xref: /openbmc/linux/drivers/gpu/drm/nouveau/nvkm/subdev/bios/dp.c (revision 4b4193256c8d3bc3a5397b5cd9494c2ad386317d)
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