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#include <linux/slab.h>
  23
  24struct pwm_bl_data {
  25        struct pwm_device       *pwm;
  26        struct device           *dev;
  27        unsigned int            period;
  28        unsigned int            lth_brightness;
  29        int                     (*notify)(struct device *,
  30                                          int brightness);
  31};
  32
  33static int pwm_backlight_update_status(struct backlight_device *bl)
  34{
  35        struct pwm_bl_data *pb = dev_get_drvdata(&bl->dev);
  36        int brightness = bl->props.brightness;
  37        int max = bl->props.max_brightness;
  38
  39        if (bl->props.power != FB_BLANK_UNBLANK)
  40                brightness = 0;
  41
  42        if (bl->props.fb_blank != FB_BLANK_UNBLANK)
  43                brightness = 0;
  44
  45        if (pb->notify)
  46                brightness = pb->notify(pb->dev, brightness);
  47
  48        if (brightness == 0) {
  49                pwm_config(pb->pwm, 0, pb->period);
  50                pwm_disable(pb->pwm);
  51        } else {
  52                brightness = pb->lth_brightness +
  53                        (brightness * (pb->period - pb->lth_brightness) / max);
  54                pwm_config(pb->pwm, brightness, pb->period);
  55                pwm_enable(pb->pwm);
  56        }
  57        return 0;
  58}
  59
  60static int pwm_backlight_get_brightness(struct backlight_device *bl)
  61{
  62        return bl->props.brightness;
  63}
  64
  65static const struct backlight_ops pwm_backlight_ops = {
  66        .update_status  = pwm_backlight_update_status,
  67        .get_brightness = pwm_backlight_get_brightness,
  68};
  69
  70static int pwm_backlight_probe(struct platform_device *pdev)
  71{
  72        struct backlight_properties props;
  73        struct platform_pwm_backlight_data *data = pdev->dev.platform_data;
  74        struct backlight_device *bl;
  75        struct pwm_bl_data *pb;
  76        int ret;
  77
  78        if (!data) {
  79                dev_err(&pdev->dev, "failed to find platform data\n");
  80                return -EINVAL;
  81        }
  82
  83        if (data->init) {
  84                ret = data->init(&pdev->dev);
  85                if (ret < 0)
  86                        return ret;
  87        }
  88
  89        pb = kzalloc(sizeof(*pb), GFP_KERNEL);
  90        if (!pb) {
  91                dev_err(&pdev->dev, "no memory for state\n");
  92                ret = -ENOMEM;
  93                goto err_alloc;
  94        }
  95
  96        pb->period = data->pwm_period_ns;
  97        pb->notify = data->notify;
  98        pb->lth_brightness = data->lth_brightness *
  99                (data->pwm_period_ns / data->max_brightness);
 100        pb->dev = &pdev->dev;
 101
 102        pb->pwm = pwm_request(data->pwm_id, "backlight");
 103        if (IS_ERR(pb->pwm)) {
 104                dev_err(&pdev->dev, "unable to request PWM for backlight\n");
 105                ret = PTR_ERR(pb->pwm);
 106                goto err_pwm;
 107        } else
 108                dev_dbg(&pdev->dev, "got pwm for backlight\n");
 109
 110        memset(&props, 0, sizeof(struct backlight_properties));
 111        props.max_brightness = data->max_brightness;
 112        bl = backlight_device_register(dev_name(&pdev->dev), &pdev->dev, pb,
 113                                       &pwm_backlight_ops, &props);
 114        if (IS_ERR(bl)) {
 115                dev_err(&pdev->dev, "failed to register backlight\n");
 116                ret = PTR_ERR(bl);
 117                goto err_bl;
 118        }
 119
 120        bl->props.brightness = data->dft_brightness;
 121        backlight_update_status(bl);
 122
 123        platform_set_drvdata(pdev, bl);
 124        return 0;
 125
 126err_bl:
 127        pwm_free(pb->pwm);
 128err_pwm:
 129        kfree(pb);
 130err_alloc:
 131        if (data->exit)
 132                data->exit(&pdev->dev);
 133        return ret;
 134}
 135
 136static int pwm_backlight_remove(struct platform_device *pdev)
 137{
 138        struct platform_pwm_backlight_data *data = pdev->dev.platform_data;
 139        struct backlight_device *bl = platform_get_drvdata(pdev);
 140        struct pwm_bl_data *pb = dev_get_drvdata(&bl->dev);
 141
 142        backlight_device_unregister(bl);
 143        pwm_config(pb->pwm, 0, pb->period);
 144        pwm_disable(pb->pwm);
 145        pwm_free(pb->pwm);
 146        kfree(pb);
 147        if (data->exit)
 148                data->exit(&pdev->dev);
 149        return 0;
 150}
 151
 152#ifdef CONFIG_PM
 153static int pwm_backlight_suspend(struct platform_device *pdev,
 154                                 pm_message_t state)
 155{
 156        struct backlight_device *bl = platform_get_drvdata(pdev);
 157        struct pwm_bl_data *pb = dev_get_drvdata(&bl->dev);
 158
 159        if (pb->notify)
 160                pb->notify(pb->dev, 0);
 161        pwm_config(pb->pwm, 0, pb->period);
 162        pwm_disable(pb->pwm);
 163        return 0;
 164}
 165
 166static int pwm_backlight_resume(struct platform_device *pdev)
 167{
 168        struct backlight_device *bl = platform_get_drvdata(pdev);
 169
 170        backlight_update_status(bl);
 171        return 0;
 172}
 173#else
 174#define pwm_backlight_suspend   NULL
 175#define pwm_backlight_resume    NULL
 176#endif
 177
 178static struct platform_driver pwm_backlight_driver = {
 179        .driver         = {
 180                .name   = "pwm-backlight",
 181                .owner  = THIS_MODULE,
 182        },
 183        .probe          = pwm_backlight_probe,
 184        .remove         = pwm_backlight_remove,
 185        .suspend        = pwm_backlight_suspend,
 186        .resume         = pwm_backlight_resume,
 187};
 188
 189static int __init pwm_backlight_init(void)
 190{
 191        return platform_driver_register(&pwm_backlight_driver);
 192}
 193module_init(pwm_backlight_init);
 194
 195static void __exit pwm_backlight_exit(void)
 196{
 197        platform_driver_unregister(&pwm_backlight_driver);
 198}
 199module_exit(pwm_backlight_exit);
 200
 201MODULE_DESCRIPTION("PWM based Backlight Driver");
 202MODULE_LICENSE("GPL");
 203MODULE_ALIAS("platform:pwm-backlight");
 204
 205