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 17*b71313e1SIlia Mirkin if (device->card_type == NV_40 && device->chipset >= 0x4c) 18*b71313e1SIlia Mirkin nv_wr32(device, 0x088060, state); 19*b71313e1SIlia 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 bool can_switch; 6877145f1cSBen Skeggs 6977145f1cSBen Skeggs spin_lock(&dev->count_lock); 7077145f1cSBen Skeggs can_switch = (dev->open_count == 0); 7177145f1cSBen Skeggs spin_unlock(&dev->count_lock); 7277145f1cSBen Skeggs return can_switch; 7377145f1cSBen Skeggs } 7477145f1cSBen Skeggs 7577145f1cSBen Skeggs static const struct vga_switcheroo_client_ops 7677145f1cSBen Skeggs nouveau_switcheroo_ops = { 7777145f1cSBen Skeggs .set_gpu_state = nouveau_switcheroo_set_state, 7877145f1cSBen Skeggs .reprobe = nouveau_switcheroo_reprobe, 7977145f1cSBen Skeggs .can_switch = nouveau_switcheroo_can_switch, 8077145f1cSBen Skeggs }; 8177145f1cSBen Skeggs 8277145f1cSBen Skeggs void 8377145f1cSBen Skeggs nouveau_vga_init(struct nouveau_drm *drm) 8477145f1cSBen Skeggs { 8577145f1cSBen Skeggs struct drm_device *dev = drm->dev; 865addcf0aSDave Airlie bool runtime = false; 8777145f1cSBen Skeggs vga_client_register(dev->pdev, dev, NULL, nouveau_vga_set_decode); 885addcf0aSDave Airlie 895addcf0aSDave Airlie if (nouveau_runtime_pm == 1) 905addcf0aSDave Airlie runtime = true; 915addcf0aSDave Airlie if ((nouveau_runtime_pm == -1) && (nouveau_is_optimus() || nouveau_is_v1_dsm())) 925addcf0aSDave Airlie runtime = true; 935addcf0aSDave Airlie vga_switcheroo_register_client(dev->pdev, &nouveau_switcheroo_ops, runtime); 945addcf0aSDave Airlie 955addcf0aSDave Airlie if (runtime && nouveau_is_v1_dsm() && !nouveau_is_optimus()) 965addcf0aSDave Airlie vga_switcheroo_init_domain_pm_ops(drm->dev->dev, &drm->vga_pm_domain); 9777145f1cSBen Skeggs } 9877145f1cSBen Skeggs 9977145f1cSBen Skeggs void 10077145f1cSBen Skeggs nouveau_vga_fini(struct nouveau_drm *drm) 10177145f1cSBen Skeggs { 10277145f1cSBen Skeggs struct drm_device *dev = drm->dev; 10377145f1cSBen Skeggs vga_switcheroo_unregister_client(dev->pdev); 10477145f1cSBen Skeggs vga_client_register(dev->pdev, NULL, NULL, NULL); 10577145f1cSBen Skeggs } 10677145f1cSBen Skeggs 10777145f1cSBen Skeggs 10877145f1cSBen Skeggs void 10977145f1cSBen Skeggs nouveau_vga_lastclose(struct drm_device *dev) 11077145f1cSBen Skeggs { 11177145f1cSBen Skeggs vga_switcheroo_process_delayed_switch(); 11277145f1cSBen Skeggs } 113