1*677dec6eSriastradh /*	$NetBSD: nouveau_nvkm_subdev_bios_disp.c,v 1.3 2021/12/18 23:45:38 riastradh Exp $	*/
2d350ecf5Sriastradh 
3d350ecf5Sriastradh /*
4d350ecf5Sriastradh  * Copyright 2012 Red Hat Inc.
5d350ecf5Sriastradh  *
6d350ecf5Sriastradh  * Permission is hereby granted, free of charge, to any person obtaining a
7d350ecf5Sriastradh  * copy of this software and associated documentation files (the "Software"),
8d350ecf5Sriastradh  * to deal in the Software without restriction, including without limitation
9d350ecf5Sriastradh  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10d350ecf5Sriastradh  * and/or sell copies of the Software, and to permit persons to whom the
11d350ecf5Sriastradh  * Software is furnished to do so, subject to the following conditions:
12d350ecf5Sriastradh  *
13d350ecf5Sriastradh  * The above copyright notice and this permission notice shall be included in
14d350ecf5Sriastradh  * all copies or substantial portions of the Software.
15d350ecf5Sriastradh  *
16d350ecf5Sriastradh  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17d350ecf5Sriastradh  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18d350ecf5Sriastradh  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19d350ecf5Sriastradh  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
20d350ecf5Sriastradh  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21d350ecf5Sriastradh  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22d350ecf5Sriastradh  * OTHER DEALINGS IN THE SOFTWARE.
23d350ecf5Sriastradh  *
24d350ecf5Sriastradh  * Authors: Ben Skeggs
25d350ecf5Sriastradh  */
26d350ecf5Sriastradh #include <sys/cdefs.h>
27*677dec6eSriastradh __KERNEL_RCSID(0, "$NetBSD: nouveau_nvkm_subdev_bios_disp.c,v 1.3 2021/12/18 23:45:38 riastradh Exp $");
28d350ecf5Sriastradh 
29d350ecf5Sriastradh #include <subdev/bios.h>
30d350ecf5Sriastradh #include <subdev/bios/bit.h>
31d350ecf5Sriastradh #include <subdev/bios/disp.h>
32d350ecf5Sriastradh 
33d350ecf5Sriastradh u16
nvbios_disp_table(struct nvkm_bios * bios,u8 * ver,u8 * hdr,u8 * cnt,u8 * len,u8 * sub)34d350ecf5Sriastradh nvbios_disp_table(struct nvkm_bios *bios,
35d350ecf5Sriastradh 		  u8 *ver, u8 *hdr, u8 *cnt, u8 *len, u8 *sub)
36d350ecf5Sriastradh {
37d350ecf5Sriastradh 	struct bit_entry U;
38d350ecf5Sriastradh 
39d350ecf5Sriastradh 	if (!bit_entry(bios, 'U', &U)) {
40d350ecf5Sriastradh 		if (U.version == 1) {
41d350ecf5Sriastradh 			u16 data = nvbios_rd16(bios, U.offset);
42d350ecf5Sriastradh 			if (data) {
43d350ecf5Sriastradh 				*ver = nvbios_rd08(bios, data + 0x00);
44d350ecf5Sriastradh 				switch (*ver) {
45d350ecf5Sriastradh 				case 0x20:
46d350ecf5Sriastradh 				case 0x21:
47d350ecf5Sriastradh 				case 0x22:
48d350ecf5Sriastradh 					*hdr = nvbios_rd08(bios, data + 0x01);
49d350ecf5Sriastradh 					*len = nvbios_rd08(bios, data + 0x02);
50d350ecf5Sriastradh 					*cnt = nvbios_rd08(bios, data + 0x03);
51d350ecf5Sriastradh 					*sub = nvbios_rd08(bios, data + 0x04);
52d350ecf5Sriastradh 					return data;
53d350ecf5Sriastradh 				default:
54d350ecf5Sriastradh 					break;
55d350ecf5Sriastradh 				}
56d350ecf5Sriastradh 			}
57d350ecf5Sriastradh 		}
58d350ecf5Sriastradh 	}
59d350ecf5Sriastradh 
60d350ecf5Sriastradh 	return 0x0000;
61d350ecf5Sriastradh }
62d350ecf5Sriastradh 
63d350ecf5Sriastradh u16
nvbios_disp_entry(struct nvkm_bios * bios,u8 idx,u8 * ver,u8 * len,u8 * sub)64d350ecf5Sriastradh nvbios_disp_entry(struct nvkm_bios *bios, u8 idx, u8 *ver, u8 *len, u8 *sub)
65d350ecf5Sriastradh {
66d350ecf5Sriastradh 	u8  hdr, cnt;
67d350ecf5Sriastradh 	u16 data = nvbios_disp_table(bios, ver, &hdr, &cnt, len, sub);
68d350ecf5Sriastradh 	if (data && idx < cnt)
69d350ecf5Sriastradh 		return data + hdr + (idx * *len);
70d350ecf5Sriastradh 	*ver = 0x00;
71d350ecf5Sriastradh 	return 0x0000;
72d350ecf5Sriastradh }
73d350ecf5Sriastradh 
74d350ecf5Sriastradh u16
nvbios_disp_parse(struct nvkm_bios * bios,u8 idx,u8 * ver,u8 * len,u8 * sub,struct nvbios_disp * info)75d350ecf5Sriastradh nvbios_disp_parse(struct nvkm_bios *bios, u8 idx, u8 *ver, u8 *len, u8 *sub,
76d350ecf5Sriastradh 		  struct nvbios_disp *info)
77d350ecf5Sriastradh {
78d350ecf5Sriastradh 	u16 data = nvbios_disp_entry(bios, idx, ver, len, sub);
79d350ecf5Sriastradh 	if (data && *len >= 2) {
80d350ecf5Sriastradh 		info->data = nvbios_rd16(bios, data + 0);
81d350ecf5Sriastradh 		return data;
82d350ecf5Sriastradh 	}
83d350ecf5Sriastradh 	return 0x0000;
84d350ecf5Sriastradh }
85d350ecf5Sriastradh 
86d350ecf5Sriastradh u16
nvbios_outp_entry(struct nvkm_bios * bios,u8 idx,u8 * ver,u8 * hdr,u8 * cnt,u8 * len)87d350ecf5Sriastradh nvbios_outp_entry(struct nvkm_bios *bios, u8 idx,
88d350ecf5Sriastradh 		  u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
89d350ecf5Sriastradh {
90d350ecf5Sriastradh 	struct nvbios_disp info;
91d350ecf5Sriastradh 	u16 data = nvbios_disp_parse(bios, idx, ver, len, hdr, &info);
92d350ecf5Sriastradh 	if (data) {
93d350ecf5Sriastradh 		*cnt = nvbios_rd08(bios, info.data + 0x05);
94d350ecf5Sriastradh 		*len = 0x06;
95d350ecf5Sriastradh 		data = info.data;
96d350ecf5Sriastradh 	}
97d350ecf5Sriastradh 	return data;
98d350ecf5Sriastradh }
99d350ecf5Sriastradh 
100d350ecf5Sriastradh u16
nvbios_outp_parse(struct nvkm_bios * bios,u8 idx,u8 * ver,u8 * hdr,u8 * cnt,u8 * len,struct nvbios_outp * info)101d350ecf5Sriastradh nvbios_outp_parse(struct nvkm_bios *bios, u8 idx,
102d350ecf5Sriastradh 		  u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_outp *info)
103d350ecf5Sriastradh {
104d350ecf5Sriastradh 	u16 data = nvbios_outp_entry(bios, idx, ver, hdr, cnt, len);
105d350ecf5Sriastradh 	if (data && *hdr >= 0x0a) {
106d350ecf5Sriastradh 		info->type      = nvbios_rd16(bios, data + 0x00);
107d350ecf5Sriastradh 		info->mask      = nvbios_rd32(bios, data + 0x02);
108d350ecf5Sriastradh 		if (*ver <= 0x20) /* match any link */
109d350ecf5Sriastradh 			info->mask |= 0x00c0;
110d350ecf5Sriastradh 		info->script[0] = nvbios_rd16(bios, data + 0x06);
111d350ecf5Sriastradh 		info->script[1] = nvbios_rd16(bios, data + 0x08);
112d350ecf5Sriastradh 		info->script[2] = 0x0000;
113d350ecf5Sriastradh 		if (*hdr >= 0x0c)
114d350ecf5Sriastradh 			info->script[2] = nvbios_rd16(bios, data + 0x0a);
115d350ecf5Sriastradh 		return data;
116d350ecf5Sriastradh 	}
117d350ecf5Sriastradh 	return 0x0000;
118d350ecf5Sriastradh }
119d350ecf5Sriastradh 
120d350ecf5Sriastradh u16
nvbios_outp_match(struct nvkm_bios * bios,u16 type,u16 mask,u8 * ver,u8 * hdr,u8 * cnt,u8 * len,struct nvbios_outp * info)121d350ecf5Sriastradh nvbios_outp_match(struct nvkm_bios *bios, u16 type, u16 mask,
122d350ecf5Sriastradh 		  u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_outp *info)
123d350ecf5Sriastradh {
124d350ecf5Sriastradh 	u16 data, idx = 0;
125d350ecf5Sriastradh 	while ((data = nvbios_outp_parse(bios, idx++, ver, hdr, cnt, len, info)) || *ver) {
126d350ecf5Sriastradh 		if (data && info->type == type) {
127d350ecf5Sriastradh 			if ((info->mask & mask) == mask)
128d350ecf5Sriastradh 				break;
129d350ecf5Sriastradh 		}
130d350ecf5Sriastradh 	}
131d350ecf5Sriastradh 	return data;
132d350ecf5Sriastradh }
133d350ecf5Sriastradh 
134d350ecf5Sriastradh u16
nvbios_ocfg_entry(struct nvkm_bios * bios,u16 outp,u8 idx,u8 * ver,u8 * hdr,u8 * cnt,u8 * len)135d350ecf5Sriastradh nvbios_ocfg_entry(struct nvkm_bios *bios, u16 outp, u8 idx,
136d350ecf5Sriastradh 		  u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
137d350ecf5Sriastradh {
138d350ecf5Sriastradh 	if (idx < *cnt)
139d350ecf5Sriastradh 		return outp + *hdr + (idx * *len);
140d350ecf5Sriastradh 	return 0x0000;
141d350ecf5Sriastradh }
142d350ecf5Sriastradh 
143d350ecf5Sriastradh u16
nvbios_ocfg_parse(struct nvkm_bios * bios,u16 outp,u8 idx,u8 * ver,u8 * hdr,u8 * cnt,u8 * len,struct nvbios_ocfg * info)144d350ecf5Sriastradh nvbios_ocfg_parse(struct nvkm_bios *bios, u16 outp, u8 idx,
145d350ecf5Sriastradh 		  u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_ocfg *info)
146d350ecf5Sriastradh {
147d350ecf5Sriastradh 	u16 data = nvbios_ocfg_entry(bios, outp, idx, ver, hdr, cnt, len);
148d350ecf5Sriastradh 	if (data) {
149*677dec6eSriastradh 		info->proto     = nvbios_rd08(bios, data + 0x00);
150*677dec6eSriastradh 		info->flags     = nvbios_rd16(bios, data + 0x01);
151d350ecf5Sriastradh 		info->clkcmp[0] = nvbios_rd16(bios, data + 0x02);
152d350ecf5Sriastradh 		info->clkcmp[1] = nvbios_rd16(bios, data + 0x04);
153d350ecf5Sriastradh 	}
154d350ecf5Sriastradh 	return data;
155d350ecf5Sriastradh }
156d350ecf5Sriastradh 
157d350ecf5Sriastradh u16
nvbios_ocfg_match(struct nvkm_bios * bios,u16 outp,u8 proto,u8 flags,u8 * ver,u8 * hdr,u8 * cnt,u8 * len,struct nvbios_ocfg * info)158*677dec6eSriastradh nvbios_ocfg_match(struct nvkm_bios *bios, u16 outp, u8 proto, u8 flags,
159d350ecf5Sriastradh 		  u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_ocfg *info)
160d350ecf5Sriastradh {
161d350ecf5Sriastradh 	u16 data, idx = 0;
162d350ecf5Sriastradh 	while ((data = nvbios_ocfg_parse(bios, outp, idx++, ver, hdr, cnt, len, info))) {
163*677dec6eSriastradh 		if ((info->proto == proto || info->proto == 0xff) &&
164*677dec6eSriastradh 		    (info->flags == flags))
165d350ecf5Sriastradh 			break;
166d350ecf5Sriastradh 	}
167d350ecf5Sriastradh 	return data;
168d350ecf5Sriastradh }
169d350ecf5Sriastradh 
170d350ecf5Sriastradh u16
nvbios_oclk_match(struct nvkm_bios * bios,u16 cmp,u32 khz)171d350ecf5Sriastradh nvbios_oclk_match(struct nvkm_bios *bios, u16 cmp, u32 khz)
172d350ecf5Sriastradh {
173d350ecf5Sriastradh 	while (cmp) {
174d350ecf5Sriastradh 		if (khz / 10 >= nvbios_rd16(bios, cmp + 0x00))
175d350ecf5Sriastradh 			return  nvbios_rd16(bios, cmp + 0x02);
176d350ecf5Sriastradh 		cmp += 0x04;
177d350ecf5Sriastradh 	}
178d350ecf5Sriastradh 	return 0x0000;
179d350ecf5Sriastradh }
180