drm/nouveau: Associate memtimings with performance levels on cards <= nv98
[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         u8 ramcfg = (nv_rd32(dev, NV_PEXTDEV_BOOT_0) & 0x3c) >> 2;
86
87         if (bios->type == NVBIOS_BIT) {
88                 if (bit_table(dev, 'P', &P))
89                         return;
90
91                 if (P.version != 1 && P.version != 2) {
92                         NV_WARN(dev, "unknown perf for BIT P %d\n", P.version);
93                         return;
94                 }
95
96                 perf = ROMPTR(bios, P.data[0]);
97                 version   = perf[0];
98                 headerlen = perf[1];
99                 if (version < 0x40) {
100                         recordlen = perf[3] + (perf[4] * perf[5]);
101                         entries   = perf[2];
102                 } else {
103                         recordlen = perf[2] + (perf[3] * perf[4]);
104                         entries   = perf[5];
105                 }
106         } else {
107                 if (bios->data[bios->offset + 6] < 0x25) {
108                         legacy_perf_init(dev);
109                         return;
110                 }
111
112                 perf = ROMPTR(bios, bios->data[bios->offset + 0x94]);
113                 if (!perf) {
114                         NV_DEBUG(dev, "perf table pointer invalid\n");
115                         return;
116                 }
117
118                 version   = perf[1];
119                 headerlen = perf[0];
120                 recordlen = perf[3];
121                 entries   = perf[2];
122         }
123
124         entry = perf + headerlen;
125         for (i = 0; i < entries; i++) {
126                 struct nouveau_pm_level *perflvl = &pm->perflvl[pm->nr_perflvl];
127
128                 perflvl->timing = NULL;
129
130                 if (entry[0] == 0xff) {
131                         entry += recordlen;
132                         continue;
133                 }
134
135                 switch (version) {
136                 case 0x12:
137                 case 0x13:
138                 case 0x15:
139                         perflvl->fanspeed = entry[55];
140                         perflvl->voltage = (recordlen > 56) ? entry[56] : 0;
141                         perflvl->core = ROM32(entry[1]) * 10;
142                         perflvl->memory = ROM32(entry[5]) * 20;
143                         break;
144                 case 0x21:
145                 case 0x23:
146                 case 0x24:
147                         perflvl->fanspeed = entry[4];
148                         perflvl->voltage = entry[5];
149                         perflvl->core = ROM16(entry[6]) * 1000;
150
151                         if (dev_priv->chipset == 0x49 ||
152                             dev_priv->chipset == 0x4b)
153                                 perflvl->memory = ROM16(entry[11]) * 1000;
154                         else
155                                 perflvl->memory = ROM16(entry[11]) * 2000;
156
157                         break;
158                 case 0x25:
159                         perflvl->fanspeed = entry[4];
160                         perflvl->voltage = entry[5];
161                         perflvl->core = ROM16(entry[6]) * 1000;
162                         perflvl->shader = ROM16(entry[10]) * 1000;
163                         perflvl->memory = ROM16(entry[12]) * 1000;
164                         break;
165                 case 0x30:
166                         perflvl->memscript = ROM16(entry[2]);
167                 case 0x35:
168                         perflvl->fanspeed = entry[6];
169                         perflvl->voltage = entry[7];
170                         perflvl->core = ROM16(entry[8]) * 1000;
171                         perflvl->shader = ROM16(entry[10]) * 1000;
172                         perflvl->memory = ROM16(entry[12]) * 1000;
173                         /*XXX: confirm on 0x35 */
174                         perflvl->unk05 = ROM16(entry[16]) * 1000;
175                         break;
176                 case 0x40:
177 #define subent(n) entry[perf[2] + ((n) * perf[3])]
178                         perflvl->fanspeed = 0; /*XXX*/
179                         perflvl->voltage = entry[2];
180                         perflvl->core = (ROM16(subent(0)) & 0xfff) * 1000;
181                         perflvl->shader = (ROM16(subent(1)) & 0xfff) * 1000;
182                         perflvl->memory = (ROM16(subent(2)) & 0xfff) * 1000;
183                         break;
184                 }
185
186                 /* make sure vid is valid */
187                 if (pm->voltage.supported && perflvl->voltage) {
188                         vid = nouveau_volt_vid_lookup(dev, perflvl->voltage);
189                         if (vid < 0) {
190                                 NV_DEBUG(dev, "drop perflvl %d, bad vid\n", i);
191                                 entry += recordlen;
192                                 continue;
193                         }
194                 }
195
196                 /* get the corresponding memory timings */
197                 if (pm->memtimings.supported) {
198                         u8  timing_id = 0xff;
199                         u16 extra_data;
200
201                         if (version > 0x15 && version < 0x40 &&
202                             ramcfg < perf[4]) {
203                                 extra_data = perf[3] + (ramcfg * perf[5]);
204                                 timing_id  = entry[extra_data + 1];
205                         }
206
207                         if (pm->memtimings.nr_timing > timing_id)
208                                 perflvl->timing =
209                                         &pm->memtimings.timing[timing_id];
210                 }
211
212                 snprintf(perflvl->name, sizeof(perflvl->name),
213                          "performance_level_%d", i);
214                 perflvl->id = i;
215                 pm->nr_perflvl++;
216
217                 entry += recordlen;
218         }
219 }
220
221 void
222 nouveau_perf_fini(struct drm_device *dev)
223 {
224 }