diff options
author | Ben Skeggs <bskeggs@redhat.com> | 2011-08-14 06:43:47 +0400 |
---|---|---|
committer | Ben Skeggs <bskeggs@redhat.com> | 2011-12-21 13:01:11 +0400 |
commit | cb9fa62671ace5ac40b9924e9014cebf04b78228 (patch) | |
tree | 4dc19083d28b04a44cc89e6f4b66efe154c6b370 /drivers/gpu/drm/nouveau/nv50_pm.c | |
parent | 8f27c54342dffbfbafbddd6e43f011e6cb16d285 (diff) | |
download | linux-cb9fa62671ace5ac40b9924e9014cebf04b78228.tar.xz |
drm/nv50/pm: add support for pwm fan control
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
Diffstat (limited to 'drivers/gpu/drm/nouveau/nv50_pm.c')
-rw-r--r-- | drivers/gpu/drm/nouveau/nv50_pm.c | 74 |
1 files changed, 74 insertions, 0 deletions
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; +} |