162306a36Sopenharmony_ci// SPDX-License-Identifier: MIT 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright © 2019 Intel Corporation 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/vga_switcheroo.h> 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include "i915_driver.h" 962306a36Sopenharmony_ci#include "i915_drv.h" 1062306a36Sopenharmony_ci#include "i915_switcheroo.h" 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_cistatic void i915_switcheroo_set_state(struct pci_dev *pdev, 1362306a36Sopenharmony_ci enum vga_switcheroo_state state) 1462306a36Sopenharmony_ci{ 1562306a36Sopenharmony_ci struct drm_i915_private *i915 = pdev_to_i915(pdev); 1662306a36Sopenharmony_ci pm_message_t pmm = { .event = PM_EVENT_SUSPEND }; 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci if (!i915) { 1962306a36Sopenharmony_ci dev_err(&pdev->dev, "DRM not initialized, aborting switch.\n"); 2062306a36Sopenharmony_ci return; 2162306a36Sopenharmony_ci } 2262306a36Sopenharmony_ci if (!HAS_DISPLAY(i915)) { 2362306a36Sopenharmony_ci dev_err(&pdev->dev, "Device state not initialized, aborting switch.\n"); 2462306a36Sopenharmony_ci return; 2562306a36Sopenharmony_ci } 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci if (state == VGA_SWITCHEROO_ON) { 2862306a36Sopenharmony_ci drm_info(&i915->drm, "switched on\n"); 2962306a36Sopenharmony_ci i915->drm.switch_power_state = DRM_SWITCH_POWER_CHANGING; 3062306a36Sopenharmony_ci /* i915 resume handler doesn't set to D0 */ 3162306a36Sopenharmony_ci pci_set_power_state(pdev, PCI_D0); 3262306a36Sopenharmony_ci i915_driver_resume_switcheroo(i915); 3362306a36Sopenharmony_ci i915->drm.switch_power_state = DRM_SWITCH_POWER_ON; 3462306a36Sopenharmony_ci } else { 3562306a36Sopenharmony_ci drm_info(&i915->drm, "switched off\n"); 3662306a36Sopenharmony_ci i915->drm.switch_power_state = DRM_SWITCH_POWER_CHANGING; 3762306a36Sopenharmony_ci i915_driver_suspend_switcheroo(i915, pmm); 3862306a36Sopenharmony_ci i915->drm.switch_power_state = DRM_SWITCH_POWER_OFF; 3962306a36Sopenharmony_ci } 4062306a36Sopenharmony_ci} 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic bool i915_switcheroo_can_switch(struct pci_dev *pdev) 4362306a36Sopenharmony_ci{ 4462306a36Sopenharmony_ci struct drm_i915_private *i915 = pdev_to_i915(pdev); 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci /* 4762306a36Sopenharmony_ci * FIXME: open_count is protected by drm_global_mutex but that would lead to 4862306a36Sopenharmony_ci * locking inversion with the driver load path. And the access here is 4962306a36Sopenharmony_ci * completely racy anyway. So don't bother with locking for now. 5062306a36Sopenharmony_ci */ 5162306a36Sopenharmony_ci return i915 && HAS_DISPLAY(i915) && atomic_read(&i915->drm.open_count) == 0; 5262306a36Sopenharmony_ci} 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistatic const struct vga_switcheroo_client_ops i915_switcheroo_ops = { 5562306a36Sopenharmony_ci .set_gpu_state = i915_switcheroo_set_state, 5662306a36Sopenharmony_ci .reprobe = NULL, 5762306a36Sopenharmony_ci .can_switch = i915_switcheroo_can_switch, 5862306a36Sopenharmony_ci}; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ciint i915_switcheroo_register(struct drm_i915_private *i915) 6162306a36Sopenharmony_ci{ 6262306a36Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(i915->drm.dev); 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci return vga_switcheroo_register_client(pdev, &i915_switcheroo_ops, false); 6562306a36Sopenharmony_ci} 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_civoid i915_switcheroo_unregister(struct drm_i915_private *i915) 6862306a36Sopenharmony_ci{ 6962306a36Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(i915->drm.dev); 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci vga_switcheroo_unregister_client(pdev); 7262306a36Sopenharmony_ci} 73