blob: 53d2ad8a0496a8b8b576600f67b44a3089169628 [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 Skeggs3b5565d2011-06-09 16:57:07 +100067 if (pm->voltage.supported && pm->voltage_set && perflvl->volt_min) {
68 ret = pm->voltage_set(dev, perflvl->volt_min);
Ben Skeggs64f1c112010-09-17 13:35:25 +100069 if (ret) {
70 NV_ERROR(dev, "voltage_set %d failed: %d\n",
Ben Skeggs3b5565d2011-06-09 16:57:07 +100071 perflvl->volt_min, ret);
Ben Skeggs64f1c112010-09-17 13:35:25 +100072 }
73 }
74
Ben Skeggs5c6dc652010-09-27 09:47:56 +100075 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);
Ben Skeggs64f1c112010-09-17 13:35:25 +100079
80 pm->cur = perflvl;
81 return 0;
82}
83
84static int
Ben Skeggs6f876982010-09-16 16:47:14 +100085nouveau_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;
Ben Skeggs6f876982010-09-16 16:47:14 +100090
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
Ben Skeggs6f876982010-09-16 16:47:14 +1000115 NV_INFO(dev, "setting performance level: %s\n", profile);
Ben Skeggs64f1c112010-09-17 13:35:25 +1000116 return nouveau_pm_perflvl_set(dev, perflvl);
Ben Skeggs6f876982010-09-16 16:47:14 +1000117}
118
119static int
Ben Skeggs330c5982010-09-16 15:39:49 +1000120nouveau_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
Ben Skeggs330c5982010-09-16 15:39:49 +1000126 memset(perflvl, 0, sizeof(*perflvl));
127
Ben Skeggs93dccbe2011-06-09 17:27:47 +1000128 if (pm->clock_get) {
129 ret = pm->clock_get(dev, PLL_CORE);
130 if (ret > 0)
131 perflvl->core = ret;
Ben Skeggs330c5982010-09-16 15:39:49 +1000132
Ben Skeggs93dccbe2011-06-09 17:27:47 +1000133 ret = pm->clock_get(dev, PLL_MEMORY);
134 if (ret > 0)
135 perflvl->memory = ret;
Ben Skeggs330c5982010-09-16 15:39:49 +1000136
Ben Skeggs93dccbe2011-06-09 17:27:47 +1000137 ret = pm->clock_get(dev, PLL_SHADER);
138 if (ret > 0)
139 perflvl->shader = ret;
Ben Skeggs330c5982010-09-16 15:39:49 +1000140
Ben Skeggs93dccbe2011-06-09 17:27:47 +1000141 ret = pm->clock_get(dev, PLL_UNK05);
142 if (ret > 0)
143 perflvl->unk05 = ret;
144 }
Ben Skeggs330c5982010-09-16 15:39:49 +1000145
146 if (pm->voltage.supported && pm->voltage_get) {
147 ret = pm->voltage_get(dev);
Ben Skeggs3b5565d2011-06-09 16:57:07 +1000148 if (ret > 0) {
149 perflvl->volt_min = ret;
150 perflvl->volt_max = ret;
151 }
Ben Skeggs330c5982010-09-16 15:39:49 +1000152 }
153
154 return 0;
155}
156
157static void
158nouveau_pm_perflvl_info(struct nouveau_pm_level *perflvl, char *ptr, int len)
159{
Ben Skeggs93dccbe2011-06-09 17:27:47 +1000160 char c[16], s[16], v[32], f[16], t[16], m[16];
Francisco Jerez0fbb1142010-09-20 16:18:28 +0200161
162 c[0] = '\0';
163 if (perflvl->core)
164 snprintf(c, sizeof(c), " core %dMHz", perflvl->core / 1000);
Ben Skeggs330c5982010-09-16 15:39:49 +1000165
166 s[0] = '\0';
167 if (perflvl->shader)
168 snprintf(s, sizeof(s), " shader %dMHz", perflvl->shader / 1000);
169
Ben Skeggs93dccbe2011-06-09 17:27:47 +1000170 m[0] = '\0';
171 if (perflvl->memory)
172 snprintf(m, sizeof(m), " memory %dMHz", perflvl->memory / 1000);
173
Ben Skeggs330c5982010-09-16 15:39:49 +1000174 v[0] = '\0';
Ben Skeggs3b5565d2011-06-09 16:57:07 +1000175 if (perflvl->volt_min && perflvl->volt_min != perflvl->volt_max) {
176 snprintf(v, sizeof(v), " voltage %dmV-%dmV",
177 perflvl->volt_min / 1000, perflvl->volt_max / 1000);
178 } else
179 if (perflvl->volt_min) {
180 snprintf(v, sizeof(v), " voltage %dmV",
181 perflvl->volt_min / 1000);
182 }
Ben Skeggs330c5982010-09-16 15:39:49 +1000183
184 f[0] = '\0';
185 if (perflvl->fanspeed)
186 snprintf(f, sizeof(f), " fanspeed %d%%", perflvl->fanspeed);
187
Martin Perese614b2e2011-04-14 00:46:19 +0200188 t[0] = '\0';
189 if (perflvl->timing)
190 snprintf(t, sizeof(t), " timing %d", perflvl->timing->id);
191
Ben Skeggs93dccbe2011-06-09 17:27:47 +1000192 snprintf(ptr, len, "%s%s%s%s%s%s\n", c, s, m, t, v, f);
Ben Skeggs330c5982010-09-16 15:39:49 +1000193}
194
195static ssize_t
196nouveau_pm_get_perflvl_info(struct device *d,
197 struct device_attribute *a, char *buf)
198{
199 struct nouveau_pm_level *perflvl = (struct nouveau_pm_level *)a;
200 char *ptr = buf;
201 int len = PAGE_SIZE;
202
Ben Skeggs93dccbe2011-06-09 17:27:47 +1000203 snprintf(ptr, len, "%d:", perflvl->id);
Ben Skeggs330c5982010-09-16 15:39:49 +1000204 ptr += strlen(buf);
205 len -= strlen(buf);
206
207 nouveau_pm_perflvl_info(perflvl, ptr, len);
208 return strlen(buf);
209}
210
211static ssize_t
212nouveau_pm_get_perflvl(struct device *d, struct device_attribute *a, char *buf)
213{
Martin Peres34e9d852010-09-22 20:54:22 +0200214 struct drm_device *dev = pci_get_drvdata(to_pci_dev(d));
Ben Skeggs330c5982010-09-16 15:39:49 +1000215 struct drm_nouveau_private *dev_priv = dev->dev_private;
216 struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
217 struct nouveau_pm_level cur;
218 int len = PAGE_SIZE, ret;
219 char *ptr = buf;
220
221 if (!pm->cur)
222 snprintf(ptr, len, "setting: boot\n");
223 else if (pm->cur == &pm->boot)
Ben Skeggs93dccbe2011-06-09 17:27:47 +1000224 snprintf(ptr, len, "setting: boot\nc:");
Ben Skeggs330c5982010-09-16 15:39:49 +1000225 else
Ben Skeggs93dccbe2011-06-09 17:27:47 +1000226 snprintf(ptr, len, "setting: static %d\nc:", pm->cur->id);
Ben Skeggs330c5982010-09-16 15:39:49 +1000227 ptr += strlen(buf);
228 len -= strlen(buf);
229
230 ret = nouveau_pm_perflvl_get(dev, &cur);
231 if (ret == 0)
232 nouveau_pm_perflvl_info(&cur, ptr, len);
233 return strlen(buf);
234}
235
236static ssize_t
237nouveau_pm_set_perflvl(struct device *d, struct device_attribute *a,
238 const char *buf, size_t count)
239{
Martin Peres34e9d852010-09-22 20:54:22 +0200240 struct drm_device *dev = pci_get_drvdata(to_pci_dev(d));
Ben Skeggs6f876982010-09-16 16:47:14 +1000241 int ret;
242
243 ret = nouveau_pm_profile_set(dev, buf);
244 if (ret)
245 return ret;
246 return strlen(buf);
Ben Skeggs330c5982010-09-16 15:39:49 +1000247}
248
Francisco Jerez5c4abd02010-09-23 20:36:42 +0200249static DEVICE_ATTR(performance_level, S_IRUGO | S_IWUSR,
250 nouveau_pm_get_perflvl, nouveau_pm_set_perflvl);
Ben Skeggs330c5982010-09-16 15:39:49 +1000251
Martin Peres34e9d852010-09-22 20:54:22 +0200252static int
253nouveau_sysfs_init(struct drm_device *dev)
Ben Skeggs330c5982010-09-16 15:39:49 +1000254{
255 struct drm_nouveau_private *dev_priv = dev->dev_private;
256 struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
257 struct device *d = &dev->pdev->dev;
Ben Skeggs330c5982010-09-16 15:39:49 +1000258 int ret, i;
259
Ben Skeggs330c5982010-09-16 15:39:49 +1000260 ret = device_create_file(d, &dev_attr_performance_level);
261 if (ret)
262 return ret;
263
264 for (i = 0; i < pm->nr_perflvl; i++) {
265 struct nouveau_pm_level *perflvl = &pm->perflvl[i];
266
267 perflvl->dev_attr.attr.name = perflvl->name;
268 perflvl->dev_attr.attr.mode = S_IRUGO;
269 perflvl->dev_attr.show = nouveau_pm_get_perflvl_info;
270 perflvl->dev_attr.store = NULL;
271 sysfs_attr_init(&perflvl->dev_attr.attr);
272
273 ret = device_create_file(d, &perflvl->dev_attr);
274 if (ret) {
275 NV_ERROR(dev, "failed pervlvl %d sysfs: %d\n",
276 perflvl->id, i);
277 perflvl->dev_attr.attr.name = NULL;
278 nouveau_pm_fini(dev);
279 return ret;
280 }
281 }
282
283 return 0;
284}
285
Martin Peres34e9d852010-09-22 20:54:22 +0200286static void
287nouveau_sysfs_fini(struct drm_device *dev)
Ben Skeggs330c5982010-09-16 15:39:49 +1000288{
289 struct drm_nouveau_private *dev_priv = dev->dev_private;
290 struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
291 struct device *d = &dev->pdev->dev;
292 int i;
293
294 device_remove_file(d, &dev_attr_performance_level);
295 for (i = 0; i < pm->nr_perflvl; i++) {
296 struct nouveau_pm_level *pl = &pm->perflvl[i];
297
298 if (!pl->dev_attr.attr.name)
299 break;
300
301 device_remove_file(d, &pl->dev_attr);
302 }
Martin Peres34e9d852010-09-22 20:54:22 +0200303}
304
Martin Peresb54262f2010-10-26 12:48:28 +0200305#ifdef CONFIG_HWMON
Martin Peres34e9d852010-09-22 20:54:22 +0200306static ssize_t
307nouveau_hwmon_show_temp(struct device *d, struct device_attribute *a, char *buf)
308{
309 struct drm_device *dev = dev_get_drvdata(d);
Francisco Jerez8155cac2010-09-23 20:58:38 +0200310 struct drm_nouveau_private *dev_priv = dev->dev_private;
311 struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
Martin Peres34e9d852010-09-22 20:54:22 +0200312
Francisco Jerez8155cac2010-09-23 20:58:38 +0200313 return snprintf(buf, PAGE_SIZE, "%d\n", pm->temp_get(dev)*1000);
Martin Peres34e9d852010-09-22 20:54:22 +0200314}
315static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, nouveau_hwmon_show_temp,
316 NULL, 0);
317
318static ssize_t
319nouveau_hwmon_max_temp(struct device *d, struct device_attribute *a, char *buf)
320{
321 struct drm_device *dev = dev_get_drvdata(d);
322 struct drm_nouveau_private *dev_priv = dev->dev_private;
323 struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
324 struct nouveau_pm_threshold_temp *temp = &pm->threshold_temp;
325
326 return snprintf(buf, PAGE_SIZE, "%d\n", temp->down_clock*1000);
327}
328static ssize_t
329nouveau_hwmon_set_max_temp(struct device *d, struct device_attribute *a,
330 const char *buf, size_t count)
331{
332 struct drm_device *dev = dev_get_drvdata(d);
333 struct drm_nouveau_private *dev_priv = dev->dev_private;
334 struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
335 struct nouveau_pm_threshold_temp *temp = &pm->threshold_temp;
336 long value;
337
Francisco Jerez5c4abd02010-09-23 20:36:42 +0200338 if (strict_strtol(buf, 10, &value) == -EINVAL)
Martin Peres34e9d852010-09-22 20:54:22 +0200339 return count;
340
341 temp->down_clock = value/1000;
342
343 nouveau_temp_safety_checks(dev);
344
345 return count;
346}
347static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO | S_IWUSR, nouveau_hwmon_max_temp,
348 nouveau_hwmon_set_max_temp,
349 0);
350
351static ssize_t
352nouveau_hwmon_critical_temp(struct device *d, struct device_attribute *a,
353 char *buf)
354{
355 struct drm_device *dev = dev_get_drvdata(d);
356 struct drm_nouveau_private *dev_priv = dev->dev_private;
357 struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
358 struct nouveau_pm_threshold_temp *temp = &pm->threshold_temp;
359
360 return snprintf(buf, PAGE_SIZE, "%d\n", temp->critical*1000);
361}
362static ssize_t
363nouveau_hwmon_set_critical_temp(struct device *d, struct device_attribute *a,
364 const char *buf,
365 size_t count)
366{
367 struct drm_device *dev = dev_get_drvdata(d);
368 struct drm_nouveau_private *dev_priv = dev->dev_private;
369 struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
370 struct nouveau_pm_threshold_temp *temp = &pm->threshold_temp;
371 long value;
372
Francisco Jerez5c4abd02010-09-23 20:36:42 +0200373 if (strict_strtol(buf, 10, &value) == -EINVAL)
Martin Peres34e9d852010-09-22 20:54:22 +0200374 return count;
375
376 temp->critical = value/1000;
377
378 nouveau_temp_safety_checks(dev);
379
380 return count;
381}
382static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO | S_IWUSR,
383 nouveau_hwmon_critical_temp,
384 nouveau_hwmon_set_critical_temp,
385 0);
386
387static ssize_t nouveau_hwmon_show_name(struct device *dev,
388 struct device_attribute *attr,
389 char *buf)
390{
391 return sprintf(buf, "nouveau\n");
392}
393static SENSOR_DEVICE_ATTR(name, S_IRUGO, nouveau_hwmon_show_name, NULL, 0);
394
395static ssize_t nouveau_hwmon_show_update_rate(struct device *dev,
396 struct device_attribute *attr,
397 char *buf)
398{
399 return sprintf(buf, "1000\n");
400}
401static SENSOR_DEVICE_ATTR(update_rate, S_IRUGO,
402 nouveau_hwmon_show_update_rate,
403 NULL, 0);
404
405static struct attribute *hwmon_attributes[] = {
406 &sensor_dev_attr_temp1_input.dev_attr.attr,
407 &sensor_dev_attr_temp1_max.dev_attr.attr,
408 &sensor_dev_attr_temp1_crit.dev_attr.attr,
409 &sensor_dev_attr_name.dev_attr.attr,
410 &sensor_dev_attr_update_rate.dev_attr.attr,
411 NULL
412};
413
414static const struct attribute_group hwmon_attrgroup = {
415 .attrs = hwmon_attributes,
416};
Martin Peresb54262f2010-10-26 12:48:28 +0200417#endif
Martin Peres34e9d852010-09-22 20:54:22 +0200418
419static int
420nouveau_hwmon_init(struct drm_device *dev)
421{
Martin Peresb54262f2010-10-26 12:48:28 +0200422#ifdef CONFIG_HWMON
Martin Peres34e9d852010-09-22 20:54:22 +0200423 struct drm_nouveau_private *dev_priv = dev->dev_private;
Francisco Jerez8155cac2010-09-23 20:58:38 +0200424 struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
Martin Peres34e9d852010-09-22 20:54:22 +0200425 struct device *hwmon_dev;
426 int ret;
427
Francisco Jerez8155cac2010-09-23 20:58:38 +0200428 if (!pm->temp_get)
429 return -ENODEV;
Martin Peres34e9d852010-09-22 20:54:22 +0200430
431 hwmon_dev = hwmon_device_register(&dev->pdev->dev);
432 if (IS_ERR(hwmon_dev)) {
433 ret = PTR_ERR(hwmon_dev);
434 NV_ERROR(dev,
435 "Unable to register hwmon device: %d\n", ret);
436 return ret;
437 }
438 dev_set_drvdata(hwmon_dev, dev);
Lucas Stach07cfe0e2011-01-06 20:29:53 +0100439 ret = sysfs_create_group(&dev->pdev->dev.kobj, &hwmon_attrgroup);
Martin Peres34e9d852010-09-22 20:54:22 +0200440 if (ret) {
441 NV_ERROR(dev,
442 "Unable to create hwmon sysfs file: %d\n", ret);
443 hwmon_device_unregister(hwmon_dev);
444 return ret;
445 }
446
Francisco Jerez8155cac2010-09-23 20:58:38 +0200447 pm->hwmon = hwmon_dev;
Martin Peresb54262f2010-10-26 12:48:28 +0200448#endif
Martin Peres34e9d852010-09-22 20:54:22 +0200449 return 0;
450}
451
452static void
453nouveau_hwmon_fini(struct drm_device *dev)
454{
Martin Peresb54262f2010-10-26 12:48:28 +0200455#ifdef CONFIG_HWMON
Martin Peres34e9d852010-09-22 20:54:22 +0200456 struct drm_nouveau_private *dev_priv = dev->dev_private;
Francisco Jerez8155cac2010-09-23 20:58:38 +0200457 struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
Martin Peres34e9d852010-09-22 20:54:22 +0200458
Francisco Jerez8155cac2010-09-23 20:58:38 +0200459 if (pm->hwmon) {
Lucas Stach8c06a3e2011-01-30 10:54:11 +0100460 sysfs_remove_group(&dev->pdev->dev.kobj, &hwmon_attrgroup);
Francisco Jerez8155cac2010-09-23 20:58:38 +0200461 hwmon_device_unregister(pm->hwmon);
Martin Peres34e9d852010-09-22 20:54:22 +0200462 }
Martin Peresb54262f2010-10-26 12:48:28 +0200463#endif
Martin Peres34e9d852010-09-22 20:54:22 +0200464}
465
Martin Peres1f962792011-04-12 00:55:44 +0200466#if defined(CONFIG_ACPI) && defined(CONFIG_POWER_SUPPLY)
Ben Skeggs60326492010-10-12 12:31:32 +1000467static int
468nouveau_pm_acpi_event(struct notifier_block *nb, unsigned long val, void *data)
469{
470 struct drm_nouveau_private *dev_priv =
471 container_of(nb, struct drm_nouveau_private, engine.pm.acpi_nb);
472 struct drm_device *dev = dev_priv->dev;
473 struct acpi_bus_event *entry = (struct acpi_bus_event *)data;
474
475 if (strcmp(entry->device_class, "ac_adapter") == 0) {
476 bool ac = power_supply_is_system_supplied();
477
478 NV_DEBUG(dev, "power supply changed: %s\n", ac ? "AC" : "DC");
479 }
480
481 return NOTIFY_OK;
482}
483#endif
484
Martin Peres34e9d852010-09-22 20:54:22 +0200485int
486nouveau_pm_init(struct drm_device *dev)
487{
488 struct drm_nouveau_private *dev_priv = dev->dev_private;
489 struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
490 char info[256];
491 int ret, i;
492
Martin Perese614b2e2011-04-14 00:46:19 +0200493 nouveau_mem_timing_init(dev);
Martin Peres34e9d852010-09-22 20:54:22 +0200494 nouveau_volt_init(dev);
495 nouveau_perf_init(dev);
496 nouveau_temp_init(dev);
497
498 NV_INFO(dev, "%d available performance level(s)\n", pm->nr_perflvl);
499 for (i = 0; i < pm->nr_perflvl; i++) {
500 nouveau_pm_perflvl_info(&pm->perflvl[i], info, sizeof(info));
Ben Skeggs93dccbe2011-06-09 17:27:47 +1000501 NV_INFO(dev, "%d:%s", pm->perflvl[i].id, info);
Martin Peres34e9d852010-09-22 20:54:22 +0200502 }
503
504 /* determine current ("boot") performance level */
505 ret = nouveau_pm_perflvl_get(dev, &pm->boot);
506 if (ret == 0) {
Martin Peres01e542c2011-03-19 22:44:35 +0100507 strncpy(pm->boot.name, "boot", 4);
Martin Peres34e9d852010-09-22 20:54:22 +0200508 pm->cur = &pm->boot;
509
510 nouveau_pm_perflvl_info(&pm->boot, info, sizeof(info));
Ben Skeggs93dccbe2011-06-09 17:27:47 +1000511 NV_INFO(dev, "c:%s", info);
Martin Peres34e9d852010-09-22 20:54:22 +0200512 }
513
514 /* switch performance levels now if requested */
515 if (nouveau_perflvl != NULL) {
516 ret = nouveau_pm_profile_set(dev, nouveau_perflvl);
517 if (ret) {
518 NV_ERROR(dev, "error setting perflvl \"%s\": %d\n",
519 nouveau_perflvl, ret);
520 }
521 }
522
523 nouveau_sysfs_init(dev);
524 nouveau_hwmon_init(dev);
Martin Peres1f962792011-04-12 00:55:44 +0200525#if defined(CONFIG_ACPI) && defined(CONFIG_POWER_SUPPLY)
Ben Skeggs60326492010-10-12 12:31:32 +1000526 pm->acpi_nb.notifier_call = nouveau_pm_acpi_event;
527 register_acpi_notifier(&pm->acpi_nb);
528#endif
Martin Peres34e9d852010-09-22 20:54:22 +0200529
530 return 0;
531}
532
533void
534nouveau_pm_fini(struct drm_device *dev)
535{
536 struct drm_nouveau_private *dev_priv = dev->dev_private;
537 struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
538
539 if (pm->cur != &pm->boot)
540 nouveau_pm_perflvl_set(dev, &pm->boot);
Ben Skeggs330c5982010-09-16 15:39:49 +1000541
Roy Spliet7760fcb2010-09-17 23:17:24 +0200542 nouveau_temp_fini(dev);
Ben Skeggs330c5982010-09-16 15:39:49 +1000543 nouveau_perf_fini(dev);
544 nouveau_volt_fini(dev);
Martin Perese614b2e2011-04-14 00:46:19 +0200545 nouveau_mem_timing_fini(dev);
Martin Peres34e9d852010-09-22 20:54:22 +0200546
Martin Peres1f962792011-04-12 00:55:44 +0200547#if defined(CONFIG_ACPI) && defined(CONFIG_POWER_SUPPLY)
Ben Skeggs60326492010-10-12 12:31:32 +1000548 unregister_acpi_notifier(&pm->acpi_nb);
549#endif
Martin Peres34e9d852010-09-22 20:54:22 +0200550 nouveau_hwmon_fini(dev);
551 nouveau_sysfs_fini(dev);
Ben Skeggs330c5982010-09-16 15:39:49 +1000552}
553
Ben Skeggs64f1c112010-09-17 13:35:25 +1000554void
555nouveau_pm_resume(struct drm_device *dev)
556{
557 struct drm_nouveau_private *dev_priv = dev->dev_private;
558 struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
559 struct nouveau_pm_level *perflvl;
560
Ben Skeggs317495b2011-02-17 11:11:28 +1000561 if (!pm->cur || pm->cur == &pm->boot)
Ben Skeggs64f1c112010-09-17 13:35:25 +1000562 return;
563
564 perflvl = pm->cur;
565 pm->cur = &pm->boot;
566 nouveau_pm_perflvl_set(dev, perflvl);
567}