1b7019ac5SIlia Mirkin // SPDX-License-Identifier: MIT 277145f1cSBen Skeggs #include <linux/vgaarb.h> 377145f1cSBen Skeggs #include <linux/vga_switcheroo.h> 477145f1cSBen Skeggs 5d0f54f51SNoralf Trønnes #include <drm/drm_fb_helper.h> 677145f1cSBen Skeggs 74dc28134SBen Skeggs #include "nouveau_drv.h" 877145f1cSBen Skeggs #include "nouveau_acpi.h" 95b8a43aeSMarcin Slusarz #include "nouveau_vga.h" 1077145f1cSBen Skeggs 1177145f1cSBen Skeggs static unsigned int 12bf44e8ceSChristoph Hellwig nouveau_vga_set_decode(struct pci_dev *pdev, bool state) 1377145f1cSBen Skeggs { 14*448359c1SBen Skeggs struct nouveau_drm *drm = pci_get_drvdata(pdev); 151167c6bcSBen Skeggs struct nvif_object *device = &drm->client.device.object; 1677145f1cSBen Skeggs 171167c6bcSBen Skeggs if (drm->client.device.info.family == NV_DEVICE_INFO_V0_CURIE && 181167c6bcSBen Skeggs drm->client.device.info.chipset >= 0x4c) 19db2bec18SBen Skeggs nvif_wr32(device, 0x088060, state); 2077145f1cSBen Skeggs else 211167c6bcSBen Skeggs if (drm->client.device.info.chipset >= 0x40) 22db2bec18SBen Skeggs nvif_wr32(device, 0x088054, state); 23db2bec18SBen Skeggs else 24db2bec18SBen Skeggs nvif_wr32(device, 0x001854, state); 2577145f1cSBen Skeggs 2677145f1cSBen Skeggs if (state) 2777145f1cSBen Skeggs return VGA_RSRC_LEGACY_IO | VGA_RSRC_LEGACY_MEM | 2877145f1cSBen Skeggs VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM; 2977145f1cSBen Skeggs else 3077145f1cSBen Skeggs return VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM; 3177145f1cSBen Skeggs } 3277145f1cSBen Skeggs 3377145f1cSBen Skeggs static void 3477145f1cSBen Skeggs nouveau_switcheroo_set_state(struct pci_dev *pdev, 3577145f1cSBen Skeggs enum vga_switcheroo_state state) 3677145f1cSBen Skeggs { 37*448359c1SBen Skeggs struct nouveau_drm *drm = pci_get_drvdata(pdev); 38*448359c1SBen Skeggs struct drm_device *dev = drm->dev; 3977145f1cSBen Skeggs 405addcf0aSDave Airlie if ((nouveau_is_optimus() || nouveau_is_v1_dsm()) && state == VGA_SWITCHEROO_OFF) 415addcf0aSDave Airlie return; 425addcf0aSDave Airlie 4377145f1cSBen Skeggs if (state == VGA_SWITCHEROO_ON) { 448dfe162aSJoe Perches pr_err("VGA switcheroo: switched nouveau on\n"); 4577145f1cSBen Skeggs dev->switch_power_state = DRM_SWITCH_POWER_CHANGING; 462d8b9ccbSDave Airlie nouveau_pmops_resume(&pdev->dev); 4777145f1cSBen Skeggs dev->switch_power_state = DRM_SWITCH_POWER_ON; 4877145f1cSBen Skeggs } else { 498dfe162aSJoe Perches pr_err("VGA switcheroo: switched nouveau off\n"); 5077145f1cSBen Skeggs dev->switch_power_state = DRM_SWITCH_POWER_CHANGING; 5177145f1cSBen Skeggs nouveau_switcheroo_optimus_dsm(); 522d8b9ccbSDave Airlie nouveau_pmops_suspend(&pdev->dev); 5377145f1cSBen Skeggs dev->switch_power_state = DRM_SWITCH_POWER_OFF; 5477145f1cSBen Skeggs } 5577145f1cSBen Skeggs } 5677145f1cSBen Skeggs 5777145f1cSBen Skeggs static void 5877145f1cSBen Skeggs nouveau_switcheroo_reprobe(struct pci_dev *pdev) 5977145f1cSBen Skeggs { 60*448359c1SBen Skeggs struct nouveau_drm *drm = pci_get_drvdata(pdev); 61*448359c1SBen Skeggs 62*448359c1SBen Skeggs drm_fb_helper_output_poll_changed(drm->dev); 6377145f1cSBen Skeggs } 6477145f1cSBen Skeggs 6577145f1cSBen Skeggs static bool 6677145f1cSBen Skeggs nouveau_switcheroo_can_switch(struct pci_dev *pdev) 6777145f1cSBen Skeggs { 68*448359c1SBen Skeggs struct nouveau_drm *drm = pci_get_drvdata(pdev); 6977145f1cSBen Skeggs 70fc8fd40eSDaniel Vetter /* 71fc8fd40eSDaniel Vetter * FIXME: open_count is protected by drm_global_mutex but that would lead to 72fc8fd40eSDaniel Vetter * locking inversion with the driver load path. And the access here is 73fc8fd40eSDaniel Vetter * completely racy anyway. So don't bother with locking for now. 74fc8fd40eSDaniel Vetter */ 75*448359c1SBen Skeggs return atomic_read(&drm->dev->open_count) == 0; 7677145f1cSBen Skeggs } 7777145f1cSBen Skeggs 7877145f1cSBen Skeggs static const struct vga_switcheroo_client_ops 7977145f1cSBen Skeggs nouveau_switcheroo_ops = { 8077145f1cSBen Skeggs .set_gpu_state = nouveau_switcheroo_set_state, 8177145f1cSBen Skeggs .reprobe = nouveau_switcheroo_reprobe, 8277145f1cSBen Skeggs .can_switch = nouveau_switcheroo_can_switch, 8377145f1cSBen Skeggs }; 8477145f1cSBen Skeggs 8577145f1cSBen Skeggs void 8677145f1cSBen Skeggs nouveau_vga_init(struct nouveau_drm *drm) 8777145f1cSBen Skeggs { 8877145f1cSBen Skeggs struct drm_device *dev = drm->dev; 89321f5c5fSBen Skeggs bool runtime = nouveau_pmops_runtime(); 904c0d42f7SThomas Zimmermann struct pci_dev *pdev; 91420b9469SAlexandre Courbot 92420b9469SAlexandre Courbot /* only relevant for PCI devices */ 934c0d42f7SThomas Zimmermann if (!dev_is_pci(dev->dev)) 94420b9469SAlexandre Courbot return; 954c0d42f7SThomas Zimmermann pdev = to_pci_dev(dev->dev); 96420b9469SAlexandre Courbot 97bf44e8ceSChristoph Hellwig vga_client_register(pdev, nouveau_vga_set_decode); 985addcf0aSDave Airlie 991d3c1103SLukas Wunner /* don't register Thunderbolt eGPU with vga_switcheroo */ 1004c0d42f7SThomas Zimmermann if (pci_is_thunderbolt_attached(pdev)) 1011d3c1103SLukas Wunner return; 1021d3c1103SLukas Wunner 1034c0d42f7SThomas Zimmermann vga_switcheroo_register_client(pdev, &nouveau_switcheroo_ops, runtime); 1045addcf0aSDave Airlie 1055addcf0aSDave Airlie if (runtime && nouveau_is_v1_dsm() && !nouveau_is_optimus()) 1065addcf0aSDave Airlie vga_switcheroo_init_domain_pm_ops(drm->dev->dev, &drm->vga_pm_domain); 10777145f1cSBen Skeggs } 10877145f1cSBen Skeggs 10977145f1cSBen Skeggs void 11077145f1cSBen Skeggs nouveau_vga_fini(struct nouveau_drm *drm) 11177145f1cSBen Skeggs { 11277145f1cSBen Skeggs struct drm_device *dev = drm->dev; 113321f5c5fSBen Skeggs bool runtime = nouveau_pmops_runtime(); 1144c0d42f7SThomas Zimmermann struct pci_dev *pdev; 11553beaa01SAlex Deucher 116876ea7beSMikko Perttunen /* only relevant for PCI devices */ 1174c0d42f7SThomas Zimmermann if (!dev_is_pci(dev->dev)) 1184c0d42f7SThomas Zimmermann return; 1194c0d42f7SThomas Zimmermann pdev = to_pci_dev(dev->dev); 1204c0d42f7SThomas Zimmermann 121b8779475SChristoph Hellwig vga_client_unregister(pdev); 1224c0d42f7SThomas Zimmermann 1234c0d42f7SThomas Zimmermann if (pci_is_thunderbolt_attached(pdev)) 124876ea7beSMikko Perttunen return; 125876ea7beSMikko Perttunen 1264c0d42f7SThomas Zimmermann vga_switcheroo_unregister_client(pdev); 12753beaa01SAlex Deucher if (runtime && nouveau_is_v1_dsm() && !nouveau_is_optimus()) 12853beaa01SAlex Deucher vga_switcheroo_fini_domain_pm_ops(drm->dev->dev); 12977145f1cSBen Skeggs } 13077145f1cSBen Skeggs 13177145f1cSBen Skeggs 13277145f1cSBen Skeggs void 13377145f1cSBen Skeggs nouveau_vga_lastclose(struct drm_device *dev) 13477145f1cSBen Skeggs { 13577145f1cSBen Skeggs vga_switcheroo_process_delayed_switch(); 13677145f1cSBen Skeggs } 137