From 3b5565ddfd8fe71f6470a5d240a6bb50ba90d4ff Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Thu, 9 Jun 2011 16:57:07 +1000 Subject: [PATCH] drm/nouveau/pm: add support for parsing perflvl voltage on fermi chips Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_drv.h | 3 +- drivers/gpu/drm/nouveau/nouveau_perf.c | 58 ++++++++++++++++++++++++++++++---- drivers/gpu/drm/nouveau/nouveau_pm.c | 24 +++++++++----- 3 files changed, 69 insertions(+), 16 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index 39d6bb313bab..84a19a5fc553 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -451,7 +451,8 @@ struct nouveau_pm_level { u32 unk05; u32 unk0a; - u32 voltage; /* microvolts */ + u32 volt_min; /* microvolts */ + u32 volt_max; u8 fanspeed; u16 memscript; diff --git a/drivers/gpu/drm/nouveau/nouveau_perf.c b/drivers/gpu/drm/nouveau/nouveau_perf.c index 117ce16f3580..18d1d995b53a 100644 --- a/drivers/gpu/drm/nouveau/nouveau_perf.c +++ b/drivers/gpu/drm/nouveau/nouveau_perf.c @@ -134,6 +134,49 @@ nouveau_perf_timing(struct drm_device *dev, struct bit_entry *P, return &pm->memtimings.timing[entry[1]]; } +static void +nouveau_perf_voltage(struct drm_device *dev, struct bit_entry *P, + struct nouveau_pm_level *perflvl) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nvbios *bios = &dev_priv->vbios; + u8 *vmap; + int id; + + id = perflvl->volt_min; + perflvl->volt_min = 0; + + /* pre-fermi vbios stores the voltage level directly in the + * perflvl entry as a multiple of 10mV + */ + if (dev_priv->card_type < NV_C0) { + perflvl->volt_min = id * 10000; + perflvl->volt_max = perflvl->volt_min; + return; + } + + /* from fermi onwards, the perflvl stores an index into yet another + * vbios table containing a min/max voltage value for the perflvl + */ + if (P->version != 2 || P->length < 34) { + NV_DEBUG(dev, "where's our volt map table ptr? %d %d\n", + P->version, P->length); + return; + } + + vmap = ROMPTR(bios, P->data[32]); + if (!vmap) { + NV_DEBUG(dev, "volt map table pointer invalid\n"); + return; + } + + if (id < vmap[3]) { + vmap += vmap[1] + (vmap[2] * id); + perflvl->volt_min = ROM32(vmap[0]); + perflvl->volt_max = ROM32(vmap[4]); + } +} + void nouveau_perf_init(struct drm_device *dev) { @@ -204,7 +247,7 @@ nouveau_perf_init(struct drm_device *dev) case 0x15: perflvl->fanspeed = entry[55]; if (recordlen > 56) - perflvl->voltage = entry[56] * 10000; + perflvl->volt_min = entry[56]; perflvl->core = ROM32(entry[1]) * 10; perflvl->memory = ROM32(entry[5]) * 20; break; @@ -212,7 +255,7 @@ nouveau_perf_init(struct drm_device *dev) case 0x23: case 0x24: perflvl->fanspeed = entry[4]; - perflvl->voltage = entry[5] * 10000; + perflvl->volt_min = entry[5]; perflvl->core = ROM16(entry[6]) * 1000; if (dev_priv->chipset == 0x49 || @@ -224,7 +267,7 @@ nouveau_perf_init(struct drm_device *dev) break; case 0x25: perflvl->fanspeed = entry[4]; - perflvl->voltage = entry[5] * 10000; + perflvl->volt_min = entry[5]; perflvl->core = ROM16(entry[6]) * 1000; perflvl->shader = ROM16(entry[10]) * 1000; perflvl->memory = ROM16(entry[12]) * 1000; @@ -233,7 +276,7 @@ nouveau_perf_init(struct drm_device *dev) perflvl->memscript = ROM16(entry[2]); case 0x35: perflvl->fanspeed = entry[6]; - perflvl->voltage = entry[7] * 10000; + perflvl->volt_min = entry[7]; perflvl->core = ROM16(entry[8]) * 1000; perflvl->shader = ROM16(entry[10]) * 1000; perflvl->memory = ROM16(entry[12]) * 1000; @@ -243,7 +286,7 @@ nouveau_perf_init(struct drm_device *dev) case 0x40: #define subent(n) entry[perf[2] + ((n) * perf[3])] perflvl->fanspeed = 0; /*XXX*/ - perflvl->voltage = entry[2] * 10000; + perflvl->volt_min = entry[2]; if (dev_priv->card_type == NV_50) { perflvl->core = ROM16(subent(0)) & 0xfff; perflvl->shader = ROM16(subent(1)) & 0xfff; @@ -263,8 +306,9 @@ nouveau_perf_init(struct drm_device *dev) } /* make sure vid is valid */ - if (pm->voltage.supported && perflvl->voltage) { - vid = nouveau_volt_vid_lookup(dev, perflvl->voltage); + nouveau_perf_voltage(dev, &P, perflvl); + if (pm->voltage.supported && perflvl->volt_min) { + vid = nouveau_volt_vid_lookup(dev, perflvl->volt_min); if (vid < 0) { NV_DEBUG(dev, "drop perflvl %d, bad vid\n", i); entry += recordlen; diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.c b/drivers/gpu/drm/nouveau/nouveau_pm.c index f5703ef68518..cab576b2f15e 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.c +++ b/drivers/gpu/drm/nouveau/nouveau_pm.c @@ -64,11 +64,11 @@ nouveau_pm_perflvl_set(struct drm_device *dev, struct nouveau_pm_level *perflvl) if (perflvl == pm->cur) return 0; - if (pm->voltage.supported && pm->voltage_set && perflvl->voltage) { - ret = pm->voltage_set(dev, perflvl->voltage); + if (pm->voltage.supported && pm->voltage_set && perflvl->volt_min) { + ret = pm->voltage_set(dev, perflvl->volt_min); if (ret) { NV_ERROR(dev, "voltage_set %d failed: %d\n", - perflvl->voltage, ret); + perflvl->volt_min, ret); } } @@ -146,8 +146,10 @@ nouveau_pm_perflvl_get(struct drm_device *dev, struct nouveau_pm_level *perflvl) if (pm->voltage.supported && pm->voltage_get) { ret = pm->voltage_get(dev); - if (ret > 0) - perflvl->voltage = ret; + if (ret > 0) { + perflvl->volt_min = ret; + perflvl->volt_max = ret; + } } return 0; @@ -156,7 +158,7 @@ nouveau_pm_perflvl_get(struct drm_device *dev, struct nouveau_pm_level *perflvl) static void nouveau_pm_perflvl_info(struct nouveau_pm_level *perflvl, char *ptr, int len) { - char c[16], s[16], v[16], f[16], t[16]; + char c[16], s[16], v[32], f[16], t[16]; c[0] = '\0'; if (perflvl->core) @@ -167,8 +169,14 @@ nouveau_pm_perflvl_info(struct nouveau_pm_level *perflvl, char *ptr, int len) snprintf(s, sizeof(s), " shader %dMHz", perflvl->shader / 1000); v[0] = '\0'; - if (perflvl->voltage) - snprintf(v, sizeof(v), " voltage %dmV", perflvl->voltage / 1000); + if (perflvl->volt_min && perflvl->volt_min != perflvl->volt_max) { + snprintf(v, sizeof(v), " voltage %dmV-%dmV", + perflvl->volt_min / 1000, perflvl->volt_max / 1000); + } else + if (perflvl->volt_min) { + snprintf(v, sizeof(v), " voltage %dmV", + perflvl->volt_min / 1000); + } f[0] = '\0'; if (perflvl->fanspeed) -- 2.11.0