18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: MIT 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright © 2019 Intel Corporation 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/vga_switcheroo.h> 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include "i915_drv.h" 98c2ecf20Sopenharmony_ci#include "i915_switcheroo.h" 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_cistatic void i915_switcheroo_set_state(struct pci_dev *pdev, 128c2ecf20Sopenharmony_ci enum vga_switcheroo_state state) 138c2ecf20Sopenharmony_ci{ 148c2ecf20Sopenharmony_ci struct drm_i915_private *i915 = pdev_to_i915(pdev); 158c2ecf20Sopenharmony_ci pm_message_t pmm = { .event = PM_EVENT_SUSPEND }; 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci if (!i915) { 188c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "DRM not initialized, aborting switch.\n"); 198c2ecf20Sopenharmony_ci return; 208c2ecf20Sopenharmony_ci } 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci if (state == VGA_SWITCHEROO_ON) { 238c2ecf20Sopenharmony_ci drm_info(&i915->drm, "switched on\n"); 248c2ecf20Sopenharmony_ci i915->drm.switch_power_state = DRM_SWITCH_POWER_CHANGING; 258c2ecf20Sopenharmony_ci /* i915 resume handler doesn't set to D0 */ 268c2ecf20Sopenharmony_ci pci_set_power_state(pdev, PCI_D0); 278c2ecf20Sopenharmony_ci i915_resume_switcheroo(i915); 288c2ecf20Sopenharmony_ci i915->drm.switch_power_state = DRM_SWITCH_POWER_ON; 298c2ecf20Sopenharmony_ci } else { 308c2ecf20Sopenharmony_ci drm_info(&i915->drm, "switched off\n"); 318c2ecf20Sopenharmony_ci i915->drm.switch_power_state = DRM_SWITCH_POWER_CHANGING; 328c2ecf20Sopenharmony_ci i915_suspend_switcheroo(i915, pmm); 338c2ecf20Sopenharmony_ci i915->drm.switch_power_state = DRM_SWITCH_POWER_OFF; 348c2ecf20Sopenharmony_ci } 358c2ecf20Sopenharmony_ci} 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistatic bool i915_switcheroo_can_switch(struct pci_dev *pdev) 388c2ecf20Sopenharmony_ci{ 398c2ecf20Sopenharmony_ci struct drm_i915_private *i915 = pdev_to_i915(pdev); 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci /* 428c2ecf20Sopenharmony_ci * FIXME: open_count is protected by drm_global_mutex but that would lead to 438c2ecf20Sopenharmony_ci * locking inversion with the driver load path. And the access here is 448c2ecf20Sopenharmony_ci * completely racy anyway. So don't bother with locking for now. 458c2ecf20Sopenharmony_ci */ 468c2ecf20Sopenharmony_ci return i915 && atomic_read(&i915->drm.open_count) == 0; 478c2ecf20Sopenharmony_ci} 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic const struct vga_switcheroo_client_ops i915_switcheroo_ops = { 508c2ecf20Sopenharmony_ci .set_gpu_state = i915_switcheroo_set_state, 518c2ecf20Sopenharmony_ci .reprobe = NULL, 528c2ecf20Sopenharmony_ci .can_switch = i915_switcheroo_can_switch, 538c2ecf20Sopenharmony_ci}; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ciint i915_switcheroo_register(struct drm_i915_private *i915) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci struct pci_dev *pdev = i915->drm.pdev; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci return vga_switcheroo_register_client(pdev, &i915_switcheroo_ops, false); 608c2ecf20Sopenharmony_ci} 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_civoid i915_switcheroo_unregister(struct drm_i915_private *i915) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci struct pci_dev *pdev = i915->drm.pdev; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci vga_switcheroo_unregister_client(pdev); 678c2ecf20Sopenharmony_ci} 68