Commit 3b5565dd authored by Ben Skeggs's avatar Ben Skeggs

drm/nouveau/pm: add support for parsing perflvl voltage on fermi chips

Signed-off-by: default avatarBen Skeggs <bskeggs@redhat.com>
parent a31214ef
...@@ -451,7 +451,8 @@ struct nouveau_pm_level { ...@@ -451,7 +451,8 @@ struct nouveau_pm_level {
u32 unk05; u32 unk05;
u32 unk0a; u32 unk0a;
u32 voltage; /* microvolts */ u32 volt_min; /* microvolts */
u32 volt_max;
u8 fanspeed; u8 fanspeed;
u16 memscript; u16 memscript;
......
...@@ -134,6 +134,49 @@ nouveau_perf_timing(struct drm_device *dev, struct bit_entry *P, ...@@ -134,6 +134,49 @@ nouveau_perf_timing(struct drm_device *dev, struct bit_entry *P,
return &pm->memtimings.timing[entry[1]]; 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 void
nouveau_perf_init(struct drm_device *dev) nouveau_perf_init(struct drm_device *dev)
{ {
...@@ -204,7 +247,7 @@ nouveau_perf_init(struct drm_device *dev) ...@@ -204,7 +247,7 @@ nouveau_perf_init(struct drm_device *dev)
case 0x15: case 0x15:
perflvl->fanspeed = entry[55]; perflvl->fanspeed = entry[55];
if (recordlen > 56) if (recordlen > 56)
perflvl->voltage = entry[56] * 10000; perflvl->volt_min = entry[56];
perflvl->core = ROM32(entry[1]) * 10; perflvl->core = ROM32(entry[1]) * 10;
perflvl->memory = ROM32(entry[5]) * 20; perflvl->memory = ROM32(entry[5]) * 20;
break; break;
...@@ -212,7 +255,7 @@ nouveau_perf_init(struct drm_device *dev) ...@@ -212,7 +255,7 @@ nouveau_perf_init(struct drm_device *dev)
case 0x23: case 0x23:
case 0x24: case 0x24:
perflvl->fanspeed = entry[4]; perflvl->fanspeed = entry[4];
perflvl->voltage = entry[5] * 10000; perflvl->volt_min = entry[5];
perflvl->core = ROM16(entry[6]) * 1000; perflvl->core = ROM16(entry[6]) * 1000;
if (dev_priv->chipset == 0x49 || if (dev_priv->chipset == 0x49 ||
...@@ -224,7 +267,7 @@ nouveau_perf_init(struct drm_device *dev) ...@@ -224,7 +267,7 @@ nouveau_perf_init(struct drm_device *dev)
break; break;
case 0x25: case 0x25:
perflvl->fanspeed = entry[4]; perflvl->fanspeed = entry[4];
perflvl->voltage = entry[5] * 10000; perflvl->volt_min = entry[5];
perflvl->core = ROM16(entry[6]) * 1000; perflvl->core = ROM16(entry[6]) * 1000;
perflvl->shader = ROM16(entry[10]) * 1000; perflvl->shader = ROM16(entry[10]) * 1000;
perflvl->memory = ROM16(entry[12]) * 1000; perflvl->memory = ROM16(entry[12]) * 1000;
...@@ -233,7 +276,7 @@ nouveau_perf_init(struct drm_device *dev) ...@@ -233,7 +276,7 @@ nouveau_perf_init(struct drm_device *dev)
perflvl->memscript = ROM16(entry[2]); perflvl->memscript = ROM16(entry[2]);
case 0x35: case 0x35:
perflvl->fanspeed = entry[6]; perflvl->fanspeed = entry[6];
perflvl->voltage = entry[7] * 10000; perflvl->volt_min = entry[7];
perflvl->core = ROM16(entry[8]) * 1000; perflvl->core = ROM16(entry[8]) * 1000;
perflvl->shader = ROM16(entry[10]) * 1000; perflvl->shader = ROM16(entry[10]) * 1000;
perflvl->memory = ROM16(entry[12]) * 1000; perflvl->memory = ROM16(entry[12]) * 1000;
...@@ -243,7 +286,7 @@ nouveau_perf_init(struct drm_device *dev) ...@@ -243,7 +286,7 @@ nouveau_perf_init(struct drm_device *dev)
case 0x40: case 0x40:
#define subent(n) entry[perf[2] + ((n) * perf[3])] #define subent(n) entry[perf[2] + ((n) * perf[3])]
perflvl->fanspeed = 0; /*XXX*/ perflvl->fanspeed = 0; /*XXX*/
perflvl->voltage = entry[2] * 10000; perflvl->volt_min = entry[2];
if (dev_priv->card_type == NV_50) { if (dev_priv->card_type == NV_50) {
perflvl->core = ROM16(subent(0)) & 0xfff; perflvl->core = ROM16(subent(0)) & 0xfff;
perflvl->shader = ROM16(subent(1)) & 0xfff; perflvl->shader = ROM16(subent(1)) & 0xfff;
...@@ -263,8 +306,9 @@ nouveau_perf_init(struct drm_device *dev) ...@@ -263,8 +306,9 @@ nouveau_perf_init(struct drm_device *dev)
} }
/* make sure vid is valid */ /* make sure vid is valid */
if (pm->voltage.supported && perflvl->voltage) { nouveau_perf_voltage(dev, &P, perflvl);
vid = nouveau_volt_vid_lookup(dev, perflvl->voltage); if (pm->voltage.supported && perflvl->volt_min) {
vid = nouveau_volt_vid_lookup(dev, perflvl->volt_min);
if (vid < 0) { if (vid < 0) {
NV_DEBUG(dev, "drop perflvl %d, bad vid\n", i); NV_DEBUG(dev, "drop perflvl %d, bad vid\n", i);
entry += recordlen; entry += recordlen;
......
...@@ -64,11 +64,11 @@ nouveau_pm_perflvl_set(struct drm_device *dev, struct nouveau_pm_level *perflvl) ...@@ -64,11 +64,11 @@ nouveau_pm_perflvl_set(struct drm_device *dev, struct nouveau_pm_level *perflvl)
if (perflvl == pm->cur) if (perflvl == pm->cur)
return 0; return 0;
if (pm->voltage.supported && pm->voltage_set && perflvl->voltage) { if (pm->voltage.supported && pm->voltage_set && perflvl->volt_min) {
ret = pm->voltage_set(dev, perflvl->voltage); ret = pm->voltage_set(dev, perflvl->volt_min);
if (ret) { if (ret) {
NV_ERROR(dev, "voltage_set %d failed: %d\n", 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) ...@@ -146,8 +146,10 @@ nouveau_pm_perflvl_get(struct drm_device *dev, struct nouveau_pm_level *perflvl)
if (pm->voltage.supported && pm->voltage_get) { if (pm->voltage.supported && pm->voltage_get) {
ret = pm->voltage_get(dev); ret = pm->voltage_get(dev);
if (ret > 0) if (ret > 0) {
perflvl->voltage = ret; perflvl->volt_min = ret;
perflvl->volt_max = ret;
}
} }
return 0; return 0;
...@@ -156,7 +158,7 @@ nouveau_pm_perflvl_get(struct drm_device *dev, struct nouveau_pm_level *perflvl) ...@@ -156,7 +158,7 @@ nouveau_pm_perflvl_get(struct drm_device *dev, struct nouveau_pm_level *perflvl)
static void static void
nouveau_pm_perflvl_info(struct nouveau_pm_level *perflvl, char *ptr, int len) 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'; c[0] = '\0';
if (perflvl->core) if (perflvl->core)
...@@ -167,8 +169,14 @@ nouveau_pm_perflvl_info(struct nouveau_pm_level *perflvl, char *ptr, int len) ...@@ -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); snprintf(s, sizeof(s), " shader %dMHz", perflvl->shader / 1000);
v[0] = '\0'; v[0] = '\0';
if (perflvl->voltage) if (perflvl->volt_min && perflvl->volt_min != perflvl->volt_max) {
snprintf(v, sizeof(v), " voltage %dmV", perflvl->voltage / 1000); 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'; f[0] = '\0';
if (perflvl->fanspeed) if (perflvl->fanspeed)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment