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_update_status(struct backlight_device *bd)
  71{
  72        struct aat2870_bl_driver_data *aat2870_bl = bl_get_data(bd);
  73        struct aat2870_data *aat2870 =
  74                        dev_get_drvdata(aat2870_bl->pdev->dev.parent);
  75        int brightness = bd->props.brightness;
  76        int ret;
  77
  78        if ((brightness < 0) || (bd->props.max_brightness < brightness)) {
  79                dev_err(&bd->dev, "invalid brightness, %d\n", brightness);
  80                return -EINVAL;
  81        }
  82
  83        dev_dbg(&bd->dev, "brightness=%d, power=%d, state=%d\n",
  84                 bd->props.brightness, bd->props.power, bd->props.state);
  85
  86        if ((bd->props.power != FB_BLANK_UNBLANK) ||
  87                        (bd->props.state & BL_CORE_FBBLANK) ||
  88                        (bd->props.state & BL_CORE_SUSPENDED))
  89                brightness = 0;
  90
  91        ret = aat2870->write(aat2870, AAT2870_BLM,
  92                             (u8)aat2870_brightness(aat2870_bl, brightness));
  93        if (ret < 0)
  94                return ret;
  95
  96        if (brightness == 0) {
  97                ret = aat2870_bl_disable(aat2870_bl);
  98                if (ret < 0)
  99                        return ret;
 100        } else if (aat2870_bl->brightness == 0) {
 101                ret = aat2870_bl_enable(aat2870_bl);
 102                if (ret < 0)
 103                        return ret;
 104        }
 105
 106        aat2870_bl->brightness = brightness;
 107
 108        return 0;
 109}
 110
 111static int aat2870_bl_check_fb(struct backlight_device *bd, struct fb_info *fi)
 112{
 113        return 1;
 114}
 115
 116static const struct backlight_ops aat2870_bl_ops = {
 117        .options = BL_CORE_SUSPENDRESUME,
 118        .update_status = aat2870_bl_update_status,
 119        .check_fb = aat2870_bl_check_fb,
 120};
 121
 122static int aat2870_bl_probe(struct platform_device *pdev)
 123{
 124        struct aat2870_bl_platform_data *pdata = dev_get_platdata(&pdev->dev);
 125        struct aat2870_bl_driver_data *aat2870_bl;
 126        struct backlight_device *bd;
 127        struct backlight_properties props;
 128        int ret = 0;
 129
 130        if (!pdata) {
 131                dev_err(&pdev->dev, "No platform data\n");
 132                ret = -ENXIO;
 133                goto out;
 134        }
 135
 136        if (pdev->id != AAT2870_ID_BL) {
 137                dev_err(&pdev->dev, "Invalid device ID, %d\n", pdev->id);
 138                ret = -EINVAL;
 139                goto out;
 140        }
 141
 142        aat2870_bl = devm_kzalloc(&pdev->dev,
 143                                  sizeof(struct aat2870_bl_driver_data),
 144                                  GFP_KERNEL);
 145        if (!aat2870_bl) {
 146                ret = -ENOMEM;
 147                goto out;
 148        }
 149
 150        memset(&props, 0, sizeof(struct backlight_properties));
 151
 152        props.type = BACKLIGHT_RAW;
 153        bd = devm_backlight_device_register(&pdev->dev, "aat2870-backlight",
 154                                        &pdev->dev, aat2870_bl, &aat2870_bl_ops,
 155                                        &props);
 156        if (IS_ERR(bd)) {
 157                dev_err(&pdev->dev,
 158                        "Failed allocate memory for backlight device\n");
 159                ret = PTR_ERR(bd);
 160                goto out;
 161        }
 162
 163        aat2870_bl->pdev = pdev;
 164        platform_set_drvdata(pdev, aat2870_bl);
 165
 166        aat2870_bl->bd = bd;
 167
 168        if (pdata->channels > 0)
 169                aat2870_bl->channels = pdata->channels;
 170        else
 171                aat2870_bl->channels = AAT2870_BL_CH_ALL;
 172
 173        if (pdata->max_current > 0)
 174                aat2870_bl->max_current = pdata->max_current;
 175        else
 176                aat2870_bl->max_current = AAT2870_CURRENT_27_9;
 177
 178        if (pdata->max_brightness > 0)
 179                bd->props.max_brightness = pdata->max_brightness;
 180        else
 181                bd->props.max_brightness = 255;
 182
 183        aat2870_bl->brightness = 0;
 184        bd->props.power = FB_BLANK_UNBLANK;
 185        bd->props.brightness = bd->props.max_brightness;
 186
 187        ret = aat2870_bl_update_status(bd);
 188        if (ret < 0) {
 189                dev_err(&pdev->dev, "Failed to initialize\n");
 190                return ret;
 191        }
 192
 193        return 0;
 194
 195out:
 196        return ret;
 197}
 198
 199static int aat2870_bl_remove(struct platform_device *pdev)
 200{
 201        struct aat2870_bl_driver_data *aat2870_bl = platform_get_drvdata(pdev);
 202        struct backlight_device *bd = aat2870_bl->bd;
 203
 204        bd->props.power = FB_BLANK_POWERDOWN;
 205        bd->props.brightness = 0;
 206        backlight_update_status(bd);
 207
 208        return 0;
 209}
 210
 211static struct platform_driver aat2870_bl_driver = {
 212        .driver = {
 213                .name   = "aat2870-backlight",
 214        },
 215        .probe          = aat2870_bl_probe,
 216        .remove         = aat2870_bl_remove,
 217};
 218
 219static int __init aat2870_bl_init(void)
 220{
 221        return platform_driver_register(&aat2870_bl_driver);
 222}
 223subsys_initcall(aat2870_bl_init);
 224
 225static void __exit aat2870_bl_exit(void)
 226{
 227        platform_driver_unregister(&aat2870_bl_driver);
 228}
 229module_exit(aat2870_bl_exit);
 230
 231MODULE_DESCRIPTION("AnalogicTech AAT2870 Backlight");
 232MODULE_LICENSE("GPL");
 233MODULE_AUTHOR("Jin Park <jinyoungp@nvidia.com>");
 234