mirror of
https://github.com/torvalds/linux.git
synced 2024-12-22 02:52:56 +00:00
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:
parent
085028ce3b
commit
2d85bc8855
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user