[Nouveau] [PATCH 1/2] drm/nouveau/pm: Add pm.(un)pause functions

2011-04-29 Thread Martin Peres
From: Martin Peres martin.pe...@ensi-bourges.fr

With this patch, cards without internal memory (IONs and other IGPs)
and cards with no memory reclock (a lot of nv40) should support
safe reclocking while gaming.

This should work on all hardware( nva3), report bugs if it doesn't.

v2: Fix missing symbol at compilation on x86_32 systems
v3: Better voltage management

Signed-off-by: Martin Peres martin.pe...@ensi-bourges.fr
---
 drivers/gpu/drm/nouveau/nouveau_drv.h   |9 ++
 drivers/gpu/drm/nouveau/nouveau_pm.c|   57 +--
 drivers/gpu/drm/nouveau/nouveau_pm.h|4 +
 drivers/gpu/drm/nouveau/nouveau_reg.h   |3 +
 drivers/gpu/drm/nouveau/nouveau_state.c |   13 ++-
 drivers/gpu/drm/nouveau/nv04_pm.c   |  126 ++
 drivers/gpu/drm/nouveau/nv50_pm.c   |  172 +++
 7 files changed, 374 insertions(+), 10 deletions(-)

diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h 
b/drivers/gpu/drm/nouveau/nouveau_drv.h
index 9c56331..01167fd 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drv.h
+++ b/drivers/gpu/drm/nouveau/nouveau_drv.h
@@ -462,6 +462,10 @@ struct nouveau_pm_memtimings {
int nr_timing;
 };
 
+struct nouveau_pm_pause_card_state {
+   u32 reg_c040;
+};
+
 struct nouveau_pm_engine {
struct nouveau_pm_voltage voltage;
struct nouveau_pm_level perflvl[NOUVEAU_PM_MAX_LEVEL];
@@ -476,6 +480,11 @@ struct nouveau_pm_engine {
struct device *hwmon;
struct notifier_block acpi_nb;
 
+   struct nouveau_pm_pause_card_state pause_state;
+
+   int (*pause)(struct drm_device *);
+   void (*unpause)(struct drm_device *);
+
int (*clock_get)(struct drm_device *, u32 id);
void *(*clock_pre)(struct drm_device *, struct nouveau_pm_level *,
   u32 id, int khz);
diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.c 
b/drivers/gpu/drm/nouveau/nouveau_pm.c
index da8d994..88f58b1 100644
--- a/drivers/gpu/drm/nouveau/nouveau_pm.c
+++ b/drivers/gpu/drm/nouveau/nouveau_pm.c
@@ -45,6 +45,10 @@ nouveau_pm_clock_set(struct drm_device *dev, struct 
nouveau_pm_level *perflvl,
if (khz == 0)
return 0;
 
+   /* Do no reclock the memory if the frequencies didn't change */
+   if (id == PLL_MEMORY  pm-cur-memory == khz)
+   return 0;
+
pre_state = pm-clock_pre(dev, perflvl, id, khz);
if (IS_ERR(pre_state))
return PTR_ERR(pre_state);
@@ -55,30 +59,66 @@ nouveau_pm_clock_set(struct drm_device *dev, struct 
nouveau_pm_level *perflvl,
 }
 
 static int
+nouveau_pm_voltage_set(struct drm_device *dev, u8 voltage)
+{
+   struct drm_nouveau_private *dev_priv = dev-dev_private;
+   struct nouveau_pm_engine *pm = dev_priv-engine.pm;
+   int ret;
+
+   if (pm-voltage.supported  pm-voltage_set  voltage) {
+   ret = pm-voltage_set(dev, voltage);
+   if (ret) {
+   NV_ERROR(dev, voltage_set %d failed: %d\n,
+voltage, ret);
+   }
+
+   return ret;
+   } else
+   return -EIO;
+}
+
+static int
 nouveau_pm_perflvl_set(struct drm_device *dev, struct nouveau_pm_level 
*perflvl)
 {
struct drm_nouveau_private *dev_priv = dev-dev_private;
struct nouveau_pm_engine *pm = dev_priv-engine.pm;
int ret;
+   uint64_t start = nv04_timer_read(dev);
 
if (perflvl == pm-cur)
return 0;
 
-   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);
-   }
-   }
+   NV_INFO(dev, setting performance level: %s\n, perflvl-name);
+
+   ret = pm-pause(dev);
+   if (ret)
+   return ret;
+
+   /* Increase the voltage now if needed */
+   if (perflvl-voltage  pm-cur-voltage)
+   nouveau_pm_voltage_set(dev, perflvl-voltage);
 
nouveau_pm_clock_set(dev, perflvl, PLL_CORE, perflvl-core);
nouveau_pm_clock_set(dev, perflvl, PLL_SHADER, perflvl-shader);
nouveau_pm_clock_set(dev, perflvl, PLL_MEMORY, perflvl-memory);
nouveau_pm_clock_set(dev, perflvl, PLL_UNK05, perflvl-unk05);
 
+   /* Decrease the voltage if needed*/
+   if (perflvl-voltage  pm-cur-voltage)
+   nouveau_pm_voltage_set(dev, perflvl-voltage);
+
+   /* Wait for PLLs to stabilize */
+   udelay(100);
+
pm-cur = perflvl;
-   return 0;
+   ret = 0;
+
+   pm-unpause(dev);
+
+   NV_DEBUG(dev, Reclocking took %lluns\n,
+(nv04_timer_read(dev) - start));
+
+   return ret;
 }
 
 static int
@@ -112,7 +152,6 @@ nouveau_pm_profile_set(struct drm_device *dev, const char 
*profile)
return -EINVAL;
}
 
-   NV_INFO(dev, 

[Nouveau] [PATCH 1/2] drm/nouveau/pm: Add pm.(un)pause functions

2011-04-28 Thread Martin Peres
From: Martin Peres martin.pe...@ensi-bourges.fr

With this patch, cards without internal memory (IONs and other IGPs)
and cards with no memory reclock (a lot of nv40) should support
safe reclocking while gaming.

This should work on all hardware( nva3), report bugs if it doesn't.

v2: Fix missing symbol at compilation on x86_32 systems
v3: Better voltage management

Signed-off-by: Martin Peres martin.pe...@ensi-bourges.fr
---
 drivers/gpu/drm/nouveau/nouveau_drv.h   |9 ++
 drivers/gpu/drm/nouveau/nouveau_pm.c|   57 +--
 drivers/gpu/drm/nouveau/nouveau_pm.h|4 +
 drivers/gpu/drm/nouveau/nouveau_reg.h   |3 +
 drivers/gpu/drm/nouveau/nouveau_state.c |   13 ++-
 drivers/gpu/drm/nouveau/nv04_pm.c   |  126 ++
 drivers/gpu/drm/nouveau/nv50_pm.c   |  172 +++
 7 files changed, 374 insertions(+), 10 deletions(-)

diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h 
b/drivers/gpu/drm/nouveau/nouveau_drv.h
index 9c56331..01167fd 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drv.h
+++ b/drivers/gpu/drm/nouveau/nouveau_drv.h
@@ -462,6 +462,10 @@ struct nouveau_pm_memtimings {
int nr_timing;
 };
 
+struct nouveau_pm_pause_card_state {
+   u32 reg_c040;
+};
+
 struct nouveau_pm_engine {
struct nouveau_pm_voltage voltage;
struct nouveau_pm_level perflvl[NOUVEAU_PM_MAX_LEVEL];
@@ -476,6 +480,11 @@ struct nouveau_pm_engine {
struct device *hwmon;
struct notifier_block acpi_nb;
 
+   struct nouveau_pm_pause_card_state pause_state;
+
+   int (*pause)(struct drm_device *);
+   void (*unpause)(struct drm_device *);
+
int (*clock_get)(struct drm_device *, u32 id);
void *(*clock_pre)(struct drm_device *, struct nouveau_pm_level *,
   u32 id, int khz);
diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.c 
b/drivers/gpu/drm/nouveau/nouveau_pm.c
index da8d994..88f58b1 100644
--- a/drivers/gpu/drm/nouveau/nouveau_pm.c
+++ b/drivers/gpu/drm/nouveau/nouveau_pm.c
@@ -45,6 +45,10 @@ nouveau_pm_clock_set(struct drm_device *dev, struct 
nouveau_pm_level *perflvl,
if (khz == 0)
return 0;
 
+   /* Do no reclock the memory if the frequencies didn't change */
+   if (id == PLL_MEMORY  pm-cur-memory == khz)
+   return 0;
+
pre_state = pm-clock_pre(dev, perflvl, id, khz);
if (IS_ERR(pre_state))
return PTR_ERR(pre_state);
@@ -55,30 +59,66 @@ nouveau_pm_clock_set(struct drm_device *dev, struct 
nouveau_pm_level *perflvl,
 }
 
 static int
+nouveau_pm_voltage_set(struct drm_device *dev, u8 voltage)
+{
+   struct drm_nouveau_private *dev_priv = dev-dev_private;
+   struct nouveau_pm_engine *pm = dev_priv-engine.pm;
+   int ret;
+
+   if (pm-voltage.supported  pm-voltage_set  voltage) {
+   ret = pm-voltage_set(dev, voltage);
+   if (ret) {
+   NV_ERROR(dev, voltage_set %d failed: %d\n,
+voltage, ret);
+   }
+
+   return ret;
+   } else
+   return -EIO;
+}
+
+static int
 nouveau_pm_perflvl_set(struct drm_device *dev, struct nouveau_pm_level 
*perflvl)
 {
struct drm_nouveau_private *dev_priv = dev-dev_private;
struct nouveau_pm_engine *pm = dev_priv-engine.pm;
int ret;
+   uint64_t start = nv04_timer_read(dev);
 
if (perflvl == pm-cur)
return 0;
 
-   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);
-   }
-   }
+   NV_INFO(dev, setting performance level: %s\n, perflvl-name);
+
+   ret = pm-pause(dev);
+   if (ret)
+   return ret;
+
+   /* Increase the voltage now if needed */
+   if (perflvl-voltage  pm-cur-voltage)
+   nouveau_pm_voltage_set(dev, perflvl-voltage);
 
nouveau_pm_clock_set(dev, perflvl, PLL_CORE, perflvl-core);
nouveau_pm_clock_set(dev, perflvl, PLL_SHADER, perflvl-shader);
nouveau_pm_clock_set(dev, perflvl, PLL_MEMORY, perflvl-memory);
nouveau_pm_clock_set(dev, perflvl, PLL_UNK05, perflvl-unk05);
 
+   /* Decrease the voltage if needed*/
+   if (perflvl-voltage  pm-cur-voltage)
+   nouveau_pm_voltage_set(dev, perflvl-voltage);
+
+   /* Wait for PLLs to stabilize */
+   udelay(100);
+
pm-cur = perflvl;
-   return 0;
+   ret = 0;
+
+   pm-unpause(dev);
+
+   NV_DEBUG(dev, Reclocking took %lluns\n,
+(nv04_timer_read(dev) - start));
+
+   return ret;
 }
 
 static int
@@ -112,7 +152,6 @@ nouveau_pm_profile_set(struct drm_device *dev, const char 
*profile)
return -EINVAL;
}
 
-   NV_INFO(dev,