drm/nouveau/pm: rework to allow selecting separate profiles for ac/battery
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
Signed-off-by: Martin Peres <martin.peres@labri.fr>
diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h
index 531e435..009089e 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drv.h
+++ b/drivers/gpu/drm/nouveau/nouveau_drv.h
@@ -485,15 +485,27 @@
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_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 cc8beb8..e649015 100644
--- a/drivers/gpu/drm/nouveau/nouveau_perf.c
+++ b/drivers/gpu/drm/nouveau/nouveau_perf.c
@@ -394,6 +394,13 @@
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 7c25567..4fff09e 100644
--- a/drivers/gpu/drm/nouveau/nouveau_pm.c
+++ b/drivers/gpu/drm/nouveau/nouveau_pm.c
@@ -168,51 +168,99 @@
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;
+ strncpy(string, profile, sizeof(string));
+ if ((ptr = strchr(string, '\n')))
+ *ptr = '\0';
- for (i = 0; i < pm->nr_perflvl; i++) {
- if (pm->perflvl[i].id == pl) {
- perflvl = &pm->perflvl[i];
- break;
- }
- }
+ ptr = strsep(&cur, ",");
+ if (ptr)
+ ac = profile_find(dev, ptr);
- if (!perflvl)
- return -EINVAL;
- }
+ ptr = strsep(&cur, ",");
+ if (ptr)
+ dc = profile_find(dev, ptr);
+ else
+ dc = ac;
- 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");
- }
+ if (ac == NULL || dc == NULL)
+ return -EINVAL;
- return ret;
+ 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 @@
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 @@
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 @@
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 @@
}
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 @@
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 1a8ae15..3f82dfe 100644
--- a/drivers/gpu/drm/nouveau/nouveau_pm.h
+++ b/drivers/gpu/drm/nouveau/nouveau_pm.h
@@ -47,6 +47,8 @@
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 *);