mirror of
https://github.com/torvalds/linux.git
synced 2024-12-22 10:56:40 +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
|
int
|
||||||
nouveau_mem_vbios_type(struct drm_device *dev)
|
nouveau_mem_vbios_type(struct drm_device *dev)
|
||||||
{
|
{
|
||||||
|
@ -25,6 +25,24 @@
|
|||||||
#ifndef __NOUVEAU_PM_H__
|
#ifndef __NOUVEAU_PM_H__
|
||||||
#define __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 */
|
/* nouveau_pm.c */
|
||||||
int nouveau_pm_init(struct drm_device *dev);
|
int nouveau_pm_init(struct drm_device *dev);
|
||||||
void nouveau_pm_fini(struct drm_device *dev);
|
void nouveau_pm_fini(struct drm_device *dev);
|
||||||
|
Loading…
Reference in New Issue
Block a user