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>
This commit is contained in:
@@ -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 {
|
|||||||
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)
|
|||||||
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,50 +168,98 @@ error:
|
|||||||
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)
|
ptr = strsep(&cur, ",");
|
||||||
|
if (ptr)
|
||||||
|
ac = profile_find(dev, ptr);
|
||||||
|
|
||||||
|
ptr = strsep(&cur, ",");
|
||||||
|
if (ptr)
|
||||||
|
dc = profile_find(dev, ptr);
|
||||||
|
else
|
||||||
|
dc = ac;
|
||||||
|
|
||||||
|
if (ac == NULL || dc == NULL)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
for (i = 0; i < pm->nr_perflvl; i++) {
|
pm->profile_ac = ac;
|
||||||
if (pm->perflvl[i].id == pl) {
|
pm->profile_dc = dc;
|
||||||
perflvl = &pm->perflvl[i];
|
nouveau_pm_trigger(dev);
|
||||||
break;
|
return 0;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!perflvl)
|
static struct nouveau_pm_level *
|
||||||
return -EINVAL;
|
nouveau_pm_static_select(struct nouveau_pm_profile *profile)
|
||||||
|
{
|
||||||
|
return container_of(profile, struct nouveau_pm_level, profile);
|
||||||
}
|
}
|
||||||
|
|
||||||
NV_INFO(dev, "setting performance level: %s", profile);
|
const struct nouveau_pm_profile_func nouveau_pm_static_profile_func = {
|
||||||
start_time = nv04_timer_read(dev);
|
.select = nouveau_pm_static_select,
|
||||||
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");
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
||||||
@@ -280,7 +328,8 @@ 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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
}
|
}
|
||||||
|
|
||||||
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)
|
|||||||
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 *,
|
|||||||
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 *);
|
||||||
|
Reference in New Issue
Block a user