Maxim Levitsky
2012-Jan-21 22:14 UTC
[Nouveau] [NOT for merge] Patches that reduce power usage on NV86
This is more or less simplified series of patches that bring power usage on my NV86 close to that of binary blob. Best regards, Maxim Levitsky
Maxim Levitsky
2012-Jan-21 22:14 UTC
[Nouveau] [PATCH 1/4] nv50: major bits of power saving
Enable PCIE power savings and PGRAPH's clock gating. This should be fairy safe to do on NV50 class cards, but should be tested on more that NV86. Also its possible to enable clock gating and otherwice reduce power usage of VDEC, but for now I choose just to turn it off. To be honest, since its usually not used and when it is. it will be probably loaded, these powe saving bits don't matter much Signed-off-by: Maxim Levitsky <maximlevitsky at gmail.com> --- drivers/gpu/drm/nouveau/nv50_mc.c | 8 ++++++++ 1 files changed, 8 insertions(+), 0 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nv50_mc.c b/drivers/gpu/drm/nouveau/nv50_mc.c index e0a9c3f..fd4ef43 100644 --- a/drivers/gpu/drm/nouveau/nv50_mc.c +++ b/drivers/gpu/drm/nouveau/nv50_mc.c @@ -32,6 +32,14 @@ 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); + + /* Enable TP automatic clock gating*/ + nv_mask(dev, 0x001098, 0x00000020, 0x00000020); + nv_mask(dev, 0x001588, 0x00000003, 0x00000001); + return 0; } -- 1.7.5.4
Maxim Levitsky
2012-Jan-21 22:14 UTC
[Nouveau] [PATCH 2/4] nv50: Power down VDEC when reclocking card. This allows to save lot of power and untill support for video decoding is implemented, its best way to make use of this engine
Signed-off-by: Maxim Levitsky <maximlevitsky at gmail.com> --- drivers/gpu/drm/nouveau/nv50_pm.c | 5 +++++ 1 files changed, 5 insertions(+), 0 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nv50_pm.c b/drivers/gpu/drm/nouveau/nv50_pm.c index 0901e56..2527298 100644 --- a/drivers/gpu/drm/nouveau/nv50_pm.c +++ b/drivers/gpu/drm/nouveau/nv50_pm.c @@ -595,6 +595,7 @@ calc_mclk(struct drm_device *dev, hwsq_wr32(hwsq, 0x611200, 0x00003330); /* enable scanout */ hwsq_fini(hwsq); + *pmast = mast; return 0; } @@ -678,6 +679,10 @@ nv50_pm_clocks_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl) } } + /* I don't use vdec - so I disable it */ + mast &= ~0x00000f00; + mast |= 0x00000500; + /* dom6: nfi what this is, but we're limited to various combinations * of the host clock frequency */ -- 1.7.5.4
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 +- 2 files changed, 41 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nv50_dac.c b/drivers/gpu/drm/nouveau/nv50_dac.c index a0f2beb..5cc0fb2 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 @@ -292,6 +303,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) @@ -299,10 +333,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 7ba28e0..c1806cb 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.c +++ b/drivers/gpu/drm/nouveau/nv50_display.c @@ -185,7 +185,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 @@ -359,6 +358,7 @@ 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: -- 1.7.5.4
Maxim Levitsky
2012-Jan-21 22:14 UTC
[Nouveau] [PATCH 4/4] nv50 - unknown and untested bits of memory controller that save around 0.1W
Not-signed-off-by: Maxim Levitsky <maximlevitsky at gmail.com> --- drivers/gpu/drm/nouveau/nv50_fb.c | 11 +++++++++++ 1 files changed, 11 insertions(+), 0 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nv50_fb.c b/drivers/gpu/drm/nouveau/nv50_fb.c index bdd2afe..ed0b5f2 100644 --- a/drivers/gpu/drm/nouveau/nv50_fb.c +++ b/drivers/gpu/drm/nouveau/nv50_fb.c @@ -86,6 +86,17 @@ nv50_fb_init(struct drm_device *dev) */ nv_wr32(dev, 0x100c08, priv->r100c08 >> 8); + /* Power usage magic - 0.1W - verified many times*/ + + 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) { -- 1.7.5.4
Seemingly Similar Threads
- [PATCH 01/10]: nouveau: assorted fixes
- [PATCH] drm/nouveau: use drm debug levels
- [PATCH 1/2] drm/nv50: Make ctxprog wait until interrupt handler is done.
- [PATCH 0/4] Some initial tidy-ups and refactoring
- [PATCH] drm/nouveau/nv50: Reclock when memory was stolen