blob: 607e4965f4ffc9dfaa81b730c596a2962202efd1 [file] [log] [blame]
Ben Skeggs330c5982010-09-16 15:39:49 +10001/*
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
Ben Skeggs60326492010-10-12 12:31:32 +100030#ifdef CONFIG_ACPI
31#include <linux/acpi.h>
32#endif
33#include <linux/power_supply.h>
Martin Peres34e9d852010-09-22 20:54:22 +020034#include <linux/hwmon.h>
35#include <linux/hwmon-sysfs.h>
36
Ben Skeggs330c5982010-09-16 15:39:49 +100037static int
Ben Skeggs5c6dc652010-09-27 09:47:56 +100038nouveau_pm_clock_set(struct drm_device *dev, struct nouveau_pm_level *perflvl,
39 u8 id, u32 khz)
Ben Skeggs6f876982010-09-16 16:47:14 +100040{
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
Ben Skeggs5c6dc652010-09-27 09:47:56 +100048 pre_state = pm->clock_pre(dev, perflvl, id, khz);
Ben Skeggs6f876982010-09-16 16:47:14 +100049 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
57static int
Ben Skeggs64f1c112010-09-17 13:35:25 +100058nouveau_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
Ben Skeggs771e1032011-07-28 11:01:21 +100067 if (pm->fanspeed_set && perflvl->fanspeed) {
68 ret = pm->fanspeed_set(dev, perflvl->fanspeed);
69 if (ret)
70 NV_ERROR(dev, "set fanspeed failed: %d\n", ret);
71 }
72
Ben Skeggs3b5565d2011-06-09 16:57:07 +100073 if (pm->voltage.supported && pm->voltage_set && perflvl->volt_min) {
74 ret = pm->voltage_set(dev, perflvl->volt_min);
Ben Skeggs64f1c112010-09-17 13:35:25 +100075 if (ret) {
76 NV_ERROR(dev, "voltage_set %d failed: %d\n",
Ben Skeggs3b5565d2011-06-09 16:57:07 +100077 perflvl->volt_min, ret);
Ben Skeggs64f1c112010-09-17 13:35:25 +100078 }
79 }
80
Ben Skeggs77e7da62011-06-17 11:25:57 +100081 if (pm->clocks_pre) {
82 void *state = pm->clocks_pre(dev, perflvl);
83 if (IS_ERR(state))
84 return PTR_ERR(state);
85 pm->clocks_set(dev, state);
86 } else
Ben Skeggsda1dc4c2011-06-10 12:07:09 +100087 if (pm->clock_set) {
88 nouveau_pm_clock_set(dev, perflvl, PLL_CORE, perflvl->core);
89 nouveau_pm_clock_set(dev, perflvl, PLL_SHADER, perflvl->shader);
90 nouveau_pm_clock_set(dev, perflvl, PLL_MEMORY, perflvl->memory);
91 nouveau_pm_clock_set(dev, perflvl, PLL_UNK05, perflvl->unk05);
92 }
Ben Skeggs64f1c112010-09-17 13:35:25 +100093
94 pm->cur = perflvl;
95 return 0;
96}
97
98static int
Ben Skeggs6f876982010-09-16 16:47:14 +100099nouveau_pm_profile_set(struct drm_device *dev, const char *profile)
100{
101 struct drm_nouveau_private *dev_priv = dev->dev_private;
102 struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
103 struct nouveau_pm_level *perflvl = NULL;
Ben Skeggs6f876982010-09-16 16:47:14 +1000104
105 /* safety precaution, for now */
106 if (nouveau_perflvl_wr != 7777)
107 return -EPERM;
108
Ben Skeggs6f876982010-09-16 16:47:14 +1000109 if (!strncmp(profile, "boot", 4))
110 perflvl = &pm->boot;
111 else {
112 int pl = simple_strtol(profile, NULL, 10);
113 int i;
114
115 for (i = 0; i < pm->nr_perflvl; i++) {
116 if (pm->perflvl[i].id == pl) {
117 perflvl = &pm->perflvl[i];
118 break;
119 }
120 }
121
122 if (!perflvl)
123 return -EINVAL;
124 }
125
Ben Skeggs6f876982010-09-16 16:47:14 +1000126 NV_INFO(dev, "setting performance level: %s\n", profile);
Ben Skeggs64f1c112010-09-17 13:35:25 +1000127 return nouveau_pm_perflvl_set(dev, perflvl);
Ben Skeggs6f876982010-09-16 16:47:14 +1000128}
129
130static int
Ben Skeggs330c5982010-09-16 15:39:49 +1000131nouveau_pm_perflvl_get(struct drm_device *dev, struct nouveau_pm_level *perflvl)
132{
133 struct drm_nouveau_private *dev_priv = dev->dev_private;
134 struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
135 int ret;
136
Ben Skeggs330c5982010-09-16 15:39:49 +1000137 memset(perflvl, 0, sizeof(*perflvl));
138
Ben Skeggs77e7da62011-06-17 11:25:57 +1000139 if (pm->clocks_get) {
140 ret = pm->clocks_get(dev, perflvl);
141 if (ret)
142 return ret;
143 } else
Ben Skeggs93dccbe2011-06-09 17:27:47 +1000144 if (pm->clock_get) {
145 ret = pm->clock_get(dev, PLL_CORE);
146 if (ret > 0)
147 perflvl->core = ret;
Ben Skeggs330c5982010-09-16 15:39:49 +1000148
Ben Skeggs93dccbe2011-06-09 17:27:47 +1000149 ret = pm->clock_get(dev, PLL_MEMORY);
150 if (ret > 0)
151 perflvl->memory = ret;
Ben Skeggs330c5982010-09-16 15:39:49 +1000152
Ben Skeggs93dccbe2011-06-09 17:27:47 +1000153 ret = pm->clock_get(dev, PLL_SHADER);
154 if (ret > 0)
155 perflvl->shader = ret;
Ben Skeggs330c5982010-09-16 15:39:49 +1000156
Ben Skeggs93dccbe2011-06-09 17:27:47 +1000157 ret = pm->clock_get(dev, PLL_UNK05);
158 if (ret > 0)
159 perflvl->unk05 = ret;
160 }
Ben Skeggs330c5982010-09-16 15:39:49 +1000161
162 if (pm->voltage.supported && pm->voltage_get) {
163 ret = pm->voltage_get(dev);
Ben Skeggs3b5565d2011-06-09 16:57:07 +1000164 if (ret > 0) {
165 perflvl->volt_min = ret;
166 perflvl->volt_max = ret;
167 }
Ben Skeggs330c5982010-09-16 15:39:49 +1000168 }
169
Ben Skeggs771e1032011-07-28 11:01:21 +1000170 if (pm->fanspeed_get)
171 perflvl->fanspeed = pm->fanspeed_get(dev);
172
Ben Skeggs330c5982010-09-16 15:39:49 +1000173 return 0;
174}
175
176static void
177nouveau_pm_perflvl_info(struct nouveau_pm_level *perflvl, char *ptr, int len)
178{
Ben Skeggs93dccbe2011-06-09 17:27:47 +1000179 char c[16], s[16], v[32], f[16], t[16], m[16];
Francisco Jerez0fbb1142010-09-20 16:18:28 +0200180
181 c[0] = '\0';
182 if (perflvl->core)
183 snprintf(c, sizeof(c), " core %dMHz", perflvl->core / 1000);
Ben Skeggs330c5982010-09-16 15:39:49 +1000184
185 s[0] = '\0';
186 if (perflvl->shader)
187 snprintf(s, sizeof(s), " shader %dMHz", perflvl->shader / 1000);
188
Ben Skeggs93dccbe2011-06-09 17:27:47 +1000189 m[0] = '\0';
190 if (perflvl->memory)
191 snprintf(m, sizeof(m), " memory %dMHz", perflvl->memory / 1000);
192
Ben Skeggs330c5982010-09-16 15:39:49 +1000193 v[0] = '\0';
Ben Skeggs3b5565d2011-06-09 16:57:07 +1000194 if (perflvl->volt_min && perflvl->volt_min != perflvl->volt_max) {
195 snprintf(v, sizeof(v), " voltage %dmV-%dmV",
196 perflvl->volt_min / 1000, perflvl->volt_max / 1000);
197 } else
198 if (perflvl->volt_min) {
199 snprintf(v, sizeof(v), " voltage %dmV",
200 perflvl->volt_min / 1000);
201 }
Ben Skeggs330c5982010-09-16 15:39:49 +1000202
203 f[0] = '\0';
204 if (perflvl->fanspeed)
205 snprintf(f, sizeof(f), " fanspeed %d%%", perflvl->fanspeed);
206
Martin Perese614b2e2011-04-14 00:46:19 +0200207 t[0] = '\0';
208 if (perflvl->timing)
209 snprintf(t, sizeof(t), " timing %d", perflvl->timing->id);
210
Ben Skeggs93dccbe2011-06-09 17:27:47 +1000211 snprintf(ptr, len, "%s%s%s%s%s%s\n", c, s, m, t, v, f);
Ben Skeggs330c5982010-09-16 15:39:49 +1000212}
213
214static ssize_t
215nouveau_pm_get_perflvl_info(struct device *d,
216 struct device_attribute *a, char *buf)
217{
218 struct nouveau_pm_level *perflvl = (struct nouveau_pm_level *)a;
219 char *ptr = buf;
220 int len = PAGE_SIZE;
221
Ben Skeggs93dccbe2011-06-09 17:27:47 +1000222 snprintf(ptr, len, "%d:", perflvl->id);
Ben Skeggs330c5982010-09-16 15:39:49 +1000223 ptr += strlen(buf);
224 len -= strlen(buf);
225
226 nouveau_pm_perflvl_info(perflvl, ptr, len);
227 return strlen(buf);
228}
229
230static ssize_t
231nouveau_pm_get_perflvl(struct device *d, struct device_attribute *a, char *buf)
232{
Martin Peres34e9d852010-09-22 20:54:22 +0200233 struct drm_device *dev = pci_get_drvdata(to_pci_dev(d));
Ben Skeggs330c5982010-09-16 15:39:49 +1000234 struct drm_nouveau_private *dev_priv = dev->dev_private;
235 struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
236 struct nouveau_pm_level cur;
237 int len = PAGE_SIZE, ret;
238 char *ptr = buf;
239
240 if (!pm->cur)
241 snprintf(ptr, len, "setting: boot\n");
242 else if (pm->cur == &pm->boot)
Ben Skeggs93dccbe2011-06-09 17:27:47 +1000243 snprintf(ptr, len, "setting: boot\nc:");
Ben Skeggs330c5982010-09-16 15:39:49 +1000244 else
Ben Skeggs93dccbe2011-06-09 17:27:47 +1000245 snprintf(ptr, len, "setting: static %d\nc:", pm->cur->id);
Ben Skeggs330c5982010-09-16 15:39:49 +1000246 ptr += strlen(buf);
247 len -= strlen(buf);
248
249 ret = nouveau_pm_perflvl_get(dev, &cur);
250 if (ret == 0)
251 nouveau_pm_perflvl_info(&cur, ptr, len);
252 return strlen(buf);
253}
254
255static ssize_t
256nouveau_pm_set_perflvl(struct device *d, struct device_attribute *a,
257 const char *buf, size_t count)
258{
Martin Peres34e9d852010-09-22 20:54:22 +0200259 struct drm_device *dev = pci_get_drvdata(to_pci_dev(d));
Ben Skeggs6f876982010-09-16 16:47:14 +1000260 int ret;
261
262 ret = nouveau_pm_profile_set(dev, buf);
263 if (ret)
264 return ret;
265 return strlen(buf);
Ben Skeggs330c5982010-09-16 15:39:49 +1000266}
267
Francisco Jerez5c4abd02010-09-23 20:36:42 +0200268static DEVICE_ATTR(performance_level, S_IRUGO | S_IWUSR,
269 nouveau_pm_get_perflvl, nouveau_pm_set_perflvl);
Ben Skeggs330c5982010-09-16 15:39:49 +1000270
Martin Peres34e9d852010-09-22 20:54:22 +0200271static int
272nouveau_sysfs_init(struct drm_device *dev)
Ben Skeggs330c5982010-09-16 15:39:49 +1000273{
274 struct drm_nouveau_private *dev_priv = dev->dev_private;
275 struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
276 struct device *d = &dev->pdev->dev;
Ben Skeggs330c5982010-09-16 15:39:49 +1000277 int ret, i;
278
Ben Skeggs330c5982010-09-16 15:39:49 +1000279 ret = device_create_file(d, &dev_attr_performance_level);
280 if (ret)
281 return ret;
282
283 for (i = 0; i < pm->nr_perflvl; i++) {
284 struct nouveau_pm_level *perflvl = &pm->perflvl[i];
285
286 perflvl->dev_attr.attr.name = perflvl->name;
287 perflvl->dev_attr.attr.mode = S_IRUGO;
288 perflvl->dev_attr.show = nouveau_pm_get_perflvl_info;
289 perflvl->dev_attr.store = NULL;
290 sysfs_attr_init(&perflvl->dev_attr.attr);
291
292 ret = device_create_file(d, &perflvl->dev_attr);
293 if (ret) {
294 NV_ERROR(dev, "failed pervlvl %d sysfs: %d\n",
295 perflvl->id, i);
296 perflvl->dev_attr.attr.name = NULL;
297 nouveau_pm_fini(dev);
298 return ret;
299 }
300 }
301
302 return 0;
303}
304
Martin Peres34e9d852010-09-22 20:54:22 +0200305static void
306nouveau_sysfs_fini(struct drm_device *dev)
Ben Skeggs330c5982010-09-16 15:39:49 +1000307{
308 struct drm_nouveau_private *dev_priv = dev->dev_private;
309 struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
310 struct device *d = &dev->pdev->dev;
311 int i;
312
313 device_remove_file(d, &dev_attr_performance_level);
314 for (i = 0; i < pm->nr_perflvl; i++) {
315 struct nouveau_pm_level *pl = &pm->perflvl[i];
316
317 if (!pl->dev_attr.attr.name)
318 break;
319
320 device_remove_file(d, &pl->dev_attr);
321 }
Martin Peres34e9d852010-09-22 20:54:22 +0200322}
323
Ken Milmore658e86e2011-07-03 19:54:28 +0100324#if defined(CONFIG_HWMON) || (defined(MODULE) && defined(CONFIG_HWMON_MODULE))
Martin Peres34e9d852010-09-22 20:54:22 +0200325static ssize_t
326nouveau_hwmon_show_temp(struct device *d, struct device_attribute *a, char *buf)
327{
328 struct drm_device *dev = dev_get_drvdata(d);
Francisco Jerez8155cac2010-09-23 20:58:38 +0200329 struct drm_nouveau_private *dev_priv = dev->dev_private;
330 struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
Martin Peres34e9d852010-09-22 20:54:22 +0200331
Francisco Jerez8155cac2010-09-23 20:58:38 +0200332 return snprintf(buf, PAGE_SIZE, "%d\n", pm->temp_get(dev)*1000);
Martin Peres34e9d852010-09-22 20:54:22 +0200333}
334static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, nouveau_hwmon_show_temp,
335 NULL, 0);
336
337static ssize_t
338nouveau_hwmon_max_temp(struct device *d, struct device_attribute *a, char *buf)
339{
340 struct drm_device *dev = dev_get_drvdata(d);
341 struct drm_nouveau_private *dev_priv = dev->dev_private;
342 struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
343 struct nouveau_pm_threshold_temp *temp = &pm->threshold_temp;
344
345 return snprintf(buf, PAGE_SIZE, "%d\n", temp->down_clock*1000);
346}
347static ssize_t
348nouveau_hwmon_set_max_temp(struct device *d, struct device_attribute *a,
349 const char *buf, size_t count)
350{
351 struct drm_device *dev = dev_get_drvdata(d);
352 struct drm_nouveau_private *dev_priv = dev->dev_private;
353 struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
354 struct nouveau_pm_threshold_temp *temp = &pm->threshold_temp;
355 long value;
356
Francisco Jerez5c4abd02010-09-23 20:36:42 +0200357 if (strict_strtol(buf, 10, &value) == -EINVAL)
Martin Peres34e9d852010-09-22 20:54:22 +0200358 return count;
359
360 temp->down_clock = value/1000;
361
362 nouveau_temp_safety_checks(dev);
363
364 return count;
365}
366static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO | S_IWUSR, nouveau_hwmon_max_temp,
367 nouveau_hwmon_set_max_temp,
368 0);
369
370static ssize_t
371nouveau_hwmon_critical_temp(struct device *d, struct device_attribute *a,
372 char *buf)
373{
374 struct drm_device *dev = dev_get_drvdata(d);
375 struct drm_nouveau_private *dev_priv = dev->dev_private;
376 struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
377 struct nouveau_pm_threshold_temp *temp = &pm->threshold_temp;
378
379 return snprintf(buf, PAGE_SIZE, "%d\n", temp->critical*1000);
380}
381static ssize_t
382nouveau_hwmon_set_critical_temp(struct device *d, struct device_attribute *a,
383 const char *buf,
384 size_t count)
385{
386 struct drm_device *dev = dev_get_drvdata(d);
387 struct drm_nouveau_private *dev_priv = dev->dev_private;
388 struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
389 struct nouveau_pm_threshold_temp *temp = &pm->threshold_temp;
390 long value;
391
Francisco Jerez5c4abd02010-09-23 20:36:42 +0200392 if (strict_strtol(buf, 10, &value) == -EINVAL)
Martin Peres34e9d852010-09-22 20:54:22 +0200393 return count;
394
395 temp->critical = value/1000;
396
397 nouveau_temp_safety_checks(dev);
398
399 return count;
400}
401static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO | S_IWUSR,
402 nouveau_hwmon_critical_temp,
403 nouveau_hwmon_set_critical_temp,
404 0);
405
406static ssize_t nouveau_hwmon_show_name(struct device *dev,
407 struct device_attribute *attr,
408 char *buf)
409{
410 return sprintf(buf, "nouveau\n");
411}
412static SENSOR_DEVICE_ATTR(name, S_IRUGO, nouveau_hwmon_show_name, NULL, 0);
413
414static ssize_t nouveau_hwmon_show_update_rate(struct device *dev,
415 struct device_attribute *attr,
416 char *buf)
417{
418 return sprintf(buf, "1000\n");
419}
420static SENSOR_DEVICE_ATTR(update_rate, S_IRUGO,
421 nouveau_hwmon_show_update_rate,
422 NULL, 0);
423
424static struct attribute *hwmon_attributes[] = {
425 &sensor_dev_attr_temp1_input.dev_attr.attr,
426 &sensor_dev_attr_temp1_max.dev_attr.attr,
427 &sensor_dev_attr_temp1_crit.dev_attr.attr,
428 &sensor_dev_attr_name.dev_attr.attr,
429 &sensor_dev_attr_update_rate.dev_attr.attr,
430 NULL
431};
432
433static const struct attribute_group hwmon_attrgroup = {
434 .attrs = hwmon_attributes,
435};
Martin Peresb54262f2010-10-26 12:48:28 +0200436#endif
Martin Peres34e9d852010-09-22 20:54:22 +0200437
438static int
439nouveau_hwmon_init(struct drm_device *dev)
440{
Ken Milmore658e86e2011-07-03 19:54:28 +0100441#if defined(CONFIG_HWMON) || (defined(MODULE) && defined(CONFIG_HWMON_MODULE))
Martin Peres34e9d852010-09-22 20:54:22 +0200442 struct drm_nouveau_private *dev_priv = dev->dev_private;
Francisco Jerez8155cac2010-09-23 20:58:38 +0200443 struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
Martin Peres34e9d852010-09-22 20:54:22 +0200444 struct device *hwmon_dev;
445 int ret;
446
Francisco Jerez8155cac2010-09-23 20:58:38 +0200447 if (!pm->temp_get)
448 return -ENODEV;
Martin Peres34e9d852010-09-22 20:54:22 +0200449
450 hwmon_dev = hwmon_device_register(&dev->pdev->dev);
451 if (IS_ERR(hwmon_dev)) {
452 ret = PTR_ERR(hwmon_dev);
453 NV_ERROR(dev,
454 "Unable to register hwmon device: %d\n", ret);
455 return ret;
456 }
457 dev_set_drvdata(hwmon_dev, dev);
Lucas Stach07cfe0e2011-01-06 20:29:53 +0100458 ret = sysfs_create_group(&dev->pdev->dev.kobj, &hwmon_attrgroup);
Martin Peres34e9d852010-09-22 20:54:22 +0200459 if (ret) {
460 NV_ERROR(dev,
461 "Unable to create hwmon sysfs file: %d\n", ret);
462 hwmon_device_unregister(hwmon_dev);
463 return ret;
464 }
465
Francisco Jerez8155cac2010-09-23 20:58:38 +0200466 pm->hwmon = hwmon_dev;
Martin Peresb54262f2010-10-26 12:48:28 +0200467#endif
Martin Peres34e9d852010-09-22 20:54:22 +0200468 return 0;
469}
470
471static void
472nouveau_hwmon_fini(struct drm_device *dev)
473{
Ken Milmore658e86e2011-07-03 19:54:28 +0100474#if defined(CONFIG_HWMON) || (defined(MODULE) && defined(CONFIG_HWMON_MODULE))
Martin Peres34e9d852010-09-22 20:54:22 +0200475 struct drm_nouveau_private *dev_priv = dev->dev_private;
Francisco Jerez8155cac2010-09-23 20:58:38 +0200476 struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
Martin Peres34e9d852010-09-22 20:54:22 +0200477
Francisco Jerez8155cac2010-09-23 20:58:38 +0200478 if (pm->hwmon) {
Lucas Stach8c06a3e2011-01-30 10:54:11 +0100479 sysfs_remove_group(&dev->pdev->dev.kobj, &hwmon_attrgroup);
Francisco Jerez8155cac2010-09-23 20:58:38 +0200480 hwmon_device_unregister(pm->hwmon);
Martin Peres34e9d852010-09-22 20:54:22 +0200481 }
Martin Peresb54262f2010-10-26 12:48:28 +0200482#endif
Martin Peres34e9d852010-09-22 20:54:22 +0200483}
484
Martin Peres1f962792011-04-12 00:55:44 +0200485#if defined(CONFIG_ACPI) && defined(CONFIG_POWER_SUPPLY)
Ben Skeggs60326492010-10-12 12:31:32 +1000486static int
487nouveau_pm_acpi_event(struct notifier_block *nb, unsigned long val, void *data)
488{
489 struct drm_nouveau_private *dev_priv =
490 container_of(nb, struct drm_nouveau_private, engine.pm.acpi_nb);
491 struct drm_device *dev = dev_priv->dev;
492 struct acpi_bus_event *entry = (struct acpi_bus_event *)data;
493
494 if (strcmp(entry->device_class, "ac_adapter") == 0) {
495 bool ac = power_supply_is_system_supplied();
496
497 NV_DEBUG(dev, "power supply changed: %s\n", ac ? "AC" : "DC");
498 }
499
500 return NOTIFY_OK;
501}
502#endif
503
Martin Peres34e9d852010-09-22 20:54:22 +0200504int
505nouveau_pm_init(struct drm_device *dev)
506{
507 struct drm_nouveau_private *dev_priv = dev->dev_private;
508 struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
509 char info[256];
510 int ret, i;
511
Martin Perese614b2e2011-04-14 00:46:19 +0200512 nouveau_mem_timing_init(dev);
Martin Peres34e9d852010-09-22 20:54:22 +0200513 nouveau_volt_init(dev);
514 nouveau_perf_init(dev);
515 nouveau_temp_init(dev);
516
517 NV_INFO(dev, "%d available performance level(s)\n", pm->nr_perflvl);
518 for (i = 0; i < pm->nr_perflvl; i++) {
519 nouveau_pm_perflvl_info(&pm->perflvl[i], info, sizeof(info));
Ben Skeggs93dccbe2011-06-09 17:27:47 +1000520 NV_INFO(dev, "%d:%s", pm->perflvl[i].id, info);
Martin Peres34e9d852010-09-22 20:54:22 +0200521 }
522
523 /* determine current ("boot") performance level */
524 ret = nouveau_pm_perflvl_get(dev, &pm->boot);
525 if (ret == 0) {
Martin Peres01e542c2011-03-19 22:44:35 +0100526 strncpy(pm->boot.name, "boot", 4);
Martin Peres34e9d852010-09-22 20:54:22 +0200527 pm->cur = &pm->boot;
528
529 nouveau_pm_perflvl_info(&pm->boot, info, sizeof(info));
Ben Skeggs93dccbe2011-06-09 17:27:47 +1000530 NV_INFO(dev, "c:%s", info);
Martin Peres34e9d852010-09-22 20:54:22 +0200531 }
532
533 /* switch performance levels now if requested */
534 if (nouveau_perflvl != NULL) {
535 ret = nouveau_pm_profile_set(dev, nouveau_perflvl);
536 if (ret) {
537 NV_ERROR(dev, "error setting perflvl \"%s\": %d\n",
538 nouveau_perflvl, ret);
539 }
540 }
541
542 nouveau_sysfs_init(dev);
543 nouveau_hwmon_init(dev);
Martin Peres1f962792011-04-12 00:55:44 +0200544#if defined(CONFIG_ACPI) && defined(CONFIG_POWER_SUPPLY)
Ben Skeggs60326492010-10-12 12:31:32 +1000545 pm->acpi_nb.notifier_call = nouveau_pm_acpi_event;
546 register_acpi_notifier(&pm->acpi_nb);
547#endif
Martin Peres34e9d852010-09-22 20:54:22 +0200548
549 return 0;
550}
551
552void
553nouveau_pm_fini(struct drm_device *dev)
554{
555 struct drm_nouveau_private *dev_priv = dev->dev_private;
556 struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
557
558 if (pm->cur != &pm->boot)
559 nouveau_pm_perflvl_set(dev, &pm->boot);
Ben Skeggs330c5982010-09-16 15:39:49 +1000560
Roy Spliet7760fcb2010-09-17 23:17:24 +0200561 nouveau_temp_fini(dev);
Ben Skeggs330c5982010-09-16 15:39:49 +1000562 nouveau_perf_fini(dev);
563 nouveau_volt_fini(dev);
Martin Perese614b2e2011-04-14 00:46:19 +0200564 nouveau_mem_timing_fini(dev);
Martin Peres34e9d852010-09-22 20:54:22 +0200565
Martin Peres1f962792011-04-12 00:55:44 +0200566#if defined(CONFIG_ACPI) && defined(CONFIG_POWER_SUPPLY)
Ben Skeggs60326492010-10-12 12:31:32 +1000567 unregister_acpi_notifier(&pm->acpi_nb);
568#endif
Martin Peres34e9d852010-09-22 20:54:22 +0200569 nouveau_hwmon_fini(dev);
570 nouveau_sysfs_fini(dev);
Ben Skeggs330c5982010-09-16 15:39:49 +1000571}
572
Ben Skeggs64f1c112010-09-17 13:35:25 +1000573void
574nouveau_pm_resume(struct drm_device *dev)
575{
576 struct drm_nouveau_private *dev_priv = dev->dev_private;
577 struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
578 struct nouveau_pm_level *perflvl;
579
Ben Skeggs317495b2011-02-17 11:11:28 +1000580 if (!pm->cur || pm->cur == &pm->boot)
Ben Skeggs64f1c112010-09-17 13:35:25 +1000581 return;
582
583 perflvl = pm->cur;
584 pm->cur = &pm->boot;
585 nouveau_pm_perflvl_set(dev, perflvl);
586}