From 6f876986bedf23b40ab707543e88fae7eac27f1f Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Thu, 16 Sep 2010 16:47:14 +1000 Subject: [PATCH] drm/nouveau: allow static performance level setting Guarded by a module parameter for the moment, read the code for the magic value which enables it. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_bios.c | 5 +- drivers/gpu/drm/nouveau/nouveau_drv.c | 8 +++ drivers/gpu/drm/nouveau/nouveau_drv.h | 2 + drivers/gpu/drm/nouveau/nouveau_pm.c | 89 +++++++++++++++++++++++++- drivers/gpu/drm/nouveau/nv04_pm.c | 2 +- drivers/gpu/drm/nouveau/nv50_pm.c | 2 +- 6 files changed, 104 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c index ef44070321e..07171dd3c16 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.c +++ b/drivers/gpu/drm/nouveau/nouveau_bios.c @@ -4832,8 +4832,11 @@ int get_pll_limits(struct drm_device *dev, uint32_t limit_match, struct pll_lims */ if (limit_match > PLL_MAX) pll_lim->reg = limit_match; - else + else { pll_lim->reg = get_pll_register(dev, limit_match); + if (!pll_lim->reg) + return -ENOENT; + } if (pll_lim_ver == 0x10 || pll_lim_ver == 0x11) { uint8_t *pll_rec = &bios->data[bios->pll_limit_tbl_ptr + headerlen + recordlen * pllindex]; diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.c b/drivers/gpu/drm/nouveau/nouveau_drv.c index 14a4960a989..b03bb6d5b98 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.c +++ b/drivers/gpu/drm/nouveau/nouveau_drv.c @@ -102,6 +102,14 @@ MODULE_PARM_DESC(reg_debug, "Register access debug bitmask:\n" int nouveau_reg_debug; module_param_named(reg_debug, nouveau_reg_debug, int, 0600); +MODULE_PARM_DESC(perflvl, "Performance level (default: boot)\n"); +char *nouveau_perflvl; +module_param_named(perflvl, nouveau_perflvl, charp, 0400); + +MODULE_PARM_DESC(perflvl_wr, "Allow perflvl changes (warning: dangerous!)\n"); +int nouveau_perflvl_wr; +module_param_named(perflvl_wr, nouveau_perflvl_wr, int, 0400); + int nouveau_fbpercrtc; #if 0 module_param_named(fbpercrtc, nouveau_fbpercrtc, int, 0400); diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index bda4d1e7c63..8d36ed6907d 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -716,6 +716,8 @@ extern int nouveau_ignorelid; extern int nouveau_nofbaccel; extern int nouveau_noaccel; extern int nouveau_override_conntype; +extern char *nouveau_perflvl; +extern int nouveau_perflvl_wr; extern int nouveau_pci_suspend(struct pci_dev *pdev, pm_message_t pm_state); extern int nouveau_pci_resume(struct pci_dev *pdev); diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.c b/drivers/gpu/drm/nouveau/nouveau_pm.c index 9cf5fd665b8..9e8e14eb6df 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.c +++ b/drivers/gpu/drm/nouveau/nouveau_pm.c @@ -27,6 +27,78 @@ #include "nouveau_drv.h" #include "nouveau_pm.h" +static int +nouveau_pm_clock_set(struct drm_device *dev, u8 id, u32 khz) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_pm_engine *pm = &dev_priv->engine.pm; + void *pre_state; + + if (khz == 0) + return 0; + + pre_state = pm->clock_pre(dev, id, khz); + if (IS_ERR(pre_state)) + return PTR_ERR(pre_state); + + if (pre_state) + pm->clock_set(dev, pre_state); + return 0; +} + +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; + int ret; + + /* safety precaution, for now */ + if (nouveau_perflvl_wr != 7777) + return -EPERM; + + if (!pm->clock_set) + return -EINVAL; + + if (!strncmp(profile, "boot", 4)) + perflvl = &pm->boot; + else { + int pl = simple_strtol(profile, NULL, 10); + int i; + + for (i = 0; i < pm->nr_perflvl; i++) { + if (pm->perflvl[i].id == pl) { + perflvl = &pm->perflvl[i]; + break; + } + } + + if (!perflvl) + return -EINVAL; + } + + if (perflvl == pm->cur) + return 0; + + NV_INFO(dev, "setting performance level: %s\n", profile); + if (pm->voltage.supported && pm->voltage_set && perflvl->voltage) { + ret = pm->voltage_set(dev, perflvl->voltage); + if (ret) { + NV_ERROR(dev, "voltage_set %d failed: %d\n", + perflvl->voltage, ret); + } + } + + nouveau_pm_clock_set(dev, PLL_CORE, perflvl->core); + nouveau_pm_clock_set(dev, PLL_SHADER, perflvl->shader); + nouveau_pm_clock_set(dev, PLL_MEMORY, perflvl->memory); + nouveau_pm_clock_set(dev, PLL_UNK05, perflvl->unk05); + + pm->cur = perflvl; + return 0; +} + static int nouveau_pm_perflvl_get(struct drm_device *dev, struct nouveau_pm_level *perflvl) { @@ -130,7 +202,13 @@ static ssize_t nouveau_pm_set_perflvl(struct device *d, struct device_attribute *a, const char *buf, size_t count) { - return -EPERM; + struct drm_device *dev = pci_get_drvdata(to_pci_dev(d)); + int ret; + + ret = nouveau_pm_profile_set(dev, buf); + if (ret) + return ret; + return strlen(buf); } DEVICE_ATTR(performance_level, S_IRUGO | S_IWUSR, @@ -163,6 +241,15 @@ 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); + } + } + /* initialise sysfs */ ret = device_create_file(d, &dev_attr_performance_level); if (ret) diff --git a/drivers/gpu/drm/nouveau/nv04_pm.c b/drivers/gpu/drm/nouveau/nv04_pm.c index 35c200eb476..15e4b9029df 100644 --- a/drivers/gpu/drm/nouveau/nv04_pm.c +++ b/drivers/gpu/drm/nouveau/nv04_pm.c @@ -50,7 +50,7 @@ nv04_pm_clock_pre(struct drm_device *dev, u32 id, int khz) ret = get_pll_limits(dev, id, &state->pll); if (ret) { kfree(state); - return ERR_PTR(ret); + return (ret == -ENOENT) ? NULL : ERR_PTR(ret); } ret = nouveau_calc_pll_mnp(dev, &state->pll, khz, &state->calc); diff --git a/drivers/gpu/drm/nouveau/nv50_pm.c b/drivers/gpu/drm/nouveau/nv50_pm.c index a616e424034..64bc29c39c3 100644 --- a/drivers/gpu/drm/nouveau/nv50_pm.c +++ b/drivers/gpu/drm/nouveau/nv50_pm.c @@ -80,7 +80,7 @@ nv50_pm_clock_pre(struct drm_device *dev, u32 id, int khz) ret = get_pll_limits(dev, id, &state->pll); if (ret < 0) { kfree(state); - return ERR_PTR(ret); + return (ret == -ENOENT) ? NULL : ERR_PTR(ret); } ret = nv50_calc_pll(dev, &state->pll, khz, &state->N, &state->M,