]>
Commit | Line | Data |
---|---|---|
d38ac521 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 | */ | |
be83cd4e | 24 | #include "mxms.h" |
d38ac521 | 25 | |
d38ac521 BS |
26 | #include <subdev/bios.h> |
27 | #include <subdev/bios/conn.h> | |
28 | #include <subdev/bios/dcb.h> | |
29 | #include <subdev/bios/mxm.h> | |
30 | ||
d38ac521 BS |
31 | struct context { |
32 | u32 *outp; | |
33 | struct mxms_odev desc; | |
34 | }; | |
35 | ||
36 | static bool | |
be83cd4e | 37 | mxm_match_tmds_partner(struct nvkm_mxm *mxm, u8 *data, void *info) |
d38ac521 BS |
38 | { |
39 | struct context *ctx = info; | |
40 | struct mxms_odev desc; | |
41 | ||
42 | mxms_output_device(mxm, data, &desc); | |
43 | if (desc.outp_type == 2 && | |
44 | desc.dig_conn == ctx->desc.dig_conn) | |
45 | return false; | |
46 | return true; | |
47 | } | |
48 | ||
49 | static bool | |
be83cd4e | 50 | mxm_match_dcb(struct nvkm_mxm *mxm, u8 *data, void *info) |
d38ac521 | 51 | { |
46484438 | 52 | struct nvkm_bios *bios = mxm->subdev.device->bios; |
d38ac521 BS |
53 | struct context *ctx = info; |
54 | u64 desc = *(u64 *)data; | |
55 | ||
56 | mxms_output_device(mxm, data, &ctx->desc); | |
57 | ||
58 | /* match dcb encoder type to mxm-ods device type */ | |
59 | if ((ctx->outp[0] & 0x0000000f) != ctx->desc.outp_type) | |
60 | return true; | |
61 | ||
62 | /* digital output, have some extra stuff to match here, there's a | |
63 | * table in the vbios that provides a mapping from the mxm digital | |
64 | * connection enum values to SOR/link | |
65 | */ | |
66 | if ((desc & 0x00000000000000f0) >= 0x20) { | |
67 | /* check against sor index */ | |
68 | u8 link = mxm_sor_map(bios, ctx->desc.dig_conn); | |
69 | if ((ctx->outp[0] & 0x0f000000) != (link & 0x0f) << 24) | |
70 | return true; | |
71 | ||
72 | /* check dcb entry has a compatible link field */ | |
73 | link = (link & 0x30) >> 4; | |
74 | if ((link & ((ctx->outp[1] & 0x00000030) >> 4)) != link) | |
75 | return true; | |
76 | } | |
77 | ||
78 | /* mark this descriptor accounted for by setting invalid device type, | |
79 | * except of course some manufactures don't follow specs properly and | |
80 | * we need to avoid killing off the TMDS function on DP connectors | |
81 | * if MXM-SIS is missing an entry for it. | |
82 | */ | |
83 | data[0] &= ~0xf0; | |
84 | if (ctx->desc.outp_type == 6 && ctx->desc.conn_type == 6 && | |
85 | mxms_foreach(mxm, 0x01, mxm_match_tmds_partner, ctx)) { | |
86 | data[0] |= 0x20; /* modify descriptor to match TMDS now */ | |
87 | } else { | |
88 | data[0] |= 0xf0; | |
89 | } | |
90 | ||
91 | return false; | |
92 | } | |
93 | ||
94 | static int | |
be83cd4e | 95 | mxm_dcb_sanitise_entry(struct nvkm_bios *bios, void *data, int idx, u16 pdcb) |
d38ac521 | 96 | { |
be83cd4e | 97 | struct nvkm_mxm *mxm = data; |
d38ac521 BS |
98 | struct context ctx = { .outp = (u32 *)(bios->data + pdcb) }; |
99 | u8 type, i2cidx, link, ver, len; | |
100 | u8 *conn; | |
101 | ||
102 | /* look for an output device structure that matches this dcb entry. | |
103 | * if one isn't found, disable it. | |
104 | */ | |
105 | if (mxms_foreach(mxm, 0x01, mxm_match_dcb, &ctx)) { | |
27cc60a1 BS |
106 | nvkm_debug(&mxm->subdev, "disable %d: %08x %08x\n", |
107 | idx, ctx.outp[0], ctx.outp[1]); | |
d38ac521 BS |
108 | ctx.outp[0] |= 0x0000000f; |
109 | return 0; | |
110 | } | |
111 | ||
112 | /* modify the output's ddc/aux port, there's a pointer to a table | |
113 | * with the mapping from mxm ddc/aux port to dcb i2c_index in the | |
114 | * vbios mxm table | |
115 | */ | |
116 | i2cidx = mxm_ddc_map(bios, ctx.desc.ddc_port); | |
117 | if ((ctx.outp[0] & 0x0000000f) != DCB_OUTPUT_DP) | |
118 | i2cidx = (i2cidx & 0x0f) << 4; | |
119 | else | |
120 | i2cidx = (i2cidx & 0xf0); | |
121 | ||
122 | if (i2cidx != 0xf0) { | |
123 | ctx.outp[0] &= ~0x000000f0; | |
124 | ctx.outp[0] |= i2cidx; | |
125 | } | |
126 | ||
127 | /* override dcb sorconf.link, based on what mxm data says */ | |
128 | switch (ctx.desc.outp_type) { | |
129 | case 0x00: /* Analog CRT */ | |
130 | case 0x01: /* Analog TV/HDTV */ | |
131 | break; | |
132 | default: | |
133 | link = mxm_sor_map(bios, ctx.desc.dig_conn) & 0x30; | |
134 | ctx.outp[1] &= ~0x00000030; | |
135 | ctx.outp[1] |= link; | |
136 | break; | |
137 | } | |
138 | ||
139 | /* we may need to fixup various other vbios tables based on what | |
140 | * the descriptor says the connector type should be. | |
141 | * | |
142 | * in a lot of cases, the vbios tables will claim DVI-I is possible, | |
143 | * and the mxm data says the connector is really HDMI. another | |
144 | * common example is DP->eDP. | |
145 | */ | |
146 | conn = bios->data; | |
20014cbe | 147 | conn += nvbios_connEe(bios, (ctx.outp[0] & 0x0000f000) >> 12, &ver, &len); |
d38ac521 BS |
148 | type = conn[0]; |
149 | switch (ctx.desc.conn_type) { | |
150 | case 0x01: /* LVDS */ | |
151 | ctx.outp[1] |= 0x00000004; /* use_power_scripts */ | |
152 | /* XXX: modify default link width in LVDS table */ | |
153 | break; | |
154 | case 0x02: /* HDMI */ | |
155 | type = DCB_CONNECTOR_HDMI_1; | |
156 | break; | |
157 | case 0x03: /* DVI-D */ | |
158 | type = DCB_CONNECTOR_DVI_D; | |
159 | break; | |
160 | case 0x0e: /* eDP, falls through to DPint */ | |
161 | ctx.outp[1] |= 0x00010000; | |
162 | case 0x07: /* DP internal, wtf is this?? HP8670w */ | |
163 | ctx.outp[1] |= 0x00000004; /* use_power_scripts? */ | |
164 | type = DCB_CONNECTOR_eDP; | |
165 | break; | |
166 | default: | |
167 | break; | |
168 | } | |
169 | ||
170 | if (mxms_version(mxm) >= 0x0300) | |
171 | conn[0] = type; | |
172 | ||
173 | return 0; | |
174 | } | |
175 | ||
176 | static bool | |
be83cd4e | 177 | mxm_show_unmatched(struct nvkm_mxm *mxm, u8 *data, void *info) |
d38ac521 | 178 | { |
27cc60a1 | 179 | struct nvkm_subdev *subdev = &mxm->subdev; |
d38ac521 BS |
180 | u64 desc = *(u64 *)data; |
181 | if ((desc & 0xf0) != 0xf0) | |
27cc60a1 | 182 | nvkm_info(subdev, "unmatched output device %016llx\n", desc); |
d38ac521 BS |
183 | return true; |
184 | } | |
185 | ||
186 | static void | |
be83cd4e | 187 | mxm_dcb_sanitise(struct nvkm_mxm *mxm) |
d38ac521 | 188 | { |
27cc60a1 BS |
189 | struct nvkm_subdev *subdev = &mxm->subdev; |
190 | struct nvkm_bios *bios = subdev->device->bios; | |
d38ac521 BS |
191 | u8 ver, hdr, cnt, len; |
192 | u16 dcb = dcb_table(bios, &ver, &hdr, &cnt, &len); | |
193 | if (dcb == 0x0000 || ver != 0x40) { | |
27cc60a1 | 194 | nvkm_debug(subdev, "unsupported DCB version\n"); |
d38ac521 BS |
195 | return; |
196 | } | |
197 | ||
72de1823 | 198 | dcb_outp_foreach(bios, mxm, mxm_dcb_sanitise_entry); |
d38ac521 BS |
199 | mxms_foreach(mxm, 0x01, mxm_show_unmatched, NULL); |
200 | } | |
201 | ||
202 | static int | |
be83cd4e BS |
203 | nv50_mxm_ctor(struct nvkm_object *parent, struct nvkm_object *engine, |
204 | struct nvkm_oclass *oclass, void *data, u32 size, | |
205 | struct nvkm_object **pobject) | |
d38ac521 | 206 | { |
2d9d5889 | 207 | struct nvkm_mxm *mxm; |
d38ac521 BS |
208 | int ret; |
209 | ||
2d9d5889 BS |
210 | ret = nvkm_mxm_create(parent, engine, oclass, &mxm); |
211 | *pobject = nv_object(mxm); | |
d38ac521 BS |
212 | if (ret) |
213 | return ret; | |
214 | ||
2d9d5889 BS |
215 | if (mxm->action & MXM_SANITISE_DCB) |
216 | mxm_dcb_sanitise(mxm); | |
d38ac521 BS |
217 | return 0; |
218 | } | |
219 | ||
be83cd4e | 220 | struct nvkm_oclass |
d38ac521 BS |
221 | nv50_mxm_oclass = { |
222 | .handle = NV_SUBDEV(MXM, 0x50), | |
be83cd4e | 223 | .ofuncs = &(struct nvkm_ofuncs) { |
d38ac521 | 224 | .ctor = nv50_mxm_ctor, |
be83cd4e BS |
225 | .dtor = _nvkm_mxm_dtor, |
226 | .init = _nvkm_mxm_init, | |
227 | .fini = _nvkm_mxm_fini, | |
d38ac521 BS |
228 | }, |
229 | }; |