diff mbox

drm/nvc0-: Fix voltage obtained from vbios.

Message ID 52C58FEA.7080702@canonical.com (mailing list archive)
State New, archived
Headers show

Commit Message

Maarten Lankhorst Jan. 2, 2014, 4:12 p.m. UTC
Coefficients are based on the formula:

uV = 0.1 * arg[0] + 150.5 * arg[1] + 22.65025 * arg[2]

It seems to be rounded downwards. I have no idea why the voltage isn't
specified in the bios directly.

Signed-off-by: Maarten Lankhorst <maarten.lankhorst@canonical.com>
----
diff mbox

Patch

diff --git a/drivers/gpu/drm/nouveau/core/subdev/bios/vmap.c b/drivers/gpu/drm/nouveau/core/subdev/bios/vmap.c
index f343a1b060e8..0a18f9496103 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/bios/vmap.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/bios/vmap.c
@@ -87,14 +87,25 @@  nvbios_vmap_entry_parse(struct nouveau_bios *bios, int idx, u8 *ver, u8 *len,
 	u16 vmap = nvbios_vmap_entry(bios, idx, ver, len);
 	memset(info, 0x00, sizeof(*info));
 	switch (!!vmap * *ver) {
-	case 0x10:
+	case 0x10: {
+		s32 accum, b, c;
+
 		info->link   = 0xff;
 		info->min    = nv_ro32(bios, vmap + 0x00);
 		info->max    = nv_ro32(bios, vmap + 0x04);
-		info->arg[0] = nv_ro32(bios, vmap + 0x08);
-		info->arg[1] = nv_ro32(bios, vmap + 0x0c);
-		info->arg[2] = nv_ro32(bios, vmap + 0x10);
+
+		accum = nv_ro32(bios, vmap + 0x08);
+		b = nv_ro32(bios, vmap + 0x0c);
+		c = nv_ro32(bios, vmap + 0x10);
+
+		accum += b * 1505;
+		accum += (c * 453 / 2) + c / 400;
+		accum /= 10;
+
+		if (accum > info->min)
+			info->min = min((u32)accum, info->max);
 		break;
+	}
 	case 0x20:
 		info->unk0   = nv_ro08(bios, vmap + 0x00);
 		info->link   = nv_ro08(bios, vmap + 0x01);
diff --git a/drivers/gpu/drm/nouveau/core/subdev/volt/base.c b/drivers/gpu/drm/nouveau/core/subdev/volt/base.c
index 32794a999106..7bf716b048bd 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/volt/base.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/volt/base.c
@@ -50,12 +50,23 @@  nouveau_volt_set(struct nouveau_volt *volt, u32 uv)
 {
 	if (volt->vid_set) {
 		int i, ret = -EINVAL;
+		u32 best_uv = INT_MAX, best_vid = 0;
+
 		for (i = 0; i < volt->vid_nr; i++) {
-			if (volt->vid[i].uv == uv) {
-				ret = volt->vid_set(volt, volt->vid[i].vid);
-				nv_debug(volt, "set %duv: %d\n", uv, ret);
+			s32 delta = volt->vid[i].uv - uv;
+
+			if (delta < 0 || best_uv < volt->vid[i].uv)
+				continue;
+
+			best_uv = volt->vid[i].uv;
+			best_vid = volt->vid[i].vid;
+			if (!delta)
 				break;
-			}
+		}
+
+		if (best_uv < INT_MAX) {
+			ret = volt->vid_set(volt, best_vid);
+			nv_debug(volt, "set %duv from %duv: %d\n", best_uv, uv, ret);
 		}
 		return ret;
 	}