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 /drivers/gpu/drm/nouveau/nouveau_pm.c | |
parent | 4709bff02adcb0d05d2d1a397e60581baa562de9 (diff) |
drm/nouveau: import initial work on vbios performance table parsing
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
Diffstat (limited to 'drivers/gpu/drm/nouveau/nouveau_pm.c')
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_pm.c | 214 |
1 files changed, 214 insertions, 0 deletions
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 | |||