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