2 * Copyright 2011 Red Hat Inc.
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:
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
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.
26 #include <subdev/bios.h>
27 #include <subdev/bios/conn.h>
28 #include <subdev/bios/dcb.h>
29 #include <subdev/bios/mxm.h>
31 struct nv50_mxm_priv
{
37 struct mxms_odev desc
;
41 mxm_match_tmds_partner(struct nvkm_mxm
*mxm
, u8
*data
, void *info
)
43 struct context
*ctx
= info
;
44 struct mxms_odev desc
;
46 mxms_output_device(mxm
, data
, &desc
);
47 if (desc
.outp_type
== 2 &&
48 desc
.dig_conn
== ctx
->desc
.dig_conn
)
54 mxm_match_dcb(struct nvkm_mxm
*mxm
, u8
*data
, void *info
)
56 struct nvkm_bios
*bios
= nvkm_bios(mxm
);
57 struct context
*ctx
= info
;
58 u64 desc
= *(u64
*)data
;
60 mxms_output_device(mxm
, data
, &ctx
->desc
);
62 /* match dcb encoder type to mxm-ods device type */
63 if ((ctx
->outp
[0] & 0x0000000f) != ctx
->desc
.outp_type
)
66 /* digital output, have some extra stuff to match here, there's a
67 * table in the vbios that provides a mapping from the mxm digital
68 * connection enum values to SOR/link
70 if ((desc
& 0x00000000000000f0) >= 0x20) {
71 /* check against sor index */
72 u8 link
= mxm_sor_map(bios
, ctx
->desc
.dig_conn
);
73 if ((ctx
->outp
[0] & 0x0f000000) != (link
& 0x0f) << 24)
76 /* check dcb entry has a compatible link field */
77 link
= (link
& 0x30) >> 4;
78 if ((link
& ((ctx
->outp
[1] & 0x00000030) >> 4)) != link
)
82 /* mark this descriptor accounted for by setting invalid device type,
83 * except of course some manufactures don't follow specs properly and
84 * we need to avoid killing off the TMDS function on DP connectors
85 * if MXM-SIS is missing an entry for it.
88 if (ctx
->desc
.outp_type
== 6 && ctx
->desc
.conn_type
== 6 &&
89 mxms_foreach(mxm
, 0x01, mxm_match_tmds_partner
, ctx
)) {
90 data
[0] |= 0x20; /* modify descriptor to match TMDS now */
99 mxm_dcb_sanitise_entry(struct nvkm_bios
*bios
, void *data
, int idx
, u16 pdcb
)
101 struct nvkm_mxm
*mxm
= data
;
102 struct context ctx
= { .outp
= (u32
*)(bios
->data
+ pdcb
) };
103 u8 type
, i2cidx
, link
, ver
, len
;
106 /* look for an output device structure that matches this dcb entry.
107 * if one isn't found, disable it.
109 if (mxms_foreach(mxm
, 0x01, mxm_match_dcb
, &ctx
)) {
110 nv_debug(mxm
, "disable %d: 0x%08x 0x%08x\n",
111 idx
, ctx
.outp
[0], ctx
.outp
[1]);
112 ctx
.outp
[0] |= 0x0000000f;
116 /* modify the output's ddc/aux port, there's a pointer to a table
117 * with the mapping from mxm ddc/aux port to dcb i2c_index in the
120 i2cidx
= mxm_ddc_map(bios
, ctx
.desc
.ddc_port
);
121 if ((ctx
.outp
[0] & 0x0000000f) != DCB_OUTPUT_DP
)
122 i2cidx
= (i2cidx
& 0x0f) << 4;
124 i2cidx
= (i2cidx
& 0xf0);
126 if (i2cidx
!= 0xf0) {
127 ctx
.outp
[0] &= ~0x000000f0;
128 ctx
.outp
[0] |= i2cidx
;
131 /* override dcb sorconf.link, based on what mxm data says */
132 switch (ctx
.desc
.outp_type
) {
133 case 0x00: /* Analog CRT */
134 case 0x01: /* Analog TV/HDTV */
137 link
= mxm_sor_map(bios
, ctx
.desc
.dig_conn
) & 0x30;
138 ctx
.outp
[1] &= ~0x00000030;
143 /* we may need to fixup various other vbios tables based on what
144 * the descriptor says the connector type should be.
146 * in a lot of cases, the vbios tables will claim DVI-I is possible,
147 * and the mxm data says the connector is really HDMI. another
148 * common example is DP->eDP.
151 conn
+= nvbios_connEe(bios
, (ctx
.outp
[0] & 0x0000f000) >> 12, &ver
, &len
);
153 switch (ctx
.desc
.conn_type
) {
154 case 0x01: /* LVDS */
155 ctx
.outp
[1] |= 0x00000004; /* use_power_scripts */
156 /* XXX: modify default link width in LVDS table */
158 case 0x02: /* HDMI */
159 type
= DCB_CONNECTOR_HDMI_1
;
161 case 0x03: /* DVI-D */
162 type
= DCB_CONNECTOR_DVI_D
;
164 case 0x0e: /* eDP, falls through to DPint */
165 ctx
.outp
[1] |= 0x00010000;
166 case 0x07: /* DP internal, wtf is this?? HP8670w */
167 ctx
.outp
[1] |= 0x00000004; /* use_power_scripts? */
168 type
= DCB_CONNECTOR_eDP
;
174 if (mxms_version(mxm
) >= 0x0300)
181 mxm_show_unmatched(struct nvkm_mxm
*mxm
, u8
*data
, void *info
)
183 u64 desc
= *(u64
*)data
;
184 if ((desc
& 0xf0) != 0xf0)
185 nv_info(mxm
, "unmatched output device 0x%016llx\n", desc
);
190 mxm_dcb_sanitise(struct nvkm_mxm
*mxm
)
192 struct nvkm_bios
*bios
= nvkm_bios(mxm
);
193 u8 ver
, hdr
, cnt
, len
;
194 u16 dcb
= dcb_table(bios
, &ver
, &hdr
, &cnt
, &len
);
195 if (dcb
== 0x0000 || ver
!= 0x40) {
196 nv_debug(mxm
, "unsupported DCB version\n");
200 dcb_outp_foreach(bios
, mxm
, mxm_dcb_sanitise_entry
);
201 mxms_foreach(mxm
, 0x01, mxm_show_unmatched
, NULL
);
205 nv50_mxm_ctor(struct nvkm_object
*parent
, struct nvkm_object
*engine
,
206 struct nvkm_oclass
*oclass
, void *data
, u32 size
,
207 struct nvkm_object
**pobject
)
209 struct nv50_mxm_priv
*priv
;
212 ret
= nvkm_mxm_create(parent
, engine
, oclass
, &priv
);
213 *pobject
= nv_object(priv
);
217 if (priv
->base
.action
& MXM_SANITISE_DCB
)
218 mxm_dcb_sanitise(&priv
->base
);
224 .handle
= NV_SUBDEV(MXM
, 0x50),
225 .ofuncs
= &(struct nvkm_ofuncs
) {
226 .ctor
= nv50_mxm_ctor
,
227 .dtor
= _nvkm_mxm_dtor
,
228 .init
= _nvkm_mxm_init
,
229 .fini
= _nvkm_mxm_fini
,