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>
This commit is contained in:
Ben Skeggs 2012-01-23 13:12:09 +10:00
parent 085028ce3b
commit 2d85bc8855
2 changed files with 114 additions and 0 deletions

View File

@ -933,6 +933,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)
{

View File

@ -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);