1c39f472eSBen Skeggs /*
2c39f472eSBen Skeggs * Copyright 2012 Red Hat Inc.
3c39f472eSBen Skeggs *
4c39f472eSBen Skeggs * Permission is hereby granted, free of charge, to any person obtaining a
5c39f472eSBen Skeggs * copy of this software and associated documentation files (the "Software"),
6c39f472eSBen Skeggs * to deal in the Software without restriction, including without limitation
7c39f472eSBen Skeggs * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8c39f472eSBen Skeggs * and/or sell copies of the Software, and to permit persons to whom the
9c39f472eSBen Skeggs * Software is furnished to do so, subject to the following conditions:
10c39f472eSBen Skeggs *
11c39f472eSBen Skeggs * The above copyright notice and this permission notice shall be included in
12c39f472eSBen Skeggs * all copies or substantial portions of the Software.
13c39f472eSBen Skeggs *
14c39f472eSBen Skeggs * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15c39f472eSBen Skeggs * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16c39f472eSBen Skeggs * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17c39f472eSBen Skeggs * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18c39f472eSBen Skeggs * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19c39f472eSBen Skeggs * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20c39f472eSBen Skeggs * OTHER DEALINGS IN THE SOFTWARE.
21c39f472eSBen Skeggs *
22c39f472eSBen Skeggs * Authors: Ben Skeggs
23c39f472eSBen Skeggs */
24d390b480SBen Skeggs #include <subdev/bios.h>
25d390b480SBen Skeggs #include <subdev/bios/bit.h>
26d390b480SBen Skeggs #include <subdev/bios/dp.h>
27c39f472eSBen Skeggs
2834508f9dSBen Skeggs u16
nvbios_dp_table(struct nvkm_bios * bios,u8 * ver,u8 * hdr,u8 * cnt,u8 * len)29d390b480SBen Skeggs nvbios_dp_table(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
30c39f472eSBen Skeggs {
31c39f472eSBen Skeggs struct bit_entry d;
32c39f472eSBen Skeggs
33c39f472eSBen Skeggs if (!bit_entry(bios, 'd', &d)) {
34c39f472eSBen Skeggs if (d.version == 1 && d.length >= 2) {
357f5f518fSBen Skeggs u16 data = nvbios_rd16(bios, d.offset);
36c39f472eSBen Skeggs if (data) {
377f5f518fSBen Skeggs *ver = nvbios_rd08(bios, data + 0x00);
38c39f472eSBen Skeggs switch (*ver) {
39f60707a6SBen Skeggs case 0x20:
40c39f472eSBen Skeggs case 0x21:
41c39f472eSBen Skeggs case 0x30:
42c39f472eSBen Skeggs case 0x40:
43c39f472eSBen Skeggs case 0x41:
4479bf9552SBen Skeggs case 0x42:
457f5f518fSBen Skeggs *hdr = nvbios_rd08(bios, data + 0x01);
467f5f518fSBen Skeggs *len = nvbios_rd08(bios, data + 0x02);
477f5f518fSBen Skeggs *cnt = nvbios_rd08(bios, data + 0x03);
48c39f472eSBen Skeggs return data;
49c39f472eSBen Skeggs default:
50c39f472eSBen Skeggs break;
51c39f472eSBen Skeggs }
52c39f472eSBen Skeggs }
53c39f472eSBen Skeggs }
54c39f472eSBen Skeggs }
55c39f472eSBen Skeggs
56c39f472eSBen Skeggs return 0x0000;
57c39f472eSBen Skeggs }
58c39f472eSBen Skeggs
59c39f472eSBen Skeggs static u16
nvbios_dpout_entry(struct nvkm_bios * bios,u8 idx,u8 * ver,u8 * hdr,u8 * cnt,u8 * len)60d390b480SBen Skeggs nvbios_dpout_entry(struct nvkm_bios *bios, u8 idx,
61c39f472eSBen Skeggs u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
62c39f472eSBen Skeggs {
63c39f472eSBen Skeggs u16 data = nvbios_dp_table(bios, ver, hdr, cnt, len);
64c39f472eSBen Skeggs if (data && idx < *cnt) {
657f5f518fSBen Skeggs u16 outp = nvbios_rd16(bios, data + *hdr + idx * *len);
66c39f472eSBen Skeggs switch (*ver * !!outp) {
67f60707a6SBen Skeggs case 0x20:
68c39f472eSBen Skeggs case 0x21:
69c39f472eSBen Skeggs case 0x30:
707f5f518fSBen Skeggs *hdr = nvbios_rd08(bios, data + 0x04);
717f5f518fSBen Skeggs *len = nvbios_rd08(bios, data + 0x05);
727f5f518fSBen Skeggs *cnt = nvbios_rd08(bios, outp + 0x04);
73c39f472eSBen Skeggs break;
74c39f472eSBen Skeggs case 0x40:
75c39f472eSBen Skeggs case 0x41:
7679bf9552SBen Skeggs case 0x42:
777f5f518fSBen Skeggs *hdr = nvbios_rd08(bios, data + 0x04);
78c39f472eSBen Skeggs *cnt = 0;
79c39f472eSBen Skeggs *len = 0;
80c39f472eSBen Skeggs break;
81c39f472eSBen Skeggs default:
82c39f472eSBen Skeggs break;
83c39f472eSBen Skeggs }
84c39f472eSBen Skeggs return outp;
85c39f472eSBen Skeggs }
86c39f472eSBen Skeggs *ver = 0x00;
87c39f472eSBen Skeggs return 0x0000;
88c39f472eSBen Skeggs }
89c39f472eSBen Skeggs
90c39f472eSBen Skeggs u16
nvbios_dpout_parse(struct nvkm_bios * bios,u8 idx,u8 * ver,u8 * hdr,u8 * cnt,u8 * len,struct nvbios_dpout * info)91d390b480SBen Skeggs nvbios_dpout_parse(struct nvkm_bios *bios, u8 idx,
92c39f472eSBen Skeggs u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
93c39f472eSBen Skeggs struct nvbios_dpout *info)
94c39f472eSBen Skeggs {
95c39f472eSBen Skeggs u16 data = nvbios_dpout_entry(bios, idx, ver, hdr, cnt, len);
96c39f472eSBen Skeggs memset(info, 0x00, sizeof(*info));
97c39f472eSBen Skeggs if (data && *ver) {
987f5f518fSBen Skeggs info->type = nvbios_rd16(bios, data + 0x00);
997f5f518fSBen Skeggs info->mask = nvbios_rd16(bios, data + 0x02);
100c39f472eSBen Skeggs switch (*ver) {
101f60707a6SBen Skeggs case 0x20:
102f60707a6SBen Skeggs info->mask |= 0x00c0; /* match any link */
103*f6e7393eSGustavo A. R. Silva fallthrough;
104c39f472eSBen Skeggs case 0x21:
105c39f472eSBen Skeggs case 0x30:
1067f5f518fSBen Skeggs info->flags = nvbios_rd08(bios, data + 0x05);
1077f5f518fSBen Skeggs info->script[0] = nvbios_rd16(bios, data + 0x06);
1087f5f518fSBen Skeggs info->script[1] = nvbios_rd16(bios, data + 0x08);
109f60707a6SBen Skeggs if (*len >= 0x0c)
1107f5f518fSBen Skeggs info->lnkcmp = nvbios_rd16(bios, data + 0x0a);
111c39f472eSBen Skeggs if (*len >= 0x0f) {
1127f5f518fSBen Skeggs info->script[2] = nvbios_rd16(bios, data + 0x0c);
1137f5f518fSBen Skeggs info->script[3] = nvbios_rd16(bios, data + 0x0e);
114c39f472eSBen Skeggs }
115c39f472eSBen Skeggs if (*len >= 0x11)
1167f5f518fSBen Skeggs info->script[4] = nvbios_rd16(bios, data + 0x10);
117c39f472eSBen Skeggs break;
118c39f472eSBen Skeggs case 0x40:
119c39f472eSBen Skeggs case 0x41:
12079bf9552SBen Skeggs case 0x42:
1217f5f518fSBen Skeggs info->flags = nvbios_rd08(bios, data + 0x04);
1227f5f518fSBen Skeggs info->script[0] = nvbios_rd16(bios, data + 0x05);
1237f5f518fSBen Skeggs info->script[1] = nvbios_rd16(bios, data + 0x07);
1247f5f518fSBen Skeggs info->lnkcmp = nvbios_rd16(bios, data + 0x09);
1257f5f518fSBen Skeggs info->script[2] = nvbios_rd16(bios, data + 0x0b);
1267f5f518fSBen Skeggs info->script[3] = nvbios_rd16(bios, data + 0x0d);
1277f5f518fSBen Skeggs info->script[4] = nvbios_rd16(bios, data + 0x0f);
128c39f472eSBen Skeggs break;
129c39f472eSBen Skeggs default:
130c39f472eSBen Skeggs data = 0x0000;
131c39f472eSBen Skeggs break;
132c39f472eSBen Skeggs }
133c39f472eSBen Skeggs }
134c39f472eSBen Skeggs return data;
135c39f472eSBen Skeggs }
136c39f472eSBen Skeggs
137c39f472eSBen Skeggs u16
nvbios_dpout_match(struct nvkm_bios * bios,u16 type,u16 mask,u8 * ver,u8 * hdr,u8 * cnt,u8 * len,struct nvbios_dpout * info)138d390b480SBen Skeggs nvbios_dpout_match(struct nvkm_bios *bios, u16 type, u16 mask,
139c39f472eSBen Skeggs u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
140c39f472eSBen Skeggs struct nvbios_dpout *info)
141c39f472eSBen Skeggs {
142c39f472eSBen Skeggs u16 data, idx = 0;
143c39f472eSBen Skeggs while ((data = nvbios_dpout_parse(bios, idx++, ver, hdr, cnt, len, info)) || *ver) {
144c39f472eSBen Skeggs if (data && info->type == type) {
145c39f472eSBen Skeggs if ((info->mask & mask) == mask)
146c39f472eSBen Skeggs break;
147c39f472eSBen Skeggs }
148c39f472eSBen Skeggs }
149c39f472eSBen Skeggs return data;
150c39f472eSBen Skeggs }
151c39f472eSBen Skeggs
152c39f472eSBen Skeggs static u16
nvbios_dpcfg_entry(struct nvkm_bios * bios,u16 outp,u8 idx,u8 * ver,u8 * hdr,u8 * cnt,u8 * len)153d390b480SBen Skeggs nvbios_dpcfg_entry(struct nvkm_bios *bios, u16 outp, u8 idx,
154c39f472eSBen Skeggs u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
155c39f472eSBen Skeggs {
156c39f472eSBen Skeggs if (*ver >= 0x40) {
157c39f472eSBen Skeggs outp = nvbios_dp_table(bios, ver, hdr, cnt, len);
158c39f472eSBen Skeggs *hdr = *hdr + (*len * * cnt);
1597f5f518fSBen Skeggs *len = nvbios_rd08(bios, outp + 0x06);
1607f5f518fSBen Skeggs *cnt = nvbios_rd08(bios, outp + 0x07) *
1617f5f518fSBen Skeggs nvbios_rd08(bios, outp + 0x05);
162c39f472eSBen Skeggs }
163c39f472eSBen Skeggs
164c39f472eSBen Skeggs if (idx < *cnt)
165c39f472eSBen Skeggs return outp + *hdr + (idx * *len);
166c39f472eSBen Skeggs
167c39f472eSBen Skeggs return 0x0000;
168c39f472eSBen Skeggs }
169c39f472eSBen Skeggs
170c39f472eSBen Skeggs u16
nvbios_dpcfg_parse(struct nvkm_bios * bios,u16 outp,u8 idx,u8 * ver,u8 * hdr,u8 * cnt,u8 * len,struct nvbios_dpcfg * info)171d390b480SBen Skeggs nvbios_dpcfg_parse(struct nvkm_bios *bios, u16 outp, u8 idx,
172c39f472eSBen Skeggs u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
173c39f472eSBen Skeggs struct nvbios_dpcfg *info)
174c39f472eSBen Skeggs {
175c39f472eSBen Skeggs u16 data = nvbios_dpcfg_entry(bios, outp, idx, ver, hdr, cnt, len);
176c39f472eSBen Skeggs memset(info, 0x00, sizeof(*info));
177c39f472eSBen Skeggs if (data) {
178c39f472eSBen Skeggs switch (*ver) {
179f60707a6SBen Skeggs case 0x20:
180c39f472eSBen Skeggs case 0x21:
1817f5f518fSBen Skeggs info->dc = nvbios_rd08(bios, data + 0x02);
1827f5f518fSBen Skeggs info->pe = nvbios_rd08(bios, data + 0x03);
1837f5f518fSBen Skeggs info->tx_pu = nvbios_rd08(bios, data + 0x04);
184c39f472eSBen Skeggs break;
185c39f472eSBen Skeggs case 0x30:
186c39f472eSBen Skeggs case 0x40:
187c39f472eSBen Skeggs case 0x41:
1887f5f518fSBen Skeggs info->pc = nvbios_rd08(bios, data + 0x00);
1897f5f518fSBen Skeggs info->dc = nvbios_rd08(bios, data + 0x01);
1907f5f518fSBen Skeggs info->pe = nvbios_rd08(bios, data + 0x02);
1917f5f518fSBen Skeggs info->tx_pu = nvbios_rd08(bios, data + 0x03);
192c39f472eSBen Skeggs break;
19379bf9552SBen Skeggs case 0x42:
19479bf9552SBen Skeggs info->dc = nvbios_rd08(bios, data + 0x00);
19579bf9552SBen Skeggs info->pe = nvbios_rd08(bios, data + 0x01);
19679bf9552SBen Skeggs info->tx_pu = nvbios_rd08(bios, data + 0x02);
19779bf9552SBen Skeggs break;
198c39f472eSBen Skeggs default:
199c39f472eSBen Skeggs data = 0x0000;
200c39f472eSBen Skeggs break;
201c39f472eSBen Skeggs }
202c39f472eSBen Skeggs }
203c39f472eSBen Skeggs return data;
204c39f472eSBen Skeggs }
205c39f472eSBen Skeggs
206c39f472eSBen Skeggs u16
nvbios_dpcfg_match(struct nvkm_bios * bios,u16 outp,u8 pc,u8 vs,u8 pe,u8 * ver,u8 * hdr,u8 * cnt,u8 * len,struct nvbios_dpcfg * info)207d390b480SBen Skeggs nvbios_dpcfg_match(struct nvkm_bios *bios, u16 outp, u8 pc, u8 vs, u8 pe,
208c39f472eSBen Skeggs u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
209c39f472eSBen Skeggs struct nvbios_dpcfg *info)
210c39f472eSBen Skeggs {
211c39f472eSBen Skeggs u8 idx = 0xff;
212c39f472eSBen Skeggs u16 data;
213c39f472eSBen Skeggs
214c39f472eSBen Skeggs if (*ver >= 0x30) {
215d83d3453SColin Ian King static const u8 vsoff[] = { 0, 4, 7, 9 };
216c39f472eSBen Skeggs idx = (pc * 10) + vsoff[vs] + pe;
21779d48dadSBen Skeggs if (*ver >= 0x40 && *ver <= 0x41 && *hdr >= 0x12)
2187f5f518fSBen Skeggs idx += nvbios_rd08(bios, outp + 0x11) * 40;
21979d48dadSBen Skeggs else
22079d48dadSBen Skeggs if (*ver >= 0x42)
22179d48dadSBen Skeggs idx += nvbios_rd08(bios, outp + 0x11) * 10;
222c39f472eSBen Skeggs } else {
223c39f472eSBen Skeggs while ((data = nvbios_dpcfg_entry(bios, outp, ++idx,
224c39f472eSBen Skeggs ver, hdr, cnt, len))) {
2257f5f518fSBen Skeggs if (nvbios_rd08(bios, data + 0x00) == vs &&
2267f5f518fSBen Skeggs nvbios_rd08(bios, data + 0x01) == pe)
227c39f472eSBen Skeggs break;
228c39f472eSBen Skeggs }
229c39f472eSBen Skeggs }
230c39f472eSBen Skeggs
231c39f472eSBen Skeggs return nvbios_dpcfg_parse(bios, outp, idx, ver, hdr, cnt, len, info);
232c39f472eSBen Skeggs }
233