diff options
Diffstat (limited to 'drivers/gpu/drm/nouveau/nouveau_mxm.c')
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_mxm.c | 723 |
1 files changed, 0 insertions, 723 deletions
diff --git a/drivers/gpu/drm/nouveau/nouveau_mxm.c b/drivers/gpu/drm/nouveau/nouveau_mxm.c deleted file mode 100644 index 07d0d1e03690..000000000000 --- a/drivers/gpu/drm/nouveau/nouveau_mxm.c +++ /dev/null | |||
@@ -1,723 +0,0 @@ | |||
1 | /* | ||
2 | * Copyright 2011 Red Hat Inc. | ||
3 | * | ||
4 | * Permission is hereby granted, free of charge, to any person obtaining a | ||
5 | * copy of this software and associated documentation files (the "Software"), | ||
6 | * to deal in the Software without restriction, including without limitation | ||
7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||
8 | * and/or sell copies of the Software, and to permit persons to whom the | ||
9 | * Software is furnished to do so, subject to the following conditions: | ||
10 | * | ||
11 | * The above copyright notice and this permission notice shall be included in | ||
12 | * all copies or substantial portions of the Software. | ||
13 | * | ||
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | ||
17 | * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR | ||
18 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | ||
19 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | ||
20 | * OTHER DEALINGS IN THE SOFTWARE. | ||
21 | * | ||
22 | * Authors: Ben Skeggs | ||
23 | */ | ||
24 | |||
25 | #include <linux/acpi.h> | ||
26 | |||
27 | #include "drmP.h" | ||
28 | #include "nouveau_drv.h" | ||
29 | |||
30 | #define MXM_DBG(dev, fmt, args...) NV_DEBUG((dev), "MXM: " fmt, ##args) | ||
31 | #define MXM_MSG(dev, fmt, args...) NV_INFO((dev), "MXM: " fmt, ##args) | ||
32 | |||
33 | static u8 * | ||
34 | mxms_data(struct drm_device *dev) | ||
35 | { | ||
36 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
37 | return dev_priv->mxms; | ||
38 | |||
39 | } | ||
40 | |||
41 | static u16 | ||
42 | mxms_version(struct drm_device *dev) | ||
43 | { | ||
44 | u8 *mxms = mxms_data(dev); | ||
45 | u16 version = (mxms[4] << 8) | mxms[5]; | ||
46 | switch (version ) { | ||
47 | case 0x0200: | ||
48 | case 0x0201: | ||
49 | case 0x0300: | ||
50 | return version; | ||
51 | default: | ||
52 | break; | ||
53 | } | ||
54 | |||
55 | MXM_DBG(dev, "unknown version %d.%d\n", mxms[4], mxms[5]); | ||
56 | return 0x0000; | ||
57 | } | ||
58 | |||
59 | static u16 | ||
60 | mxms_headerlen(struct drm_device *dev) | ||
61 | { | ||
62 | return 8; | ||
63 | } | ||
64 | |||
65 | static u16 | ||
66 | mxms_structlen(struct drm_device *dev) | ||
67 | { | ||
68 | return *(u16 *)&mxms_data(dev)[6]; | ||
69 | } | ||
70 | |||
71 | static bool | ||
72 | mxms_checksum(struct drm_device *dev) | ||
73 | { | ||
74 | u16 size = mxms_headerlen(dev) + mxms_structlen(dev); | ||
75 | u8 *mxms = mxms_data(dev), sum = 0; | ||
76 | while (size--) | ||
77 | sum += *mxms++; | ||
78 | if (sum) { | ||
79 | MXM_DBG(dev, "checksum invalid\n"); | ||
80 | return false; | ||
81 | } | ||
82 | return true; | ||
83 | } | ||
84 | |||
85 | static bool | ||
86 | mxms_valid(struct drm_device *dev) | ||
87 | { | ||
88 | u8 *mxms = mxms_data(dev); | ||
89 | if (*(u32 *)mxms != 0x5f4d584d) { | ||
90 | MXM_DBG(dev, "signature invalid\n"); | ||
91 | return false; | ||
92 | } | ||
93 | |||
94 | if (!mxms_version(dev) || !mxms_checksum(dev)) | ||
95 | return false; | ||
96 | |||
97 | return true; | ||
98 | } | ||
99 | |||
100 | static bool | ||
101 | mxms_foreach(struct drm_device *dev, u8 types, | ||
102 | bool (*exec)(struct drm_device *, u8 *, void *), void *info) | ||
103 | { | ||
104 | u8 *mxms = mxms_data(dev); | ||
105 | u8 *desc = mxms + mxms_headerlen(dev); | ||
106 | u8 *fini = desc + mxms_structlen(dev) - 1; | ||
107 | while (desc < fini) { | ||
108 | u8 type = desc[0] & 0x0f; | ||
109 | u8 headerlen = 0; | ||
110 | u8 recordlen = 0; | ||
111 | u8 entries = 0; | ||
112 | |||
113 | switch (type) { | ||
114 | case 0: /* Output Device Structure */ | ||
115 | if (mxms_version(dev) >= 0x0300) | ||
116 | headerlen = 8; | ||
117 | else | ||
118 | headerlen = 6; | ||
119 | break; | ||
120 | case 1: /* System Cooling Capability Structure */ | ||
121 | case 2: /* Thermal Structure */ | ||
122 | case 3: /* Input Power Structure */ | ||
123 | headerlen = 4; | ||
124 | break; | ||
125 | case 4: /* GPIO Device Structure */ | ||
126 | headerlen = 4; | ||
127 | recordlen = 2; | ||
128 | entries = (ROM32(desc[0]) & 0x01f00000) >> 20; | ||
129 | break; | ||
130 | case 5: /* Vendor Specific Structure */ | ||
131 | headerlen = 8; | ||
132 | break; | ||
133 | case 6: /* Backlight Control Structure */ | ||
134 | if (mxms_version(dev) >= 0x0300) { | ||
135 | headerlen = 4; | ||
136 | recordlen = 8; | ||
137 | entries = (desc[1] & 0xf0) >> 4; | ||
138 | } else { | ||
139 | headerlen = 8; | ||
140 | } | ||
141 | break; | ||
142 | case 7: /* Fan Control Structure */ | ||
143 | headerlen = 8; | ||
144 | recordlen = 4; | ||
145 | entries = desc[1] & 0x07; | ||
146 | break; | ||
147 | default: | ||
148 | MXM_DBG(dev, "unknown descriptor type %d\n", type); | ||
149 | return false; | ||
150 | } | ||
151 | |||
152 | if ((drm_debug & DRM_UT_DRIVER) && (exec == NULL)) { | ||
153 | static const char * mxms_desc_name[] = { | ||
154 | "ODS", "SCCS", "TS", "IPS", | ||
155 | "GSD", "VSS", "BCS", "FCS", | ||
156 | }; | ||
157 | u8 *dump = desc; | ||
158 | int i, j; | ||
159 | |||
160 | MXM_DBG(dev, "%4s: ", mxms_desc_name[type]); | ||
161 | for (j = headerlen - 1; j >= 0; j--) | ||
162 | printk("%02x", dump[j]); | ||
163 | printk("\n"); | ||
164 | dump += headerlen; | ||
165 | |||
166 | for (i = 0; i < entries; i++, dump += recordlen) { | ||
167 | MXM_DBG(dev, " "); | ||
168 | for (j = recordlen - 1; j >= 0; j--) | ||
169 | printk("%02x", dump[j]); | ||
170 | printk("\n"); | ||
171 | } | ||
172 | } | ||
173 | |||
174 | if (types & (1 << type)) { | ||
175 | if (!exec(dev, desc, info)) | ||
176 | return false; | ||
177 | } | ||
178 | |||
179 | desc += headerlen + (entries * recordlen); | ||
180 | } | ||
181 | |||
182 | return true; | ||
183 | } | ||
184 | |||
185 | static u8 * | ||
186 | mxm_table(struct drm_device *dev, u8 *size) | ||
187 | { | ||
188 | struct bit_entry x; | ||
189 | |||
190 | if (bit_table(dev, 'x', &x)) { | ||
191 | MXM_DBG(dev, "BIT 'x' table not present\n"); | ||
192 | return NULL; | ||
193 | } | ||
194 | |||
195 | if (x.version != 1 || x.length < 3) { | ||
196 | MXM_MSG(dev, "BIT x table %d/%d unknown\n", | ||
197 | x.version, x.length); | ||
198 | return NULL; | ||
199 | } | ||
200 | |||
201 | *size = x.length; | ||
202 | return x.data; | ||
203 | } | ||
204 | |||
205 | /* These map MXM v2.x digital connection values to the appropriate SOR/link, | ||
206 | * hopefully they're correct for all boards within the same chipset... | ||
207 | * | ||
208 | * MXM v3.x VBIOS are nicer and provide pointers to these tables. | ||
209 | */ | ||
210 | static u8 nv84_sor_map[16] = { | ||
211 | 0x00, 0x12, 0x22, 0x11, 0x32, 0x31, 0x11, 0x31, | ||
212 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 | ||
213 | }; | ||
214 | |||
215 | static u8 nv92_sor_map[16] = { | ||
216 | 0x00, 0x12, 0x22, 0x11, 0x32, 0x31, 0x11, 0x31, | ||
217 | 0x11, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 | ||
218 | }; | ||
219 | |||
220 | static u8 nv94_sor_map[16] = { | ||
221 | 0x00, 0x14, 0x24, 0x11, 0x34, 0x31, 0x11, 0x31, | ||
222 | 0x11, 0x31, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00 | ||
223 | }; | ||
224 | |||
225 | static u8 nv96_sor_map[16] = { | ||
226 | 0x00, 0x14, 0x24, 0x00, 0x34, 0x00, 0x11, 0x31, | ||
227 | 0x11, 0x31, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00 | ||
228 | }; | ||
229 | |||
230 | static u8 nv98_sor_map[16] = { | ||
231 | 0x00, 0x14, 0x12, 0x11, 0x00, 0x31, 0x11, 0x31, | ||
232 | 0x11, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 | ||
233 | }; | ||
234 | |||
235 | static u8 | ||
236 | mxm_sor_map(struct drm_device *dev, u8 conn) | ||
237 | { | ||
238 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
239 | u8 len, *mxm = mxm_table(dev, &len); | ||
240 | if (mxm && len >= 6) { | ||
241 | u8 *map = ROMPTR(dev, mxm[4]); | ||
242 | if (map) { | ||
243 | if (map[0] == 0x10) { | ||
244 | if (conn < map[3]) | ||
245 | return map[map[1] + conn]; | ||
246 | return 0x00; | ||
247 | } | ||
248 | |||
249 | MXM_MSG(dev, "unknown sor map 0x%02x\n", map[0]); | ||
250 | } | ||
251 | } | ||
252 | |||
253 | if (dev_priv->chipset == 0x84 || dev_priv->chipset == 0x86) | ||
254 | return nv84_sor_map[conn]; | ||
255 | if (dev_priv->chipset == 0x92) | ||
256 | return nv92_sor_map[conn]; | ||
257 | if (dev_priv->chipset == 0x94) | ||
258 | return nv94_sor_map[conn]; | ||
259 | if (dev_priv->chipset == 0x96) | ||
260 | return nv96_sor_map[conn]; | ||
261 | if (dev_priv->chipset == 0x98) | ||
262 | return nv98_sor_map[conn]; | ||
263 | |||
264 | MXM_MSG(dev, "missing sor map\n"); | ||
265 | return 0x00; | ||
266 | } | ||
267 | |||
268 | static u8 | ||
269 | mxm_ddc_map(struct drm_device *dev, u8 port) | ||
270 | { | ||
271 | u8 len, *mxm = mxm_table(dev, &len); | ||
272 | if (mxm && len >= 8) { | ||
273 | u8 *map = ROMPTR(dev, mxm[6]); | ||
274 | if (map) { | ||
275 | if (map[0] == 0x10) { | ||
276 | if (port < map[3]) | ||
277 | return map[map[1] + port]; | ||
278 | return 0x00; | ||
279 | } | ||
280 | |||
281 | MXM_MSG(dev, "unknown ddc map 0x%02x\n", map[0]); | ||
282 | } | ||
283 | } | ||
284 | |||
285 | /* v2.x: directly write port as dcb i2cidx */ | ||
286 | return (port << 4) | port; | ||
287 | } | ||
288 | |||
289 | struct mxms_odev { | ||
290 | u8 outp_type; | ||
291 | u8 conn_type; | ||
292 | u8 ddc_port; | ||
293 | u8 dig_conn; | ||
294 | }; | ||
295 | |||
296 | static void | ||
297 | mxms_output_device(struct drm_device *dev, u8 *pdata, struct mxms_odev *desc) | ||
298 | { | ||
299 | u64 data = ROM32(pdata[0]); | ||
300 | if (mxms_version(dev) >= 0x0300) | ||
301 | data |= (u64)ROM16(pdata[4]) << 32; | ||
302 | |||
303 | desc->outp_type = (data & 0x00000000000000f0ULL) >> 4; | ||
304 | desc->ddc_port = (data & 0x0000000000000f00ULL) >> 8; | ||
305 | desc->conn_type = (data & 0x000000000001f000ULL) >> 12; | ||
306 | desc->dig_conn = (data & 0x0000000000780000ULL) >> 19; | ||
307 | } | ||
308 | |||
309 | struct context { | ||
310 | u32 *outp; | ||
311 | struct mxms_odev desc; | ||
312 | }; | ||
313 | |||
314 | static bool | ||
315 | mxm_match_tmds_partner(struct drm_device *dev, u8 *data, void *info) | ||
316 | { | ||
317 | struct context *ctx = info; | ||
318 | struct mxms_odev desc; | ||
319 | |||
320 | mxms_output_device(dev, data, &desc); | ||
321 | if (desc.outp_type == 2 && | ||
322 | desc.dig_conn == ctx->desc.dig_conn) | ||
323 | return false; | ||
324 | return true; | ||
325 | } | ||
326 | |||
327 | static bool | ||
328 | mxm_match_dcb(struct drm_device *dev, u8 *data, void *info) | ||
329 | { | ||
330 | struct context *ctx = info; | ||
331 | u64 desc = *(u64 *)data; | ||
332 | |||
333 | mxms_output_device(dev, data, &ctx->desc); | ||
334 | |||
335 | /* match dcb encoder type to mxm-ods device type */ | ||
336 | if ((ctx->outp[0] & 0x0000000f) != ctx->desc.outp_type) | ||
337 | return true; | ||
338 | |||
339 | /* digital output, have some extra stuff to match here, there's a | ||
340 | * table in the vbios that provides a mapping from the mxm digital | ||
341 | * connection enum values to SOR/link | ||
342 | */ | ||
343 | if ((desc & 0x00000000000000f0) >= 0x20) { | ||
344 | /* check against sor index */ | ||
345 | u8 link = mxm_sor_map(dev, ctx->desc.dig_conn); | ||
346 | if ((ctx->outp[0] & 0x0f000000) != (link & 0x0f) << 24) | ||
347 | return true; | ||
348 | |||
349 | /* check dcb entry has a compatible link field */ | ||
350 | link = (link & 0x30) >> 4; | ||
351 | if ((link & ((ctx->outp[1] & 0x00000030) >> 4)) != link) | ||
352 | return true; | ||
353 | } | ||
354 | |||
355 | /* mark this descriptor accounted for by setting invalid device type, | ||
356 | * except of course some manufactures don't follow specs properly and | ||
357 | * we need to avoid killing off the TMDS function on DP connectors | ||
358 | * if MXM-SIS is missing an entry for it. | ||
359 | */ | ||
360 | data[0] &= ~0xf0; | ||
361 | if (ctx->desc.outp_type == 6 && ctx->desc.conn_type == 6 && | ||
362 | mxms_foreach(dev, 0x01, mxm_match_tmds_partner, ctx)) { | ||
363 | data[0] |= 0x20; /* modify descriptor to match TMDS now */ | ||
364 | } else { | ||
365 | data[0] |= 0xf0; | ||
366 | } | ||
367 | |||
368 | return false; | ||
369 | } | ||
370 | |||
371 | static int | ||
372 | mxm_dcb_sanitise_entry(struct drm_device *dev, void *data, int idx, u8 *dcbe) | ||
373 | { | ||
374 | struct context ctx = { .outp = (u32 *)dcbe }; | ||
375 | u8 type, i2cidx, link; | ||
376 | u8 *conn; | ||
377 | |||
378 | /* look for an output device structure that matches this dcb entry. | ||
379 | * if one isn't found, disable it. | ||
380 | */ | ||
381 | if (mxms_foreach(dev, 0x01, mxm_match_dcb, &ctx)) { | ||
382 | MXM_DBG(dev, "disable %d: 0x%08x 0x%08x\n", | ||
383 | idx, ctx.outp[0], ctx.outp[1]); | ||
384 | ctx.outp[0] |= 0x0000000f; | ||
385 | return 0; | ||
386 | } | ||
387 | |||
388 | /* modify the output's ddc/aux port, there's a pointer to a table | ||
389 | * with the mapping from mxm ddc/aux port to dcb i2c_index in the | ||
390 | * vbios mxm table | ||
391 | */ | ||
392 | i2cidx = mxm_ddc_map(dev, ctx.desc.ddc_port); | ||
393 | if ((ctx.outp[0] & 0x0000000f) != OUTPUT_DP) | ||
394 | i2cidx = (i2cidx & 0x0f) << 4; | ||
395 | else | ||
396 | i2cidx = (i2cidx & 0xf0); | ||
397 | |||
398 | if (i2cidx != 0xf0) { | ||
399 | ctx.outp[0] &= ~0x000000f0; | ||
400 | ctx.outp[0] |= i2cidx; | ||
401 | } | ||
402 | |||
403 | /* override dcb sorconf.link, based on what mxm data says */ | ||
404 | switch (ctx.desc.outp_type) { | ||
405 | case 0x00: /* Analog CRT */ | ||
406 | case 0x01: /* Analog TV/HDTV */ | ||
407 | break; | ||
408 | default: | ||
409 | link = mxm_sor_map(dev, ctx.desc.dig_conn) & 0x30; | ||
410 | ctx.outp[1] &= ~0x00000030; | ||
411 | ctx.outp[1] |= link; | ||
412 | break; | ||
413 | } | ||
414 | |||
415 | /* we may need to fixup various other vbios tables based on what | ||
416 | * the descriptor says the connector type should be. | ||
417 | * | ||
418 | * in a lot of cases, the vbios tables will claim DVI-I is possible, | ||
419 | * and the mxm data says the connector is really HDMI. another | ||
420 | * common example is DP->eDP. | ||
421 | */ | ||
422 | conn = dcb_conn(dev, (ctx.outp[0] & 0x0000f000) >> 12); | ||
423 | type = conn[0]; | ||
424 | switch (ctx.desc.conn_type) { | ||
425 | case 0x01: /* LVDS */ | ||
426 | ctx.outp[1] |= 0x00000004; /* use_power_scripts */ | ||
427 | /* XXX: modify default link width in LVDS table */ | ||
428 | break; | ||
429 | case 0x02: /* HDMI */ | ||
430 | type = DCB_CONNECTOR_HDMI_1; | ||
431 | break; | ||
432 | case 0x03: /* DVI-D */ | ||
433 | type = DCB_CONNECTOR_DVI_D; | ||
434 | break; | ||
435 | case 0x0e: /* eDP, falls through to DPint */ | ||
436 | ctx.outp[1] |= 0x00010000; | ||
437 | case 0x07: /* DP internal, wtf is this?? HP8670w */ | ||
438 | ctx.outp[1] |= 0x00000004; /* use_power_scripts? */ | ||
439 | type = DCB_CONNECTOR_eDP; | ||
440 | break; | ||
441 | default: | ||
442 | break; | ||
443 | } | ||
444 | |||
445 | if (mxms_version(dev) >= 0x0300) | ||
446 | conn[0] = type; | ||
447 | |||
448 | return 0; | ||
449 | } | ||
450 | |||
451 | static bool | ||
452 | mxm_show_unmatched(struct drm_device *dev, u8 *data, void *info) | ||
453 | { | ||
454 | u64 desc = *(u64 *)data; | ||
455 | if ((desc & 0xf0) != 0xf0) | ||
456 | MXM_MSG(dev, "unmatched output device 0x%016llx\n", desc); | ||
457 | return true; | ||
458 | } | ||
459 | |||
460 | static void | ||
461 | mxm_dcb_sanitise(struct drm_device *dev) | ||
462 | { | ||
463 | u8 *dcb = dcb_table(dev); | ||
464 | if (!dcb || dcb[0] != 0x40) { | ||
465 | MXM_DBG(dev, "unsupported DCB version\n"); | ||
466 | return; | ||
467 | } | ||
468 | |||
469 | dcb_outp_foreach(dev, NULL, mxm_dcb_sanitise_entry); | ||
470 | mxms_foreach(dev, 0x01, mxm_show_unmatched, NULL); | ||
471 | } | ||
472 | |||
473 | static bool | ||
474 | mxm_shadow_rom_fetch(struct nouveau_i2c_chan *i2c, u8 addr, | ||
475 | u8 offset, u8 size, u8 *data) | ||
476 | { | ||
477 | struct i2c_msg msgs[] = { | ||
478 | { .addr = addr, .flags = 0, .len = 1, .buf = &offset }, | ||
479 | { .addr = addr, .flags = I2C_M_RD, .len = size, .buf = data, }, | ||
480 | }; | ||
481 | |||
482 | return i2c_transfer(&i2c->adapter, msgs, 2) == 2; | ||
483 | } | ||
484 | |||
485 | static bool | ||
486 | mxm_shadow_rom(struct drm_device *dev, u8 version) | ||
487 | { | ||
488 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
489 | struct nouveau_i2c_chan *i2c = NULL; | ||
490 | u8 i2cidx, mxms[6], addr, size; | ||
491 | |||
492 | i2cidx = mxm_ddc_map(dev, 1 /* LVDS_DDC */) & 0x0f; | ||
493 | if (i2cidx < 0x0f) | ||
494 | i2c = nouveau_i2c_find(dev, i2cidx); | ||
495 | if (!i2c) | ||
496 | return false; | ||
497 | |||
498 | addr = 0x54; | ||
499 | if (!mxm_shadow_rom_fetch(i2c, addr, 0, 6, mxms)) { | ||
500 | addr = 0x56; | ||
501 | if (!mxm_shadow_rom_fetch(i2c, addr, 0, 6, mxms)) | ||
502 | return false; | ||
503 | } | ||
504 | |||
505 | dev_priv->mxms = mxms; | ||
506 | size = mxms_headerlen(dev) + mxms_structlen(dev); | ||
507 | dev_priv->mxms = kmalloc(size, GFP_KERNEL); | ||
508 | |||
509 | if (dev_priv->mxms && | ||
510 | mxm_shadow_rom_fetch(i2c, addr, 0, size, dev_priv->mxms)) | ||
511 | return true; | ||
512 | |||
513 | kfree(dev_priv->mxms); | ||
514 | dev_priv->mxms = NULL; | ||
515 | return false; | ||
516 | } | ||
517 | |||
518 | #if defined(CONFIG_ACPI) | ||
519 | static bool | ||
520 | mxm_shadow_dsm(struct drm_device *dev, u8 version) | ||
521 | { | ||
522 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
523 | static char muid[] = { | ||
524 | 0x00, 0xA4, 0x04, 0x40, 0x7D, 0x91, 0xF2, 0x4C, | ||
525 | 0xB8, 0x9C, 0x79, 0xB6, 0x2F, 0xD5, 0x56, 0x65 | ||
526 | }; | ||
527 | u32 mxms_args[] = { 0x00000000 }; | ||
528 | union acpi_object args[4] = { | ||
529 | /* _DSM MUID */ | ||
530 | { .buffer.type = 3, | ||
531 | .buffer.length = sizeof(muid), | ||
532 | .buffer.pointer = muid, | ||
533 | }, | ||
534 | /* spec says this can be zero to mean "highest revision", but | ||
535 | * of course there's at least one bios out there which fails | ||
536 | * unless you pass in exactly the version it supports.. | ||
537 | */ | ||
538 | { .integer.type = ACPI_TYPE_INTEGER, | ||
539 | .integer.value = (version & 0xf0) << 4 | (version & 0x0f), | ||
540 | }, | ||
541 | /* MXMS function */ | ||
542 | { .integer.type = ACPI_TYPE_INTEGER, | ||
543 | .integer.value = 0x00000010, | ||
544 | }, | ||
545 | /* Pointer to MXMS arguments */ | ||
546 | { .buffer.type = ACPI_TYPE_BUFFER, | ||
547 | .buffer.length = sizeof(mxms_args), | ||
548 | .buffer.pointer = (char *)mxms_args, | ||
549 | }, | ||
550 | }; | ||
551 | struct acpi_object_list list = { ARRAY_SIZE(args), args }; | ||
552 | struct acpi_buffer retn = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
553 | union acpi_object *obj; | ||
554 | acpi_handle handle; | ||
555 | int ret; | ||
556 | |||
557 | handle = DEVICE_ACPI_HANDLE(&dev->pdev->dev); | ||
558 | if (!handle) | ||
559 | return false; | ||
560 | |||
561 | ret = acpi_evaluate_object(handle, "_DSM", &list, &retn); | ||
562 | if (ret) { | ||
563 | MXM_DBG(dev, "DSM MXMS failed: %d\n", ret); | ||
564 | return false; | ||
565 | } | ||
566 | |||
567 | obj = retn.pointer; | ||
568 | if (obj->type == ACPI_TYPE_BUFFER) { | ||
569 | dev_priv->mxms = kmemdup(obj->buffer.pointer, | ||
570 | obj->buffer.length, GFP_KERNEL); | ||
571 | } else | ||
572 | if (obj->type == ACPI_TYPE_INTEGER) { | ||
573 | MXM_DBG(dev, "DSM MXMS returned 0x%llx\n", obj->integer.value); | ||
574 | } | ||
575 | |||
576 | kfree(obj); | ||
577 | return dev_priv->mxms != NULL; | ||
578 | } | ||
579 | #endif | ||
580 | |||
581 | #if defined(CONFIG_ACPI_WMI) || defined(CONFIG_ACPI_WMI_MODULE) | ||
582 | |||
583 | #define WMI_WMMX_GUID "F6CB5C3C-9CAE-4EBD-B577-931EA32A2CC0" | ||
584 | |||
585 | static u8 | ||
586 | wmi_wmmx_mxmi(struct drm_device *dev, u8 version) | ||
587 | { | ||
588 | u32 mxmi_args[] = { 0x494D584D /* MXMI */, version, 0 }; | ||
589 | struct acpi_buffer args = { sizeof(mxmi_args), mxmi_args }; | ||
590 | struct acpi_buffer retn = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
591 | union acpi_object *obj; | ||
592 | acpi_status status; | ||
593 | |||
594 | status = wmi_evaluate_method(WMI_WMMX_GUID, 0, 0, &args, &retn); | ||
595 | if (ACPI_FAILURE(status)) { | ||
596 | MXM_DBG(dev, "WMMX MXMI returned %d\n", status); | ||
597 | return 0x00; | ||
598 | } | ||
599 | |||
600 | obj = retn.pointer; | ||
601 | if (obj->type == ACPI_TYPE_INTEGER) { | ||
602 | version = obj->integer.value; | ||
603 | MXM_DBG(dev, "WMMX MXMI version %d.%d\n", | ||
604 | (version >> 4), version & 0x0f); | ||
605 | } else { | ||
606 | version = 0; | ||
607 | MXM_DBG(dev, "WMMX MXMI returned non-integer\n"); | ||
608 | } | ||
609 | |||
610 | kfree(obj); | ||
611 | return version; | ||
612 | } | ||
613 | |||
614 | static bool | ||
615 | mxm_shadow_wmi(struct drm_device *dev, u8 version) | ||
616 | { | ||
617 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
618 | u32 mxms_args[] = { 0x534D584D /* MXMS */, version, 0 }; | ||
619 | struct acpi_buffer args = { sizeof(mxms_args), mxms_args }; | ||
620 | struct acpi_buffer retn = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
621 | union acpi_object *obj; | ||
622 | acpi_status status; | ||
623 | |||
624 | if (!wmi_has_guid(WMI_WMMX_GUID)) { | ||
625 | MXM_DBG(dev, "WMMX GUID not found\n"); | ||
626 | return false; | ||
627 | } | ||
628 | |||
629 | mxms_args[1] = wmi_wmmx_mxmi(dev, 0x00); | ||
630 | if (!mxms_args[1]) | ||
631 | mxms_args[1] = wmi_wmmx_mxmi(dev, version); | ||
632 | if (!mxms_args[1]) | ||
633 | return false; | ||
634 | |||
635 | status = wmi_evaluate_method(WMI_WMMX_GUID, 0, 0, &args, &retn); | ||
636 | if (ACPI_FAILURE(status)) { | ||
637 | MXM_DBG(dev, "WMMX MXMS returned %d\n", status); | ||
638 | return false; | ||
639 | } | ||
640 | |||
641 | obj = retn.pointer; | ||
642 | if (obj->type == ACPI_TYPE_BUFFER) { | ||
643 | dev_priv->mxms = kmemdup(obj->buffer.pointer, | ||
644 | obj->buffer.length, GFP_KERNEL); | ||
645 | } | ||
646 | |||
647 | kfree(obj); | ||
648 | return dev_priv->mxms != NULL; | ||
649 | } | ||
650 | #endif | ||
651 | |||
652 | struct mxm_shadow_h { | ||
653 | const char *name; | ||
654 | bool (*exec)(struct drm_device *, u8 version); | ||
655 | } _mxm_shadow[] = { | ||
656 | { "ROM", mxm_shadow_rom }, | ||
657 | #if defined(CONFIG_ACPI) | ||
658 | { "DSM", mxm_shadow_dsm }, | ||
659 | #endif | ||
660 | #if defined(CONFIG_ACPI_WMI) || defined(CONFIG_ACPI_WMI_MODULE) | ||
661 | { "WMI", mxm_shadow_wmi }, | ||
662 | #endif | ||
663 | {} | ||
664 | }; | ||
665 | |||
666 | static int | ||
667 | mxm_shadow(struct drm_device *dev, u8 version) | ||
668 | { | ||
669 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
670 | struct mxm_shadow_h *shadow = _mxm_shadow; | ||
671 | do { | ||
672 | MXM_DBG(dev, "checking %s\n", shadow->name); | ||
673 | if (shadow->exec(dev, version)) { | ||
674 | if (mxms_valid(dev)) | ||
675 | return 0; | ||
676 | kfree(dev_priv->mxms); | ||
677 | dev_priv->mxms = NULL; | ||
678 | } | ||
679 | } while ((++shadow)->name); | ||
680 | return -ENOENT; | ||
681 | } | ||
682 | |||
683 | int | ||
684 | nouveau_mxm_init(struct drm_device *dev) | ||
685 | { | ||
686 | u8 mxm_size, *mxm = mxm_table(dev, &mxm_size); | ||
687 | if (!mxm || !mxm[0]) { | ||
688 | MXM_MSG(dev, "no VBIOS data, nothing to do\n"); | ||
689 | return 0; | ||
690 | } | ||
691 | |||
692 | MXM_MSG(dev, "BIOS version %d.%d\n", mxm[0] >> 4, mxm[0] & 0x0f); | ||
693 | |||
694 | if (mxm_shadow(dev, mxm[0])) { | ||
695 | MXM_MSG(dev, "failed to locate valid SIS\n"); | ||
696 | #if 0 | ||
697 | /* we should, perhaps, fall back to some kind of limited | ||
698 | * mode here if the x86 vbios hasn't already done the | ||
699 | * work for us (so we prevent loading with completely | ||
700 | * whacked vbios tables). | ||
701 | */ | ||
702 | return -EINVAL; | ||
703 | #else | ||
704 | return 0; | ||
705 | #endif | ||
706 | } | ||
707 | |||
708 | MXM_MSG(dev, "MXMS Version %d.%d\n", | ||
709 | mxms_version(dev) >> 8, mxms_version(dev) & 0xff); | ||
710 | mxms_foreach(dev, 0, NULL, NULL); | ||
711 | |||
712 | if (nouveau_mxmdcb) | ||
713 | mxm_dcb_sanitise(dev); | ||
714 | return 0; | ||
715 | } | ||
716 | |||
717 | void | ||
718 | nouveau_mxm_fini(struct drm_device *dev) | ||
719 | { | ||
720 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
721 | kfree(dev_priv->mxms); | ||
722 | dev_priv->mxms = NULL; | ||
723 | } | ||