Commit fa37e8dd authored by Martin Peres's avatar Martin Peres Committed by Ben Skeggs
Browse files

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: default avatarBen Skeggs <bskeggs@redhat.com>
Signed-off-by: default avatarMartin Peres <martin.peres@labri.fr>
parent 06afd4e8
Loading
Loading
Loading
Loading
+75 −8
Original line number Diff line number Diff line
@@ -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);
+1 −3
Original line number Diff line number Diff line
@@ -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;
+6 −2
Original line number Diff line number Diff line
@@ -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,