1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24#include "mxms.h"
25
26#include <subdev/bios.h>
27#include <subdev/bios/conn.h>
28#include <subdev/bios/dcb.h>
29#include <subdev/bios/mxm.h>
30
31struct context {
32 u32 *outp;
33 struct mxms_odev desc;
34};
35
36static bool
37mxm_match_tmds_partner(struct nvkm_mxm *mxm, u8 *data, void *info)
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
49static bool
50mxm_match_dcb(struct nvkm_mxm *mxm, u8 *data, void *info)
51{
52 struct nvkm_bios *bios = mxm->subdev.device->bios;
53 struct context *ctx = info;
54 u64 desc = *(u64 *)data;
55
56 mxms_output_device(mxm, data, &ctx->desc);
57
58
59 if ((ctx->outp[0] & 0x0000000f) != ctx->desc.outp_type)
60 return true;
61
62
63
64
65
66 if ((desc & 0x00000000000000f0) >= 0x20) {
67
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
73 link = (link & 0x30) >> 4;
74 if ((link & ((ctx->outp[1] & 0x00000030) >> 4)) != link)
75 return true;
76 }
77
78
79
80
81
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;
87 } else {
88 data[0] |= 0xf0;
89 }
90
91 return false;
92}
93
94static int
95mxm_dcb_sanitise_entry(struct nvkm_bios *bios, void *data, int idx, u16 pdcb)
96{
97 struct nvkm_mxm *mxm = data;
98 struct context ctx = { .outp = (u32 *)(bios->data + pdcb) };
99 u8 type, i2cidx, link, ver, len;
100 u8 *conn;
101
102
103
104
105 if (mxms_foreach(mxm, 0x01, mxm_match_dcb, &ctx)) {
106 nvkm_debug(&mxm->subdev, "disable %d: %08x %08x\n",
107 idx, ctx.outp[0], ctx.outp[1]);
108 ctx.outp[0] |= 0x0000000f;
109 return 0;
110 }
111
112
113
114
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
128 switch (ctx.desc.outp_type) {
129 case 0x00:
130 case 0x01:
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
140
141
142
143
144
145
146 conn = bios->data;
147 conn += nvbios_connEe(bios, (ctx.outp[0] & 0x0000f000) >> 12, &ver, &len);
148 type = conn[0];
149 switch (ctx.desc.conn_type) {
150 case 0x01:
151 ctx.outp[1] |= 0x00000004;
152
153 break;
154 case 0x02:
155 type = DCB_CONNECTOR_HDMI_1;
156 break;
157 case 0x03:
158 type = DCB_CONNECTOR_DVI_D;
159 break;
160 case 0x0e:
161 ctx.outp[1] |= 0x00010000;
162
163 case 0x07:
164 ctx.outp[1] |= 0x00000004;
165 type = DCB_CONNECTOR_eDP;
166 break;
167 default:
168 break;
169 }
170
171 if (mxms_version(mxm) >= 0x0300)
172 conn[0] = type;
173
174 return 0;
175}
176
177static bool
178mxm_show_unmatched(struct nvkm_mxm *mxm, u8 *data, void *info)
179{
180 struct nvkm_subdev *subdev = &mxm->subdev;
181 u64 desc = *(u64 *)data;
182 if ((desc & 0xf0) != 0xf0)
183 nvkm_info(subdev, "unmatched output device %016llx\n", desc);
184 return true;
185}
186
187static void
188mxm_dcb_sanitise(struct nvkm_mxm *mxm)
189{
190 struct nvkm_subdev *subdev = &mxm->subdev;
191 struct nvkm_bios *bios = subdev->device->bios;
192 u8 ver, hdr, cnt, len;
193 u16 dcb = dcb_table(bios, &ver, &hdr, &cnt, &len);
194 if (dcb == 0x0000 || (ver != 0x40 && ver != 0x41)) {
195 nvkm_warn(subdev, "unsupported DCB version\n");
196 return;
197 }
198
199 dcb_outp_foreach(bios, mxm, mxm_dcb_sanitise_entry);
200 mxms_foreach(mxm, 0x01, mxm_show_unmatched, NULL);
201}
202
203int
204nv50_mxm_new(struct nvkm_device *device, int index, struct nvkm_subdev **pmxm)
205{
206 struct nvkm_mxm *mxm;
207 int ret;
208
209 ret = nvkm_mxm_new_(device, index, &mxm);
210 if (mxm)
211 *pmxm = &mxm->subdev;
212 if (ret)
213 return ret;
214
215 if (mxm->action & MXM_SANITISE_DCB)
216 mxm_dcb_sanitise(mxm);
217
218 return 0;
219}
220