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) { | |
0b3b5579 BS |
130 | if (entry[1] != 0xff) |
131 | NV_WARN(dev, "timingset %d does not exist\n", entry[1]); | |
fcfc7688 BS |
132 | return NULL; |
133 | } | |
134 | ||
135 | return &pm->memtimings.timing[entry[1]]; | |
136 | } | |
137 | ||
3b5565dd BS |
138 | static void |
139 | nouveau_perf_voltage(struct drm_device *dev, struct bit_entry *P, | |
140 | struct nouveau_pm_level *perflvl) | |
141 | { | |
142 | struct drm_nouveau_private *dev_priv = dev->dev_private; | |
143 | struct nvbios *bios = &dev_priv->vbios; | |
144 | u8 *vmap; | |
145 | int id; | |
146 | ||
147 | id = perflvl->volt_min; | |
148 | perflvl->volt_min = 0; | |
149 | ||
03ce8d9e BS |
150 | /* boards using voltage table version <0x40 store the voltage |
151 | * level directly in the perflvl entry as a multiple of 10mV | |
3b5565dd | 152 | */ |
03ce8d9e | 153 | if (dev_priv->engine.pm.voltage.version < 0x40) { |
3b5565dd BS |
154 | perflvl->volt_min = id * 10000; |
155 | perflvl->volt_max = perflvl->volt_min; | |
156 | return; | |
157 | } | |
158 | ||
03ce8d9e | 159 | /* on newer ones, the perflvl stores an index into yet another |
3b5565dd BS |
160 | * vbios table containing a min/max voltage value for the perflvl |
161 | */ | |
162 | if (P->version != 2 || P->length < 34) { | |
163 | NV_DEBUG(dev, "where's our volt map table ptr? %d %d\n", | |
164 | P->version, P->length); | |
165 | return; | |
166 | } | |
167 | ||
168 | vmap = ROMPTR(bios, P->data[32]); | |
169 | if (!vmap) { | |
170 | NV_DEBUG(dev, "volt map table pointer invalid\n"); | |
171 | return; | |
172 | } | |
173 | ||
174 | if (id < vmap[3]) { | |
175 | vmap += vmap[1] + (vmap[2] * id); | |
176 | perflvl->volt_min = ROM32(vmap[0]); | |
177 | perflvl->volt_max = ROM32(vmap[4]); | |
178 | } | |
179 | } | |
180 | ||
330c5988 BS |
181 | void |
182 | nouveau_perf_init(struct drm_device *dev) | |
183 | { | |
184 | struct drm_nouveau_private *dev_priv = dev->dev_private; | |
185 | struct nouveau_pm_engine *pm = &dev_priv->engine.pm; | |
186 | struct nvbios *bios = &dev_priv->vbios; | |
187 | struct bit_entry P; | |
9a782488 RS |
188 | struct nouveau_pm_memtimings *memtimings = &pm->memtimings; |
189 | struct nouveau_pm_tbl_header mt_hdr; | |
330c5988 BS |
190 | u8 version, headerlen, recordlen, entries; |
191 | u8 *perf, *entry; | |
192 | int vid, i; | |
193 | ||
194 | if (bios->type == NVBIOS_BIT) { | |
195 | if (bit_table(dev, 'P', &P)) | |
196 | return; | |
197 | ||
198 | if (P.version != 1 && P.version != 2) { | |
199 | NV_WARN(dev, "unknown perf for BIT P %d\n", P.version); | |
200 | return; | |
201 | } | |
202 | ||
203 | perf = ROMPTR(bios, P.data[0]); | |
204 | version = perf[0]; | |
205 | headerlen = perf[1]; | |
206 | if (version < 0x40) { | |
207 | recordlen = perf[3] + (perf[4] * perf[5]); | |
208 | entries = perf[2]; | |
3f8e11e4 BS |
209 | |
210 | pm->pwm_divisor = ROM16(perf[6]); | |
330c5988 BS |
211 | } else { |
212 | recordlen = perf[2] + (perf[3] * perf[4]); | |
213 | entries = perf[5]; | |
214 | } | |
215 | } else { | |
2756a4f5 | 216 | if (bios->data[bios->offset + 6] < 0x25) { |
0fbb114a | 217 | legacy_perf_init(dev); |
330c5988 BS |
218 | return; |
219 | } | |
220 | ||
221 | perf = ROMPTR(bios, bios->data[bios->offset + 0x94]); | |
222 | if (!perf) { | |
223 | NV_DEBUG(dev, "perf table pointer invalid\n"); | |
224 | return; | |
225 | } | |
226 | ||
227 | version = perf[1]; | |
228 | headerlen = perf[0]; | |
229 | recordlen = perf[3]; | |
230 | entries = perf[2]; | |
231 | } | |
232 | ||
29055440 EV |
233 | if (entries > NOUVEAU_PM_MAX_LEVEL) { |
234 | NV_DEBUG(dev, "perf table has too many entries - buggy vbios?\n"); | |
235 | entries = NOUVEAU_PM_MAX_LEVEL; | |
236 | } | |
237 | ||
330c5988 | 238 | entry = perf + headerlen; |
9a782488 RS |
239 | |
240 | /* For version 0x15, initialize memtiming table */ | |
241 | if(version == 0x15) { | |
242 | memtimings->timing = | |
243 | kcalloc(entries, sizeof(*memtimings->timing), GFP_KERNEL); | |
ef5ced4b | 244 | if (!memtimings->timing) { |
9a782488 RS |
245 | NV_WARN(dev,"Could not allocate memtiming table\n"); |
246 | return; | |
247 | } | |
248 | ||
249 | mt_hdr.entry_cnt = entries; | |
250 | mt_hdr.entry_len = 14; | |
251 | mt_hdr.version = version; | |
252 | mt_hdr.header_len = 4; | |
253 | } | |
254 | ||
330c5988 BS |
255 | for (i = 0; i < entries; i++) { |
256 | struct nouveau_pm_level *perflvl = &pm->perflvl[pm->nr_perflvl]; | |
257 | ||
e614b2e7 MP |
258 | perflvl->timing = NULL; |
259 | ||
330c5988 BS |
260 | if (entry[0] == 0xff) { |
261 | entry += recordlen; | |
262 | continue; | |
263 | } | |
264 | ||
265 | switch (version) { | |
266 | case 0x12: | |
267 | case 0x13: | |
268 | case 0x15: | |
269 | perflvl->fanspeed = entry[55]; | |
c3450239 | 270 | if (recordlen > 56) |
3b5565dd | 271 | perflvl->volt_min = entry[56]; |
07b12669 | 272 | perflvl->core = ROM32(entry[1]) * 10; |
e829d804 | 273 | perflvl->memory = ROM32(entry[5]) * 20; |
330c5988 BS |
274 | break; |
275 | case 0x21: | |
276 | case 0x23: | |
277 | case 0x24: | |
278 | perflvl->fanspeed = entry[4]; | |
3b5565dd | 279 | perflvl->volt_min = entry[5]; |
9f403603 BS |
280 | perflvl->shader = ROM16(entry[6]) * 1000; |
281 | perflvl->core = perflvl->shader; | |
282 | perflvl->core += (signed char)entry[8] * 1000; | |
e829d804 FJ |
283 | if (dev_priv->chipset == 0x49 || |
284 | dev_priv->chipset == 0x4b) | |
285 | perflvl->memory = ROM16(entry[11]) * 1000; | |
286 | else | |
287 | perflvl->memory = ROM16(entry[11]) * 2000; | |
330c5988 BS |
288 | break; |
289 | case 0x25: | |
290 | perflvl->fanspeed = entry[4]; | |
3b5565dd | 291 | perflvl->volt_min = entry[5]; |
07b12669 BS |
292 | perflvl->core = ROM16(entry[6]) * 1000; |
293 | perflvl->shader = ROM16(entry[10]) * 1000; | |
294 | perflvl->memory = ROM16(entry[12]) * 1000; | |
330c5988 BS |
295 | break; |
296 | case 0x30: | |
aee582de | 297 | perflvl->memscript = ROM16(entry[2]); |
330c5988 BS |
298 | case 0x35: |
299 | perflvl->fanspeed = entry[6]; | |
3b5565dd | 300 | perflvl->volt_min = entry[7]; |
07b12669 BS |
301 | perflvl->core = ROM16(entry[8]) * 1000; |
302 | perflvl->shader = ROM16(entry[10]) * 1000; | |
303 | perflvl->memory = ROM16(entry[12]) * 1000; | |
35bb5089 | 304 | perflvl->vdec = ROM16(entry[16]) * 1000; |
f3fbaf34 | 305 | perflvl->dom6 = ROM16(entry[20]) * 1000; |
330c5988 BS |
306 | break; |
307 | case 0x40: | |
9698b9a6 | 308 | #define subent(n) (ROM16(entry[perf[2] + ((n) * perf[3])]) & 0xfff) * 1000 |
330c5988 | 309 | perflvl->fanspeed = 0; /*XXX*/ |
3b5565dd | 310 | perflvl->volt_min = entry[2]; |
40f6193b | 311 | if (dev_priv->card_type == NV_50) { |
9698b9a6 BS |
312 | perflvl->core = subent(0); |
313 | perflvl->shader = subent(1); | |
314 | perflvl->memory = subent(2); | |
315 | perflvl->vdec = subent(3); | |
316 | perflvl->unka0 = subent(4); | |
40f6193b | 317 | } else { |
9698b9a6 BS |
318 | perflvl->hub06 = subent(0); |
319 | perflvl->hub01 = subent(1); | |
320 | perflvl->copy = subent(2); | |
321 | perflvl->shader = subent(3); | |
322 | perflvl->rop = subent(4); | |
323 | perflvl->memory = subent(5); | |
324 | perflvl->vdec = subent(6); | |
325 | perflvl->daemon = subent(10); | |
326 | perflvl->hub07 = subent(11); | |
40f6193b | 327 | perflvl->core = perflvl->shader / 2; |
40f6193b | 328 | } |
330c5988 BS |
329 | break; |
330 | } | |
331 | ||
330c5988 | 332 | /* make sure vid is valid */ |
3b5565dd BS |
333 | nouveau_perf_voltage(dev, &P, perflvl); |
334 | if (pm->voltage.supported && perflvl->volt_min) { | |
335 | vid = nouveau_volt_vid_lookup(dev, perflvl->volt_min); | |
330c5988 BS |
336 | if (vid < 0) { |
337 | NV_DEBUG(dev, "drop perflvl %d, bad vid\n", i); | |
338 | entry += recordlen; | |
339 | continue; | |
340 | } | |
341 | } | |
342 | ||
e614b2e7 | 343 | /* get the corresponding memory timings */ |
9a782488 RS |
344 | if (version == 0x15) { |
345 | memtimings->timing[i].id = i; | |
346 | nv30_mem_timing_entry(dev,&mt_hdr,(struct nouveau_pm_tbl_entry*) &entry[41],0,&memtimings->timing[i]); | |
347 | perflvl->timing = &memtimings->timing[i]; | |
348 | } else if (version > 0x15) { | |
fcfc7688 BS |
349 | /* last 3 args are for < 0x40, ignored for >= 0x40 */ |
350 | perflvl->timing = | |
351 | nouveau_perf_timing(dev, &P, | |
352 | perflvl->memory / 1000, | |
353 | entry + perf[3], | |
354 | perf[5], perf[4]); | |
e614b2e7 MP |
355 | } |
356 | ||
330c5988 BS |
357 | snprintf(perflvl->name, sizeof(perflvl->name), |
358 | "performance_level_%d", i); | |
359 | perflvl->id = i; | |
360 | pm->nr_perflvl++; | |
361 | ||
362 | entry += recordlen; | |
363 | } | |
364 | } | |
365 | ||
366 | void | |
367 | nouveau_perf_fini(struct drm_device *dev) | |
368 | { | |
369 | } |