• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2012 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  */
24 
25 
26 #include "subdev/bios.h"
27 #include "subdev/bios/bit.h"
28 #include "subdev/bios/dp.h"
29 
30 static u16
nvbios_dp_table(struct nouveau_bios * bios,u8 * ver,u8 * hdr,u8 * cnt,u8 * len)31 nvbios_dp_table(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
32 {
33 	struct bit_entry d;
34 
35 	if (!bit_entry(bios, 'd', &d)) {
36 		if (d.version == 1 && d.length >= 2) {
37 			u16 data = nv_ro16(bios, d.offset);
38 			if (data) {
39 				*ver = nv_ro08(bios, data + 0x00);
40 				switch (*ver) {
41 				case 0x21:
42 				case 0x30:
43 				case 0x40:
44 					*hdr = nv_ro08(bios, data + 0x01);
45 					*len = nv_ro08(bios, data + 0x02);
46 					*cnt = nv_ro08(bios, data + 0x03);
47 					return data;
48 				default:
49 					break;
50 				}
51 			}
52 		}
53 	}
54 
55 	return 0x0000;
56 }
57 
58 static u16
nvbios_dpout_entry(struct nouveau_bios * bios,u8 idx,u8 * ver,u8 * hdr,u8 * cnt,u8 * len)59 nvbios_dpout_entry(struct nouveau_bios *bios, u8 idx,
60 		   u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
61 {
62 	u16 data = nvbios_dp_table(bios, ver, hdr, cnt, len);
63 	if (data && idx < *cnt) {
64 		u16 outp = nv_ro16(bios, data + *hdr + idx * *len);
65 		switch (*ver * !!outp) {
66 		case 0x21:
67 		case 0x30:
68 			*hdr = nv_ro08(bios, data + 0x04);
69 			*len = nv_ro08(bios, data + 0x05);
70 			*cnt = nv_ro08(bios, outp + 0x04);
71 			break;
72 		case 0x40:
73 			*hdr = nv_ro08(bios, data + 0x04);
74 			*cnt = 0;
75 			*len = 0;
76 			break;
77 		default:
78 			break;
79 		}
80 		return outp;
81 	}
82 	*ver = 0x00;
83 	return 0x0000;
84 }
85 
86 u16
nvbios_dpout_parse(struct nouveau_bios * bios,u8 idx,u8 * ver,u8 * hdr,u8 * cnt,u8 * len,struct nvbios_dpout * info)87 nvbios_dpout_parse(struct nouveau_bios *bios, u8 idx,
88 		   u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
89 		   struct nvbios_dpout *info)
90 {
91 	u16 data = nvbios_dpout_entry(bios, idx, ver, hdr, cnt, len);
92 	memset(info, 0x00, sizeof(*info));
93 	if (data && *ver) {
94 		info->type = nv_ro16(bios, data + 0x00);
95 		info->mask = nv_ro16(bios, data + 0x02);
96 		switch (*ver) {
97 		case 0x21:
98 		case 0x30:
99 			info->flags     = nv_ro08(bios, data + 0x05);
100 			info->script[0] = nv_ro16(bios, data + 0x06);
101 			info->script[1] = nv_ro16(bios, data + 0x08);
102 			info->lnkcmp    = nv_ro16(bios, data + 0x0a);
103 			if (*len >= 0x0f) {
104 				info->script[2] = nv_ro16(bios, data + 0x0c);
105 				info->script[3] = nv_ro16(bios, data + 0x0e);
106 			}
107 			if (*len >= 0x11)
108 				info->script[4] = nv_ro16(bios, data + 0x10);
109 			break;
110 		case 0x40:
111 			info->flags     = nv_ro08(bios, data + 0x04);
112 			info->script[0] = nv_ro16(bios, data + 0x05);
113 			info->script[1] = nv_ro16(bios, data + 0x07);
114 			info->lnkcmp    = nv_ro16(bios, data + 0x09);
115 			info->script[2] = nv_ro16(bios, data + 0x0b);
116 			info->script[3] = nv_ro16(bios, data + 0x0d);
117 			info->script[4] = nv_ro16(bios, data + 0x0f);
118 			break;
119 		default:
120 			data = 0x0000;
121 			break;
122 		}
123 	}
124 	return data;
125 }
126 
127 u16
nvbios_dpout_match(struct nouveau_bios * bios,u16 type,u16 mask,u8 * ver,u8 * hdr,u8 * cnt,u8 * len,struct nvbios_dpout * info)128 nvbios_dpout_match(struct nouveau_bios *bios, u16 type, u16 mask,
129 		   u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
130 		   struct nvbios_dpout *info)
131 {
132 	u16 data, idx = 0;
133 	while ((data = nvbios_dpout_parse(bios, idx++, ver, hdr, cnt, len, info)) || *ver) {
134 		if (data && info->type == type) {
135 			if ((info->mask & mask) == mask)
136 				break;
137 		}
138 	}
139 	return data;
140 }
141 
142 static u16
nvbios_dpcfg_entry(struct nouveau_bios * bios,u16 outp,u8 idx,u8 * ver,u8 * hdr,u8 * cnt,u8 * len)143 nvbios_dpcfg_entry(struct nouveau_bios *bios, u16 outp, u8 idx,
144 		   u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
145 {
146 	if (*ver >= 0x40) {
147 		outp = nvbios_dp_table(bios, ver, hdr, cnt, len);
148 		*hdr = *hdr + (*len * * cnt);
149 		*len = nv_ro08(bios, outp + 0x06);
150 		*cnt = nv_ro08(bios, outp + 0x07);
151 	}
152 
153 	if (idx < *cnt)
154 		return outp + *hdr + (idx * *len);
155 
156 	return 0x0000;
157 }
158 
159 u16
nvbios_dpcfg_parse(struct nouveau_bios * bios,u16 outp,u8 idx,u8 * ver,u8 * hdr,u8 * cnt,u8 * len,struct nvbios_dpcfg * info)160 nvbios_dpcfg_parse(struct nouveau_bios *bios, u16 outp, u8 idx,
161 		   u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
162 		   struct nvbios_dpcfg *info)
163 {
164 	u16 data = nvbios_dpcfg_entry(bios, outp, idx, ver, hdr, cnt, len);
165 	memset(info, 0x00, sizeof(*info));
166 	if (data) {
167 		switch (*ver) {
168 		case 0x21:
169 			info->dc    = nv_ro08(bios, data + 0x02);
170 			info->pe    = nv_ro08(bios, data + 0x03);
171 			info->tx_pu = nv_ro08(bios, data + 0x04);
172 			break;
173 		case 0x30:
174 		case 0x40:
175 			info->pc    = nv_ro08(bios, data + 0x00);
176 			info->dc    = nv_ro08(bios, data + 0x01);
177 			info->pe    = nv_ro08(bios, data + 0x02);
178 			info->tx_pu = nv_ro08(bios, data + 0x03);
179 			break;
180 		default:
181 			data = 0x0000;
182 			break;
183 		}
184 	}
185 	return data;
186 }
187 
188 u16
nvbios_dpcfg_match(struct nouveau_bios * bios,u16 outp,u8 pc,u8 vs,u8 pe,u8 * ver,u8 * hdr,u8 * cnt,u8 * len,struct nvbios_dpcfg * info)189 nvbios_dpcfg_match(struct nouveau_bios *bios, u16 outp, u8 pc, u8 vs, u8 pe,
190 		   u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
191 		   struct nvbios_dpcfg *info)
192 {
193 	u8 idx = 0xff;
194 	u16 data;
195 
196 	if (*ver >= 0x30) {
197 		const u8 vsoff[] = { 0, 4, 7, 9 };
198 		idx = (pc * 10) + vsoff[vs] + pe;
199 	} else {
200 		while ((data = nvbios_dpcfg_entry(bios, outp, ++idx,
201 						  ver, hdr, cnt, len))) {
202 			if (nv_ro08(bios, data + 0x00) == vs &&
203 			    nv_ro08(bios, data + 0x01) == pe)
204 				break;
205 		}
206 	}
207 
208 	return nvbios_dpcfg_parse(bios, outp, idx, ver, hdr, cnt, len, info);
209 }
210