1*677dec6eSriastradh /* $NetBSD: nouveau_vga.c,v 1.3 2021/12/18 23:45:32 riastradh Exp $ */
29d5db0c2Sriastradh
3*677dec6eSriastradh // SPDX-License-Identifier: MIT
49d5db0c2Sriastradh #include <sys/cdefs.h>
5*677dec6eSriastradh __KERNEL_RCSID(0, "$NetBSD: nouveau_vga.c,v 1.3 2021/12/18 23:45:32 riastradh Exp $");
69d5db0c2Sriastradh
7cb459498Sriastradh #include <linux/vgaarb.h>
8cb459498Sriastradh #include <linux/vga_switcheroo.h>
9cb459498Sriastradh
10cb459498Sriastradh #include <drm/drm_crtc_helper.h>
11*677dec6eSriastradh #include <drm/drm_fb_helper.h>
12cb459498Sriastradh
13*677dec6eSriastradh #include "nouveau_drv.h"
14cb459498Sriastradh #include "nouveau_acpi.h"
15cb459498Sriastradh #include "nouveau_fbcon.h"
16cb459498Sriastradh #include "nouveau_vga.h"
17cb459498Sriastradh
18cb459498Sriastradh static unsigned int
nouveau_vga_set_decode(void * priv,bool state)19cb459498Sriastradh nouveau_vga_set_decode(void *priv, bool state)
20cb459498Sriastradh {
21d350ecf5Sriastradh struct nouveau_drm *drm = nouveau_drm(priv);
22*677dec6eSriastradh struct nvif_object *device = &drm->client.device.object;
23cb459498Sriastradh
24*677dec6eSriastradh if (drm->client.device.info.family == NV_DEVICE_INFO_V0_CURIE &&
25*677dec6eSriastradh drm->client.device.info.chipset >= 0x4c)
26d350ecf5Sriastradh nvif_wr32(device, 0x088060, state);
27cb459498Sriastradh else
28*677dec6eSriastradh if (drm->client.device.info.chipset >= 0x40)
29d350ecf5Sriastradh nvif_wr32(device, 0x088054, state);
30d350ecf5Sriastradh else
31d350ecf5Sriastradh nvif_wr32(device, 0x001854, state);
32cb459498Sriastradh
33cb459498Sriastradh if (state)
34cb459498Sriastradh return VGA_RSRC_LEGACY_IO | VGA_RSRC_LEGACY_MEM |
35cb459498Sriastradh VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM;
36cb459498Sriastradh else
37cb459498Sriastradh return VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM;
38cb459498Sriastradh }
39cb459498Sriastradh
40cb459498Sriastradh static void
nouveau_switcheroo_set_state(struct pci_dev * pdev,enum vga_switcheroo_state state)41cb459498Sriastradh nouveau_switcheroo_set_state(struct pci_dev *pdev,
42cb459498Sriastradh enum vga_switcheroo_state state)
43cb459498Sriastradh {
44cb459498Sriastradh struct drm_device *dev = pci_get_drvdata(pdev);
45cb459498Sriastradh
46cb459498Sriastradh if ((nouveau_is_optimus() || nouveau_is_v1_dsm()) && state == VGA_SWITCHEROO_OFF)
47cb459498Sriastradh return;
48cb459498Sriastradh
49cb459498Sriastradh if (state == VGA_SWITCHEROO_ON) {
50*677dec6eSriastradh pr_err("VGA switcheroo: switched nouveau on\n");
51cb459498Sriastradh dev->switch_power_state = DRM_SWITCH_POWER_CHANGING;
52cb459498Sriastradh nouveau_pmops_resume(&pdev->dev);
53cb459498Sriastradh dev->switch_power_state = DRM_SWITCH_POWER_ON;
54cb459498Sriastradh } else {
55*677dec6eSriastradh pr_err("VGA switcheroo: switched nouveau off\n");
56cb459498Sriastradh dev->switch_power_state = DRM_SWITCH_POWER_CHANGING;
57cb459498Sriastradh nouveau_switcheroo_optimus_dsm();
58cb459498Sriastradh nouveau_pmops_suspend(&pdev->dev);
59cb459498Sriastradh dev->switch_power_state = DRM_SWITCH_POWER_OFF;
60cb459498Sriastradh }
61cb459498Sriastradh }
62cb459498Sriastradh
63cb459498Sriastradh static void
nouveau_switcheroo_reprobe(struct pci_dev * pdev)64cb459498Sriastradh nouveau_switcheroo_reprobe(struct pci_dev *pdev)
65cb459498Sriastradh {
66cb459498Sriastradh struct drm_device *dev = pci_get_drvdata(pdev);
67*677dec6eSriastradh drm_fb_helper_output_poll_changed(dev);
68cb459498Sriastradh }
69cb459498Sriastradh
70cb459498Sriastradh static bool
nouveau_switcheroo_can_switch(struct pci_dev * pdev)71cb459498Sriastradh nouveau_switcheroo_can_switch(struct pci_dev *pdev)
72cb459498Sriastradh {
73cb459498Sriastradh struct drm_device *dev = pci_get_drvdata(pdev);
74cb459498Sriastradh
75d350ecf5Sriastradh /*
76d350ecf5Sriastradh * FIXME: open_count is protected by drm_global_mutex but that would lead to
77d350ecf5Sriastradh * locking inversion with the driver load path. And the access here is
78d350ecf5Sriastradh * completely racy anyway. So don't bother with locking for now.
79d350ecf5Sriastradh */
80d350ecf5Sriastradh return dev->open_count == 0;
81cb459498Sriastradh }
82cb459498Sriastradh
83cb459498Sriastradh static const struct vga_switcheroo_client_ops
84cb459498Sriastradh nouveau_switcheroo_ops = {
85cb459498Sriastradh .set_gpu_state = nouveau_switcheroo_set_state,
86cb459498Sriastradh .reprobe = nouveau_switcheroo_reprobe,
87cb459498Sriastradh .can_switch = nouveau_switcheroo_can_switch,
88cb459498Sriastradh };
89cb459498Sriastradh
90cb459498Sriastradh void
nouveau_vga_init(struct nouveau_drm * drm)91cb459498Sriastradh nouveau_vga_init(struct nouveau_drm *drm)
92cb459498Sriastradh {
93cb459498Sriastradh struct drm_device *dev = drm->dev;
94*677dec6eSriastradh bool runtime = nouveau_pmops_runtime();
95cb459498Sriastradh
96cb459498Sriastradh /* only relevant for PCI devices */
97cb459498Sriastradh if (!dev->pdev)
98cb459498Sriastradh return;
99cb459498Sriastradh
100cb459498Sriastradh vga_client_register(dev->pdev, dev, NULL, nouveau_vga_set_decode);
101cb459498Sriastradh
102*677dec6eSriastradh /* don't register Thunderbolt eGPU with vga_switcheroo */
103*677dec6eSriastradh if (pci_is_thunderbolt_attached(dev->pdev))
104*677dec6eSriastradh return;
105*677dec6eSriastradh
106cb459498Sriastradh vga_switcheroo_register_client(dev->pdev, &nouveau_switcheroo_ops, runtime);
107cb459498Sriastradh
108cb459498Sriastradh if (runtime && nouveau_is_v1_dsm() && !nouveau_is_optimus())
109cb459498Sriastradh vga_switcheroo_init_domain_pm_ops(drm->dev->dev, &drm->vga_pm_domain);
110cb459498Sriastradh }
111cb459498Sriastradh
112cb459498Sriastradh void
nouveau_vga_fini(struct nouveau_drm * drm)113cb459498Sriastradh nouveau_vga_fini(struct nouveau_drm *drm)
114cb459498Sriastradh {
115cb459498Sriastradh struct drm_device *dev = drm->dev;
116*677dec6eSriastradh bool runtime = nouveau_pmops_runtime();
117d350ecf5Sriastradh
118*677dec6eSriastradh /* only relevant for PCI devices */
119*677dec6eSriastradh if (!dev->pdev)
120*677dec6eSriastradh return;
121*677dec6eSriastradh
122*677dec6eSriastradh vga_client_register(dev->pdev, NULL, NULL, NULL);
123*677dec6eSriastradh
124*677dec6eSriastradh if (pci_is_thunderbolt_attached(dev->pdev))
125*677dec6eSriastradh return;
126d350ecf5Sriastradh
127cb459498Sriastradh vga_switcheroo_unregister_client(dev->pdev);
128d350ecf5Sriastradh if (runtime && nouveau_is_v1_dsm() && !nouveau_is_optimus())
129d350ecf5Sriastradh vga_switcheroo_fini_domain_pm_ops(drm->dev->dev);
130cb459498Sriastradh }
131cb459498Sriastradh
132cb459498Sriastradh
133cb459498Sriastradh void
nouveau_vga_lastclose(struct drm_device * dev)134cb459498Sriastradh nouveau_vga_lastclose(struct drm_device *dev)
135cb459498Sriastradh {
136cb459498Sriastradh vga_switcheroo_process_delayed_switch();
137cb459498Sriastradh }
138