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 <bskeggs@redhat.com> Signed-off-by: Martin Peres <martin.peres@labri.fr>
This commit is contained in:
Родитель
06afd4e83b
Коммит
fa37e8dda2
|
@ -31,6 +31,71 @@
|
|||
#include <subdev/gpio.h>
|
||||
#include <subdev/timer.h>
|
||||
|
||||
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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -34,13 +34,17 @@
|
|||
#include <subdev/timer.h>
|
||||
|
||||
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,
|
||||
|
|
Загрузка…
Ссылка в новой задаче