All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/2] safer reclocking take 2
@ 2011-04-28 18:33 Martin Peres
       [not found] ` <1304015622-5910-1-git-send-email-martin.peres-GANU6spQydw@public.gmane.org>
  0 siblings, 1 reply; 10+ messages in thread
From: Martin Peres @ 2011-04-28 18:33 UTC (permalink / raw)
  To: nouveau-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW

Hi everyone,

This new version of the previous patchset features some memory reclocking and voltage management improvements.

Memory: Reclock it after all the other engines so as we don't need to reclock it back again if pausing didn't work.
Voltage: Bump the voltage before reclocking or lower it after reclocking. It should increase the stability when downclocking.

If you didn't test the previous pathset, test this one. If the former worked for you, this one will too.

Martin

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH 1/2] drm/nouveau/pm: Add pm.(un)pause functions
       [not found] ` <1304015622-5910-1-git-send-email-martin.peres-GANU6spQydw@public.gmane.org>
@ 2011-04-28 18:33   ` Martin Peres
  2011-04-28 18:33   ` [PATCH 2/2] drm/nouveau/nv50: reclock memory using PMS on nv50 Martin Peres
  2011-04-30  0:17   ` (no subject) Martin Peres
  2 siblings, 0 replies; 10+ messages in thread
From: Martin Peres @ 2011-04-28 18:33 UTC (permalink / raw)
  To: nouveau-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW; +Cc: Martin Peres

From: Martin Peres <martin.peres-Iz16wY1oaNPLSKGbIzaifA@public.gmane.org>

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.peres-Iz16wY1oaNPLSKGbIzaifA@public.gmane.org>
---
 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, "setting performance level: %s\n", profile);
 	return nouveau_pm_perflvl_set(dev, perflvl);
 }
 
diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.h b/drivers/gpu/drm/nouveau/nouveau_pm.h
index 4a9838dd..566f72d 100644
--- a/drivers/gpu/drm/nouveau/nouveau_pm.h
+++ b/drivers/gpu/drm/nouveau/nouveau_pm.h
@@ -51,12 +51,16 @@ int nv04_pm_clock_get(struct drm_device *, u32 id);
 void *nv04_pm_clock_pre(struct drm_device *, struct nouveau_pm_level *,
 			u32 id, int khz);
 void nv04_pm_clock_set(struct drm_device *, void *);
+int nv04_pm_pause(struct drm_device *dev);
+void nv04_pm_unpause(struct drm_device *dev);
 
 /* nv50_pm.c */
 int nv50_pm_clock_get(struct drm_device *, u32 id);
 void *nv50_pm_clock_pre(struct drm_device *, struct nouveau_pm_level *,
 			u32 id, int khz);
 void nv50_pm_clock_set(struct drm_device *, void *);
+int nv50_pm_pause(struct drm_device *dev);
+void nv50_pm_unpause(struct drm_device *dev);
 
 /* nva3_pm.c */
 int nva3_pm_clock_get(struct drm_device *, u32 id);
diff --git a/drivers/gpu/drm/nouveau/nouveau_reg.h b/drivers/gpu/drm/nouveau/nouveau_reg.h
index f18cdfc..485d7d0 100644
--- a/drivers/gpu/drm/nouveau/nouveau_reg.h
+++ b/drivers/gpu/drm/nouveau/nouveau_reg.h
@@ -695,8 +695,11 @@
 #define NV50_PROM__ESIZE                                       0x10000
 
 #define NV50_PGRAPH                                         0x00400000
+#define NV50_PGRAPH_CONTROL                                 0x00400500
+#define NV50_PGRAPH_FIFO_STATUS                             0x00400504
 #define NV50_PGRAPH__LEN                                           0x1
 #define NV50_PGRAPH__ESIZE                                     0x10000
+#define NV50_PFIFO_FREEZE                                       0x2504
 
 #define NV50_PDISPLAY                                                0x00610000
 #define NV50_PDISPLAY_OBJECTS                                        0x00610010
diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c
index 38ea662..3fc8455 100644
--- a/drivers/gpu/drm/nouveau/nouveau_state.c
+++ b/drivers/gpu/drm/nouveau/nouveau_state.c
@@ -90,6 +90,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
 		engine->pm.clock_get		= nv04_pm_clock_get;
 		engine->pm.clock_pre		= nv04_pm_clock_pre;
 		engine->pm.clock_set		= nv04_pm_clock_set;
+		engine->pm.pause		= nv04_pm_pause;
+		engine->pm.unpause		= nv04_pm_unpause;
 		engine->vram.init		= nouveau_mem_detect;
 		engine->vram.flags_valid	= nouveau_mem_flags_valid;
 		break;
@@ -138,6 +140,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
 		engine->pm.clock_get		= nv04_pm_clock_get;
 		engine->pm.clock_pre		= nv04_pm_clock_pre;
 		engine->pm.clock_set		= nv04_pm_clock_set;
+		engine->pm.pause		= nv04_pm_pause;
+		engine->pm.unpause		= nv04_pm_unpause;
 		engine->vram.init		= nouveau_mem_detect;
 		engine->vram.flags_valid	= nouveau_mem_flags_valid;
 		break;
@@ -186,6 +190,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
 		engine->pm.clock_get		= nv04_pm_clock_get;
 		engine->pm.clock_pre		= nv04_pm_clock_pre;
 		engine->pm.clock_set		= nv04_pm_clock_set;
+		engine->pm.pause		= nv04_pm_pause;
+		engine->pm.unpause		= nv04_pm_unpause;
 		engine->vram.init		= nouveau_mem_detect;
 		engine->vram.flags_valid	= nouveau_mem_flags_valid;
 		break;
@@ -236,6 +242,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
 		engine->pm.clock_set		= nv04_pm_clock_set;
 		engine->pm.voltage_get		= nouveau_voltage_gpio_get;
 		engine->pm.voltage_set		= nouveau_voltage_gpio_set;
+		engine->pm.pause		= nv04_pm_pause;
+		engine->pm.unpause		= nv04_pm_unpause;
 		engine->vram.init		= nouveau_mem_detect;
 		engine->vram.flags_valid	= nouveau_mem_flags_valid;
 		break;
@@ -288,6 +296,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
 		engine->pm.voltage_get		= nouveau_voltage_gpio_get;
 		engine->pm.voltage_set		= nouveau_voltage_gpio_set;
 		engine->pm.temp_get		= nv40_temp_get;
+		engine->pm.pause		= nv04_pm_pause;
+		engine->pm.unpause		= nv04_pm_unpause;
 		engine->vram.init		= nouveau_mem_detect;
 		engine->vram.flags_valid	= nouveau_mem_flags_valid;
 		break;
@@ -361,6 +371,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
 		}
 		engine->pm.voltage_get		= nouveau_voltage_gpio_get;
 		engine->pm.voltage_set		= nouveau_voltage_gpio_set;
+		engine->pm.pause		= nv50_pm_pause;
+		engine->pm.unpause		= nv50_pm_unpause;
 		if (dev_priv->chipset >= 0x84)
 			engine->pm.temp_get	= nv84_temp_get;
 		else
@@ -1137,4 +1149,3 @@ bool nouveau_wait_for_idle(struct drm_device *dev)
 
 	return true;
 }
-
diff --git a/drivers/gpu/drm/nouveau/nv04_pm.c b/drivers/gpu/drm/nouveau/nv04_pm.c
index eb1c70d..6f5ad051 100644
--- a/drivers/gpu/drm/nouveau/nv04_pm.c
+++ b/drivers/gpu/drm/nouveau/nv04_pm.c
@@ -88,3 +88,129 @@ nv04_pm_clock_set(struct drm_device *dev, void *pre_state)
 	kfree(state);
 }
 
+int
+nv04_pm_pause(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
+	unsigned long irq_flags, hold_spin = 0;
+	/* initial guess... */
+	uint32_t mask300 = 0xffffffff;
+	uint32_t mask700 = 0xffffbfff;
+	uint64_t start = nv04_timer_read(dev);
+
+	/* Do not allow the card to allocate/destroy a
+	 * new channel while reclocking.
+	 *
+	 * We try to hold it for the shortest period of time possible
+	 */
+	spin_lock_irqsave(&dev_priv->context_switch_lock, irq_flags);
+	hold_spin = 1;
+
+	/* Don't context switch */
+	nv04_fifo_reassign(dev, false);
+
+	/* PDISPLAY magic */
+	nv_mask(dev, NV50_PDISPLAY_PIO_CTRL, 0x0, 0x1);
+
+	/* Pause PFIFO's puller */
+	nv04_fifo_cache_pull(dev, false);
+
+	/* Wait for PFIFO's DMA_PUSH to deplete (Not busy) */
+	if (!nouveau_wait_eq(dev, 100000, NV04_PFIFO_CACHE1_DMA_PUSH,
+				0x100, 0x100)) {
+		NV_ERROR(dev, "PFIFO DMA_PUSH never depletes (0x%x)\n",
+			nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_PUSH));
+		goto err_pfifo_freeze;
+	}
+	nv_mask(dev, NV04_PFIFO_CACHE1_DMA_PUSH, 0x1, 0);
+
+	/* Pause PGRAPH's FIFO */
+	nv_wr32(dev, NV04_PGRAPH_FIFO, 0);
+
+	/* Now that the card is paused,
+	 * there is no problem with channel creation
+	 */
+	spin_unlock_irqrestore(&dev_priv->context_switch_lock, irq_flags);
+	hold_spin = 0;
+
+	/* Wait for PGRAPH to be really stopped */
+	if (!nouveau_wait_eq(dev, 1000000, 0x400300, mask300, 0x4) ||
+		!nouveau_wait_eq(dev, 8000000, NV04_PGRAPH_STATUS,
+				mask700, 0x0)) {
+		/* if you see this message,
+		* mask* above probably need to be adjusted
+		* to not contain the bits you see failing */
+		NV_ERROR(dev,
+		    "PGRAPH: wait for idle fail: %08x %08x!\n",
+		    nv_rd32(dev, NV04_PGRAPH_STATUS),
+		    nv_rd32(dev, 0x400300));
+
+		goto err_pgraph;
+	}
+
+	if (dev_priv->card_type == NV_40)
+		pm->pause_state.reg_c040 = nv_mask(dev, 0xc040, 0x333, 0);
+
+	NV_DEBUG(dev, "PM.pause took %lluns\n",
+		(nv04_timer_read(dev) - start));
+
+	return 0;
+
+err_pgraph:
+	nv_wr32(dev, NV04_PGRAPH_FIFO, 1);
+
+	nv_mask(dev, NV04_PFIFO_CACHE1_DMA_PUSH, 0, 0x1);
+
+err_pfifo_freeze:
+	nv04_fifo_cache_pull(dev, true);
+	nv04_fifo_reassign(dev, true);
+
+	/* PDISPLAY magic */
+	nv_mask(dev, NV50_PDISPLAY_PIO_CTRL, 0x1, 0x0);
+
+	if (hold_spin)
+		spin_unlock_irqrestore(&dev_priv->context_switch_lock,
+					irq_flags);
+
+	return -EAGAIN;
+}
+
+void
+nv04_pm_unpause(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
+	unsigned long irq_flags;
+
+	/* Do not allow the card to allocate/destroy a
+	 * new channel while unpausing.
+	 */
+	spin_lock_irqsave(&dev_priv->context_switch_lock, irq_flags);
+
+	if (dev_priv->card_type == NV_40) {
+		nv_wr32(dev, 0xc040, pm->pause_state.reg_c040);
+		nv_wr32(dev, 0xc04c, nv_rd32(dev, 0xc04c));
+	}
+
+	/* Unpause PGRAPH */
+	nv_wr32(dev, NV04_PGRAPH_FIFO, 1);
+
+	/* Unpause pfifo caches */
+	nv_mask(dev, NV04_PFIFO_CACHE1_DMA_PUSH, 0, 0x1);
+	nv04_fifo_cache_pull(dev, true);
+	nv04_fifo_reassign(dev, true);
+
+	/* PDISPLAY magic */
+	nv_mask(dev, NV50_PDISPLAY_PIO_CTRL, 0x1, 0x0);
+
+	/* TODO: De-activated for the moment, it makes things unstable */
+#if 0
+	if (dev_priv->card_type == NV_40) {
+		nv_wr32(dev, 0x1580, nv_rd32(dev, 0x1580));
+		nv_wr32(dev, 0xc044, nv_rd32(dev, 0xc44));
+	}
+#endif
+
+	spin_unlock_irqrestore(&dev_priv->context_switch_lock, irq_flags);
+}
diff --git a/drivers/gpu/drm/nouveau/nv50_pm.c b/drivers/gpu/drm/nouveau/nv50_pm.c
index 8a28100..4dd2d76 100644
--- a/drivers/gpu/drm/nouveau/nv50_pm.c
+++ b/drivers/gpu/drm/nouveau/nv50_pm.c
@@ -130,6 +130,7 @@ nv50_pm_clock_set(struct drm_device *dev, void *pre_state)
 		nv_wr32(dev, 0x100210, 0);
 		nv_wr32(dev, 0x1002dc, 1);
 	}
+	/* TODO: Tweek 0x4700 before reclocking UNK05 */
 
 	tmp  = nv_rd32(dev, reg + 0) & 0xfff8ffff;
 	tmp |= 0x80000000 | (P << 16);
@@ -144,3 +145,174 @@ nv50_pm_clock_set(struct drm_device *dev, void *pre_state)
 	kfree(state);
 }
 
+int
+nv50_pm_pause(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
+	unsigned long irq_flags, hold_spin = 0;
+	/* initial guess... */
+	uint32_t mask380 = 0xffffffff;
+	uint32_t mask384 = 0xffffffff;
+	uint32_t mask388 = 0xffffffff;
+	uint32_t mask504 = 0x00000001;
+	uint32_t mask700 = 0x00000001;
+	int i = 0;
+	uint64_t start = nv04_timer_read(dev);
+
+	/* Do not allow the card to allocate/destroy a
+	 * new channel while reclocking.
+	 *
+	 * We try to hold it for the shortest period of time possible
+	 */
+	spin_lock_irqsave(&dev_priv->context_switch_lock, irq_flags);
+	hold_spin = 1;
+
+	/* Don't context switch */
+	nv04_fifo_reassign(dev, false);
+
+	/* PDISPLAY magic */
+	nv_mask(dev, NV50_PDISPLAY_PIO_CTRL, 0x0, 0x1);
+
+	nv_wr32(dev, NV50_PFIFO_FREEZE, 1);
+	if (!nouveau_wait_eq(dev, 100000, NV50_PFIFO_FREEZE, 0x10, 0x10)) {
+		NV_ERROR(dev, "PFIFO freeze failed\n");
+		goto err_pfifo_freeze;
+	}
+
+	/* Wait for PFIFO's DMA_PUSH to deplete */
+	if (!nouveau_wait_eq(dev, 100000, NV04_PFIFO_CACHE1_DMA_PUSH,
+				0x100, 0x100)) {
+		NV_ERROR(dev, "PFIFO DMA_PUSH never depleted (0x%x)\n",
+			nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_PUSH));
+		goto err_pfifo_freeze;
+	}
+
+	/* Pause PFIFO's caches */
+	nv04_fifo_cache_pull(dev, false);
+	nv_mask(dev, NV04_PFIFO_CACHE1_DMA_PUSH, 0x1, 0);
+
+	/* Empty PGRAPH's FIFO */
+	do {
+		/* Un-pause PGRAPH's FIFO (in case it was) */
+		nv_mask(dev, NV50_PGRAPH_CONTROL, 0, 0x1);
+
+		/* Wait for PGRAPH's FIFO to deplete */
+		if (!nouveau_wait_eq(dev, 100000, NV50_PGRAPH_FIFO_STATUS,
+					mask504, 0x1)) {
+			if (nv_rd32(dev, NV04_PGRAPH_STATUS) & 0x100) {
+				NV_ERROR(dev,
+				"PGRAPH: PGRAPH paused while running a ctxprog,"
+				" NV40_PGRAPH_CTXCTL_0310 = 0x%x\n",
+				nv_rd32(dev, NV40_PGRAPH_CTXCTL_0310));
+			}
+
+			goto err_ctx_prog_playing;
+		}
+
+		/* Pause PGRAPH's FIFO */
+		nv_mask(dev, NV50_PGRAPH_CONTROL, 0x1, 0);
+
+		/* Limit the number of loops to 2 */
+		i++;
+		if (i > 1)
+			goto err_pgraph_stop;
+	} while ((nv_rd32(dev, NV50_PGRAPH_FIFO_STATUS) & mask504) == 0);
+
+	/* Now that the PGRAPH's FIFO is paused,
+	 * there is no problem with channel creation.
+	 */
+	spin_unlock_irqrestore(&dev_priv->context_switch_lock, irq_flags);
+	hold_spin = 0;
+
+	/* Wait for PGRAPH engines to stop */
+	if (!nouveau_wait_eq(dev, 100000, 0x400380, mask380, 0x0) ||
+	    !nouveau_wait_eq(dev, 100000, 0x400384, mask384, 0x0) ||
+	    !nouveau_wait_eq(dev, 100000, 0x400388, mask388, 0x0) ||
+	    !nouveau_wait_eq(dev, 500000, NV04_PGRAPH_STATUS, mask700, 0x0)) {
+		/* if you see this message,
+		* mask* above probably need to be adjusted
+		* to not contain the bits you see failing */
+		NV_ERROR(dev,
+		    "PGRAPH: wait for idle fail: %08x %08x %08x %08x %08x!\n",
+		    nv_rd32(dev, 0x400380),
+		    nv_rd32(dev, 0x400384),
+		    nv_rd32(dev, 0x400388),
+		    nv_rd32(dev, NV50_PGRAPH_FIFO_STATUS),
+		    nv_rd32(dev, NV04_PGRAPH_STATUS));
+
+		goto err_pgraph_stop;
+	}
+
+	/* De-activate the PLLs */
+	pm->pause_state.reg_c040 = nv_mask(dev, 0xc040, 0x30, 0x100000);
+
+	NV_DEBUG(dev, "PM.pause took %lluns\n",
+		   (nv04_timer_read(dev) - start));
+
+	return 0;
+
+err_pgraph_stop:
+	nv_mask(dev, NV50_PGRAPH_CONTROL, 0, 0x1);
+
+err_ctx_prog_playing:
+	nv_mask(dev, NV04_PFIFO_CACHE1_DMA_PUSH, 0, 0x1);
+	nv04_fifo_cache_pull(dev, true);
+
+err_pfifo_freeze:
+	nv_wr32(dev, NV50_PFIFO_FREEZE, 0);
+
+	nv_mask(dev, NV50_PDISPLAY_PIO_CTRL, 0x1, 0x0);
+
+	nv04_fifo_reassign(dev, true);
+
+	if (hold_spin)
+		spin_unlock_irqrestore(&dev_priv->context_switch_lock,
+					irq_flags);
+	return -EAGAIN;
+}
+
+void
+nv50_pm_unpause(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
+	unsigned long irq_flags;
+
+	/* Do not allow the card to allocate/destroy a
+	 * new channel while unpausing.
+	 */
+	spin_lock_irqsave(&dev_priv->context_switch_lock, irq_flags);
+
+	/* Restore the PLL supervisor state */
+	nv_wr32(dev, 0xc040, pm->pause_state.reg_c040);
+	nv_wr32(dev, 0xc04c, 0x10);
+	nv_wr32(dev, 0xc040, pm->pause_state.reg_c040);
+
+	/* Unpause pfifo caches */
+	nv_mask(dev, NV04_PFIFO_CACHE1_DMA_PUSH, 0, 0x1);
+	nv04_fifo_cache_pull(dev, true);
+
+	/* Unpause PGRAPH */
+	nv_mask(dev, NV50_PGRAPH_CONTROL, 0, 0x1);
+
+	/* Un-pause PFIFO */
+	nv_wr32(dev, NV50_PFIFO_FREEZE, 0);
+
+	/* PDISPLAY magic */
+	nv_wr32(dev, 0x616308, 0x10);
+	nv_wr32(dev, 0x616b08, 0x10);
+	nv_mask(dev, NV50_PDISPLAY_PIO_CTRL, 0x1, 0x0);
+
+	/* Re-allow context switch */
+	nv04_fifo_reassign(dev, true);
+
+	/* the blob also clear c040's bit 26 using PMS when the
+	 * performance level is set to 0.
+	 * I haven't seen difference in power consumption, so,
+	 * I leave it for later.
+	 */
+	nv_wr32(dev, 0xc040, pm->pause_state.reg_c040);
+
+	spin_unlock_irqrestore(&dev_priv->context_switch_lock, irq_flags);
+}
-- 
1.7.4.4

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 2/2] drm/nouveau/nv50: reclock memory using PMS on nv50
       [not found] ` <1304015622-5910-1-git-send-email-martin.peres-GANU6spQydw@public.gmane.org>
  2011-04-28 18:33   ` [PATCH 1/2] drm/nouveau/pm: Add pm.(un)pause functions Martin Peres
@ 2011-04-28 18:33   ` Martin Peres
  2011-04-30  0:17   ` (no subject) Martin Peres
  2 siblings, 0 replies; 10+ messages in thread
From: Martin Peres @ 2011-04-28 18:33 UTC (permalink / raw)
  To: nouveau-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW; +Cc: Martin Peres

From: Martin Peres <martin.peres-Iz16wY1oaNPLSKGbIzaifA@public.gmane.org>

v2: Reclock memory after reclocking the other engines

Signed-off-by: Martin Peres <martin.peres-Iz16wY1oaNPLSKGbIzaifA@public.gmane.org>
---
 drivers/gpu/drm/nouveau/nouveau_pm.c |   11 +--
 drivers/gpu/drm/nouveau/nv50_pm.c    |  153 +++++++++++++++++++++++++++++++---
 2 files changed, 144 insertions(+), 20 deletions(-)

diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.c b/drivers/gpu/drm/nouveau/nouveau_pm.c
index 88f58b1..44d01bb 100644
--- a/drivers/gpu/drm/nouveau/nouveau_pm.c
+++ b/drivers/gpu/drm/nouveau/nouveau_pm.c
@@ -45,10 +45,6 @@ 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);
@@ -100,7 +96,6 @@ nouveau_pm_perflvl_set(struct drm_device *dev, struct nouveau_pm_level *perflvl)
 
 	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*/
@@ -110,11 +105,13 @@ nouveau_pm_perflvl_set(struct drm_device *dev, struct nouveau_pm_level *perflvl)
 	/* Wait for PLLs to stabilize */
 	udelay(100);
 
+	pm->unpause(dev);
+
+	nouveau_pm_clock_set(dev, perflvl, PLL_MEMORY, perflvl->memory);
+
 	pm->cur = perflvl;
 	ret = 0;
 
-	pm->unpause(dev);
-
 	NV_DEBUG(dev, "Reclocking took %lluns\n",
 		 (nv04_timer_read(dev) - start));
 
diff --git a/drivers/gpu/drm/nouveau/nv50_pm.c b/drivers/gpu/drm/nouveau/nv50_pm.c
index 4dd2d76..9b81f03 100644
--- a/drivers/gpu/drm/nouveau/nv50_pm.c
+++ b/drivers/gpu/drm/nouveau/nv50_pm.c
@@ -26,9 +26,11 @@
 #include "nouveau_drv.h"
 #include "nouveau_bios.h"
 #include "nouveau_pm.h"
+#include "nouveau_pms.h"
 
 struct nv50_pm_state {
 	struct nouveau_pm_level *perflvl;
+	struct pms_ucode ucode;
 	struct pll_lims pll;
 	enum pll_types type;
 	int N, M, P;
@@ -73,14 +75,20 @@ void *
 nv50_pm_clock_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl,
 		  u32 id, int khz)
 {
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
 	struct nv50_pm_state *state;
-	int dummy, ret;
+	struct pms_ucode *pms;
+	u32 reg0_old, reg0_new;
+	u32 crtc_mask;
+	u32 reg_c040;
+	int ret, dummy, i;
 
 	state = kzalloc(sizeof(*state), GFP_KERNEL);
 	if (!state)
 		return ERR_PTR(-ENOMEM);
 	state->type = id;
 	state->perflvl = perflvl;
+	pms = &state->ucode;
 
 	ret = get_pll_limits(dev, id, &state->pll);
 	if (ret < 0) {
@@ -95,20 +103,88 @@ nv50_pm_clock_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl,
 		return ERR_PTR(ret);
 	}
 
+	reg0_old = nv_rd32(dev, state->pll.reg + 0);
+	reg0_new = 0x80000000 | (state->P << 16) | (reg0_old & 0xfff8ffff);
+
+	reg_c040 = nv_rd32(dev, 0xc040);
+
+	crtc_mask = 0;
+	for (i = 0; i < 2; i++) {
+		if (nv_rd32(dev, NV50_PDISPLAY_CRTC_C(i, CLOCK)))
+			crtc_mask |= (1 << i);
+	}
+
+	pms_init(pms);
+
+	switch (state->type) {
+	case PLL_MEMORY:
+		/* Wait for vblank on all the CRTCs */
+		if (crtc_mask) {
+			pms_op5f(pms, crtc_mask, 0x00);
+			pms_op5f(pms, crtc_mask, 0x01);
+		}
+
+		pms_wr32(pms, 0x002504, 0x00000001);
+		pms_unkn(pms, 0x06); /* unknown */
+		pms_unkn(pms, 0xb0); /* Disable bus access */
+		pms_op5f(pms, 0x00, 0x01);
+
+		pms_wr32(pms, 0x1002d4, 0x00000001);
+		pms_wr32(pms, 0x1002d0, 0x00000001);
+
+		pms_wr32(pms, 0x100210, 0x00000000);
+		pms_wr32(pms, 0x1002dc, 0x00000001);
+		pms_wr32(pms, state->pll.reg + 0, reg0_old);
+		pms_wr32(pms, state->pll.reg + 4, (state->N << 8) | state->M);
+
+		pms_wr32(pms, state->pll.reg + 0, reg0_new);
+		pms_wr32(pms, 0x1002dc, 0x00000000);
+		pms_wr32(pms, 0x100210, 0x80000000);
+		pms_unkn(pms, 0x07); /* unknown */
+
+		pms_unkn(pms, 0x0b);
+		pms_unkn(pms, 0xd0); /* Enable bus access again */
+		pms_op5f(pms, 0x00, 0x01);
+		pms_wr32(pms, 0x002504, 0x00000000);
+		break;
+	default:
+		pms_unkn(pms, 0xb0); /* Disable bus access */
+
+		pms_wr32(pms, 0xc040,
+			(reg_c040 & ~(1 << 5 | 1 << 4)) | (1 << 20));
+		pms_wr32(pms, state->pll.reg + 0, reg0_new);
+		pms_wr32(pms, state->pll.reg + 4, (state->N << 8) | state->M);
+		pms_unkn(pms, 0x0e);
+
+		pms_wr32(pms, 0xc040, reg_c040);
+		pms_wr32(pms, 0xc040, 0x10);
+
+		pms_wr32(pms, 0xc040, reg_c040);
+
+		pms_unkn(pms, 0xd0); /* Enable bus access again */
+		break;
+	}
+	pms_fini(pms);
+
 	return state;
 }
 
 void
 nv50_pm_clock_set(struct drm_device *dev, void *pre_state)
 {
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
 	struct nv50_pm_state *state = pre_state;
 	struct nouveau_pm_level *perflvl = state->perflvl;
-	u32 reg = state->pll.reg, tmp;
+	struct pms_ucode *pms = &state->ucode;
 	struct bit_entry BIT_M;
+	u32 pbus1098, r100b0c, r619f00;
+	u32 pms_data, pms_kick;
 	u16 script;
+	u32 reg = state->pll.reg, tmp;
 	int N = state->N;
 	int M = state->M;
 	int P = state->P;
+	int i;
 
 	if (state->type == PLL_MEMORY && perflvl->memscript &&
 	    bit_table(dev, 'M', &BIT_M) == 0 &&
@@ -126,20 +202,71 @@ nv50_pm_clock_set(struct drm_device *dev, void *pre_state)
 		nouveau_bios_run_init_table(dev, perflvl->memscript, NULL);
 	}
 
+	/* only use PMS for changing the memory clocks */
 	if (state->type == PLL_MEMORY) {
-		nv_wr32(dev, 0x100210, 0);
-		nv_wr32(dev, 0x1002dc, 1);
-	}
-	/* TODO: Tweek 0x4700 before reclocking UNK05 */
-
-	tmp  = nv_rd32(dev, reg + 0) & 0xfff8ffff;
-	tmp |= 0x80000000 | (P << 16);
-	nv_wr32(dev, reg + 0, tmp);
-	nv_wr32(dev, reg + 4, (N << 8) | M);
+		if (dev_priv->chipset < 0x90) {
+			pms_data = 0x001400;
+			pms_kick = 0x00000003;
+		} else {
+			pms_data = 0x080000;
+			pms_kick = 0x00000001;
+		}
 
-	if (state->type == PLL_MEMORY) {
-		nv_wr32(dev, 0x1002dc, 0);
-		nv_wr32(dev, 0x100210, 0x80000000);
+		/* upload ucode */
+		pbus1098 = nv_mask(dev, 0x001098, 0x00000008, 0x00000000);
+		nv_wr32(dev, 0x001304, 0x00000000);
+		for (i = 0; i < pms->len / 4; i++)
+			nv_wr32(dev, pms_data + (i * 4), pms->ptr.u32[i]);
+		nv_wr32(dev, 0x001098, pbus1098 | 0x18);
+
+		nv_mask(dev, 0x616308, 0x00000000, 0x00000010);
+		nv_mask(dev, 0x616b08, 0x00000000, 0x00000010);
+
+		/* and run it! there's some pre and post script operations that
+		* nvidia do too, need to figure those out
+		*/
+		nv_mask(dev, 0x100200, 0x00000800, 0x00000000);
+		r100b0c = nv_mask(dev, 0x100b0c, 0x000000ff, 0x00000012);
+		r619f00 = nv_mask(dev, 0x619f00, 0x00000008, 0x00000000);
+		nv_wr32(dev, 0x00130c, pms_kick);
+		if (!nv_wait(dev, 0x001308, 0x00000100, 0x00000000)) {
+			NV_ERROR(dev, "pms ucode exec timed out\n");
+			NV_ERROR(dev, "0x001308: 0x%08x\n",
+				nv_rd32(dev, 0x001308));
+			for (i = 0; i < pms->len / 4; i++) {
+				NV_ERROR(dev, "0x%06x: 0x%08x\n",
+					0x1400 + (i * 4),
+					nv_rd32(dev, 0x001400 + (i * 4)));
+			}
+		}
+		nv_wr32(dev, 0x619f00, r619f00);
+		nv_wr32(dev, 0x100b0c, r100b0c);
+		nv_mask(dev, 0x616308, 0x00000000, 0x00000010);
+		nv_mask(dev, 0x616b08, 0x00000000, 0x00000010);
+
+		/*if (perflvl->id == 0) {
+			nv_wr32(dev, 0x100228, 0x00020102);
+			nv_wr32(dev, 0x100230, 0x28000808);
+			nv_wr32(dev, 0x100234, 0x06020702);
+		} else if (perflvl->id == 1) {
+			nv_wr32(dev, 0x100228, 0x00040305);
+			nv_wr32(dev, 0x100230, 0x28000808);
+			nv_wr32(dev, 0x100234, 0x11050905);
+		}else if (perflvl->id == 2) {
+			nv_wr32(dev, 0x100228, 0x0008080c);
+			nv_wr32(dev, 0x100230, 0x28000808);
+			nv_wr32(dev, 0x100234, 0x270c0c09);
+		}*/
+
+		nv_mask(dev, 0x100200, 0x00000000, 0x00000800);
+
+	} else {
+		/* TODO: Tweek 0x4700 before reclocking UNK05 */
+
+		tmp  = nv_rd32(dev, reg + 0) & 0xfff8ffff;
+		tmp |= 0x80000000 | (P << 16);
+		nv_wr32(dev, reg + 0, tmp);
+		nv_wr32(dev, reg + 4, (N << 8) | M);
 	}
 
 	kfree(state);
-- 
1.7.4.4

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* (no subject)
       [not found] ` <1304015622-5910-1-git-send-email-martin.peres-GANU6spQydw@public.gmane.org>
  2011-04-28 18:33   ` [PATCH 1/2] drm/nouveau/pm: Add pm.(un)pause functions Martin Peres
  2011-04-28 18:33   ` [PATCH 2/2] drm/nouveau/nv50: reclock memory using PMS on nv50 Martin Peres
@ 2011-04-30  0:17   ` Martin Peres
       [not found]     ` <1304122633-21621-1-git-send-email-martin.peres-GANU6spQydw@public.gmane.org>
  2 siblings, 1 reply; 10+ messages in thread
From: Martin Peres @ 2011-04-30  0:17 UTC (permalink / raw)
  To: nouveau-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW

Sorry, forgot to add nouveau_pms.h

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH 1/2] drm/nouveau/pm: Add pm.(un)pause functions
       [not found]     ` <1304122633-21621-1-git-send-email-martin.peres-GANU6spQydw@public.gmane.org>
@ 2011-04-30  0:17       ` Martin Peres
  2011-04-30  0:17       ` [PATCH 2/2] drm/nouveau/nv50: reclock memory using PMS on nv50 Martin Peres
  1 sibling, 0 replies; 10+ messages in thread
From: Martin Peres @ 2011-04-30  0:17 UTC (permalink / raw)
  To: nouveau-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW; +Cc: Martin Peres

From: Martin Peres <martin.peres-Iz16wY1oaNPLSKGbIzaifA@public.gmane.org>

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.peres-Iz16wY1oaNPLSKGbIzaifA@public.gmane.org>
---
 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, "setting performance level: %s\n", profile);
 	return nouveau_pm_perflvl_set(dev, perflvl);
 }
 
diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.h b/drivers/gpu/drm/nouveau/nouveau_pm.h
index 4a9838dd..566f72d 100644
--- a/drivers/gpu/drm/nouveau/nouveau_pm.h
+++ b/drivers/gpu/drm/nouveau/nouveau_pm.h
@@ -51,12 +51,16 @@ int nv04_pm_clock_get(struct drm_device *, u32 id);
 void *nv04_pm_clock_pre(struct drm_device *, struct nouveau_pm_level *,
 			u32 id, int khz);
 void nv04_pm_clock_set(struct drm_device *, void *);
+int nv04_pm_pause(struct drm_device *dev);
+void nv04_pm_unpause(struct drm_device *dev);
 
 /* nv50_pm.c */
 int nv50_pm_clock_get(struct drm_device *, u32 id);
 void *nv50_pm_clock_pre(struct drm_device *, struct nouveau_pm_level *,
 			u32 id, int khz);
 void nv50_pm_clock_set(struct drm_device *, void *);
+int nv50_pm_pause(struct drm_device *dev);
+void nv50_pm_unpause(struct drm_device *dev);
 
 /* nva3_pm.c */
 int nva3_pm_clock_get(struct drm_device *, u32 id);
diff --git a/drivers/gpu/drm/nouveau/nouveau_reg.h b/drivers/gpu/drm/nouveau/nouveau_reg.h
index f18cdfc..485d7d0 100644
--- a/drivers/gpu/drm/nouveau/nouveau_reg.h
+++ b/drivers/gpu/drm/nouveau/nouveau_reg.h
@@ -695,8 +695,11 @@
 #define NV50_PROM__ESIZE                                       0x10000
 
 #define NV50_PGRAPH                                         0x00400000
+#define NV50_PGRAPH_CONTROL                                 0x00400500
+#define NV50_PGRAPH_FIFO_STATUS                             0x00400504
 #define NV50_PGRAPH__LEN                                           0x1
 #define NV50_PGRAPH__ESIZE                                     0x10000
+#define NV50_PFIFO_FREEZE                                       0x2504
 
 #define NV50_PDISPLAY                                                0x00610000
 #define NV50_PDISPLAY_OBJECTS                                        0x00610010
diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c
index 38ea662..3fc8455 100644
--- a/drivers/gpu/drm/nouveau/nouveau_state.c
+++ b/drivers/gpu/drm/nouveau/nouveau_state.c
@@ -90,6 +90,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
 		engine->pm.clock_get		= nv04_pm_clock_get;
 		engine->pm.clock_pre		= nv04_pm_clock_pre;
 		engine->pm.clock_set		= nv04_pm_clock_set;
+		engine->pm.pause		= nv04_pm_pause;
+		engine->pm.unpause		= nv04_pm_unpause;
 		engine->vram.init		= nouveau_mem_detect;
 		engine->vram.flags_valid	= nouveau_mem_flags_valid;
 		break;
@@ -138,6 +140,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
 		engine->pm.clock_get		= nv04_pm_clock_get;
 		engine->pm.clock_pre		= nv04_pm_clock_pre;
 		engine->pm.clock_set		= nv04_pm_clock_set;
+		engine->pm.pause		= nv04_pm_pause;
+		engine->pm.unpause		= nv04_pm_unpause;
 		engine->vram.init		= nouveau_mem_detect;
 		engine->vram.flags_valid	= nouveau_mem_flags_valid;
 		break;
@@ -186,6 +190,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
 		engine->pm.clock_get		= nv04_pm_clock_get;
 		engine->pm.clock_pre		= nv04_pm_clock_pre;
 		engine->pm.clock_set		= nv04_pm_clock_set;
+		engine->pm.pause		= nv04_pm_pause;
+		engine->pm.unpause		= nv04_pm_unpause;
 		engine->vram.init		= nouveau_mem_detect;
 		engine->vram.flags_valid	= nouveau_mem_flags_valid;
 		break;
@@ -236,6 +242,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
 		engine->pm.clock_set		= nv04_pm_clock_set;
 		engine->pm.voltage_get		= nouveau_voltage_gpio_get;
 		engine->pm.voltage_set		= nouveau_voltage_gpio_set;
+		engine->pm.pause		= nv04_pm_pause;
+		engine->pm.unpause		= nv04_pm_unpause;
 		engine->vram.init		= nouveau_mem_detect;
 		engine->vram.flags_valid	= nouveau_mem_flags_valid;
 		break;
@@ -288,6 +296,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
 		engine->pm.voltage_get		= nouveau_voltage_gpio_get;
 		engine->pm.voltage_set		= nouveau_voltage_gpio_set;
 		engine->pm.temp_get		= nv40_temp_get;
+		engine->pm.pause		= nv04_pm_pause;
+		engine->pm.unpause		= nv04_pm_unpause;
 		engine->vram.init		= nouveau_mem_detect;
 		engine->vram.flags_valid	= nouveau_mem_flags_valid;
 		break;
@@ -361,6 +371,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
 		}
 		engine->pm.voltage_get		= nouveau_voltage_gpio_get;
 		engine->pm.voltage_set		= nouveau_voltage_gpio_set;
+		engine->pm.pause		= nv50_pm_pause;
+		engine->pm.unpause		= nv50_pm_unpause;
 		if (dev_priv->chipset >= 0x84)
 			engine->pm.temp_get	= nv84_temp_get;
 		else
@@ -1137,4 +1149,3 @@ bool nouveau_wait_for_idle(struct drm_device *dev)
 
 	return true;
 }
-
diff --git a/drivers/gpu/drm/nouveau/nv04_pm.c b/drivers/gpu/drm/nouveau/nv04_pm.c
index eb1c70d..6f5ad051 100644
--- a/drivers/gpu/drm/nouveau/nv04_pm.c
+++ b/drivers/gpu/drm/nouveau/nv04_pm.c
@@ -88,3 +88,129 @@ nv04_pm_clock_set(struct drm_device *dev, void *pre_state)
 	kfree(state);
 }
 
+int
+nv04_pm_pause(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
+	unsigned long irq_flags, hold_spin = 0;
+	/* initial guess... */
+	uint32_t mask300 = 0xffffffff;
+	uint32_t mask700 = 0xffffbfff;
+	uint64_t start = nv04_timer_read(dev);
+
+	/* Do not allow the card to allocate/destroy a
+	 * new channel while reclocking.
+	 *
+	 * We try to hold it for the shortest period of time possible
+	 */
+	spin_lock_irqsave(&dev_priv->context_switch_lock, irq_flags);
+	hold_spin = 1;
+
+	/* Don't context switch */
+	nv04_fifo_reassign(dev, false);
+
+	/* PDISPLAY magic */
+	nv_mask(dev, NV50_PDISPLAY_PIO_CTRL, 0x0, 0x1);
+
+	/* Pause PFIFO's puller */
+	nv04_fifo_cache_pull(dev, false);
+
+	/* Wait for PFIFO's DMA_PUSH to deplete (Not busy) */
+	if (!nouveau_wait_eq(dev, 100000, NV04_PFIFO_CACHE1_DMA_PUSH,
+				0x100, 0x100)) {
+		NV_ERROR(dev, "PFIFO DMA_PUSH never depletes (0x%x)\n",
+			nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_PUSH));
+		goto err_pfifo_freeze;
+	}
+	nv_mask(dev, NV04_PFIFO_CACHE1_DMA_PUSH, 0x1, 0);
+
+	/* Pause PGRAPH's FIFO */
+	nv_wr32(dev, NV04_PGRAPH_FIFO, 0);
+
+	/* Now that the card is paused,
+	 * there is no problem with channel creation
+	 */
+	spin_unlock_irqrestore(&dev_priv->context_switch_lock, irq_flags);
+	hold_spin = 0;
+
+	/* Wait for PGRAPH to be really stopped */
+	if (!nouveau_wait_eq(dev, 1000000, 0x400300, mask300, 0x4) ||
+		!nouveau_wait_eq(dev, 8000000, NV04_PGRAPH_STATUS,
+				mask700, 0x0)) {
+		/* if you see this message,
+		* mask* above probably need to be adjusted
+		* to not contain the bits you see failing */
+		NV_ERROR(dev,
+		    "PGRAPH: wait for idle fail: %08x %08x!\n",
+		    nv_rd32(dev, NV04_PGRAPH_STATUS),
+		    nv_rd32(dev, 0x400300));
+
+		goto err_pgraph;
+	}
+
+	if (dev_priv->card_type == NV_40)
+		pm->pause_state.reg_c040 = nv_mask(dev, 0xc040, 0x333, 0);
+
+	NV_DEBUG(dev, "PM.pause took %lluns\n",
+		(nv04_timer_read(dev) - start));
+
+	return 0;
+
+err_pgraph:
+	nv_wr32(dev, NV04_PGRAPH_FIFO, 1);
+
+	nv_mask(dev, NV04_PFIFO_CACHE1_DMA_PUSH, 0, 0x1);
+
+err_pfifo_freeze:
+	nv04_fifo_cache_pull(dev, true);
+	nv04_fifo_reassign(dev, true);
+
+	/* PDISPLAY magic */
+	nv_mask(dev, NV50_PDISPLAY_PIO_CTRL, 0x1, 0x0);
+
+	if (hold_spin)
+		spin_unlock_irqrestore(&dev_priv->context_switch_lock,
+					irq_flags);
+
+	return -EAGAIN;
+}
+
+void
+nv04_pm_unpause(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
+	unsigned long irq_flags;
+
+	/* Do not allow the card to allocate/destroy a
+	 * new channel while unpausing.
+	 */
+	spin_lock_irqsave(&dev_priv->context_switch_lock, irq_flags);
+
+	if (dev_priv->card_type == NV_40) {
+		nv_wr32(dev, 0xc040, pm->pause_state.reg_c040);
+		nv_wr32(dev, 0xc04c, nv_rd32(dev, 0xc04c));
+	}
+
+	/* Unpause PGRAPH */
+	nv_wr32(dev, NV04_PGRAPH_FIFO, 1);
+
+	/* Unpause pfifo caches */
+	nv_mask(dev, NV04_PFIFO_CACHE1_DMA_PUSH, 0, 0x1);
+	nv04_fifo_cache_pull(dev, true);
+	nv04_fifo_reassign(dev, true);
+
+	/* PDISPLAY magic */
+	nv_mask(dev, NV50_PDISPLAY_PIO_CTRL, 0x1, 0x0);
+
+	/* TODO: De-activated for the moment, it makes things unstable */
+#if 0
+	if (dev_priv->card_type == NV_40) {
+		nv_wr32(dev, 0x1580, nv_rd32(dev, 0x1580));
+		nv_wr32(dev, 0xc044, nv_rd32(dev, 0xc44));
+	}
+#endif
+
+	spin_unlock_irqrestore(&dev_priv->context_switch_lock, irq_flags);
+}
diff --git a/drivers/gpu/drm/nouveau/nv50_pm.c b/drivers/gpu/drm/nouveau/nv50_pm.c
index 8a28100..4dd2d76 100644
--- a/drivers/gpu/drm/nouveau/nv50_pm.c
+++ b/drivers/gpu/drm/nouveau/nv50_pm.c
@@ -130,6 +130,7 @@ nv50_pm_clock_set(struct drm_device *dev, void *pre_state)
 		nv_wr32(dev, 0x100210, 0);
 		nv_wr32(dev, 0x1002dc, 1);
 	}
+	/* TODO: Tweek 0x4700 before reclocking UNK05 */
 
 	tmp  = nv_rd32(dev, reg + 0) & 0xfff8ffff;
 	tmp |= 0x80000000 | (P << 16);
@@ -144,3 +145,174 @@ nv50_pm_clock_set(struct drm_device *dev, void *pre_state)
 	kfree(state);
 }
 
+int
+nv50_pm_pause(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
+	unsigned long irq_flags, hold_spin = 0;
+	/* initial guess... */
+	uint32_t mask380 = 0xffffffff;
+	uint32_t mask384 = 0xffffffff;
+	uint32_t mask388 = 0xffffffff;
+	uint32_t mask504 = 0x00000001;
+	uint32_t mask700 = 0x00000001;
+	int i = 0;
+	uint64_t start = nv04_timer_read(dev);
+
+	/* Do not allow the card to allocate/destroy a
+	 * new channel while reclocking.
+	 *
+	 * We try to hold it for the shortest period of time possible
+	 */
+	spin_lock_irqsave(&dev_priv->context_switch_lock, irq_flags);
+	hold_spin = 1;
+
+	/* Don't context switch */
+	nv04_fifo_reassign(dev, false);
+
+	/* PDISPLAY magic */
+	nv_mask(dev, NV50_PDISPLAY_PIO_CTRL, 0x0, 0x1);
+
+	nv_wr32(dev, NV50_PFIFO_FREEZE, 1);
+	if (!nouveau_wait_eq(dev, 100000, NV50_PFIFO_FREEZE, 0x10, 0x10)) {
+		NV_ERROR(dev, "PFIFO freeze failed\n");
+		goto err_pfifo_freeze;
+	}
+
+	/* Wait for PFIFO's DMA_PUSH to deplete */
+	if (!nouveau_wait_eq(dev, 100000, NV04_PFIFO_CACHE1_DMA_PUSH,
+				0x100, 0x100)) {
+		NV_ERROR(dev, "PFIFO DMA_PUSH never depleted (0x%x)\n",
+			nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_PUSH));
+		goto err_pfifo_freeze;
+	}
+
+	/* Pause PFIFO's caches */
+	nv04_fifo_cache_pull(dev, false);
+	nv_mask(dev, NV04_PFIFO_CACHE1_DMA_PUSH, 0x1, 0);
+
+	/* Empty PGRAPH's FIFO */
+	do {
+		/* Un-pause PGRAPH's FIFO (in case it was) */
+		nv_mask(dev, NV50_PGRAPH_CONTROL, 0, 0x1);
+
+		/* Wait for PGRAPH's FIFO to deplete */
+		if (!nouveau_wait_eq(dev, 100000, NV50_PGRAPH_FIFO_STATUS,
+					mask504, 0x1)) {
+			if (nv_rd32(dev, NV04_PGRAPH_STATUS) & 0x100) {
+				NV_ERROR(dev,
+				"PGRAPH: PGRAPH paused while running a ctxprog,"
+				" NV40_PGRAPH_CTXCTL_0310 = 0x%x\n",
+				nv_rd32(dev, NV40_PGRAPH_CTXCTL_0310));
+			}
+
+			goto err_ctx_prog_playing;
+		}
+
+		/* Pause PGRAPH's FIFO */
+		nv_mask(dev, NV50_PGRAPH_CONTROL, 0x1, 0);
+
+		/* Limit the number of loops to 2 */
+		i++;
+		if (i > 1)
+			goto err_pgraph_stop;
+	} while ((nv_rd32(dev, NV50_PGRAPH_FIFO_STATUS) & mask504) == 0);
+
+	/* Now that the PGRAPH's FIFO is paused,
+	 * there is no problem with channel creation.
+	 */
+	spin_unlock_irqrestore(&dev_priv->context_switch_lock, irq_flags);
+	hold_spin = 0;
+
+	/* Wait for PGRAPH engines to stop */
+	if (!nouveau_wait_eq(dev, 100000, 0x400380, mask380, 0x0) ||
+	    !nouveau_wait_eq(dev, 100000, 0x400384, mask384, 0x0) ||
+	    !nouveau_wait_eq(dev, 100000, 0x400388, mask388, 0x0) ||
+	    !nouveau_wait_eq(dev, 500000, NV04_PGRAPH_STATUS, mask700, 0x0)) {
+		/* if you see this message,
+		* mask* above probably need to be adjusted
+		* to not contain the bits you see failing */
+		NV_ERROR(dev,
+		    "PGRAPH: wait for idle fail: %08x %08x %08x %08x %08x!\n",
+		    nv_rd32(dev, 0x400380),
+		    nv_rd32(dev, 0x400384),
+		    nv_rd32(dev, 0x400388),
+		    nv_rd32(dev, NV50_PGRAPH_FIFO_STATUS),
+		    nv_rd32(dev, NV04_PGRAPH_STATUS));
+
+		goto err_pgraph_stop;
+	}
+
+	/* De-activate the PLLs */
+	pm->pause_state.reg_c040 = nv_mask(dev, 0xc040, 0x30, 0x100000);
+
+	NV_DEBUG(dev, "PM.pause took %lluns\n",
+		   (nv04_timer_read(dev) - start));
+
+	return 0;
+
+err_pgraph_stop:
+	nv_mask(dev, NV50_PGRAPH_CONTROL, 0, 0x1);
+
+err_ctx_prog_playing:
+	nv_mask(dev, NV04_PFIFO_CACHE1_DMA_PUSH, 0, 0x1);
+	nv04_fifo_cache_pull(dev, true);
+
+err_pfifo_freeze:
+	nv_wr32(dev, NV50_PFIFO_FREEZE, 0);
+
+	nv_mask(dev, NV50_PDISPLAY_PIO_CTRL, 0x1, 0x0);
+
+	nv04_fifo_reassign(dev, true);
+
+	if (hold_spin)
+		spin_unlock_irqrestore(&dev_priv->context_switch_lock,
+					irq_flags);
+	return -EAGAIN;
+}
+
+void
+nv50_pm_unpause(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
+	unsigned long irq_flags;
+
+	/* Do not allow the card to allocate/destroy a
+	 * new channel while unpausing.
+	 */
+	spin_lock_irqsave(&dev_priv->context_switch_lock, irq_flags);
+
+	/* Restore the PLL supervisor state */
+	nv_wr32(dev, 0xc040, pm->pause_state.reg_c040);
+	nv_wr32(dev, 0xc04c, 0x10);
+	nv_wr32(dev, 0xc040, pm->pause_state.reg_c040);
+
+	/* Unpause pfifo caches */
+	nv_mask(dev, NV04_PFIFO_CACHE1_DMA_PUSH, 0, 0x1);
+	nv04_fifo_cache_pull(dev, true);
+
+	/* Unpause PGRAPH */
+	nv_mask(dev, NV50_PGRAPH_CONTROL, 0, 0x1);
+
+	/* Un-pause PFIFO */
+	nv_wr32(dev, NV50_PFIFO_FREEZE, 0);
+
+	/* PDISPLAY magic */
+	nv_wr32(dev, 0x616308, 0x10);
+	nv_wr32(dev, 0x616b08, 0x10);
+	nv_mask(dev, NV50_PDISPLAY_PIO_CTRL, 0x1, 0x0);
+
+	/* Re-allow context switch */
+	nv04_fifo_reassign(dev, true);
+
+	/* the blob also clear c040's bit 26 using PMS when the
+	 * performance level is set to 0.
+	 * I haven't seen difference in power consumption, so,
+	 * I leave it for later.
+	 */
+	nv_wr32(dev, 0xc040, pm->pause_state.reg_c040);
+
+	spin_unlock_irqrestore(&dev_priv->context_switch_lock, irq_flags);
+}
-- 
1.7.5

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 2/2] drm/nouveau/nv50: reclock memory using PMS on nv50
       [not found]     ` <1304122633-21621-1-git-send-email-martin.peres-GANU6spQydw@public.gmane.org>
  2011-04-30  0:17       ` [PATCH 1/2] drm/nouveau/pm: Add pm.(un)pause functions Martin Peres
@ 2011-04-30  0:17       ` Martin Peres
       [not found]         ` <1304122633-21621-3-git-send-email-martin.peres-GANU6spQydw@public.gmane.org>
  1 sibling, 1 reply; 10+ messages in thread
From: Martin Peres @ 2011-04-30  0:17 UTC (permalink / raw)
  To: nouveau-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW; +Cc: Martin Peres

From: Martin Peres <martin.peres-Iz16wY1oaNPLSKGbIzaifA@public.gmane.org>

v2: Reclock memory after reclocking the other engines

Signed-off-by: Martin Peres <martin.peres-Iz16wY1oaNPLSKGbIzaifA@public.gmane.org>
---
 drivers/gpu/drm/nouveau/nouveau_pm.c  |   11 +--
 drivers/gpu/drm/nouveau/nouveau_pms.h |   98 +++++++++++++++++++++
 drivers/gpu/drm/nouveau/nv50_pm.c     |  153 ++++++++++++++++++++++++++++++---
 3 files changed, 242 insertions(+), 20 deletions(-)
 create mode 100644 drivers/gpu/drm/nouveau/nouveau_pms.h

diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.c b/drivers/gpu/drm/nouveau/nouveau_pm.c
index 88f58b1..44d01bb 100644
--- a/drivers/gpu/drm/nouveau/nouveau_pm.c
+++ b/drivers/gpu/drm/nouveau/nouveau_pm.c
@@ -45,10 +45,6 @@ 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);
@@ -100,7 +96,6 @@ nouveau_pm_perflvl_set(struct drm_device *dev, struct nouveau_pm_level *perflvl)
 
 	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*/
@@ -110,11 +105,13 @@ nouveau_pm_perflvl_set(struct drm_device *dev, struct nouveau_pm_level *perflvl)
 	/* Wait for PLLs to stabilize */
 	udelay(100);
 
+	pm->unpause(dev);
+
+	nouveau_pm_clock_set(dev, perflvl, PLL_MEMORY, perflvl->memory);
+
 	pm->cur = perflvl;
 	ret = 0;
 
-	pm->unpause(dev);
-
 	NV_DEBUG(dev, "Reclocking took %lluns\n",
 		 (nv04_timer_read(dev) - start));
 
diff --git a/drivers/gpu/drm/nouveau/nouveau_pms.h b/drivers/gpu/drm/nouveau/nouveau_pms.h
new file mode 100644
index 0000000..d7a445b
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nouveau_pms.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2010 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#ifndef __NOUVEAU_PMS_H__
+#define __NOUVEAU_PMS_H__
+
+struct pms_ucode {
+	u8 data[256];
+	union {
+		u8  *u08;
+		u16 *u16;
+		u32 *u32;
+	} ptr;
+	u16 len;
+
+	u32 reg;
+	u32 val;
+};
+
+static inline void
+pms_init(struct pms_ucode *pms)
+{
+	pms->ptr.u08 = pms->data;
+	pms->reg = 0xffffffff;
+	pms->val = 0xffffffff;
+}
+
+static inline void
+pms_fini(struct pms_ucode *pms)
+{
+	do {
+		*pms->ptr.u08++ = 0x7f;
+		pms->len = pms->ptr.u08 - pms->data;
+	} while (pms->len & 3);
+	pms->ptr.u08 = pms->data;
+}
+
+static inline void
+pms_unkn(struct pms_ucode *pms, u8 v0)
+{
+	*pms->ptr.u08++ = v0;
+}
+
+static inline void
+pms_op5f(struct pms_ucode *pms, u8 v0, u8 v1)
+{
+	*pms->ptr.u08++ = 0x5f;
+	*pms->ptr.u08++ = v0;
+	*pms->ptr.u08++ = v1;
+}
+
+static inline void
+pms_wr32(struct pms_ucode *pms, u32 reg, u32 val)
+{
+	if (val != pms->val) {
+		if ((val & 0xffff0000) == (pms->val & 0xffff0000)) {
+			*pms->ptr.u08++ = 0x42;
+			*pms->ptr.u16++ = (val & 0x0000ffff);
+		} else {
+			*pms->ptr.u08++ = 0xe2;
+			*pms->ptr.u32++ = val;
+		}
+
+		pms->val = val;
+	}
+
+	if ((reg & 0xffff0000) == (pms->reg & 0xffff0000)) {
+		*pms->ptr.u08++ = 0x40;
+		*pms->ptr.u16++ = (reg & 0x0000ffff);
+	} else {
+		*pms->ptr.u08++ = 0xe0;
+		*pms->ptr.u32++ = reg;
+	}
+	pms->reg = reg;
+}
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/nv50_pm.c b/drivers/gpu/drm/nouveau/nv50_pm.c
index 4dd2d76..9b81f03 100644
--- a/drivers/gpu/drm/nouveau/nv50_pm.c
+++ b/drivers/gpu/drm/nouveau/nv50_pm.c
@@ -26,9 +26,11 @@
 #include "nouveau_drv.h"
 #include "nouveau_bios.h"
 #include "nouveau_pm.h"
+#include "nouveau_pms.h"
 
 struct nv50_pm_state {
 	struct nouveau_pm_level *perflvl;
+	struct pms_ucode ucode;
 	struct pll_lims pll;
 	enum pll_types type;
 	int N, M, P;
@@ -73,14 +75,20 @@ void *
 nv50_pm_clock_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl,
 		  u32 id, int khz)
 {
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
 	struct nv50_pm_state *state;
-	int dummy, ret;
+	struct pms_ucode *pms;
+	u32 reg0_old, reg0_new;
+	u32 crtc_mask;
+	u32 reg_c040;
+	int ret, dummy, i;
 
 	state = kzalloc(sizeof(*state), GFP_KERNEL);
 	if (!state)
 		return ERR_PTR(-ENOMEM);
 	state->type = id;
 	state->perflvl = perflvl;
+	pms = &state->ucode;
 
 	ret = get_pll_limits(dev, id, &state->pll);
 	if (ret < 0) {
@@ -95,20 +103,88 @@ nv50_pm_clock_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl,
 		return ERR_PTR(ret);
 	}
 
+	reg0_old = nv_rd32(dev, state->pll.reg + 0);
+	reg0_new = 0x80000000 | (state->P << 16) | (reg0_old & 0xfff8ffff);
+
+	reg_c040 = nv_rd32(dev, 0xc040);
+
+	crtc_mask = 0;
+	for (i = 0; i < 2; i++) {
+		if (nv_rd32(dev, NV50_PDISPLAY_CRTC_C(i, CLOCK)))
+			crtc_mask |= (1 << i);
+	}
+
+	pms_init(pms);
+
+	switch (state->type) {
+	case PLL_MEMORY:
+		/* Wait for vblank on all the CRTCs */
+		if (crtc_mask) {
+			pms_op5f(pms, crtc_mask, 0x00);
+			pms_op5f(pms, crtc_mask, 0x01);
+		}
+
+		pms_wr32(pms, 0x002504, 0x00000001);
+		pms_unkn(pms, 0x06); /* unknown */
+		pms_unkn(pms, 0xb0); /* Disable bus access */
+		pms_op5f(pms, 0x00, 0x01);
+
+		pms_wr32(pms, 0x1002d4, 0x00000001);
+		pms_wr32(pms, 0x1002d0, 0x00000001);
+
+		pms_wr32(pms, 0x100210, 0x00000000);
+		pms_wr32(pms, 0x1002dc, 0x00000001);
+		pms_wr32(pms, state->pll.reg + 0, reg0_old);
+		pms_wr32(pms, state->pll.reg + 4, (state->N << 8) | state->M);
+
+		pms_wr32(pms, state->pll.reg + 0, reg0_new);
+		pms_wr32(pms, 0x1002dc, 0x00000000);
+		pms_wr32(pms, 0x100210, 0x80000000);
+		pms_unkn(pms, 0x07); /* unknown */
+
+		pms_unkn(pms, 0x0b);
+		pms_unkn(pms, 0xd0); /* Enable bus access again */
+		pms_op5f(pms, 0x00, 0x01);
+		pms_wr32(pms, 0x002504, 0x00000000);
+		break;
+	default:
+		pms_unkn(pms, 0xb0); /* Disable bus access */
+
+		pms_wr32(pms, 0xc040,
+			(reg_c040 & ~(1 << 5 | 1 << 4)) | (1 << 20));
+		pms_wr32(pms, state->pll.reg + 0, reg0_new);
+		pms_wr32(pms, state->pll.reg + 4, (state->N << 8) | state->M);
+		pms_unkn(pms, 0x0e);
+
+		pms_wr32(pms, 0xc040, reg_c040);
+		pms_wr32(pms, 0xc040, 0x10);
+
+		pms_wr32(pms, 0xc040, reg_c040);
+
+		pms_unkn(pms, 0xd0); /* Enable bus access again */
+		break;
+	}
+	pms_fini(pms);
+
 	return state;
 }
 
 void
 nv50_pm_clock_set(struct drm_device *dev, void *pre_state)
 {
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
 	struct nv50_pm_state *state = pre_state;
 	struct nouveau_pm_level *perflvl = state->perflvl;
-	u32 reg = state->pll.reg, tmp;
+	struct pms_ucode *pms = &state->ucode;
 	struct bit_entry BIT_M;
+	u32 pbus1098, r100b0c, r619f00;
+	u32 pms_data, pms_kick;
 	u16 script;
+	u32 reg = state->pll.reg, tmp;
 	int N = state->N;
 	int M = state->M;
 	int P = state->P;
+	int i;
 
 	if (state->type == PLL_MEMORY && perflvl->memscript &&
 	    bit_table(dev, 'M', &BIT_M) == 0 &&
@@ -126,20 +202,71 @@ nv50_pm_clock_set(struct drm_device *dev, void *pre_state)
 		nouveau_bios_run_init_table(dev, perflvl->memscript, NULL);
 	}
 
+	/* only use PMS for changing the memory clocks */
 	if (state->type == PLL_MEMORY) {
-		nv_wr32(dev, 0x100210, 0);
-		nv_wr32(dev, 0x1002dc, 1);
-	}
-	/* TODO: Tweek 0x4700 before reclocking UNK05 */
-
-	tmp  = nv_rd32(dev, reg + 0) & 0xfff8ffff;
-	tmp |= 0x80000000 | (P << 16);
-	nv_wr32(dev, reg + 0, tmp);
-	nv_wr32(dev, reg + 4, (N << 8) | M);
+		if (dev_priv->chipset < 0x90) {
+			pms_data = 0x001400;
+			pms_kick = 0x00000003;
+		} else {
+			pms_data = 0x080000;
+			pms_kick = 0x00000001;
+		}
 
-	if (state->type == PLL_MEMORY) {
-		nv_wr32(dev, 0x1002dc, 0);
-		nv_wr32(dev, 0x100210, 0x80000000);
+		/* upload ucode */
+		pbus1098 = nv_mask(dev, 0x001098, 0x00000008, 0x00000000);
+		nv_wr32(dev, 0x001304, 0x00000000);
+		for (i = 0; i < pms->len / 4; i++)
+			nv_wr32(dev, pms_data + (i * 4), pms->ptr.u32[i]);
+		nv_wr32(dev, 0x001098, pbus1098 | 0x18);
+
+		nv_mask(dev, 0x616308, 0x00000000, 0x00000010);
+		nv_mask(dev, 0x616b08, 0x00000000, 0x00000010);
+
+		/* and run it! there's some pre and post script operations that
+		* nvidia do too, need to figure those out
+		*/
+		nv_mask(dev, 0x100200, 0x00000800, 0x00000000);
+		r100b0c = nv_mask(dev, 0x100b0c, 0x000000ff, 0x00000012);
+		r619f00 = nv_mask(dev, 0x619f00, 0x00000008, 0x00000000);
+		nv_wr32(dev, 0x00130c, pms_kick);
+		if (!nv_wait(dev, 0x001308, 0x00000100, 0x00000000)) {
+			NV_ERROR(dev, "pms ucode exec timed out\n");
+			NV_ERROR(dev, "0x001308: 0x%08x\n",
+				nv_rd32(dev, 0x001308));
+			for (i = 0; i < pms->len / 4; i++) {
+				NV_ERROR(dev, "0x%06x: 0x%08x\n",
+					0x1400 + (i * 4),
+					nv_rd32(dev, 0x001400 + (i * 4)));
+			}
+		}
+		nv_wr32(dev, 0x619f00, r619f00);
+		nv_wr32(dev, 0x100b0c, r100b0c);
+		nv_mask(dev, 0x616308, 0x00000000, 0x00000010);
+		nv_mask(dev, 0x616b08, 0x00000000, 0x00000010);
+
+		/*if (perflvl->id == 0) {
+			nv_wr32(dev, 0x100228, 0x00020102);
+			nv_wr32(dev, 0x100230, 0x28000808);
+			nv_wr32(dev, 0x100234, 0x06020702);
+		} else if (perflvl->id == 1) {
+			nv_wr32(dev, 0x100228, 0x00040305);
+			nv_wr32(dev, 0x100230, 0x28000808);
+			nv_wr32(dev, 0x100234, 0x11050905);
+		}else if (perflvl->id == 2) {
+			nv_wr32(dev, 0x100228, 0x0008080c);
+			nv_wr32(dev, 0x100230, 0x28000808);
+			nv_wr32(dev, 0x100234, 0x270c0c09);
+		}*/
+
+		nv_mask(dev, 0x100200, 0x00000000, 0x00000800);
+
+	} else {
+		/* TODO: Tweek 0x4700 before reclocking UNK05 */
+
+		tmp  = nv_rd32(dev, reg + 0) & 0xfff8ffff;
+		tmp |= 0x80000000 | (P << 16);
+		nv_wr32(dev, reg + 0, tmp);
+		nv_wr32(dev, reg + 4, (N << 8) | M);
 	}
 
 	kfree(state);
-- 
1.7.5

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [PATCH 2/2] drm/nouveau/nv50: reclock memory using PMS on nv50
       [not found]         ` <1304122633-21621-3-git-send-email-martin.peres-GANU6spQydw@public.gmane.org>
@ 2011-05-06 23:42           ` Emil Velikov
  2011-05-07  1:50             ` Martin Peres
  0 siblings, 1 reply; 10+ messages in thread
From: Emil Velikov @ 2011-05-06 23:42 UTC (permalink / raw)
  To: nouveau-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW, Martin Peres; +Cc: Martin Peres

On Sat, 30 Apr 2011 01:17:13 +0100, Martin Peres <martin.peres-GANU6spQydw@public.gmane.org> wrote:

> From: Martin Peres <martin.peres-Iz16wY1oaNPLSKGbIzaifA@public.gmane.org>
>
> v2: Reclock memory after reclocking the other engines
>
> Signed-off-by: Martin Peres <martin.peres-Iz16wY1oaNPLSKGbIzaifA@public.gmane.org>
> ---
>  drivers/gpu/drm/nouveau/nouveau_pm.c  |   11 +--
>  drivers/gpu/drm/nouveau/nouveau_pms.h |   98 +++++++++++++++++++++
>  drivers/gpu/drm/nouveau/nv50_pm.c     |  153 ++++++++++++++++++++++++++++++---
>  3 files changed, 242 insertions(+), 20 deletions(-)
>  create mode 100644 drivers/gpu/drm/nouveau/nouveau_pms.h
>
> diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.c b/drivers/gpu/drm/nouveau/nouveau_pm.c
> index 88f58b1..44d01bb 100644
> --- a/drivers/gpu/drm/nouveau/nouveau_pm.c
> +++ b/drivers/gpu/drm/nouveau/nouveau_pm.c
> @@ -45,10 +45,6 @@ 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);
> @@ -100,7 +96,6 @@ nouveau_pm_perflvl_set(struct drm_device *dev, struct nouveau_pm_level *perflvl)
> 	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*/
> @@ -110,11 +105,13 @@ nouveau_pm_perflvl_set(struct drm_device *dev, struct nouveau_pm_level *perflvl)
>  	/* Wait for PLLs to stabilize */
>  	udelay(100);
>+	pm->unpause(dev);
> +
> +	nouveau_pm_clock_set(dev, perflvl, PLL_MEMORY, perflvl->memory);
> +
>  	pm->cur = perflvl;
>  	ret = 0;
>-	pm->unpause(dev);
> -
>  	NV_DEBUG(dev, "Reclocking took %lluns\n",
>  		 (nv04_timer_read(dev) - start));
>diff --git a/drivers/gpu/drm/nouveau/nouveau_pms.h b/drivers/gpu/drm/nouveau/nouveau_pms.h
> new file mode 100644
> index 0000000..d7a445b
> --- /dev/null
> +++ b/drivers/gpu/drm/nouveau/nouveau_pms.h
> @@ -0,0 +1,98 @@
> +/*
> + * Copyright 2010 Red Hat Inc.
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> + * copy of this software and associated documentation files (the "Software"),
> + * to deal in the Software without restriction, including without limitation
> + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
> + * and/or sell copies of the Software, and to permit persons to whom the
> + * Software is furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
> + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
> + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
> + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
> + * OTHER DEALINGS IN THE SOFTWARE.
> + *
> + * Authors: Ben Skeggs
> + */
> +
> +#ifndef __NOUVEAU_PMS_H__
> +#define __NOUVEAU_PMS_H__
> +
> +struct pms_ucode {
> +	u8 data[256];
> +	union {
> +		u8  *u08;
> +		u16 *u16;
> +		u32 *u32;
> +	} ptr;
> +	u16 len;
> +
> +	u32 reg;
> +	u32 val;
> +};
> +
> +static inline void
> +pms_init(struct pms_ucode *pms)
> +{
> +	pms->ptr.u08 = pms->data;
> +	pms->reg = 0xffffffff;
> +	pms->val = 0xffffffff;
> +}
> +
> +static inline void
> +pms_fini(struct pms_ucode *pms)
> +{
> +	do {
> +		*pms->ptr.u08++ = 0x7f;
> +		pms->len = pms->ptr.u08 - pms->data;
> +	} while (pms->len & 3);
> +	pms->ptr.u08 = pms->data;
> +}
> +
> +static inline void
> +pms_unkn(struct pms_ucode *pms, u8 v0)
> +{
> +	*pms->ptr.u08++ = v0;
> +}
> +
> +static inline void
> +pms_op5f(struct pms_ucode *pms, u8 v0, u8 v1)
> +{
> +	*pms->ptr.u08++ = 0x5f;
> +	*pms->ptr.u08++ = v0;
> +	*pms->ptr.u08++ = v1;
> +}
> +
> +static inline void
> +pms_wr32(struct pms_ucode *pms, u32 reg, u32 val)
> +{
> +	if (val != pms->val) {
> +		if ((val & 0xffff0000) == (pms->val & 0xffff0000)) {
> +			*pms->ptr.u08++ = 0x42;
> +			*pms->ptr.u16++ = (val & 0x0000ffff);
> +		} else {
> +			*pms->ptr.u08++ = 0xe2;
> +			*pms->ptr.u32++ = val;
> +		}
> +
> +		pms->val = val;
> +	}
> +
> +	if ((reg & 0xffff0000) == (pms->reg & 0xffff0000)) {
> +		*pms->ptr.u08++ = 0x40;
> +		*pms->ptr.u16++ = (reg & 0x0000ffff);
> +	} else {
> +		*pms->ptr.u08++ = 0xe0;
> +		*pms->ptr.u32++ = reg;
> +	}
> +	pms->reg = reg;
> +}
> +
> +#endif
> diff --git a/drivers/gpu/drm/nouveau/nv50_pm.c b/drivers/gpu/drm/nouveau/nv50_pm.c
> index 4dd2d76..9b81f03 100644
> --- a/drivers/gpu/drm/nouveau/nv50_pm.c
> +++ b/drivers/gpu/drm/nouveau/nv50_pm.c
> @@ -26,9 +26,11 @@
>  #include "nouveau_drv.h"
>  #include "nouveau_bios.h"
>  #include "nouveau_pm.h"
> +#include "nouveau_pms.h"
> struct nv50_pm_state {
>  	struct nouveau_pm_level *perflvl;
> +	struct pms_ucode ucode;
>  	struct pll_lims pll;
>  	enum pll_types type;
>  	int N, M, P;
> @@ -73,14 +75,20 @@ void *
>  nv50_pm_clock_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl,
>  		  u32 id, int khz)
>  {
> +	struct drm_nouveau_private *dev_priv = dev->dev_private;
>  	struct nv50_pm_state *state;
> -	int dummy, ret;
> +	struct pms_ucode *pms;
> +	u32 reg0_old, reg0_new;
> +	u32 crtc_mask;
> +	u32 reg_c040;
> +	int ret, dummy, i;
> 	state = kzalloc(sizeof(*state), GFP_KERNEL);
>  	if (!state)
>  		return ERR_PTR(-ENOMEM);
>  	state->type = id;
>  	state->perflvl = perflvl;
> +	pms = &state->ucode;
> 	ret = get_pll_limits(dev, id, &state->pll);
>  	if (ret < 0) {
> @@ -95,20 +103,88 @@ nv50_pm_clock_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl,
>  		return ERR_PTR(ret);
>  	}
>+	reg0_old = nv_rd32(dev, state->pll.reg + 0);
> +	reg0_new = 0x80000000 | (state->P << 16) | (reg0_old & 0xfff8ffff);
> +
> +	reg_c040 = nv_rd32(dev, 0xc040);
> +
> +	crtc_mask = 0;
> +	for (i = 0; i < 2; i++) {
> +		if (nv_rd32(dev, NV50_PDISPLAY_CRTC_C(i, CLOCK)))
> +			crtc_mask |= (1 << i);
> +	}
> +
> +	pms_init(pms);
> +
> +	switch (state->type) {
> +	case PLL_MEMORY:
> +		/* Wait for vblank on all the CRTCs */
> +		if (crtc_mask) {
> +			pms_op5f(pms, crtc_mask, 0x00);
> +			pms_op5f(pms, crtc_mask, 0x01);
> +		}
> +
> +		pms_wr32(pms, 0x002504, 0x00000001);
> +		pms_unkn(pms, 0x06); /* unknown */
> +		pms_unkn(pms, 0xb0); /* Disable bus access */
> +		pms_op5f(pms, 0x00, 0x01);
> +
> +		pms_wr32(pms, 0x1002d4, 0x00000001);
> +		pms_wr32(pms, 0x1002d0, 0x00000001);
> +
> +		pms_wr32(pms, 0x100210, 0x00000000);
> +		pms_wr32(pms, 0x1002dc, 0x00000001);
> +		pms_wr32(pms, state->pll.reg + 0, reg0_old);
> +		pms_wr32(pms, state->pll.reg + 4, (state->N << 8) | state->M);
> +
> +		pms_wr32(pms, state->pll.reg + 0, reg0_new);
> +		pms_wr32(pms, 0x1002dc, 0x00000000);
> +		pms_wr32(pms, 0x100210, 0x80000000);
> +		pms_unkn(pms, 0x07); /* unknown */
> +
> +		pms_unkn(pms, 0x0b);
> +		pms_unkn(pms, 0xd0); /* Enable bus access again */
> +		pms_op5f(pms, 0x00, 0x01);
This one should be "pms_op5f(pms, 0x00, 0x00);"

Confirmed with a few nv84/86/96 traces
Still not sure how much difference it is going to make (stability wise)

> +		pms_wr32(pms, 0x002504, 0x00000000);
> +		break;
> +	default:
> +		pms_unkn(pms, 0xb0); /* Disable bus access */
> +
> +		pms_wr32(pms, 0xc040,
> +			(reg_c040 & ~(1 << 5 | 1 << 4)) | (1 << 20));
> +		pms_wr32(pms, state->pll.reg + 0, reg0_new);
> +		pms_wr32(pms, state->pll.reg + 4, (state->N << 8) | state->M);
> +		pms_unkn(pms, 0x0e);
> +
> +		pms_wr32(pms, 0xc040, reg_c040);
> +		pms_wr32(pms, 0xc040, 0x10);
> +
> +		pms_wr32(pms, 0xc040, reg_c040);
> +
> +		pms_unkn(pms, 0xd0); /* Enable bus access again */
> +		break;
> +	}
> +	pms_fini(pms);
> +
>  	return state;
>  }
> void
>  nv50_pm_clock_set(struct drm_device *dev, void *pre_state)
>  {
> +	struct drm_nouveau_private *dev_priv = dev->dev_private;
>  	struct nv50_pm_state *state = pre_state;
>  	struct nouveau_pm_level *perflvl = state->perflvl;
> -	u32 reg = state->pll.reg, tmp;
> +	struct pms_ucode *pms = &state->ucode;
>  	struct bit_entry BIT_M;
> +	u32 pbus1098, r100b0c, r619f00;
> +	u32 pms_data, pms_kick;
>  	u16 script;
> +	u32 reg = state->pll.reg, tmp;
>  	int N = state->N;
>  	int M = state->M;
>  	int P = state->P;
> +	int i;
> 	if (state->type == PLL_MEMORY && perflvl->memscript &&
>  	    bit_table(dev, 'M', &BIT_M) == 0 &&
> @@ -126,20 +202,71 @@ nv50_pm_clock_set(struct drm_device *dev, void *pre_state)
>  		nouveau_bios_run_init_table(dev, perflvl->memscript, NULL);
>  	}
>+	/* only use PMS for changing the memory clocks */
>  	if (state->type == PLL_MEMORY) {
> -		nv_wr32(dev, 0x100210, 0);
> -		nv_wr32(dev, 0x1002dc, 1);
> -	}
> -	/* TODO: Tweek 0x4700 before reclocking UNK05 */
> -
> -	tmp  = nv_rd32(dev, reg + 0) & 0xfff8ffff;
> -	tmp |= 0x80000000 | (P << 16);
> -	nv_wr32(dev, reg + 0, tmp);
> -	nv_wr32(dev, reg + 4, (N << 8) | M);
> +		if (dev_priv->chipset < 0x90) {
> +			pms_data = 0x001400;
> +			pms_kick = 0x00000003;
> +		} else {
> +			pms_data = 0x080000;
> +			pms_kick = 0x00000001;
> +		}
>-	if (state->type == PLL_MEMORY) {
> -		nv_wr32(dev, 0x1002dc, 0);
> -		nv_wr32(dev, 0x100210, 0x80000000);
> +		/* upload ucode */
> +		pbus1098 = nv_mask(dev, 0x001098, 0x00000008, 0x00000000);
> +		nv_wr32(dev, 0x001304, 0x00000000);
> +		for (i = 0; i < pms->len / 4; i++)
> +			nv_wr32(dev, pms_data + (i * 4), pms->ptr.u32[i]);
> +		nv_wr32(dev, 0x001098, pbus1098 | 0x18);
> +
> +		nv_mask(dev, 0x616308, 0x00000000, 0x00000010);
> +		nv_mask(dev, 0x616b08, 0x00000000, 0x00000010);
> +
> +		/* and run it! there's some pre and post script operations that
> +		* nvidia do too, need to figure those out
> +		*/
> +		nv_mask(dev, 0x100200, 0x00000800, 0x00000000);
> +		r100b0c = nv_mask(dev, 0x100b0c, 0x000000ff, 0x00000012);
> +		r619f00 = nv_mask(dev, 0x619f00, 0x00000008, 0x00000000);
> +		nv_wr32(dev, 0x00130c, pms_kick);
> +		if (!nv_wait(dev, 0x001308, 0x00000100, 0x00000000)) {
> +			NV_ERROR(dev, "pms ucode exec timed out\n");
> +			NV_ERROR(dev, "0x001308: 0x%08x\n",
> +				nv_rd32(dev, 0x001308));
> +			for (i = 0; i < pms->len / 4; i++) {
> +				NV_ERROR(dev, "0x%06x: 0x%08x\n",
> +					0x1400 + (i * 4),
> +					nv_rd32(dev, 0x001400 + (i * 4)));
> +			}
> +		}
> +		nv_wr32(dev, 0x619f00, r619f00);
> +		nv_wr32(dev, 0x100b0c, r100b0c);
> +		nv_mask(dev, 0x616308, 0x00000000, 0x00000010);
> +		nv_mask(dev, 0x616b08, 0x00000000, 0x00000010);
> +
> +		/*if (perflvl->id == 0) {
> +			nv_wr32(dev, 0x100228, 0x00020102);
> +			nv_wr32(dev, 0x100230, 0x28000808);
> +			nv_wr32(dev, 0x100234, 0x06020702);
> +		} else if (perflvl->id == 1) {
> +			nv_wr32(dev, 0x100228, 0x00040305);
> +			nv_wr32(dev, 0x100230, 0x28000808);
> +			nv_wr32(dev, 0x100234, 0x11050905);
> +		}else if (perflvl->id == 2) {
> +			nv_wr32(dev, 0x100228, 0x0008080c);
> +			nv_wr32(dev, 0x100230, 0x28000808);
> +			nv_wr32(dev, 0x100234, 0x270c0c09);
> +		}*/
> +
> +		nv_mask(dev, 0x100200, 0x00000000, 0x00000800);
> +
> +	} else {
> +		/* TODO: Tweek 0x4700 before reclocking UNK05 */
> +
> +		tmp  = nv_rd32(dev, reg + 0) & 0xfff8ffff;
> +		tmp |= 0x80000000 | (P << 16);
> +		nv_wr32(dev, reg + 0, tmp);
> +		nv_wr32(dev, reg + 4, (N << 8) | M);
>  	}
> 	kfree(state);

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 2/2] drm/nouveau/nv50: reclock memory using PMS on nv50
  2011-05-06 23:42           ` Emil Velikov
@ 2011-05-07  1:50             ` Martin Peres
       [not found]               ` <4DC4A56B.4080008-Iz16wY1oaNPLSKGbIzaifA@public.gmane.org>
  0 siblings, 1 reply; 10+ messages in thread
From: Martin Peres @ 2011-05-07  1:50 UTC (permalink / raw)
  To: Emil Velikov; +Cc: nouveau-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW

Le 07/05/2011 01:42, Emil Velikov a écrit :
> On Sat, 30 Apr 2011 01:17:13 +0100, Martin Peres 
> <martin.peres@free.fr> wrote:
>
>> From: Martin Peres <martin.peres@ensi-bourges.fr>
>>
>> v2: Reclock memory after reclocking the other engines
>>
>> Signed-off-by: Martin Peres <martin.peres@ensi-bourges.fr>
>> ---
>>  drivers/gpu/drm/nouveau/nouveau_pm.c  |   11 +--
>>  drivers/gpu/drm/nouveau/nouveau_pms.h |   98 +++++++++++++++++++++
>>  drivers/gpu/drm/nouveau/nv50_pm.c     |  153 
>> ++++++++++++++++++++++++++++++---
>>  3 files changed, 242 insertions(+), 20 deletions(-)
>>  create mode 100644 drivers/gpu/drm/nouveau/nouveau_pms.h
>>
>> diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.c 
>> b/drivers/gpu/drm/nouveau/nouveau_pm.c
>> index 88f58b1..44d01bb 100644
>> --- a/drivers/gpu/drm/nouveau/nouveau_pm.c
>> +++ b/drivers/gpu/drm/nouveau/nouveau_pm.c
>> @@ -45,10 +45,6 @@ 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);
>> @@ -100,7 +96,6 @@ nouveau_pm_perflvl_set(struct drm_device *dev, 
>> struct nouveau_pm_level *perflvl)
>>     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*/
>> @@ -110,11 +105,13 @@ nouveau_pm_perflvl_set(struct drm_device *dev, 
>> struct nouveau_pm_level *perflvl)
>>      /* Wait for PLLs to stabilize */
>>      udelay(100);
>> +    pm->unpause(dev);
>> +
>> +    nouveau_pm_clock_set(dev, perflvl, PLL_MEMORY, perflvl->memory);
>> +
>>      pm->cur = perflvl;
>>      ret = 0;
>> -    pm->unpause(dev);
>> -
>>      NV_DEBUG(dev, "Reclocking took %lluns\n",
>>           (nv04_timer_read(dev) - start));
>> diff --git a/drivers/gpu/drm/nouveau/nouveau_pms.h 
>> b/drivers/gpu/drm/nouveau/nouveau_pms.h
>> new file mode 100644
>> index 0000000..d7a445b
>> --- /dev/null
>> +++ b/drivers/gpu/drm/nouveau/nouveau_pms.h
>> @@ -0,0 +1,98 @@
>> +/*
>> + * Copyright 2010 Red Hat Inc.
>> + *
>> + * Permission is hereby granted, free of charge, to any person 
>> obtaining a
>> + * copy of this software and associated documentation files (the 
>> "Software"),
>> + * to deal in the Software without restriction, including without 
>> limitation
>> + * the rights to use, copy, modify, merge, publish, distribute, 
>> sublicense,
>> + * and/or sell copies of the Software, and to permit persons to whom 
>> the
>> + * Software is furnished to do so, subject to the following conditions:
>> + *
>> + * The above copyright notice and this permission notice shall be 
>> included in
>> + * all copies or substantial portions of the Software.
>> + *
>> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
>> EXPRESS OR
>> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 
>> MERCHANTABILITY,
>> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO 
>> EVENT SHALL
>> + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, 
>> DAMAGES OR
>> + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 
>> OTHERWISE,
>> + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 
>> USE OR
>> + * OTHER DEALINGS IN THE SOFTWARE.
>> + *
>> + * Authors: Ben Skeggs
>> + */
>> +
>> +#ifndef __NOUVEAU_PMS_H__
>> +#define __NOUVEAU_PMS_H__
>> +
>> +struct pms_ucode {
>> +    u8 data[256];
>> +    union {
>> +        u8  *u08;
>> +        u16 *u16;
>> +        u32 *u32;
>> +    } ptr;
>> +    u16 len;
>> +
>> +    u32 reg;
>> +    u32 val;
>> +};
>> +
>> +static inline void
>> +pms_init(struct pms_ucode *pms)
>> +{
>> +    pms->ptr.u08 = pms->data;
>> +    pms->reg = 0xffffffff;
>> +    pms->val = 0xffffffff;
>> +}
>> +
>> +static inline void
>> +pms_fini(struct pms_ucode *pms)
>> +{
>> +    do {
>> +        *pms->ptr.u08++ = 0x7f;
>> +        pms->len = pms->ptr.u08 - pms->data;
>> +    } while (pms->len & 3);
>> +    pms->ptr.u08 = pms->data;
>> +}
>> +
>> +static inline void
>> +pms_unkn(struct pms_ucode *pms, u8 v0)
>> +{
>> +    *pms->ptr.u08++ = v0;
>> +}
>> +
>> +static inline void
>> +pms_op5f(struct pms_ucode *pms, u8 v0, u8 v1)
>> +{
>> +    *pms->ptr.u08++ = 0x5f;
>> +    *pms->ptr.u08++ = v0;
>> +    *pms->ptr.u08++ = v1;
>> +}
>> +
>> +static inline void
>> +pms_wr32(struct pms_ucode *pms, u32 reg, u32 val)
>> +{
>> +    if (val != pms->val) {
>> +        if ((val & 0xffff0000) == (pms->val & 0xffff0000)) {
>> +            *pms->ptr.u08++ = 0x42;
>> +            *pms->ptr.u16++ = (val & 0x0000ffff);
>> +        } else {
>> +            *pms->ptr.u08++ = 0xe2;
>> +            *pms->ptr.u32++ = val;
>> +        }
>> +
>> +        pms->val = val;
>> +    }
>> +
>> +    if ((reg & 0xffff0000) == (pms->reg & 0xffff0000)) {
>> +        *pms->ptr.u08++ = 0x40;
>> +        *pms->ptr.u16++ = (reg & 0x0000ffff);
>> +    } else {
>> +        *pms->ptr.u08++ = 0xe0;
>> +        *pms->ptr.u32++ = reg;
>> +    }
>> +    pms->reg = reg;
>> +}
>> +
>> +#endif
>> diff --git a/drivers/gpu/drm/nouveau/nv50_pm.c 
>> b/drivers/gpu/drm/nouveau/nv50_pm.c
>> index 4dd2d76..9b81f03 100644
>> --- a/drivers/gpu/drm/nouveau/nv50_pm.c
>> +++ b/drivers/gpu/drm/nouveau/nv50_pm.c
>> @@ -26,9 +26,11 @@
>>  #include "nouveau_drv.h"
>>  #include "nouveau_bios.h"
>>  #include "nouveau_pm.h"
>> +#include "nouveau_pms.h"
>> struct nv50_pm_state {
>>      struct nouveau_pm_level *perflvl;
>> +    struct pms_ucode ucode;
>>      struct pll_lims pll;
>>      enum pll_types type;
>>      int N, M, P;
>> @@ -73,14 +75,20 @@ void *
>>  nv50_pm_clock_pre(struct drm_device *dev, struct nouveau_pm_level 
>> *perflvl,
>>            u32 id, int khz)
>>  {
>> +    struct drm_nouveau_private *dev_priv = dev->dev_private;
>>      struct nv50_pm_state *state;
>> -    int dummy, ret;
>> +    struct pms_ucode *pms;
>> +    u32 reg0_old, reg0_new;
>> +    u32 crtc_mask;
>> +    u32 reg_c040;
>> +    int ret, dummy, i;
>>     state = kzalloc(sizeof(*state), GFP_KERNEL);
>>      if (!state)
>>          return ERR_PTR(-ENOMEM);
>>      state->type = id;
>>      state->perflvl = perflvl;
>> +    pms = &state->ucode;
>>     ret = get_pll_limits(dev, id, &state->pll);
>>      if (ret < 0) {
>> @@ -95,20 +103,88 @@ nv50_pm_clock_pre(struct drm_device *dev, struct 
>> nouveau_pm_level *perflvl,
>>          return ERR_PTR(ret);
>>      }
>> +    reg0_old = nv_rd32(dev, state->pll.reg + 0);
>> +    reg0_new = 0x80000000 | (state->P << 16) | (reg0_old & 0xfff8ffff);
>> +
>> +    reg_c040 = nv_rd32(dev, 0xc040);
>> +
>> +    crtc_mask = 0;
>> +    for (i = 0; i < 2; i++) {
>> +        if (nv_rd32(dev, NV50_PDISPLAY_CRTC_C(i, CLOCK)))
>> +            crtc_mask |= (1 << i);
>> +    }
>> +
>> +    pms_init(pms);
>> +
>> +    switch (state->type) {
>> +    case PLL_MEMORY:
>> +        /* Wait for vblank on all the CRTCs */
>> +        if (crtc_mask) {
>> +            pms_op5f(pms, crtc_mask, 0x00);
>> +            pms_op5f(pms, crtc_mask, 0x01);
>> +        }
>> +
>> +        pms_wr32(pms, 0x002504, 0x00000001);
>> +        pms_unkn(pms, 0x06); /* unknown */
>> +        pms_unkn(pms, 0xb0); /* Disable bus access */
>> +        pms_op5f(pms, 0x00, 0x01);
>> +
>> +        pms_wr32(pms, 0x1002d4, 0x00000001);
>> +        pms_wr32(pms, 0x1002d0, 0x00000001);
>> +
>> +        pms_wr32(pms, 0x100210, 0x00000000);
>> +        pms_wr32(pms, 0x1002dc, 0x00000001);
>> +        pms_wr32(pms, state->pll.reg + 0, reg0_old);
>> +        pms_wr32(pms, state->pll.reg + 4, (state->N << 8) | state->M);
>> +
>> +        pms_wr32(pms, state->pll.reg + 0, reg0_new);
>> +        pms_wr32(pms, 0x1002dc, 0x00000000);
>> +        pms_wr32(pms, 0x100210, 0x80000000);
>> +        pms_unkn(pms, 0x07); /* unknown */
>> +
>> +        pms_unkn(pms, 0x0b);
>> +        pms_unkn(pms, 0xd0); /* Enable bus access again */
>> +        pms_op5f(pms, 0x00, 0x01);
> This one should be "pms_op5f(pms, 0x00, 0x00);"
>
> Confirmed with a few nv84/86/96 traces
> Still not sure how much difference it is going to make (stability wise)
Crap, I use so many variations of this script. I must have screwed up 
this a little.

Anyway, I now own a nv84 that is quite unstable with this code so I'm 
trying to fix it (long and painful necessary process)
>
>> +        pms_wr32(pms, 0x002504, 0x00000000);
>> +        break;
>> +    default:
>> +        pms_unkn(pms, 0xb0); /* Disable bus access */
>> +
>> +        pms_wr32(pms, 0xc040,
>> +            (reg_c040 & ~(1 << 5 | 1 << 4)) | (1 << 20));
>> +        pms_wr32(pms, state->pll.reg + 0, reg0_new);
>> +        pms_wr32(pms, state->pll.reg + 4, (state->N << 8) | state->M);
>> +        pms_unkn(pms, 0x0e);
>> +
>> +        pms_wr32(pms, 0xc040, reg_c040);
>> +        pms_wr32(pms, 0xc040, 0x10);
>> +
>> +        pms_wr32(pms, 0xc040, reg_c040);
>> +
>> +        pms_unkn(pms, 0xd0); /* Enable bus access again */
>> +        break;
>> +    }
>> +    pms_fini(pms);
>> +
>>      return state;
>>  }
>> void
>>  nv50_pm_clock_set(struct drm_device *dev, void *pre_state)
>>  {
>> +    struct drm_nouveau_private *dev_priv = dev->dev_private;
>>      struct nv50_pm_state *state = pre_state;
>>      struct nouveau_pm_level *perflvl = state->perflvl;
>> -    u32 reg = state->pll.reg, tmp;
>> +    struct pms_ucode *pms = &state->ucode;
>>      struct bit_entry BIT_M;
>> +    u32 pbus1098, r100b0c, r619f00;
>> +    u32 pms_data, pms_kick;
>>      u16 script;
>> +    u32 reg = state->pll.reg, tmp;
>>      int N = state->N;
>>      int M = state->M;
>>      int P = state->P;
>> +    int i;
>>     if (state->type == PLL_MEMORY && perflvl->memscript &&
>>          bit_table(dev, 'M', &BIT_M) == 0 &&
>> @@ -126,20 +202,71 @@ nv50_pm_clock_set(struct drm_device *dev, void 
>> *pre_state)
>>          nouveau_bios_run_init_table(dev, perflvl->memscript, NULL);
>>      }
>> +    /* only use PMS for changing the memory clocks */
>>      if (state->type == PLL_MEMORY) {
>> -        nv_wr32(dev, 0x100210, 0);
>> -        nv_wr32(dev, 0x1002dc, 1);
>> -    }
>> -    /* TODO: Tweek 0x4700 before reclocking UNK05 */
>> -
>> -    tmp  = nv_rd32(dev, reg + 0) & 0xfff8ffff;
>> -    tmp |= 0x80000000 | (P << 16);
>> -    nv_wr32(dev, reg + 0, tmp);
>> -    nv_wr32(dev, reg + 4, (N << 8) | M);
>> +        if (dev_priv->chipset < 0x90) {
>> +            pms_data = 0x001400;
>> +            pms_kick = 0x00000003;
>> +        } else {
>> +            pms_data = 0x080000;
>> +            pms_kick = 0x00000001;
>> +        }
>> -    if (state->type == PLL_MEMORY) {
>> -        nv_wr32(dev, 0x1002dc, 0);
>> -        nv_wr32(dev, 0x100210, 0x80000000);
>> +        /* upload ucode */
>> +        pbus1098 = nv_mask(dev, 0x001098, 0x00000008, 0x00000000);
>> +        nv_wr32(dev, 0x001304, 0x00000000);
>> +        for (i = 0; i < pms->len / 4; i++)
>> +            nv_wr32(dev, pms_data + (i * 4), pms->ptr.u32[i]);
>> +        nv_wr32(dev, 0x001098, pbus1098 | 0x18);
>> +
>> +        nv_mask(dev, 0x616308, 0x00000000, 0x00000010);
>> +        nv_mask(dev, 0x616b08, 0x00000000, 0x00000010);
>> +
>> +        /* and run it! there's some pre and post script operations that
>> +        * nvidia do too, need to figure those out
>> +        */
>> +        nv_mask(dev, 0x100200, 0x00000800, 0x00000000);
>> +        r100b0c = nv_mask(dev, 0x100b0c, 0x000000ff, 0x00000012);
>> +        r619f00 = nv_mask(dev, 0x619f00, 0x00000008, 0x00000000);
>> +        nv_wr32(dev, 0x00130c, pms_kick);
>> +        if (!nv_wait(dev, 0x001308, 0x00000100, 0x00000000)) {
>> +            NV_ERROR(dev, "pms ucode exec timed out\n");
>> +            NV_ERROR(dev, "0x001308: 0x%08x\n",
>> +                nv_rd32(dev, 0x001308));
>> +            for (i = 0; i < pms->len / 4; i++) {
>> +                NV_ERROR(dev, "0x%06x: 0x%08x\n",
>> +                    0x1400 + (i * 4),
>> +                    nv_rd32(dev, 0x001400 + (i * 4)));
>> +            }
>> +        }
>> +        nv_wr32(dev, 0x619f00, r619f00);
>> +        nv_wr32(dev, 0x100b0c, r100b0c);
>> +        nv_mask(dev, 0x616308, 0x00000000, 0x00000010);
>> +        nv_mask(dev, 0x616b08, 0x00000000, 0x00000010);
>> +
>> +        /*if (perflvl->id == 0) {
>> +            nv_wr32(dev, 0x100228, 0x00020102);
>> +            nv_wr32(dev, 0x100230, 0x28000808);
>> +            nv_wr32(dev, 0x100234, 0x06020702);
>> +        } else if (perflvl->id == 1) {
>> +            nv_wr32(dev, 0x100228, 0x00040305);
>> +            nv_wr32(dev, 0x100230, 0x28000808);
>> +            nv_wr32(dev, 0x100234, 0x11050905);
>> +        }else if (perflvl->id == 2) {
>> +            nv_wr32(dev, 0x100228, 0x0008080c);
>> +            nv_wr32(dev, 0x100230, 0x28000808);
>> +            nv_wr32(dev, 0x100234, 0x270c0c09);
>> +        }*/
>> +
>> +        nv_mask(dev, 0x100200, 0x00000000, 0x00000800);
>> +
>> +    } else {
>> +        /* TODO: Tweek 0x4700 before reclocking UNK05 */
>> +
>> +        tmp  = nv_rd32(dev, reg + 0) & 0xfff8ffff;
>> +        tmp |= 0x80000000 | (P << 16);
>> +        nv_wr32(dev, reg + 0, tmp);
>> +        nv_wr32(dev, reg + 4, (N << 8) | M);
>>      }
>>     kfree(state);

_______________________________________________
Nouveau mailing list
Nouveau@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/nouveau

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 2/2] drm/nouveau/nv50: reclock memory using PMS on nv50
       [not found]               ` <4DC4A56B.4080008-Iz16wY1oaNPLSKGbIzaifA@public.gmane.org>
@ 2011-05-07  7:48                 ` Maxim Levitsky
  0 siblings, 0 replies; 10+ messages in thread
From: Maxim Levitsky @ 2011-05-07  7:48 UTC (permalink / raw)
  To: Martin Peres; +Cc: nouveau-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW

[-- Attachment #1: Type: text/plain, Size: 299 bytes --]

Just my 0.2 cent, here are PMS scripts executed by the blob (270.41.06)
with slightly better formatting (script attached)
This is trace from bare Xorg start on my NV86.


-- 
Best regards,
        Maxim Levitsky

Visit my blog: http://maximlevitsky.wordpress.com
Warning: Above blog contains rants.

[-- Attachment #2: pms_scripts.txt --]
[-- Type: text/plain, Size: 10634 bytes --]

script1:

00000000: e2 17 01 10 00     data 0x100117
00000005: e0 0c a8 61 00     addr 0x61a80c
0000000a: 00                 ???
0000000b: 40 0c a8           addr 0x61a80c
0000000e: 05                 ??? [unknown: 05]
0000000f: e2 00 00 00 00     data 0
00000014: 40 0c a8           addr 0x61a80c
00000017: 7f                 exit


script2:

00000000: e2 a7 00 10 00     data 0x1000a7
00000005: e0 0c a0 61 00     addr 0x61a00c
0000000a: 00                 ???
0000000b: 40 0c a0           addr 0x61a00c
0000000e: 05                 ??? [unknown: 05]
0000000f: e2 00 00 00 00     data 0
00000014: 40 0c a0           addr 0x61a00c
00000017: 7f                 exit


script3:

00000000: e2 00 33 00 00     data 0x3300
00000005: e0 00 12 61 00     addr 0x611200
0000000a: 42 01 00           data 0x1
0000000d: e0 04 25 00 00     addr 0x2504
00000012: 07                 ??? [unknown: 07]
00000013: b0                 ??? [unknown: b0]
00000014: 5f 00 01           unk5f [unknown: 00 00 01]
00000017: e0 d4 02 10 00     addr 0x1002d4
0000001c: 40 d0 02           addr 0x1002d0
0000001f: 40 d0 02           addr 0x1002d0
00000022: 42 00 00           data 0
00000025: 40 10 02           addr 0x100210
00000028: 42 01 00           data 0x1
0000002b: 40 dc 02           addr 0x1002dc
0000002e: 42 05 25           data 0x2505
00000031: e0 0c 40 00 00     addr 0x400c
00000036: e2 00 e4 59 80     data 0x8059e400
0000003b: 40 08 40           addr 0x4008
0000003e: 0d                 ??? [unknown: 0d]
0000003f: 0a                 ??? [unknown: 0a]
00000040: 40 08 40           addr 0x4008
00000043: e2 00 00 00 00     data 0
00000048: e0 dc 02 10 00     addr 0x1002dc
0000004d: e2 00 00 00 80     data 0x80000000
00000052: 40 10 02           addr 0x100210
00000055: 07                 ??? [unknown: 07]
00000056: e2 62 0a 00 00     data 0xa62
0000005b: 40 c0 02           addr 0x1002c0
0000005e: e2 04 09 01 0c     data 0xc010904
00000063: 40 24 02           addr 0x100224
00000066: e2 18 2a 12 06     data 0x6122a18
0000006b: 40 20 02           addr 0x100220
0000006e: e2 62 0b 00 00     data 0xb62
00000073: 40 c0 02           addr 0x1002c0
00000076: 42 62 0a           data 0xa62
00000079: 40 c0 02           addr 0x1002c0
0000007c: 0b                 ??? [unknown: 0b]
0000007d: d0                 ??? [unknown: d0]
0000007e: 5f 00 00           unk5f
00000081: 42 30 33           data 0x3330
00000084: e0 00 12 61 00     addr 0x611200
00000089: 7f                 exit
0000008a: 7f                 exit
0000008b: 7f                 exit


script4:
------------little bug here------

00000000: 0a                 ??? [unknown: 0a]
00000001: 7f                 exit
00000002: e2 00 33 00 00     data 0x3300
00000007: e0 00 12 61 00     addr 0x611200
0000000c: 42 01 00           data 0x1
0000000f: e0 04 25 00 00     addr 0x2504
00000014: 07                 ??? [unknown: 07]
00000015: b0                 ??? [unknown: b0]
00000016: 5f 00 01           unk5f [unknown: 00 00 01]
00000019: e0 d4 02 10 00     addr 0x1002d4
0000001e: 40 d0 02           addr 0x1002d0
00000021: 40 d0 02           addr 0x1002d0
00000024: 42 00 00           data 0
00000027: 40 10 02           addr 0x100210
0000002a: 42 01 00           data 0x1
0000002d: 40 dc 02           addr 0x1002dc
00000030: 42 05 25           data 0x2505
00000033: e0 0c 40 00 00     addr 0x400c
00000038: e2 00 e4 59 80     data 0x8059e400
0000003d: 40 08 40           addr 0x4008
00000040: 0d                 ??? [unknown: 0d]
00000041: 0a                 ??? [unknown: 0a]
00000042: 40 08 40           addr 0x4008
00000045: e2 00 00 00 00     data 0
0000004a: e0 dc 02 10 00     addr 0x1002dc
0000004f: e2 00 00 00 80     data 0x80000000
00000054: 40 10 02           addr 0x100210
00000057: 07                 ??? [unknown: 07]
00000058: e2 62 0a 00 00     data 0xa62
0000005d: 40 c0 02           addr 0x1002c0
00000060: e2 04 09 01 0c     data 0xc010904
00000065: 40 24 02           addr 0x100224
00000068: e2 18 2a 12 06     data 0x6122a18
0000006d: 40 20 02           addr 0x100220
00000070: e2 62 0b 00 00     data 0xb62
00000075: 40 c0 02           addr 0x1002c0
00000078: 42 62 0a           data 0xa62
0000007b: 40 c0 02           addr 0x1002c0
0000007e: 0b                 ??? [unknown: 0b]
0000007f: d0                 ??? [unknown: d0]
00000080: 5f 00 00           unk5f
00000083: 42 30 33           data 0x3330
00000086: e0 00 12 61 00     addr 0x611200
0000008b: 7f                 exit



script5:

00000000: 0a                 ??? [unknown: 0a]
00000001: 00                 ???
00000002: e2 00 ?? ?? ??     data 0 [incomplete]


script6:

00000000: e2 00 33 00 00     data 0x3300
00000005: e0 00 12 61 00     addr 0x611200
0000000a: 07                 ??? [unknown: 07]
0000000b: b0                 ??? [unknown: b0]
0000000c: 5f 00 01           unk5f [unknown: 00 00 01]
0000000f: e2 83 db f0 2e     data 0x2ef0db83
00000014: e0 40 c0 00 00     addr 0xc040
00000019: e2 00 00 00 80     data 0x80000000
0000001e: 40 20 40           addr 0x4020
00000021: e2 83 db f0 2e     data 0x2ef0db83
00000026: 40 40 c0           addr 0xc040
00000029: e2 00 00 09 a0     data 0xa0090000
0000002e: 40 28 40           addr 0x4028
00000031: e2 02 10 00 00     data 0x1002
00000036: 40 2c 40           addr 0x402c
00000039: 0e                 ??? [unknown: 0e]
0000003a: e2 a3 db e0 2e     data 0x2ee0dba3
0000003f: 40 40 c0           addr 0xc040
00000042: e2 10 00 00 00     data 0x10
00000047: 40 4c c0           addr 0xc04c
0000004a: e2 a3 db e0 2e     data 0x2ee0dba3
0000004f: 40 40 c0           addr 0xc040
00000052: d0                 ??? [unknown: d0]
00000053: 5f 00 00           unk5f
00000056: e2 30 33 00 00     data 0x3330
0000005b: e0 00 12 61 00     addr 0x611200
00000060: 7f                 exit
00000061: 7f                 exit
00000062: 7f                 exit
00000063: 7f                 exit


script7:

00000000: 5f 01 00           unk5f [unknown: 00 01 00]
00000003: 5f 01 01           unk5f [unknown: 00 01 01]
00000006: b0                 ??? [unknown: b0]
00000007: 5f 00 01           unk5f [unknown: 00 00 01]
0000000a: e2 83 db f0 2e     data 0x2ef0db83
0000000f: e0 40 c0 00 00     addr 0xc040
00000014: e2 00 00 00 80     data 0x80000000
00000019: 40 20 40           addr 0x4020
0000001c: e2 83 db f0 2e     data 0x2ef0db83
00000021: 40 40 c0           addr 0xc040
00000024: e2 00 00 09 a0     data 0xa0090000
00000029: 40 28 40           addr 0x4028
0000002c: e2 02 0b 00 00     data 0xb02
00000031: 40 2c 40           addr 0x402c
00000034: 0e                 ??? [unknown: 0e]
00000035: e2 a3 db e0 2e     data 0x2ee0dba3
0000003a: 40 40 c0           addr 0xc040
0000003d: e2 10 00 00 00     data 0x10
00000042: 40 4c c0           addr 0xc04c
00000045: e2 a3 db e0 2e     data 0x2ee0dba3
0000004a: 40 40 c0           addr 0xc040
0000004d: d0                 ??? [unknown: d0]
0000004e: 5f 00 00           unk5f
00000051: 7f                 exit
00000052: 7f                 exit
00000053: 7f                 exit


script8:

00000000: 5f 01 01           unk5f [unknown: 00 01 01]
00000003: 5f 01 00           unk5f [unknown: 00 01 00]
00000006: e2 a3 db e0 2a     data 0x2ae0dba3
0000000b: e0 40 c0 00 00     addr 0xc040
00000010: 7f                 exit
00000011: 7f                 exit
00000012: 7f                 exit
00000013: 7f                 exit


script9:


00000000: 5f 01 01           unk5f [unknown: 00 01 01]
00000003: 5f 01 00           unk5f [unknown: 00 01 00]
00000006: e2 01 00 00 00     data 0x1
0000000b: e0 04 25 00 00     addr 0x2504
00000010: 06                 ??? [unknown: 06]
00000011: b0                 ??? [unknown: b0]
00000012: 5f 00 01           unk5f [unknown: 00 00 01]
00000015: e0 d4 02 10 00     addr 0x1002d4
0000001a: 40 d0 02           addr 0x1002d0
0000001d: 40 d0 02           addr 0x1002d0
00000020: 42 00 00           data 0
00000023: 40 10 02           addr 0x100210
00000026: 42 01 00           data 0x1
00000029: 40 dc 02           addr 0x1002dc
0000002c: e2 00 e6 02 80     data 0x8002e600
00000031: e0 08 40 00 00     addr 0x4008
00000036: e2 00 e2 18 80     data 0x8018e200
0000003b: 40 08 40           addr 0x4008
0000003e: e2 00 00 00 00     data 0
00000043: e0 dc 02 10 00     addr 0x1002dc
00000048: e2 00 00 00 80     data 0x80000000
0000004d: 40 10 02           addr 0x100210
00000050: 07                 ??? [unknown: 07]
00000051: e2 62 06 00 00     data 0x662
00000056: 40 c0 02           addr 0x1002c0
00000059: e2 04 0a 01 0a     data 0xa010a04
0000005e: 40 24 02           addr 0x100224
00000061: e2 0f 23 11 07     data 0x711230f
00000066: 40 20 02           addr 0x100220
00000069: e2 77 21 02 00     data 0x22177
0000006e: 40 1c 07           addr 0x10071c
00000071: e2 00 00 00 00     data 0
00000076: 40 3c 05           addr 0x10053c
00000079: 42 62 07           data 0x762
0000007c: 40 c0 02           addr 0x1002c0
0000007f: 42 62 06           data 0x662
00000082: 40 c0 02           addr 0x1002c0
00000085: 0b                 ??? [unknown: 0b]
00000086: d0                 ??? [unknown: d0]
00000087: 5f 00 00           unk5f
0000008a: 7f                 exit
0000008b: 7f                 exit


script10:

00000000: 5f 01 00           unk5f [unknown: 00 01 00]
00000003: 5f 01 01           unk5f [unknown: 00 01 01]
00000006: b0                 ??? [unknown: b0]
00000007: 5f 00 01           unk5f [unknown: 00 00 01]
0000000a: e2 83 db f0 2a     data 0x2af0db83
0000000f: e0 40 c0 00 00     addr 0xc040
00000014: e2 00 00 01 80     data 0x80010000
00000019: 40 20 40           addr 0x4020
0000001c: e2 83 db f0 2a     data 0x2af0db83
00000021: 40 40 c0           addr 0xc040
00000024: e2 00 00 12 a0     data 0xa0120000
00000029: 40 28 40           addr 0x4028
0000002c: e2 04 1b 00 00     data 0x1b04
00000031: 40 2c 40           addr 0x402c
00000034: 0e                 ??? [unknown: 0e]
00000035: e2 a3 db e0 2a     data 0x2ae0dba3
0000003a: 40 40 c0           addr 0xc040
0000003d: e2 10 00 00 00     data 0x10
00000042: 40 4c c0           addr 0xc04c
00000045: e2 a3 db e0 2a     data 0x2ae0dba3
0000004a: 40 40 c0           addr 0xc040
0000004d: d0                 ??? [unknown: d0]
0000004e: 5f 00 00           unk5f
00000051: 7f                 exit
00000052: 7f                 exit
00000053: 7f                 exit


script11:

00000000: 5f 01 01           unk5f [unknown: 00 01 01]
00000003: 5f 01 00           unk5f [unknown: 00 01 00]
00000006: e2 a3 db e0 2e     data 0x2ee0dba3
0000000b: e0 40 c0 00 00     addr 0xc040
00000010: 7f                 exit
00000011: 7f                 exit
00000012: 7f                 exit
00000013: 7f                 exit







[-- Attachment #3: pms_translated.txt --]
[-- Type: text/plain, Size: 3170 bytes --]

executing PMS script 1

0x61a80c = 0x100117
00                 
0x61a80c = 0x100117
05                 
0x61a80c = 0x0
exit


executing PMS script 2

0x61a00c = 0x1000a7
00                 
0x61a00c = 0x1000a7
05                 
0x61a00c = 0x0
exit


executing PMS script 3

0x611200 = 0x3300
0x2504 = 0x1
07                 
b0                 
5f 00 01           
0x1002d4 = 0x1
0x1002d0 = 0x1
0x1002d0 = 0x1
0x100210 = 0x0
0x1002dc = 0x1
0x400c = 0x2505
0x4008 = 0x8059e400
0d                 
0a                 
0x4008 = 0x8059e400
0x1002dc = 0x0
0x100210 = 0x80000000
07                 
0x1002c0 = 0xa62
0x100224 = 0xc010904
0x100220 = 0x6122a18
0x1002c0 = 0xb62
0x1002c0 = 0xa62
0b                 
d0                 
5f 00 00           
0x611200 = 0x3330
exit
exit
exit


executing PMS script 4
------------little bug here------

0a                 
exit
0x611200 = 0x3300
0x2504 = 0x1
07                 
b0                 
5f 00 01           
0x1002d4 = 0x1
0x1002d0 = 0x1
0x1002d0 = 0x1
0x100210 = 0x0
0x1002dc = 0x1
0x400c = 0x2505
0x4008 = 0x8059e400
0d                 
0a                 
0x4008 = 0x8059e400
0x1002dc = 0x0
0x100210 = 0x80000000
07                 
0x1002c0 = 0xa62
0x100224 = 0xc010904
0x100220 = 0x6122a18
0x1002c0 = 0xb62
0x1002c0 = 0xa62
0b                 
d0                 
5f 00 00           
0x611200 = 0x3330
exit



executing PMS script 5

0a                 
00                 


executing PMS script 6

0x611200 = 0x3300
07                 
b0                 
5f 00 01           
0xc040 = 0x2ef0db83
0x4020 = 0x80000000
0xc040 = 0x2ef0db83
0x4028 = 0xa0090000
0x402c = 0x1002
0e                 
0xc040 = 0x2ee0dba3
0xc04c = 0x10
0xc040 = 0x2ee0dba3
d0                 
5f 00 00           
0x611200 = 0x3330
exit
exit
exit
exit


executing PMS script 7

5f 01 00           
5f 01 01           
b0                 
5f 00 01           
0xc040 = 0x2ef0db83
0x4020 = 0x80000000
0xc040 = 0x2ef0db83
0x4028 = 0xa0090000
0x402c = 0xb02
0e                 
0xc040 = 0x2ee0dba3
0xc04c = 0x10
0xc040 = 0x2ee0dba3
d0                 
5f 00 00           
exit
exit
exit


executing PMS script 8

5f 01 01           
5f 01 00           
0xc040 = 0x2ae0dba3
exit
exit
exit
exit


executing PMS script 9


5f 01 01           
5f 01 00           
0x2504 = 0x1
06                 
b0                 
5f 00 01           
0x1002d4 = 0x1
0x1002d0 = 0x1
0x1002d0 = 0x1
0x100210 = 0x0
0x1002dc = 0x1
0x4008 = 0x8002e600
0x4008 = 0x8018e200
0x1002dc = 0x0
0x100210 = 0x80000000
07                 
0x1002c0 = 0x662
0x100224 = 0xa010a04
0x100220 = 0x711230f
0x10071c = 0x22177
0x10053c = 0x0
0x1002c0 = 0x762
0x1002c0 = 0x662
0b                 
d0                 
5f 00 00           
exit
exit


executing PMS script 10

5f 01 00           
5f 01 01           
b0                 
5f 00 01           
0xc040 = 0x2af0db83
0x4020 = 0x80010000
0xc040 = 0x2af0db83
0x4028 = 0xa0120000
0x402c = 0x1b04
0e                 
0xc040 = 0x2ae0dba3
0xc04c = 0x10
0xc040 = 0x2ae0dba3
d0                 
5f 00 00           
exit
exit
exit


executing PMS script 11

5f 01 01           
5f 01 00           
0xc040 = 0x2ee0dba3
exit
exit
exit
exit







[-- Attachment #4: pms_exec.py --]
[-- Type: text/x-python, Size: 1284 bytes --]

#! /usr/bin/env python

import fileinput
import re
import os
import sys

command_pattern = re.compile(".*(data|addr|exit) ?(0x.*|[0-9]*|)?")
unknown_pattern = re.compile("[0-9|a-f]:\ *(.*)(\?\?\?|unk5f)")

for line in fileinput.input():

    script_number = 0

    #start of new script
    if "script" in line:
           script_number = int(line.split("script")[1][:-2])
           print "executing PMS script %d" % script_number

           address = 0xFFFFFFFF
           value= 0xFFFFFFFF
           continue


    match = command_pattern.search(line)
    if match:
        if match.groups()[0] == "data":
                value = int(match.groups()[1], 0)
        elif match.groups()[0] == "addr":
                address = int(match.groups()[1], 0)
                print "0x%x = 0x%x" % (address, value)
        elif match.groups()[0] == "exit":
                print "exit"

        continue



    match = unknown_pattern.search(line)
    if match:
            print match.groups()[0]
            continue

    sys.stdout.write(line)
    continue


"""
            answer = raw_input("do you want to: %s :" % execstring)

            if answer == "no":
                continue

            print "executing %s" % execstring            

            os.system(execstring)
"""


[-- Attachment #5: Type: text/plain, Size: 181 bytes --]

_______________________________________________
Nouveau mailing list
Nouveau-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW@public.gmane.org
http://lists.freedesktop.org/mailman/listinfo/nouveau

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH 2/2] drm/nouveau/nv50: reclock memory using PMS on nv50
       [not found] ` <1303948692-22455-1-git-send-email-martin.peres-GANU6spQydw@public.gmane.org>
@ 2011-04-27 23:58   ` Martin Peres
  0 siblings, 0 replies; 10+ messages in thread
From: Martin Peres @ 2011-04-27 23:58 UTC (permalink / raw)
  To: nouveau-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW; +Cc: Martin Peres

From: Martin Peres <martin.peres-Iz16wY1oaNPLSKGbIzaifA@public.gmane.org>

Signed-off-by: Martin Peres <martin.peres-Iz16wY1oaNPLSKGbIzaifA@public.gmane.org>
---
 drivers/gpu/drm/nouveau/nouveau_pm.c  |    7 +-
 drivers/gpu/drm/nouveau/nouveau_pms.h |   98 +++++++++++++++++++++
 drivers/gpu/drm/nouveau/nv50_pm.c     |  153 ++++++++++++++++++++++++++++++---
 3 files changed, 243 insertions(+), 15 deletions(-)
 create mode 100644 drivers/gpu/drm/nouveau/nouveau_pms.h

diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.c b/drivers/gpu/drm/nouveau/nouveau_pm.c
index 3bffe60..abc0ef9 100644
--- a/drivers/gpu/drm/nouveau/nouveau_pm.c
+++ b/drivers/gpu/drm/nouveau/nouveau_pm.c
@@ -79,13 +79,16 @@ nouveau_pm_perflvl_set(struct drm_device *dev, struct nouveau_pm_level *perflvl)
 		}
 	}
 
+	nouveau_pm_clock_set(dev, perflvl, PLL_MEMORY, perflvl->memory);
+
 	ret = pm->pause(dev);
-	if (ret)
+	if (ret) {
+		nouveau_pm_clock_set(dev, pm->cur, PLL_MEMORY, perflvl->memory);
 		return ret;
+	}
 
 	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);
 
 	/* Wait for PLLs to stabilize */
diff --git a/drivers/gpu/drm/nouveau/nouveau_pms.h b/drivers/gpu/drm/nouveau/nouveau_pms.h
new file mode 100644
index 0000000..d7a445b
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nouveau_pms.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2010 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#ifndef __NOUVEAU_PMS_H__
+#define __NOUVEAU_PMS_H__
+
+struct pms_ucode {
+	u8 data[256];
+	union {
+		u8  *u08;
+		u16 *u16;
+		u32 *u32;
+	} ptr;
+	u16 len;
+
+	u32 reg;
+	u32 val;
+};
+
+static inline void
+pms_init(struct pms_ucode *pms)
+{
+	pms->ptr.u08 = pms->data;
+	pms->reg = 0xffffffff;
+	pms->val = 0xffffffff;
+}
+
+static inline void
+pms_fini(struct pms_ucode *pms)
+{
+	do {
+		*pms->ptr.u08++ = 0x7f;
+		pms->len = pms->ptr.u08 - pms->data;
+	} while (pms->len & 3);
+	pms->ptr.u08 = pms->data;
+}
+
+static inline void
+pms_unkn(struct pms_ucode *pms, u8 v0)
+{
+	*pms->ptr.u08++ = v0;
+}
+
+static inline void
+pms_op5f(struct pms_ucode *pms, u8 v0, u8 v1)
+{
+	*pms->ptr.u08++ = 0x5f;
+	*pms->ptr.u08++ = v0;
+	*pms->ptr.u08++ = v1;
+}
+
+static inline void
+pms_wr32(struct pms_ucode *pms, u32 reg, u32 val)
+{
+	if (val != pms->val) {
+		if ((val & 0xffff0000) == (pms->val & 0xffff0000)) {
+			*pms->ptr.u08++ = 0x42;
+			*pms->ptr.u16++ = (val & 0x0000ffff);
+		} else {
+			*pms->ptr.u08++ = 0xe2;
+			*pms->ptr.u32++ = val;
+		}
+
+		pms->val = val;
+	}
+
+	if ((reg & 0xffff0000) == (pms->reg & 0xffff0000)) {
+		*pms->ptr.u08++ = 0x40;
+		*pms->ptr.u16++ = (reg & 0x0000ffff);
+	} else {
+		*pms->ptr.u08++ = 0xe0;
+		*pms->ptr.u32++ = reg;
+	}
+	pms->reg = reg;
+}
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/nv50_pm.c b/drivers/gpu/drm/nouveau/nv50_pm.c
index 4dd2d76..9b81f03 100644
--- a/drivers/gpu/drm/nouveau/nv50_pm.c
+++ b/drivers/gpu/drm/nouveau/nv50_pm.c
@@ -26,9 +26,11 @@
 #include "nouveau_drv.h"
 #include "nouveau_bios.h"
 #include "nouveau_pm.h"
+#include "nouveau_pms.h"
 
 struct nv50_pm_state {
 	struct nouveau_pm_level *perflvl;
+	struct pms_ucode ucode;
 	struct pll_lims pll;
 	enum pll_types type;
 	int N, M, P;
@@ -73,14 +75,20 @@ void *
 nv50_pm_clock_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl,
 		  u32 id, int khz)
 {
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
 	struct nv50_pm_state *state;
-	int dummy, ret;
+	struct pms_ucode *pms;
+	u32 reg0_old, reg0_new;
+	u32 crtc_mask;
+	u32 reg_c040;
+	int ret, dummy, i;
 
 	state = kzalloc(sizeof(*state), GFP_KERNEL);
 	if (!state)
 		return ERR_PTR(-ENOMEM);
 	state->type = id;
 	state->perflvl = perflvl;
+	pms = &state->ucode;
 
 	ret = get_pll_limits(dev, id, &state->pll);
 	if (ret < 0) {
@@ -95,20 +103,88 @@ nv50_pm_clock_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl,
 		return ERR_PTR(ret);
 	}
 
+	reg0_old = nv_rd32(dev, state->pll.reg + 0);
+	reg0_new = 0x80000000 | (state->P << 16) | (reg0_old & 0xfff8ffff);
+
+	reg_c040 = nv_rd32(dev, 0xc040);
+
+	crtc_mask = 0;
+	for (i = 0; i < 2; i++) {
+		if (nv_rd32(dev, NV50_PDISPLAY_CRTC_C(i, CLOCK)))
+			crtc_mask |= (1 << i);
+	}
+
+	pms_init(pms);
+
+	switch (state->type) {
+	case PLL_MEMORY:
+		/* Wait for vblank on all the CRTCs */
+		if (crtc_mask) {
+			pms_op5f(pms, crtc_mask, 0x00);
+			pms_op5f(pms, crtc_mask, 0x01);
+		}
+
+		pms_wr32(pms, 0x002504, 0x00000001);
+		pms_unkn(pms, 0x06); /* unknown */
+		pms_unkn(pms, 0xb0); /* Disable bus access */
+		pms_op5f(pms, 0x00, 0x01);
+
+		pms_wr32(pms, 0x1002d4, 0x00000001);
+		pms_wr32(pms, 0x1002d0, 0x00000001);
+
+		pms_wr32(pms, 0x100210, 0x00000000);
+		pms_wr32(pms, 0x1002dc, 0x00000001);
+		pms_wr32(pms, state->pll.reg + 0, reg0_old);
+		pms_wr32(pms, state->pll.reg + 4, (state->N << 8) | state->M);
+
+		pms_wr32(pms, state->pll.reg + 0, reg0_new);
+		pms_wr32(pms, 0x1002dc, 0x00000000);
+		pms_wr32(pms, 0x100210, 0x80000000);
+		pms_unkn(pms, 0x07); /* unknown */
+
+		pms_unkn(pms, 0x0b);
+		pms_unkn(pms, 0xd0); /* Enable bus access again */
+		pms_op5f(pms, 0x00, 0x01);
+		pms_wr32(pms, 0x002504, 0x00000000);
+		break;
+	default:
+		pms_unkn(pms, 0xb0); /* Disable bus access */
+
+		pms_wr32(pms, 0xc040,
+			(reg_c040 & ~(1 << 5 | 1 << 4)) | (1 << 20));
+		pms_wr32(pms, state->pll.reg + 0, reg0_new);
+		pms_wr32(pms, state->pll.reg + 4, (state->N << 8) | state->M);
+		pms_unkn(pms, 0x0e);
+
+		pms_wr32(pms, 0xc040, reg_c040);
+		pms_wr32(pms, 0xc040, 0x10);
+
+		pms_wr32(pms, 0xc040, reg_c040);
+
+		pms_unkn(pms, 0xd0); /* Enable bus access again */
+		break;
+	}
+	pms_fini(pms);
+
 	return state;
 }
 
 void
 nv50_pm_clock_set(struct drm_device *dev, void *pre_state)
 {
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
 	struct nv50_pm_state *state = pre_state;
 	struct nouveau_pm_level *perflvl = state->perflvl;
-	u32 reg = state->pll.reg, tmp;
+	struct pms_ucode *pms = &state->ucode;
 	struct bit_entry BIT_M;
+	u32 pbus1098, r100b0c, r619f00;
+	u32 pms_data, pms_kick;
 	u16 script;
+	u32 reg = state->pll.reg, tmp;
 	int N = state->N;
 	int M = state->M;
 	int P = state->P;
+	int i;
 
 	if (state->type == PLL_MEMORY && perflvl->memscript &&
 	    bit_table(dev, 'M', &BIT_M) == 0 &&
@@ -126,20 +202,71 @@ nv50_pm_clock_set(struct drm_device *dev, void *pre_state)
 		nouveau_bios_run_init_table(dev, perflvl->memscript, NULL);
 	}
 
+	/* only use PMS for changing the memory clocks */
 	if (state->type == PLL_MEMORY) {
-		nv_wr32(dev, 0x100210, 0);
-		nv_wr32(dev, 0x1002dc, 1);
-	}
-	/* TODO: Tweek 0x4700 before reclocking UNK05 */
-
-	tmp  = nv_rd32(dev, reg + 0) & 0xfff8ffff;
-	tmp |= 0x80000000 | (P << 16);
-	nv_wr32(dev, reg + 0, tmp);
-	nv_wr32(dev, reg + 4, (N << 8) | M);
+		if (dev_priv->chipset < 0x90) {
+			pms_data = 0x001400;
+			pms_kick = 0x00000003;
+		} else {
+			pms_data = 0x080000;
+			pms_kick = 0x00000001;
+		}
 
-	if (state->type == PLL_MEMORY) {
-		nv_wr32(dev, 0x1002dc, 0);
-		nv_wr32(dev, 0x100210, 0x80000000);
+		/* upload ucode */
+		pbus1098 = nv_mask(dev, 0x001098, 0x00000008, 0x00000000);
+		nv_wr32(dev, 0x001304, 0x00000000);
+		for (i = 0; i < pms->len / 4; i++)
+			nv_wr32(dev, pms_data + (i * 4), pms->ptr.u32[i]);
+		nv_wr32(dev, 0x001098, pbus1098 | 0x18);
+
+		nv_mask(dev, 0x616308, 0x00000000, 0x00000010);
+		nv_mask(dev, 0x616b08, 0x00000000, 0x00000010);
+
+		/* and run it! there's some pre and post script operations that
+		* nvidia do too, need to figure those out
+		*/
+		nv_mask(dev, 0x100200, 0x00000800, 0x00000000);
+		r100b0c = nv_mask(dev, 0x100b0c, 0x000000ff, 0x00000012);
+		r619f00 = nv_mask(dev, 0x619f00, 0x00000008, 0x00000000);
+		nv_wr32(dev, 0x00130c, pms_kick);
+		if (!nv_wait(dev, 0x001308, 0x00000100, 0x00000000)) {
+			NV_ERROR(dev, "pms ucode exec timed out\n");
+			NV_ERROR(dev, "0x001308: 0x%08x\n",
+				nv_rd32(dev, 0x001308));
+			for (i = 0; i < pms->len / 4; i++) {
+				NV_ERROR(dev, "0x%06x: 0x%08x\n",
+					0x1400 + (i * 4),
+					nv_rd32(dev, 0x001400 + (i * 4)));
+			}
+		}
+		nv_wr32(dev, 0x619f00, r619f00);
+		nv_wr32(dev, 0x100b0c, r100b0c);
+		nv_mask(dev, 0x616308, 0x00000000, 0x00000010);
+		nv_mask(dev, 0x616b08, 0x00000000, 0x00000010);
+
+		/*if (perflvl->id == 0) {
+			nv_wr32(dev, 0x100228, 0x00020102);
+			nv_wr32(dev, 0x100230, 0x28000808);
+			nv_wr32(dev, 0x100234, 0x06020702);
+		} else if (perflvl->id == 1) {
+			nv_wr32(dev, 0x100228, 0x00040305);
+			nv_wr32(dev, 0x100230, 0x28000808);
+			nv_wr32(dev, 0x100234, 0x11050905);
+		}else if (perflvl->id == 2) {
+			nv_wr32(dev, 0x100228, 0x0008080c);
+			nv_wr32(dev, 0x100230, 0x28000808);
+			nv_wr32(dev, 0x100234, 0x270c0c09);
+		}*/
+
+		nv_mask(dev, 0x100200, 0x00000000, 0x00000800);
+
+	} else {
+		/* TODO: Tweek 0x4700 before reclocking UNK05 */
+
+		tmp  = nv_rd32(dev, reg + 0) & 0xfff8ffff;
+		tmp |= 0x80000000 | (P << 16);
+		nv_wr32(dev, reg + 0, tmp);
+		nv_wr32(dev, reg + 4, (N << 8) | M);
 	}
 
 	kfree(state);
-- 
1.7.4.4

^ permalink raw reply related	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2011-05-07  7:48 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-04-28 18:33 [PATCH 0/2] safer reclocking take 2 Martin Peres
     [not found] ` <1304015622-5910-1-git-send-email-martin.peres-GANU6spQydw@public.gmane.org>
2011-04-28 18:33   ` [PATCH 1/2] drm/nouveau/pm: Add pm.(un)pause functions Martin Peres
2011-04-28 18:33   ` [PATCH 2/2] drm/nouveau/nv50: reclock memory using PMS on nv50 Martin Peres
2011-04-30  0:17   ` (no subject) Martin Peres
     [not found]     ` <1304122633-21621-1-git-send-email-martin.peres-GANU6spQydw@public.gmane.org>
2011-04-30  0:17       ` [PATCH 1/2] drm/nouveau/pm: Add pm.(un)pause functions Martin Peres
2011-04-30  0:17       ` [PATCH 2/2] drm/nouveau/nv50: reclock memory using PMS on nv50 Martin Peres
     [not found]         ` <1304122633-21621-3-git-send-email-martin.peres-GANU6spQydw@public.gmane.org>
2011-05-06 23:42           ` Emil Velikov
2011-05-07  1:50             ` Martin Peres
     [not found]               ` <4DC4A56B.4080008-Iz16wY1oaNPLSKGbIzaifA@public.gmane.org>
2011-05-07  7:48                 ` Maxim Levitsky
  -- strict thread matches above, loose matches on Subject: below --
2011-04-27 23:58 [PATCH 0/2] reclocking stability improvements Martin Peres
     [not found] ` <1303948692-22455-1-git-send-email-martin.peres-GANU6spQydw@public.gmane.org>
2011-04-27 23:58   ` [PATCH 2/2] drm/nouveau/nv50: reclock memory using PMS on nv50 Martin Peres

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.