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