drm/nouveau/pm: add support for parsing perflvl voltage on fermi chips
authorBen Skeggs <bskeggs@redhat.com>
Thu, 9 Jun 2011 06:57:07 +0000 (16:57 +1000)
committerBen Skeggs <bskeggs@redhat.com>
Tue, 20 Sep 2011 06:01:14 +0000 (16:01 +1000)
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
drivers/gpu/drm/nouveau/nouveau_drv.h
drivers/gpu/drm/nouveau/nouveau_perf.c
drivers/gpu/drm/nouveau/nouveau_pm.c

index 39d6bb313babc9d7b252a6fd7c24c9b78aab2193..84a19a5fc553d0f4d696dc0b33101bd6bae720b1 100644 (file)
@@ -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;
index 117ce16f3580b0b53ce2e8dc981d3db572815b9b..18d1d995b53a35da6c56a62c411b1c1f761ae61a 100644 (file)
@@ -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;
index f5703ef685182bb31ad0c67f55e24c69a3d45958..cab576b2f15e5c8ed11aacf3e33ae0eedb5ad1f1 100644 (file)
@@ -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)