Commit | Line | Data |
---|---|---|
b4c26818 BS |
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 | ||
93d9206d BS |
25 | #include <linux/acpi.h> |
26 | ||
760285e7 | 27 | #include <drm/drmP.h> |
b4c26818 BS |
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 | ||
c37e9905 BS |
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 | ||
b4c26818 | 485 | static bool |
93d9206d | 486 | mxm_shadow_rom(struct drm_device *dev, u8 version) |
b4c26818 | 487 | { |
c37e9905 BS |
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; | |
b4c26818 BS |
515 | return false; |
516 | } | |
517 | ||
3952315b | 518 | #if defined(CONFIG_ACPI) |
b4c26818 | 519 | static bool |
93d9206d | 520 | mxm_shadow_dsm(struct drm_device *dev, u8 version) |
b4c26818 | 521 | { |
3952315b BS |
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; | |
b4c26818 | 578 | } |
3952315b | 579 | #endif |
b4c26818 | 580 | |
93d9206d BS |
581 | #if defined(CONFIG_ACPI_WMI) || defined(CONFIG_ACPI_WMI_MODULE) |
582 | ||
583 | #define WMI_WMMX_GUID "F6CB5C3C-9CAE-4EBD-B577-931EA32A2CC0" | |
584 | ||
84ddfda6 BS |
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 | ||
93d9206d BS |
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 | ||
84ddfda6 BS |
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]) | |
93d9206d BS |
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 | ||
b4c26818 BS |
652 | struct mxm_shadow_h { |
653 | const char *name; | |
93d9206d | 654 | bool (*exec)(struct drm_device *, u8 version); |
b4c26818 BS |
655 | } _mxm_shadow[] = { |
656 | { "ROM", mxm_shadow_rom }, | |
3952315b | 657 | #if defined(CONFIG_ACPI) |
b4c26818 | 658 | { "DSM", mxm_shadow_dsm }, |
3952315b | 659 | #endif |
93d9206d BS |
660 | #if defined(CONFIG_ACPI_WMI) || defined(CONFIG_ACPI_WMI_MODULE) |
661 | { "WMI", mxm_shadow_wmi }, | |
662 | #endif | |
b4c26818 BS |
663 | {} |
664 | }; | |
665 | ||
666 | static int | |
93d9206d | 667 | mxm_shadow(struct drm_device *dev, u8 version) |
b4c26818 BS |
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); | |
93d9206d | 673 | if (shadow->exec(dev, version)) { |
b4c26818 BS |
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 | ||
93d9206d | 694 | if (mxm_shadow(dev, mxm[0])) { |
b4c26818 | 695 | MXM_MSG(dev, "failed to locate valid SIS\n"); |
ce2e7895 BS |
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 | */ | |
b4c26818 | 702 | return -EINVAL; |
ce2e7895 BS |
703 | #else |
704 | return 0; | |
705 | #endif | |
b4c26818 BS |
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 | } |