Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2d85bc88 authored by Ben Skeggs's avatar Ben Skeggs
Browse files

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: default avatarBen Skeggs <bskeggs@redhat.com>
Signed-off-by: default avatarMartin Peres <martin.peres@labri.fr>
parent 085028ce
Loading
Loading
Loading
Loading
+96 −0
Original line number Diff line number Diff line
@@ -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)
{
+18 −0
Original line number Diff line number Diff line
@@ -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);