diff options
author | Ben Skeggs <bskeggs@redhat.com> | 2011-10-12 02:36:42 -0400 |
---|---|---|
committer | Ben Skeggs <bskeggs@redhat.com> | 2011-12-21 04:01:41 -0500 |
commit | b4c26818aeb4159dd537eff6453ae5ebf7a69723 (patch) | |
tree | c88d961c30eb46da840781f5c1173c4cdcf6b8f3 /drivers/gpu/drm/nouveau/nouveau_mxm.c | |
parent | befb51e9c97e783c86a1da27bdda3a638d2f02c7 (diff) |
drm/nouveau/mxm: initial implementation of dcb sanitisation
The DCB table provided by the VBIOS on most MXM chips has a number of
entries which either need to be disabled, or modified according to the
MXM-SIS Output Device Descriptors.
The x86 vbios code usually takes care of this for us, however, with the
large number of laptops now with switchable graphics or optimus, a lot
of the time nouveau is responsible for POSTing the card instead - leaving
some fun situations like, plugging in a monitor and having nouveau decide
3 connectors actually just got plugged in..
No MXM-SIS fetching methods implemented yet.
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
Diffstat (limited to 'drivers/gpu/drm/nouveau/nouveau_mxm.c')
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_mxm.c | 540 |
1 files changed, 540 insertions, 0 deletions
diff --git a/drivers/gpu/drm/nouveau/nouveau_mxm.c b/drivers/gpu/drm/nouveau/nouveau_mxm.c new file mode 100644 index 000000000000..ae9a5ac750fd --- /dev/null +++ b/drivers/gpu/drm/nouveau/nouveau_mxm.c | |||
@@ -0,0 +1,540 @@ | |||
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 "drmP.h" | ||
26 | #include "nouveau_drv.h" | ||
27 | |||
28 | #define MXM_DBG(dev, fmt, args...) NV_DEBUG((dev), "MXM: " fmt, ##args) | ||
29 | #define MXM_MSG(dev, fmt, args...) NV_INFO((dev), "MXM: " fmt, ##args) | ||
30 | |||
31 | static u8 * | ||
32 | mxms_data(struct drm_device *dev) | ||
33 | { | ||
34 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
35 | return dev_priv->mxms; | ||
36 | |||
37 | } | ||
38 | |||
39 | static u16 | ||
40 | mxms_version(struct drm_device *dev) | ||
41 | { | ||
42 | u8 *mxms = mxms_data(dev); | ||
43 | u16 version = (mxms[4] << 8) | mxms[5]; | ||
44 | switch (version ) { | ||
45 | case 0x0200: | ||
46 | case 0x0201: | ||
47 | case 0x0300: | ||
48 | return version; | ||
49 | default: | ||
50 | break; | ||
51 | } | ||
52 | |||
53 | MXM_DBG(dev, "unknown version %d.%d\n", mxms[4], mxms[5]); | ||
54 | return 0x0000; | ||
55 | } | ||
56 | |||
57 | static u16 | ||
58 | mxms_headerlen(struct drm_device *dev) | ||
59 | { | ||
60 | return 8; | ||
61 | } | ||
62 | |||
63 | static u16 | ||
64 | mxms_structlen(struct drm_device *dev) | ||
65 | { | ||
66 | return *(u16 *)&mxms_data(dev)[6]; | ||
67 | } | ||
68 | |||
69 | static bool | ||
70 | mxms_checksum(struct drm_device *dev) | ||
71 | { | ||
72 | u16 size = mxms_headerlen(dev) + mxms_structlen(dev); | ||
73 | u8 *mxms = mxms_data(dev), sum = 0; | ||
74 | while (size--) | ||
75 | sum += *mxms++; | ||
76 | if (sum) { | ||
77 | MXM_DBG(dev, "checksum invalid\n"); | ||
78 | return false; | ||
79 | } | ||
80 | return true; | ||
81 | } | ||
82 | |||
83 | static bool | ||
84 | mxms_valid(struct drm_device *dev) | ||
85 | { | ||
86 | u8 *mxms = mxms_data(dev); | ||
87 | if (*(u32 *)mxms != 0x5f4d584d) { | ||
88 | MXM_DBG(dev, "signature invalid\n"); | ||
89 | return false; | ||
90 | } | ||
91 | |||
92 | if (!mxms_version(dev) || !mxms_checksum(dev)) | ||
93 | return false; | ||
94 | |||
95 | return true; | ||
96 | } | ||
97 | |||
98 | static bool | ||
99 | mxms_foreach(struct drm_device *dev, u8 types, | ||
100 | bool (*exec)(struct drm_device *, u8 *, void *), void *info) | ||
101 | { | ||
102 | u8 *mxms = mxms_data(dev); | ||
103 | u8 *desc = mxms + mxms_headerlen(dev); | ||
104 | u8 *fini = desc + mxms_structlen(dev) - 1; | ||
105 | while (desc < fini) { | ||
106 | u8 type = desc[0] & 0x0f; | ||
107 | u8 headerlen = 0; | ||
108 | u8 recordlen = 0; | ||
109 | u8 entries = 0; | ||
110 | |||
111 | switch (type) { | ||
112 | case 0: /* Output Device Structure */ | ||
113 | if (mxms_version(dev) >= 0x0300) | ||
114 | headerlen = 8; | ||
115 | else | ||
116 | headerlen = 6; | ||
117 | break; | ||
118 | case 1: /* System Cooling Capability Structure */ | ||
119 | case 2: /* Thermal Structure */ | ||
120 | case 3: /* Input Power Structure */ | ||
121 | headerlen = 4; | ||
122 | break; | ||
123 | case 4: /* GPIO Device Structure */ | ||
124 | headerlen = 4; | ||
125 | recordlen = 2; | ||
126 | entries = (ROM32(desc[0]) & 0x01f00000) >> 20; | ||
127 | break; | ||
128 | case 5: /* Vendor Specific Structure */ | ||
129 | headerlen = 8; | ||
130 | break; | ||
131 | case 6: /* Backlight Control Structure */ | ||
132 | if (mxms_version(dev) >= 0x0300) { | ||
133 | headerlen = 4; | ||
134 | recordlen = 8; | ||
135 | entries = (desc[1] & 0xf0) >> 4; | ||
136 | } else { | ||
137 | headerlen = 8; | ||
138 | } | ||
139 | break; | ||
140 | case 7: /* Fan Control Structure */ | ||
141 | headerlen = 8; | ||
142 | recordlen = 4; | ||
143 | entries = desc[1] & 0x07; | ||
144 | break; | ||
145 | default: | ||
146 | MXM_DBG(dev, "unknown descriptor type %d\n", type); | ||
147 | return false; | ||
148 | } | ||
149 | |||
150 | if ((drm_debug & DRM_UT_DRIVER) && (exec == NULL)) { | ||
151 | static const char * mxms_desc_name[] = { | ||
152 | "ODS", "SCCS", "TS", "IPS", | ||
153 | "GSD", "VSS", "BCS", "FCS", | ||
154 | }; | ||
155 | u8 *dump = desc; | ||
156 | int i, j; | ||
157 | |||
158 | MXM_DBG(dev, "%4s: ", mxms_desc_name[type]); | ||
159 | for (j = headerlen - 1; j >= 0; j--) | ||
160 | printk("%02x", dump[j]); | ||
161 | printk("\n"); | ||
162 | dump += headerlen; | ||
163 | |||
164 | for (i = 0; i < entries; i++, dump += recordlen) { | ||
165 | MXM_DBG(dev, " "); | ||
166 | for (j = recordlen - 1; j >= 0; j--) | ||
167 | printk("%02x", dump[j]); | ||
168 | printk("\n"); | ||
169 | } | ||
170 | } | ||
171 | |||
172 | if (types & (1 << type)) { | ||
173 | if (!exec(dev, desc, info)) | ||
174 | return false; | ||
175 | } | ||
176 | |||
177 | desc += headerlen + (entries * recordlen); | ||
178 | } | ||
179 | |||
180 | return true; | ||
181 | } | ||
182 | |||
183 | static u8 * | ||
184 | mxm_table(struct drm_device *dev, u8 *size) | ||
185 | { | ||
186 | struct bit_entry x; | ||
187 | |||
188 | if (bit_table(dev, 'x', &x)) { | ||
189 | MXM_DBG(dev, "BIT 'x' table not present\n"); | ||
190 | return NULL; | ||
191 | } | ||
192 | |||
193 | if (x.version != 1 || x.length < 3) { | ||
194 | MXM_MSG(dev, "BIT x table %d/%d unknown\n", | ||
195 | x.version, x.length); | ||
196 | return NULL; | ||
197 | } | ||
198 | |||
199 | *size = x.length; | ||
200 | return x.data; | ||
201 | } | ||
202 | |||
203 | /* These map MXM v2.x digital connection values to the appropriate SOR/link, | ||
204 | * hopefully they're correct for all boards within the same chipset... | ||
205 | * | ||
206 | * MXM v3.x VBIOS are nicer and provide pointers to these tables. | ||
207 | */ | ||
208 | static u8 nv84_sor_map[16] = { | ||
209 | 0x00, 0x12, 0x22, 0x11, 0x32, 0x31, 0x11, 0x31, | ||
210 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 | ||
211 | }; | ||
212 | |||
213 | static u8 nv92_sor_map[16] = { | ||
214 | 0x00, 0x12, 0x22, 0x11, 0x32, 0x31, 0x11, 0x31, | ||
215 | 0x11, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 | ||
216 | }; | ||
217 | |||
218 | static u8 nv94_sor_map[16] = { | ||
219 | 0x00, 0x14, 0x24, 0x11, 0x34, 0x31, 0x11, 0x31, | ||
220 | 0x11, 0x31, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00 | ||
221 | }; | ||
222 | |||
223 | static u8 nv96_sor_map[16] = { | ||
224 | 0x00, 0x14, 0x24, 0x00, 0x34, 0x00, 0x11, 0x31, | ||
225 | 0x11, 0x31, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00 | ||
226 | }; | ||
227 | |||
228 | static u8 nv98_sor_map[16] = { | ||
229 | 0x00, 0x14, 0x12, 0x11, 0x00, 0x31, 0x11, 0x31, | ||
230 | 0x11, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 | ||
231 | }; | ||
232 | |||
233 | static u8 | ||
234 | mxm_sor_map(struct drm_device *dev, u8 conn) | ||
235 | { | ||
236 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
237 | u8 len, *mxm = mxm_table(dev, &len); | ||
238 | if (mxm && len >= 6) { | ||
239 | u8 *map = ROMPTR(dev, mxm[4]); | ||
240 | if (map) { | ||
241 | if (map[0] == 0x10) { | ||
242 | if (conn < map[3]) | ||
243 | return map[map[1] + conn]; | ||
244 | return 0x00; | ||
245 | } | ||
246 | |||
247 | MXM_MSG(dev, "unknown sor map 0x%02x\n", map[0]); | ||
248 | } | ||
249 | } | ||
250 | |||
251 | if (dev_priv->chipset == 0x84 || dev_priv->chipset == 0x86) | ||
252 | return nv84_sor_map[conn]; | ||
253 | if (dev_priv->chipset == 0x92) | ||
254 | return nv92_sor_map[conn]; | ||
255 | if (dev_priv->chipset == 0x94) | ||
256 | return nv94_sor_map[conn]; | ||
257 | if (dev_priv->chipset == 0x96) | ||
258 | return nv96_sor_map[conn]; | ||
259 | if (dev_priv->chipset == 0x98) | ||
260 | return nv98_sor_map[conn]; | ||
261 | |||
262 | MXM_MSG(dev, "missing sor map\n"); | ||
263 | return 0x00; | ||
264 | } | ||
265 | |||
266 | static u8 | ||
267 | mxm_ddc_map(struct drm_device *dev, u8 port) | ||
268 | { | ||
269 | u8 len, *mxm = mxm_table(dev, &len); | ||
270 | if (mxm && len >= 8) { | ||
271 | u8 *map = ROMPTR(dev, mxm[6]); | ||
272 | if (map) { | ||
273 | if (map[0] == 0x10) { | ||
274 | if (port < map[3]) | ||
275 | return map[map[1] + port]; | ||
276 | return 0x00; | ||
277 | } | ||
278 | |||
279 | MXM_MSG(dev, "unknown ddc map 0x%02x\n", map[0]); | ||
280 | } | ||
281 | } | ||
282 | |||
283 | /* v2.x: directly write port as dcb i2cidx */ | ||
284 | return (port << 4) | port; | ||
285 | } | ||
286 | |||
287 | struct mxms_odev { | ||
288 | u8 outp_type; | ||
289 | u8 conn_type; | ||
290 | u8 ddc_port; | ||
291 | u8 dig_conn; | ||
292 | }; | ||
293 | |||
294 | static void | ||
295 | mxms_output_device(struct drm_device *dev, u8 *pdata, struct mxms_odev *desc) | ||
296 | { | ||
297 | u64 data = ROM32(pdata[0]); | ||
298 | if (mxms_version(dev) >= 0x0300) | ||
299 | data |= (u64)ROM16(pdata[4]) << 32; | ||
300 | |||
301 | desc->outp_type = (data & 0x00000000000000f0ULL) >> 4; | ||
302 | desc->ddc_port = (data & 0x0000000000000f00ULL) >> 8; | ||
303 | desc->conn_type = (data & 0x000000000001f000ULL) >> 12; | ||
304 | desc->dig_conn = (data & 0x0000000000780000ULL) >> 19; | ||
305 | } | ||
306 | |||
307 | struct context { | ||
308 | u32 *outp; | ||
309 | struct mxms_odev desc; | ||
310 | }; | ||
311 | |||
312 | static bool | ||
313 | mxm_match_tmds_partner(struct drm_device *dev, u8 *data, void *info) | ||
314 | { | ||
315 | struct context *ctx = info; | ||
316 | struct mxms_odev desc; | ||
317 | |||
318 | mxms_output_device(dev, data, &desc); | ||
319 | if (desc.outp_type == 2 && | ||
320 | desc.dig_conn == ctx->desc.dig_conn) | ||
321 | return false; | ||
322 | return true; | ||
323 | } | ||
324 | |||
325 | static bool | ||
326 | mxm_match_dcb(struct drm_device *dev, u8 *data, void *info) | ||
327 | { | ||
328 | struct context *ctx = info; | ||
329 | u64 desc = *(u64 *)data; | ||
330 | |||
331 | mxms_output_device(dev, data, &ctx->desc); | ||
332 | |||
333 | /* match dcb encoder type to mxm-ods device type */ | ||
334 | if ((ctx->outp[0] & 0x0000000f) != ctx->desc.outp_type) | ||
335 | return true; | ||
336 | |||
337 | /* digital output, have some extra stuff to match here, there's a | ||
338 | * table in the vbios that provides a mapping from the mxm digital | ||
339 | * connection enum values to SOR/link | ||
340 | */ | ||
341 | if ((desc & 0x00000000000000f0) >= 0x20) { | ||
342 | /* check against sor index */ | ||
343 | u8 link = mxm_sor_map(dev, ctx->desc.dig_conn); | ||
344 | if ((ctx->outp[0] & 0x0f000000) != (link & 0x0f) << 24) | ||
345 | return true; | ||
346 | |||
347 | /* check dcb entry has a compatible link field */ | ||
348 | link = (link & 0x30) >> 4; | ||
349 | if ((link & ((ctx->outp[1] & 0x00000030) >> 4)) != link) | ||
350 | return true; | ||
351 | } | ||
352 | |||
353 | /* mark this descriptor accounted for by setting invalid device type, | ||
354 | * except of course some manufactures don't follow specs properly and | ||
355 | * we need to avoid killing off the TMDS function on DP connectors | ||
356 | * if MXM-SIS is missing an entry for it. | ||
357 | */ | ||
358 | data[0] &= ~0xf0; | ||
359 | if (ctx->desc.outp_type == 6 && ctx->desc.conn_type == 6 && | ||
360 | mxms_foreach(dev, 0x01, mxm_match_tmds_partner, ctx)) { | ||
361 | data[0] |= 0x20; /* modify descriptor to match TMDS now */ | ||
362 | } else { | ||
363 | data[0] |= 0xf0; | ||
364 | } | ||
365 | |||
366 | return false; | ||
367 | } | ||
368 | |||
369 | static int | ||
370 | mxm_dcb_sanitise_entry(struct drm_device *dev, void *data, int idx, u8 *dcbe) | ||
371 | { | ||
372 | struct context ctx = { .outp = (u32 *)dcbe }; | ||
373 | u8 type, i2cidx, link; | ||
374 | u8 *conn; | ||
375 | |||
376 | /* look for an output device structure that matches this dcb entry. | ||
377 | * if one isn't found, disable it. | ||
378 | */ | ||
379 | if (mxms_foreach(dev, 0x01, mxm_match_dcb, &ctx)) { | ||
380 | MXM_DBG(dev, "disable %d: 0x%08x 0x%08x\n", | ||
381 | idx, ctx.outp[0], ctx.outp[1]); | ||
382 | ctx.outp[0] |= 0x0000000f; | ||
383 | return 0; | ||
384 | } | ||
385 | |||
386 | /* modify the output's ddc/aux port, there's a pointer to a table | ||
387 | * with the mapping from mxm ddc/aux port to dcb i2c_index in the | ||
388 | * vbios mxm table | ||
389 | */ | ||
390 | i2cidx = mxm_ddc_map(dev, ctx.desc.ddc_port); | ||
391 | if ((ctx.outp[0] & 0x0000000f) != OUTPUT_DP) | ||
392 | i2cidx = (i2cidx & 0x0f) << 4; | ||
393 | else | ||
394 | i2cidx = (i2cidx & 0xf0); | ||
395 | |||
396 | if (i2cidx != 0xf0) { | ||
397 | ctx.outp[0] &= ~0x000000f0; | ||
398 | ctx.outp[0] |= i2cidx; | ||
399 | } | ||
400 | |||
401 | /* override dcb sorconf.link, based on what mxm data says */ | ||
402 | switch (ctx.desc.outp_type) { | ||
403 | case 0x00: /* Analog CRT */ | ||
404 | case 0x01: /* Analog TV/HDTV */ | ||
405 | break; | ||
406 | default: | ||
407 | link = mxm_sor_map(dev, ctx.desc.dig_conn) & 0x30; | ||
408 | ctx.outp[1] &= ~0x00000030; | ||
409 | ctx.outp[1] |= link; | ||
410 | break; | ||
411 | } | ||
412 | |||
413 | /* we may need to fixup various other vbios tables based on what | ||
414 | * the descriptor says the connector type should be. | ||
415 | * | ||
416 | * in a lot of cases, the vbios tables will claim DVI-I is possible, | ||
417 | * and the mxm data says the connector is really HDMI. another | ||
418 | * common example is DP->eDP. | ||
419 | */ | ||
420 | conn = dcb_conn(dev, (ctx.outp[0] & 0x0000f000) >> 12); | ||
421 | type = conn[0]; | ||
422 | switch (ctx.desc.conn_type) { | ||
423 | case 0x01: /* LVDS */ | ||
424 | ctx.outp[1] |= 0x00000004; /* use_power_scripts */ | ||
425 | /* XXX: modify default link width in LVDS table */ | ||
426 | break; | ||
427 | case 0x02: /* HDMI */ | ||
428 | type = DCB_CONNECTOR_HDMI_1; | ||
429 | break; | ||
430 | case 0x03: /* DVI-D */ | ||
431 | type = DCB_CONNECTOR_DVI_D; | ||
432 | break; | ||
433 | case 0x0e: /* eDP, falls through to DPint */ | ||
434 | ctx.outp[1] |= 0x00010000; | ||
435 | case 0x07: /* DP internal, wtf is this?? HP8670w */ | ||
436 | ctx.outp[1] |= 0x00000004; /* use_power_scripts? */ | ||
437 | type = DCB_CONNECTOR_eDP; | ||
438 | break; | ||
439 | default: | ||
440 | break; | ||
441 | } | ||
442 | |||
443 | if (mxms_version(dev) >= 0x0300) | ||
444 | conn[0] = type; | ||
445 | |||
446 | return 0; | ||
447 | } | ||
448 | |||
449 | static bool | ||
450 | mxm_show_unmatched(struct drm_device *dev, u8 *data, void *info) | ||
451 | { | ||
452 | u64 desc = *(u64 *)data; | ||
453 | if ((desc & 0xf0) != 0xf0) | ||
454 | MXM_MSG(dev, "unmatched output device 0x%016llx\n", desc); | ||
455 | return true; | ||
456 | } | ||
457 | |||
458 | static void | ||
459 | mxm_dcb_sanitise(struct drm_device *dev) | ||
460 | { | ||
461 | u8 *dcb = dcb_table(dev); | ||
462 | if (!dcb || dcb[0] != 0x40) { | ||
463 | MXM_DBG(dev, "unsupported DCB version\n"); | ||
464 | return; | ||
465 | } | ||
466 | |||
467 | dcb_outp_foreach(dev, NULL, mxm_dcb_sanitise_entry); | ||
468 | mxms_foreach(dev, 0x01, mxm_show_unmatched, NULL); | ||
469 | } | ||
470 | |||
471 | static bool | ||
472 | mxm_shadow_rom(struct drm_device *dev) | ||
473 | { | ||
474 | return false; | ||
475 | } | ||
476 | |||
477 | static bool | ||
478 | mxm_shadow_dsm(struct drm_device *dev) | ||
479 | { | ||
480 | return false; | ||
481 | } | ||
482 | |||
483 | struct mxm_shadow_h { | ||
484 | const char *name; | ||
485 | bool (*exec)(struct drm_device *); | ||
486 | } _mxm_shadow[] = { | ||
487 | { "ROM", mxm_shadow_rom }, | ||
488 | { "DSM", mxm_shadow_dsm }, | ||
489 | {} | ||
490 | }; | ||
491 | |||
492 | static int | ||
493 | mxm_shadow(struct drm_device *dev) | ||
494 | { | ||
495 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
496 | struct mxm_shadow_h *shadow = _mxm_shadow; | ||
497 | do { | ||
498 | MXM_DBG(dev, "checking %s\n", shadow->name); | ||
499 | if (shadow->exec(dev)) { | ||
500 | if (mxms_valid(dev)) | ||
501 | return 0; | ||
502 | kfree(dev_priv->mxms); | ||
503 | dev_priv->mxms = NULL; | ||
504 | } | ||
505 | } while ((++shadow)->name); | ||
506 | return -ENOENT; | ||
507 | } | ||
508 | |||
509 | int | ||
510 | nouveau_mxm_init(struct drm_device *dev) | ||
511 | { | ||
512 | u8 mxm_size, *mxm = mxm_table(dev, &mxm_size); | ||
513 | if (!mxm || !mxm[0]) { | ||
514 | MXM_MSG(dev, "no VBIOS data, nothing to do\n"); | ||
515 | return 0; | ||
516 | } | ||
517 | |||
518 | MXM_MSG(dev, "BIOS version %d.%d\n", mxm[0] >> 4, mxm[0] & 0x0f); | ||
519 | |||
520 | if (mxm_shadow(dev)) { | ||
521 | MXM_MSG(dev, "failed to locate valid SIS\n"); | ||
522 | return -EINVAL; | ||
523 | } | ||
524 | |||
525 | MXM_MSG(dev, "MXMS Version %d.%d\n", | ||
526 | mxms_version(dev) >> 8, mxms_version(dev) & 0xff); | ||
527 | mxms_foreach(dev, 0, NULL, NULL); | ||
528 | |||
529 | if (nouveau_mxmdcb) | ||
530 | mxm_dcb_sanitise(dev); | ||
531 | return 0; | ||
532 | } | ||
533 | |||
534 | void | ||
535 | nouveau_mxm_fini(struct drm_device *dev) | ||
536 | { | ||
537 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
538 | kfree(dev_priv->mxms); | ||
539 | dev_priv->mxms = NULL; | ||
540 | } | ||