linux/drivers/video/backlight/pwm_bl.c
<<
>>
Prefs
   1/*
   2 * linux/drivers/video/backlight/pwm_bl.c
   3 *
   4 * simple PWM based backlight control, board code has to setup
   5 * 1) pin configuration so PWM waveforms can output
   6 * 2) platform_data being correctly configured
   7 *
   8 * This program is free software; you can redistribute it and/or modify
   9 * it under the terms of the GNU General Public License version 2 as
  10 * published by the Free Software Foundation.
  11 */
  12
  13#include <linux/module.h>
  14#include <linux/kernel.h>
  15#include <linux/init.h>
  16#include <linux/platform_device.h>
  17#include <linux/fb.h>
  18#include <linux/backlight.h>
  19#include <linux/err.h>
  20#include <linux/pwm.h>
  21#include <linux/pwm_backlight.h>
  22
  23struct pwm_bl_data {
  24        struct pwm_device       *pwm;
  25        unsigned int            period;
  26        int                     (*notify)(int brightness);
  27};
  28
  29static int pwm_backlight_update_status(struct backlight_device *bl)
  30{
  31        struct pwm_bl_data *pb = dev_get_drvdata(&bl->dev);
  32        int brightness = bl->props.brightness;
  33        int max = bl->props.max_brightness;
  34
  35        if (bl->props.power != FB_BLANK_UNBLANK)
  36                brightness = 0;
  37
  38        if (bl->props.fb_blank != FB_BLANK_UNBLANK)
  39                brightness = 0;
  40
  41        if (pb->notify)
  42                brightness = pb->notify(brightness);
  43
  44        if (brightness == 0) {
  45                pwm_config(pb->pwm, 0, pb->period);
  46                pwm_disable(pb->pwm);
  47        } else {
  48                pwm_config(pb->pwm, brightness * pb->period / max, pb->period);
  49                pwm_enable(pb->pwm);
  50        }
  51        return 0;
  52}
  53
  54static int pwm_backlight_get_brightness(struct backlight_device *bl)
  55{
  56        return bl->props.brightness;
  57}
  58
  59static struct backlight_ops pwm_backlight_ops = {
  60        .update_status  = pwm_backlight_update_status,
  61        .get_brightness = pwm_backlight_get_brightness,
  62};
  63
  64static int pwm_backlight_probe(struct platform_device *pdev)
  65{
  66        struct platform_pwm_backlight_data *data = pdev->dev.platform_data;
  67        struct backlight_device *bl;
  68        struct pwm_bl_data *pb;
  69        int ret;
  70
  71        if (!data) {
  72                dev_err(&pdev->dev, "failed to find platform data\n");
  73                return -EINVAL;
  74        }
  75
  76        if (data->init) {
  77                ret = data->init(&pdev->dev);
  78                if (ret < 0)
  79                        return ret;
  80        }
  81
  82        pb = kzalloc(sizeof(*pb), GFP_KERNEL);
  83        if (!pb) {
  84                dev_err(&pdev->dev, "no memory for state\n");
  85                ret = -ENOMEM;
  86                goto err_alloc;
  87        }
  88
  89        pb->period = data->pwm_period_ns;
  90        pb->notify = data->notify;
  91
  92        pb->pwm = pwm_request(data->pwm_id, "backlight");
  93        if (IS_ERR(pb->pwm)) {
  94                dev_err(&pdev->dev, "unable to request PWM for backlight\n");
  95                ret = PTR_ERR(pb->pwm);
  96                goto err_pwm;
  97        } else
  98                dev_dbg(&pdev->dev, "got pwm for backlight\n");
  99
 100        bl = backlight_device_register(dev_name(&pdev->dev), &pdev->dev,
 101                        pb, &pwm_backlight_ops);
 102        if (IS_ERR(bl)) {
 103                dev_err(&pdev->dev, "failed to register backlight\n");
 104                ret = PTR_ERR(bl);
 105                goto err_bl;
 106        }
 107
 108        bl->props.max_brightness = data->max_brightness;
 109        bl->props.brightness = data->dft_brightness;
 110        backlight_update_status(bl);
 111
 112        platform_set_drvdata(pdev, bl);
 113        return 0;
 114
 115err_bl:
 116        pwm_free(pb->pwm);
 117err_pwm:
 118        kfree(pb);
 119err_alloc:
 120        if (data->exit)
 121                data->exit(&pdev->dev);
 122        return ret;
 123}
 124
 125static int pwm_backlight_remove(struct platform_device *pdev)
 126{
 127        struct platform_pwm_backlight_data *data = pdev->dev.platform_data;
 128        struct backlight_device *bl = platform_get_drvdata(pdev);
 129        struct pwm_bl_data *pb = dev_get_drvdata(&bl->dev);
 130
 131        backlight_device_unregister(bl);
 132        pwm_config(pb->pwm, 0, pb->period);
 133        pwm_disable(pb->pwm);
 134        pwm_free(pb->pwm);
 135        kfree(pb);
 136        if (data->exit)
 137                data->exit(&pdev->dev);
 138        return 0;
 139}
 140
 141#ifdef CONFIG_PM
 142static int pwm_backlight_suspend(struct platform_device *pdev,
 143                                 pm_message_t state)
 144{
 145        struct backlight_device *bl = platform_get_drvdata(pdev);
 146        struct pwm_bl_data *pb = dev_get_drvdata(&bl->dev);
 147
 148        if (pb->notify)
 149                pb->notify(0);
 150        pwm_config(pb->pwm, 0, pb->period);
 151        pwm_disable(pb->pwm);
 152        return 0;
 153}
 154
 155static int pwm_backlight_resume(struct platform_device *pdev)
 156{
 157        struct backlight_device *bl = platform_get_drvdata(pdev);
 158
 159        backlight_update_status(bl);
 160        return 0;
 161}
 162#else
 163#define pwm_backlight_suspend   NULL
 164#define pwm_backlight_resume    NULL
 165#endif
 166
 167static struct platform_driver pwm_backlight_driver = {
 168        .driver         = {
 169                .name   = "pwm-backlight",
 170                .owner  = THIS_MODULE,
 171        },
 172        .probe          = pwm_backlight_probe,
 173        .remove         = pwm_backlight_remove,
 174        .suspend        = pwm_backlight_suspend,
 175        .resume         = pwm_backlight_resume,
 176};
 177
 178static int __init pwm_backlight_init(void)
 179{
 180        return platform_driver_register(&pwm_backlight_driver);
 181}
 182module_init(pwm_backlight_init);
 183
 184static void __exit pwm_backlight_exit(void)
 185{
 186        platform_driver_unregister(&pwm_backlight_driver);
 187}
 188module_exit(pwm_backlight_exit);
 189
 190MODULE_DESCRIPTION("PWM based Backlight Driver");
 191MODULE_LICENSE("GPL");
 192MODULE_ALIAS("platform:pwm-backlight");
 193
 194