Maarten Lankhorst
2014-Jan-02 16:12 UTC
[Nouveau] [PATCH] drm/nvc0-: Fix voltage obtained from vbios.
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 at canonical.com> ---- 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; }