drm/nouveau/clk: Respect voltage limits in nvkm_cstate_prog
authorKarol Herbst <karolherbst@gmail.com>
Sat, 16 Jul 2016 13:26:25 +0000 (15:26 +0200)
committerBen Skeggs <bskeggs@redhat.com>
Wed, 12 Oct 2016 07:29:24 +0000 (17:29 +1000)
We should never allow to select a cstate which current voltage (depending
on the temperature) is higher than

1. the max volt entries in the voltage map table.
2. what tha gpu actually can volt to.

v3: Use find_best for all cstates before actually trying.
    Add nvkm_cstate_get function to get cstate by index.
v5: Cstates with voltages lower then min_uv are valid.
    Move nvkm_cstate_get into the previous commit.

Signed-off-by: Karol Herbst <karolherbst@gmail.com>
Reviewed-by: Martin Peres <martin.peres@free.fr>
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
drivers/gpu/drm/nouveau/include/nvkm/subdev/volt.h
drivers/gpu/drm/nouveau/nvkm/subdev/clk/base.c
drivers/gpu/drm/nouveau/nvkm/subdev/volt/base.c

index 6fd933d..441f6e7 100644 (file)
@@ -27,6 +27,7 @@ struct nvkm_volt {
        u8 max2_id;
 };
 
+int nvkm_volt_map(struct nvkm_volt *volt, u8 id, u8 temperature);
 int nvkm_volt_map_min(struct nvkm_volt *volt, u8 id);
 int nvkm_volt_get(struct nvkm_volt *);
 int nvkm_volt_set_id(struct nvkm_volt *, u8 id, u8 min_id, u8 temp,
index 688c908..60392b2 100644 (file)
@@ -74,6 +74,57 @@ nvkm_clk_adjust(struct nvkm_clk *clk, bool adjust,
 /******************************************************************************
  * C-States
  *****************************************************************************/
+static bool
+nvkm_cstate_valid(struct nvkm_clk *clk, struct nvkm_cstate *cstate,
+                 u32 max_volt, int temp)
+{
+       struct nvkm_volt *volt = clk->subdev.device->volt;
+       int voltage;
+
+       if (!volt)
+               return true;
+
+       voltage = nvkm_volt_map(volt, cstate->voltage, temp);
+       if (voltage < 0)
+               return false;
+       return voltage <= min(max_volt, volt->max_uv);
+}
+
+static struct nvkm_cstate *
+nvkm_cstate_find_best(struct nvkm_clk *clk, struct nvkm_pstate *pstate,
+                     struct nvkm_cstate *start)
+{
+       struct nvkm_device *device = clk->subdev.device;
+       struct nvkm_volt *volt = device->volt;
+       struct nvkm_cstate *cstate;
+       int max_volt;
+
+       if (!pstate || !start)
+               return NULL;
+
+       if (!volt)
+               return start;
+
+       max_volt = volt->max_uv;
+       if (volt->max0_id != 0xff)
+               max_volt = min(max_volt,
+                              nvkm_volt_map(volt, volt->max0_id, clk->temp));
+       if (volt->max1_id != 0xff)
+               max_volt = min(max_volt,
+                              nvkm_volt_map(volt, volt->max1_id, clk->temp));
+       if (volt->max2_id != 0xff)
+               max_volt = min(max_volt,
+                              nvkm_volt_map(volt, volt->max2_id, clk->temp));
+
+       for (cstate = start; &cstate->head != &pstate->list;
+            cstate = list_entry(cstate->head.prev, typeof(*cstate), head)) {
+               if (nvkm_cstate_valid(clk, cstate, max_volt, clk->temp))
+                       break;
+       }
+
+       return cstate;
+}
+
 static struct nvkm_cstate *
 nvkm_cstate_get(struct nvkm_clk *clk, struct nvkm_pstate *pstate, int cstatei)
 {
@@ -101,6 +152,7 @@ nvkm_cstate_prog(struct nvkm_clk *clk, struct nvkm_pstate *pstate, int cstatei)
 
        if (!list_empty(&pstate->list)) {
                cstate = nvkm_cstate_get(clk, pstate, cstatei);
+               cstate = nvkm_cstate_find_best(clk, pstate, cstate);
        } else {
                cstate = &pstate->base;
        }
index 40ba088..5e07bd3 100644 (file)
@@ -88,7 +88,7 @@ nvkm_volt_map_min(struct nvkm_volt *volt, u8 id)
        return id ? id * 10000 : -ENODEV;
 }
 
-static int
+int
 nvkm_volt_map(struct nvkm_volt *volt, u8 id, u8 temp)
 {
        struct nvkm_bios *bios = volt->subdev.device->bios;