From cb9fa62671ace5ac40b9924e9014cebf04b78228 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Sun, 14 Aug 2011 12:43:47 +1000 Subject: [PATCH] drm/nv50/pm: add support for pwm fan control Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_pm.c | 7 ++- drivers/gpu/drm/nouveau/nouveau_pm.h | 2 + drivers/gpu/drm/nouveau/nouveau_state.c | 4 ++ drivers/gpu/drm/nouveau/nv50_pm.c | 74 +++++++++++++++++++++++++ 4 files changed, 85 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.c b/drivers/gpu/drm/nouveau/nouveau_pm.c index 607e4965f4ff..ee2872ed60ee 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.c +++ b/drivers/gpu/drm/nouveau/nouveau_pm.c @@ -167,8 +167,11 @@ nouveau_pm_perflvl_get(struct drm_device *dev, struct nouveau_pm_level *perflvl) } } - if (pm->fanspeed_get) - perflvl->fanspeed = pm->fanspeed_get(dev); + if (pm->fanspeed_get) { + ret = pm->fanspeed_get(dev); + if (ret > 0) + perflvl->fanspeed = ret; + } return 0; } diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.h b/drivers/gpu/drm/nouveau/nouveau_pm.h index f19b0507fdfd..1b0bcef9ff35 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.h +++ b/drivers/gpu/drm/nouveau/nouveau_pm.h @@ -66,6 +66,8 @@ int nv50_pm_clock_get(struct drm_device *, u32 id); void *nv50_pm_clock_pre(struct drm_device *, struct nouveau_pm_level *, u32 id, int khz); void nv50_pm_clock_set(struct drm_device *, void *); +int nv50_pm_fanspeed_get(struct drm_device *); +int nv50_pm_fanspeed_set(struct drm_device *, int percent); /* nva3_pm.c */ int nva3_pm_clocks_get(struct drm_device *, struct nouveau_pm_level *); diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c index e7ba6e7c0938..16195e9a9f91 100644 --- a/drivers/gpu/drm/nouveau/nouveau_state.c +++ b/drivers/gpu/drm/nouveau/nouveau_state.c @@ -386,6 +386,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->pm.temp_get = nv84_temp_get; else engine->pm.temp_get = nv40_temp_get; + engine->pm.fanspeed_get = nv50_pm_fanspeed_get; + engine->pm.fanspeed_set = nv50_pm_fanspeed_set; engine->vram.init = nv50_vram_init; engine->vram.takedown = nv50_vram_fini; engine->vram.get = nv50_vram_new; @@ -441,6 +443,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->pm.clocks_get = nvc0_pm_clocks_get; engine->pm.voltage_get = nouveau_voltage_gpio_get; engine->pm.voltage_set = nouveau_voltage_gpio_set; + engine->pm.fanspeed_get = nv50_pm_fanspeed_get; + engine->pm.fanspeed_set = nv50_pm_fanspeed_set; break; case 0xd0: engine->instmem.init = nvc0_instmem_init; diff --git a/drivers/gpu/drm/nouveau/nv50_pm.c b/drivers/gpu/drm/nouveau/nv50_pm.c index 3d5a86b98282..713c718206e3 100644 --- a/drivers/gpu/drm/nouveau/nv50_pm.c +++ b/drivers/gpu/drm/nouveau/nv50_pm.c @@ -144,3 +144,77 @@ nv50_pm_clock_set(struct drm_device *dev, void *pre_state) kfree(state); } +struct pwm_info { + int id; + int invert; + u8 tag; + u32 ctrl; + int line; +}; + +static int +nv50_pm_fanspeed_pwm(struct drm_device *dev, struct pwm_info *pwm) +{ + struct dcb_gpio_entry *gpio; + + gpio = nouveau_bios_gpio_entry(dev, 0x09); + if (gpio) { + pwm->tag = gpio->tag; + pwm->id = (gpio->line == 9) ? 1 : 0; + pwm->invert = gpio->state[0] & 1; + pwm->ctrl = (gpio->line < 16) ? 0xe100 : 0xe28c; + pwm->line = (gpio->line & 0xf); + return 0; + } + + return -ENOENT; +} + +int +nv50_pm_fanspeed_get(struct drm_device *dev) +{ + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio; + struct pwm_info pwm; + int ret; + + ret = nv50_pm_fanspeed_pwm(dev, &pwm); + if (ret) + return ret; + + if (nv_rd32(dev, pwm.ctrl) & (0x00000001 << pwm.line)) { + u32 divs = nv_rd32(dev, 0x00e114 + (pwm.id * 8)); + u32 duty = nv_rd32(dev, 0x00e118 + (pwm.id * 8)); + if (divs) { + divs = max(divs, duty); + if (pwm.invert) + duty = divs - duty; + return (duty * 100) / divs; + } + + return 0; + } + + return pgpio->get(dev, pwm.tag) * 100; +} + +int +nv50_pm_fanspeed_set(struct drm_device *dev, int percent) +{ + struct pwm_info pwm; + u32 divs, duty; + int ret; + + ret = nv50_pm_fanspeed_pwm(dev, &pwm); + if (ret) + return ret; + + divs = nv_rd32(dev, 0x00e114 + (pwm.id * 8)); + duty = ((divs * percent) + 99) / 100; + if (pwm.invert) + duty = divs - duty; + + nv_mask(dev, pwm.ctrl, 0x00010001 << pwm.line, 0x00000001 << pwm.line); + nv_wr32(dev, 0x00e118 + (pwm.id * 8), 0x80000000 | duty); + return 0; +}