Commit 8d7bb400 authored by Ben Skeggs's avatar Ben Skeggs

drm/nouveau/pm: rework to allow selecting separate profiles for ac/battery

Signed-off-by: default avatarBen Skeggs <bskeggs@redhat.com>
Signed-off-by: default avatarMartin Peres <martin.peres@labri.fr>
parent b830973b
...@@ -485,15 +485,27 @@ struct nouveau_pm_tbl_entry { ...@@ -485,15 +485,27 @@ struct nouveau_pm_tbl_entry {
u8 tUNK_20, tUNK_21; 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 #define NOUVEAU_PM_MAX_LEVEL 8
struct nouveau_pm_level { struct nouveau_pm_level {
struct nouveau_pm_profile profile;
struct device_attribute dev_attr; struct device_attribute dev_attr;
char name[32]; char name[32];
int id; int id;
struct nouveau_pm_memtiming timing;
u32 memory; u32 memory;
u16 memscript; u16 memscript;
struct nouveau_pm_memtiming timing;
u32 core; u32 core;
u32 shader; u32 shader;
...@@ -542,6 +554,10 @@ struct nouveau_pm_engine { ...@@ -542,6 +554,10 @@ struct nouveau_pm_engine {
struct nouveau_pm_threshold_temp threshold_temp; struct nouveau_pm_threshold_temp threshold_temp;
struct nouveau_pm_fan fan; 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 boot;
struct nouveau_pm_level *cur; struct nouveau_pm_level *cur;
......
...@@ -394,6 +394,13 @@ nouveau_perf_init(struct drm_device *dev) ...@@ -394,6 +394,13 @@ nouveau_perf_init(struct drm_device *dev)
snprintf(perflvl->name, sizeof(perflvl->name), snprintf(perflvl->name, sizeof(perflvl->name),
"performance_level_%d", i); "performance_level_%d", i);
perflvl->id = 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++; pm->nr_perflvl++;
} }
} }
......
...@@ -168,51 +168,99 @@ nouveau_pm_perflvl_set(struct drm_device *dev, struct nouveau_pm_level *perflvl) ...@@ -168,51 +168,99 @@ nouveau_pm_perflvl_set(struct drm_device *dev, struct nouveau_pm_level *perflvl)
return ret; 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 static int
nouveau_pm_profile_set(struct drm_device *dev, const char *profile) nouveau_pm_profile_set(struct drm_device *dev, const char *profile)
{ {
struct drm_nouveau_private *dev_priv = dev->dev_private; struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_pm_engine *pm = &dev_priv->engine.pm; struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
struct nouveau_pm_level *perflvl = NULL; struct nouveau_pm_profile *ac = NULL, *dc = NULL;
u64 start_time; char string[16], *cur = string, *ptr;
int ret = 0;
long pl;
/* safety precaution, for now */ /* safety precaution, for now */
if (nouveau_perflvl_wr != 7777) if (nouveau_perflvl_wr != 7777)
return -EPERM; return -EPERM;
if (!strncmp(profile, "boot", 4)) strncpy(string, profile, sizeof(string));
perflvl = &pm->boot; if ((ptr = strchr(string, '\n')))
else { *ptr = '\0';
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;
}
}
if (!perflvl) ptr = strsep(&cur, ",");
return -EINVAL; if (ptr)
} ac = profile_find(dev, ptr);
NV_INFO(dev, "setting performance level: %s", profile); ptr = strsep(&cur, ",");
start_time = nv04_timer_read(dev); if (ptr)
ret = nouveau_pm_perflvl_set(dev, perflvl); dc = profile_find(dev, ptr);
if (!ret) { else
NV_INFO(dev, "> reclocking took %lluns\n\n", dc = ac;
(nv04_timer_read(dev) - start_time));
} else {
NV_INFO(dev, "> reclocking failed\n\n");
}
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 static int
nouveau_pm_perflvl_get(struct drm_device *dev, struct nouveau_pm_level *perflvl) 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) ...@@ -273,14 +321,15 @@ nouveau_pm_perflvl_info(struct nouveau_pm_level *perflvl, char *ptr, int len)
if (perflvl->fanspeed) if (perflvl->fanspeed)
snprintf(f, sizeof(f), " fanspeed %d%%", 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 static ssize_t
nouveau_pm_get_perflvl_info(struct device *d, nouveau_pm_get_perflvl_info(struct device *d,
struct device_attribute *a, char *buf) 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; char *ptr = buf;
int len = PAGE_SIZE; int len = PAGE_SIZE;
...@@ -302,12 +351,8 @@ nouveau_pm_get_perflvl(struct device *d, struct device_attribute *a, char *buf) ...@@ -302,12 +351,8 @@ nouveau_pm_get_perflvl(struct device *d, struct device_attribute *a, char *buf)
int len = PAGE_SIZE, ret; int len = PAGE_SIZE, ret;
char *ptr = buf; char *ptr = buf;
if (!pm->cur) snprintf(ptr, len, "profile: %s, %s\nc:",
snprintf(ptr, len, "setting: boot\n"); pm->profile_ac->name, pm->profile_dc->name);
else if (pm->cur == &pm->boot)
snprintf(ptr, len, "setting: boot\nc:");
else
snprintf(ptr, len, "setting: static %d\nc:", pm->cur->id);
ptr += strlen(buf); ptr += strlen(buf);
len -= strlen(buf); len -= strlen(buf);
...@@ -776,6 +821,7 @@ nouveau_pm_acpi_event(struct notifier_block *nb, unsigned long val, void *data) ...@@ -776,6 +821,7 @@ nouveau_pm_acpi_event(struct notifier_block *nb, unsigned long val, void *data)
bool ac = power_supply_is_system_supplied(); bool ac = power_supply_is_system_supplied();
NV_DEBUG(dev, "power supply changed: %s\n", ac ? "AC" : "DC"); NV_DEBUG(dev, "power supply changed: %s\n", ac ? "AC" : "DC");
nouveau_pm_trigger(dev);
} }
return NOTIFY_OK; return NOTIFY_OK;
...@@ -802,6 +848,14 @@ nouveau_pm_init(struct drm_device *dev) ...@@ -802,6 +848,14 @@ nouveau_pm_init(struct drm_device *dev)
} }
strncpy(pm->boot.name, "boot", 4); 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; pm->cur = &pm->boot;
/* add performance levels from vbios */ /* add performance levels from vbios */
...@@ -818,13 +872,8 @@ nouveau_pm_init(struct drm_device *dev) ...@@ -818,13 +872,8 @@ nouveau_pm_init(struct drm_device *dev)
NV_INFO(dev, "c:%s", info); NV_INFO(dev, "c:%s", info);
/* switch performance levels now if requested */ /* switch performance levels now if requested */
if (nouveau_perflvl != NULL) { if (nouveau_perflvl != NULL)
ret = nouveau_pm_profile_set(dev, nouveau_perflvl); nouveau_pm_profile_set(dev, nouveau_perflvl);
if (ret) {
NV_ERROR(dev, "error setting perflvl \"%s\": %d\n",
nouveau_perflvl, ret);
}
}
/* determine the current fan speed */ /* determine the current fan speed */
pm->fan.percent = nouveau_pwmfan_get(dev); pm->fan.percent = nouveau_pwmfan_get(dev);
......
...@@ -47,6 +47,8 @@ int nouveau_mem_exec(struct nouveau_mem_exec_func *, ...@@ -47,6 +47,8 @@ int nouveau_mem_exec(struct nouveau_mem_exec_func *,
int nouveau_pm_init(struct drm_device *dev); int nouveau_pm_init(struct drm_device *dev);
void nouveau_pm_fini(struct drm_device *dev); void nouveau_pm_fini(struct drm_device *dev);
void nouveau_pm_resume(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 */ /* nouveau_volt.c */
void nouveau_volt_init(struct drm_device *); void nouveau_volt_init(struct drm_device *);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment