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 777145f1cSBen Skeggs #include "nouveau_drm.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 { 1577145f1cSBen Skeggs struct nouveau_device *device = nouveau_dev(priv); 1677145f1cSBen Skeggs 17b71313e1SIlia Mirkin if (device->card_type == NV_40 && device->chipset >= 0x4c) 18b71313e1SIlia Mirkin nv_wr32(device, 0x088060, state); 19b71313e1SIlia Mirkin else if (device->chipset >= 0x40) 2077145f1cSBen Skeggs nv_wr32(device, 0x088054, state); 2177145f1cSBen Skeggs else 2277145f1cSBen Skeggs nv_wr32(device, 0x001854, state); 2377145f1cSBen Skeggs 2477145f1cSBen Skeggs if (state) 2577145f1cSBen Skeggs return VGA_RSRC_LEGACY_IO | VGA_RSRC_LEGACY_MEM | 2677145f1cSBen Skeggs VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM; 2777145f1cSBen Skeggs else 2877145f1cSBen Skeggs return VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM; 2977145f1cSBen Skeggs } 3077145f1cSBen Skeggs 3177145f1cSBen Skeggs static void 3277145f1cSBen Skeggs nouveau_switcheroo_set_state(struct pci_dev *pdev, 3377145f1cSBen Skeggs enum vga_switcheroo_state state) 3477145f1cSBen Skeggs { 3577145f1cSBen Skeggs struct drm_device *dev = pci_get_drvdata(pdev); 3677145f1cSBen Skeggs 375addcf0aSDave Airlie if ((nouveau_is_optimus() || nouveau_is_v1_dsm()) && state == VGA_SWITCHEROO_OFF) 385addcf0aSDave Airlie return; 395addcf0aSDave Airlie 4077145f1cSBen Skeggs if (state == VGA_SWITCHEROO_ON) { 4177145f1cSBen Skeggs printk(KERN_ERR "VGA switcheroo: switched nouveau on\n"); 4277145f1cSBen Skeggs dev->switch_power_state = DRM_SWITCH_POWER_CHANGING; 432d8b9ccbSDave Airlie nouveau_pmops_resume(&pdev->dev); 4477145f1cSBen Skeggs drm_kms_helper_poll_enable(dev); 4577145f1cSBen Skeggs dev->switch_power_state = DRM_SWITCH_POWER_ON; 4677145f1cSBen Skeggs } else { 4777145f1cSBen Skeggs printk(KERN_ERR "VGA switcheroo: switched nouveau off\n"); 4877145f1cSBen Skeggs dev->switch_power_state = DRM_SWITCH_POWER_CHANGING; 4977145f1cSBen Skeggs drm_kms_helper_poll_disable(dev); 5077145f1cSBen Skeggs nouveau_switcheroo_optimus_dsm(); 512d8b9ccbSDave Airlie nouveau_pmops_suspend(&pdev->dev); 5277145f1cSBen Skeggs dev->switch_power_state = DRM_SWITCH_POWER_OFF; 5377145f1cSBen Skeggs } 5477145f1cSBen Skeggs } 5577145f1cSBen Skeggs 5677145f1cSBen Skeggs static void 5777145f1cSBen Skeggs nouveau_switcheroo_reprobe(struct pci_dev *pdev) 5877145f1cSBen Skeggs { 5977145f1cSBen Skeggs struct drm_device *dev = pci_get_drvdata(pdev); 6077145f1cSBen Skeggs nouveau_fbcon_output_poll_changed(dev); 6177145f1cSBen Skeggs } 6277145f1cSBen Skeggs 6377145f1cSBen Skeggs static bool 6477145f1cSBen Skeggs nouveau_switcheroo_can_switch(struct pci_dev *pdev) 6577145f1cSBen Skeggs { 6677145f1cSBen Skeggs struct drm_device *dev = pci_get_drvdata(pdev); 6777145f1cSBen Skeggs 68*fc8fd40eSDaniel Vetter /* 69*fc8fd40eSDaniel Vetter * FIXME: open_count is protected by drm_global_mutex but that would lead to 70*fc8fd40eSDaniel Vetter * locking inversion with the driver load path. And the access here is 71*fc8fd40eSDaniel Vetter * completely racy anyway. So don't bother with locking for now. 72*fc8fd40eSDaniel Vetter */ 73*fc8fd40eSDaniel Vetter return dev->open_count == 0; 7477145f1cSBen Skeggs } 7577145f1cSBen Skeggs 7677145f1cSBen Skeggs static const struct vga_switcheroo_client_ops 7777145f1cSBen Skeggs nouveau_switcheroo_ops = { 7877145f1cSBen Skeggs .set_gpu_state = nouveau_switcheroo_set_state, 7977145f1cSBen Skeggs .reprobe = nouveau_switcheroo_reprobe, 8077145f1cSBen Skeggs .can_switch = nouveau_switcheroo_can_switch, 8177145f1cSBen Skeggs }; 8277145f1cSBen Skeggs 8377145f1cSBen Skeggs void 8477145f1cSBen Skeggs nouveau_vga_init(struct nouveau_drm *drm) 8577145f1cSBen Skeggs { 8677145f1cSBen Skeggs struct drm_device *dev = drm->dev; 875addcf0aSDave Airlie bool runtime = false; 88420b9469SAlexandre Courbot 89420b9469SAlexandre Courbot /* only relevant for PCI devices */ 90420b9469SAlexandre Courbot if (!dev->pdev) 91420b9469SAlexandre Courbot return; 92420b9469SAlexandre Courbot 9377145f1cSBen Skeggs vga_client_register(dev->pdev, dev, NULL, nouveau_vga_set_decode); 945addcf0aSDave Airlie 955addcf0aSDave Airlie if (nouveau_runtime_pm == 1) 965addcf0aSDave Airlie runtime = true; 975addcf0aSDave Airlie if ((nouveau_runtime_pm == -1) && (nouveau_is_optimus() || nouveau_is_v1_dsm())) 985addcf0aSDave Airlie runtime = true; 995addcf0aSDave Airlie vga_switcheroo_register_client(dev->pdev, &nouveau_switcheroo_ops, runtime); 1005addcf0aSDave Airlie 1015addcf0aSDave Airlie if (runtime && nouveau_is_v1_dsm() && !nouveau_is_optimus()) 1025addcf0aSDave Airlie vga_switcheroo_init_domain_pm_ops(drm->dev->dev, &drm->vga_pm_domain); 10377145f1cSBen Skeggs } 10477145f1cSBen Skeggs 10577145f1cSBen Skeggs void 10677145f1cSBen Skeggs nouveau_vga_fini(struct nouveau_drm *drm) 10777145f1cSBen Skeggs { 10877145f1cSBen Skeggs struct drm_device *dev = drm->dev; 10977145f1cSBen Skeggs vga_switcheroo_unregister_client(dev->pdev); 11077145f1cSBen Skeggs vga_client_register(dev->pdev, NULL, NULL, NULL); 11177145f1cSBen Skeggs } 11277145f1cSBen Skeggs 11377145f1cSBen Skeggs 11477145f1cSBen Skeggs void 11577145f1cSBen Skeggs nouveau_vga_lastclose(struct drm_device *dev) 11677145f1cSBen Skeggs { 11777145f1cSBen Skeggs vga_switcheroo_process_delayed_switch(); 11877145f1cSBen Skeggs } 119