From mboxrd@z Thu Jan 1 00:00:00 1970 From: Martin Peres Subject: [Patch] Add a custom power management perflvl Date: Sun, 21 Nov 2010 03:47:43 +0100 Message-ID: <4CE8884F.10008@free.fr> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="------------020001050001020609060509" Return-path: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: nouveau-bounces+gcfxn-nouveau=m.gmane.org-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW@public.gmane.org Errors-To: nouveau-bounces+gcfxn-nouveau=m.gmane.org-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW@public.gmane.org To: nouveau List-Id: nouveau.vger.kernel.org This is a multi-part message in MIME format. --------------020001050001020609060509 Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit Hi everyone, Please comment on this patch allowing you to set all the PM-related clocks on the card. The patch is described in length in the commit log. There is something some of you will like and some won't. The custom_* files will always contain the values of the current perflvl unless the user changed them after the latest reclock. I may also add all the available voltage in sysfs in the same way Ben added all the available performance levels (performance_level_X). If you no-one has anything against it, please push it to master. Martin --------------020001050001020609060509 Content-Type: text/x-patch; name="0001-Add-a-custom-power-management-perflvl.patch" Content-Disposition: attachment; filename="0001-Add-a-custom-power-management-perflvl.patch" Content-Transfer-Encoding: quoted-printable >From 1df2984f21ba0bf034684b1b1287fb86a255a15c Mon Sep 17 00:00:00 2001 From: Martin Peres Date: Sat, 20 Nov 2010 18:29:45 +0100 Subject: [PATCH] Add a custom power management perflvl MIME-Version: 1.0 Content-Type: text/plain; charset=3DUTF-8 Content-Transfer-Encoding: 8bit This is to allow people to tweak their clocks at will. Marcin asked for something like this to help him REing. I was personnaly interested to see if openarena fps rate would scale with= the GPU clocks (it doesn't ... yet). This patch will also allow overclockers to test their cards (On my NVS 14= 0M: Memory +66%, core: +66%, shader: +25%). This patch introduces a few sysfs files: - custom_*: Get/set the current clock/voltage value (kHz or *10mV) Once you've set all the values you wanted to change, just echo "custom" i= nto performance_level. WARNINGS: 1) Pay attention to your current temperature, my card's temperature rose = by 9=C2=B0C. We'll need to use the thermal zones to be able to monitor that and downcl= ock the card automatically (if someone wants to help here, he would be we= lcome). 2) Changing clocks isn't safe at the moment, I'm working on this but the = reclocking process is really different accross the boards. So far, nv84 and nv86 work perfectly (as in no flicker and being able to = reclock while playing games). By the time I get this patch ready, please stop 3D applications, switch t= o a tty, do the reclock and come back to X. Signed-off-by: Martin Peres --- drivers/gpu/drm/nouveau/nouveau_drv.h | 1 + drivers/gpu/drm/nouveau/nouveau_pm.c | 265 +++++++++++++++++++++++++++= +++++- 2 files changed, 263 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouv= eau/nouveau_drv.h index a52b1da..5928301 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -463,6 +463,7 @@ struct nouveau_pm_engine { struct nouveau_pm_threshold_temp threshold_temp; =20 struct nouveau_pm_level boot; + struct nouveau_pm_level custom; struct nouveau_pm_level *cur; =20 struct device *hwmon; diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.c b/drivers/gpu/drm/nouve= au/nouveau_pm.c index d938141..7bcb27f 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.c +++ b/drivers/gpu/drm/nouveau/nouveau_pm.c @@ -35,6 +35,10 @@ #include =20 static int +nouveau_pm_perflvl_get(struct drm_device *dev, + struct nouveau_pm_level *perflvl); + +static int nouveau_pm_clock_set(struct drm_device *dev, struct nouveau_pm_level *pe= rflvl, u8 id, u32 khz) { @@ -61,7 +65,7 @@ nouveau_pm_perflvl_set(struct drm_device *dev, struct n= ouveau_pm_level *perflvl) struct nouveau_pm_engine *pm =3D &dev_priv->engine.pm; int ret; =20 - if (perflvl =3D=3D pm->cur) + if (perflvl !=3D &pm->custom && perflvl =3D=3D pm->cur) return 0; =20 if (pm->voltage.supported && pm->voltage_set && perflvl->voltage) { @@ -78,6 +82,10 @@ nouveau_pm_perflvl_set(struct drm_device *dev, struct = nouveau_pm_level *perflvl) nouveau_pm_clock_set(dev, perflvl, PLL_UNK05, perflvl->unk05); =20 pm->cur =3D perflvl; + + /* Copy the new profile to the custom profile */ + nouveau_pm_perflvl_get(dev, &pm->custom); + return 0; } =20 @@ -97,6 +105,8 @@ nouveau_pm_profile_set(struct drm_device *dev, const c= har *profile) =20 if (!strncmp(profile, "boot", 4)) perflvl =3D &pm->boot; + else if (!strncmp(profile, "custom", 6)) + perflvl =3D &pm->custom; else { int pl =3D simple_strtol(profile, NULL, 10); int i; @@ -208,6 +218,8 @@ nouveau_pm_get_perflvl(struct device *d, struct devic= e_attribute *a, char *buf) snprintf(ptr, len, "setting: boot\n"); else if (pm->cur =3D=3D &pm->boot) snprintf(ptr, len, "setting: boot\nc: "); + else if (pm->cur =3D=3D &pm->custom) + snprintf(ptr, len, "setting: custom\nclocks: "); else snprintf(ptr, len, "setting: static %d\nc: ", pm->cur->id); ptr +=3D strlen(buf); @@ -221,7 +233,7 @@ nouveau_pm_get_perflvl(struct device *d, struct devic= e_attribute *a, char *buf) =20 static ssize_t nouveau_pm_set_perflvl(struct device *d, struct device_attribute *a, - const char *buf, size_t count) + const char *buf, size_t count) { struct drm_device *dev =3D pci_get_drvdata(to_pci_dev(d)); int ret; @@ -233,7 +245,226 @@ nouveau_pm_set_perflvl(struct device *d, struct dev= ice_attribute *a, } =20 static DEVICE_ATTR(performance_level, S_IRUGO | S_IWUSR, - nouveau_pm_get_perflvl, nouveau_pm_set_perflvl); + nouveau_pm_get_perflvl, nouveau_pm_set_perflvl); + +static ssize_t +nouveau_pm_get_custom_core(struct device *d, + struct device_attribute *a, char *buf) +{ + struct drm_device *dev =3D pci_get_drvdata(to_pci_dev(d)); + struct drm_nouveau_private *dev_priv =3D dev->dev_private; + struct nouveau_pm_engine *pm =3D &dev_priv->engine.pm; + int len =3D PAGE_SIZE; + char *ptr =3D buf; + + snprintf(ptr, len, "%d\n", pm->custom.core); + return strlen(buf); +} + +static ssize_t +nouveau_pm_set_custom_core(struct device *d, struct device_attribute *a, + const char *buf, size_t count) +{ + struct drm_device *dev =3D pci_get_drvdata(to_pci_dev(d)); + struct drm_nouveau_private *dev_priv =3D dev->dev_private; + struct nouveau_pm_engine *pm =3D &dev_priv->engine.pm; + void *pre_state; + long sysfs_value; + + if (strict_strtol(buf, 10, &sysfs_value) =3D=3D -EINVAL) + return -EINVAL; + + if (sysfs_value < 2000) + return -EINVAL; + + pre_state =3D pm->clock_pre(dev, &pm->custom, PLL_CORE, + (u32) sysfs_value); + if (IS_ERR(pre_state)) + return -ENOENT; + else + kfree(pre_state); + + pm->custom.core =3D (u32) sysfs_value; + + return strlen(buf); +} + +static DEVICE_ATTR(custom_core, S_IRUGO | S_IWUSR, + nouveau_pm_get_custom_core, nouveau_pm_set_custom_core); + +static ssize_t +nouveau_pm_get_custom_memory(struct device *d, + struct device_attribute *a, char *buf) +{ + struct drm_device *dev =3D pci_get_drvdata(to_pci_dev(d)); + struct drm_nouveau_private *dev_priv =3D dev->dev_private; + struct nouveau_pm_engine *pm =3D &dev_priv->engine.pm; + int len =3D PAGE_SIZE; + char *ptr =3D buf; + + snprintf(ptr, len, "%d\n", pm->custom.memory); + return strlen(buf); +} + +static ssize_t +nouveau_pm_set_custom_memory(struct device *d, struct device_attribute *= a, + const char *buf, size_t count) +{ + struct drm_device *dev =3D pci_get_drvdata(to_pci_dev(d)); + struct drm_nouveau_private *dev_priv =3D dev->dev_private; + struct nouveau_pm_engine *pm =3D &dev_priv->engine.pm; + void *pre_state; + long sysfs_value; + + if (strict_strtol(buf, 10, &sysfs_value) =3D=3D -EINVAL) + return -EINVAL; + + if (sysfs_value < 2000) + return -EINVAL; + + pre_state =3D pm->clock_pre(dev, &pm->custom, PLL_MEMORY, + (u32) sysfs_value); + if (IS_ERR(pre_state)) + return -ENOENT; + else + kfree(pre_state); + + pm->custom.memory =3D (u32) sysfs_value; + + return strlen(buf); +} + +static DEVICE_ATTR(custom_memory, S_IRUGO | S_IWUSR, + nouveau_pm_get_custom_memory, nouveau_pm_set_custom_memory); + +static ssize_t +nouveau_pm_get_custom_shader(struct device *d, + struct device_attribute *a, char *buf) +{ + struct drm_device *dev =3D pci_get_drvdata(to_pci_dev(d)); + struct drm_nouveau_private *dev_priv =3D dev->dev_private; + struct nouveau_pm_engine *pm =3D &dev_priv->engine.pm; + int len =3D PAGE_SIZE; + char *ptr =3D buf; + + snprintf(ptr, len, "%d\n", pm->custom.shader); + return strlen(buf); +} + +static ssize_t +nouveau_pm_set_custom_shader(struct device *d, struct device_attribute *= a, + const char *buf, size_t count) +{ + struct drm_device *dev =3D pci_get_drvdata(to_pci_dev(d)); + struct drm_nouveau_private *dev_priv =3D dev->dev_private; + struct nouveau_pm_engine *pm =3D &dev_priv->engine.pm; + void *pre_state; + long sysfs_value; + + if (strict_strtol(buf, 10, &sysfs_value) =3D=3D -EINVAL) + return -EINVAL; + + if (sysfs_value < 2000) + return -EINVAL; + + pre_state =3D pm->clock_pre(dev, &pm->custom, PLL_SHADER, + (u32) sysfs_value); + if (IS_ERR(pre_state)) + return -ENOENT; + else + kfree(pre_state); + + pm->custom.shader =3D (u32) sysfs_value; + + return strlen(buf); +} + +static DEVICE_ATTR(custom_shader, S_IRUGO | S_IWUSR, + nouveau_pm_get_custom_shader, + nouveau_pm_set_custom_shader); + +static ssize_t +nouveau_pm_get_custom_unk05(struct device *d, + struct device_attribute *a, char *buf) +{ + struct drm_device *dev =3D pci_get_drvdata(to_pci_dev(d)); + struct drm_nouveau_private *dev_priv =3D dev->dev_private; + struct nouveau_pm_engine *pm =3D &dev_priv->engine.pm; + int len =3D PAGE_SIZE; + char *ptr =3D buf; + + snprintf(ptr, len, "%d\n", pm->custom.unk05); + return strlen(buf); +} + +static ssize_t +nouveau_pm_set_custom_unk05(struct device *d, struct device_attribute *a= , + const char *buf, size_t count) +{ + struct drm_device *dev =3D pci_get_drvdata(to_pci_dev(d)); + struct drm_nouveau_private *dev_priv =3D dev->dev_private; + struct nouveau_pm_engine *pm =3D &dev_priv->engine.pm; + void *pre_state; + long sysfs_value; + + if (strict_strtol(buf, 10, &sysfs_value) =3D=3D -EINVAL) + return -EINVAL; + + if (sysfs_value < 2000) + return -EINVAL; + + pre_state =3D pm->clock_pre(dev, &pm->custom, PLL_UNK05, + (u32) sysfs_value); + if (IS_ERR(pre_state)) + return -ENOENT; + else + kfree(pre_state); + + pm->custom.unk05 =3D (u32) sysfs_value; + + return strlen(buf); +} + +static DEVICE_ATTR(custom_unk05, S_IRUGO | S_IWUSR, + nouveau_pm_get_custom_unk05, nouveau_pm_set_custom_unk05); + +static ssize_t +nouveau_pm_get_custom_voltage(struct device *d, + struct device_attribute *a, char *buf) +{ + struct drm_device *dev =3D pci_get_drvdata(to_pci_dev(d)); + struct drm_nouveau_private *dev_priv =3D dev->dev_private; + struct nouveau_pm_engine *pm =3D &dev_priv->engine.pm; + int len =3D PAGE_SIZE; + char *ptr =3D buf; + + snprintf(ptr, len, "%d\n", pm->custom.voltage); + + return strlen(buf); +} + +static ssize_t +nouveau_pm_set_custom_voltage(struct device *d, struct device_attribute = *a, + const char *buf, size_t count) +{ + struct drm_device *dev =3D pci_get_drvdata(to_pci_dev(d)); + struct drm_nouveau_private *dev_priv =3D dev->dev_private; + struct nouveau_pm_engine *pm =3D &dev_priv->engine.pm; + long voltage_sysfs; + + if (strict_strtol(buf, 10, &voltage_sysfs) =3D=3D -EINVAL) + return -EINVAL; + + if (nouveau_volt_vid_lookup(dev, voltage_sysfs) < 0) + return -ENOENT; + + pm->custom.voltage =3D (u32) voltage_sysfs; + return strlen(buf); +} + +static DEVICE_ATTR(custom_voltage, S_IRUGO | S_IWUSR, + nouveau_pm_get_custom_voltage, + nouveau_pm_set_custom_voltage); =20 static int nouveau_sysfs_init(struct drm_device *dev) @@ -266,6 +497,26 @@ nouveau_sysfs_init(struct drm_device *dev) } } =20 + ret =3D device_create_file(d, &dev_attr_custom_core); + if (ret) + return ret; + + ret =3D device_create_file(d, &dev_attr_custom_memory); + if (ret) + return ret; + + ret =3D device_create_file(d, &dev_attr_custom_shader); + if (ret) + return ret; + + ret =3D device_create_file(d, &dev_attr_custom_unk05); + if (ret) + return ret; + + ret =3D device_create_file(d, &dev_attr_custom_voltage); + if (ret) + return ret; + return 0; } =20 @@ -286,6 +537,11 @@ nouveau_sysfs_fini(struct drm_device *dev) =20 device_remove_file(d, &pl->dev_attr); } + device_remove_file(d, &dev_attr_custom_core); + device_remove_file(d, &dev_attr_custom_memory); + device_remove_file(d, &dev_attr_custom_shader); + device_remove_file(d, &dev_attr_custom_unk05); + device_remove_file(d, &dev_attr_custom_voltage); } =20 #ifdef CONFIG_HWMON @@ -497,6 +753,9 @@ nouveau_pm_init(struct drm_device *dev) NV_INFO(dev, "c: %s", info); } =20 + /* set custom clocks to the boot performance level */ + nouveau_pm_perflvl_get(dev, &pm->custom); + /* switch performance levels now if requested */ if (nouveau_perflvl !=3D NULL) { ret =3D nouveau_pm_profile_set(dev, nouveau_perflvl); --=20 1.7.3.2 --------------020001050001020609060509 Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Disposition: inline _______________________________________________ Nouveau mailing list Nouveau-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW@public.gmane.org http://lists.freedesktop.org/mailman/listinfo/nouveau --------------020001050001020609060509--