diff options
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_drv.h | 18 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_perf.c | 7 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_pm.c | 139 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_pm.h | 2 |
4 files changed, 120 insertions, 46 deletions
diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index 531e435d9fb3..009089e093f3 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h | |||
@@ -485,15 +485,27 @@ struct nouveau_pm_tbl_entry { | |||
485 | u8 tUNK_20, tUNK_21; | 485 | u8 tUNK_20, tUNK_21; |
486 | }; | 486 | }; |
487 | 487 | ||
488 | struct nouveau_pm_profile; | ||
489 | struct nouveau_pm_profile_func { | ||
490 | struct nouveau_pm_level *(*select)(struct nouveau_pm_profile *); | ||
491 | }; | ||
492 | |||
493 | struct nouveau_pm_profile { | ||
494 | const struct nouveau_pm_profile_func *func; | ||
495 | struct list_head head; | ||
496 | char name[8]; | ||
497 | }; | ||
498 | |||
488 | #define NOUVEAU_PM_MAX_LEVEL 8 | 499 | #define NOUVEAU_PM_MAX_LEVEL 8 |
489 | struct nouveau_pm_level { | 500 | struct nouveau_pm_level { |
501 | struct nouveau_pm_profile profile; | ||
490 | struct device_attribute dev_attr; | 502 | struct device_attribute dev_attr; |
491 | char name[32]; | 503 | char name[32]; |
492 | int id; | 504 | int id; |
493 | 505 | ||
506 | struct nouveau_pm_memtiming timing; | ||
494 | u32 memory; | 507 | u32 memory; |
495 | u16 memscript; | 508 | u16 memscript; |
496 | struct nouveau_pm_memtiming timing; | ||
497 | 509 | ||
498 | u32 core; | 510 | u32 core; |
499 | u32 shader; | 511 | u32 shader; |
@@ -542,6 +554,10 @@ struct nouveau_pm_engine { | |||
542 | struct nouveau_pm_threshold_temp threshold_temp; | 554 | struct nouveau_pm_threshold_temp threshold_temp; |
543 | struct nouveau_pm_fan fan; | 555 | struct nouveau_pm_fan fan; |
544 | 556 | ||
557 | struct nouveau_pm_profile *profile_ac; | ||
558 | struct nouveau_pm_profile *profile_dc; | ||
559 | struct list_head profiles; | ||
560 | |||
545 | struct nouveau_pm_level boot; | 561 | struct nouveau_pm_level boot; |
546 | struct nouveau_pm_level *cur; | 562 | struct nouveau_pm_level *cur; |
547 | 563 | ||
diff --git a/drivers/gpu/drm/nouveau/nouveau_perf.c b/drivers/gpu/drm/nouveau/nouveau_perf.c index cc8beb8e2f06..e64901509f90 100644 --- a/drivers/gpu/drm/nouveau/nouveau_perf.c +++ b/drivers/gpu/drm/nouveau/nouveau_perf.c | |||
@@ -394,6 +394,13 @@ nouveau_perf_init(struct drm_device *dev) | |||
394 | snprintf(perflvl->name, sizeof(perflvl->name), | 394 | snprintf(perflvl->name, sizeof(perflvl->name), |
395 | "performance_level_%d", i); | 395 | "performance_level_%d", i); |
396 | perflvl->id = i; | 396 | perflvl->id = i; |
397 | |||
398 | snprintf(perflvl->profile.name, sizeof(perflvl->profile.name), | ||
399 | "%d", perflvl->id); | ||
400 | perflvl->profile.func = &nouveau_pm_static_profile_func; | ||
401 | list_add_tail(&perflvl->profile.head, &pm->profiles); | ||
402 | |||
403 | |||
397 | pm->nr_perflvl++; | 404 | pm->nr_perflvl++; |
398 | } | 405 | } |
399 | } | 406 | } |
diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.c b/drivers/gpu/drm/nouveau/nouveau_pm.c index 7c25567fb56a..4fff09e3f592 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.c +++ b/drivers/gpu/drm/nouveau/nouveau_pm.c | |||
@@ -168,51 +168,99 @@ error: | |||
168 | return ret; | 168 | return ret; |
169 | } | 169 | } |
170 | 170 | ||
171 | void | ||
172 | nouveau_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 | /* select performance level based on profile */ | ||
187 | perflvl = profile->func->select(profile); | ||
188 | |||
189 | /* change perflvl, if necessary */ | ||
190 | if (perflvl != pm->cur) { | ||
191 | struct nouveau_timer_engine *ptimer = &dev_priv->engine.timer; | ||
192 | u64 time0 = ptimer->read(dev); | ||
193 | |||
194 | NV_INFO(dev, "setting performance level: %d", perflvl->id); | ||
195 | ret = nouveau_pm_perflvl_set(dev, perflvl); | ||
196 | if (ret) | ||
197 | NV_INFO(dev, "> reclocking failed: %d\n\n", ret); | ||
198 | |||
199 | NV_INFO(dev, "> reclocking took %lluns\n\n", | ||
200 | ptimer->read(dev) - time0); | ||
201 | } | ||
202 | } | ||
203 | |||
204 | static struct nouveau_pm_profile * | ||
205 | profile_find(struct drm_device *dev, const char *string) | ||
206 | { | ||
207 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
208 | struct nouveau_pm_engine *pm = &dev_priv->engine.pm; | ||
209 | struct nouveau_pm_profile *profile; | ||
210 | |||
211 | list_for_each_entry(profile, &pm->profiles, head) { | ||
212 | if (!strncmp(profile->name, string, sizeof(profile->name))) | ||
213 | return profile; | ||
214 | } | ||
215 | |||
216 | return NULL; | ||
217 | } | ||
218 | |||
171 | static int | 219 | static int |
172 | nouveau_pm_profile_set(struct drm_device *dev, const char *profile) | 220 | nouveau_pm_profile_set(struct drm_device *dev, const char *profile) |
173 | { | 221 | { |
174 | struct drm_nouveau_private *dev_priv = dev->dev_private; | 222 | struct drm_nouveau_private *dev_priv = dev->dev_private; |
175 | struct nouveau_pm_engine *pm = &dev_priv->engine.pm; | 223 | struct nouveau_pm_engine *pm = &dev_priv->engine.pm; |
176 | struct nouveau_pm_level *perflvl = NULL; | 224 | struct nouveau_pm_profile *ac = NULL, *dc = NULL; |
177 | u64 start_time; | 225 | char string[16], *cur = string, *ptr; |
178 | int ret = 0; | ||
179 | long pl; | ||
180 | 226 | ||
181 | /* safety precaution, for now */ | 227 | /* safety precaution, for now */ |
182 | if (nouveau_perflvl_wr != 7777) | 228 | if (nouveau_perflvl_wr != 7777) |
183 | return -EPERM; | 229 | return -EPERM; |
184 | 230 | ||
185 | if (!strncmp(profile, "boot", 4)) | 231 | strncpy(string, profile, sizeof(string)); |
186 | perflvl = &pm->boot; | 232 | if ((ptr = strchr(string, '\n'))) |
187 | else { | 233 | *ptr = '\0'; |
188 | int i; | ||
189 | if (kstrtol(profile, 10, &pl) == -EINVAL) | ||
190 | return -EINVAL; | ||
191 | |||
192 | for (i = 0; i < pm->nr_perflvl; i++) { | ||
193 | if (pm->perflvl[i].id == pl) { | ||
194 | perflvl = &pm->perflvl[i]; | ||
195 | break; | ||
196 | } | ||
197 | } | ||
198 | 234 | ||
199 | if (!perflvl) | 235 | ptr = strsep(&cur, ","); |
200 | return -EINVAL; | 236 | if (ptr) |
201 | } | 237 | ac = profile_find(dev, ptr); |
202 | 238 | ||
203 | NV_INFO(dev, "setting performance level: %s", profile); | 239 | ptr = strsep(&cur, ","); |
204 | start_time = nv04_timer_read(dev); | 240 | if (ptr) |
205 | ret = nouveau_pm_perflvl_set(dev, perflvl); | 241 | dc = profile_find(dev, ptr); |
206 | if (!ret) { | 242 | else |
207 | NV_INFO(dev, "> reclocking took %lluns\n\n", | 243 | dc = ac; |
208 | (nv04_timer_read(dev) - start_time)); | ||
209 | } else { | ||
210 | NV_INFO(dev, "> reclocking failed\n\n"); | ||
211 | } | ||
212 | 244 | ||
213 | return ret; | 245 | if (ac == NULL || dc == NULL) |
246 | return -EINVAL; | ||
247 | |||
248 | pm->profile_ac = ac; | ||
249 | pm->profile_dc = dc; | ||
250 | nouveau_pm_trigger(dev); | ||
251 | return 0; | ||
252 | } | ||
253 | |||
254 | static struct nouveau_pm_level * | ||
255 | nouveau_pm_static_select(struct nouveau_pm_profile *profile) | ||
256 | { | ||
257 | return container_of(profile, struct nouveau_pm_level, profile); | ||
214 | } | 258 | } |
215 | 259 | ||
260 | const struct nouveau_pm_profile_func nouveau_pm_static_profile_func = { | ||
261 | .select = nouveau_pm_static_select, | ||
262 | }; | ||
263 | |||
216 | static int | 264 | static int |
217 | nouveau_pm_perflvl_get(struct drm_device *dev, struct nouveau_pm_level *perflvl) | 265 | nouveau_pm_perflvl_get(struct drm_device *dev, struct nouveau_pm_level *perflvl) |
218 | { | 266 | { |
@@ -273,14 +321,15 @@ nouveau_pm_perflvl_info(struct nouveau_pm_level *perflvl, char *ptr, int len) | |||
273 | if (perflvl->fanspeed) | 321 | if (perflvl->fanspeed) |
274 | snprintf(f, sizeof(f), " fanspeed %d%%", perflvl->fanspeed); | 322 | snprintf(f, sizeof(f), " fanspeed %d%%", perflvl->fanspeed); |
275 | 323 | ||
276 | snprintf(ptr, len, "%s%s%s%s%s\n", c, s, m, v, f); | 324 | snprintf(ptr, len, "%s%s%s%s%s\n", c, s, m, v, f); |
277 | } | 325 | } |
278 | 326 | ||
279 | static ssize_t | 327 | static ssize_t |
280 | nouveau_pm_get_perflvl_info(struct device *d, | 328 | nouveau_pm_get_perflvl_info(struct device *d, |
281 | struct device_attribute *a, char *buf) | 329 | struct device_attribute *a, char *buf) |
282 | { | 330 | { |
283 | struct nouveau_pm_level *perflvl = (struct nouveau_pm_level *)a; | 331 | struct nouveau_pm_level *perflvl = |
332 | container_of(a, struct nouveau_pm_level, dev_attr); | ||
284 | char *ptr = buf; | 333 | char *ptr = buf; |
285 | int len = PAGE_SIZE; | 334 | int len = PAGE_SIZE; |
286 | 335 | ||
@@ -302,12 +351,8 @@ nouveau_pm_get_perflvl(struct device *d, struct device_attribute *a, char *buf) | |||
302 | int len = PAGE_SIZE, ret; | 351 | int len = PAGE_SIZE, ret; |
303 | char *ptr = buf; | 352 | char *ptr = buf; |
304 | 353 | ||
305 | if (!pm->cur) | 354 | snprintf(ptr, len, "profile: %s, %s\nc:", |
306 | snprintf(ptr, len, "setting: boot\n"); | 355 | pm->profile_ac->name, pm->profile_dc->name); |
307 | else if (pm->cur == &pm->boot) | ||
308 | snprintf(ptr, len, "setting: boot\nc:"); | ||
309 | else | ||
310 | snprintf(ptr, len, "setting: static %d\nc:", pm->cur->id); | ||
311 | ptr += strlen(buf); | 356 | ptr += strlen(buf); |
312 | len -= strlen(buf); | 357 | len -= strlen(buf); |
313 | 358 | ||
@@ -776,6 +821,7 @@ nouveau_pm_acpi_event(struct notifier_block *nb, unsigned long val, void *data) | |||
776 | bool ac = power_supply_is_system_supplied(); | 821 | bool ac = power_supply_is_system_supplied(); |
777 | 822 | ||
778 | NV_DEBUG(dev, "power supply changed: %s\n", ac ? "AC" : "DC"); | 823 | NV_DEBUG(dev, "power supply changed: %s\n", ac ? "AC" : "DC"); |
824 | nouveau_pm_trigger(dev); | ||
779 | } | 825 | } |
780 | 826 | ||
781 | return NOTIFY_OK; | 827 | return NOTIFY_OK; |
@@ -802,6 +848,14 @@ nouveau_pm_init(struct drm_device *dev) | |||
802 | } | 848 | } |
803 | 849 | ||
804 | strncpy(pm->boot.name, "boot", 4); | 850 | strncpy(pm->boot.name, "boot", 4); |
851 | strncpy(pm->boot.profile.name, "boot", 4); | ||
852 | pm->boot.profile.func = &nouveau_pm_static_profile_func; | ||
853 | |||
854 | INIT_LIST_HEAD(&pm->profiles); | ||
855 | list_add(&pm->boot.profile.head, &pm->profiles); | ||
856 | |||
857 | pm->profile_ac = &pm->boot.profile; | ||
858 | pm->profile_dc = &pm->boot.profile; | ||
805 | pm->cur = &pm->boot; | 859 | pm->cur = &pm->boot; |
806 | 860 | ||
807 | /* add performance levels from vbios */ | 861 | /* add performance levels from vbios */ |
@@ -818,13 +872,8 @@ nouveau_pm_init(struct drm_device *dev) | |||
818 | NV_INFO(dev, "c:%s", info); | 872 | NV_INFO(dev, "c:%s", info); |
819 | 873 | ||
820 | /* switch performance levels now if requested */ | 874 | /* switch performance levels now if requested */ |
821 | if (nouveau_perflvl != NULL) { | 875 | if (nouveau_perflvl != NULL) |
822 | ret = nouveau_pm_profile_set(dev, nouveau_perflvl); | 876 | nouveau_pm_profile_set(dev, nouveau_perflvl); |
823 | if (ret) { | ||
824 | NV_ERROR(dev, "error setting perflvl \"%s\": %d\n", | ||
825 | nouveau_perflvl, ret); | ||
826 | } | ||
827 | } | ||
828 | 877 | ||
829 | /* determine the current fan speed */ | 878 | /* determine the current fan speed */ |
830 | pm->fan.percent = nouveau_pwmfan_get(dev); | 879 | pm->fan.percent = nouveau_pwmfan_get(dev); |
diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.h b/drivers/gpu/drm/nouveau/nouveau_pm.h index 1a8ae15803a0..3f82dfea61dd 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.h +++ b/drivers/gpu/drm/nouveau/nouveau_pm.h | |||
@@ -47,6 +47,8 @@ int nouveau_mem_exec(struct nouveau_mem_exec_func *, | |||
47 | int nouveau_pm_init(struct drm_device *dev); | 47 | int nouveau_pm_init(struct drm_device *dev); |
48 | void nouveau_pm_fini(struct drm_device *dev); | 48 | void nouveau_pm_fini(struct drm_device *dev); |
49 | void nouveau_pm_resume(struct drm_device *dev); | 49 | void nouveau_pm_resume(struct drm_device *dev); |
50 | extern const struct nouveau_pm_profile_func nouveau_pm_static_profile_func; | ||
51 | void nouveau_pm_trigger(struct drm_device *dev); | ||
50 | 52 | ||
51 | /* nouveau_volt.c */ | 53 | /* nouveau_volt.c */ |
52 | void nouveau_volt_init(struct drm_device *); | 54 | void nouveau_volt_init(struct drm_device *); |