aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/nouveau/nouveau_pm.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/nouveau/nouveau_pm.c')
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_pm.c235
1 files changed, 170 insertions, 65 deletions
diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.c b/drivers/gpu/drm/nouveau/nouveau_pm.c
index 9064d7f19794..34d591b7d4ef 100644
--- a/drivers/gpu/drm/nouveau/nouveau_pm.c
+++ b/drivers/gpu/drm/nouveau/nouveau_pm.c
@@ -50,7 +50,7 @@ nouveau_pwmfan_get(struct drm_device *dev)
50 ret = nouveau_gpio_find(dev, 0, DCB_GPIO_PWM_FAN, 0xff, &gpio); 50 ret = nouveau_gpio_find(dev, 0, DCB_GPIO_PWM_FAN, 0xff, &gpio);
51 if (ret == 0) { 51 if (ret == 0) {
52 ret = pm->pwm_get(dev, gpio.line, &divs, &duty); 52 ret = pm->pwm_get(dev, gpio.line, &divs, &duty);
53 if (ret == 0) { 53 if (ret == 0 && divs) {
54 divs = max(divs, duty); 54 divs = max(divs, duty);
55 if (dev_priv->card_type <= NV_40 || (gpio.log[0] & 1)) 55 if (dev_priv->card_type <= NV_40 || (gpio.log[0] & 1))
56 duty = divs - duty; 56 duty = divs - duty;
@@ -77,7 +77,7 @@ nouveau_pwmfan_set(struct drm_device *dev, int percent)
77 77
78 ret = nouveau_gpio_find(dev, 0, DCB_GPIO_PWM_FAN, 0xff, &gpio); 78 ret = nouveau_gpio_find(dev, 0, DCB_GPIO_PWM_FAN, 0xff, &gpio);
79 if (ret == 0) { 79 if (ret == 0) {
80 divs = pm->pwm_divisor; 80 divs = pm->fan.pwm_divisor;
81 if (pm->fan.pwm_freq) { 81 if (pm->fan.pwm_freq) {
82 /*XXX: PNVIO clock more than likely... */ 82 /*XXX: PNVIO clock more than likely... */
83 divs = 135000 / pm->fan.pwm_freq; 83 divs = 135000 / pm->fan.pwm_freq;
@@ -89,7 +89,10 @@ nouveau_pwmfan_set(struct drm_device *dev, int percent)
89 if (dev_priv->card_type <= NV_40 || (gpio.log[0] & 1)) 89 if (dev_priv->card_type <= NV_40 || (gpio.log[0] & 1))
90 duty = divs - duty; 90 duty = divs - duty;
91 91
92 return pm->pwm_set(dev, gpio.line, divs, duty); 92 ret = pm->pwm_set(dev, gpio.line, divs, duty);
93 if (!ret)
94 pm->fan.percent = percent;
95 return ret;
93 } 96 }
94 97
95 return -ENODEV; 98 return -ENODEV;
@@ -144,9 +147,13 @@ nouveau_pm_perflvl_set(struct drm_device *dev, struct nouveau_pm_level *perflvl)
144 return ret; 147 return ret;
145 148
146 state = pm->clocks_pre(dev, perflvl); 149 state = pm->clocks_pre(dev, perflvl);
147 if (IS_ERR(state)) 150 if (IS_ERR(state)) {
148 return PTR_ERR(state); 151 ret = PTR_ERR(state);
149 pm->clocks_set(dev, state); 152 goto error;
153 }
154 ret = pm->clocks_set(dev, state);
155 if (ret)
156 goto error;
150 157
151 ret = nouveau_pm_perflvl_aux(dev, perflvl, perflvl, pm->cur); 158 ret = nouveau_pm_perflvl_aux(dev, perflvl, perflvl, pm->cur);
152 if (ret) 159 if (ret)
@@ -154,6 +161,65 @@ nouveau_pm_perflvl_set(struct drm_device *dev, struct nouveau_pm_level *perflvl)
154 161
155 pm->cur = perflvl; 162 pm->cur = perflvl;
156 return 0; 163 return 0;
164
165error:
166 /* restore the fan speed and voltage before leaving */
167 nouveau_pm_perflvl_aux(dev, perflvl, perflvl, pm->cur);
168 return ret;
169}
170
171void
172nouveau_pm_trigger(struct drm_device *dev)
173{
174 struct drm_nouveau_private *dev_priv = dev->dev_private;
175 struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
176 struct nouveau_pm_profile *profile = NULL;
177 struct nouveau_pm_level *perflvl = NULL;
178 int ret;
179
180 /* select power profile based on current power source */
181 if (power_supply_is_system_supplied())
182 profile = pm->profile_ac;
183 else
184 profile = pm->profile_dc;
185
186 if (profile != pm->profile) {
187 pm->profile->func->fini(pm->profile);
188 pm->profile = profile;
189 pm->profile->func->init(pm->profile);
190 }
191
192 /* select performance level based on profile */
193 perflvl = profile->func->select(profile);
194
195 /* change perflvl, if necessary */
196 if (perflvl != pm->cur) {
197 struct nouveau_timer_engine *ptimer = &dev_priv->engine.timer;
198 u64 time0 = ptimer->read(dev);
199
200 NV_INFO(dev, "setting performance level: %d", perflvl->id);
201 ret = nouveau_pm_perflvl_set(dev, perflvl);
202 if (ret)
203 NV_INFO(dev, "> reclocking failed: %d\n\n", ret);
204
205 NV_INFO(dev, "> reclocking took %lluns\n\n",
206 ptimer->read(dev) - time0);
207 }
208}
209
210static struct nouveau_pm_profile *
211profile_find(struct drm_device *dev, const char *string)
212{
213 struct drm_nouveau_private *dev_priv = dev->dev_private;
214 struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
215 struct nouveau_pm_profile *profile;
216
217 list_for_each_entry(profile, &pm->profiles, head) {
218 if (!strncmp(profile->name, string, sizeof(profile->name)))
219 return profile;
220 }
221
222 return NULL;
157} 223}
158 224
159static int 225static int
@@ -161,33 +227,54 @@ nouveau_pm_profile_set(struct drm_device *dev, const char *profile)
161{ 227{
162 struct drm_nouveau_private *dev_priv = dev->dev_private; 228 struct drm_nouveau_private *dev_priv = dev->dev_private;
163 struct nouveau_pm_engine *pm = &dev_priv->engine.pm; 229 struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
164 struct nouveau_pm_level *perflvl = NULL; 230 struct nouveau_pm_profile *ac = NULL, *dc = NULL;
231 char string[16], *cur = string, *ptr;
165 232
166 /* safety precaution, for now */ 233 /* safety precaution, for now */
167 if (nouveau_perflvl_wr != 7777) 234 if (nouveau_perflvl_wr != 7777)
168 return -EPERM; 235 return -EPERM;
169 236
170 if (!strncmp(profile, "boot", 4)) 237 strncpy(string, profile, sizeof(string));
171 perflvl = &pm->boot; 238 if ((ptr = strchr(string, '\n')))
172 else { 239 *ptr = '\0';
173 int pl = simple_strtol(profile, NULL, 10);
174 int i;
175 240
176 for (i = 0; i < pm->nr_perflvl; i++) { 241 ptr = strsep(&cur, ",");
177 if (pm->perflvl[i].id == pl) { 242 if (ptr)
178 perflvl = &pm->perflvl[i]; 243 ac = profile_find(dev, ptr);
179 break;
180 }
181 }
182 244
183 if (!perflvl) 245 ptr = strsep(&cur, ",");
184 return -EINVAL; 246 if (ptr)
185 } 247 dc = profile_find(dev, ptr);
248 else
249 dc = ac;
250
251 if (ac == NULL || dc == NULL)
252 return -EINVAL;
253
254 pm->profile_ac = ac;
255 pm->profile_dc = dc;
256 nouveau_pm_trigger(dev);
257 return 0;
258}
259
260static void
261nouveau_pm_static_dummy(struct nouveau_pm_profile *profile)
262{
263}
186 264
187 NV_INFO(dev, "setting performance level: %s\n", profile); 265static struct nouveau_pm_level *
188 return nouveau_pm_perflvl_set(dev, perflvl); 266nouveau_pm_static_select(struct nouveau_pm_profile *profile)
267{
268 return container_of(profile, struct nouveau_pm_level, profile);
189} 269}
190 270
271const struct nouveau_pm_profile_func nouveau_pm_static_profile_func = {
272 .destroy = nouveau_pm_static_dummy,
273 .init = nouveau_pm_static_dummy,
274 .fini = nouveau_pm_static_dummy,
275 .select = nouveau_pm_static_select,
276};
277
191static int 278static int
192nouveau_pm_perflvl_get(struct drm_device *dev, struct nouveau_pm_level *perflvl) 279nouveau_pm_perflvl_get(struct drm_device *dev, struct nouveau_pm_level *perflvl)
193{ 280{
@@ -197,9 +284,11 @@ nouveau_pm_perflvl_get(struct drm_device *dev, struct nouveau_pm_level *perflvl)
197 284
198 memset(perflvl, 0, sizeof(*perflvl)); 285 memset(perflvl, 0, sizeof(*perflvl));
199 286
200 ret = pm->clocks_get(dev, perflvl); 287 if (pm->clocks_get) {
201 if (ret) 288 ret = pm->clocks_get(dev, perflvl);
202 return ret; 289 if (ret)
290 return ret;
291 }
203 292
204 if (pm->voltage.supported && pm->voltage_get) { 293 if (pm->voltage.supported && pm->voltage_get) {
205 ret = pm->voltage_get(dev); 294 ret = pm->voltage_get(dev);
@@ -213,13 +302,14 @@ nouveau_pm_perflvl_get(struct drm_device *dev, struct nouveau_pm_level *perflvl)
213 if (ret > 0) 302 if (ret > 0)
214 perflvl->fanspeed = ret; 303 perflvl->fanspeed = ret;
215 304
305 nouveau_mem_timing_read(dev, &perflvl->timing);
216 return 0; 306 return 0;
217} 307}
218 308
219static void 309static void
220nouveau_pm_perflvl_info(struct nouveau_pm_level *perflvl, char *ptr, int len) 310nouveau_pm_perflvl_info(struct nouveau_pm_level *perflvl, char *ptr, int len)
221{ 311{
222 char c[16], s[16], v[32], f[16], t[16], m[16]; 312 char c[16], s[16], v[32], f[16], m[16];
223 313
224 c[0] = '\0'; 314 c[0] = '\0';
225 if (perflvl->core) 315 if (perflvl->core)
@@ -247,18 +337,15 @@ nouveau_pm_perflvl_info(struct nouveau_pm_level *perflvl, char *ptr, int len)
247 if (perflvl->fanspeed) 337 if (perflvl->fanspeed)
248 snprintf(f, sizeof(f), " fanspeed %d%%", perflvl->fanspeed); 338 snprintf(f, sizeof(f), " fanspeed %d%%", perflvl->fanspeed);
249 339
250 t[0] = '\0'; 340 snprintf(ptr, len, "%s%s%s%s%s\n", c, s, m, v, f);
251 if (perflvl->timing)
252 snprintf(t, sizeof(t), " timing %d", perflvl->timing->id);
253
254 snprintf(ptr, len, "%s%s%s%s%s%s\n", c, s, m, t, v, f);
255} 341}
256 342
257static ssize_t 343static ssize_t
258nouveau_pm_get_perflvl_info(struct device *d, 344nouveau_pm_get_perflvl_info(struct device *d,
259 struct device_attribute *a, char *buf) 345 struct device_attribute *a, char *buf)
260{ 346{
261 struct nouveau_pm_level *perflvl = (struct nouveau_pm_level *)a; 347 struct nouveau_pm_level *perflvl =
348 container_of(a, struct nouveau_pm_level, dev_attr);
262 char *ptr = buf; 349 char *ptr = buf;
263 int len = PAGE_SIZE; 350 int len = PAGE_SIZE;
264 351
@@ -280,12 +367,8 @@ nouveau_pm_get_perflvl(struct device *d, struct device_attribute *a, char *buf)
280 int len = PAGE_SIZE, ret; 367 int len = PAGE_SIZE, ret;
281 char *ptr = buf; 368 char *ptr = buf;
282 369
283 if (!pm->cur) 370 snprintf(ptr, len, "profile: %s, %s\nc:",
284 snprintf(ptr, len, "setting: boot\n"); 371 pm->profile_ac->name, pm->profile_dc->name);
285 else if (pm->cur == &pm->boot)
286 snprintf(ptr, len, "setting: boot\nc:");
287 else
288 snprintf(ptr, len, "setting: static %d\nc:", pm->cur->id);
289 ptr += strlen(buf); 372 ptr += strlen(buf);
290 len -= strlen(buf); 373 len -= strlen(buf);
291 374
@@ -397,7 +480,7 @@ nouveau_hwmon_set_max_temp(struct device *d, struct device_attribute *a,
397 struct nouveau_pm_threshold_temp *temp = &pm->threshold_temp; 480 struct nouveau_pm_threshold_temp *temp = &pm->threshold_temp;
398 long value; 481 long value;
399 482
400 if (strict_strtol(buf, 10, &value) == -EINVAL) 483 if (kstrtol(buf, 10, &value) == -EINVAL)
401 return count; 484 return count;
402 485
403 temp->down_clock = value/1000; 486 temp->down_clock = value/1000;
@@ -432,7 +515,7 @@ nouveau_hwmon_set_critical_temp(struct device *d, struct device_attribute *a,
432 struct nouveau_pm_threshold_temp *temp = &pm->threshold_temp; 515 struct nouveau_pm_threshold_temp *temp = &pm->threshold_temp;
433 long value; 516 long value;
434 517
435 if (strict_strtol(buf, 10, &value) == -EINVAL) 518 if (kstrtol(buf, 10, &value) == -EINVAL)
436 return count; 519 return count;
437 520
438 temp->critical = value/1000; 521 temp->critical = value/1000;
@@ -529,7 +612,7 @@ nouveau_hwmon_set_pwm0(struct device *d, struct device_attribute *a,
529 if (nouveau_perflvl_wr != 7777) 612 if (nouveau_perflvl_wr != 7777)
530 return -EPERM; 613 return -EPERM;
531 614
532 if (strict_strtol(buf, 10, &value) == -EINVAL) 615 if (kstrtol(buf, 10, &value) == -EINVAL)
533 return -EINVAL; 616 return -EINVAL;
534 617
535 if (value < pm->fan.min_duty) 618 if (value < pm->fan.min_duty)
@@ -568,7 +651,7 @@ nouveau_hwmon_set_pwm0_min(struct device *d, struct device_attribute *a,
568 struct nouveau_pm_engine *pm = &dev_priv->engine.pm; 651 struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
569 long value; 652 long value;
570 653
571 if (strict_strtol(buf, 10, &value) == -EINVAL) 654 if (kstrtol(buf, 10, &value) == -EINVAL)
572 return -EINVAL; 655 return -EINVAL;
573 656
574 if (value < 0) 657 if (value < 0)
@@ -609,7 +692,7 @@ nouveau_hwmon_set_pwm0_max(struct device *d, struct device_attribute *a,
609 struct nouveau_pm_engine *pm = &dev_priv->engine.pm; 692 struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
610 long value; 693 long value;
611 694
612 if (strict_strtol(buf, 10, &value) == -EINVAL) 695 if (kstrtol(buf, 10, &value) == -EINVAL)
613 return -EINVAL; 696 return -EINVAL;
614 697
615 if (value < 0) 698 if (value < 0)
@@ -731,8 +814,10 @@ nouveau_hwmon_fini(struct drm_device *dev)
731 814
732 if (pm->hwmon) { 815 if (pm->hwmon) {
733 sysfs_remove_group(&dev->pdev->dev.kobj, &hwmon_attrgroup); 816 sysfs_remove_group(&dev->pdev->dev.kobj, &hwmon_attrgroup);
734 sysfs_remove_group(&dev->pdev->dev.kobj, &hwmon_pwm_fan_attrgroup); 817 sysfs_remove_group(&dev->pdev->dev.kobj,
735 sysfs_remove_group(&dev->pdev->dev.kobj, &hwmon_fan_rpm_attrgroup); 818 &hwmon_pwm_fan_attrgroup);
819 sysfs_remove_group(&dev->pdev->dev.kobj,
820 &hwmon_fan_rpm_attrgroup);
736 821
737 hwmon_device_unregister(pm->hwmon); 822 hwmon_device_unregister(pm->hwmon);
738 } 823 }
@@ -752,6 +837,7 @@ nouveau_pm_acpi_event(struct notifier_block *nb, unsigned long val, void *data)
752 bool ac = power_supply_is_system_supplied(); 837 bool ac = power_supply_is_system_supplied();
753 838
754 NV_DEBUG(dev, "power supply changed: %s\n", ac ? "AC" : "DC"); 839 NV_DEBUG(dev, "power supply changed: %s\n", ac ? "AC" : "DC");
840 nouveau_pm_trigger(dev);
755 } 841 }
756 842
757 return NOTIFY_OK; 843 return NOTIFY_OK;
@@ -766,35 +852,48 @@ nouveau_pm_init(struct drm_device *dev)
766 char info[256]; 852 char info[256];
767 int ret, i; 853 int ret, i;
768 854
769 nouveau_mem_timing_init(dev); 855 /* parse aux tables from vbios */
770 nouveau_volt_init(dev); 856 nouveau_volt_init(dev);
771 nouveau_perf_init(dev);
772 nouveau_temp_init(dev); 857 nouveau_temp_init(dev);
773 858
859 /* determine current ("boot") performance level */
860 ret = nouveau_pm_perflvl_get(dev, &pm->boot);
861 if (ret) {
862 NV_ERROR(dev, "failed to determine boot perflvl\n");
863 return ret;
864 }
865
866 strncpy(pm->boot.name, "boot", 4);
867 strncpy(pm->boot.profile.name, "boot", 4);
868 pm->boot.profile.func = &nouveau_pm_static_profile_func;
869
870 INIT_LIST_HEAD(&pm->profiles);
871 list_add(&pm->boot.profile.head, &pm->profiles);
872
873 pm->profile_ac = &pm->boot.profile;
874 pm->profile_dc = &pm->boot.profile;
875 pm->profile = &pm->boot.profile;
876 pm->cur = &pm->boot;
877
878 /* add performance levels from vbios */
879 nouveau_perf_init(dev);
880
881 /* display available performance levels */
774 NV_INFO(dev, "%d available performance level(s)\n", pm->nr_perflvl); 882 NV_INFO(dev, "%d available performance level(s)\n", pm->nr_perflvl);
775 for (i = 0; i < pm->nr_perflvl; i++) { 883 for (i = 0; i < pm->nr_perflvl; i++) {
776 nouveau_pm_perflvl_info(&pm->perflvl[i], info, sizeof(info)); 884 nouveau_pm_perflvl_info(&pm->perflvl[i], info, sizeof(info));
777 NV_INFO(dev, "%d:%s", pm->perflvl[i].id, info); 885 NV_INFO(dev, "%d:%s", pm->perflvl[i].id, info);
778 } 886 }
779 887
780 /* determine current ("boot") performance level */ 888 nouveau_pm_perflvl_info(&pm->boot, info, sizeof(info));
781 ret = nouveau_pm_perflvl_get(dev, &pm->boot); 889 NV_INFO(dev, "c:%s", info);
782 if (ret == 0) {
783 strncpy(pm->boot.name, "boot", 4);
784 pm->cur = &pm->boot;
785
786 nouveau_pm_perflvl_info(&pm->boot, info, sizeof(info));
787 NV_INFO(dev, "c:%s", info);
788 }
789 890
790 /* switch performance levels now if requested */ 891 /* switch performance levels now if requested */
791 if (nouveau_perflvl != NULL) { 892 if (nouveau_perflvl != NULL)
792 ret = nouveau_pm_profile_set(dev, nouveau_perflvl); 893 nouveau_pm_profile_set(dev, nouveau_perflvl);
793 if (ret) { 894
794 NV_ERROR(dev, "error setting perflvl \"%s\": %d\n", 895 /* determine the current fan speed */
795 nouveau_perflvl, ret); 896 pm->fan.percent = nouveau_pwmfan_get(dev);
796 }
797 }
798 897
799 nouveau_sysfs_init(dev); 898 nouveau_sysfs_init(dev);
800 nouveau_hwmon_init(dev); 899 nouveau_hwmon_init(dev);
@@ -811,6 +910,12 @@ nouveau_pm_fini(struct drm_device *dev)
811{ 910{
812 struct drm_nouveau_private *dev_priv = dev->dev_private; 911 struct drm_nouveau_private *dev_priv = dev->dev_private;
813 struct nouveau_pm_engine *pm = &dev_priv->engine.pm; 912 struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
913 struct nouveau_pm_profile *profile, *tmp;
914
915 list_for_each_entry_safe(profile, tmp, &pm->profiles, head) {
916 list_del(&profile->head);
917 profile->func->destroy(profile);
918 }
814 919
815 if (pm->cur != &pm->boot) 920 if (pm->cur != &pm->boot)
816 nouveau_pm_perflvl_set(dev, &pm->boot); 921 nouveau_pm_perflvl_set(dev, &pm->boot);
@@ -818,7 +923,6 @@ nouveau_pm_fini(struct drm_device *dev)
818 nouveau_temp_fini(dev); 923 nouveau_temp_fini(dev);
819 nouveau_perf_fini(dev); 924 nouveau_perf_fini(dev);
820 nouveau_volt_fini(dev); 925 nouveau_volt_fini(dev);
821 nouveau_mem_timing_fini(dev);
822 926
823#if defined(CONFIG_ACPI) && defined(CONFIG_POWER_SUPPLY) 927#if defined(CONFIG_ACPI) && defined(CONFIG_POWER_SUPPLY)
824 unregister_acpi_notifier(&pm->acpi_nb); 928 unregister_acpi_notifier(&pm->acpi_nb);
@@ -840,4 +944,5 @@ nouveau_pm_resume(struct drm_device *dev)
840 perflvl = pm->cur; 944 perflvl = pm->cur;
841 pm->cur = &pm->boot; 945 pm->cur = &pm->boot;
842 nouveau_pm_perflvl_set(dev, perflvl); 946 nouveau_pm_perflvl_set(dev, perflvl);
947 nouveau_pwmfan_set(dev, pm->fan.percent);
843} 948}