]>
Commit | Line | Data |
---|---|---|
330c5988 BS |
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 | ||
6032649d BS |
30 | #ifdef CONFIG_ACPI |
31 | #include <linux/acpi.h> | |
32 | #endif | |
33 | #include <linux/power_supply.h> | |
34e9d85a MP |
34 | #include <linux/hwmon.h> |
35 | #include <linux/hwmon-sysfs.h> | |
36 | ||
6f876986 | 37 | static int |
5c6dc657 BS |
38 | nouveau_pm_clock_set(struct drm_device *dev, struct nouveau_pm_level *perflvl, |
39 | u8 id, u32 khz) | |
6f876986 BS |
40 | { |
41 | struct drm_nouveau_private *dev_priv = dev->dev_private; | |
42 | struct nouveau_pm_engine *pm = &dev_priv->engine.pm; | |
43 | void *pre_state; | |
44 | ||
45 | if (khz == 0) | |
46 | return 0; | |
47 | ||
5c6dc657 | 48 | pre_state = pm->clock_pre(dev, perflvl, id, khz); |
6f876986 BS |
49 | if (IS_ERR(pre_state)) |
50 | return PTR_ERR(pre_state); | |
51 | ||
52 | if (pre_state) | |
53 | pm->clock_set(dev, pre_state); | |
54 | return 0; | |
55 | } | |
56 | ||
64f1c11a BS |
57 | static int |
58 | nouveau_pm_perflvl_set(struct drm_device *dev, struct nouveau_pm_level *perflvl) | |
59 | { | |
60 | struct drm_nouveau_private *dev_priv = dev->dev_private; | |
61 | struct nouveau_pm_engine *pm = &dev_priv->engine.pm; | |
62 | int ret; | |
63 | ||
64 | if (perflvl == pm->cur) | |
65 | return 0; | |
66 | ||
67 | if (pm->voltage.supported && pm->voltage_set && perflvl->voltage) { | |
68 | ret = pm->voltage_set(dev, perflvl->voltage); | |
69 | if (ret) { | |
70 | NV_ERROR(dev, "voltage_set %d failed: %d\n", | |
71 | perflvl->voltage, ret); | |
72 | } | |
73 | } | |
74 | ||
5c6dc657 BS |
75 | nouveau_pm_clock_set(dev, perflvl, PLL_CORE, perflvl->core); |
76 | nouveau_pm_clock_set(dev, perflvl, PLL_SHADER, perflvl->shader); | |
77 | nouveau_pm_clock_set(dev, perflvl, PLL_MEMORY, perflvl->memory); | |
78 | nouveau_pm_clock_set(dev, perflvl, PLL_UNK05, perflvl->unk05); | |
64f1c11a BS |
79 | |
80 | pm->cur = perflvl; | |
81 | return 0; | |
82 | } | |
83 | ||
6f876986 BS |
84 | static int |
85 | nouveau_pm_profile_set(struct drm_device *dev, const char *profile) | |
86 | { | |
87 | struct drm_nouveau_private *dev_priv = dev->dev_private; | |
88 | struct nouveau_pm_engine *pm = &dev_priv->engine.pm; | |
89 | struct nouveau_pm_level *perflvl = NULL; | |
6f876986 BS |
90 | |
91 | /* safety precaution, for now */ | |
92 | if (nouveau_perflvl_wr != 7777) | |
93 | return -EPERM; | |
94 | ||
95 | if (!pm->clock_set) | |
96 | return -EINVAL; | |
97 | ||
98 | if (!strncmp(profile, "boot", 4)) | |
99 | perflvl = &pm->boot; | |
100 | else { | |
101 | int pl = simple_strtol(profile, NULL, 10); | |
102 | int i; | |
103 | ||
104 | for (i = 0; i < pm->nr_perflvl; i++) { | |
105 | if (pm->perflvl[i].id == pl) { | |
106 | perflvl = &pm->perflvl[i]; | |
107 | break; | |
108 | } | |
109 | } | |
110 | ||
111 | if (!perflvl) | |
112 | return -EINVAL; | |
113 | } | |
114 | ||
6f876986 | 115 | NV_INFO(dev, "setting performance level: %s\n", profile); |
64f1c11a | 116 | return nouveau_pm_perflvl_set(dev, perflvl); |
6f876986 BS |
117 | } |
118 | ||
330c5988 BS |
119 | static int |
120 | nouveau_pm_perflvl_get(struct drm_device *dev, struct nouveau_pm_level *perflvl) | |
121 | { | |
122 | struct drm_nouveau_private *dev_priv = dev->dev_private; | |
123 | struct nouveau_pm_engine *pm = &dev_priv->engine.pm; | |
124 | int ret; | |
125 | ||
126 | if (!pm->clock_get) | |
127 | return -EINVAL; | |
128 | ||
129 | memset(perflvl, 0, sizeof(*perflvl)); | |
130 | ||
131 | ret = pm->clock_get(dev, PLL_CORE); | |
132 | if (ret > 0) | |
133 | perflvl->core = ret; | |
134 | ||
135 | ret = pm->clock_get(dev, PLL_MEMORY); | |
136 | if (ret > 0) | |
137 | perflvl->memory = ret; | |
138 | ||
139 | ret = pm->clock_get(dev, PLL_SHADER); | |
140 | if (ret > 0) | |
141 | perflvl->shader = ret; | |
142 | ||
143 | ret = pm->clock_get(dev, PLL_UNK05); | |
144 | if (ret > 0) | |
145 | perflvl->unk05 = ret; | |
146 | ||
147 | if (pm->voltage.supported && pm->voltage_get) { | |
148 | ret = pm->voltage_get(dev); | |
149 | if (ret > 0) | |
150 | perflvl->voltage = ret; | |
151 | } | |
152 | ||
153 | return 0; | |
154 | } | |
155 | ||
156 | static void | |
157 | nouveau_pm_perflvl_info(struct nouveau_pm_level *perflvl, char *ptr, int len) | |
158 | { | |
e614b2e7 | 159 | char c[16], s[16], v[16], f[16], t[16]; |
0fbb114a FJ |
160 | |
161 | c[0] = '\0'; | |
162 | if (perflvl->core) | |
163 | snprintf(c, sizeof(c), " core %dMHz", perflvl->core / 1000); | |
330c5988 BS |
164 | |
165 | s[0] = '\0'; | |
166 | if (perflvl->shader) | |
167 | snprintf(s, sizeof(s), " shader %dMHz", perflvl->shader / 1000); | |
168 | ||
169 | v[0] = '\0'; | |
170 | if (perflvl->voltage) | |
c3450239 | 171 | snprintf(v, sizeof(v), " voltage %dmV", perflvl->voltage / 1000); |
330c5988 BS |
172 | |
173 | f[0] = '\0'; | |
174 | if (perflvl->fanspeed) | |
175 | snprintf(f, sizeof(f), " fanspeed %d%%", perflvl->fanspeed); | |
176 | ||
e614b2e7 MP |
177 | t[0] = '\0'; |
178 | if (perflvl->timing) | |
179 | snprintf(t, sizeof(t), " timing %d", perflvl->timing->id); | |
180 | ||
181 | snprintf(ptr, len, "memory %dMHz%s%s%s%s%s\n", perflvl->memory / 1000, | |
182 | c, s, v, f, t); | |
330c5988 BS |
183 | } |
184 | ||
185 | static ssize_t | |
186 | nouveau_pm_get_perflvl_info(struct device *d, | |
187 | struct device_attribute *a, char *buf) | |
188 | { | |
189 | struct nouveau_pm_level *perflvl = (struct nouveau_pm_level *)a; | |
190 | char *ptr = buf; | |
191 | int len = PAGE_SIZE; | |
192 | ||
193 | snprintf(ptr, len, "%d: ", perflvl->id); | |
194 | ptr += strlen(buf); | |
195 | len -= strlen(buf); | |
196 | ||
197 | nouveau_pm_perflvl_info(perflvl, ptr, len); | |
198 | return strlen(buf); | |
199 | } | |
200 | ||
201 | static ssize_t | |
202 | nouveau_pm_get_perflvl(struct device *d, struct device_attribute *a, char *buf) | |
203 | { | |
34e9d85a | 204 | struct drm_device *dev = pci_get_drvdata(to_pci_dev(d)); |
330c5988 BS |
205 | struct drm_nouveau_private *dev_priv = dev->dev_private; |
206 | struct nouveau_pm_engine *pm = &dev_priv->engine.pm; | |
207 | struct nouveau_pm_level cur; | |
208 | int len = PAGE_SIZE, ret; | |
209 | char *ptr = buf; | |
210 | ||
211 | if (!pm->cur) | |
212 | snprintf(ptr, len, "setting: boot\n"); | |
213 | else if (pm->cur == &pm->boot) | |
214 | snprintf(ptr, len, "setting: boot\nc: "); | |
215 | else | |
216 | snprintf(ptr, len, "setting: static %d\nc: ", pm->cur->id); | |
217 | ptr += strlen(buf); | |
218 | len -= strlen(buf); | |
219 | ||
220 | ret = nouveau_pm_perflvl_get(dev, &cur); | |
221 | if (ret == 0) | |
222 | nouveau_pm_perflvl_info(&cur, ptr, len); | |
223 | return strlen(buf); | |
224 | } | |
225 | ||
226 | static ssize_t | |
227 | nouveau_pm_set_perflvl(struct device *d, struct device_attribute *a, | |
228 | const char *buf, size_t count) | |
229 | { | |
34e9d85a | 230 | struct drm_device *dev = pci_get_drvdata(to_pci_dev(d)); |
6f876986 BS |
231 | int ret; |
232 | ||
233 | ret = nouveau_pm_profile_set(dev, buf); | |
234 | if (ret) | |
235 | return ret; | |
236 | return strlen(buf); | |
330c5988 BS |
237 | } |
238 | ||
5c4abd09 FJ |
239 | static DEVICE_ATTR(performance_level, S_IRUGO | S_IWUSR, |
240 | nouveau_pm_get_perflvl, nouveau_pm_set_perflvl); | |
330c5988 | 241 | |
34e9d85a MP |
242 | static int |
243 | nouveau_sysfs_init(struct drm_device *dev) | |
330c5988 BS |
244 | { |
245 | struct drm_nouveau_private *dev_priv = dev->dev_private; | |
246 | struct nouveau_pm_engine *pm = &dev_priv->engine.pm; | |
247 | struct device *d = &dev->pdev->dev; | |
330c5988 BS |
248 | int ret, i; |
249 | ||
330c5988 BS |
250 | ret = device_create_file(d, &dev_attr_performance_level); |
251 | if (ret) | |
252 | return ret; | |
253 | ||
254 | for (i = 0; i < pm->nr_perflvl; i++) { | |
255 | struct nouveau_pm_level *perflvl = &pm->perflvl[i]; | |
256 | ||
257 | perflvl->dev_attr.attr.name = perflvl->name; | |
258 | perflvl->dev_attr.attr.mode = S_IRUGO; | |
259 | perflvl->dev_attr.show = nouveau_pm_get_perflvl_info; | |
260 | perflvl->dev_attr.store = NULL; | |
261 | sysfs_attr_init(&perflvl->dev_attr.attr); | |
262 | ||
263 | ret = device_create_file(d, &perflvl->dev_attr); | |
264 | if (ret) { | |
265 | NV_ERROR(dev, "failed pervlvl %d sysfs: %d\n", | |
266 | perflvl->id, i); | |
267 | perflvl->dev_attr.attr.name = NULL; | |
268 | nouveau_pm_fini(dev); | |
269 | return ret; | |
270 | } | |
271 | } | |
272 | ||
273 | return 0; | |
274 | } | |
275 | ||
34e9d85a MP |
276 | static void |
277 | nouveau_sysfs_fini(struct drm_device *dev) | |
330c5988 BS |
278 | { |
279 | struct drm_nouveau_private *dev_priv = dev->dev_private; | |
280 | struct nouveau_pm_engine *pm = &dev_priv->engine.pm; | |
281 | struct device *d = &dev->pdev->dev; | |
282 | int i; | |
283 | ||
284 | device_remove_file(d, &dev_attr_performance_level); | |
285 | for (i = 0; i < pm->nr_perflvl; i++) { | |
286 | struct nouveau_pm_level *pl = &pm->perflvl[i]; | |
287 | ||
288 | if (!pl->dev_attr.attr.name) | |
289 | break; | |
290 | ||
291 | device_remove_file(d, &pl->dev_attr); | |
292 | } | |
34e9d85a MP |
293 | } |
294 | ||
b54262f3 | 295 | #ifdef CONFIG_HWMON |
34e9d85a MP |
296 | static ssize_t |
297 | nouveau_hwmon_show_temp(struct device *d, struct device_attribute *a, char *buf) | |
298 | { | |
299 | struct drm_device *dev = dev_get_drvdata(d); | |
8155cac4 FJ |
300 | struct drm_nouveau_private *dev_priv = dev->dev_private; |
301 | struct nouveau_pm_engine *pm = &dev_priv->engine.pm; | |
34e9d85a | 302 | |
8155cac4 | 303 | return snprintf(buf, PAGE_SIZE, "%d\n", pm->temp_get(dev)*1000); |
34e9d85a MP |
304 | } |
305 | static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, nouveau_hwmon_show_temp, | |
306 | NULL, 0); | |
307 | ||
308 | static ssize_t | |
309 | nouveau_hwmon_max_temp(struct device *d, struct device_attribute *a, char *buf) | |
310 | { | |
311 | struct drm_device *dev = dev_get_drvdata(d); | |
312 | struct drm_nouveau_private *dev_priv = dev->dev_private; | |
313 | struct nouveau_pm_engine *pm = &dev_priv->engine.pm; | |
314 | struct nouveau_pm_threshold_temp *temp = &pm->threshold_temp; | |
315 | ||
316 | return snprintf(buf, PAGE_SIZE, "%d\n", temp->down_clock*1000); | |
317 | } | |
318 | static ssize_t | |
319 | nouveau_hwmon_set_max_temp(struct device *d, struct device_attribute *a, | |
320 | const char *buf, size_t count) | |
321 | { | |
322 | struct drm_device *dev = dev_get_drvdata(d); | |
323 | struct drm_nouveau_private *dev_priv = dev->dev_private; | |
324 | struct nouveau_pm_engine *pm = &dev_priv->engine.pm; | |
325 | struct nouveau_pm_threshold_temp *temp = &pm->threshold_temp; | |
326 | long value; | |
327 | ||
5c4abd09 | 328 | if (strict_strtol(buf, 10, &value) == -EINVAL) |
34e9d85a MP |
329 | return count; |
330 | ||
331 | temp->down_clock = value/1000; | |
332 | ||
333 | nouveau_temp_safety_checks(dev); | |
334 | ||
335 | return count; | |
336 | } | |
337 | static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO | S_IWUSR, nouveau_hwmon_max_temp, | |
338 | nouveau_hwmon_set_max_temp, | |
339 | 0); | |
340 | ||
341 | static ssize_t | |
342 | nouveau_hwmon_critical_temp(struct device *d, struct device_attribute *a, | |
343 | char *buf) | |
344 | { | |
345 | struct drm_device *dev = dev_get_drvdata(d); | |
346 | struct drm_nouveau_private *dev_priv = dev->dev_private; | |
347 | struct nouveau_pm_engine *pm = &dev_priv->engine.pm; | |
348 | struct nouveau_pm_threshold_temp *temp = &pm->threshold_temp; | |
349 | ||
350 | return snprintf(buf, PAGE_SIZE, "%d\n", temp->critical*1000); | |
351 | } | |
352 | static ssize_t | |
353 | nouveau_hwmon_set_critical_temp(struct device *d, struct device_attribute *a, | |
354 | const char *buf, | |
355 | size_t count) | |
356 | { | |
357 | struct drm_device *dev = dev_get_drvdata(d); | |
358 | struct drm_nouveau_private *dev_priv = dev->dev_private; | |
359 | struct nouveau_pm_engine *pm = &dev_priv->engine.pm; | |
360 | struct nouveau_pm_threshold_temp *temp = &pm->threshold_temp; | |
361 | long value; | |
362 | ||
5c4abd09 | 363 | if (strict_strtol(buf, 10, &value) == -EINVAL) |
34e9d85a MP |
364 | return count; |
365 | ||
366 | temp->critical = value/1000; | |
367 | ||
368 | nouveau_temp_safety_checks(dev); | |
369 | ||
370 | return count; | |
371 | } | |
372 | static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO | S_IWUSR, | |
373 | nouveau_hwmon_critical_temp, | |
374 | nouveau_hwmon_set_critical_temp, | |
375 | 0); | |
376 | ||
377 | static ssize_t nouveau_hwmon_show_name(struct device *dev, | |
378 | struct device_attribute *attr, | |
379 | char *buf) | |
380 | { | |
381 | return sprintf(buf, "nouveau\n"); | |
382 | } | |
383 | static SENSOR_DEVICE_ATTR(name, S_IRUGO, nouveau_hwmon_show_name, NULL, 0); | |
384 | ||
385 | static ssize_t nouveau_hwmon_show_update_rate(struct device *dev, | |
386 | struct device_attribute *attr, | |
387 | char *buf) | |
388 | { | |
389 | return sprintf(buf, "1000\n"); | |
390 | } | |
391 | static SENSOR_DEVICE_ATTR(update_rate, S_IRUGO, | |
392 | nouveau_hwmon_show_update_rate, | |
393 | NULL, 0); | |
394 | ||
395 | static struct attribute *hwmon_attributes[] = { | |
396 | &sensor_dev_attr_temp1_input.dev_attr.attr, | |
397 | &sensor_dev_attr_temp1_max.dev_attr.attr, | |
398 | &sensor_dev_attr_temp1_crit.dev_attr.attr, | |
399 | &sensor_dev_attr_name.dev_attr.attr, | |
400 | &sensor_dev_attr_update_rate.dev_attr.attr, | |
401 | NULL | |
402 | }; | |
403 | ||
404 | static const struct attribute_group hwmon_attrgroup = { | |
405 | .attrs = hwmon_attributes, | |
406 | }; | |
b54262f3 | 407 | #endif |
34e9d85a MP |
408 | |
409 | static int | |
410 | nouveau_hwmon_init(struct drm_device *dev) | |
411 | { | |
b54262f3 | 412 | #ifdef CONFIG_HWMON |
34e9d85a | 413 | struct drm_nouveau_private *dev_priv = dev->dev_private; |
8155cac4 | 414 | struct nouveau_pm_engine *pm = &dev_priv->engine.pm; |
34e9d85a MP |
415 | struct device *hwmon_dev; |
416 | int ret; | |
417 | ||
8155cac4 FJ |
418 | if (!pm->temp_get) |
419 | return -ENODEV; | |
34e9d85a MP |
420 | |
421 | hwmon_dev = hwmon_device_register(&dev->pdev->dev); | |
422 | if (IS_ERR(hwmon_dev)) { | |
423 | ret = PTR_ERR(hwmon_dev); | |
424 | NV_ERROR(dev, | |
425 | "Unable to register hwmon device: %d\n", ret); | |
426 | return ret; | |
427 | } | |
428 | dev_set_drvdata(hwmon_dev, dev); | |
07cfe0e7 | 429 | ret = sysfs_create_group(&dev->pdev->dev.kobj, &hwmon_attrgroup); |
34e9d85a MP |
430 | if (ret) { |
431 | NV_ERROR(dev, | |
432 | "Unable to create hwmon sysfs file: %d\n", ret); | |
433 | hwmon_device_unregister(hwmon_dev); | |
434 | return ret; | |
435 | } | |
436 | ||
8155cac4 | 437 | pm->hwmon = hwmon_dev; |
b54262f3 | 438 | #endif |
34e9d85a MP |
439 | return 0; |
440 | } | |
441 | ||
442 | static void | |
443 | nouveau_hwmon_fini(struct drm_device *dev) | |
444 | { | |
b54262f3 | 445 | #ifdef CONFIG_HWMON |
34e9d85a | 446 | struct drm_nouveau_private *dev_priv = dev->dev_private; |
8155cac4 | 447 | struct nouveau_pm_engine *pm = &dev_priv->engine.pm; |
34e9d85a | 448 | |
8155cac4 | 449 | if (pm->hwmon) { |
8c06a3e0 | 450 | sysfs_remove_group(&dev->pdev->dev.kobj, &hwmon_attrgroup); |
8155cac4 | 451 | hwmon_device_unregister(pm->hwmon); |
34e9d85a | 452 | } |
b54262f3 | 453 | #endif |
34e9d85a MP |
454 | } |
455 | ||
1f962797 | 456 | #if defined(CONFIG_ACPI) && defined(CONFIG_POWER_SUPPLY) |
6032649d BS |
457 | static int |
458 | nouveau_pm_acpi_event(struct notifier_block *nb, unsigned long val, void *data) | |
459 | { | |
460 | struct drm_nouveau_private *dev_priv = | |
461 | container_of(nb, struct drm_nouveau_private, engine.pm.acpi_nb); | |
462 | struct drm_device *dev = dev_priv->dev; | |
463 | struct acpi_bus_event *entry = (struct acpi_bus_event *)data; | |
464 | ||
465 | if (strcmp(entry->device_class, "ac_adapter") == 0) { | |
466 | bool ac = power_supply_is_system_supplied(); | |
467 | ||
468 | NV_DEBUG(dev, "power supply changed: %s\n", ac ? "AC" : "DC"); | |
469 | } | |
470 | ||
471 | return NOTIFY_OK; | |
472 | } | |
473 | #endif | |
474 | ||
34e9d85a MP |
475 | int |
476 | nouveau_pm_init(struct drm_device *dev) | |
477 | { | |
478 | struct drm_nouveau_private *dev_priv = dev->dev_private; | |
479 | struct nouveau_pm_engine *pm = &dev_priv->engine.pm; | |
480 | char info[256]; | |
481 | int ret, i; | |
482 | ||
e614b2e7 | 483 | nouveau_mem_timing_init(dev); |
34e9d85a MP |
484 | nouveau_volt_init(dev); |
485 | nouveau_perf_init(dev); | |
486 | nouveau_temp_init(dev); | |
487 | ||
488 | NV_INFO(dev, "%d available performance level(s)\n", pm->nr_perflvl); | |
489 | for (i = 0; i < pm->nr_perflvl; i++) { | |
490 | nouveau_pm_perflvl_info(&pm->perflvl[i], info, sizeof(info)); | |
491 | NV_INFO(dev, "%d: %s", pm->perflvl[i].id, info); | |
492 | } | |
493 | ||
494 | /* determine current ("boot") performance level */ | |
495 | ret = nouveau_pm_perflvl_get(dev, &pm->boot); | |
496 | if (ret == 0) { | |
01e542c6 | 497 | strncpy(pm->boot.name, "boot", 4); |
34e9d85a MP |
498 | pm->cur = &pm->boot; |
499 | ||
500 | nouveau_pm_perflvl_info(&pm->boot, info, sizeof(info)); | |
501 | NV_INFO(dev, "c: %s", info); | |
502 | } | |
503 | ||
504 | /* switch performance levels now if requested */ | |
505 | if (nouveau_perflvl != NULL) { | |
506 | ret = nouveau_pm_profile_set(dev, nouveau_perflvl); | |
507 | if (ret) { | |
508 | NV_ERROR(dev, "error setting perflvl \"%s\": %d\n", | |
509 | nouveau_perflvl, ret); | |
510 | } | |
511 | } | |
512 | ||
513 | nouveau_sysfs_init(dev); | |
514 | nouveau_hwmon_init(dev); | |
1f962797 | 515 | #if defined(CONFIG_ACPI) && defined(CONFIG_POWER_SUPPLY) |
6032649d BS |
516 | pm->acpi_nb.notifier_call = nouveau_pm_acpi_event; |
517 | register_acpi_notifier(&pm->acpi_nb); | |
518 | #endif | |
34e9d85a MP |
519 | |
520 | return 0; | |
521 | } | |
522 | ||
523 | void | |
524 | nouveau_pm_fini(struct drm_device *dev) | |
525 | { | |
526 | struct drm_nouveau_private *dev_priv = dev->dev_private; | |
527 | struct nouveau_pm_engine *pm = &dev_priv->engine.pm; | |
528 | ||
529 | if (pm->cur != &pm->boot) | |
530 | nouveau_pm_perflvl_set(dev, &pm->boot); | |
330c5988 | 531 | |
7760fcb0 | 532 | nouveau_temp_fini(dev); |
330c5988 BS |
533 | nouveau_perf_fini(dev); |
534 | nouveau_volt_fini(dev); | |
e614b2e7 | 535 | nouveau_mem_timing_fini(dev); |
34e9d85a | 536 | |
1f962797 | 537 | #if defined(CONFIG_ACPI) && defined(CONFIG_POWER_SUPPLY) |
6032649d BS |
538 | unregister_acpi_notifier(&pm->acpi_nb); |
539 | #endif | |
34e9d85a MP |
540 | nouveau_hwmon_fini(dev); |
541 | nouveau_sysfs_fini(dev); | |
330c5988 BS |
542 | } |
543 | ||
64f1c11a BS |
544 | void |
545 | nouveau_pm_resume(struct drm_device *dev) | |
546 | { | |
547 | struct drm_nouveau_private *dev_priv = dev->dev_private; | |
548 | struct nouveau_pm_engine *pm = &dev_priv->engine.pm; | |
549 | struct nouveau_pm_level *perflvl; | |
550 | ||
317495b2 | 551 | if (!pm->cur || pm->cur == &pm->boot) |
64f1c11a BS |
552 | return; |
553 | ||
554 | perflvl = pm->cur; | |
555 | pm->cur = &pm->boot; | |
556 | nouveau_pm_perflvl_set(dev, perflvl); | |
557 | } |