[Nouveau] [PATCH 10/10] nv50: WIP: initial import of power save magic.
Maxim Levitsky
maximlevitsky at gmail.com
Sun Oct 9 13:58:40 PDT 2011
NOT-signed-off-by: Maxim Levitsky <maximlevitsky at gmail.com>
---
drivers/gpu/drm/nouveau/nv50_dac.c | 47 +++++++++++++++++++++++++++-----
drivers/gpu/drm/nouveau/nv50_display.c | 2 +-
drivers/gpu/drm/nouveau/nv50_fb.c | 9 ++++++
drivers/gpu/drm/nouveau/nv50_mc.c | 11 +++++++
4 files changed, 61 insertions(+), 8 deletions(-)
diff --git a/drivers/gpu/drm/nouveau/nv50_dac.c b/drivers/gpu/drm/nouveau/nv50_dac.c
index 808f3ec..8b5a40d 100644
--- a/drivers/gpu/drm/nouveau/nv50_dac.c
+++ b/drivers/gpu/drm/nouveau/nv50_dac.c
@@ -70,10 +70,12 @@ nv50_dac_detect(struct drm_encoder *encoder, struct drm_connector *connector)
struct drm_device *dev = encoder->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
enum drm_connector_status status = connector_status_disconnected;
- uint32_t dpms_state, load_pattern, load_state;
+ uint32_t dpms_state, load_pattern, load_state, clk;
int or = nv_encoder->or;
- nv_wr32(dev, NV50_PDISPLAY_DAC_CLK_CTRL1(or), 0x00000001);
+ clk = nv_rd32(dev, NV50_PDISPLAY_DAC_CLK_CTRL1(or));
+ nv_wr32(dev, NV50_PDISPLAY_DAC_CLK_CTRL1(or), 0x01);
+
dpms_state = nv_rd32(dev, NV50_PDISPLAY_DAC_DPMS_CTRL(or));
nv_wr32(dev, NV50_PDISPLAY_DAC_DPMS_CTRL(or),
@@ -115,6 +117,8 @@ nv50_dac_detect(struct drm_encoder *encoder, struct drm_connector *connector)
else
NV_DEBUG_KMS(dev, "Load was not detected on output with or %d\n", or);
+ nv_wr32(dev, NV50_PDISPLAY_DAC_CLK_CTRL1(or), clk);
+
return status;
}
@@ -123,7 +127,7 @@ nv50_dac_dpms(struct drm_encoder *encoder, int mode)
{
struct drm_device *dev = encoder->dev;
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
- uint32_t val;
+ uint32_t val, clk;
int or = nv_encoder->or;
NV_DEBUG_KMS(dev, "or %d mode %d\n", or, mode);
@@ -138,9 +142,13 @@ nv50_dac_dpms(struct drm_encoder *encoder, int mode)
}
val = nv_rd32(dev, NV50_PDISPLAY_DAC_DPMS_CTRL(or)) & ~0x7F;
+ clk = nv_rd32(dev, NV50_PDISPLAY_DAC_CLK_CTRL1(or));
- if (mode != DRM_MODE_DPMS_ON)
+ if (mode != DRM_MODE_DPMS_ON) {
val |= NV50_PDISPLAY_DAC_DPMS_CTRL_BLANKED;
+ clk |= 0x02;
+ } else
+ clk &= ~0x02;
switch (mode) {
case DRM_MODE_DPMS_STANDBY:
@@ -160,6 +168,9 @@ nv50_dac_dpms(struct drm_encoder *encoder, int mode)
nv_wr32(dev, NV50_PDISPLAY_DAC_DPMS_CTRL(or), val |
NV50_PDISPLAY_DAC_DPMS_CTRL_PENDING);
+
+ nv_wr32(dev, NV50_PDISPLAY_DAC_CLK_CTRL1(or), clk);
+
}
static void
@@ -297,6 +308,29 @@ nv50_dac_create(struct drm_connector *connector, struct dcb_entry *entry)
{
struct nouveau_encoder *nv_encoder;
struct drm_encoder *encoder;
+ int type, or;
+
+ switch (entry->type) {
+ case OUTPUT_ANALOG:
+ type = DRM_MODE_ENCODER_DAC;
+ break;
+ case OUTPUT_TV:
+ type = DRM_MODE_ENCODER_TVDAC;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ or = ffs(entry->or) - 1;
+
+ if (type == DRM_MODE_ENCODER_DAC)
+ nv_wr32(connector->dev, NV50_PDISPLAY_DAC_CLK_CTRL1(or), 0x00000003);
+ else
+ nv_wr32(connector->dev, NV50_PDISPLAY_DAC_CLK_CTRL1(or), 0x80000002);
+
+ /* FIXME: add support for TV-out */
+ if (type != DRM_MODE_ENCODER_DAC)
+ return -EINVAL;
nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL);
if (!nv_encoder)
@@ -304,10 +338,9 @@ nv50_dac_create(struct drm_connector *connector, struct dcb_entry *entry)
encoder = to_drm_encoder(nv_encoder);
nv_encoder->dcb = entry;
- nv_encoder->or = ffs(entry->or) - 1;
+ nv_encoder->or = or;
- drm_encoder_init(connector->dev, encoder, &nv50_dac_encoder_funcs,
- DRM_MODE_ENCODER_DAC);
+ drm_encoder_init(connector->dev, encoder, &nv50_dac_encoder_funcs, type);
drm_encoder_helper_add(encoder, &nv50_dac_helper_funcs);
encoder->possible_crtcs = entry->heads;
diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c
index d23ca00..d9dac41 100644
--- a/drivers/gpu/drm/nouveau/nv50_display.c
+++ b/drivers/gpu/drm/nouveau/nv50_display.c
@@ -112,7 +112,6 @@ nv50_display_init(struct drm_device *dev)
for (i = 0; i < 3; i++) {
nv_wr32(dev, NV50_PDISPLAY_DAC_DPMS_CTRL(i), 0x00550000 |
NV50_PDISPLAY_DAC_DPMS_CTRL_PENDING);
- nv_wr32(dev, NV50_PDISPLAY_DAC_CLK_CTRL1(i), 0x00000001);
}
/* The precise purpose is unknown, i suspect it has something to do
@@ -321,6 +320,7 @@ int nv50_display_create(struct drm_device *dev)
nv50_sor_create(connector, entry);
break;
case OUTPUT_ANALOG:
+ case OUTPUT_TV:
nv50_dac_create(connector, entry);
break;
default:
diff --git a/drivers/gpu/drm/nouveau/nv50_fb.c b/drivers/gpu/drm/nouveau/nv50_fb.c
index bdd2afe..fe49500 100644
--- a/drivers/gpu/drm/nouveau/nv50_fb.c
+++ b/drivers/gpu/drm/nouveau/nv50_fb.c
@@ -86,6 +86,15 @@ nv50_fb_init(struct drm_device *dev)
*/
nv_wr32(dev, 0x100c08, priv->r100c08 >> 8);
+ /* Power usage magic */
+ nv_wr32(dev, 0x100000, 0x0000c042);
+ nv_wr32(dev, 0x100004, 0x0000c042);
+ nv_wr32(dev, 0x100008, 0x0000c042);
+ nv_wr32(dev, 0x100b78, 0x0000c042);
+ nv_wr32(dev, 0x100c0c, 0x0000c042);
+ nv_wr32(dev, 0x100d04, 0x0000c042);
+ nv_wr32(dev, 0x100e0c, 0x0000c042);
+
/* This is needed to get meaningful information from 100c90
* on traps. No idea what these values mean exactly. */
switch (dev_priv->chipset) {
diff --git a/drivers/gpu/drm/nouveau/nv50_mc.c b/drivers/gpu/drm/nouveau/nv50_mc.c
index e0a9c3f..76367568 100644
--- a/drivers/gpu/drm/nouveau/nv50_mc.c
+++ b/drivers/gpu/drm/nouveau/nv50_mc.c
@@ -32,6 +32,17 @@ int
nv50_mc_init(struct drm_device *dev)
{
nv_wr32(dev, NV03_PMC_ENABLE, 0xFFFFFFFF);
+
+ /* PPCI+0x150 - some PCIE powersave bit*/
+ nv_mask(dev, 0x088150, 0x00000100, 0x00000000);
+
+ /* PBUS.DEBUG_6 - enable PGRAPH automatic clock gating*/
+ nv_mask(dev, 0x001098, 0x00000020, 0x00000020);
+ nv_mask(dev, 0x001588, 0x00000003, 0x00000001);
+
+ /* PCONTROL+0x40 - disable VP2 clock */
+ nv_mask(dev, 0x00c040, 0x00000F00, 0x00000200);
+
return 0;
}
--
1.7.4.1
More information about the Nouveau
mailing list