diff options
author | Ben Skeggs <bskeggs@redhat.com> | 2010-09-16 01:39:49 -0400 |
---|---|---|
committer | Ben Skeggs <bskeggs@redhat.com> | 2010-09-24 02:27:00 -0400 |
commit | 330c5988ee78045e6a731c3693251aaa5b0d14e3 (patch) | |
tree | f5313d35a321f665f2fa0e805a0317e1f97d3c28 | |
parent | 4709bff02adcb0d05d2d1a397e60581baa562de9 (diff) |
drm/nouveau: import initial work on vbios performance table parsing
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
-rw-r--r-- | drivers/gpu/drm/nouveau/Makefile | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_drv.h | 46 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_perf.c | 159 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_pm.c | 214 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_pm.h | 44 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_state.c | 5 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_volt.c | 209 |
7 files changed, 678 insertions, 0 deletions
diff --git a/drivers/gpu/drm/nouveau/Makefile b/drivers/gpu/drm/nouveau/Makefile index d6cfbf259876..2fd61888a83d 100644 --- a/drivers/gpu/drm/nouveau/Makefile +++ b/drivers/gpu/drm/nouveau/Makefile | |||
@@ -10,6 +10,7 @@ nouveau-y := nouveau_drv.o nouveau_state.o nouveau_channel.o nouveau_mem.o \ | |||
10 | nouveau_hw.o nouveau_calc.o nouveau_bios.o nouveau_i2c.o \ | 10 | nouveau_hw.o nouveau_calc.o nouveau_bios.o nouveau_i2c.o \ |
11 | nouveau_display.o nouveau_connector.o nouveau_fbcon.o \ | 11 | nouveau_display.o nouveau_connector.o nouveau_fbcon.o \ |
12 | nouveau_dp.o nouveau_ramht.o \ | 12 | nouveau_dp.o nouveau_ramht.o \ |
13 | nouveau_pm.o nouveau_volt.o nouveau_perf.o \ | ||
13 | nv04_timer.o \ | 14 | nv04_timer.o \ |
14 | nv04_mc.o nv40_mc.o nv50_mc.o \ | 15 | nv04_mc.o nv40_mc.o nv50_mc.o \ |
15 | nv04_fb.o nv10_fb.o nv30_fb.o nv40_fb.o nv50_fb.o nvc0_fb.o \ | 16 | nv04_fb.o nv10_fb.o nv30_fb.o nv40_fb.o nv50_fb.o nvc0_fb.o \ |
diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index d2fecc05eae4..bda4d1e7c63a 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h | |||
@@ -359,6 +359,51 @@ struct nouveau_gpio_engine { | |||
359 | void (*irq_enable)(struct drm_device *, enum dcb_gpio_tag, bool on); | 359 | void (*irq_enable)(struct drm_device *, enum dcb_gpio_tag, bool on); |
360 | }; | 360 | }; |
361 | 361 | ||
362 | struct nouveau_pm_voltage_level { | ||
363 | u8 voltage; | ||
364 | u8 vid; | ||
365 | }; | ||
366 | |||
367 | struct nouveau_pm_voltage { | ||
368 | bool supported; | ||
369 | u8 vid_mask; | ||
370 | |||
371 | struct nouveau_pm_voltage_level *level; | ||
372 | int nr_level; | ||
373 | }; | ||
374 | |||
375 | #define NOUVEAU_PM_MAX_LEVEL 8 | ||
376 | struct nouveau_pm_level { | ||
377 | struct device_attribute dev_attr; | ||
378 | char name[32]; | ||
379 | int id; | ||
380 | |||
381 | u32 core; | ||
382 | u32 memory; | ||
383 | u32 shader; | ||
384 | u32 unk05; | ||
385 | |||
386 | u8 voltage; | ||
387 | u8 fanspeed; | ||
388 | }; | ||
389 | |||
390 | struct nouveau_pm_engine { | ||
391 | struct nouveau_pm_voltage voltage; | ||
392 | struct nouveau_pm_level perflvl[NOUVEAU_PM_MAX_LEVEL]; | ||
393 | int nr_perflvl; | ||
394 | |||
395 | struct nouveau_pm_level boot; | ||
396 | struct nouveau_pm_level *cur; | ||
397 | |||
398 | int (*clock_get)(struct drm_device *, u32 id); | ||
399 | void *(*clock_pre)(struct drm_device *, u32 id, int khz); | ||
400 | void (*clock_set)(struct drm_device *, void *); | ||
401 | int (*voltage_get)(struct drm_device *); | ||
402 | int (*voltage_set)(struct drm_device *, int voltage); | ||
403 | int (*fanspeed_get)(struct drm_device *); | ||
404 | int (*fanspeed_set)(struct drm_device *, int fanspeed); | ||
405 | }; | ||
406 | |||
362 | struct nouveau_engine { | 407 | struct nouveau_engine { |
363 | struct nouveau_instmem_engine instmem; | 408 | struct nouveau_instmem_engine instmem; |
364 | struct nouveau_mc_engine mc; | 409 | struct nouveau_mc_engine mc; |
@@ -368,6 +413,7 @@ struct nouveau_engine { | |||
368 | struct nouveau_fifo_engine fifo; | 413 | struct nouveau_fifo_engine fifo; |
369 | struct nouveau_display_engine display; | 414 | struct nouveau_display_engine display; |
370 | struct nouveau_gpio_engine gpio; | 415 | struct nouveau_gpio_engine gpio; |
416 | struct nouveau_pm_engine pm; | ||
371 | }; | 417 | }; |
372 | 418 | ||
373 | struct nouveau_pll_vals { | 419 | struct nouveau_pll_vals { |
diff --git a/drivers/gpu/drm/nouveau/nouveau_perf.c b/drivers/gpu/drm/nouveau/nouveau_perf.c new file mode 100644 index 000000000000..a882a366487f --- /dev/null +++ b/drivers/gpu/drm/nouveau/nouveau_perf.c | |||
@@ -0,0 +1,159 @@ | |||
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 | void | ||
31 | nouveau_perf_init(struct drm_device *dev) | ||
32 | { | ||
33 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
34 | struct nouveau_pm_engine *pm = &dev_priv->engine.pm; | ||
35 | struct nvbios *bios = &dev_priv->vbios; | ||
36 | struct bit_entry P; | ||
37 | u8 version, headerlen, recordlen, entries; | ||
38 | u8 *perf, *entry; | ||
39 | int vid, i; | ||
40 | |||
41 | if (bios->type == NVBIOS_BIT) { | ||
42 | if (bit_table(dev, 'P', &P)) | ||
43 | return; | ||
44 | |||
45 | if (P.version != 1 && P.version != 2) { | ||
46 | NV_WARN(dev, "unknown perf for BIT P %d\n", P.version); | ||
47 | return; | ||
48 | } | ||
49 | |||
50 | perf = ROMPTR(bios, P.data[0]); | ||
51 | version = perf[0]; | ||
52 | headerlen = perf[1]; | ||
53 | if (version < 0x40) { | ||
54 | recordlen = perf[3] + (perf[4] * perf[5]); | ||
55 | entries = perf[2]; | ||
56 | } else { | ||
57 | recordlen = perf[2] + (perf[3] * perf[4]); | ||
58 | entries = perf[5]; | ||
59 | } | ||
60 | } else { | ||
61 | if (bios->data[bios->offset + 6] < 0x27) { | ||
62 | NV_DEBUG(dev, "BMP version too old for perf\n"); | ||
63 | return; | ||
64 | } | ||
65 | |||
66 | perf = ROMPTR(bios, bios->data[bios->offset + 0x94]); | ||
67 | if (!perf) { | ||
68 | NV_DEBUG(dev, "perf table pointer invalid\n"); | ||
69 | return; | ||
70 | } | ||
71 | |||
72 | version = perf[1]; | ||
73 | headerlen = perf[0]; | ||
74 | recordlen = perf[3]; | ||
75 | entries = perf[2]; | ||
76 | } | ||
77 | |||
78 | entry = perf + headerlen; | ||
79 | for (i = 0; i < entries; i++) { | ||
80 | struct nouveau_pm_level *perflvl = &pm->perflvl[pm->nr_perflvl]; | ||
81 | |||
82 | if (entry[0] == 0xff) { | ||
83 | entry += recordlen; | ||
84 | continue; | ||
85 | } | ||
86 | |||
87 | switch (version) { | ||
88 | case 0x12: | ||
89 | case 0x13: | ||
90 | case 0x15: | ||
91 | perflvl->fanspeed = entry[55]; | ||
92 | perflvl->voltage = entry[56]; | ||
93 | perflvl->core = ROM32(entry[1]) / 100; | ||
94 | perflvl->memory = ROM32(entry[5]) / 100; | ||
95 | break; | ||
96 | case 0x21: | ||
97 | case 0x23: | ||
98 | case 0x24: | ||
99 | perflvl->fanspeed = entry[4]; | ||
100 | perflvl->voltage = entry[5]; | ||
101 | perflvl->core = ROM16(entry[6]); | ||
102 | perflvl->memory = ROM16(entry[11]); | ||
103 | break; | ||
104 | case 0x25: | ||
105 | perflvl->fanspeed = entry[4]; | ||
106 | perflvl->voltage = entry[5]; | ||
107 | perflvl->core = ROM16(entry[6]); | ||
108 | perflvl->shader = ROM16(entry[10]); | ||
109 | perflvl->memory = ROM16(entry[12]); | ||
110 | break; | ||
111 | case 0x30: | ||
112 | case 0x35: | ||
113 | perflvl->fanspeed = entry[6]; | ||
114 | perflvl->voltage = entry[7]; | ||
115 | perflvl->core = ROM16(entry[8]); | ||
116 | perflvl->shader = ROM16(entry[10]); | ||
117 | perflvl->memory = ROM16(entry[12]); | ||
118 | /*XXX: confirm on 0x35 */ | ||
119 | perflvl->unk05 = ROM16(entry[16]); | ||
120 | break; | ||
121 | case 0x40: | ||
122 | #define subent(n) entry[perf[2] + ((n) * perf[3])] | ||
123 | perflvl->fanspeed = 0; /*XXX*/ | ||
124 | perflvl->voltage = 0; /*XXX: entry[2] */; | ||
125 | perflvl->core = ROM16(subent(0)) & 0xfff; | ||
126 | perflvl->shader = ROM16(subent(1)) & 0xfff; | ||
127 | perflvl->memory = ROM16(subent(2)) & 0xfff; | ||
128 | break; | ||
129 | } | ||
130 | |||
131 | /* convert MHz -> KHz, it's more convenient */ | ||
132 | perflvl->core *= 1000; | ||
133 | perflvl->memory *= 1000; | ||
134 | perflvl->shader *= 1000; | ||
135 | perflvl->unk05 *= 1000; | ||
136 | |||
137 | /* make sure vid is valid */ | ||
138 | if (pm->voltage.supported && perflvl->voltage) { | ||
139 | vid = nouveau_volt_vid_lookup(dev, perflvl->voltage); | ||
140 | if (vid < 0) { | ||
141 | NV_DEBUG(dev, "drop perflvl %d, bad vid\n", i); | ||
142 | entry += recordlen; | ||
143 | continue; | ||
144 | } | ||
145 | } | ||
146 | |||
147 | snprintf(perflvl->name, sizeof(perflvl->name), | ||
148 | "performance_level_%d", i); | ||
149 | perflvl->id = i; | ||
150 | pm->nr_perflvl++; | ||
151 | |||
152 | entry += recordlen; | ||
153 | } | ||
154 | } | ||
155 | |||
156 | void | ||
157 | nouveau_perf_fini(struct drm_device *dev) | ||
158 | { | ||
159 | } | ||
diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.c b/drivers/gpu/drm/nouveau/nouveau_pm.c new file mode 100644 index 000000000000..9cf5fd665b8c --- /dev/null +++ b/drivers/gpu/drm/nouveau/nouveau_pm.c | |||
@@ -0,0 +1,214 @@ | |||
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 int | ||
31 | nouveau_pm_perflvl_get(struct drm_device *dev, struct nouveau_pm_level *perflvl) | ||
32 | { | ||
33 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
34 | struct nouveau_pm_engine *pm = &dev_priv->engine.pm; | ||
35 | int ret; | ||
36 | |||
37 | if (!pm->clock_get) | ||
38 | return -EINVAL; | ||
39 | |||
40 | memset(perflvl, 0, sizeof(*perflvl)); | ||
41 | |||
42 | ret = pm->clock_get(dev, PLL_CORE); | ||
43 | if (ret > 0) | ||
44 | perflvl->core = ret; | ||
45 | |||
46 | ret = pm->clock_get(dev, PLL_MEMORY); | ||
47 | if (ret > 0) | ||
48 | perflvl->memory = ret; | ||
49 | |||
50 | ret = pm->clock_get(dev, PLL_SHADER); | ||
51 | if (ret > 0) | ||
52 | perflvl->shader = ret; | ||
53 | |||
54 | ret = pm->clock_get(dev, PLL_UNK05); | ||
55 | if (ret > 0) | ||
56 | perflvl->unk05 = ret; | ||
57 | |||
58 | if (pm->voltage.supported && pm->voltage_get) { | ||
59 | ret = pm->voltage_get(dev); | ||
60 | if (ret > 0) | ||
61 | perflvl->voltage = ret; | ||
62 | } | ||
63 | |||
64 | return 0; | ||
65 | } | ||
66 | |||
67 | static void | ||
68 | nouveau_pm_perflvl_info(struct nouveau_pm_level *perflvl, char *ptr, int len) | ||
69 | { | ||
70 | char s[16], v[16], f[16]; | ||
71 | |||
72 | s[0] = '\0'; | ||
73 | if (perflvl->shader) | ||
74 | snprintf(s, sizeof(s), " shader %dMHz", perflvl->shader / 1000); | ||
75 | |||
76 | v[0] = '\0'; | ||
77 | if (perflvl->voltage) | ||
78 | snprintf(v, sizeof(v), " voltage %dmV", perflvl->voltage * 10); | ||
79 | |||
80 | f[0] = '\0'; | ||
81 | if (perflvl->fanspeed) | ||
82 | snprintf(f, sizeof(f), " fanspeed %d%%", perflvl->fanspeed); | ||
83 | |||
84 | snprintf(ptr, len, "core %dMHz memory %dMHz%s%s%s\n", | ||
85 | perflvl->core / 1000, perflvl->memory / 1000, s, v, f); | ||
86 | } | ||
87 | |||
88 | static ssize_t | ||
89 | nouveau_pm_get_perflvl_info(struct device *d, | ||
90 | struct device_attribute *a, char *buf) | ||
91 | { | ||
92 | struct nouveau_pm_level *perflvl = (struct nouveau_pm_level *)a; | ||
93 | char *ptr = buf; | ||
94 | int len = PAGE_SIZE; | ||
95 | |||
96 | snprintf(ptr, len, "%d: ", perflvl->id); | ||
97 | ptr += strlen(buf); | ||
98 | len -= strlen(buf); | ||
99 | |||
100 | nouveau_pm_perflvl_info(perflvl, ptr, len); | ||
101 | return strlen(buf); | ||
102 | } | ||
103 | |||
104 | static ssize_t | ||
105 | nouveau_pm_get_perflvl(struct device *d, struct device_attribute *a, char *buf) | ||
106 | { | ||
107 | struct drm_device *dev = pci_get_drvdata(to_pci_dev(d)); | ||
108 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
109 | struct nouveau_pm_engine *pm = &dev_priv->engine.pm; | ||
110 | struct nouveau_pm_level cur; | ||
111 | int len = PAGE_SIZE, ret; | ||
112 | char *ptr = buf; | ||
113 | |||
114 | if (!pm->cur) | ||
115 | snprintf(ptr, len, "setting: boot\n"); | ||
116 | else if (pm->cur == &pm->boot) | ||
117 | snprintf(ptr, len, "setting: boot\nc: "); | ||
118 | else | ||
119 | snprintf(ptr, len, "setting: static %d\nc: ", pm->cur->id); | ||
120 | ptr += strlen(buf); | ||
121 | len -= strlen(buf); | ||
122 | |||
123 | ret = nouveau_pm_perflvl_get(dev, &cur); | ||
124 | if (ret == 0) | ||
125 | nouveau_pm_perflvl_info(&cur, ptr, len); | ||
126 | return strlen(buf); | ||
127 | } | ||
128 | |||
129 | static ssize_t | ||
130 | nouveau_pm_set_perflvl(struct device *d, struct device_attribute *a, | ||
131 | const char *buf, size_t count) | ||
132 | { | ||
133 | return -EPERM; | ||
134 | } | ||
135 | |||
136 | DEVICE_ATTR(performance_level, S_IRUGO | S_IWUSR, | ||
137 | nouveau_pm_get_perflvl, nouveau_pm_set_perflvl); | ||
138 | |||
139 | int | ||
140 | nouveau_pm_init(struct drm_device *dev) | ||
141 | { | ||
142 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
143 | struct nouveau_pm_engine *pm = &dev_priv->engine.pm; | ||
144 | struct device *d = &dev->pdev->dev; | ||
145 | char info[256]; | ||
146 | int ret, i; | ||
147 | |||
148 | nouveau_volt_init(dev); | ||
149 | nouveau_perf_init(dev); | ||
150 | |||
151 | NV_INFO(dev, "%d available performance level(s)\n", pm->nr_perflvl); | ||
152 | for (i = 0; i < pm->nr_perflvl; i++) { | ||
153 | nouveau_pm_perflvl_info(&pm->perflvl[i], info, sizeof(info)); | ||
154 | NV_INFO(dev, "%d: %s", pm->perflvl[i].id, info); | ||
155 | } | ||
156 | |||
157 | /* determine current ("boot") performance level */ | ||
158 | ret = nouveau_pm_perflvl_get(dev, &pm->boot); | ||
159 | if (ret == 0) { | ||
160 | pm->cur = &pm->boot; | ||
161 | |||
162 | nouveau_pm_perflvl_info(&pm->boot, info, sizeof(info)); | ||
163 | NV_INFO(dev, "c: %s", info); | ||
164 | } | ||
165 | |||
166 | /* initialise sysfs */ | ||
167 | ret = device_create_file(d, &dev_attr_performance_level); | ||
168 | if (ret) | ||
169 | return ret; | ||
170 | |||
171 | for (i = 0; i < pm->nr_perflvl; i++) { | ||
172 | struct nouveau_pm_level *perflvl = &pm->perflvl[i]; | ||
173 | |||
174 | perflvl->dev_attr.attr.name = perflvl->name; | ||
175 | perflvl->dev_attr.attr.mode = S_IRUGO; | ||
176 | perflvl->dev_attr.show = nouveau_pm_get_perflvl_info; | ||
177 | perflvl->dev_attr.store = NULL; | ||
178 | sysfs_attr_init(&perflvl->dev_attr.attr); | ||
179 | |||
180 | ret = device_create_file(d, &perflvl->dev_attr); | ||
181 | if (ret) { | ||
182 | NV_ERROR(dev, "failed pervlvl %d sysfs: %d\n", | ||
183 | perflvl->id, i); | ||
184 | perflvl->dev_attr.attr.name = NULL; | ||
185 | nouveau_pm_fini(dev); | ||
186 | return ret; | ||
187 | } | ||
188 | } | ||
189 | |||
190 | return 0; | ||
191 | } | ||
192 | |||
193 | void | ||
194 | nouveau_pm_fini(struct drm_device *dev) | ||
195 | { | ||
196 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
197 | struct nouveau_pm_engine *pm = &dev_priv->engine.pm; | ||
198 | struct device *d = &dev->pdev->dev; | ||
199 | int i; | ||
200 | |||
201 | device_remove_file(d, &dev_attr_performance_level); | ||
202 | for (i = 0; i < pm->nr_perflvl; i++) { | ||
203 | struct nouveau_pm_level *pl = &pm->perflvl[i]; | ||
204 | |||
205 | if (!pl->dev_attr.attr.name) | ||
206 | break; | ||
207 | |||
208 | device_remove_file(d, &pl->dev_attr); | ||
209 | } | ||
210 | |||
211 | nouveau_perf_fini(dev); | ||
212 | nouveau_volt_fini(dev); | ||
213 | } | ||
214 | |||
diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.h b/drivers/gpu/drm/nouveau/nouveau_pm.h new file mode 100644 index 000000000000..a401ec0a1269 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nouveau_pm.h | |||
@@ -0,0 +1,44 @@ | |||
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 | #ifndef __NOUVEAU_PM_H__ | ||
26 | #define __NOUVEAU_PM_H__ | ||
27 | |||
28 | /* nouveau_pm.c */ | ||
29 | int nouveau_pm_init(struct drm_device *dev); | ||
30 | void nouveau_pm_fini(struct drm_device *dev); | ||
31 | |||
32 | /* nouveau_volt.c */ | ||
33 | void nouveau_volt_init(struct drm_device *); | ||
34 | void nouveau_volt_fini(struct drm_device *); | ||
35 | int nouveau_volt_vid_lookup(struct drm_device *, int voltage); | ||
36 | int nouveau_volt_lvl_lookup(struct drm_device *, int vid); | ||
37 | int nouveau_voltage_gpio_get(struct drm_device *); | ||
38 | int nouveau_voltage_gpio_set(struct drm_device *, int voltage); | ||
39 | |||
40 | /* nouveau_perf.c */ | ||
41 | void nouveau_perf_init(struct drm_device *); | ||
42 | void nouveau_perf_fini(struct drm_device *); | ||
43 | |||
44 | #endif | ||
diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c index be859604cf64..18c4a8a85940 100644 --- a/drivers/gpu/drm/nouveau/nouveau_state.c +++ b/drivers/gpu/drm/nouveau/nouveau_state.c | |||
@@ -36,6 +36,7 @@ | |||
36 | #include "nouveau_drm.h" | 36 | #include "nouveau_drm.h" |
37 | #include "nouveau_fbcon.h" | 37 | #include "nouveau_fbcon.h" |
38 | #include "nouveau_ramht.h" | 38 | #include "nouveau_ramht.h" |
39 | #include "nouveau_pm.h" | ||
39 | #include "nv50_display.h" | 40 | #include "nv50_display.h" |
40 | 41 | ||
41 | static void nouveau_stub_takedown(struct drm_device *dev) {} | 42 | static void nouveau_stub_takedown(struct drm_device *dev) {} |
@@ -527,6 +528,8 @@ nouveau_card_init(struct drm_device *dev) | |||
527 | if (ret) | 528 | if (ret) |
528 | goto out_display_early; | 529 | goto out_display_early; |
529 | 530 | ||
531 | nouveau_pm_init(dev); | ||
532 | |||
530 | ret = nouveau_mem_vram_init(dev); | 533 | ret = nouveau_mem_vram_init(dev); |
531 | if (ret) | 534 | if (ret) |
532 | goto out_bios; | 535 | goto out_bios; |
@@ -635,6 +638,7 @@ out_gpuobj: | |||
635 | out_vram: | 638 | out_vram: |
636 | nouveau_mem_vram_fini(dev); | 639 | nouveau_mem_vram_fini(dev); |
637 | out_bios: | 640 | out_bios: |
641 | nouveau_pm_fini(dev); | ||
638 | nouveau_bios_takedown(dev); | 642 | nouveau_bios_takedown(dev); |
639 | out_display_early: | 643 | out_display_early: |
640 | engine->display.late_takedown(dev); | 644 | engine->display.late_takedown(dev); |
@@ -677,6 +681,7 @@ static void nouveau_card_takedown(struct drm_device *dev) | |||
677 | 681 | ||
678 | drm_irq_uninstall(dev); | 682 | drm_irq_uninstall(dev); |
679 | 683 | ||
684 | nouveau_pm_fini(dev); | ||
680 | nouveau_bios_takedown(dev); | 685 | nouveau_bios_takedown(dev); |
681 | 686 | ||
682 | vga_client_register(dev->pdev, NULL, NULL, NULL); | 687 | vga_client_register(dev->pdev, NULL, NULL, NULL); |
diff --git a/drivers/gpu/drm/nouveau/nouveau_volt.c b/drivers/gpu/drm/nouveau/nouveau_volt.c new file mode 100644 index 000000000000..6ce857688eb6 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nouveau_volt.c | |||
@@ -0,0 +1,209 @@ | |||
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 const enum dcb_gpio_tag vidtag[] = { 0x04, 0x05, 0x06, 0x1a }; | ||
31 | static int nr_vidtag = sizeof(vidtag) / sizeof(vidtag[0]); | ||
32 | |||
33 | int | ||
34 | nouveau_voltage_gpio_get(struct drm_device *dev) | ||
35 | { | ||
36 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
37 | struct nouveau_gpio_engine *gpio = &dev_priv->engine.gpio; | ||
38 | struct nouveau_pm_voltage *volt = &dev_priv->engine.pm.voltage; | ||
39 | u8 vid = 0; | ||
40 | int i; | ||
41 | |||
42 | for (i = 0; i < nr_vidtag; i++) { | ||
43 | if (!(volt->vid_mask & (1 << i))) | ||
44 | continue; | ||
45 | |||
46 | vid |= gpio->get(dev, vidtag[i]) << i; | ||
47 | } | ||
48 | |||
49 | return nouveau_volt_lvl_lookup(dev, vid); | ||
50 | } | ||
51 | |||
52 | int | ||
53 | nouveau_voltage_gpio_set(struct drm_device *dev, int voltage) | ||
54 | { | ||
55 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
56 | struct nouveau_gpio_engine *gpio = &dev_priv->engine.gpio; | ||
57 | struct nouveau_pm_voltage *volt = &dev_priv->engine.pm.voltage; | ||
58 | int vid, i; | ||
59 | |||
60 | vid = nouveau_volt_vid_lookup(dev, voltage); | ||
61 | if (vid < 0) | ||
62 | return vid; | ||
63 | |||
64 | for (i = 0; i < nr_vidtag; i++) { | ||
65 | if (!(volt->vid_mask & (1 << i))) | ||
66 | continue; | ||
67 | |||
68 | gpio->set(dev, vidtag[i], !!(vid & (1 << i))); | ||
69 | } | ||
70 | |||
71 | return 0; | ||
72 | } | ||
73 | |||
74 | int | ||
75 | nouveau_volt_vid_lookup(struct drm_device *dev, int voltage) | ||
76 | { | ||
77 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
78 | struct nouveau_pm_voltage *volt = &dev_priv->engine.pm.voltage; | ||
79 | int i; | ||
80 | |||
81 | for (i = 0; i < volt->nr_level; i++) { | ||
82 | if (volt->level[i].voltage == voltage) | ||
83 | return volt->level[i].vid; | ||
84 | } | ||
85 | |||
86 | return -ENOENT; | ||
87 | } | ||
88 | |||
89 | int | ||
90 | nouveau_volt_lvl_lookup(struct drm_device *dev, int vid) | ||
91 | { | ||
92 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
93 | struct nouveau_pm_voltage *volt = &dev_priv->engine.pm.voltage; | ||
94 | int i; | ||
95 | |||
96 | for (i = 0; i < volt->nr_level; i++) { | ||
97 | if (volt->level[i].vid == vid) | ||
98 | return volt->level[i].voltage; | ||
99 | } | ||
100 | |||
101 | return -ENOENT; | ||
102 | } | ||
103 | |||
104 | void | ||
105 | nouveau_volt_init(struct drm_device *dev) | ||
106 | { | ||
107 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
108 | struct nouveau_pm_engine *pm = &dev_priv->engine.pm; | ||
109 | struct nouveau_pm_voltage *voltage = &pm->voltage; | ||
110 | struct nvbios *bios = &dev_priv->vbios; | ||
111 | struct bit_entry P; | ||
112 | u8 *volt = NULL, *entry; | ||
113 | int i, recordlen, entries, vidmask, vidshift; | ||
114 | |||
115 | if (bios->type == NVBIOS_BIT) { | ||
116 | if (bit_table(dev, 'P', &P)) | ||
117 | return; | ||
118 | |||
119 | if (P.version == 1) | ||
120 | volt = ROMPTR(bios, P.data[16]); | ||
121 | else | ||
122 | if (P.version == 2) | ||
123 | volt = ROMPTR(bios, P.data[12]); | ||
124 | else { | ||
125 | NV_WARN(dev, "unknown volt for BIT P %d\n", P.version); | ||
126 | } | ||
127 | } else { | ||
128 | if (bios->data[bios->offset + 6] < 0x27) { | ||
129 | NV_DEBUG(dev, "BMP version too old for voltage\n"); | ||
130 | return; | ||
131 | } | ||
132 | |||
133 | volt = ROMPTR(bios, bios->data[bios->offset + 0x98]); | ||
134 | } | ||
135 | |||
136 | if (!volt) { | ||
137 | NV_DEBUG(dev, "voltage table pointer invalid\n"); | ||
138 | return; | ||
139 | } | ||
140 | |||
141 | switch (volt[0]) { | ||
142 | case 0x10: | ||
143 | case 0x11: | ||
144 | case 0x12: | ||
145 | recordlen = 5; | ||
146 | entries = volt[2]; | ||
147 | vidshift = 0; | ||
148 | vidmask = volt[4]; | ||
149 | break; | ||
150 | case 0x20: | ||
151 | recordlen = volt[3]; | ||
152 | entries = volt[2]; | ||
153 | vidshift = 0; /* could be vidshift like 0x30? */ | ||
154 | vidmask = volt[5]; | ||
155 | break; | ||
156 | case 0x30: | ||
157 | recordlen = volt[2]; | ||
158 | entries = volt[3]; | ||
159 | vidshift = hweight8(volt[5]); | ||
160 | vidmask = volt[4]; | ||
161 | break; | ||
162 | default: | ||
163 | NV_WARN(dev, "voltage table 0x%02x unknown\n", volt[0]); | ||
164 | return; | ||
165 | } | ||
166 | |||
167 | /* validate vid mask */ | ||
168 | voltage->vid_mask = vidmask; | ||
169 | if (!voltage->vid_mask) | ||
170 | return; | ||
171 | |||
172 | i = 0; | ||
173 | while (vidmask) { | ||
174 | if (i > nr_vidtag) { | ||
175 | NV_DEBUG(dev, "vid bit %d unknown\n", i); | ||
176 | return; | ||
177 | } | ||
178 | |||
179 | if (!nouveau_bios_gpio_entry(dev, vidtag[i])) { | ||
180 | NV_DEBUG(dev, "vid bit %d has no gpio tag\n", i); | ||
181 | return; | ||
182 | } | ||
183 | |||
184 | vidmask >>= 1; | ||
185 | i++; | ||
186 | } | ||
187 | |||
188 | /* parse vbios entries into common format */ | ||
189 | voltage->level = kcalloc(entries, sizeof(*voltage->level), GFP_KERNEL); | ||
190 | if (!voltage->level) | ||
191 | return; | ||
192 | |||
193 | entry = volt + volt[1]; | ||
194 | for (i = 0; i < entries; i++, entry += recordlen) { | ||
195 | voltage->level[i].voltage = entry[0]; | ||
196 | voltage->level[i].vid = entry[1] >> vidshift; | ||
197 | } | ||
198 | voltage->nr_level = entries; | ||
199 | voltage->supported = true; | ||
200 | } | ||
201 | |||
202 | void | ||
203 | nouveau_volt_fini(struct drm_device *dev) | ||
204 | { | ||
205 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
206 | struct nouveau_pm_voltage *volt = &dev_priv->engine.pm.voltage; | ||
207 | |||
208 | kfree(volt->level); | ||
209 | } | ||