Merge branch 'davinci-fixes' of git://gitorious.org/linux-davinci/linux-davinci into...
[pandora-kernel.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 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
75 void
76 nouveau_perf_init(struct drm_device *dev)
77 {
78         struct drm_nouveau_private *dev_priv = dev->dev_private;
79         struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
80         struct nvbios *bios = &dev_priv->vbios;
81         struct bit_entry P;
82         u8 version, headerlen, recordlen, entries;
83         u8 *perf, *entry;
84         int vid, i;
85
86         if (bios->type == NVBIOS_BIT) {
87                 if (bit_table(dev, 'P', &P))
88                         return;
89
90                 if (P.version != 1 && P.version != 2) {
91                         NV_WARN(dev, "unknown perf for BIT P %d\n", P.version);
92                         return;
93                 }
94
95                 perf = ROMPTR(bios, P.data[0]);
96                 version   = perf[0];
97                 headerlen = perf[1];
98                 if (version < 0x40) {
99                         recordlen = perf[3] + (perf[4] * perf[5]);
100                         entries   = perf[2];
101                 } else {
102                         recordlen = perf[2] + (perf[3] * perf[4]);
103                         entries   = perf[5];
104                 }
105         } else {
106                 if (bios->data[bios->offset + 6] < 0x25) {
107                         legacy_perf_init(dev);
108                         return;
109                 }
110
111                 perf = ROMPTR(bios, bios->data[bios->offset + 0x94]);
112                 if (!perf) {
113                         NV_DEBUG(dev, "perf table pointer invalid\n");
114                         return;
115                 }
116
117                 version   = perf[1];
118                 headerlen = perf[0];
119                 recordlen = perf[3];
120                 entries   = perf[2];
121         }
122
123         entry = perf + headerlen;
124         for (i = 0; i < entries; i++) {
125                 struct nouveau_pm_level *perflvl = &pm->perflvl[pm->nr_perflvl];
126
127                 if (entry[0] == 0xff) {
128                         entry += recordlen;
129                         continue;
130                 }
131
132                 switch (version) {
133                 case 0x12:
134                 case 0x13:
135                 case 0x15:
136                         perflvl->fanspeed = entry[55];
137                         perflvl->voltage = (recordlen > 56) ? entry[56] : 0;
138                         perflvl->core = ROM32(entry[1]) * 10;
139                         perflvl->memory = ROM32(entry[5]) * 20;
140                         break;
141                 case 0x21:
142                 case 0x23:
143                 case 0x24:
144                         perflvl->fanspeed = entry[4];
145                         perflvl->voltage = entry[5];
146                         perflvl->core = ROM16(entry[6]) * 1000;
147
148                         if (dev_priv->chipset == 0x49 ||
149                             dev_priv->chipset == 0x4b)
150                                 perflvl->memory = ROM16(entry[11]) * 1000;
151                         else
152                                 perflvl->memory = ROM16(entry[11]) * 2000;
153
154                         break;
155                 case 0x25:
156                         perflvl->fanspeed = entry[4];
157                         perflvl->voltage = entry[5];
158                         perflvl->core = ROM16(entry[6]) * 1000;
159                         perflvl->shader = ROM16(entry[10]) * 1000;
160                         perflvl->memory = ROM16(entry[12]) * 1000;
161                         break;
162                 case 0x30:
163                         perflvl->memscript = ROM16(entry[2]);
164                 case 0x35:
165                         perflvl->fanspeed = entry[6];
166                         perflvl->voltage = entry[7];
167                         perflvl->core = ROM16(entry[8]) * 1000;
168                         perflvl->shader = ROM16(entry[10]) * 1000;
169                         perflvl->memory = ROM16(entry[12]) * 1000;
170                         /*XXX: confirm on 0x35 */
171                         perflvl->unk05 = ROM16(entry[16]) * 1000;
172                         break;
173                 case 0x40:
174 #define subent(n) entry[perf[2] + ((n) * perf[3])]
175                         perflvl->fanspeed = 0; /*XXX*/
176                         perflvl->voltage = entry[2];
177                         perflvl->core = (ROM16(subent(0)) & 0xfff) * 1000;
178                         perflvl->shader = (ROM16(subent(1)) & 0xfff) * 1000;
179                         perflvl->memory = (ROM16(subent(2)) & 0xfff) * 1000;
180                         break;
181                 }
182
183                 /* make sure vid is valid */
184                 if (pm->voltage.supported && perflvl->voltage) {
185                         vid = nouveau_volt_vid_lookup(dev, perflvl->voltage);
186                         if (vid < 0) {
187                                 NV_DEBUG(dev, "drop perflvl %d, bad vid\n", i);
188                                 entry += recordlen;
189                                 continue;
190                         }
191                 }
192
193                 snprintf(perflvl->name, sizeof(perflvl->name),
194                          "performance_level_%d", i);
195                 perflvl->id = i;
196                 pm->nr_perflvl++;
197
198                 entry += recordlen;
199         }
200 }
201
202 void
203 nouveau_perf_fini(struct drm_device *dev)
204 {
205 }