diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.c b/drivers/gpu/drm/nouveau/nouveau_pm.c index 4df8e0090df399c0d3fe65687f27cf44335c6d70..c6ebf693ffb8f6edafe8e70fb0ab1e1974b897c3 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.c +++ b/drivers/gpu/drm/nouveau/nouveau_pm.c @@ -97,39 +97,62 @@ nouveau_pwmfan_set(struct drm_device *dev, int percent) } static int -nouveau_pm_perflvl_set(struct drm_device *dev, struct nouveau_pm_level *perflvl) +nouveau_pm_perflvl_aux(struct drm_device *dev, struct nouveau_pm_level *perflvl, + struct nouveau_pm_level *a, struct nouveau_pm_level *b) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_pm_engine *pm = &dev_priv->engine.pm; - void *state; int ret; - if (perflvl == pm->cur) - return 0; - /*XXX: not on all boards, we should control based on temperature * on recent boards.. or maybe on some other factor we don't * know about? */ - if (perflvl->fanspeed) { + if (a->fanspeed && b->fanspeed && b->fanspeed > a->fanspeed) { ret = nouveau_pwmfan_set(dev, perflvl->fanspeed); - if (ret && ret != -ENODEV) - NV_ERROR(dev, "set fanspeed failed: %d\n", ret); + if (ret && ret != -ENODEV) { + NV_ERROR(dev, "fanspeed set failed: %d\n", ret); + return ret; + } } - 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->volt_min, ret); + if (pm->voltage.supported && pm->voltage_set) { + if (a->volt_min && b->volt_min && b->volt_min > a->volt_min) { + ret = pm->voltage_set(dev, perflvl->volt_min); + if (ret) { + NV_ERROR(dev, "voltage set failed: %d\n", ret); + return ret; + } } } + return 0; +} + +static int +nouveau_pm_perflvl_set(struct drm_device *dev, struct nouveau_pm_level *perflvl) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_pm_engine *pm = &dev_priv->engine.pm; + void *state; + int ret; + + if (perflvl == pm->cur) + return 0; + + ret = nouveau_pm_perflvl_aux(dev, perflvl, pm->cur, perflvl); + if (ret) + return ret; + state = pm->clocks_pre(dev, perflvl); if (IS_ERR(state)) return PTR_ERR(state); pm->clocks_set(dev, state); + ret = nouveau_pm_perflvl_aux(dev, perflvl, perflvl, pm->cur); + if (ret) + return ret; + pm->cur = perflvl; return 0; }