1 /*
2  * Intel ACPI functions
3  *
4  * _DSM related code stolen from nouveau_acpi.c.
5  */
6 #include <linux/pci.h>
7 #include <linux/acpi.h>
8 #include <drm/drmP.h>
9 #include "i915_drv.h"
10 
11 #define INTEL_DSM_REVISION_ID 1 /* For Calpella anyway... */
12 #define INTEL_DSM_FN_PLATFORM_MUX_INFO 1 /* No args */
13 
14 static struct intel_dsm_priv {
15 	acpi_handle dhandle;
16 } intel_dsm_priv;
17 
18 static const u8 intel_dsm_guid[] = {
19 	0xd3, 0x73, 0xd8, 0x7e,
20 	0xd0, 0xc2,
21 	0x4f, 0x4e,
22 	0xa8, 0x54,
23 	0x0f, 0x13, 0x17, 0xb0, 0x1c, 0x2c
24 };
25 
intel_dsm_port_name(u8 id)26 static char *intel_dsm_port_name(u8 id)
27 {
28 	switch (id) {
29 	case 0:
30 		return "Reserved";
31 	case 1:
32 		return "Analog VGA";
33 	case 2:
34 		return "LVDS";
35 	case 3:
36 		return "Reserved";
37 	case 4:
38 		return "HDMI/DVI_B";
39 	case 5:
40 		return "HDMI/DVI_C";
41 	case 6:
42 		return "HDMI/DVI_D";
43 	case 7:
44 		return "DisplayPort_A";
45 	case 8:
46 		return "DisplayPort_B";
47 	case 9:
48 		return "DisplayPort_C";
49 	case 0xa:
50 		return "DisplayPort_D";
51 	case 0xb:
52 	case 0xc:
53 	case 0xd:
54 		return "Reserved";
55 	case 0xe:
56 		return "WiDi";
57 	default:
58 		return "bad type";
59 	}
60 }
61 
intel_dsm_mux_type(u8 type)62 static char *intel_dsm_mux_type(u8 type)
63 {
64 	switch (type) {
65 	case 0:
66 		return "unknown";
67 	case 1:
68 		return "No MUX, iGPU only";
69 	case 2:
70 		return "No MUX, dGPU only";
71 	case 3:
72 		return "MUXed between iGPU and dGPU";
73 	default:
74 		return "bad type";
75 	}
76 }
77 
intel_dsm_platform_mux_info(void)78 static void intel_dsm_platform_mux_info(void)
79 {
80 	int i;
81 	union acpi_object *pkg, *connector_count;
82 
83 	pkg = acpi_evaluate_dsm_typed(intel_dsm_priv.dhandle, intel_dsm_guid,
84 			INTEL_DSM_REVISION_ID, INTEL_DSM_FN_PLATFORM_MUX_INFO,
85 			NULL, ACPI_TYPE_PACKAGE);
86 	if (!pkg) {
87 		DRM_DEBUG_DRIVER("failed to evaluate _DSM\n");
88 		return;
89 	}
90 
91 	connector_count = &pkg->package.elements[0];
92 	DRM_DEBUG_DRIVER("MUX info connectors: %lld\n",
93 		  (unsigned long long)connector_count->integer.value);
94 	for (i = 1; i < pkg->package.count; i++) {
95 		union acpi_object *obj = &pkg->package.elements[i];
96 		union acpi_object *connector_id = &obj->package.elements[0];
97 		union acpi_object *info = &obj->package.elements[1];
98 		DRM_DEBUG_DRIVER("Connector id: 0x%016llx\n",
99 			  (unsigned long long)connector_id->integer.value);
100 		DRM_DEBUG_DRIVER("  port id: %s\n",
101 		       intel_dsm_port_name(info->buffer.pointer[0]));
102 		DRM_DEBUG_DRIVER("  display mux info: %s\n",
103 		       intel_dsm_mux_type(info->buffer.pointer[1]));
104 		DRM_DEBUG_DRIVER("  aux/dc mux info: %s\n",
105 		       intel_dsm_mux_type(info->buffer.pointer[2]));
106 		DRM_DEBUG_DRIVER("  hpd mux info: %s\n",
107 		       intel_dsm_mux_type(info->buffer.pointer[3]));
108 	}
109 
110 	ACPI_FREE(pkg);
111 }
112 
intel_dsm_pci_probe(struct pci_dev * pdev)113 static bool intel_dsm_pci_probe(struct pci_dev *pdev)
114 {
115 	acpi_handle dhandle;
116 
117 	dhandle = ACPI_HANDLE(&pdev->dev);
118 	if (!dhandle)
119 		return false;
120 
121 	if (!acpi_check_dsm(dhandle, intel_dsm_guid, INTEL_DSM_REVISION_ID,
122 			    1 << INTEL_DSM_FN_PLATFORM_MUX_INFO)) {
123 		DRM_DEBUG_KMS("no _DSM method for intel device\n");
124 		return false;
125 	}
126 
127 	intel_dsm_priv.dhandle = dhandle;
128 	intel_dsm_platform_mux_info();
129 
130 	return true;
131 }
132 
intel_dsm_detect(void)133 static bool intel_dsm_detect(void)
134 {
135 	char acpi_method_name[255] = { 0 };
136 	struct acpi_buffer buffer = {sizeof(acpi_method_name), acpi_method_name};
137 	struct pci_dev *pdev = NULL;
138 	bool has_dsm = false;
139 	int vga_count = 0;
140 
141 	while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev)) != NULL) {
142 		vga_count++;
143 		has_dsm |= intel_dsm_pci_probe(pdev);
144 	}
145 
146 	if (vga_count == 2 && has_dsm) {
147 		acpi_get_name(intel_dsm_priv.dhandle, ACPI_FULL_PATHNAME, &buffer);
148 		DRM_DEBUG_DRIVER("vga_switcheroo: detected DSM switching method %s handle\n",
149 				 acpi_method_name);
150 		return true;
151 	}
152 
153 	return false;
154 }
155 
intel_register_dsm_handler(void)156 void intel_register_dsm_handler(void)
157 {
158 	if (!intel_dsm_detect())
159 		return;
160 }
161 
intel_unregister_dsm_handler(void)162 void intel_unregister_dsm_handler(void)
163 {
164 }
165