linux/drivers/video/backlight/aat2870_bl.c
<<
>>
Prefs
   1/*
   2 * linux/drivers/video/backlight/aat2870_bl.c
   3 *
   4 * Copyright (c) 2011, NVIDIA Corporation.
   5 * Author: Jin Park <jinyoungp@nvidia.com>
   6 *
   7 * This program is free software; you can redistribute it and/or
   8 * modify it under the terms of the GNU General Public License
   9 * version 2 as published by the Free Software Foundation.
  10 *
  11 * This program is distributed in the hope that it will be useful, but
  12 * WITHOUT ANY WARRANTY; without even the implied warranty of
  13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14 * General Public License for more details.
  15 *
  16 * You should have received a copy of the GNU General Public License
  17 * along with this program; if not, write to the Free Software
  18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
  19 * 02110-1301 USA
  20 */
  21
  22#include <linux/module.h>
  23#include <linux/kernel.h>
  24#include <linux/init.h>
  25#include <linux/platform_device.h>
  26#include <linux/mutex.h>
  27#include <linux/delay.h>
  28#include <linux/fb.h>
  29#include <linux/backlight.h>
  30#include <linux/mfd/aat2870.h>
  31
  32struct aat2870_bl_driver_data {
  33        struct platform_device *pdev;
  34        struct backlight_device *bd;
  35
  36        int channels;
  37        int max_current;
  38        int brightness; /* current brightness */
  39};
  40
  41static inline int aat2870_brightness(struct aat2870_bl_driver_data *aat2870_bl,
  42                                     int brightness)
  43{
  44        struct backlight_device *bd = aat2870_bl->bd;
  45        int val;
  46
  47        val = brightness * (aat2870_bl->max_current - 1);
  48        val /= bd->props.max_brightness;
  49
  50        return val;
  51}
  52
  53static inline int aat2870_bl_enable(struct aat2870_bl_driver_data *aat2870_bl)
  54{
  55        struct aat2870_data *aat2870
  56                        = dev_get_drvdata(aat2870_bl->pdev->dev.parent);
  57
  58        return aat2870->write(aat2870, AAT2870_BL_CH_EN,
  59                              (u8)aat2870_bl->channels);
  60}
  61
  62static inline int aat2870_bl_disable(struct aat2870_bl_driver_data *aat2870_bl)
  63{
  64        struct aat2870_data *aat2870
  65                        = dev_get_drvdata(aat2870_bl->pdev->dev.parent);
  66
  67        return aat2870->write(aat2870, AAT2870_BL_CH_EN, 0x0);
  68}
  69
  70static int aat2870_bl_get_brightness(struct backlight_device *bd)
  71{
  72        return bd->props.brightness;
  73}
  74
  75static int aat2870_bl_update_status(struct backlight_device *bd)
  76{
  77        struct aat2870_bl_driver_data *aat2870_bl = bl_get_data(bd);
  78        struct aat2870_data *aat2870 =
  79                        dev_get_drvdata(aat2870_bl->pdev->dev.parent);
  80        int brightness = bd->props.brightness;
  81        int ret;
  82
  83        if ((brightness < 0) || (bd->props.max_brightness < brightness)) {
  84                dev_err(&bd->dev, "invalid brightness, %d\n", brightness);
  85                return -EINVAL;
  86        }
  87
  88        dev_dbg(&bd->dev, "brightness=%d, power=%d, state=%d\n",
  89                 bd->props.brightness, bd->props.power, bd->props.state);
  90
  91        if ((bd->props.power != FB_BLANK_UNBLANK) ||
  92                        (bd->props.state & BL_CORE_FBBLANK) ||
  93                        (bd->props.state & BL_CORE_SUSPENDED))
  94                brightness = 0;
  95
  96        ret = aat2870->write(aat2870, AAT2870_BLM,
  97                             (u8)aat2870_brightness(aat2870_bl, brightness));
  98        if (ret < 0)
  99                return ret;
 100
 101        if (brightness == 0) {
 102                ret = aat2870_bl_disable(aat2870_bl);
 103                if (ret < 0)
 104                        return ret;
 105        } else if (aat2870_bl->brightness == 0) {
 106                ret = aat2870_bl_enable(aat2870_bl);
 107                if (ret < 0)
 108                        return ret;
 109        }
 110
 111        aat2870_bl->brightness = brightness;
 112
 113        return 0;
 114}
 115
 116static int aat2870_bl_check_fb(struct backlight_device *bd, struct fb_info *fi)
 117{
 118        return 1;
 119}
 120
 121static const struct backlight_ops aat2870_bl_ops = {
 122        .options = BL_CORE_SUSPENDRESUME,
 123        .get_brightness = aat2870_bl_get_brightness,
 124        .update_status = aat2870_bl_update_status,
 125        .check_fb = aat2870_bl_check_fb,
 126};
 127
 128static int aat2870_bl_probe(struct platform_device *pdev)
 129{
 130        struct aat2870_bl_platform_data *pdata = pdev->dev.platform_data;
 131        struct aat2870_bl_driver_data *aat2870_bl;
 132        struct backlight_device *bd;
 133        struct backlight_properties props;
 134        int ret = 0;
 135
 136        if (!pdata) {
 137                dev_err(&pdev->dev, "No platform data\n");
 138                ret = -ENXIO;
 139                goto out;
 140        }
 141
 142        if (pdev->id != AAT2870_ID_BL) {
 143                dev_err(&pdev->dev, "Invalid device ID, %d\n", pdev->id);
 144                ret = -EINVAL;
 145                goto out;
 146        }
 147
 148        aat2870_bl = devm_kzalloc(&pdev->dev,
 149                                  sizeof(struct aat2870_bl_driver_data),
 150                                  GFP_KERNEL);
 151        if (!aat2870_bl) {
 152                dev_err(&pdev->dev,
 153                        "Failed to allocate memory for aat2870 backlight\n");
 154                ret = -ENOMEM;
 155                goto out;
 156        }
 157
 158        memset(&props, 0, sizeof(struct backlight_properties));
 159
 160        props.type = BACKLIGHT_RAW;
 161        bd = backlight_device_register("aat2870-backlight", &pdev->dev,
 162                                       aat2870_bl, &aat2870_bl_ops, &props);
 163        if (IS_ERR(bd)) {
 164                dev_err(&pdev->dev,
 165                        "Failed allocate memory for backlight device\n");
 166                ret = PTR_ERR(bd);
 167                goto out;
 168        }
 169
 170        aat2870_bl->pdev = pdev;
 171        platform_set_drvdata(pdev, aat2870_bl);
 172
 173        aat2870_bl->bd = bd;
 174
 175        if (pdata->channels > 0)
 176                aat2870_bl->channels = pdata->channels;
 177        else
 178                aat2870_bl->channels = AAT2870_BL_CH_ALL;
 179
 180        if (pdata->max_current > 0)
 181                aat2870_bl->max_current = pdata->max_current;
 182        else
 183                aat2870_bl->max_current = AAT2870_CURRENT_27_9;
 184
 185        if (pdata->max_brightness > 0)
 186                bd->props.max_brightness = pdata->max_brightness;
 187        else
 188                bd->props.max_brightness = 255;
 189
 190        aat2870_bl->brightness = 0;
 191        bd->props.power = FB_BLANK_UNBLANK;
 192        bd->props.brightness = bd->props.max_brightness;
 193
 194        ret = aat2870_bl_update_status(bd);
 195        if (ret < 0) {
 196                dev_err(&pdev->dev, "Failed to initialize\n");
 197                goto out_bl_dev_unregister;
 198        }
 199
 200        return 0;
 201
 202out_bl_dev_unregister:
 203        backlight_device_unregister(bd);
 204out:
 205        return ret;
 206}
 207
 208static int aat2870_bl_remove(struct platform_device *pdev)
 209{
 210        struct aat2870_bl_driver_data *aat2870_bl = platform_get_drvdata(pdev);
 211        struct backlight_device *bd = aat2870_bl->bd;
 212
 213        bd->props.power = FB_BLANK_POWERDOWN;
 214        bd->props.brightness = 0;
 215        backlight_update_status(bd);
 216
 217        backlight_device_unregister(bd);
 218
 219        return 0;
 220}
 221
 222static struct platform_driver aat2870_bl_driver = {
 223        .driver = {
 224                .name   = "aat2870-backlight",
 225                .owner  = THIS_MODULE,
 226        },
 227        .probe          = aat2870_bl_probe,
 228        .remove         = aat2870_bl_remove,
 229};
 230
 231static int __init aat2870_bl_init(void)
 232{
 233        return platform_driver_register(&aat2870_bl_driver);
 234}
 235subsys_initcall(aat2870_bl_init);
 236
 237static void __exit aat2870_bl_exit(void)
 238{
 239        platform_driver_unregister(&aat2870_bl_driver);
 240}
 241module_exit(aat2870_bl_exit);
 242
 243MODULE_DESCRIPTION("AnalogicTech AAT2870 Backlight");
 244MODULE_LICENSE("GPL");
 245MODULE_AUTHOR("Jin Park <jinyoungp@nvidia.com>");
 246