nouveau_mxm.c revision 93d9206d08dd18e3aaeed90a3e076b8c323fdd72
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
33static u8 *
34mxms_data(struct drm_device *dev)
35{
36	struct drm_nouveau_private *dev_priv = dev->dev_private;
37	return dev_priv->mxms;
38
39}
40
41static u16
42mxms_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
59static u16
60mxms_headerlen(struct drm_device *dev)
61{
62	return 8;
63}
64
65static u16
66mxms_structlen(struct drm_device *dev)
67{
68	return *(u16 *)&mxms_data(dev)[6];
69}
70
71static bool
72mxms_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
85static bool
86mxms_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
100static bool
101mxms_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
185static u8 *
186mxm_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 */
210static 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
215static 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
220static 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
225static 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
230static 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
235static u8
236mxm_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
268static u8
269mxm_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
289struct mxms_odev {
290	u8 outp_type;
291	u8 conn_type;
292	u8 ddc_port;
293	u8 dig_conn;
294};
295
296static void
297mxms_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
309struct context {
310	u32 *outp;
311	struct mxms_odev desc;
312};
313
314static bool
315mxm_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
327static bool
328mxm_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
371static int
372mxm_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
451static bool
452mxm_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
460static void
461mxm_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
473static bool
474mxm_shadow_rom(struct drm_device *dev, u8 version)
475{
476	return false;
477}
478
479static bool
480mxm_shadow_dsm(struct drm_device *dev, u8 version)
481{
482	return false;
483}
484
485#if defined(CONFIG_ACPI_WMI) || defined(CONFIG_ACPI_WMI_MODULE)
486
487#define WMI_WMMX_GUID "F6CB5C3C-9CAE-4EBD-B577-931EA32A2CC0"
488
489static bool
490mxm_shadow_wmi(struct drm_device *dev, u8 version)
491{
492	struct drm_nouveau_private *dev_priv = dev->dev_private;
493	u32 mxms_args[] = { 0x534D584D /* MXMS */, version, 0 };
494	struct acpi_buffer args = { sizeof(mxms_args), mxms_args };
495	struct acpi_buffer retn = { ACPI_ALLOCATE_BUFFER, NULL };
496	union acpi_object *obj;
497	acpi_status status;
498
499	if (!wmi_has_guid(WMI_WMMX_GUID))
500		return false;
501
502	status = wmi_evaluate_method(WMI_WMMX_GUID, 0, 0, &args, &retn);
503	if (ACPI_FAILURE(status)) {
504		MXM_DBG(dev, "WMMX MXMS returned %d\n", status);
505		return false;
506	}
507
508	obj = retn.pointer;
509	if (obj->type == ACPI_TYPE_BUFFER) {
510		dev_priv->mxms = kmemdup(obj->buffer.pointer,
511					 obj->buffer.length, GFP_KERNEL);
512	}
513
514	kfree(obj);
515	return dev_priv->mxms != NULL;
516}
517#endif
518
519struct mxm_shadow_h {
520	const char *name;
521	bool (*exec)(struct drm_device *, u8 version);
522} _mxm_shadow[] = {
523	{ "ROM", mxm_shadow_rom },
524	{ "DSM", mxm_shadow_dsm },
525#if defined(CONFIG_ACPI_WMI) || defined(CONFIG_ACPI_WMI_MODULE)
526	{ "WMI", mxm_shadow_wmi },
527#endif
528	{}
529};
530
531static int
532mxm_shadow(struct drm_device *dev, u8 version)
533{
534	struct drm_nouveau_private *dev_priv = dev->dev_private;
535	struct mxm_shadow_h *shadow = _mxm_shadow;
536	do {
537		MXM_DBG(dev, "checking %s\n", shadow->name);
538		if (shadow->exec(dev, version)) {
539			if (mxms_valid(dev))
540				return 0;
541			kfree(dev_priv->mxms);
542			dev_priv->mxms = NULL;
543		}
544	} while ((++shadow)->name);
545	return -ENOENT;
546}
547
548int
549nouveau_mxm_init(struct drm_device *dev)
550{
551	u8 mxm_size, *mxm = mxm_table(dev, &mxm_size);
552	if (!mxm || !mxm[0]) {
553		MXM_MSG(dev, "no VBIOS data, nothing to do\n");
554		return 0;
555	}
556
557	MXM_MSG(dev, "BIOS version %d.%d\n", mxm[0] >> 4, mxm[0] & 0x0f);
558
559	if (mxm_shadow(dev, mxm[0])) {
560		MXM_MSG(dev, "failed to locate valid SIS\n");
561		return -EINVAL;
562	}
563
564	MXM_MSG(dev, "MXMS Version %d.%d\n",
565		mxms_version(dev) >> 8, mxms_version(dev) & 0xff);
566	mxms_foreach(dev, 0, NULL, NULL);
567
568	if (nouveau_mxmdcb)
569		mxm_dcb_sanitise(dev);
570	return 0;
571}
572
573void
574nouveau_mxm_fini(struct drm_device *dev)
575{
576	struct drm_nouveau_private *dev_priv = dev->dev_private;
577	kfree(dev_priv->mxms);
578	dev_priv->mxms = NULL;
579}
580