177145f1cSBen Skeggs #include <linux/vgaarb.h> 277145f1cSBen Skeggs #include <linux/vga_switcheroo.h> 377145f1cSBen Skeggs 4612a9aabSLinus Torvalds #include <drm/drmP.h> 5612a9aabSLinus Torvalds #include <drm/drm_crtc_helper.h> 677145f1cSBen Skeggs 74dc28134SBen Skeggs #include "nouveau_drv.h" 877145f1cSBen Skeggs #include "nouveau_acpi.h" 977145f1cSBen Skeggs #include "nouveau_fbcon.h" 105b8a43aeSMarcin Slusarz #include "nouveau_vga.h" 1177145f1cSBen Skeggs 1277145f1cSBen Skeggs static unsigned int 1377145f1cSBen Skeggs nouveau_vga_set_decode(void *priv, bool state) 1477145f1cSBen Skeggs { 15a01ca78cSBen Skeggs struct nouveau_drm *drm = nouveau_drm(priv); 161167c6bcSBen Skeggs struct nvif_object *device = &drm->client.device.object; 1777145f1cSBen Skeggs 181167c6bcSBen Skeggs if (drm->client.device.info.family == NV_DEVICE_INFO_V0_CURIE && 191167c6bcSBen Skeggs drm->client.device.info.chipset >= 0x4c) 20db2bec18SBen Skeggs nvif_wr32(device, 0x088060, state); 2177145f1cSBen Skeggs else 221167c6bcSBen Skeggs if (drm->client.device.info.chipset >= 0x40) 23db2bec18SBen Skeggs nvif_wr32(device, 0x088054, state); 24db2bec18SBen Skeggs else 25db2bec18SBen Skeggs nvif_wr32(device, 0x001854, state); 2677145f1cSBen Skeggs 2777145f1cSBen Skeggs if (state) 2877145f1cSBen Skeggs return VGA_RSRC_LEGACY_IO | VGA_RSRC_LEGACY_MEM | 2977145f1cSBen Skeggs VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM; 3077145f1cSBen Skeggs else 3177145f1cSBen Skeggs return VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM; 3277145f1cSBen Skeggs } 3377145f1cSBen Skeggs 3477145f1cSBen Skeggs static void 3577145f1cSBen Skeggs nouveau_switcheroo_set_state(struct pci_dev *pdev, 3677145f1cSBen Skeggs enum vga_switcheroo_state state) 3777145f1cSBen Skeggs { 3877145f1cSBen Skeggs struct drm_device *dev = pci_get_drvdata(pdev); 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 drm_kms_helper_poll_enable(dev); 4877145f1cSBen Skeggs dev->switch_power_state = DRM_SWITCH_POWER_ON; 4977145f1cSBen Skeggs } else { 508dfe162aSJoe Perches pr_err("VGA switcheroo: switched nouveau off\n"); 5177145f1cSBen Skeggs dev->switch_power_state = DRM_SWITCH_POWER_CHANGING; 5277145f1cSBen Skeggs drm_kms_helper_poll_disable(dev); 5377145f1cSBen Skeggs nouveau_switcheroo_optimus_dsm(); 542d8b9ccbSDave Airlie nouveau_pmops_suspend(&pdev->dev); 5577145f1cSBen Skeggs dev->switch_power_state = DRM_SWITCH_POWER_OFF; 5677145f1cSBen Skeggs } 5777145f1cSBen Skeggs } 5877145f1cSBen Skeggs 5977145f1cSBen Skeggs static void 6077145f1cSBen Skeggs nouveau_switcheroo_reprobe(struct pci_dev *pdev) 6177145f1cSBen Skeggs { 6277145f1cSBen Skeggs struct drm_device *dev = pci_get_drvdata(pdev); 6377145f1cSBen Skeggs nouveau_fbcon_output_poll_changed(dev); 6477145f1cSBen Skeggs } 6577145f1cSBen Skeggs 6677145f1cSBen Skeggs static bool 6777145f1cSBen Skeggs nouveau_switcheroo_can_switch(struct pci_dev *pdev) 6877145f1cSBen Skeggs { 6977145f1cSBen Skeggs struct drm_device *dev = pci_get_drvdata(pdev); 7077145f1cSBen Skeggs 71fc8fd40eSDaniel Vetter /* 72fc8fd40eSDaniel Vetter * FIXME: open_count is protected by drm_global_mutex but that would lead to 73fc8fd40eSDaniel Vetter * locking inversion with the driver load path. And the access here is 74fc8fd40eSDaniel Vetter * completely racy anyway. So don't bother with locking for now. 75fc8fd40eSDaniel Vetter */ 76fc8fd40eSDaniel Vetter return dev->open_count == 0; 7777145f1cSBen Skeggs } 7877145f1cSBen Skeggs 7977145f1cSBen Skeggs static const struct vga_switcheroo_client_ops 8077145f1cSBen Skeggs nouveau_switcheroo_ops = { 8177145f1cSBen Skeggs .set_gpu_state = nouveau_switcheroo_set_state, 8277145f1cSBen Skeggs .reprobe = nouveau_switcheroo_reprobe, 8377145f1cSBen Skeggs .can_switch = nouveau_switcheroo_can_switch, 8477145f1cSBen Skeggs }; 8577145f1cSBen Skeggs 8677145f1cSBen Skeggs void 8777145f1cSBen Skeggs nouveau_vga_init(struct nouveau_drm *drm) 8877145f1cSBen Skeggs { 8977145f1cSBen Skeggs struct drm_device *dev = drm->dev; 90321f5c5fSBen Skeggs bool runtime = nouveau_pmops_runtime(); 91420b9469SAlexandre Courbot 92420b9469SAlexandre Courbot /* only relevant for PCI devices */ 93420b9469SAlexandre Courbot if (!dev->pdev) 94420b9469SAlexandre Courbot return; 95420b9469SAlexandre Courbot 9677145f1cSBen Skeggs vga_client_register(dev->pdev, dev, NULL, nouveau_vga_set_decode); 975addcf0aSDave Airlie 981d3c1103SLukas Wunner /* don't register Thunderbolt eGPU with vga_switcheroo */ 991d3c1103SLukas Wunner if (pci_is_thunderbolt_attached(dev->pdev)) 1001d3c1103SLukas Wunner return; 1011d3c1103SLukas Wunner 1025addcf0aSDave Airlie vga_switcheroo_register_client(dev->pdev, &nouveau_switcheroo_ops, runtime); 1035addcf0aSDave Airlie 1045addcf0aSDave Airlie if (runtime && nouveau_is_v1_dsm() && !nouveau_is_optimus()) 1055addcf0aSDave Airlie vga_switcheroo_init_domain_pm_ops(drm->dev->dev, &drm->vga_pm_domain); 10677145f1cSBen Skeggs } 10777145f1cSBen Skeggs 10877145f1cSBen Skeggs void 10977145f1cSBen Skeggs nouveau_vga_fini(struct nouveau_drm *drm) 11077145f1cSBen Skeggs { 11177145f1cSBen Skeggs struct drm_device *dev = drm->dev; 112321f5c5fSBen Skeggs bool runtime = nouveau_pmops_runtime(); 11353beaa01SAlex Deucher 114*876ea7beSMikko Perttunen /* only relevant for PCI devices */ 115*876ea7beSMikko Perttunen if (!dev->pdev) 116*876ea7beSMikko Perttunen return; 117*876ea7beSMikko Perttunen 1181d3c1103SLukas Wunner vga_client_register(dev->pdev, NULL, NULL, NULL); 1191d3c1103SLukas Wunner 1201d3c1103SLukas Wunner if (pci_is_thunderbolt_attached(dev->pdev)) 1211d3c1103SLukas Wunner return; 1221d3c1103SLukas Wunner 12377145f1cSBen Skeggs vga_switcheroo_unregister_client(dev->pdev); 12453beaa01SAlex Deucher if (runtime && nouveau_is_v1_dsm() && !nouveau_is_optimus()) 12553beaa01SAlex Deucher vga_switcheroo_fini_domain_pm_ops(drm->dev->dev); 12677145f1cSBen Skeggs } 12777145f1cSBen Skeggs 12877145f1cSBen Skeggs 12977145f1cSBen Skeggs void 13077145f1cSBen Skeggs nouveau_vga_lastclose(struct drm_device *dev) 13177145f1cSBen Skeggs { 13277145f1cSBen Skeggs vga_switcheroo_process_delayed_switch(); 13377145f1cSBen Skeggs } 134