* Add pause/unpause methods for PFIFO & PGRAPH. Use them to get stable clock changes
@ 2010-09-29 11:25 Martin Peres
[not found] ` <4CA3223F.9030503-GANU6spQydw@public.gmane.org>
0 siblings, 1 reply; 7+ messages in thread
From: Martin Peres @ 2010-09-29 11:25 UTC (permalink / raw)
To: nouveau
[-- Attachment #1: Type: text/plain, Size: 283 bytes --]
Hi,
With these patches, I get reliable clock changes on my nv86. It should
work from nv50 to nvc0 (non-included).
Now, we need to wait for vblank. This is on my TODO but if someone knows
how to do so, please send me pointers to it or implement it yourself :)
Regards,
Martin
[-- Attachment #2: 0001-Add-pause-unpause-methods-to-the-PFIFO-engine.patch --]
[-- Type: text/x-patch, Size: 6897 bytes --]
From a219259d5ea46fb18f8e36c6c5a2f9e9e63fe53e Mon Sep 17 00:00:00 2001
From: Martin Peres <martin.peres-Iz16wY1oaNPLSKGbIzaifA@public.gmane.org>
Date: Wed, 29 Sep 2010 14:56:37 +0200
Subject: [PATCH 1/3] Add pause/unpause methods to the PFIFO engine
Signed-off-by: Martin Peres <martin.peres-Iz16wY1oaNPLSKGbIzaifA@public.gmane.org>
---
drivers/gpu/drm/nouveau/nouveau_drv.h | 8 ++++++++
drivers/gpu/drm/nouveau/nouveau_reg.h | 1 +
drivers/gpu/drm/nouveau/nouveau_state.c | 14 ++++++++++++++
drivers/gpu/drm/nouveau/nv50_fifo.c | 18 ++++++++++++++++++
4 files changed, 41 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h
index 591254e..c256c0a 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drv.h
+++ b/drivers/gpu/drm/nouveau/nouveau_drv.h
@@ -304,6 +304,9 @@ struct nouveau_fifo_engine {
void (*destroy_context)(struct nouveau_channel *);
int (*load_context)(struct nouveau_channel *);
int (*unload_context)(struct drm_device *);
+
+ int (*pause)(struct drm_device *);
+ int (*unpause)(struct drm_device *);
};
struct nouveau_pgraph_object_method {
@@ -339,6 +342,9 @@ struct nouveau_pgraph_engine {
void (*set_region_tiling)(struct drm_device *dev, int i, uint32_t addr,
uint32_t size, uint32_t pitch);
+
+ int (*pause)(struct drm_device *);
+ int (*unpause)(struct drm_device *);
};
struct nouveau_display_engine {
@@ -1036,6 +1042,8 @@ extern int nv50_fifo_create_context(struct nouveau_channel *);
extern void nv50_fifo_destroy_context(struct nouveau_channel *);
extern int nv50_fifo_load_context(struct nouveau_channel *);
extern int nv50_fifo_unload_context(struct drm_device *);
+extern int nv50_fifo_pause(struct drm_device *);
+extern int nv50_fifo_unpause(struct drm_device *);
/* nvc0_fifo.c */
extern int nvc0_fifo_init(struct drm_device *);
diff --git a/drivers/gpu/drm/nouveau/nouveau_reg.h b/drivers/gpu/drm/nouveau/nouveau_reg.h
index 1b42541..ee6dae1 100644
--- a/drivers/gpu/drm/nouveau/nouveau_reg.h
+++ b/drivers/gpu/drm/nouveau/nouveau_reg.h
@@ -701,6 +701,7 @@
#define NV50_PGRAPH 0x00400000
#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 75bce91..cfc34f5 100644
--- a/drivers/gpu/drm/nouveau/nouveau_state.c
+++ b/drivers/gpu/drm/nouveau/nouveau_state.c
@@ -86,6 +86,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
engine->fifo.destroy_context = nv04_fifo_destroy_context;
engine->fifo.load_context = nv04_fifo_load_context;
engine->fifo.unload_context = nv04_fifo_unload_context;
+ engine->fifo.pause = NULL;
+ engine->fifo.unpause = NULL;
engine->display.early_init = nv04_display_early_init;
engine->display.late_takedown = nv04_display_late_takedown;
engine->display.create = nv04_display_create;
@@ -140,6 +142,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
engine->fifo.destroy_context = nv10_fifo_destroy_context;
engine->fifo.load_context = nv10_fifo_load_context;
engine->fifo.unload_context = nv10_fifo_unload_context;
+ engine->fifo.pause = NULL;
+ engine->fifo.unpause = NULL;
engine->display.early_init = nv04_display_early_init;
engine->display.late_takedown = nv04_display_late_takedown;
engine->display.create = nv04_display_create;
@@ -194,6 +198,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
engine->fifo.destroy_context = nv10_fifo_destroy_context;
engine->fifo.load_context = nv10_fifo_load_context;
engine->fifo.unload_context = nv10_fifo_unload_context;
+ engine->fifo.pause = NULL;
+ engine->fifo.unpause = NULL;
engine->display.early_init = nv04_display_early_init;
engine->display.late_takedown = nv04_display_late_takedown;
engine->display.create = nv04_display_create;
@@ -248,6 +254,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
engine->fifo.destroy_context = nv10_fifo_destroy_context;
engine->fifo.load_context = nv10_fifo_load_context;
engine->fifo.unload_context = nv10_fifo_unload_context;
+ engine->fifo.pause = NULL;
+ engine->fifo.unpause = NULL;
engine->display.early_init = nv04_display_early_init;
engine->display.late_takedown = nv04_display_late_takedown;
engine->display.create = nv04_display_create;
@@ -305,6 +313,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
engine->fifo.destroy_context = nv40_fifo_destroy_context;
engine->fifo.load_context = nv40_fifo_load_context;
engine->fifo.unload_context = nv40_fifo_unload_context;
+ engine->fifo.pause = NULL;
+ engine->fifo.unpause = NULL;
engine->display.early_init = nv04_display_early_init;
engine->display.late_takedown = nv04_display_late_takedown;
engine->display.create = nv04_display_create;
@@ -365,6 +375,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
engine->fifo.destroy_context = nv50_fifo_destroy_context;
engine->fifo.load_context = nv50_fifo_load_context;
engine->fifo.unload_context = nv50_fifo_unload_context;
+ engine->fifo.pause = nv50_fifo_pause;
+ engine->fifo.unpause = nv50_fifo_unpause;
engine->display.early_init = nv50_display_early_init;
engine->display.late_takedown = nv50_display_late_takedown;
engine->display.create = nv50_display_create;
@@ -434,6 +446,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
engine->fifo.destroy_context = nvc0_fifo_destroy_context;
engine->fifo.load_context = nvc0_fifo_load_context;
engine->fifo.unload_context = nvc0_fifo_unload_context;
+ engine->fifo.pause = NULL;
+ engine->fifo.unpause = NULL;
engine->display.early_init = nv50_display_early_init;
engine->display.late_takedown = nv50_display_late_takedown;
engine->display.create = nv50_display_create;
diff --git a/drivers/gpu/drm/nouveau/nv50_fifo.c b/drivers/gpu/drm/nouveau/nv50_fifo.c
index a46a961..dd746eb 100644
--- a/drivers/gpu/drm/nouveau/nv50_fifo.c
+++ b/drivers/gpu/drm/nouveau/nv50_fifo.c
@@ -464,3 +464,21 @@ nv50_fifo_unload_context(struct drm_device *dev)
return 0;
}
+int
+nv50_fifo_pause(struct drm_device *dev)
+{
+ nv_wr32(dev, NV50_PFIFO_FREEZE, 1);
+ if (!nouveau_wait_until(dev, 2000000000ULL, NV50_PFIFO_FREEZE,
+ 0x10, 0x10)) {
+ NV_ERROR(dev, "PFIFO freeze fail!\n");
+ return -EIO;
+ }
+ return 0;
+}
+
+int
+nv50_fifo_unpause(struct drm_device *dev)
+{
+ nv_wr32(dev, NV50_PFIFO_FREEZE, 0);
+ return 0;
+}
--
1.7.2
[-- Attachment #3: 0002-Add-pause-unpause-methods-to-the-PGRAPH-engine.patch --]
[-- Type: text/x-patch, Size: 6951 bytes --]
From beaaa41076b6e9ceed9840caf0293de3dd5ec34b Mon Sep 17 00:00:00 2001
From: Martin Peres <martin.peres-Iz16wY1oaNPLSKGbIzaifA@public.gmane.org>
Date: Wed, 29 Sep 2010 15:11:12 +0200
Subject: [PATCH 2/3] Add pause/unpause methods to the PGRAPH engine
Signed-off-by: Martin Peres <martin.peres-Iz16wY1oaNPLSKGbIzaifA@public.gmane.org>
---
drivers/gpu/drm/nouveau/nouveau_drv.h | 2 +
drivers/gpu/drm/nouveau/nouveau_reg.h | 2 +
drivers/gpu/drm/nouveau/nouveau_state.c | 14 +++++++++++
drivers/gpu/drm/nouveau/nv50_graph.c | 39 +++++++++++++++++++++++++++++++
4 files changed, 57 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h
index c256c0a..bed57d0 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drv.h
+++ b/drivers/gpu/drm/nouveau/nouveau_drv.h
@@ -1121,6 +1121,8 @@ extern int nv50_graph_load_context(struct nouveau_channel *);
extern int nv50_graph_unload_context(struct drm_device *);
extern void nv50_graph_context_switch(struct drm_device *);
extern int nv50_grctx_init(struct nouveau_grctx *);
+extern int nv50_graph_pause(struct drm_device *dev);
+extern int nv50_graph_unpause(struct drm_device *dev);
/* nvc0_graph.c */
extern int nvc0_graph_init(struct drm_device *);
diff --git a/drivers/gpu/drm/nouveau/nouveau_reg.h b/drivers/gpu/drm/nouveau/nouveau_reg.h
index ee6dae1..346b77a 100644
--- a/drivers/gpu/drm/nouveau/nouveau_reg.h
+++ b/drivers/gpu/drm/nouveau/nouveau_reg.h
@@ -699,6 +699,8 @@
#define NV50_PROM__ESIZE 0x10000
#define NV50_PGRAPH 0x00400000
+#define NV50_PGRAPH_CONTROL 0x00400500
+#define NV50_PGRAPH_STATUS 0x00400700
#define NV50_PGRAPH__LEN 0x1
#define NV50_PGRAPH__ESIZE 0x10000
#define NV50_PFIFO_FREEZE 0x2504
diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c
index cfc34f5..fc5fb46 100644
--- a/drivers/gpu/drm/nouveau/nouveau_state.c
+++ b/drivers/gpu/drm/nouveau/nouveau_state.c
@@ -74,6 +74,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
engine->graph.destroy_context = nv04_graph_destroy_context;
engine->graph.load_context = nv04_graph_load_context;
engine->graph.unload_context = nv04_graph_unload_context;
+ engine->graph.pause = NULL;
+ engine->graph.unpause = NULL;
engine->fifo.channels = 16;
engine->fifo.init = nv04_fifo_init;
engine->fifo.takedown = nouveau_stub_takedown;
@@ -130,6 +132,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
engine->graph.load_context = nv10_graph_load_context;
engine->graph.unload_context = nv10_graph_unload_context;
engine->graph.set_region_tiling = nv10_graph_set_region_tiling;
+ engine->graph.pause = NULL;
+ engine->graph.unpause = NULL;
engine->fifo.channels = 32;
engine->fifo.init = nv10_fifo_init;
engine->fifo.takedown = nouveau_stub_takedown;
@@ -186,6 +190,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
engine->graph.load_context = nv20_graph_load_context;
engine->graph.unload_context = nv20_graph_unload_context;
engine->graph.set_region_tiling = nv20_graph_set_region_tiling;
+ engine->graph.pause = NULL;
+ engine->graph.unpause = NULL;
engine->fifo.channels = 32;
engine->fifo.init = nv10_fifo_init;
engine->fifo.takedown = nouveau_stub_takedown;
@@ -242,6 +248,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
engine->graph.load_context = nv20_graph_load_context;
engine->graph.unload_context = nv20_graph_unload_context;
engine->graph.set_region_tiling = nv20_graph_set_region_tiling;
+ engine->graph.pause = NULL;
+ engine->graph.unpause = NULL;
engine->fifo.channels = 32;
engine->fifo.init = nv10_fifo_init;
engine->fifo.takedown = nouveau_stub_takedown;
@@ -301,6 +309,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
engine->graph.load_context = nv40_graph_load_context;
engine->graph.unload_context = nv40_graph_unload_context;
engine->graph.set_region_tiling = nv40_graph_set_region_tiling;
+ engine->graph.pause = NULL;
+ engine->graph.unpause = NULL;
engine->fifo.channels = 32;
engine->fifo.init = nv40_fifo_init;
engine->fifo.takedown = nouveau_stub_takedown;
@@ -364,6 +374,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
engine->graph.destroy_context = nv50_graph_destroy_context;
engine->graph.load_context = nv50_graph_load_context;
engine->graph.unload_context = nv50_graph_unload_context;
+ engine->graph.pause = nv50_graph_pause;
+ engine->graph.unpause = nv50_graph_unpause;
engine->fifo.channels = 128;
engine->fifo.init = nv50_fifo_init;
engine->fifo.takedown = nv50_fifo_takedown;
@@ -435,6 +447,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
engine->graph.destroy_context = nvc0_graph_destroy_context;
engine->graph.load_context = nvc0_graph_load_context;
engine->graph.unload_context = nvc0_graph_unload_context;
+ engine->graph.pause = NULL;
+ engine->graph.unpause = NULL;
engine->fifo.channels = 128;
engine->fifo.init = nvc0_fifo_init;
engine->fifo.takedown = nvc0_fifo_takedown;
diff --git a/drivers/gpu/drm/nouveau/nv50_graph.c b/drivers/gpu/drm/nouveau/nv50_graph.c
index cbf5ae2..a32cb2d 100644
--- a/drivers/gpu/drm/nouveau/nv50_graph.c
+++ b/drivers/gpu/drm/nouveau/nv50_graph.c
@@ -381,6 +381,45 @@ nv50_graph_nvsw_vblsem_release(struct nouveau_channel *chan, int grclass,
return 0;
}
+
+
+int
+nv50_graph_pause(struct drm_device *dev)
+{
+ uint64_t start;
+ /* initial guess... */
+ uint32_t mask380 = 0xffffffff;
+ uint32_t mask384 = 0xffffffff;
+ uint32_t mask388 = 0xffffffff;
+ uint32_t mask700 = 0xffffffff;
+
+ nv_wr32(dev, NV50_PGRAPH_CONTROL, 0);
+ start = nv04_timer_read(dev);
+ while ((nv_rd32(dev, 0x400380) & mask380) ||
+ (nv_rd32(dev, 0x400384) & mask384) ||
+ (nv_rd32(dev, 0x400388) & mask388) ||
+ (nv_rd32(dev, NV50_PGRAPH_STATUS) & mask700)) {
+ if (nv04_timer_read(dev) - start >= 2000000000) {
+ /* 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!\n",
+ nv_rd32(dev, 0x400380), nv_rd32(dev, 0x400384),
+ nv_rd32(dev, 0x400388), nv_rd32(dev, NV50_PGRAPH_STATUS));
+
+ nv50_graph_unpause(dev);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+int
+nv50_graph_unpause(struct drm_device *dev)
+{
+ nv_wr32(dev, NV50_PGRAPH_CONTROL, 0x10001);
+ return 0;
+}
+
static struct nouveau_pgraph_object_method nv50_graph_nvsw_methods[] = {
{ 0x018c, nv50_graph_nvsw_dma_vblsem },
{ 0x0400, nv50_graph_nvsw_vblsem_offset },
--
1.7.2
[-- Attachment #4: 0003-Idle-PGRAPH-and-PFIFO-before-changing-the-clocks.patch --]
[-- Type: text/x-patch, Size: 1278 bytes --]
From 75cf222f354600b307000c5f23d4d52a202d2a0b Mon Sep 17 00:00:00 2001
From: Martin Peres <martin.peres-Iz16wY1oaNPLSKGbIzaifA@public.gmane.org>
Date: Wed, 29 Sep 2010 15:16:22 +0200
Subject: [PATCH 3/3] Idle PGRAPH and PFIFO before changing the clocks
Signed-off-by: Martin Peres <martin.peres-Iz16wY1oaNPLSKGbIzaifA@public.gmane.org>
---
drivers/gpu/drm/nouveau/nouveau_pm.c | 8 ++++++++
1 files changed, 8 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.c b/drivers/gpu/drm/nouveau/nouveau_pm.c
index 1c99c55..ecfad1c 100644
--- a/drivers/gpu/drm/nouveau/nouveau_pm.c
+++ b/drivers/gpu/drm/nouveau/nouveau_pm.c
@@ -68,11 +68,19 @@ nouveau_pm_perflvl_set(struct drm_device *dev, struct nouveau_pm_level *perflvl)
}
}
+ dev_priv->engine.fifo.pause(dev);
+ dev_priv->engine.graph.pause(dev);
+
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);
+ /* assume the PLLs have stabilized already */
+
+ dev_priv->engine.fifo.unpause(dev);
+ dev_priv->engine.graph.unpause(dev);
+
pm->cur = perflvl;
return 0;
}
--
1.7.2
[-- 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 related [flat|nested] 7+ messages in thread[parent not found: <4CA3223F.9030503-GANU6spQydw@public.gmane.org>]
* Re: Add pause/unpause methods for PFIFO & PGRAPH. Use them to get stable clock changes [not found] ` <4CA3223F.9030503-GANU6spQydw@public.gmane.org> @ 2010-09-30 17:14 ` Martin Peres [not found] ` <4CA4C57E.5090400-GANU6spQydw@public.gmane.org> 0 siblings, 1 reply; 7+ messages in thread From: Martin Peres @ 2010-09-30 17:14 UTC (permalink / raw) To: nouveau-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW [-- Attachment #1: Type: text/plain, Size: 915 bytes --] Hi, Here is an updated set of patches (in regards on comments got on IRC and an handling error when using several channels). These updated patch allow me to run 5 glxgears at the same time and reclock the card almost 100 times per second without crashing. Don't panic if you get theses in your logs: PGRAPH: wait for idle fail: 00000000 00000000 00000000 00000103! PGRAPH: PGRAPH paused while running a ctxprog, NV40_PGRAPH_CTXCTL_0310 = 0x11 It means that we tried to reclock during a context switch. It would be safe to continue as when the program is at 0x11, it means it waits for another process (which is stopped because we stopped PFIFO before). I could allow to get this value (ie continue and clock), but if someone changes the ctxprog, he would have to update it here. For the moment, I don't mind if from time to time, setting the clocks fail. Waiting for your comments. Regards, Martin [-- Attachment #2: 0001-Add-pause-unpause-methods-to-the-PFIFO-engine.patch --] [-- Type: text/x-patch, Size: 6814 bytes --] From a219259d5ea46fb18f8e36c6c5a2f9e9e63fe53e Mon Sep 17 00:00:00 2001 From: Martin Peres <martin.peres-Iz16wY1oaNPLSKGbIzaifA@public.gmane.org> Date: Wed, 29 Sep 2010 14:56:37 +0200 Subject: [PATCH 1/3] Add pause/unpause methods to the PFIFO engine --- drivers/gpu/drm/nouveau/nouveau_drv.h | 8 ++++++++ drivers/gpu/drm/nouveau/nouveau_reg.h | 1 + drivers/gpu/drm/nouveau/nouveau_state.c | 14 ++++++++++++++ drivers/gpu/drm/nouveau/nv50_fifo.c | 18 ++++++++++++++++++ 4 files changed, 41 insertions(+), 0 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index 591254e..c256c0a 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -304,6 +304,9 @@ struct nouveau_fifo_engine { void (*destroy_context)(struct nouveau_channel *); int (*load_context)(struct nouveau_channel *); int (*unload_context)(struct drm_device *); + + int (*pause)(struct drm_device *); + int (*unpause)(struct drm_device *); }; struct nouveau_pgraph_object_method { @@ -339,6 +342,9 @@ struct nouveau_pgraph_engine { void (*set_region_tiling)(struct drm_device *dev, int i, uint32_t addr, uint32_t size, uint32_t pitch); + + int (*pause)(struct drm_device *); + int (*unpause)(struct drm_device *); }; struct nouveau_display_engine { @@ -1036,6 +1042,8 @@ extern int nv50_fifo_create_context(struct nouveau_channel *); extern void nv50_fifo_destroy_context(struct nouveau_channel *); extern int nv50_fifo_load_context(struct nouveau_channel *); extern int nv50_fifo_unload_context(struct drm_device *); +extern int nv50_fifo_pause(struct drm_device *); +extern int nv50_fifo_unpause(struct drm_device *); /* nvc0_fifo.c */ extern int nvc0_fifo_init(struct drm_device *); diff --git a/drivers/gpu/drm/nouveau/nouveau_reg.h b/drivers/gpu/drm/nouveau/nouveau_reg.h index 1b42541..ee6dae1 100644 --- a/drivers/gpu/drm/nouveau/nouveau_reg.h +++ b/drivers/gpu/drm/nouveau/nouveau_reg.h @@ -701,6 +701,7 @@ #define NV50_PGRAPH 0x00400000 #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 75bce91..cfc34f5 100644 --- a/drivers/gpu/drm/nouveau/nouveau_state.c +++ b/drivers/gpu/drm/nouveau/nouveau_state.c @@ -86,6 +86,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->fifo.destroy_context = nv04_fifo_destroy_context; engine->fifo.load_context = nv04_fifo_load_context; engine->fifo.unload_context = nv04_fifo_unload_context; + engine->fifo.pause = NULL; + engine->fifo.unpause = NULL; engine->display.early_init = nv04_display_early_init; engine->display.late_takedown = nv04_display_late_takedown; engine->display.create = nv04_display_create; @@ -140,6 +142,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->fifo.destroy_context = nv10_fifo_destroy_context; engine->fifo.load_context = nv10_fifo_load_context; engine->fifo.unload_context = nv10_fifo_unload_context; + engine->fifo.pause = NULL; + engine->fifo.unpause = NULL; engine->display.early_init = nv04_display_early_init; engine->display.late_takedown = nv04_display_late_takedown; engine->display.create = nv04_display_create; @@ -194,6 +198,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->fifo.destroy_context = nv10_fifo_destroy_context; engine->fifo.load_context = nv10_fifo_load_context; engine->fifo.unload_context = nv10_fifo_unload_context; + engine->fifo.pause = NULL; + engine->fifo.unpause = NULL; engine->display.early_init = nv04_display_early_init; engine->display.late_takedown = nv04_display_late_takedown; engine->display.create = nv04_display_create; @@ -248,6 +254,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->fifo.destroy_context = nv10_fifo_destroy_context; engine->fifo.load_context = nv10_fifo_load_context; engine->fifo.unload_context = nv10_fifo_unload_context; + engine->fifo.pause = NULL; + engine->fifo.unpause = NULL; engine->display.early_init = nv04_display_early_init; engine->display.late_takedown = nv04_display_late_takedown; engine->display.create = nv04_display_create; @@ -305,6 +313,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->fifo.destroy_context = nv40_fifo_destroy_context; engine->fifo.load_context = nv40_fifo_load_context; engine->fifo.unload_context = nv40_fifo_unload_context; + engine->fifo.pause = NULL; + engine->fifo.unpause = NULL; engine->display.early_init = nv04_display_early_init; engine->display.late_takedown = nv04_display_late_takedown; engine->display.create = nv04_display_create; @@ -365,6 +375,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->fifo.destroy_context = nv50_fifo_destroy_context; engine->fifo.load_context = nv50_fifo_load_context; engine->fifo.unload_context = nv50_fifo_unload_context; + engine->fifo.pause = nv50_fifo_pause; + engine->fifo.unpause = nv50_fifo_unpause; engine->display.early_init = nv50_display_early_init; engine->display.late_takedown = nv50_display_late_takedown; engine->display.create = nv50_display_create; @@ -434,6 +446,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->fifo.destroy_context = nvc0_fifo_destroy_context; engine->fifo.load_context = nvc0_fifo_load_context; engine->fifo.unload_context = nvc0_fifo_unload_context; + engine->fifo.pause = NULL; + engine->fifo.unpause = NULL; engine->display.early_init = nv50_display_early_init; engine->display.late_takedown = nv50_display_late_takedown; engine->display.create = nv50_display_create; diff --git a/drivers/gpu/drm/nouveau/nv50_fifo.c b/drivers/gpu/drm/nouveau/nv50_fifo.c index a46a961..dd746eb 100644 --- a/drivers/gpu/drm/nouveau/nv50_fifo.c +++ b/drivers/gpu/drm/nouveau/nv50_fifo.c @@ -464,3 +464,21 @@ nv50_fifo_unload_context(struct drm_device *dev) return 0; } +int +nv50_fifo_pause(struct drm_device *dev) +{ + nv_wr32(dev, NV50_PFIFO_FREEZE, 1); + if (!nouveau_wait_until(dev, 2000000000ULL, NV50_PFIFO_FREEZE, + 0x10, 0x10)) { + NV_ERROR(dev, "PFIFO freeze fail!\n"); + return -EIO; + } + return 0; +} + +int +nv50_fifo_unpause(struct drm_device *dev) +{ + nv_wr32(dev, NV50_PFIFO_FREEZE, 0); + return 0; +} -- 1.7.2 [-- Attachment #3: 0002-Add-pause-unpause-methods-to-the-PGRAPH-engine-v2.patch --] [-- Type: text/x-patch, Size: 7289 bytes --] From c9392831ac4b3907322fb35c742ba5509d8cf0a1 Mon Sep 17 00:00:00 2001 From: Martin Peres <martin.peres-Iz16wY1oaNPLSKGbIzaifA@public.gmane.org> Date: Thu, 30 Sep 2010 20:43:21 +0200 Subject: [PATCH 2/3] Add pause/unpause methods to the PGRAPH engine v2 Signed-off-by: Martin Peres <martin.peres-Iz16wY1oaNPLSKGbIzaifA@public.gmane.org> --- drivers/gpu/drm/nouveau/nouveau_drv.h | 2 + drivers/gpu/drm/nouveau/nouveau_reg.h | 2 + drivers/gpu/drm/nouveau/nouveau_state.c | 14 +++++++++ drivers/gpu/drm/nouveau/nv50_graph.c | 48 +++++++++++++++++++++++++++++++ 4 files changed, 66 insertions(+), 0 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index c256c0a..bed57d0 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -1121,6 +1121,8 @@ extern int nv50_graph_load_context(struct nouveau_channel *); extern int nv50_graph_unload_context(struct drm_device *); extern void nv50_graph_context_switch(struct drm_device *); extern int nv50_grctx_init(struct nouveau_grctx *); +extern int nv50_graph_pause(struct drm_device *dev); +extern int nv50_graph_unpause(struct drm_device *dev); /* nvc0_graph.c */ extern int nvc0_graph_init(struct drm_device *); diff --git a/drivers/gpu/drm/nouveau/nouveau_reg.h b/drivers/gpu/drm/nouveau/nouveau_reg.h index ee6dae1..346b77a 100644 --- a/drivers/gpu/drm/nouveau/nouveau_reg.h +++ b/drivers/gpu/drm/nouveau/nouveau_reg.h @@ -699,6 +699,8 @@ #define NV50_PROM__ESIZE 0x10000 #define NV50_PGRAPH 0x00400000 +#define NV50_PGRAPH_CONTROL 0x00400500 +#define NV50_PGRAPH_STATUS 0x00400700 #define NV50_PGRAPH__LEN 0x1 #define NV50_PGRAPH__ESIZE 0x10000 #define NV50_PFIFO_FREEZE 0x2504 diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c index cfc34f5..fc5fb46 100644 --- a/drivers/gpu/drm/nouveau/nouveau_state.c +++ b/drivers/gpu/drm/nouveau/nouveau_state.c @@ -74,6 +74,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->graph.destroy_context = nv04_graph_destroy_context; engine->graph.load_context = nv04_graph_load_context; engine->graph.unload_context = nv04_graph_unload_context; + engine->graph.pause = NULL; + engine->graph.unpause = NULL; engine->fifo.channels = 16; engine->fifo.init = nv04_fifo_init; engine->fifo.takedown = nouveau_stub_takedown; @@ -130,6 +132,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->graph.load_context = nv10_graph_load_context; engine->graph.unload_context = nv10_graph_unload_context; engine->graph.set_region_tiling = nv10_graph_set_region_tiling; + engine->graph.pause = NULL; + engine->graph.unpause = NULL; engine->fifo.channels = 32; engine->fifo.init = nv10_fifo_init; engine->fifo.takedown = nouveau_stub_takedown; @@ -186,6 +190,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->graph.load_context = nv20_graph_load_context; engine->graph.unload_context = nv20_graph_unload_context; engine->graph.set_region_tiling = nv20_graph_set_region_tiling; + engine->graph.pause = NULL; + engine->graph.unpause = NULL; engine->fifo.channels = 32; engine->fifo.init = nv10_fifo_init; engine->fifo.takedown = nouveau_stub_takedown; @@ -242,6 +248,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->graph.load_context = nv20_graph_load_context; engine->graph.unload_context = nv20_graph_unload_context; engine->graph.set_region_tiling = nv20_graph_set_region_tiling; + engine->graph.pause = NULL; + engine->graph.unpause = NULL; engine->fifo.channels = 32; engine->fifo.init = nv10_fifo_init; engine->fifo.takedown = nouveau_stub_takedown; @@ -301,6 +309,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->graph.load_context = nv40_graph_load_context; engine->graph.unload_context = nv40_graph_unload_context; engine->graph.set_region_tiling = nv40_graph_set_region_tiling; + engine->graph.pause = NULL; + engine->graph.unpause = NULL; engine->fifo.channels = 32; engine->fifo.init = nv40_fifo_init; engine->fifo.takedown = nouveau_stub_takedown; @@ -364,6 +374,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->graph.destroy_context = nv50_graph_destroy_context; engine->graph.load_context = nv50_graph_load_context; engine->graph.unload_context = nv50_graph_unload_context; + engine->graph.pause = nv50_graph_pause; + engine->graph.unpause = nv50_graph_unpause; engine->fifo.channels = 128; engine->fifo.init = nv50_fifo_init; engine->fifo.takedown = nv50_fifo_takedown; @@ -435,6 +447,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->graph.destroy_context = nvc0_graph_destroy_context; engine->graph.load_context = nvc0_graph_load_context; engine->graph.unload_context = nvc0_graph_unload_context; + engine->graph.pause = NULL; + engine->graph.unpause = NULL; engine->fifo.channels = 128; engine->fifo.init = nvc0_fifo_init; engine->fifo.takedown = nvc0_fifo_takedown; diff --git a/drivers/gpu/drm/nouveau/nv50_graph.c b/drivers/gpu/drm/nouveau/nv50_graph.c index cbf5ae2..252b432 100644 --- a/drivers/gpu/drm/nouveau/nv50_graph.c +++ b/drivers/gpu/drm/nouveau/nv50_graph.c @@ -381,6 +381,54 @@ nv50_graph_nvsw_vblsem_release(struct nouveau_channel *chan, int grclass, return 0; } + + +int +nv50_graph_pause(struct drm_device *dev) +{ + uint64_t start; + /* initial guess... */ + uint32_t mask380 = 0xffffffff; + uint32_t mask384 = 0xffffffff; + uint32_t mask388 = 0xffffffff; + uint32_t mask700 = 0x00000001; + + start = nv04_timer_read(dev); + nv_wr32(dev, NV50_PGRAPH_CONTROL, 0x10000); + while ((nv_rd32(dev, 0x400380) & mask380) || + (nv_rd32(dev, 0x400384) & mask384) || + (nv_rd32(dev, 0x400388) & mask388) || + (nv_rd32(dev, NV50_PGRAPH_STATUS) & mask700)) { + if (nv04_timer_read(dev) - start >= 50000000) { + /* 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!\n", + nv_rd32(dev, 0x400380), nv_rd32(dev, 0x400384), + nv_rd32(dev, 0x400388), nv_rd32(dev, NV50_PGRAPH_STATUS)); + + if (nv_rd32(dev, NV50_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)); + + nv50_graph_unpause(dev); + return -EIO; + } + + /* After a 1ms wait, do not use all the */ + if (nv04_timer_read(dev) - start >= 1000000) + schedule(); + } + return 0; +} + +int +nv50_graph_unpause(struct drm_device *dev) +{ + nv_wr32(dev, NV50_PGRAPH_CONTROL, 0x10001); + return 0; +} + static struct nouveau_pgraph_object_method nv50_graph_nvsw_methods[] = { { 0x018c, nv50_graph_nvsw_dma_vblsem }, { 0x0400, nv50_graph_nvsw_vblsem_offset }, -- 1.7.2 [-- Attachment #4: 0003-Idle-PGRAPH-and-PFIFO-before-changing-the-clocks-v2.patch --] [-- Type: text/x-patch, Size: 1580 bytes --] From 6f442a081ef358332f1e155a3c16f1f7c6152c21 Mon Sep 17 00:00:00 2001 From: Martin Peres <martin.peres-Iz16wY1oaNPLSKGbIzaifA@public.gmane.org> Date: Thu, 30 Sep 2010 20:57:48 +0200 Subject: [PATCH 3/3] Idle PGRAPH and PFIFO before changing the clocks v2 Signed-off-by: Martin Peres <martin.peres-Iz16wY1oaNPLSKGbIzaifA@public.gmane.org> --- drivers/gpu/drm/nouveau/nouveau_pm.c | 19 +++++++++++++++++++ 1 files changed, 19 insertions(+), 0 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.c b/drivers/gpu/drm/nouveau/nouveau_pm.c index 1c99c55..fb3681c 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.c +++ b/drivers/gpu/drm/nouveau/nouveau_pm.c @@ -68,11 +68,30 @@ nouveau_pm_perflvl_set(struct drm_device *dev, struct nouveau_pm_level *perflvl) } } + /* Pause the engines, if possible */ + if (dev_priv->engine.fifo.pause) { + if (dev_priv->engine.fifo.pause(dev)) + return -EIO; + } + if (dev_priv->engine.graph.pause) { + if (dev_priv->engine.graph.pause(dev)) + return -EIO; + } + 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 */ + udelay(100); + + /* Un-pause the engines, if possible */ + if (dev_priv->engine.fifo.unpause) + dev_priv->engine.fifo.unpause(dev); + if (dev_priv->engine.graph.unpause) + dev_priv->engine.graph.unpause(dev); + pm->cur = perflvl; return 0; } -- 1.7.2 [-- 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 related [flat|nested] 7+ messages in thread
[parent not found: <4CA4C57E.5090400-GANU6spQydw@public.gmane.org>]
* Re: Add pause/unpause methods for PFIFO & PGRAPH. Use them to get stable clock changes [not found] ` <4CA4C57E.5090400-GANU6spQydw@public.gmane.org> @ 2010-10-07 1:33 ` Martin Peres [not found] ` <4CAD2383.50907-GANU6spQydw@public.gmane.org> 0 siblings, 1 reply; 7+ messages in thread From: Martin Peres @ 2010-10-07 1:33 UTC (permalink / raw) To: nouveau-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW Hi, Here is an updated version, all in one patch. When we agree on the code, I'll split it into 3 patches. The new code is way better. As suggested on IRC, I also disable IRQs and PFIFO cache pull and push. Please have a look and test it. This patch needs to get upstream as it gives a reliable way to change the clock even in the middle of a game. It is not perfect yet, changing the clocks too often (like several thousand of times) lead to an hung card :) Regards, Martin ^ permalink raw reply [flat|nested] 7+ messages in thread
[parent not found: <4CAD2383.50907-GANU6spQydw@public.gmane.org>]
* Re: Add pause/unpause methods for PFIFO & PGRAPH. Use them to get stable clock changes [not found] ` <4CAD2383.50907-GANU6spQydw@public.gmane.org> @ 2010-10-07 15:38 ` Martin Peres 2010-10-07 15:38 ` Martin Peres 1 sibling, 0 replies; 7+ messages in thread From: Martin Peres @ 2010-10-07 15:38 UTC (permalink / raw) To: nouveau-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW [-- Attachment #1: Type: text/plain, Size: 214 bytes --] Le 07/10/2010 03:33, Martin Peres a écrit : > Hi, > > Here is an updated version, all in one patch. When we agree on the > code, I'll split it into 3 patches. > Sorry, I forgot to attach it. Here it is. [-- Attachment #2: 0001-Pause-the-card-before-reclocking.patch --] [-- Type: text/x-patch, Size: 15996 bytes --] From 86e7dd89810b37a12ae189633de41aacf07355cb Mon Sep 17 00:00:00 2001 From: Martin Peres <martin.peres-Iz16wY1oaNPLSKGbIzaifA@public.gmane.org> Date: Thu, 7 Oct 2010 05:20:38 +0200 Subject: [PATCH] Pause the card before reclocking Signed-off-by: Martin Peres <martin.peres-Iz16wY1oaNPLSKGbIzaifA@public.gmane.org> --- drivers/gpu/drm/nouveau/nouveau_drv.h | 10 ++++++ drivers/gpu/drm/nouveau/nouveau_pm.c | 50 +++++++++++++++++++++++++++++- drivers/gpu/drm/nouveau/nouveau_reg.h | 3 ++ drivers/gpu/drm/nouveau/nouveau_state.c | 35 +++++++++++++++++++++- drivers/gpu/drm/nouveau/nv50_fifo.c | 17 ++++++++++ drivers/gpu/drm/nouveau/nv50_graph.c | 48 +++++++++++++++++++++++++++++ 6 files changed, 160 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index 591254e..9317bc3 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -304,6 +304,9 @@ struct nouveau_fifo_engine { void (*destroy_context)(struct nouveau_channel *); int (*load_context)(struct nouveau_channel *); int (*unload_context)(struct drm_device *); + + int (*pause)(struct drm_device *); + void (*unpause)(struct drm_device *); }; struct nouveau_pgraph_object_method { @@ -339,6 +342,9 @@ struct nouveau_pgraph_engine { void (*set_region_tiling)(struct drm_device *dev, int i, uint32_t addr, uint32_t size, uint32_t pitch); + + int (*pause)(struct drm_device *); + void (*unpause)(struct drm_device *); }; struct nouveau_display_engine { @@ -1036,6 +1042,8 @@ extern int nv50_fifo_create_context(struct nouveau_channel *); extern void nv50_fifo_destroy_context(struct nouveau_channel *); extern int nv50_fifo_load_context(struct nouveau_channel *); extern int nv50_fifo_unload_context(struct drm_device *); +extern int nv50_fifo_pause(struct drm_device *); +extern void nv50_fifo_unpause(struct drm_device *); /* nvc0_fifo.c */ extern int nvc0_fifo_init(struct drm_device *); @@ -1113,6 +1121,8 @@ extern int nv50_graph_load_context(struct nouveau_channel *); extern int nv50_graph_unload_context(struct drm_device *); extern void nv50_graph_context_switch(struct drm_device *); extern int nv50_grctx_init(struct nouveau_grctx *); +extern int nv50_graph_pause(struct drm_device *dev); +extern void nv50_graph_unpause(struct drm_device *dev); /* nvc0_graph.c */ extern int nvc0_graph_init(struct drm_device *); diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.c b/drivers/gpu/drm/nouveau/nouveau_pm.c index 1c99c55..b546a4d 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.c +++ b/drivers/gpu/drm/nouveau/nouveau_pm.c @@ -55,6 +55,7 @@ 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; + uint32_t status; int ret; if (perflvl == pm->cur) @@ -68,13 +69,58 @@ nouveau_pm_perflvl_set(struct drm_device *dev, struct nouveau_pm_level *perflvl) } } + /* TODO: Wait for vblank */ + + /* Disable interrupts */ + nv_wr32(dev, 0x140, 0); + + /* Pause the engines, if possible */ + if (dev_priv->engine.fifo.pause(dev)) { + ret = -EIO; + goto out; + } + if (dev_priv->engine.graph.pause(dev)) { + ret = -EIO; + goto out; + } + + /* Disable the PFIFO cache pulling */ + status = nv_rd32(dev, 0x003250); + nv_wr32(dev, 0x003250, status&0xfffffffe); + + /* Disable the PFIFO cache dma push */ + status = nv_rd32(dev, 0x003220); + nv_wr32(dev, 0x003220, status&0xfffffffe); + + /* Change the clocks */ 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 */ + udelay(100); + pm->cur = perflvl; - return 0; + ret = 0; + +out: + /* Re-enable the PFIFO cache dma push */ + status = nv_rd32(dev, 0x003220); + nv_wr32(dev, 0x003220, status|0x1); + + /* Re-enable the PFIFO cache pulling */ + status = nv_rd32(dev, 0x003250); + nv_wr32(dev, 0x003250, status|0x1); + + /* Un-pause the engines */ + dev_priv->engine.fifo.unpause(dev); + dev_priv->engine.graph.unpause(dev); + + /* Re-enable interrupts */ + nv_wr32(dev, 0x140, 1); + + return ret; } static int @@ -108,7 +154,7 @@ nouveau_pm_profile_set(struct drm_device *dev, const char *profile) return -EINVAL; } - NV_INFO(dev, "setting performance level: %s\n", profile); + NV_INFO(dev, "setting performance level: %s", profile); return nouveau_pm_perflvl_set(dev, perflvl); } diff --git a/drivers/gpu/drm/nouveau/nouveau_reg.h b/drivers/gpu/drm/nouveau/nouveau_reg.h index 1b42541..346b77a 100644 --- a/drivers/gpu/drm/nouveau/nouveau_reg.h +++ b/drivers/gpu/drm/nouveau/nouveau_reg.h @@ -699,8 +699,11 @@ #define NV50_PROM__ESIZE 0x10000 #define NV50_PGRAPH 0x00400000 +#define NV50_PGRAPH_CONTROL 0x00400500 +#define NV50_PGRAPH_STATUS 0x00400700 #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 75bce91..704a4b2 100644 --- a/drivers/gpu/drm/nouveau/nouveau_state.c +++ b/drivers/gpu/drm/nouveau/nouveau_state.c @@ -42,6 +42,12 @@ static void nouveau_stub_takedown(struct drm_device *dev) {} static int nouveau_stub_init(struct drm_device *dev) { return 0; } +int nouveau_fifo_pause_dummy(struct drm_device *dev) { return 0; } +void nouveau_fifo_unpause_dummy(struct drm_device *dev) { } + +int nouveau_graph_pause_dummy(struct drm_device *dev) { return 0; } +void nouveau_graph_unpause_dummy(struct drm_device *dev) {} + static int nouveau_init_engine_ptrs(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; @@ -74,6 +80,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->graph.destroy_context = nv04_graph_destroy_context; engine->graph.load_context = nv04_graph_load_context; engine->graph.unload_context = nv04_graph_unload_context; + engine->graph.pause = nouveau_graph_pause_dummy; + engine->graph.unpause = nouveau_graph_unpause_dummy; engine->fifo.channels = 16; engine->fifo.init = nv04_fifo_init; engine->fifo.takedown = nouveau_stub_takedown; @@ -86,6 +94,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->fifo.destroy_context = nv04_fifo_destroy_context; engine->fifo.load_context = nv04_fifo_load_context; engine->fifo.unload_context = nv04_fifo_unload_context; + engine->fifo.pause = nouveau_fifo_pause_dummy; + engine->fifo.unpause = nouveau_fifo_unpause_dummy; engine->display.early_init = nv04_display_early_init; engine->display.late_takedown = nv04_display_late_takedown; engine->display.create = nv04_display_create; @@ -128,6 +138,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->graph.load_context = nv10_graph_load_context; engine->graph.unload_context = nv10_graph_unload_context; engine->graph.set_region_tiling = nv10_graph_set_region_tiling; + engine->graph.pause = nouveau_graph_pause_dummy; + engine->graph.unpause = nouveau_graph_unpause_dummy; engine->fifo.channels = 32; engine->fifo.init = nv10_fifo_init; engine->fifo.takedown = nouveau_stub_takedown; @@ -140,6 +152,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->fifo.destroy_context = nv10_fifo_destroy_context; engine->fifo.load_context = nv10_fifo_load_context; engine->fifo.unload_context = nv10_fifo_unload_context; + engine->fifo.pause = nouveau_fifo_pause_dummy; + engine->fifo.unpause = nouveau_fifo_unpause_dummy; engine->display.early_init = nv04_display_early_init; engine->display.late_takedown = nv04_display_late_takedown; engine->display.create = nv04_display_create; @@ -182,6 +196,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->graph.load_context = nv20_graph_load_context; engine->graph.unload_context = nv20_graph_unload_context; engine->graph.set_region_tiling = nv20_graph_set_region_tiling; + engine->graph.pause = nouveau_graph_pause_dummy; + engine->graph.unpause = nouveau_graph_unpause_dummy; engine->fifo.channels = 32; engine->fifo.init = nv10_fifo_init; engine->fifo.takedown = nouveau_stub_takedown; @@ -194,6 +210,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->fifo.destroy_context = nv10_fifo_destroy_context; engine->fifo.load_context = nv10_fifo_load_context; engine->fifo.unload_context = nv10_fifo_unload_context; + engine->fifo.pause = nouveau_fifo_pause_dummy; + engine->fifo.unpause = nouveau_fifo_unpause_dummy; engine->display.early_init = nv04_display_early_init; engine->display.late_takedown = nv04_display_late_takedown; engine->display.create = nv04_display_create; @@ -236,6 +254,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->graph.load_context = nv20_graph_load_context; engine->graph.unload_context = nv20_graph_unload_context; engine->graph.set_region_tiling = nv20_graph_set_region_tiling; + engine->graph.pause = nouveau_graph_pause_dummy; + engine->graph.unpause = nouveau_graph_unpause_dummy; engine->fifo.channels = 32; engine->fifo.init = nv10_fifo_init; engine->fifo.takedown = nouveau_stub_takedown; @@ -248,6 +268,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->fifo.destroy_context = nv10_fifo_destroy_context; engine->fifo.load_context = nv10_fifo_load_context; engine->fifo.unload_context = nv10_fifo_unload_context; + engine->fifo.pause = nouveau_fifo_pause_dummy; + engine->fifo.unpause = nouveau_fifo_unpause_dummy; engine->display.early_init = nv04_display_early_init; engine->display.late_takedown = nv04_display_late_takedown; engine->display.create = nv04_display_create; @@ -293,6 +315,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->graph.load_context = nv40_graph_load_context; engine->graph.unload_context = nv40_graph_unload_context; engine->graph.set_region_tiling = nv40_graph_set_region_tiling; + engine->graph.pause = nouveau_graph_pause_dummy; + engine->graph.unpause = nouveau_graph_unpause_dummy; engine->fifo.channels = 32; engine->fifo.init = nv40_fifo_init; engine->fifo.takedown = nouveau_stub_takedown; @@ -305,6 +329,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->fifo.destroy_context = nv40_fifo_destroy_context; engine->fifo.load_context = nv40_fifo_load_context; engine->fifo.unload_context = nv40_fifo_unload_context; + engine->fifo.pause = nouveau_fifo_pause_dummy; + engine->fifo.unpause = nouveau_fifo_unpause_dummy; engine->display.early_init = nv04_display_early_init; engine->display.late_takedown = nv04_display_late_takedown; engine->display.create = nv04_display_create; @@ -354,6 +380,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->graph.destroy_context = nv50_graph_destroy_context; engine->graph.load_context = nv50_graph_load_context; engine->graph.unload_context = nv50_graph_unload_context; + engine->graph.pause = nv50_graph_pause; + engine->graph.unpause = nv50_graph_unpause; engine->fifo.channels = 128; engine->fifo.init = nv50_fifo_init; engine->fifo.takedown = nv50_fifo_takedown; @@ -365,6 +393,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->fifo.destroy_context = nv50_fifo_destroy_context; engine->fifo.load_context = nv50_fifo_load_context; engine->fifo.unload_context = nv50_fifo_unload_context; + engine->fifo.pause = nv50_fifo_pause; + engine->fifo.unpause = nv50_fifo_unpause; engine->display.early_init = nv50_display_early_init; engine->display.late_takedown = nv50_display_late_takedown; engine->display.create = nv50_display_create; @@ -423,6 +453,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->graph.destroy_context = nvc0_graph_destroy_context; engine->graph.load_context = nvc0_graph_load_context; engine->graph.unload_context = nvc0_graph_unload_context; + engine->graph.pause = nouveau_graph_pause_dummy; + engine->graph.unpause = nouveau_graph_unpause_dummy; engine->fifo.channels = 128; engine->fifo.init = nvc0_fifo_init; engine->fifo.takedown = nvc0_fifo_takedown; @@ -434,6 +466,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->fifo.destroy_context = nvc0_fifo_destroy_context; engine->fifo.load_context = nvc0_fifo_load_context; engine->fifo.unload_context = nvc0_fifo_unload_context; + engine->fifo.pause = nouveau_fifo_pause_dummy; + engine->fifo.unpause = nouveau_fifo_unpause_dummy; engine->display.early_init = nv50_display_early_init; engine->display.late_takedown = nv50_display_late_takedown; engine->display.create = nv50_display_create; @@ -1093,4 +1127,3 @@ bool nouveau_wait_for_idle(struct drm_device *dev) return true; } - diff --git a/drivers/gpu/drm/nouveau/nv50_fifo.c b/drivers/gpu/drm/nouveau/nv50_fifo.c index a46a961..42467cf 100644 --- a/drivers/gpu/drm/nouveau/nv50_fifo.c +++ b/drivers/gpu/drm/nouveau/nv50_fifo.c @@ -464,3 +464,20 @@ nv50_fifo_unload_context(struct drm_device *dev) return 0; } +int +nv50_fifo_pause(struct drm_device *dev) +{ + nv_wr32(dev, NV50_PFIFO_FREEZE, 1); + if (!nouveau_wait_until(dev, 2000000000ULL, NV50_PFIFO_FREEZE, + 0x10, 0x10)) { + NV_ERROR(dev, "PFIFO freeze fail!\n"); + return -EIO; + } + return 0; +} + +void +nv50_fifo_unpause(struct drm_device *dev) +{ + nv_wr32(dev, NV50_PFIFO_FREEZE, 0); +} diff --git a/drivers/gpu/drm/nouveau/nv50_graph.c b/drivers/gpu/drm/nouveau/nv50_graph.c index cbf5ae2..b24c397 100644 --- a/drivers/gpu/drm/nouveau/nv50_graph.c +++ b/drivers/gpu/drm/nouveau/nv50_graph.c @@ -381,6 +381,54 @@ nv50_graph_nvsw_vblsem_release(struct nouveau_channel *chan, int grclass, return 0; } + + +int +nv50_graph_pause(struct drm_device *dev) +{ + uint64_t start; + /* initial guess... */ + uint32_t mask380 = 0xffffffff; + uint32_t mask384 = 0xffffffff; + uint32_t mask388 = 0xffffffff; + uint32_t mask700 = 0x00000001; + + start = nv04_timer_read(dev); + nv_wr32(dev, NV50_PGRAPH_CONTROL, 0x10000); + while ((nv_rd32(dev, 0x400380) & mask380) || + (nv_rd32(dev, 0x400384) & mask384) || + (nv_rd32(dev, 0x400388) & mask388) || + (nv_rd32(dev, NV50_PGRAPH_STATUS) & mask700)) { + if (nv04_timer_read(dev) - start >= 10000000) { + /* 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!\n", + nv_rd32(dev, 0x400380), + nv_rd32(dev, 0x400384), + nv_rd32(dev, 0x400388), + nv_rd32(dev, NV50_PGRAPH_STATUS)); + + if (nv_rd32(dev, NV50_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)); + + nv50_graph_unpause(dev); + return -EIO; + } + } + return 0; +} + +void +nv50_graph_unpause(struct drm_device *dev) +{ + nv_wr32(dev, NV50_PGRAPH_CONTROL, 0x10001); +} + static struct nouveau_pgraph_object_method nv50_graph_nvsw_methods[] = { { 0x018c, nv50_graph_nvsw_dma_vblsem }, { 0x0400, nv50_graph_nvsw_vblsem_offset }, -- 1.7.2 [-- Attachment #3: Type: text/plain, Size: 181 bytes --] _______________________________________________ Nouveau mailing list Nouveau-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW@public.gmane.org http://lists.freedesktop.org/mailman/listinfo/nouveau ^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: Add pause/unpause methods for PFIFO & PGRAPH. Use them to get stable clock changes [not found] ` <4CAD2383.50907-GANU6spQydw@public.gmane.org> 2010-10-07 15:38 ` Martin Peres @ 2010-10-07 15:38 ` Martin Peres [not found] ` <4CADE982.7080309-GANU6spQydw@public.gmane.org> 1 sibling, 1 reply; 7+ messages in thread From: Martin Peres @ 2010-10-07 15:38 UTC (permalink / raw) To: nouveau-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW [-- Attachment #1: Type: text/plain, Size: 214 bytes --] Le 07/10/2010 03:33, Martin Peres a écrit : > Hi, > > Here is an updated version, all in one patch. When we agree on the > code, I'll split it into 3 patches. > Sorry, I forgot to attach it. Here it is. [-- Attachment #2: 0001-Pause-the-card-before-reclocking.patch --] [-- Type: text/x-patch, Size: 15996 bytes --] From 86e7dd89810b37a12ae189633de41aacf07355cb Mon Sep 17 00:00:00 2001 From: Martin Peres <martin.peres-Iz16wY1oaNPLSKGbIzaifA@public.gmane.org> Date: Thu, 7 Oct 2010 05:20:38 +0200 Subject: [PATCH] Pause the card before reclocking Signed-off-by: Martin Peres <martin.peres-Iz16wY1oaNPLSKGbIzaifA@public.gmane.org> --- drivers/gpu/drm/nouveau/nouveau_drv.h | 10 ++++++ drivers/gpu/drm/nouveau/nouveau_pm.c | 50 +++++++++++++++++++++++++++++- drivers/gpu/drm/nouveau/nouveau_reg.h | 3 ++ drivers/gpu/drm/nouveau/nouveau_state.c | 35 +++++++++++++++++++++- drivers/gpu/drm/nouveau/nv50_fifo.c | 17 ++++++++++ drivers/gpu/drm/nouveau/nv50_graph.c | 48 +++++++++++++++++++++++++++++ 6 files changed, 160 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index 591254e..9317bc3 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -304,6 +304,9 @@ struct nouveau_fifo_engine { void (*destroy_context)(struct nouveau_channel *); int (*load_context)(struct nouveau_channel *); int (*unload_context)(struct drm_device *); + + int (*pause)(struct drm_device *); + void (*unpause)(struct drm_device *); }; struct nouveau_pgraph_object_method { @@ -339,6 +342,9 @@ struct nouveau_pgraph_engine { void (*set_region_tiling)(struct drm_device *dev, int i, uint32_t addr, uint32_t size, uint32_t pitch); + + int (*pause)(struct drm_device *); + void (*unpause)(struct drm_device *); }; struct nouveau_display_engine { @@ -1036,6 +1042,8 @@ extern int nv50_fifo_create_context(struct nouveau_channel *); extern void nv50_fifo_destroy_context(struct nouveau_channel *); extern int nv50_fifo_load_context(struct nouveau_channel *); extern int nv50_fifo_unload_context(struct drm_device *); +extern int nv50_fifo_pause(struct drm_device *); +extern void nv50_fifo_unpause(struct drm_device *); /* nvc0_fifo.c */ extern int nvc0_fifo_init(struct drm_device *); @@ -1113,6 +1121,8 @@ extern int nv50_graph_load_context(struct nouveau_channel *); extern int nv50_graph_unload_context(struct drm_device *); extern void nv50_graph_context_switch(struct drm_device *); extern int nv50_grctx_init(struct nouveau_grctx *); +extern int nv50_graph_pause(struct drm_device *dev); +extern void nv50_graph_unpause(struct drm_device *dev); /* nvc0_graph.c */ extern int nvc0_graph_init(struct drm_device *); diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.c b/drivers/gpu/drm/nouveau/nouveau_pm.c index 1c99c55..b546a4d 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.c +++ b/drivers/gpu/drm/nouveau/nouveau_pm.c @@ -55,6 +55,7 @@ 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; + uint32_t status; int ret; if (perflvl == pm->cur) @@ -68,13 +69,58 @@ nouveau_pm_perflvl_set(struct drm_device *dev, struct nouveau_pm_level *perflvl) } } + /* TODO: Wait for vblank */ + + /* Disable interrupts */ + nv_wr32(dev, 0x140, 0); + + /* Pause the engines, if possible */ + if (dev_priv->engine.fifo.pause(dev)) { + ret = -EIO; + goto out; + } + if (dev_priv->engine.graph.pause(dev)) { + ret = -EIO; + goto out; + } + + /* Disable the PFIFO cache pulling */ + status = nv_rd32(dev, 0x003250); + nv_wr32(dev, 0x003250, status&0xfffffffe); + + /* Disable the PFIFO cache dma push */ + status = nv_rd32(dev, 0x003220); + nv_wr32(dev, 0x003220, status&0xfffffffe); + + /* Change the clocks */ 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 */ + udelay(100); + pm->cur = perflvl; - return 0; + ret = 0; + +out: + /* Re-enable the PFIFO cache dma push */ + status = nv_rd32(dev, 0x003220); + nv_wr32(dev, 0x003220, status|0x1); + + /* Re-enable the PFIFO cache pulling */ + status = nv_rd32(dev, 0x003250); + nv_wr32(dev, 0x003250, status|0x1); + + /* Un-pause the engines */ + dev_priv->engine.fifo.unpause(dev); + dev_priv->engine.graph.unpause(dev); + + /* Re-enable interrupts */ + nv_wr32(dev, 0x140, 1); + + return ret; } static int @@ -108,7 +154,7 @@ nouveau_pm_profile_set(struct drm_device *dev, const char *profile) return -EINVAL; } - NV_INFO(dev, "setting performance level: %s\n", profile); + NV_INFO(dev, "setting performance level: %s", profile); return nouveau_pm_perflvl_set(dev, perflvl); } diff --git a/drivers/gpu/drm/nouveau/nouveau_reg.h b/drivers/gpu/drm/nouveau/nouveau_reg.h index 1b42541..346b77a 100644 --- a/drivers/gpu/drm/nouveau/nouveau_reg.h +++ b/drivers/gpu/drm/nouveau/nouveau_reg.h @@ -699,8 +699,11 @@ #define NV50_PROM__ESIZE 0x10000 #define NV50_PGRAPH 0x00400000 +#define NV50_PGRAPH_CONTROL 0x00400500 +#define NV50_PGRAPH_STATUS 0x00400700 #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 75bce91..704a4b2 100644 --- a/drivers/gpu/drm/nouveau/nouveau_state.c +++ b/drivers/gpu/drm/nouveau/nouveau_state.c @@ -42,6 +42,12 @@ static void nouveau_stub_takedown(struct drm_device *dev) {} static int nouveau_stub_init(struct drm_device *dev) { return 0; } +int nouveau_fifo_pause_dummy(struct drm_device *dev) { return 0; } +void nouveau_fifo_unpause_dummy(struct drm_device *dev) { } + +int nouveau_graph_pause_dummy(struct drm_device *dev) { return 0; } +void nouveau_graph_unpause_dummy(struct drm_device *dev) {} + static int nouveau_init_engine_ptrs(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; @@ -74,6 +80,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->graph.destroy_context = nv04_graph_destroy_context; engine->graph.load_context = nv04_graph_load_context; engine->graph.unload_context = nv04_graph_unload_context; + engine->graph.pause = nouveau_graph_pause_dummy; + engine->graph.unpause = nouveau_graph_unpause_dummy; engine->fifo.channels = 16; engine->fifo.init = nv04_fifo_init; engine->fifo.takedown = nouveau_stub_takedown; @@ -86,6 +94,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->fifo.destroy_context = nv04_fifo_destroy_context; engine->fifo.load_context = nv04_fifo_load_context; engine->fifo.unload_context = nv04_fifo_unload_context; + engine->fifo.pause = nouveau_fifo_pause_dummy; + engine->fifo.unpause = nouveau_fifo_unpause_dummy; engine->display.early_init = nv04_display_early_init; engine->display.late_takedown = nv04_display_late_takedown; engine->display.create = nv04_display_create; @@ -128,6 +138,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->graph.load_context = nv10_graph_load_context; engine->graph.unload_context = nv10_graph_unload_context; engine->graph.set_region_tiling = nv10_graph_set_region_tiling; + engine->graph.pause = nouveau_graph_pause_dummy; + engine->graph.unpause = nouveau_graph_unpause_dummy; engine->fifo.channels = 32; engine->fifo.init = nv10_fifo_init; engine->fifo.takedown = nouveau_stub_takedown; @@ -140,6 +152,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->fifo.destroy_context = nv10_fifo_destroy_context; engine->fifo.load_context = nv10_fifo_load_context; engine->fifo.unload_context = nv10_fifo_unload_context; + engine->fifo.pause = nouveau_fifo_pause_dummy; + engine->fifo.unpause = nouveau_fifo_unpause_dummy; engine->display.early_init = nv04_display_early_init; engine->display.late_takedown = nv04_display_late_takedown; engine->display.create = nv04_display_create; @@ -182,6 +196,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->graph.load_context = nv20_graph_load_context; engine->graph.unload_context = nv20_graph_unload_context; engine->graph.set_region_tiling = nv20_graph_set_region_tiling; + engine->graph.pause = nouveau_graph_pause_dummy; + engine->graph.unpause = nouveau_graph_unpause_dummy; engine->fifo.channels = 32; engine->fifo.init = nv10_fifo_init; engine->fifo.takedown = nouveau_stub_takedown; @@ -194,6 +210,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->fifo.destroy_context = nv10_fifo_destroy_context; engine->fifo.load_context = nv10_fifo_load_context; engine->fifo.unload_context = nv10_fifo_unload_context; + engine->fifo.pause = nouveau_fifo_pause_dummy; + engine->fifo.unpause = nouveau_fifo_unpause_dummy; engine->display.early_init = nv04_display_early_init; engine->display.late_takedown = nv04_display_late_takedown; engine->display.create = nv04_display_create; @@ -236,6 +254,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->graph.load_context = nv20_graph_load_context; engine->graph.unload_context = nv20_graph_unload_context; engine->graph.set_region_tiling = nv20_graph_set_region_tiling; + engine->graph.pause = nouveau_graph_pause_dummy; + engine->graph.unpause = nouveau_graph_unpause_dummy; engine->fifo.channels = 32; engine->fifo.init = nv10_fifo_init; engine->fifo.takedown = nouveau_stub_takedown; @@ -248,6 +268,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->fifo.destroy_context = nv10_fifo_destroy_context; engine->fifo.load_context = nv10_fifo_load_context; engine->fifo.unload_context = nv10_fifo_unload_context; + engine->fifo.pause = nouveau_fifo_pause_dummy; + engine->fifo.unpause = nouveau_fifo_unpause_dummy; engine->display.early_init = nv04_display_early_init; engine->display.late_takedown = nv04_display_late_takedown; engine->display.create = nv04_display_create; @@ -293,6 +315,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->graph.load_context = nv40_graph_load_context; engine->graph.unload_context = nv40_graph_unload_context; engine->graph.set_region_tiling = nv40_graph_set_region_tiling; + engine->graph.pause = nouveau_graph_pause_dummy; + engine->graph.unpause = nouveau_graph_unpause_dummy; engine->fifo.channels = 32; engine->fifo.init = nv40_fifo_init; engine->fifo.takedown = nouveau_stub_takedown; @@ -305,6 +329,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->fifo.destroy_context = nv40_fifo_destroy_context; engine->fifo.load_context = nv40_fifo_load_context; engine->fifo.unload_context = nv40_fifo_unload_context; + engine->fifo.pause = nouveau_fifo_pause_dummy; + engine->fifo.unpause = nouveau_fifo_unpause_dummy; engine->display.early_init = nv04_display_early_init; engine->display.late_takedown = nv04_display_late_takedown; engine->display.create = nv04_display_create; @@ -354,6 +380,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->graph.destroy_context = nv50_graph_destroy_context; engine->graph.load_context = nv50_graph_load_context; engine->graph.unload_context = nv50_graph_unload_context; + engine->graph.pause = nv50_graph_pause; + engine->graph.unpause = nv50_graph_unpause; engine->fifo.channels = 128; engine->fifo.init = nv50_fifo_init; engine->fifo.takedown = nv50_fifo_takedown; @@ -365,6 +393,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->fifo.destroy_context = nv50_fifo_destroy_context; engine->fifo.load_context = nv50_fifo_load_context; engine->fifo.unload_context = nv50_fifo_unload_context; + engine->fifo.pause = nv50_fifo_pause; + engine->fifo.unpause = nv50_fifo_unpause; engine->display.early_init = nv50_display_early_init; engine->display.late_takedown = nv50_display_late_takedown; engine->display.create = nv50_display_create; @@ -423,6 +453,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->graph.destroy_context = nvc0_graph_destroy_context; engine->graph.load_context = nvc0_graph_load_context; engine->graph.unload_context = nvc0_graph_unload_context; + engine->graph.pause = nouveau_graph_pause_dummy; + engine->graph.unpause = nouveau_graph_unpause_dummy; engine->fifo.channels = 128; engine->fifo.init = nvc0_fifo_init; engine->fifo.takedown = nvc0_fifo_takedown; @@ -434,6 +466,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->fifo.destroy_context = nvc0_fifo_destroy_context; engine->fifo.load_context = nvc0_fifo_load_context; engine->fifo.unload_context = nvc0_fifo_unload_context; + engine->fifo.pause = nouveau_fifo_pause_dummy; + engine->fifo.unpause = nouveau_fifo_unpause_dummy; engine->display.early_init = nv50_display_early_init; engine->display.late_takedown = nv50_display_late_takedown; engine->display.create = nv50_display_create; @@ -1093,4 +1127,3 @@ bool nouveau_wait_for_idle(struct drm_device *dev) return true; } - diff --git a/drivers/gpu/drm/nouveau/nv50_fifo.c b/drivers/gpu/drm/nouveau/nv50_fifo.c index a46a961..42467cf 100644 --- a/drivers/gpu/drm/nouveau/nv50_fifo.c +++ b/drivers/gpu/drm/nouveau/nv50_fifo.c @@ -464,3 +464,20 @@ nv50_fifo_unload_context(struct drm_device *dev) return 0; } +int +nv50_fifo_pause(struct drm_device *dev) +{ + nv_wr32(dev, NV50_PFIFO_FREEZE, 1); + if (!nouveau_wait_until(dev, 2000000000ULL, NV50_PFIFO_FREEZE, + 0x10, 0x10)) { + NV_ERROR(dev, "PFIFO freeze fail!\n"); + return -EIO; + } + return 0; +} + +void +nv50_fifo_unpause(struct drm_device *dev) +{ + nv_wr32(dev, NV50_PFIFO_FREEZE, 0); +} diff --git a/drivers/gpu/drm/nouveau/nv50_graph.c b/drivers/gpu/drm/nouveau/nv50_graph.c index cbf5ae2..b24c397 100644 --- a/drivers/gpu/drm/nouveau/nv50_graph.c +++ b/drivers/gpu/drm/nouveau/nv50_graph.c @@ -381,6 +381,54 @@ nv50_graph_nvsw_vblsem_release(struct nouveau_channel *chan, int grclass, return 0; } + + +int +nv50_graph_pause(struct drm_device *dev) +{ + uint64_t start; + /* initial guess... */ + uint32_t mask380 = 0xffffffff; + uint32_t mask384 = 0xffffffff; + uint32_t mask388 = 0xffffffff; + uint32_t mask700 = 0x00000001; + + start = nv04_timer_read(dev); + nv_wr32(dev, NV50_PGRAPH_CONTROL, 0x10000); + while ((nv_rd32(dev, 0x400380) & mask380) || + (nv_rd32(dev, 0x400384) & mask384) || + (nv_rd32(dev, 0x400388) & mask388) || + (nv_rd32(dev, NV50_PGRAPH_STATUS) & mask700)) { + if (nv04_timer_read(dev) - start >= 10000000) { + /* 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!\n", + nv_rd32(dev, 0x400380), + nv_rd32(dev, 0x400384), + nv_rd32(dev, 0x400388), + nv_rd32(dev, NV50_PGRAPH_STATUS)); + + if (nv_rd32(dev, NV50_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)); + + nv50_graph_unpause(dev); + return -EIO; + } + } + return 0; +} + +void +nv50_graph_unpause(struct drm_device *dev) +{ + nv_wr32(dev, NV50_PGRAPH_CONTROL, 0x10001); +} + static struct nouveau_pgraph_object_method nv50_graph_nvsw_methods[] = { { 0x018c, nv50_graph_nvsw_dma_vblsem }, { 0x0400, nv50_graph_nvsw_vblsem_offset }, -- 1.7.2 [-- Attachment #3: Type: text/plain, Size: 181 bytes --] _______________________________________________ Nouveau mailing list Nouveau-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW@public.gmane.org http://lists.freedesktop.org/mailman/listinfo/nouveau ^ permalink raw reply related [flat|nested] 7+ messages in thread
[parent not found: <4CADE982.7080309-GANU6spQydw@public.gmane.org>]
* Re: Add pause/unpause methods for PFIFO & PGRAPH. Use them to get stable clock changes [not found] ` <4CADE982.7080309-GANU6spQydw@public.gmane.org> @ 2010-10-28 16:34 ` Martin Peres [not found] ` <4CC9A605.5050700-GANU6spQydw@public.gmane.org> 0 siblings, 1 reply; 7+ messages in thread From: Martin Peres @ 2010-10-28 16:34 UTC (permalink / raw) To: nouveau-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW [-- Attachment #1: Type: text/plain, Size: 151 bytes --] Hi, Please merge this patch, it helps a lot when it comes to safe re-clocking. It isn't perfect yet but it will satisfy most users. Cheers, Martin [-- Attachment #2: 0001-Pause-the-card-before-reclocking.patch --] [-- Type: text/x-patch, Size: 15826 bytes --] From 58605d78ec7a576502a8f46953f6e2f0092eb180 Mon Sep 17 00:00:00 2001 From: Martin Peres <martin.peres-Iz16wY1oaNPLSKGbIzaifA@public.gmane.org> Date: Thu, 28 Oct 2010 20:27:08 +0200 Subject: [PATCH] Pause the card before reclocking Signed-off-by: Martin Peres <martin.peres-Iz16wY1oaNPLSKGbIzaifA@public.gmane.org> --- drivers/gpu/drm/nouveau/nouveau_drv.h | 10 ++++++ drivers/gpu/drm/nouveau/nouveau_pm.c | 50 +++++++++++++++++++++++++++++- drivers/gpu/drm/nouveau/nouveau_reg.h | 3 ++ drivers/gpu/drm/nouveau/nouveau_state.c | 35 +++++++++++++++++++++- drivers/gpu/drm/nouveau/nv50_fifo.c | 18 +++++++++++ drivers/gpu/drm/nouveau/nv50_graph.c | 46 ++++++++++++++++++++++++++++ 6 files changed, 159 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index fc162c2..6f3b81b 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -338,6 +338,9 @@ struct nouveau_fifo_engine { int (*load_context)(struct nouveau_channel *); int (*unload_context)(struct drm_device *); void (*tlb_flush)(struct drm_device *dev); + + int (*pause)(struct drm_device *); + void (*unpause)(struct drm_device *); }; struct nouveau_pgraph_engine { @@ -361,6 +364,9 @@ struct nouveau_pgraph_engine { void (*tlb_flush)(struct drm_device *dev); void (*set_tile_region)(struct drm_device *dev, int i); + + int (*pause)(struct drm_device *); + void (*unpause)(struct drm_device *); }; struct nouveau_display_engine { @@ -1076,6 +1082,8 @@ extern void nv50_fifo_destroy_context(struct nouveau_channel *); extern int nv50_fifo_load_context(struct nouveau_channel *); extern int nv50_fifo_unload_context(struct drm_device *); extern void nv50_fifo_tlb_flush(struct drm_device *dev); +extern int nv50_fifo_pause(struct drm_device *); +extern void nv50_fifo_unpause(struct drm_device *); /* nvc0_fifo.c */ extern int nvc0_fifo_init(struct drm_device *); @@ -1148,6 +1156,8 @@ extern void nv50_graph_context_switch(struct drm_device *); extern int nv50_grctx_init(struct nouveau_grctx *); extern void nv50_graph_tlb_flush(struct drm_device *dev); extern void nv86_graph_tlb_flush(struct drm_device *dev); +extern int nv50_graph_pause(struct drm_device *dev); +extern void nv50_graph_unpause(struct drm_device *dev); /* nvc0_graph.c */ extern int nvc0_graph_init(struct drm_device *); diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.c b/drivers/gpu/drm/nouveau/nouveau_pm.c index 8ef1d5b..bc7c70e 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.c +++ b/drivers/gpu/drm/nouveau/nouveau_pm.c @@ -59,6 +59,7 @@ 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; + uint32_t status; int ret; if (perflvl == pm->cur) @@ -72,13 +73,58 @@ nouveau_pm_perflvl_set(struct drm_device *dev, struct nouveau_pm_level *perflvl) } } + /* TODO: Wait for vblank */ + + /* Disable interrupts */ + nv_wr32(dev, 0x140, 0); + + /* Pause the engines, if possible */ + if (dev_priv->engine.fifo.pause(dev)) { + ret = -EIO; + goto out; + } + if (dev_priv->engine.graph.pause(dev)) { + ret = -EIO; + goto out; + } + + /* Disable the PFIFO cache pulling */ + status = nv_rd32(dev, 0x003250); + nv_wr32(dev, 0x003250, status&0xfffffffe); + + /* Disable the PFIFO cache dma push */ + status = nv_rd32(dev, 0x003220); + nv_wr32(dev, 0x003220, status&0xfffffffe); + + /* Change the clocks */ 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 */ + udelay(100); + pm->cur = perflvl; - return 0; + ret = 0; + +out: + /* Re-enable the PFIFO cache dma push */ + status = nv_rd32(dev, 0x003220); + nv_wr32(dev, 0x003220, status|0x1); + + /* Re-enable the PFIFO cache pulling */ + status = nv_rd32(dev, 0x003250); + nv_wr32(dev, 0x003250, status|0x1); + + /* Un-pause the engines */ + dev_priv->engine.fifo.unpause(dev); + dev_priv->engine.graph.unpause(dev); + + /* Re-enable interrupts */ + nv_wr32(dev, 0x140, 1); + + return ret; } static int @@ -112,7 +158,7 @@ nouveau_pm_profile_set(struct drm_device *dev, const char *profile) return -EINVAL; } - NV_INFO(dev, "setting performance level: %s\n", profile); + NV_INFO(dev, "setting performance level: %s", profile); return nouveau_pm_perflvl_set(dev, perflvl); } diff --git a/drivers/gpu/drm/nouveau/nouveau_reg.h b/drivers/gpu/drm/nouveau/nouveau_reg.h index b6384d3..951c268 100644 --- a/drivers/gpu/drm/nouveau/nouveau_reg.h +++ b/drivers/gpu/drm/nouveau/nouveau_reg.h @@ -700,8 +700,11 @@ #define NV50_PROM__ESIZE 0x10000 #define NV50_PGRAPH 0x00400000 +#define NV50_PGRAPH_CONTROL 0x00400500 +#define NV50_PGRAPH_STATUS 0x00400700 #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 1a7a50c..a41a028 100644 --- a/drivers/gpu/drm/nouveau/nouveau_state.c +++ b/drivers/gpu/drm/nouveau/nouveau_state.c @@ -42,6 +42,12 @@ static void nouveau_stub_takedown(struct drm_device *dev) {} static int nouveau_stub_init(struct drm_device *dev) { return 0; } +int nouveau_fifo_pause_dummy(struct drm_device *dev) { return 0; } +void nouveau_fifo_unpause_dummy(struct drm_device *dev) { } + +int nouveau_graph_pause_dummy(struct drm_device *dev) { return 0; } +void nouveau_graph_unpause_dummy(struct drm_device *dev) {} + static int nouveau_init_engine_ptrs(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; @@ -73,6 +79,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->graph.destroy_context = nv04_graph_destroy_context; engine->graph.load_context = nv04_graph_load_context; engine->graph.unload_context = nv04_graph_unload_context; + engine->graph.pause = nouveau_graph_pause_dummy; + engine->graph.unpause = nouveau_graph_unpause_dummy; engine->fifo.channels = 16; engine->fifo.init = nv04_fifo_init; engine->fifo.takedown = nouveau_stub_takedown; @@ -85,6 +93,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->fifo.destroy_context = nv04_fifo_destroy_context; engine->fifo.load_context = nv04_fifo_load_context; engine->fifo.unload_context = nv04_fifo_unload_context; + engine->fifo.pause = nouveau_fifo_pause_dummy; + engine->fifo.unpause = nouveau_fifo_unpause_dummy; engine->display.early_init = nv04_display_early_init; engine->display.late_takedown = nv04_display_late_takedown; engine->display.create = nv04_display_create; @@ -130,6 +140,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->graph.load_context = nv10_graph_load_context; engine->graph.unload_context = nv10_graph_unload_context; engine->graph.set_tile_region = nv10_graph_set_tile_region; + engine->graph.pause = nouveau_graph_pause_dummy; + engine->graph.unpause = nouveau_graph_unpause_dummy; engine->fifo.channels = 32; engine->fifo.init = nv10_fifo_init; engine->fifo.takedown = nouveau_stub_takedown; @@ -142,6 +154,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->fifo.destroy_context = nv04_fifo_destroy_context; engine->fifo.load_context = nv10_fifo_load_context; engine->fifo.unload_context = nv10_fifo_unload_context; + engine->fifo.pause = nouveau_fifo_pause_dummy; + engine->fifo.unpause = nouveau_fifo_unpause_dummy; engine->display.early_init = nv04_display_early_init; engine->display.late_takedown = nv04_display_late_takedown; engine->display.create = nv04_display_create; @@ -187,6 +201,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->graph.load_context = nv20_graph_load_context; engine->graph.unload_context = nv20_graph_unload_context; engine->graph.set_tile_region = nv20_graph_set_tile_region; + engine->graph.pause = nouveau_graph_pause_dummy; + engine->graph.unpause = nouveau_graph_unpause_dummy; engine->fifo.channels = 32; engine->fifo.init = nv10_fifo_init; engine->fifo.takedown = nouveau_stub_takedown; @@ -199,6 +215,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->fifo.destroy_context = nv04_fifo_destroy_context; engine->fifo.load_context = nv10_fifo_load_context; engine->fifo.unload_context = nv10_fifo_unload_context; + engine->fifo.pause = nouveau_fifo_pause_dummy; + engine->fifo.unpause = nouveau_fifo_unpause_dummy; engine->display.early_init = nv04_display_early_init; engine->display.late_takedown = nv04_display_late_takedown; engine->display.create = nv04_display_create; @@ -244,6 +262,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->graph.load_context = nv20_graph_load_context; engine->graph.unload_context = nv20_graph_unload_context; engine->graph.set_tile_region = nv20_graph_set_tile_region; + engine->graph.pause = nouveau_graph_pause_dummy; + engine->graph.unpause = nouveau_graph_unpause_dummy; engine->fifo.channels = 32; engine->fifo.init = nv10_fifo_init; engine->fifo.takedown = nouveau_stub_takedown; @@ -256,6 +276,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->fifo.destroy_context = nv04_fifo_destroy_context; engine->fifo.load_context = nv10_fifo_load_context; engine->fifo.unload_context = nv10_fifo_unload_context; + engine->fifo.pause = nouveau_fifo_pause_dummy; + engine->fifo.unpause = nouveau_fifo_unpause_dummy; engine->display.early_init = nv04_display_early_init; engine->display.late_takedown = nv04_display_late_takedown; engine->display.create = nv04_display_create; @@ -304,6 +326,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->graph.load_context = nv40_graph_load_context; engine->graph.unload_context = nv40_graph_unload_context; engine->graph.set_tile_region = nv40_graph_set_tile_region; + engine->graph.pause = nouveau_graph_pause_dummy; + engine->graph.unpause = nouveau_graph_unpause_dummy; engine->fifo.channels = 32; engine->fifo.init = nv40_fifo_init; engine->fifo.takedown = nouveau_stub_takedown; @@ -316,6 +340,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->fifo.destroy_context = nv04_fifo_destroy_context; engine->fifo.load_context = nv40_fifo_load_context; engine->fifo.unload_context = nv40_fifo_unload_context; + engine->fifo.pause = nouveau_fifo_pause_dummy; + engine->fifo.unpause = nouveau_fifo_unpause_dummy; engine->display.early_init = nv04_display_early_init; engine->display.late_takedown = nv04_display_late_takedown; engine->display.create = nv04_display_create; @@ -366,6 +392,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->graph.destroy_context = nv50_graph_destroy_context; engine->graph.load_context = nv50_graph_load_context; engine->graph.unload_context = nv50_graph_unload_context; + engine->graph.pause = nv50_graph_pause; + engine->graph.unpause = nv50_graph_unpause; if (dev_priv->chipset != 0x86) engine->graph.tlb_flush = nv50_graph_tlb_flush; else { @@ -387,6 +415,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->fifo.load_context = nv50_fifo_load_context; engine->fifo.unload_context = nv50_fifo_unload_context; engine->fifo.tlb_flush = nv50_fifo_tlb_flush; + engine->fifo.pause = nv50_fifo_pause; + engine->fifo.unpause = nv50_fifo_unpause; engine->display.early_init = nv50_display_early_init; engine->display.late_takedown = nv50_display_late_takedown; engine->display.create = nv50_display_create; @@ -467,6 +497,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->graph.destroy_context = nvc0_graph_destroy_context; engine->graph.load_context = nvc0_graph_load_context; engine->graph.unload_context = nvc0_graph_unload_context; + engine->graph.pause = nouveau_graph_pause_dummy; + engine->graph.unpause = nouveau_graph_unpause_dummy; engine->fifo.channels = 128; engine->fifo.init = nvc0_fifo_init; engine->fifo.takedown = nvc0_fifo_takedown; @@ -478,6 +510,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->fifo.destroy_context = nvc0_fifo_destroy_context; engine->fifo.load_context = nvc0_fifo_load_context; engine->fifo.unload_context = nvc0_fifo_unload_context; + engine->fifo.pause = nouveau_fifo_pause_dummy; + engine->fifo.unpause = nouveau_fifo_unpause_dummy; engine->display.early_init = nv50_display_early_init; engine->display.late_takedown = nv50_display_late_takedown; engine->display.create = nv50_display_create; @@ -1167,4 +1201,3 @@ bool nouveau_wait_for_idle(struct drm_device *dev) return true; } - diff --git a/drivers/gpu/drm/nouveau/nv50_fifo.c b/drivers/gpu/drm/nouveau/nv50_fifo.c index d3295aa..ea8cc34 100644 --- a/drivers/gpu/drm/nouveau/nv50_fifo.c +++ b/drivers/gpu/drm/nouveau/nv50_fifo.c @@ -487,3 +487,21 @@ nv50_fifo_tlb_flush(struct drm_device *dev) { nv50_vm_flush(dev, 5); } + +int +nv50_fifo_pause(struct drm_device *dev) +{ + nv_wr32(dev, NV50_PFIFO_FREEZE, 1); + if (!nouveau_wait_until(dev, 2000000000ULL, NV50_PFIFO_FREEZE, + 0x10, 0x10)) { + NV_ERROR(dev, "PFIFO freeze fail!\n"); + return -EIO; + } + return 0; +} + +void +nv50_fifo_unpause(struct drm_device *dev) +{ + nv_wr32(dev, NV50_PFIFO_FREEZE, 0); +} diff --git a/drivers/gpu/drm/nouveau/nv50_graph.c b/drivers/gpu/drm/nouveau/nv50_graph.c index e0f5294..9c0543b 100644 --- a/drivers/gpu/drm/nouveau/nv50_graph.c +++ b/drivers/gpu/drm/nouveau/nv50_graph.c @@ -507,3 +507,49 @@ nv86_graph_tlb_flush(struct drm_device *dev) nv_mask(dev, 0x400500, 0x00000001, 0x00000001); spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags); } + +int +nv50_graph_pause(struct drm_device *dev) +{ + uint64_t start; + /* initial guess... */ + uint32_t mask380 = 0xffffffff; + uint32_t mask384 = 0xffffffff; + uint32_t mask388 = 0xffffffff; + uint32_t mask700 = 0x00000001; + + start = nv04_timer_read(dev); + nv_wr32(dev, NV50_PGRAPH_CONTROL, 0x10000); + while ((nv_rd32(dev, 0x400380) & mask380) || + (nv_rd32(dev, 0x400384) & mask384) || + (nv_rd32(dev, 0x400388) & mask388) || + (nv_rd32(dev, NV50_PGRAPH_STATUS) & mask700)) { + if (nv04_timer_read(dev) - start >= 10000000) { + /* 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!\n", + nv_rd32(dev, 0x400380), + nv_rd32(dev, 0x400384), + nv_rd32(dev, 0x400388), + nv_rd32(dev, NV50_PGRAPH_STATUS)); + + if (nv_rd32(dev, NV50_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)); + + nv50_graph_unpause(dev); + return -EIO; + } + } + return 0; +} + +void +nv50_graph_unpause(struct drm_device *dev) +{ + nv_wr32(dev, NV50_PGRAPH_CONTROL, 0x10001); +} -- 1.7.3.2 [-- Attachment #3: Type: text/plain, Size: 181 bytes --] _______________________________________________ Nouveau mailing list Nouveau-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW@public.gmane.org http://lists.freedesktop.org/mailman/listinfo/nouveau ^ permalink raw reply related [flat|nested] 7+ messages in thread
[parent not found: <4CC9A605.5050700-GANU6spQydw@public.gmane.org>]
* [To developers and hardcore NV50+ users] Experimental patch to get safer clock changes [not found] ` <4CC9A605.5050700-GANU6spQydw@public.gmane.org> @ 2010-11-03 1:32 ` Martin Peres 0 siblings, 0 replies; 7+ messages in thread From: Martin Peres @ 2010-11-03 1:32 UTC (permalink / raw) To: nouveau-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW [-- Attachment #1: Type: text/plain, Size: 2073 bytes --] Hi folks, Please do not merge this patch. I'm just asking people with nv50+ to try this very experimental patch. Hopefully, the results are good and I can start rework the patch to make it run faster and, hopefully, flicker-free. This patch introduces Ben Skeggs's PMS-based clock changing code that I reworked a little. It also introduces the use of the 0xc040 register (I'm still REing it, it seems like a PLL supervisor, it is really weird but gives dir1212's nv92 way more stability). This should run fine on nv84 to nv94 but,I have no idea on how it will perform on later cards. To test this patch, you should edit and use the (very small) script "test_mode_changes.sh" that is basically changing the clocks every 100ms. Launch it and then, please do GPU intensive tasks like playing games. If it crashes, that's a fail. I personally can leave my computer in openarena for minutes without crashes nor nasty messages in dmesg. I then get bored continue hacking. So, please report success or failure accompanied with the mmiotrace of your card like explained here: http://github.com/pathscale/pscnv/wiki/pm_mmiotrace) in the case it fails. Wish you luck :) As for what is on my todo list: - I may generate a big PMS script to handle all the clock changes. The problem is that it takes a lot of time to execute scripts on my card and I really want clock changes to happen fast to lower the performance hit of the reclock (see the third item). - If this patch works, I'll rework and send it for inclusion. - I'll try working on nv40 a bit (see what I can backport). - Try reverse engineering performance counters so as the clock can be changed according to the card's current load. This is supposed to be hard (IIRC), so, It should take me a while. Kindly, Martin PS: I really don't want everyone to jump on this patch and try it. Only people actually aware of my work and understanding what are the changes I made should test this patch. I really don't want dozens of people testing this, it is not ready for people other than nouveau devs. [-- Attachment #2: 0001-Pause-the-card-before-reclocking-and-use-PMS-to-recl.patch --] [-- Type: text/x-patch, Size: 26271 bytes --] From 61a256a6fa171c7c310a5abedd7cf178c8403f8b Mon Sep 17 00:00:00 2001 From: Martin Peres <martin.peres-Iz16wY1oaNPLSKGbIzaifA@public.gmane.org> Date: Wed, 3 Nov 2010 02:49:52 +0100 Subject: [PATCH] Pause the card before reclocking and use PMS to reclock the memory clocks. This is a very experimental patch. Please test with caution. This should work on nv84 -> nv94 (included), I don't know how it is supposed to behave on later cards. How to test? Well, make a script (or use the one I attached to the mail) to change the clocks every 100ms, launch it and then go play open arena or anything else that is GPU intensive. Please report every success or failure stories. Pitfalls: Changing the clocks will result in a garbelled screen for a few ms. This is really anoying but shouldn't impact the card's stability. Signed-off-by: Martin Peres <martin.peres-Iz16wY1oaNPLSKGbIzaifA@public.gmane.org> --- drivers/gpu/drm/nouveau/nouveau_drv.h | 10 ++ drivers/gpu/drm/nouveau/nouveau_pm.c | 60 +++++++++++- drivers/gpu/drm/nouveau/nouveau_pms.h | 98 ++++++++++++++++++ drivers/gpu/drm/nouveau/nouveau_reg.h | 3 + drivers/gpu/drm/nouveau/nouveau_state.c | 35 +++++++- drivers/gpu/drm/nouveau/nv50_fifo.c | 18 ++++ drivers/gpu/drm/nouveau/nv50_graph.c | 46 +++++++++ drivers/gpu/drm/nouveau/nv50_pm.c | 164 +++++++++++++++++++++++++++---- 8 files changed, 412 insertions(+), 22 deletions(-) create mode 100644 drivers/gpu/drm/nouveau/nouveau_pms.h diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index fc162c2..6f3b81b 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -338,6 +338,9 @@ struct nouveau_fifo_engine { int (*load_context)(struct nouveau_channel *); int (*unload_context)(struct drm_device *); void (*tlb_flush)(struct drm_device *dev); + + int (*pause)(struct drm_device *); + void (*unpause)(struct drm_device *); }; struct nouveau_pgraph_engine { @@ -361,6 +364,9 @@ struct nouveau_pgraph_engine { void (*tlb_flush)(struct drm_device *dev); void (*set_tile_region)(struct drm_device *dev, int i); + + int (*pause)(struct drm_device *); + void (*unpause)(struct drm_device *); }; struct nouveau_display_engine { @@ -1076,6 +1082,8 @@ extern void nv50_fifo_destroy_context(struct nouveau_channel *); extern int nv50_fifo_load_context(struct nouveau_channel *); extern int nv50_fifo_unload_context(struct drm_device *); extern void nv50_fifo_tlb_flush(struct drm_device *dev); +extern int nv50_fifo_pause(struct drm_device *); +extern void nv50_fifo_unpause(struct drm_device *); /* nvc0_fifo.c */ extern int nvc0_fifo_init(struct drm_device *); @@ -1148,6 +1156,8 @@ extern void nv50_graph_context_switch(struct drm_device *); extern int nv50_grctx_init(struct nouveau_grctx *); extern void nv50_graph_tlb_flush(struct drm_device *dev); extern void nv86_graph_tlb_flush(struct drm_device *dev); +extern int nv50_graph_pause(struct drm_device *dev); +extern void nv50_graph_unpause(struct drm_device *dev); /* nvc0_graph.c */ extern int nvc0_graph_init(struct drm_device *); diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.c b/drivers/gpu/drm/nouveau/nouveau_pm.c index 8ef1d5b..268a6d3 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.c +++ b/drivers/gpu/drm/nouveau/nouveau_pm.c @@ -59,6 +59,7 @@ 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; + uint32_t status, rc040; int ret; if (perflvl == pm->cur) @@ -72,13 +73,66 @@ nouveau_pm_perflvl_set(struct drm_device *dev, struct nouveau_pm_level *perflvl) } } + /* Disable interrupts */ + nv_wr32(dev, 0x140, 0); + + /* Pause the engines, if possible */ + if (dev_priv->engine.fifo.pause(dev)) { + ret = -EIO; + goto out; + } + if (dev_priv->engine.graph.pause(dev)) { + ret = -EIO; + goto out; + } + + /* Disable the PFIFO cache pulling */ + status = nv_rd32(dev, 0x003250); + nv_wr32(dev, 0x003250, status&0xfffffffe); + + /* Disable the PFIFO cache dma push */ + status = nv_rd32(dev, 0x003220); + nv_wr32(dev, 0x003220, status&0xfffffffe); + + /* Save the PLL supervisor state */ + rc040 = nv_rd32(dev, 0xc040); + + /* TODO: Wait for vblank */ + + /* Change the clocks */ + nouveau_pm_clock_set(dev, perflvl, PLL_MEMORY, perflvl->memory); 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 */ + udelay(100); + + /* Restaure the PLL supervisor state */ + nv_wr32(dev, 0xc040, rc040); + nv_wr32(dev, 0xc040, 0x10); + nv_wr32(dev, 0xc040, rc040); + pm->cur = perflvl; - return 0; + ret = 0; + +out: + /* Re-enable the PFIFO cache dma push */ + status = nv_rd32(dev, 0x003220); + nv_wr32(dev, 0x003220, status|0x1); + + /* Re-enable the PFIFO cache pulling */ + status = nv_rd32(dev, 0x003250); + nv_wr32(dev, 0x003250, status|0x1); + + /* Un-pause the engines */ + dev_priv->engine.fifo.unpause(dev); + dev_priv->engine.graph.unpause(dev); + + /* Re-enable interrupts */ + nv_wr32(dev, 0x140, 1); + + return ret; } static int @@ -112,7 +166,7 @@ nouveau_pm_profile_set(struct drm_device *dev, const char *profile) return -EINVAL; } - NV_INFO(dev, "setting performance level: %s\n", profile); + NV_INFO(dev, "setting performance level: %s", profile); return nouveau_pm_perflvl_set(dev, perflvl); } 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/nouveau_reg.h b/drivers/gpu/drm/nouveau/nouveau_reg.h index b6384d3..951c268 100644 --- a/drivers/gpu/drm/nouveau/nouveau_reg.h +++ b/drivers/gpu/drm/nouveau/nouveau_reg.h @@ -700,8 +700,11 @@ #define NV50_PROM__ESIZE 0x10000 #define NV50_PGRAPH 0x00400000 +#define NV50_PGRAPH_CONTROL 0x00400500 +#define NV50_PGRAPH_STATUS 0x00400700 #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 1a7a50c..a41a028 100644 --- a/drivers/gpu/drm/nouveau/nouveau_state.c +++ b/drivers/gpu/drm/nouveau/nouveau_state.c @@ -42,6 +42,12 @@ static void nouveau_stub_takedown(struct drm_device *dev) {} static int nouveau_stub_init(struct drm_device *dev) { return 0; } +int nouveau_fifo_pause_dummy(struct drm_device *dev) { return 0; } +void nouveau_fifo_unpause_dummy(struct drm_device *dev) { } + +int nouveau_graph_pause_dummy(struct drm_device *dev) { return 0; } +void nouveau_graph_unpause_dummy(struct drm_device *dev) {} + static int nouveau_init_engine_ptrs(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; @@ -73,6 +79,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->graph.destroy_context = nv04_graph_destroy_context; engine->graph.load_context = nv04_graph_load_context; engine->graph.unload_context = nv04_graph_unload_context; + engine->graph.pause = nouveau_graph_pause_dummy; + engine->graph.unpause = nouveau_graph_unpause_dummy; engine->fifo.channels = 16; engine->fifo.init = nv04_fifo_init; engine->fifo.takedown = nouveau_stub_takedown; @@ -85,6 +93,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->fifo.destroy_context = nv04_fifo_destroy_context; engine->fifo.load_context = nv04_fifo_load_context; engine->fifo.unload_context = nv04_fifo_unload_context; + engine->fifo.pause = nouveau_fifo_pause_dummy; + engine->fifo.unpause = nouveau_fifo_unpause_dummy; engine->display.early_init = nv04_display_early_init; engine->display.late_takedown = nv04_display_late_takedown; engine->display.create = nv04_display_create; @@ -130,6 +140,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->graph.load_context = nv10_graph_load_context; engine->graph.unload_context = nv10_graph_unload_context; engine->graph.set_tile_region = nv10_graph_set_tile_region; + engine->graph.pause = nouveau_graph_pause_dummy; + engine->graph.unpause = nouveau_graph_unpause_dummy; engine->fifo.channels = 32; engine->fifo.init = nv10_fifo_init; engine->fifo.takedown = nouveau_stub_takedown; @@ -142,6 +154,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->fifo.destroy_context = nv04_fifo_destroy_context; engine->fifo.load_context = nv10_fifo_load_context; engine->fifo.unload_context = nv10_fifo_unload_context; + engine->fifo.pause = nouveau_fifo_pause_dummy; + engine->fifo.unpause = nouveau_fifo_unpause_dummy; engine->display.early_init = nv04_display_early_init; engine->display.late_takedown = nv04_display_late_takedown; engine->display.create = nv04_display_create; @@ -187,6 +201,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->graph.load_context = nv20_graph_load_context; engine->graph.unload_context = nv20_graph_unload_context; engine->graph.set_tile_region = nv20_graph_set_tile_region; + engine->graph.pause = nouveau_graph_pause_dummy; + engine->graph.unpause = nouveau_graph_unpause_dummy; engine->fifo.channels = 32; engine->fifo.init = nv10_fifo_init; engine->fifo.takedown = nouveau_stub_takedown; @@ -199,6 +215,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->fifo.destroy_context = nv04_fifo_destroy_context; engine->fifo.load_context = nv10_fifo_load_context; engine->fifo.unload_context = nv10_fifo_unload_context; + engine->fifo.pause = nouveau_fifo_pause_dummy; + engine->fifo.unpause = nouveau_fifo_unpause_dummy; engine->display.early_init = nv04_display_early_init; engine->display.late_takedown = nv04_display_late_takedown; engine->display.create = nv04_display_create; @@ -244,6 +262,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->graph.load_context = nv20_graph_load_context; engine->graph.unload_context = nv20_graph_unload_context; engine->graph.set_tile_region = nv20_graph_set_tile_region; + engine->graph.pause = nouveau_graph_pause_dummy; + engine->graph.unpause = nouveau_graph_unpause_dummy; engine->fifo.channels = 32; engine->fifo.init = nv10_fifo_init; engine->fifo.takedown = nouveau_stub_takedown; @@ -256,6 +276,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->fifo.destroy_context = nv04_fifo_destroy_context; engine->fifo.load_context = nv10_fifo_load_context; engine->fifo.unload_context = nv10_fifo_unload_context; + engine->fifo.pause = nouveau_fifo_pause_dummy; + engine->fifo.unpause = nouveau_fifo_unpause_dummy; engine->display.early_init = nv04_display_early_init; engine->display.late_takedown = nv04_display_late_takedown; engine->display.create = nv04_display_create; @@ -304,6 +326,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->graph.load_context = nv40_graph_load_context; engine->graph.unload_context = nv40_graph_unload_context; engine->graph.set_tile_region = nv40_graph_set_tile_region; + engine->graph.pause = nouveau_graph_pause_dummy; + engine->graph.unpause = nouveau_graph_unpause_dummy; engine->fifo.channels = 32; engine->fifo.init = nv40_fifo_init; engine->fifo.takedown = nouveau_stub_takedown; @@ -316,6 +340,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->fifo.destroy_context = nv04_fifo_destroy_context; engine->fifo.load_context = nv40_fifo_load_context; engine->fifo.unload_context = nv40_fifo_unload_context; + engine->fifo.pause = nouveau_fifo_pause_dummy; + engine->fifo.unpause = nouveau_fifo_unpause_dummy; engine->display.early_init = nv04_display_early_init; engine->display.late_takedown = nv04_display_late_takedown; engine->display.create = nv04_display_create; @@ -366,6 +392,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->graph.destroy_context = nv50_graph_destroy_context; engine->graph.load_context = nv50_graph_load_context; engine->graph.unload_context = nv50_graph_unload_context; + engine->graph.pause = nv50_graph_pause; + engine->graph.unpause = nv50_graph_unpause; if (dev_priv->chipset != 0x86) engine->graph.tlb_flush = nv50_graph_tlb_flush; else { @@ -387,6 +415,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->fifo.load_context = nv50_fifo_load_context; engine->fifo.unload_context = nv50_fifo_unload_context; engine->fifo.tlb_flush = nv50_fifo_tlb_flush; + engine->fifo.pause = nv50_fifo_pause; + engine->fifo.unpause = nv50_fifo_unpause; engine->display.early_init = nv50_display_early_init; engine->display.late_takedown = nv50_display_late_takedown; engine->display.create = nv50_display_create; @@ -467,6 +497,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->graph.destroy_context = nvc0_graph_destroy_context; engine->graph.load_context = nvc0_graph_load_context; engine->graph.unload_context = nvc0_graph_unload_context; + engine->graph.pause = nouveau_graph_pause_dummy; + engine->graph.unpause = nouveau_graph_unpause_dummy; engine->fifo.channels = 128; engine->fifo.init = nvc0_fifo_init; engine->fifo.takedown = nvc0_fifo_takedown; @@ -478,6 +510,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->fifo.destroy_context = nvc0_fifo_destroy_context; engine->fifo.load_context = nvc0_fifo_load_context; engine->fifo.unload_context = nvc0_fifo_unload_context; + engine->fifo.pause = nouveau_fifo_pause_dummy; + engine->fifo.unpause = nouveau_fifo_unpause_dummy; engine->display.early_init = nv50_display_early_init; engine->display.late_takedown = nv50_display_late_takedown; engine->display.create = nv50_display_create; @@ -1167,4 +1201,3 @@ bool nouveau_wait_for_idle(struct drm_device *dev) return true; } - diff --git a/drivers/gpu/drm/nouveau/nv50_fifo.c b/drivers/gpu/drm/nouveau/nv50_fifo.c index d3295aa..ea8cc34 100644 --- a/drivers/gpu/drm/nouveau/nv50_fifo.c +++ b/drivers/gpu/drm/nouveau/nv50_fifo.c @@ -487,3 +487,21 @@ nv50_fifo_tlb_flush(struct drm_device *dev) { nv50_vm_flush(dev, 5); } + +int +nv50_fifo_pause(struct drm_device *dev) +{ + nv_wr32(dev, NV50_PFIFO_FREEZE, 1); + if (!nouveau_wait_until(dev, 2000000000ULL, NV50_PFIFO_FREEZE, + 0x10, 0x10)) { + NV_ERROR(dev, "PFIFO freeze fail!\n"); + return -EIO; + } + return 0; +} + +void +nv50_fifo_unpause(struct drm_device *dev) +{ + nv_wr32(dev, NV50_PFIFO_FREEZE, 0); +} diff --git a/drivers/gpu/drm/nouveau/nv50_graph.c b/drivers/gpu/drm/nouveau/nv50_graph.c index e0f5294..9c0543b 100644 --- a/drivers/gpu/drm/nouveau/nv50_graph.c +++ b/drivers/gpu/drm/nouveau/nv50_graph.c @@ -507,3 +507,49 @@ nv86_graph_tlb_flush(struct drm_device *dev) nv_mask(dev, 0x400500, 0x00000001, 0x00000001); spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags); } + +int +nv50_graph_pause(struct drm_device *dev) +{ + uint64_t start; + /* initial guess... */ + uint32_t mask380 = 0xffffffff; + uint32_t mask384 = 0xffffffff; + uint32_t mask388 = 0xffffffff; + uint32_t mask700 = 0x00000001; + + start = nv04_timer_read(dev); + nv_wr32(dev, NV50_PGRAPH_CONTROL, 0x10000); + while ((nv_rd32(dev, 0x400380) & mask380) || + (nv_rd32(dev, 0x400384) & mask384) || + (nv_rd32(dev, 0x400388) & mask388) || + (nv_rd32(dev, NV50_PGRAPH_STATUS) & mask700)) { + if (nv04_timer_read(dev) - start >= 10000000) { + /* 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!\n", + nv_rd32(dev, 0x400380), + nv_rd32(dev, 0x400384), + nv_rd32(dev, 0x400388), + nv_rd32(dev, NV50_PGRAPH_STATUS)); + + if (nv_rd32(dev, NV50_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)); + + nv50_graph_unpause(dev); + return -EIO; + } + } + return 0; +} + +void +nv50_graph_unpause(struct drm_device *dev) +{ + nv_wr32(dev, NV50_PGRAPH_CONTROL, 0x10001); +} diff --git a/drivers/gpu/drm/nouveau/nv50_pm.c b/drivers/gpu/drm/nouveau/nv50_pm.c index adc2ec7..f8a35ea 100644 --- a/drivers/gpu/drm/nouveau/nv50_pm.c +++ b/drivers/gpu/drm/nouveau/nv50_pm.c @@ -26,11 +26,13 @@ #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 pll_lims pll; + struct pms_ucode ucode; enum pll_types type; + struct pll_lims pll; int N, M, P; }; @@ -61,14 +63,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, 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) { @@ -76,28 +84,98 @@ nv50_pm_clock_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl, return (ret == -ENOENT) ? NULL : ERR_PTR(ret); } - ret = nv50_calc_pll(dev, &state->pll, khz, &state->N, &state->M, - &dummy, &dummy, &state->P); + ret = nv50_calc_pll(dev, &state->pll, khz, &state->N, + &state->M, &i, &i, &state->P); if (ret < 0) { kfree(state); 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); + if (crtc_mask) { + pms_op5f(pms, crtc_mask, 0x00); + pms_op5f(pms, crtc_mask, 0x01); + } + switch (state->type) { + case PLL_MEMORY: + if (dev_priv->chipset >= 0x92) + pms_wr32(pms, 0x611200, 0x00003300); + + pms_wr32(pms, 0x002504, 0x00000001); + pms_unkn(pms, 0x06); + pms_unkn(pms, 0xb0); + pms_op5f(pms, 0x00, 0x01); + + pms_wr32(pms, 0x100210, 0x00000000); + pms_wr32(pms, 0x1002dc, 0x00000001); + pms_wr32(pms, state->pll.reg + 0, reg0_old | 0x00000200); + pms_wr32(pms, state->pll.reg + 4, (state->N << 8) | state->M); + pms_wr32(pms, state->pll.reg + 0, reg0_new | 0x00000200); + pms_unkn(pms, 0x0d); + pms_unkn(pms, 0x0a); + pms_wr32(pms, state->pll.reg + 0, reg0_new); + pms_wr32(pms, 0x1002dc, 0x00000000); + pms_wr32(pms, 0x100210, 0x80000000); + pms_unkn(pms, 0x07); + + pms_unkn(pms, 0x09); + pms_unkn(pms, 0x05); + pms_unkn(pms, 0x0b); + + pms_unkn(pms, 0xd0); + pms_op5f(pms, 0x00, 0x00); + if (dev_priv->chipset >= 0x92) + pms_wr32(pms, 0x611200, 0x00003300); + pms_wr32(pms, 0x002504, 0x00000000); + break; + default: + pms_unkn(pms, 0xb0); + pms_op5f(pms, 0x00, 0x01); + + 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); + pms_op5f(pms, 0x00, 0x00); + 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; - int N = state->N; - int M = state->M; - int P = state->P; + int i; + /* execute mem scripts from the M bit table if needed */ if (state->type == PLL_MEMORY && perflvl->memscript && bit_table(dev, 'M', &BIT_M) == 0 && BIT_M.version == 1 && BIT_M.length >= 0x0b) { @@ -114,19 +192,69 @@ 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); - } + if (dev_priv->chipset < 0x90) { + pms_data = 0x001400; + pms_kick = 0x00000003; + } else { + pms_data = 0x080000; + pms_kick = 0x00000001; + } - 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); + /* 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); - if (state->type == PLL_MEMORY) { - nv_wr32(dev, 0x1002dc, 0); - nv_wr32(dev, 0x100210, 0x80000000); + 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); + nv_mask(dev, 0x100200, 0x00000000, 0x00000800); + } else { + u32 reg0; + + reg0 = nv_rd32(dev, state->pll.reg + 0) & 0xfff8ffff; + reg0 |= 0x80000000 | (state->P << 16); + + /* set the PLL supervisor as needed */ + if (state->type == PLL_UNK05) + nv_mask(dev, 0xc040, 1 << 8 | 1 << 11, 0); + else if (state->type == PLL_CORE || state->type == PLL_SHADER) + nv_mask(dev, 0xc040, 1 << 5 | 1 << 4, 1 << 20); + + /* set REG0 */ + nv_wr32(dev, state->pll.reg + 0, reg0); + + /* reset the PLL supervisor. This may not be needed */ + if (state->type == PLL_UNK05) + nv_mask(dev, 0xc040, 1 << 8 | 1 << 11, 0); + else if (state->type == PLL_CORE || state->type == PLL_SHADER) + nv_mask(dev, 0xc040, 1 << 5 | 1 << 4, 1 << 20); + + /* set REG1 */ + nv_wr32(dev, state->pll.reg + 4, (state->N << 8) | state->M); } kfree(state); -- 1.7.3.2 [-- Attachment #3: test_mode_changes.sh --] [-- Type: application/x-shellscript, Size: 171 bytes --] [-- Attachment #4: Type: text/plain, Size: 181 bytes --] _______________________________________________ Nouveau mailing list Nouveau-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW@public.gmane.org http://lists.freedesktop.org/mailman/listinfo/nouveau ^ permalink raw reply related [flat|nested] 7+ messages in thread
end of thread, other threads:[~2010-11-03 1:32 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-09-29 11:25 Add pause/unpause methods for PFIFO & PGRAPH. Use them to get stable clock changes Martin Peres
[not found] ` <4CA3223F.9030503-GANU6spQydw@public.gmane.org>
2010-09-30 17:14 ` Martin Peres
[not found] ` <4CA4C57E.5090400-GANU6spQydw@public.gmane.org>
2010-10-07 1:33 ` Martin Peres
[not found] ` <4CAD2383.50907-GANU6spQydw@public.gmane.org>
2010-10-07 15:38 ` Martin Peres
2010-10-07 15:38 ` Martin Peres
[not found] ` <4CADE982.7080309-GANU6spQydw@public.gmane.org>
2010-10-28 16:34 ` Martin Peres
[not found] ` <4CC9A605.5050700-GANU6spQydw@public.gmane.org>
2010-11-03 1:32 ` [To developers and hardcore NV50+ users] Experimental patch to get safer " 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.