summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBen Skeggs <bskeggs@redhat.com>2012-01-23 07:12:09 +0400
committerBen Skeggs <bskeggs@redhat.com>2012-03-13 11:08:13 +0400
commit2d85bc8855d2734f5585861b261c5fe7a7ed92c1 (patch)
tree37fdb182217e9adbfe3e526cf8587e081ebf3846
parent085028ce3bf7136c5ab2eeb8bf012024d88905c8 (diff)
downloadlinux-2d85bc8855d2734f5585861b261c5fe7a7ed92c1.tar.xz
drm/nouveau/pm: introduce ram reclocking helper
This will probably result in more lines of code, however, we're going to have at least 3 slightly different implementations of this very soon and I'd rather keep the ram reclocking logic separate from the hw specifics. DDR2/DDR3/GDDR3 implemented thus far, others will be added as necessary. Signed-off-by: Ben Skeggs <bskeggs@redhat.com> Signed-off-by: Martin Peres <martin.peres@labri.fr>
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_mem.c96
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_pm.h18
2 files changed, 114 insertions, 0 deletions
diff --git a/drivers/gpu/drm/nouveau/nouveau_mem.c b/drivers/gpu/drm/nouveau/nouveau_mem.c
index cc46811e2d37..cf82f17cf03a 100644
--- a/drivers/gpu/drm/nouveau/nouveau_mem.c
+++ b/drivers/gpu/drm/nouveau/nouveau_mem.c
@@ -934,6 +934,102 @@ nouveau_mem_timing_read(struct drm_device *dev, struct nouveau_pm_memtiming *t)
}
int
+nouveau_mem_exec(struct nouveau_mem_exec_func *exec,
+ struct nouveau_pm_level *perflvl)
+{
+ struct drm_nouveau_private *dev_priv = exec->dev->dev_private;
+ struct nouveau_pm_memtiming *info = &perflvl->timing;
+ u32 tMRD = 1000, tCKSRE = 0, tCKSRX = 0, tXS = 0, tDLLK = 0;
+ u32 mr[3] = { info->mr[0], info->mr[1], info->mr[2] };
+ u32 mr1_dlloff;
+
+ switch (dev_priv->vram_type) {
+ case NV_MEM_TYPE_DDR2:
+ tDLLK = 2000;
+ mr1_dlloff = 0x00000001;
+ break;
+ case NV_MEM_TYPE_DDR3:
+ tDLLK = 12000;
+ mr1_dlloff = 0x00000001;
+ break;
+ case NV_MEM_TYPE_GDDR3:
+ tDLLK = 40000;
+ mr1_dlloff = 0x00000040;
+ break;
+ default:
+ NV_ERROR(exec->dev, "cannot reclock unsupported memtype\n");
+ return -ENODEV;
+ }
+
+ /* fetch current MRs */
+ switch (dev_priv->vram_type) {
+ case NV_MEM_TYPE_DDR3:
+ mr[2] = exec->mrg(exec, 2);
+ default:
+ mr[1] = exec->mrg(exec, 1);
+ mr[0] = exec->mrg(exec, 0);
+ break;
+ }
+
+ /* DLL 'on' -> DLL 'off' mode, disable before entering self-refresh */
+ if (!(mr[1] & mr1_dlloff) && (info->mr[1] & mr1_dlloff)) {
+ exec->precharge(exec);
+ exec->mrs (exec, 1, mr[1] | mr1_dlloff);
+ exec->wait(exec, tMRD);
+ }
+
+ /* enter self-refresh mode */
+ exec->precharge(exec);
+ exec->refresh(exec);
+ exec->refresh(exec);
+ exec->refresh_auto(exec, false);
+ exec->refresh_self(exec, true);
+ exec->wait(exec, tCKSRE);
+
+ /* modify input clock frequency */
+ exec->clock_set(exec);
+
+ /* exit self-refresh mode */
+ exec->wait(exec, tCKSRX);
+ exec->precharge(exec);
+ exec->refresh_self(exec, false);
+ exec->refresh_auto(exec, true);
+ exec->wait(exec, tXS);
+
+ /* update MRs */
+ if (mr[2] != info->mr[2]) {
+ exec->mrs (exec, 2, info->mr[2]);
+ exec->wait(exec, tMRD);
+ }
+
+ if (mr[1] != info->mr[1]) {
+ exec->mrs (exec, 1, info->mr[1]);
+ exec->wait(exec, tMRD);
+ }
+
+ if (mr[0] != info->mr[0]) {
+ exec->mrs (exec, 0, info->mr[0]);
+ exec->wait(exec, tMRD);
+ }
+
+ /* update PFB timing registers */
+ exec->timing_set(exec);
+
+ /* DLL reset */
+ if (!(info->mr[1] & mr1_dlloff)) {
+ exec->mrs (exec, 0, info->mr[0] | 0x00000100);
+ exec->wait(exec, tMRD);
+ exec->mrs (exec, 0, info->mr[0] | 0x00000000);
+ exec->wait(exec, tMRD);
+ exec->wait(exec, tDLLK);
+ if (dev_priv->vram_type == NV_MEM_TYPE_GDDR3)
+ exec->precharge(exec);
+ }
+
+ return 0;
+}
+
+int
nouveau_mem_vbios_type(struct drm_device *dev)
{
struct bit_entry M;
diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.h b/drivers/gpu/drm/nouveau/nouveau_pm.h
index 9e7ad33aa091..5a61f05f49da 100644
--- a/drivers/gpu/drm/nouveau/nouveau_pm.h
+++ b/drivers/gpu/drm/nouveau/nouveau_pm.h
@@ -25,6 +25,24 @@
#ifndef __NOUVEAU_PM_H__
#define __NOUVEAU_PM_H__
+struct nouveau_mem_exec_func {
+ struct drm_device *dev;
+ void (*precharge)(struct nouveau_mem_exec_func *);
+ void (*refresh)(struct nouveau_mem_exec_func *);
+ void (*refresh_auto)(struct nouveau_mem_exec_func *, bool);
+ void (*refresh_self)(struct nouveau_mem_exec_func *, bool);
+ void (*wait)(struct nouveau_mem_exec_func *, u32 nsec);
+ u32 (*mrg)(struct nouveau_mem_exec_func *, int mr);
+ void (*mrs)(struct nouveau_mem_exec_func *, int mr, u32 data);
+ void (*clock_set)(struct nouveau_mem_exec_func *);
+ void (*timing_set)(struct nouveau_mem_exec_func *);
+ void *priv;
+};
+
+/* nouveau_mem.c */
+int nouveau_mem_exec(struct nouveau_mem_exec_func *,
+ struct nouveau_pm_level *);
+
/* nouveau_pm.c */
int nouveau_pm_init(struct drm_device *dev);
void nouveau_pm_fini(struct drm_device *dev);