From fa37e8dda2617d48fbc6b17dd6e986e7f4c2bc8b Mon Sep 17 00:00:00 2001 From: Martin Peres Date: Wed, 5 Dec 2012 19:46:35 +1000 Subject: [PATCH] drm/nouveau/fan: obey fan bump/slow periods as defined by vbios v2 (Ben Skeggs): - split from larger patch - fixed to not require alarm resched patch Signed-off-by: Ben Skeggs Signed-off-by: Martin Peres --- .../gpu/drm/nouveau/core/subdev/therm/fan.c | 83 +++++++++++++++++-- .../drm/nouveau/core/subdev/therm/fantog.c | 4 +- .../gpu/drm/nouveau/core/subdev/therm/priv.h | 8 +- 3 files changed, 82 insertions(+), 13 deletions(-) diff --git a/drivers/gpu/drm/nouveau/core/subdev/therm/fan.c b/drivers/gpu/drm/nouveau/core/subdev/therm/fan.c index aed72a2d9198..1a0f86364a6e 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/therm/fan.c +++ b/drivers/gpu/drm/nouveau/core/subdev/therm/fan.c @@ -31,6 +31,71 @@ #include #include +static int +nouveau_fan_update(struct nouveau_fan *fan, bool immediate, int target) +{ + struct nouveau_therm *therm = fan->parent; + struct nouveau_therm_priv *priv = (void *)therm; + struct nouveau_timer *ptimer = nouveau_timer(priv); + unsigned long flags; + int ret = 0; + u32 duty; + + /* update target fan speed, restricting to allowed range */ + spin_lock_irqsave(&fan->lock, flags); + if (target < 0) + target = fan->percent; + target = max_t(u8, target, fan->bios.min_duty); + target = min_t(u8, target, fan->bios.max_duty); + fan->percent = target; + + /* smooth out the fanspeed increase/decrease */ + duty = fan->get(therm); + if (!immediate && duty >= 0) { + /* the constant "3" is a rough approximation taken from + * nvidia's behaviour. + * it is meant to bump the fan speed more incrementally + */ + if (duty < target) + duty = min(duty + 3, (u32) target); + else if (duty > target) + duty = max(duty - 3, (u32) target); + } else { + duty = target; + } + + ret = fan->set(therm, duty); + if (ret) + goto done; + + /* schedule next fan update, if not at target speed already */ + if (list_empty(&fan->alarm.head) && target != duty) { + u16 bump_period = fan->bios.bump_period; + u16 slow_down_period = fan->bios.slow_down_period; + u64 delay; + + if (duty > target) + delay = slow_down_period; + else if (duty == target) + delay = min(bump_period, slow_down_period) ; + else + delay = bump_period; + + ptimer->alarm(ptimer, delay * 1000 * 1000, &fan->alarm); + } + +done: + spin_unlock_irqrestore(&fan->lock, flags); + return ret; +} + +static void +nouveau_fan_alarm(struct nouveau_alarm *alarm) +{ + struct nouveau_fan *fan = container_of(alarm, struct nouveau_fan, alarm); + nouveau_fan_update(fan, false, -1); +} + int nouveau_therm_fan_get(struct nouveau_therm *therm) { @@ -39,19 +104,14 @@ nouveau_therm_fan_get(struct nouveau_therm *therm) } int -nouveau_therm_fan_set(struct nouveau_therm *therm, int percent) +nouveau_therm_fan_set(struct nouveau_therm *therm, bool immediate, int percent) { struct nouveau_therm_priv *priv = (void *)therm; - if (percent < priv->fan->bios.min_duty) - percent = priv->fan->bios.min_duty; - if (percent > priv->fan->bios.max_duty) - percent = priv->fan->bios.max_duty; - if (priv->fan->mode == FAN_CONTROL_NONE) return -EINVAL; - return priv->fan->set(therm, percent); + return nouveau_fan_update(priv->fan, immediate, percent); } int @@ -136,7 +196,7 @@ nouveau_therm_fan_user_set(struct nouveau_therm *therm, int percent) if (priv->fan->mode != FAN_CONTROL_MANUAL) return -EINVAL; - return nouveau_therm_fan_set(therm, percent); + return nouveau_therm_fan_set(therm, true, percent); } void @@ -147,6 +207,8 @@ nouveau_therm_fan_set_defaults(struct nouveau_therm *therm) priv->fan->bios.pwm_freq = 0; priv->fan->bios.min_duty = 0; priv->fan->bios.max_duty = 100; + priv->fan->bios.bump_period = 500; + priv->fan->bios.slow_down_period = 2000; } static void @@ -190,6 +252,11 @@ nouveau_therm_fan_ctor(struct nouveau_therm *therm) if (ret) priv->fan->tach.func = DCB_GPIO_UNUSED; + /* initialise fan bump/slow update handling */ + priv->fan->parent = therm; + nouveau_alarm_init(&priv->fan->alarm, nouveau_fan_alarm); + spin_lock_init(&priv->fan->lock); + /* other random init... */ nouveau_therm_fan_set_defaults(therm); nvbios_perf_fan_parse(bios, &priv->fan->perf); diff --git a/drivers/gpu/drm/nouveau/core/subdev/therm/fantog.c b/drivers/gpu/drm/nouveau/core/subdev/therm/fantog.c index b08e4e2b33e6..7e50f1419ac6 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/therm/fantog.c +++ b/drivers/gpu/drm/nouveau/core/subdev/therm/fantog.c @@ -32,7 +32,6 @@ struct nouveau_fantog_priv { struct nouveau_fan base; - struct nouveau_therm *parent; struct nouveau_alarm alarm; spinlock_t lock; u32 period_us; @@ -43,7 +42,7 @@ struct nouveau_fantog_priv { static void nouveau_fantog_update(struct nouveau_fantog_priv *priv, int percent) { - struct nouveau_therm_priv *tpriv = (void *)priv->parent; + struct nouveau_therm_priv *tpriv = (void *)priv->base.parent; struct nouveau_timer *ptimer = nouveau_timer(tpriv); struct nouveau_gpio *gpio = nouveau_gpio(tpriv); unsigned long flags; @@ -104,7 +103,6 @@ nouveau_fantog_create(struct nouveau_therm *therm, struct dcb_gpio_func *func) if (!priv) return -ENOMEM; - priv->parent = therm; priv->base.type = "toggle"; priv->base.get = nouveau_fantog_get; priv->base.set = nouveau_fantog_set; diff --git a/drivers/gpu/drm/nouveau/core/subdev/therm/priv.h b/drivers/gpu/drm/nouveau/core/subdev/therm/priv.h index 34c85e62685d..fca580f16009 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/therm/priv.h +++ b/drivers/gpu/drm/nouveau/core/subdev/therm/priv.h @@ -34,13 +34,17 @@ #include struct nouveau_fan { + struct nouveau_therm *parent; const char *type; enum nouveau_therm_fan_mode mode; - int percent; struct nvbios_therm_fan bios; struct nvbios_perf_fan perf; + struct nouveau_alarm alarm; + spinlock_t lock; + int percent; + int (*get)(struct nouveau_therm *therm); int (*set)(struct nouveau_therm *therm, int percent); @@ -71,7 +75,7 @@ int nouveau_therm_sensor_ctor(struct nouveau_therm *therm); int nouveau_therm_fan_ctor(struct nouveau_therm *therm); int nouveau_therm_fan_get(struct nouveau_therm *therm); -int nouveau_therm_fan_set(struct nouveau_therm *therm, int percent); +int nouveau_therm_fan_set(struct nouveau_therm *therm, bool now, int percent); int nouveau_therm_fan_user_get(struct nouveau_therm *therm); int nouveau_therm_fan_user_set(struct nouveau_therm *therm, int percent); int nouveau_therm_fan_set_mode(struct nouveau_therm *therm, -- 2.30.2