From 8d7bb400638906075c38cb07891993cf95076aa7 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Tue, 24 Jan 2012 15:59:07 +1000 Subject: [PATCH] drm/nouveau/pm: rework to allow selecting separate profiles for ac/battery Signed-off-by: Ben Skeggs Signed-off-by: Martin Peres --- drivers/gpu/drm/nouveau/nouveau_drv.h | 18 +++- drivers/gpu/drm/nouveau/nouveau_perf.c | 7 ++ drivers/gpu/drm/nouveau/nouveau_pm.c | 139 +++++++++++++++++-------- 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 { u8 tUNK_20, tUNK_21; }; +struct nouveau_pm_profile; +struct nouveau_pm_profile_func { + struct nouveau_pm_level *(*select)(struct nouveau_pm_profile *); +}; + +struct nouveau_pm_profile { + const struct nouveau_pm_profile_func *func; + struct list_head head; + char name[8]; +}; + #define NOUVEAU_PM_MAX_LEVEL 8 struct nouveau_pm_level { + struct nouveau_pm_profile profile; struct device_attribute dev_attr; char name[32]; int id; + struct nouveau_pm_memtiming timing; u32 memory; u16 memscript; - struct nouveau_pm_memtiming timing; u32 core; u32 shader; @@ -542,6 +554,10 @@ struct nouveau_pm_engine { struct nouveau_pm_threshold_temp threshold_temp; struct nouveau_pm_fan fan; + struct nouveau_pm_profile *profile_ac; + struct nouveau_pm_profile *profile_dc; + struct list_head profiles; + struct nouveau_pm_level boot; struct nouveau_pm_level *cur; 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) snprintf(perflvl->name, sizeof(perflvl->name), "performance_level_%d", i); perflvl->id = i; + + snprintf(perflvl->profile.name, sizeof(perflvl->profile.name), + "%d", perflvl->id); + perflvl->profile.func = &nouveau_pm_static_profile_func; + list_add_tail(&perflvl->profile.head, &pm->profiles); + + pm->nr_perflvl++; } } 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: return ret; } +void +nouveau_pm_trigger(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_pm_engine *pm = &dev_priv->engine.pm; + struct nouveau_pm_profile *profile = NULL; + struct nouveau_pm_level *perflvl = NULL; + int ret; + + /* select power profile based on current power source */ + if (power_supply_is_system_supplied()) + profile = pm->profile_ac; + else + profile = pm->profile_dc; + + /* select performance level based on profile */ + perflvl = profile->func->select(profile); + + /* change perflvl, if necessary */ + if (perflvl != pm->cur) { + struct nouveau_timer_engine *ptimer = &dev_priv->engine.timer; + u64 time0 = ptimer->read(dev); + + NV_INFO(dev, "setting performance level: %d", perflvl->id); + ret = nouveau_pm_perflvl_set(dev, perflvl); + if (ret) + NV_INFO(dev, "> reclocking failed: %d\n\n", ret); + + NV_INFO(dev, "> reclocking took %lluns\n\n", + ptimer->read(dev) - time0); + } +} + +static struct nouveau_pm_profile * +profile_find(struct drm_device *dev, const char *string) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_pm_engine *pm = &dev_priv->engine.pm; + struct nouveau_pm_profile *profile; + + list_for_each_entry(profile, &pm->profiles, head) { + if (!strncmp(profile->name, string, sizeof(profile->name))) + return profile; + } + + return NULL; +} + static int nouveau_pm_profile_set(struct drm_device *dev, const char *profile) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_pm_engine *pm = &dev_priv->engine.pm; - struct nouveau_pm_level *perflvl = NULL; - u64 start_time; - int ret = 0; - long pl; + struct nouveau_pm_profile *ac = NULL, *dc = NULL; + char string[16], *cur = string, *ptr; /* safety precaution, for now */ if (nouveau_perflvl_wr != 7777) return -EPERM; - if (!strncmp(profile, "boot", 4)) - perflvl = &pm->boot; - else { - int i; - if (kstrtol(profile, 10, &pl) == -EINVAL) - return -EINVAL; - - for (i = 0; i < pm->nr_perflvl; i++) { - if (pm->perflvl[i].id == pl) { - perflvl = &pm->perflvl[i]; - break; - } - } + strncpy(string, profile, sizeof(string)); + if ((ptr = strchr(string, '\n'))) + *ptr = '\0'; - if (!perflvl) - return -EINVAL; - } + ptr = strsep(&cur, ","); + if (ptr) + ac = profile_find(dev, ptr); - NV_INFO(dev, "setting performance level: %s", profile); - start_time = nv04_timer_read(dev); - ret = nouveau_pm_perflvl_set(dev, perflvl); - if (!ret) { - NV_INFO(dev, "> reclocking took %lluns\n\n", - (nv04_timer_read(dev) - start_time)); - } else { - NV_INFO(dev, "> reclocking failed\n\n"); - } + ptr = strsep(&cur, ","); + if (ptr) + dc = profile_find(dev, ptr); + else + dc = ac; - return ret; + if (ac == NULL || dc == NULL) + return -EINVAL; + + pm->profile_ac = ac; + pm->profile_dc = dc; + nouveau_pm_trigger(dev); + return 0; +} + +static struct nouveau_pm_level * +nouveau_pm_static_select(struct nouveau_pm_profile *profile) +{ + return container_of(profile, struct nouveau_pm_level, profile); } +const struct nouveau_pm_profile_func nouveau_pm_static_profile_func = { + .select = nouveau_pm_static_select, +}; + static int nouveau_pm_perflvl_get(struct drm_device *dev, struct nouveau_pm_level *perflvl) { @@ -273,14 +321,15 @@ nouveau_pm_perflvl_info(struct nouveau_pm_level *perflvl, char *ptr, int len) if (perflvl->fanspeed) snprintf(f, sizeof(f), " fanspeed %d%%", perflvl->fanspeed); - snprintf(ptr, len, "%s%s%s%s%s\n", c, s, m, v, f); + snprintf(ptr, len, "%s%s%s%s%s\n", c, s, m, v, f); } static ssize_t nouveau_pm_get_perflvl_info(struct device *d, struct device_attribute *a, char *buf) { - struct nouveau_pm_level *perflvl = (struct nouveau_pm_level *)a; + struct nouveau_pm_level *perflvl = + container_of(a, struct nouveau_pm_level, dev_attr); char *ptr = buf; int len = PAGE_SIZE; @@ -302,12 +351,8 @@ nouveau_pm_get_perflvl(struct device *d, struct device_attribute *a, char *buf) int len = PAGE_SIZE, ret; char *ptr = buf; - if (!pm->cur) - snprintf(ptr, len, "setting: boot\n"); - else if (pm->cur == &pm->boot) - snprintf(ptr, len, "setting: boot\nc:"); - else - snprintf(ptr, len, "setting: static %d\nc:", pm->cur->id); + snprintf(ptr, len, "profile: %s, %s\nc:", + pm->profile_ac->name, pm->profile_dc->name); ptr += strlen(buf); len -= strlen(buf); @@ -776,6 +821,7 @@ nouveau_pm_acpi_event(struct notifier_block *nb, unsigned long val, void *data) bool ac = power_supply_is_system_supplied(); NV_DEBUG(dev, "power supply changed: %s\n", ac ? "AC" : "DC"); + nouveau_pm_trigger(dev); } return NOTIFY_OK; @@ -802,6 +848,14 @@ nouveau_pm_init(struct drm_device *dev) } strncpy(pm->boot.name, "boot", 4); + strncpy(pm->boot.profile.name, "boot", 4); + pm->boot.profile.func = &nouveau_pm_static_profile_func; + + INIT_LIST_HEAD(&pm->profiles); + list_add(&pm->boot.profile.head, &pm->profiles); + + pm->profile_ac = &pm->boot.profile; + pm->profile_dc = &pm->boot.profile; pm->cur = &pm->boot; /* add performance levels from vbios */ @@ -818,13 +872,8 @@ nouveau_pm_init(struct drm_device *dev) NV_INFO(dev, "c:%s", info); /* switch performance levels now if requested */ - if (nouveau_perflvl != NULL) { - ret = nouveau_pm_profile_set(dev, nouveau_perflvl); - if (ret) { - NV_ERROR(dev, "error setting perflvl \"%s\": %d\n", - nouveau_perflvl, ret); - } - } + if (nouveau_perflvl != NULL) + nouveau_pm_profile_set(dev, nouveau_perflvl); /* determine the current fan speed */ 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 *, int nouveau_pm_init(struct drm_device *dev); void nouveau_pm_fini(struct drm_device *dev); void nouveau_pm_resume(struct drm_device *dev); +extern const struct nouveau_pm_profile_func nouveau_pm_static_profile_func; +void nouveau_pm_trigger(struct drm_device *dev); /* nouveau_volt.c */ void nouveau_volt_init(struct drm_device *); -- 2.30.2