summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/nouveau/core
diff options
context:
space:
mode:
authorMartin Peres <martin.peres@labri.fr>2012-12-05 19:46:35 +1000
committerBen Skeggs <bskeggs@redhat.com>2013-02-20 16:00:20 +1000
commitfa37e8dda2617d48fbc6b17dd6e986e7f4c2bc8b (patch)
tree94b9b9112fae35c5e3e1b6078b23482a87151d01 /drivers/gpu/drm/nouveau/core
parent06afd4e83b83907b735279bb0f08d74aeb8f2e3b (diff)
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>
Diffstat (limited to 'drivers/gpu/drm/nouveau/core')
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/therm/fan.c83
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/therm/fantog.c4
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/therm/priv.h8
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 <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);
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 <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,