drm/nouveau/clk: Respect voltage limits in nvkm_cstate_prog

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>
This commit is contained in:
Karol Herbst 2016-07-16 15:26:25 +02:00 committed by Ben Skeggs
parent 0d6f81003e
commit 1f7f3d91ad
3 changed files with 54 additions and 1 deletions

View File

@ -27,6 +27,7 @@ struct nvkm_volt {
u8 max2_id; 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_map_min(struct nvkm_volt *volt, u8 id);
int nvkm_volt_get(struct nvkm_volt *); int nvkm_volt_get(struct nvkm_volt *);
int nvkm_volt_set_id(struct nvkm_volt *, u8 id, u8 min_id, u8 temp, int nvkm_volt_set_id(struct nvkm_volt *, u8 id, u8 min_id, u8 temp,

View File

@ -74,6 +74,57 @@ nvkm_clk_adjust(struct nvkm_clk *clk, bool adjust,
/****************************************************************************** /******************************************************************************
* C-States * 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 * static struct nvkm_cstate *
nvkm_cstate_get(struct nvkm_clk *clk, struct nvkm_pstate *pstate, int cstatei) 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)) { if (!list_empty(&pstate->list)) {
cstate = nvkm_cstate_get(clk, pstate, cstatei); cstate = nvkm_cstate_get(clk, pstate, cstatei);
cstate = nvkm_cstate_find_best(clk, pstate, cstate);
} else { } else {
cstate = &pstate->base; cstate = &pstate->base;
} }

View File

@ -88,7 +88,7 @@ nvkm_volt_map_min(struct nvkm_volt *volt, u8 id)
return id ? id * 10000 : -ENODEV; return id ? id * 10000 : -ENODEV;
} }
static int int
nvkm_volt_map(struct nvkm_volt *volt, u8 id, u8 temp) nvkm_volt_map(struct nvkm_volt *volt, u8 id, u8 temp)
{ {
struct nvkm_bios *bios = volt->subdev.device->bios; struct nvkm_bios *bios = volt->subdev.device->bios;