]> git.karo-electronics.de Git - karo-tx-linux.git/blob - drivers/gpu/drm/nouveau/nouveau_perf.c
Merge branch 'next' of git://git.infradead.org/users/vkoul/slave-dma
[karo-tx-linux.git] / drivers / gpu / drm / nouveau / nouveau_perf.c
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
30 static u8 *
31 nouveau_perf_table(struct drm_device *dev, u8 *ver)
32 {
33         struct drm_nouveau_private *dev_priv = dev->dev_private;
34         struct nvbios *bios = &dev_priv->vbios;
35         struct bit_entry P;
36
37         if (!bit_table(dev, 'P', &P) && P.version && P.version <= 2) {
38                 u8 *perf = ROMPTR(dev, P.data[0]);
39                 if (perf) {
40                         *ver = perf[0];
41                         return perf;
42                 }
43         }
44
45         if (bios->type == NVBIOS_BMP) {
46                 if (bios->data[bios->offset + 6] >= 0x25) {
47                         u8 *perf = ROMPTR(dev, bios->data[bios->offset + 0x94]);
48                         if (perf) {
49                                 *ver = perf[1];
50                                 return perf;
51                         }
52                 }
53         }
54
55         return NULL;
56 }
57
58 static u8 *
59 nouveau_perf_entry(struct drm_device *dev, int idx,
60                    u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
61 {
62         u8 *perf = nouveau_perf_table(dev, ver);
63         if (perf) {
64                 if (*ver >= 0x12 && *ver < 0x20 && idx < perf[2]) {
65                         *hdr = perf[3];
66                         *cnt = 0;
67                         *len = 0;
68                         return perf + perf[0] + idx * perf[3];
69                 } else
70                 if (*ver >= 0x20 && *ver < 0x40 && idx < perf[2]) {
71                         *hdr = perf[3];
72                         *cnt = perf[4];
73                         *len = perf[5];
74                         return perf + perf[1] + idx * (*hdr + (*cnt * *len));
75                 } else
76                 if (*ver >= 0x40 && *ver < 0x41 && idx < perf[5]) {
77                         *hdr = perf[2];
78                         *cnt = perf[4];
79                         *len = perf[3];
80                         return perf + perf[1] + idx * (*hdr + (*cnt * *len));
81                 }
82         }
83         return NULL;
84 }
85
86 u8 *
87 nouveau_perf_rammap(struct drm_device *dev, u32 freq,
88                     u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
89 {
90         struct drm_nouveau_private *dev_priv = dev->dev_private;
91         struct bit_entry P;
92         u8 *perf, i = 0;
93
94         if (!bit_table(dev, 'P', &P) && P.version == 2) {
95                 u8 *rammap = ROMPTR(dev, P.data[4]);
96                 if (rammap) {
97                         u8 *ramcfg = rammap + rammap[1];
98
99                         *ver = rammap[0];
100                         *hdr = rammap[2];
101                         *cnt = rammap[4];
102                         *len = rammap[3];
103
104                         freq /= 1000;
105                         for (i = 0; i < rammap[5]; i++) {
106                                 if (freq >= ROM16(ramcfg[0]) &&
107                                     freq <= ROM16(ramcfg[2]))
108                                         return ramcfg;
109
110                                 ramcfg += *hdr + (*cnt * *len);
111                         }
112                 }
113
114                 return NULL;
115         }
116
117         if (dev_priv->chipset == 0x49 ||
118             dev_priv->chipset == 0x4b)
119                 freq /= 2;
120
121         while ((perf = nouveau_perf_entry(dev, i++, ver, hdr, cnt, len))) {
122                 if (*ver >= 0x20 && *ver < 0x25) {
123                         if (perf[0] != 0xff && freq <= ROM16(perf[11]) * 1000)
124                                 break;
125                 } else
126                 if (*ver >= 0x25 && *ver < 0x40) {
127                         if (perf[0] != 0xff && freq <= ROM16(perf[12]) * 1000)
128                                 break;
129                 }
130         }
131
132         if (perf) {
133                 u8 *ramcfg = perf + *hdr;
134                 *ver = 0x00;
135                 *hdr = 0;
136                 return ramcfg;
137         }
138
139         return NULL;
140 }
141
142 u8 *
143 nouveau_perf_ramcfg(struct drm_device *dev, u32 freq, u8 *ver, u8 *len)
144 {
145         struct drm_nouveau_private *dev_priv = dev->dev_private;
146         struct nvbios *bios = &dev_priv->vbios;
147         u8 strap, hdr, cnt;
148         u8 *rammap;
149
150         strap = (nv_rd32(dev, 0x101000) & 0x0000003c) >> 2;
151         if (bios->ram_restrict_tbl_ptr)
152                 strap = bios->data[bios->ram_restrict_tbl_ptr + strap];
153
154         rammap = nouveau_perf_rammap(dev, freq, ver, &hdr, &cnt, len);
155         if (rammap && strap < cnt)
156                 return rammap + hdr + (strap * *len);
157
158         return NULL;
159 }
160
161 u8 *
162 nouveau_perf_timing(struct drm_device *dev, u32 freq, u8 *ver, u8 *len)
163 {
164         struct drm_nouveau_private *dev_priv = dev->dev_private;
165         struct nvbios *bios = &dev_priv->vbios;
166         struct bit_entry P;
167         u8 *perf, *timing = NULL;
168         u8 i = 0, hdr, cnt;
169
170         if (bios->type == NVBIOS_BMP) {
171                 while ((perf = nouveau_perf_entry(dev, i++, ver, &hdr, &cnt,
172                                                   len)) && *ver == 0x15) {
173                         if (freq <= ROM32(perf[5]) * 20) {
174                                 *ver = 0x00;
175                                 *len = 14;
176                                 return perf + 41;
177                         }
178                 }
179                 return NULL;
180         }
181
182         if (!bit_table(dev, 'P', &P)) {
183                 if (P.version == 1)
184                         timing = ROMPTR(dev, P.data[4]);
185                 else
186                 if (P.version == 2)
187                         timing = ROMPTR(dev, P.data[8]);
188         }
189
190         if (timing && timing[0] == 0x10) {
191                 u8 *ramcfg = nouveau_perf_ramcfg(dev, freq, ver, len);
192                 if (ramcfg && ramcfg[1] < timing[2]) {
193                         *ver = timing[0];
194                         *len = timing[3];
195                         return timing + timing[1] + (ramcfg[1] * timing[3]);
196                 }
197         }
198
199         return NULL;
200 }
201
202 static void
203 legacy_perf_init(struct drm_device *dev)
204 {
205         struct drm_nouveau_private *dev_priv = dev->dev_private;
206         struct nvbios *bios = &dev_priv->vbios;
207         struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
208         char *perf, *entry, *bmp = &bios->data[bios->offset];
209         int headerlen, use_straps;
210
211         if (bmp[5] < 0x5 || bmp[6] < 0x14) {
212                 NV_DEBUG(dev, "BMP version too old for perf\n");
213                 return;
214         }
215
216         perf = ROMPTR(dev, bmp[0x73]);
217         if (!perf) {
218                 NV_DEBUG(dev, "No memclock table pointer found.\n");
219                 return;
220         }
221
222         switch (perf[0]) {
223         case 0x12:
224         case 0x14:
225         case 0x18:
226                 use_straps = 0;
227                 headerlen = 1;
228                 break;
229         case 0x01:
230                 use_straps = perf[1] & 1;
231                 headerlen = (use_straps ? 8 : 2);
232                 break;
233         default:
234                 NV_WARN(dev, "Unknown memclock table version %x.\n", perf[0]);
235                 return;
236         }
237
238         entry = perf + headerlen;
239         if (use_straps)
240                 entry += (nv_rd32(dev, NV_PEXTDEV_BOOT_0) & 0x3c) >> 1;
241
242         sprintf(pm->perflvl[0].name, "performance_level_0");
243         pm->perflvl[0].memory = ROM16(entry[0]) * 20;
244         pm->nr_perflvl = 1;
245 }
246
247 static void
248 nouveau_perf_voltage(struct drm_device *dev, struct nouveau_pm_level *perflvl)
249 {
250         struct drm_nouveau_private *dev_priv = dev->dev_private;
251         struct bit_entry P;
252         u8 *vmap;
253         int id;
254
255         id = perflvl->volt_min;
256         perflvl->volt_min = 0;
257
258         /* boards using voltage table version <0x40 store the voltage
259          * level directly in the perflvl entry as a multiple of 10mV
260          */
261         if (dev_priv->engine.pm.voltage.version < 0x40) {
262                 perflvl->volt_min = id * 10000;
263                 perflvl->volt_max = perflvl->volt_min;
264                 return;
265         }
266
267         /* on newer ones, the perflvl stores an index into yet another
268          * vbios table containing a min/max voltage value for the perflvl
269          */
270         if (bit_table(dev, 'P', &P) || P.version != 2 || P.length < 34) {
271                 NV_DEBUG(dev, "where's our volt map table ptr? %d %d\n",
272                          P.version, P.length);
273                 return;
274         }
275
276         vmap = ROMPTR(dev, P.data[32]);
277         if (!vmap) {
278                 NV_DEBUG(dev, "volt map table pointer invalid\n");
279                 return;
280         }
281
282         if (id < vmap[3]) {
283                 vmap += vmap[1] + (vmap[2] * id);
284                 perflvl->volt_min = ROM32(vmap[0]);
285                 perflvl->volt_max = ROM32(vmap[4]);
286         }
287 }
288
289 void
290 nouveau_perf_init(struct drm_device *dev)
291 {
292         struct drm_nouveau_private *dev_priv = dev->dev_private;
293         struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
294         struct nvbios *bios = &dev_priv->vbios;
295         u8 *perf, ver, hdr, cnt, len;
296         int ret, vid, i = -1;
297
298         if (bios->type == NVBIOS_BMP && bios->data[bios->offset + 6] < 0x25) {
299                 legacy_perf_init(dev);
300                 return;
301         }
302
303         perf = nouveau_perf_table(dev, &ver);
304         if (ver >= 0x20 && ver < 0x40)
305                 pm->fan.pwm_divisor = ROM16(perf[6]);
306
307         while ((perf = nouveau_perf_entry(dev, ++i, &ver, &hdr, &cnt, &len))) {
308                 struct nouveau_pm_level *perflvl = &pm->perflvl[pm->nr_perflvl];
309
310                 if (perf[0] == 0xff)
311                         continue;
312
313                 switch (ver) {
314                 case 0x12:
315                 case 0x13:
316                 case 0x15:
317                         perflvl->fanspeed = perf[55];
318                         if (hdr > 56)
319                                 perflvl->volt_min = perf[56];
320                         perflvl->core = ROM32(perf[1]) * 10;
321                         perflvl->memory = ROM32(perf[5]) * 20;
322                         break;
323                 case 0x21:
324                 case 0x23:
325                 case 0x24:
326                         perflvl->fanspeed = perf[4];
327                         perflvl->volt_min = perf[5];
328                         perflvl->shader = ROM16(perf[6]) * 1000;
329                         perflvl->core = perflvl->shader;
330                         perflvl->core += (signed char)perf[8] * 1000;
331                         if (dev_priv->chipset == 0x49 ||
332                             dev_priv->chipset == 0x4b)
333                                 perflvl->memory = ROM16(perf[11]) * 1000;
334                         else
335                                 perflvl->memory = ROM16(perf[11]) * 2000;
336                         break;
337                 case 0x25:
338                         perflvl->fanspeed = perf[4];
339                         perflvl->volt_min = perf[5];
340                         perflvl->core = ROM16(perf[6]) * 1000;
341                         perflvl->shader = ROM16(perf[10]) * 1000;
342                         perflvl->memory = ROM16(perf[12]) * 1000;
343                         break;
344                 case 0x30:
345                         perflvl->memscript = ROM16(perf[2]);
346                 case 0x35:
347                         perflvl->fanspeed = perf[6];
348                         perflvl->volt_min = perf[7];
349                         perflvl->core = ROM16(perf[8]) * 1000;
350                         perflvl->shader = ROM16(perf[10]) * 1000;
351                         perflvl->memory = ROM16(perf[12]) * 1000;
352                         perflvl->vdec = ROM16(perf[16]) * 1000;
353                         perflvl->dom6 = ROM16(perf[20]) * 1000;
354                         break;
355                 case 0x40:
356 #define subent(n) ((ROM16(perf[hdr + (n) * len]) & 0xfff) * 1000)
357                         perflvl->fanspeed = 0; /*XXX*/
358                         perflvl->volt_min = perf[2];
359                         if (dev_priv->card_type == NV_50) {
360                                 perflvl->core   = subent(0);
361                                 perflvl->shader = subent(1);
362                                 perflvl->memory = subent(2);
363                                 perflvl->vdec   = subent(3);
364                                 perflvl->unka0  = subent(4);
365                         } else {
366                                 perflvl->hub06  = subent(0);
367                                 perflvl->hub01  = subent(1);
368                                 perflvl->copy   = subent(2);
369                                 perflvl->shader = subent(3);
370                                 perflvl->rop    = subent(4);
371                                 perflvl->memory = subent(5);
372                                 perflvl->vdec   = subent(6);
373                                 perflvl->daemon = subent(10);
374                                 perflvl->hub07  = subent(11);
375                                 perflvl->core   = perflvl->shader / 2;
376                         }
377                         break;
378                 }
379
380                 /* make sure vid is valid */
381                 nouveau_perf_voltage(dev, perflvl);
382                 if (pm->voltage.supported && perflvl->volt_min) {
383                         vid = nouveau_volt_vid_lookup(dev, perflvl->volt_min);
384                         if (vid < 0) {
385                                 NV_DEBUG(dev, "perflvl %d, bad vid\n", i);
386                                 continue;
387                         }
388                 }
389
390                 /* get the corresponding memory timings */
391                 ret = nouveau_mem_timing_calc(dev, perflvl->memory,
392                                                   &perflvl->timing);
393                 if (ret) {
394                         NV_DEBUG(dev, "perflvl %d, bad timing: %d\n", i, ret);
395                         continue;
396                 }
397
398                 snprintf(perflvl->name, sizeof(perflvl->name),
399                          "performance_level_%d", i);
400                 perflvl->id = i;
401
402                 snprintf(perflvl->profile.name, sizeof(perflvl->profile.name),
403                          "%d", perflvl->id);
404                 perflvl->profile.func = &nouveau_pm_static_profile_func;
405                 list_add_tail(&perflvl->profile.head, &pm->profiles);
406
407
408                 pm->nr_perflvl++;
409         }
410 }
411
412 void
413 nouveau_perf_fini(struct drm_device *dev)
414 {
415 }