Commit | Line | Data |
---|---|---|
330c5988 BS |
1 | /* |
2 | * Copyright 2010 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 | #include "drmP.h" | |
26 | ||
27 | #include "nouveau_drv.h" | |
28 | #include "nouveau_pm.h" | |
29 | ||
0fbb114a FJ |
30 | static void |
31 | legacy_perf_init(struct drm_device *dev) | |
32 | { | |
33 | struct drm_nouveau_private *dev_priv = dev->dev_private; | |
34 | struct nvbios *bios = &dev_priv->vbios; | |
35 | struct nouveau_pm_engine *pm = &dev_priv->engine.pm; | |
36 | char *perf, *entry, *bmp = &bios->data[bios->offset]; | |
37 | int headerlen, use_straps; | |
38 | ||
39 | if (bmp[5] < 0x5 || bmp[6] < 0x14) { | |
40 | NV_DEBUG(dev, "BMP version too old for perf\n"); | |
41 | return; | |
42 | } | |
43 | ||
44 | perf = ROMPTR(bios, bmp[0x73]); | |
45 | if (!perf) { | |
46 | NV_DEBUG(dev, "No memclock table pointer found.\n"); | |
47 | return; | |
48 | } | |
49 | ||
50 | switch (perf[0]) { | |
51 | case 0x12: | |
52 | case 0x14: | |
53 | case 0x18: | |
54 | use_straps = 0; | |
55 | headerlen = 1; | |
56 | break; | |
57 | case 0x01: | |
58 | use_straps = perf[1] & 1; | |
59 | headerlen = (use_straps ? 8 : 2); | |
60 | break; | |
61 | default: | |
62 | NV_WARN(dev, "Unknown memclock table version %x.\n", perf[0]); | |
63 | return; | |
64 | } | |
65 | ||
66 | entry = perf + headerlen; | |
67 | if (use_straps) | |
68 | entry += (nv_rd32(dev, NV_PEXTDEV_BOOT_0) & 0x3c) >> 1; | |
69 | ||
70 | sprintf(pm->perflvl[0].name, "performance_level_0"); | |
71 | pm->perflvl[0].memory = ROM16(entry[0]) * 20; | |
72 | pm->nr_perflvl = 1; | |
73 | } | |
74 | ||
fcfc7688 BS |
75 | static struct nouveau_pm_memtiming * |
76 | nouveau_perf_timing(struct drm_device *dev, struct bit_entry *P, | |
77 | u16 memclk, u8 *entry, u8 recordlen, u8 entries) | |
78 | { | |
79 | struct drm_nouveau_private *dev_priv = dev->dev_private; | |
80 | struct nouveau_pm_engine *pm = &dev_priv->engine.pm; | |
81 | struct nvbios *bios = &dev_priv->vbios; | |
82 | u8 ramcfg; | |
83 | int i; | |
84 | ||
85 | /* perf v2 has a separate "timing map" table, we have to match | |
86 | * the target memory clock to a specific entry, *then* use | |
87 | * ramcfg to select the correct subentry | |
88 | */ | |
89 | if (P->version == 2) { | |
90 | u8 *tmap = ROMPTR(bios, P->data[4]); | |
91 | if (!tmap) { | |
92 | NV_DEBUG(dev, "no timing map pointer\n"); | |
93 | return NULL; | |
94 | } | |
95 | ||
96 | if (tmap[0] != 0x10) { | |
97 | NV_WARN(dev, "timing map 0x%02x unknown\n", tmap[0]); | |
98 | return NULL; | |
99 | } | |
100 | ||
101 | entry = tmap + tmap[1]; | |
102 | recordlen = tmap[2] + (tmap[4] * tmap[3]); | |
103 | for (i = 0; i < tmap[5]; i++, entry += recordlen) { | |
104 | if (memclk >= ROM16(entry[0]) && | |
105 | memclk <= ROM16(entry[2])) | |
106 | break; | |
107 | } | |
108 | ||
109 | if (i == tmap[5]) { | |
110 | NV_WARN(dev, "no match in timing map table\n"); | |
111 | return NULL; | |
112 | } | |
113 | ||
114 | entry += tmap[2]; | |
115 | recordlen = tmap[3]; | |
116 | entries = tmap[4]; | |
117 | } | |
118 | ||
96d1fcf8 BS |
119 | ramcfg = (nv_rd32(dev, NV_PEXTDEV_BOOT_0) & 0x0000003c) >> 2; |
120 | if (bios->ram_restrict_tbl_ptr) | |
121 | ramcfg = bios->data[bios->ram_restrict_tbl_ptr + ramcfg]; | |
122 | ||
fcfc7688 BS |
123 | if (ramcfg >= entries) { |
124 | NV_WARN(dev, "ramcfg strap out of bounds!\n"); | |
125 | return NULL; | |
126 | } | |
127 | ||
128 | entry += ramcfg * recordlen; | |
129 | if (entry[1] >= pm->memtimings.nr_timing) { | |
130 | NV_WARN(dev, "timingset %d does not exist\n", entry[1]); | |
131 | return NULL; | |
132 | } | |
133 | ||
134 | return &pm->memtimings.timing[entry[1]]; | |
135 | } | |
136 | ||
3b5565dd BS |
137 | static void |
138 | nouveau_perf_voltage(struct drm_device *dev, struct bit_entry *P, | |
139 | struct nouveau_pm_level *perflvl) | |
140 | { | |
141 | struct drm_nouveau_private *dev_priv = dev->dev_private; | |
142 | struct nvbios *bios = &dev_priv->vbios; | |
143 | u8 *vmap; | |
144 | int id; | |
145 | ||
146 | id = perflvl->volt_min; | |
147 | perflvl->volt_min = 0; | |
148 | ||
03ce8d9e BS |
149 | /* boards using voltage table version <0x40 store the voltage |
150 | * level directly in the perflvl entry as a multiple of 10mV | |
3b5565dd | 151 | */ |
03ce8d9e | 152 | if (dev_priv->engine.pm.voltage.version < 0x40) { |
3b5565dd BS |
153 | perflvl->volt_min = id * 10000; |
154 | perflvl->volt_max = perflvl->volt_min; | |
155 | return; | |
156 | } | |
157 | ||
03ce8d9e | 158 | /* on newer ones, the perflvl stores an index into yet another |
3b5565dd BS |
159 | * vbios table containing a min/max voltage value for the perflvl |
160 | */ | |
161 | if (P->version != 2 || P->length < 34) { | |
162 | NV_DEBUG(dev, "where's our volt map table ptr? %d %d\n", | |
163 | P->version, P->length); | |
164 | return; | |
165 | } | |
166 | ||
167 | vmap = ROMPTR(bios, P->data[32]); | |
168 | if (!vmap) { | |
169 | NV_DEBUG(dev, "volt map table pointer invalid\n"); | |
170 | return; | |
171 | } | |
172 | ||
173 | if (id < vmap[3]) { | |
174 | vmap += vmap[1] + (vmap[2] * id); | |
175 | perflvl->volt_min = ROM32(vmap[0]); | |
176 | perflvl->volt_max = ROM32(vmap[4]); | |
177 | } | |
178 | } | |
179 | ||
330c5988 BS |
180 | void |
181 | nouveau_perf_init(struct drm_device *dev) | |
182 | { | |
183 | struct drm_nouveau_private *dev_priv = dev->dev_private; | |
184 | struct nouveau_pm_engine *pm = &dev_priv->engine.pm; | |
185 | struct nvbios *bios = &dev_priv->vbios; | |
186 | struct bit_entry P; | |
187 | u8 version, headerlen, recordlen, entries; | |
188 | u8 *perf, *entry; | |
189 | int vid, i; | |
190 | ||
191 | if (bios->type == NVBIOS_BIT) { | |
192 | if (bit_table(dev, 'P', &P)) | |
193 | return; | |
194 | ||
195 | if (P.version != 1 && P.version != 2) { | |
196 | NV_WARN(dev, "unknown perf for BIT P %d\n", P.version); | |
197 | return; | |
198 | } | |
199 | ||
200 | perf = ROMPTR(bios, P.data[0]); | |
201 | version = perf[0]; | |
202 | headerlen = perf[1]; | |
203 | if (version < 0x40) { | |
204 | recordlen = perf[3] + (perf[4] * perf[5]); | |
205 | entries = perf[2]; | |
206 | } else { | |
207 | recordlen = perf[2] + (perf[3] * perf[4]); | |
208 | entries = perf[5]; | |
209 | } | |
210 | } else { | |
2756a4f5 | 211 | if (bios->data[bios->offset + 6] < 0x25) { |
0fbb114a | 212 | legacy_perf_init(dev); |
330c5988 BS |
213 | return; |
214 | } | |
215 | ||
216 | perf = ROMPTR(bios, bios->data[bios->offset + 0x94]); | |
217 | if (!perf) { | |
218 | NV_DEBUG(dev, "perf table pointer invalid\n"); | |
219 | return; | |
220 | } | |
221 | ||
222 | version = perf[1]; | |
223 | headerlen = perf[0]; | |
224 | recordlen = perf[3]; | |
225 | entries = perf[2]; | |
226 | } | |
227 | ||
29055440 EV |
228 | if (entries > NOUVEAU_PM_MAX_LEVEL) { |
229 | NV_DEBUG(dev, "perf table has too many entries - buggy vbios?\n"); | |
230 | entries = NOUVEAU_PM_MAX_LEVEL; | |
231 | } | |
232 | ||
330c5988 BS |
233 | entry = perf + headerlen; |
234 | for (i = 0; i < entries; i++) { | |
235 | struct nouveau_pm_level *perflvl = &pm->perflvl[pm->nr_perflvl]; | |
236 | ||
e614b2e7 MP |
237 | perflvl->timing = NULL; |
238 | ||
330c5988 BS |
239 | if (entry[0] == 0xff) { |
240 | entry += recordlen; | |
241 | continue; | |
242 | } | |
243 | ||
244 | switch (version) { | |
245 | case 0x12: | |
246 | case 0x13: | |
247 | case 0x15: | |
248 | perflvl->fanspeed = entry[55]; | |
c3450239 | 249 | if (recordlen > 56) |
3b5565dd | 250 | perflvl->volt_min = entry[56]; |
07b12669 | 251 | perflvl->core = ROM32(entry[1]) * 10; |
e829d804 | 252 | perflvl->memory = ROM32(entry[5]) * 20; |
330c5988 BS |
253 | break; |
254 | case 0x21: | |
255 | case 0x23: | |
256 | case 0x24: | |
257 | perflvl->fanspeed = entry[4]; | |
3b5565dd | 258 | perflvl->volt_min = entry[5]; |
07b12669 | 259 | perflvl->core = ROM16(entry[6]) * 1000; |
e829d804 FJ |
260 | |
261 | if (dev_priv->chipset == 0x49 || | |
262 | dev_priv->chipset == 0x4b) | |
263 | perflvl->memory = ROM16(entry[11]) * 1000; | |
264 | else | |
265 | perflvl->memory = ROM16(entry[11]) * 2000; | |
266 | ||
330c5988 BS |
267 | break; |
268 | case 0x25: | |
269 | perflvl->fanspeed = entry[4]; | |
3b5565dd | 270 | perflvl->volt_min = entry[5]; |
07b12669 BS |
271 | perflvl->core = ROM16(entry[6]) * 1000; |
272 | perflvl->shader = ROM16(entry[10]) * 1000; | |
273 | perflvl->memory = ROM16(entry[12]) * 1000; | |
330c5988 BS |
274 | break; |
275 | case 0x30: | |
aee582de | 276 | perflvl->memscript = ROM16(entry[2]); |
330c5988 BS |
277 | case 0x35: |
278 | perflvl->fanspeed = entry[6]; | |
3b5565dd | 279 | perflvl->volt_min = entry[7]; |
07b12669 BS |
280 | perflvl->core = ROM16(entry[8]) * 1000; |
281 | perflvl->shader = ROM16(entry[10]) * 1000; | |
282 | perflvl->memory = ROM16(entry[12]) * 1000; | |
330c5988 | 283 | /*XXX: confirm on 0x35 */ |
07b12669 | 284 | perflvl->unk05 = ROM16(entry[16]) * 1000; |
330c5988 BS |
285 | break; |
286 | case 0x40: | |
287 | #define subent(n) entry[perf[2] + ((n) * perf[3])] | |
288 | perflvl->fanspeed = 0; /*XXX*/ | |
3b5565dd | 289 | perflvl->volt_min = entry[2]; |
40f6193b BS |
290 | if (dev_priv->card_type == NV_50) { |
291 | perflvl->core = ROM16(subent(0)) & 0xfff; | |
292 | perflvl->shader = ROM16(subent(1)) & 0xfff; | |
293 | perflvl->memory = ROM16(subent(2)) & 0xfff; | |
4fd2847e BS |
294 | perflvl->vdec = ROM16(subent(3)) & 0xfff; |
295 | perflvl->unka0 = ROM16(subent(4)) & 0xfff; | |
40f6193b BS |
296 | } else { |
297 | perflvl->shader = ROM16(subent(3)) & 0xfff; | |
298 | perflvl->core = perflvl->shader / 2; | |
047d2df5 | 299 | perflvl->unk0a = ROM16(subent(4)) & 0xfff; |
40f6193b BS |
300 | perflvl->memory = ROM16(subent(5)) & 0xfff; |
301 | } | |
302 | ||
303 | perflvl->core *= 1000; | |
304 | perflvl->shader *= 1000; | |
305 | perflvl->memory *= 1000; | |
047d2df5 | 306 | perflvl->unk0a *= 1000; |
4fd2847e BS |
307 | perflvl->vdec *= 1000; |
308 | perflvl->unka0 *= 1000; | |
330c5988 BS |
309 | break; |
310 | } | |
311 | ||
330c5988 | 312 | /* make sure vid is valid */ |
3b5565dd BS |
313 | nouveau_perf_voltage(dev, &P, perflvl); |
314 | if (pm->voltage.supported && perflvl->volt_min) { | |
315 | vid = nouveau_volt_vid_lookup(dev, perflvl->volt_min); | |
330c5988 BS |
316 | if (vid < 0) { |
317 | NV_DEBUG(dev, "drop perflvl %d, bad vid\n", i); | |
318 | entry += recordlen; | |
319 | continue; | |
320 | } | |
321 | } | |
322 | ||
e614b2e7 | 323 | /* get the corresponding memory timings */ |
730673b6 | 324 | if (version > 0x15) { |
fcfc7688 BS |
325 | /* last 3 args are for < 0x40, ignored for >= 0x40 */ |
326 | perflvl->timing = | |
327 | nouveau_perf_timing(dev, &P, | |
328 | perflvl->memory / 1000, | |
329 | entry + perf[3], | |
330 | perf[5], perf[4]); | |
e614b2e7 MP |
331 | } |
332 | ||
330c5988 BS |
333 | snprintf(perflvl->name, sizeof(perflvl->name), |
334 | "performance_level_%d", i); | |
335 | perflvl->id = i; | |
336 | pm->nr_perflvl++; | |
337 | ||
338 | entry += recordlen; | |
339 | } | |
340 | } | |
341 | ||
342 | void | |
343 | nouveau_perf_fini(struct drm_device *dev) | |
344 | { | |
345 | } |