linux/drivers/video/backlight/tps65217_bl.c
<<
>>
Prefs
   1/*
   2 * tps65217_bl.c
   3 *
   4 * TPS65217 backlight driver
   5 *
   6 * Copyright (C) 2012 Matthias Kaehlcke
   7 * Author: Matthias Kaehlcke <matthias@kaehlcke.net>
   8 *
   9 * This program is free software; you can redistribute it and/or
  10 * modify it under the terms of the GNU General Public License as
  11 * published by the Free Software Foundation version 2.
  12 *
  13 * This program is distributed "as is" WITHOUT ANY WARRANTY of any
  14 * kind, whether express or implied; without even the implied warranty
  15 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16 * GNU General Public License for more details.
  17 */
  18
  19#include <linux/kernel.h>
  20#include <linux/backlight.h>
  21#include <linux/err.h>
  22#include <linux/fb.h>
  23#include <linux/mfd/tps65217.h>
  24#include <linux/module.h>
  25#include <linux/platform_device.h>
  26#include <linux/slab.h>
  27
  28struct tps65217_bl {
  29        struct tps65217 *tps;
  30        struct device *dev;
  31        struct backlight_device *bl;
  32        bool is_enabled;
  33};
  34
  35static int tps65217_bl_enable(struct tps65217_bl *tps65217_bl)
  36{
  37        int rc;
  38
  39        rc = tps65217_set_bits(tps65217_bl->tps, TPS65217_REG_WLEDCTRL1,
  40                        TPS65217_WLEDCTRL1_ISINK_ENABLE,
  41                        TPS65217_WLEDCTRL1_ISINK_ENABLE, TPS65217_PROTECT_NONE);
  42        if (rc) {
  43                dev_err(tps65217_bl->dev,
  44                        "failed to enable backlight: %d\n", rc);
  45                return rc;
  46        }
  47
  48        tps65217_bl->is_enabled = true;
  49
  50        dev_dbg(tps65217_bl->dev, "backlight enabled\n");
  51
  52        return 0;
  53}
  54
  55static int tps65217_bl_disable(struct tps65217_bl *tps65217_bl)
  56{
  57        int rc;
  58
  59        rc = tps65217_clear_bits(tps65217_bl->tps,
  60                                TPS65217_REG_WLEDCTRL1,
  61                                TPS65217_WLEDCTRL1_ISINK_ENABLE,
  62                                TPS65217_PROTECT_NONE);
  63        if (rc) {
  64                dev_err(tps65217_bl->dev,
  65                        "failed to disable backlight: %d\n", rc);
  66                return rc;
  67        }
  68
  69        tps65217_bl->is_enabled = false;
  70
  71        dev_dbg(tps65217_bl->dev, "backlight disabled\n");
  72
  73        return 0;
  74}
  75
  76static int tps65217_bl_update_status(struct backlight_device *bl)
  77{
  78        struct tps65217_bl *tps65217_bl = bl_get_data(bl);
  79        int rc;
  80        int brightness = bl->props.brightness;
  81
  82        if (bl->props.state & BL_CORE_SUSPENDED)
  83                brightness = 0;
  84
  85        if ((bl->props.power != FB_BLANK_UNBLANK) ||
  86                (bl->props.fb_blank != FB_BLANK_UNBLANK))
  87                /* framebuffer in low power mode or blanking active */
  88                brightness = 0;
  89
  90        if (brightness > 0) {
  91                rc = tps65217_reg_write(tps65217_bl->tps,
  92                                        TPS65217_REG_WLEDCTRL2,
  93                                        brightness - 1,
  94                                        TPS65217_PROTECT_NONE);
  95                if (rc) {
  96                        dev_err(tps65217_bl->dev,
  97                                "failed to set brightness level: %d\n", rc);
  98                        return rc;
  99                }
 100
 101                dev_dbg(tps65217_bl->dev, "brightness set to %d\n", brightness);
 102
 103                if (!tps65217_bl->is_enabled)
 104                        rc = tps65217_bl_enable(tps65217_bl);
 105        } else {
 106                rc = tps65217_bl_disable(tps65217_bl);
 107        }
 108
 109        return rc;
 110}
 111
 112static const struct backlight_ops tps65217_bl_ops = {
 113        .options        = BL_CORE_SUSPENDRESUME,
 114        .update_status  = tps65217_bl_update_status,
 115};
 116
 117static int tps65217_bl_hw_init(struct tps65217_bl *tps65217_bl,
 118                        struct tps65217_bl_pdata *pdata)
 119{
 120        int rc;
 121
 122        rc = tps65217_bl_disable(tps65217_bl);
 123        if (rc)
 124                return rc;
 125
 126        switch (pdata->isel) {
 127        case TPS65217_BL_ISET1:
 128                /* select ISET_1 current level */
 129                rc = tps65217_clear_bits(tps65217_bl->tps,
 130                                        TPS65217_REG_WLEDCTRL1,
 131                                        TPS65217_WLEDCTRL1_ISEL,
 132                                        TPS65217_PROTECT_NONE);
 133                if (rc) {
 134                        dev_err(tps65217_bl->dev,
 135                                "failed to select ISET1 current level: %d)\n",
 136                                rc);
 137                        return rc;
 138                }
 139
 140                dev_dbg(tps65217_bl->dev, "selected ISET1 current level\n");
 141
 142                break;
 143
 144        case TPS65217_BL_ISET2:
 145                /* select ISET2 current level */
 146                rc = tps65217_set_bits(tps65217_bl->tps, TPS65217_REG_WLEDCTRL1,
 147                                TPS65217_WLEDCTRL1_ISEL,
 148                                TPS65217_WLEDCTRL1_ISEL, TPS65217_PROTECT_NONE);
 149                if (rc) {
 150                        dev_err(tps65217_bl->dev,
 151                                "failed to select ISET2 current level: %d\n",
 152                                rc);
 153                        return rc;
 154                }
 155
 156                dev_dbg(tps65217_bl->dev, "selected ISET2 current level\n");
 157
 158                break;
 159
 160        default:
 161                dev_err(tps65217_bl->dev,
 162                        "invalid value for current level: %d\n", pdata->isel);
 163                return -EINVAL;
 164        }
 165
 166        /* set PWM frequency */
 167        rc = tps65217_set_bits(tps65217_bl->tps,
 168                        TPS65217_REG_WLEDCTRL1,
 169                        TPS65217_WLEDCTRL1_FDIM_MASK,
 170                        pdata->fdim,
 171                        TPS65217_PROTECT_NONE);
 172        if (rc) {
 173                dev_err(tps65217_bl->dev,
 174                        "failed to select PWM dimming frequency: %d\n",
 175                        rc);
 176                return rc;
 177        }
 178
 179        return 0;
 180}
 181
 182#ifdef CONFIG_OF
 183static struct tps65217_bl_pdata *
 184tps65217_bl_parse_dt(struct platform_device *pdev)
 185{
 186        struct tps65217 *tps = dev_get_drvdata(pdev->dev.parent);
 187        struct device_node *node;
 188        struct tps65217_bl_pdata *pdata, *err;
 189        u32 val;
 190
 191        node = of_get_child_by_name(tps->dev->of_node, "backlight");
 192        if (!node)
 193                return ERR_PTR(-ENODEV);
 194
 195        pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
 196        if (!pdata) {
 197                err = ERR_PTR(-ENOMEM);
 198                goto err;
 199        }
 200
 201        pdata->isel = TPS65217_BL_ISET1;
 202        if (!of_property_read_u32(node, "isel", &val)) {
 203                if (val < TPS65217_BL_ISET1 ||
 204                        val > TPS65217_BL_ISET2) {
 205                        dev_err(&pdev->dev,
 206                                "invalid 'isel' value in the device tree\n");
 207                        err = ERR_PTR(-EINVAL);
 208                        goto err;
 209                }
 210
 211                pdata->isel = val;
 212        }
 213
 214        pdata->fdim = TPS65217_BL_FDIM_200HZ;
 215        if (!of_property_read_u32(node, "fdim", &val)) {
 216                switch (val) {
 217                case 100:
 218                        pdata->fdim = TPS65217_BL_FDIM_100HZ;
 219                        break;
 220
 221                case 200:
 222                        pdata->fdim = TPS65217_BL_FDIM_200HZ;
 223                        break;
 224
 225                case 500:
 226                        pdata->fdim = TPS65217_BL_FDIM_500HZ;
 227                        break;
 228
 229                case 1000:
 230                        pdata->fdim = TPS65217_BL_FDIM_1000HZ;
 231                        break;
 232
 233                default:
 234                        dev_err(&pdev->dev,
 235                                "invalid 'fdim' value in the device tree\n");
 236                        err = ERR_PTR(-EINVAL);
 237                        goto err;
 238                }
 239        }
 240
 241        if (!of_property_read_u32(node, "default-brightness", &val)) {
 242                if (val > 100) {
 243                        dev_err(&pdev->dev,
 244                                "invalid 'default-brightness' value in the device tree\n");
 245                        err = ERR_PTR(-EINVAL);
 246                        goto err;
 247                }
 248
 249                pdata->dft_brightness = val;
 250        }
 251
 252        of_node_put(node);
 253
 254        return pdata;
 255
 256err:
 257        of_node_put(node);
 258
 259        return err;
 260}
 261#else
 262static struct tps65217_bl_pdata *
 263tps65217_bl_parse_dt(struct platform_device *pdev)
 264{
 265        return NULL;
 266}
 267#endif
 268
 269static int tps65217_bl_probe(struct platform_device *pdev)
 270{
 271        int rc;
 272        struct tps65217 *tps = dev_get_drvdata(pdev->dev.parent);
 273        struct tps65217_bl *tps65217_bl;
 274        struct tps65217_bl_pdata *pdata;
 275        struct backlight_properties bl_props;
 276
 277        pdata = tps65217_bl_parse_dt(pdev);
 278        if (IS_ERR(pdata))
 279                return PTR_ERR(pdata);
 280
 281        tps65217_bl = devm_kzalloc(&pdev->dev, sizeof(*tps65217_bl),
 282                                GFP_KERNEL);
 283        if (tps65217_bl == NULL)
 284                return -ENOMEM;
 285
 286        tps65217_bl->tps = tps;
 287        tps65217_bl->dev = &pdev->dev;
 288        tps65217_bl->is_enabled = false;
 289
 290        rc = tps65217_bl_hw_init(tps65217_bl, pdata);
 291        if (rc)
 292                return rc;
 293
 294        memset(&bl_props, 0, sizeof(struct backlight_properties));
 295        bl_props.type = BACKLIGHT_RAW;
 296        bl_props.max_brightness = 100;
 297
 298        tps65217_bl->bl = devm_backlight_device_register(&pdev->dev, pdev->name,
 299                                                tps65217_bl->dev, tps65217_bl,
 300                                                &tps65217_bl_ops, &bl_props);
 301        if (IS_ERR(tps65217_bl->bl)) {
 302                dev_err(tps65217_bl->dev,
 303                        "registration of backlight device failed: %d\n", rc);
 304                return PTR_ERR(tps65217_bl->bl);
 305        }
 306
 307        tps65217_bl->bl->props.brightness = pdata->dft_brightness;
 308        backlight_update_status(tps65217_bl->bl);
 309        platform_set_drvdata(pdev, tps65217_bl);
 310
 311        return 0;
 312}
 313
 314#ifdef CONFIG_OF
 315static const struct of_device_id tps65217_bl_of_match[] = {
 316        { .compatible = "ti,tps65217-bl", },
 317        { /* sentinel */ },
 318};
 319MODULE_DEVICE_TABLE(of, tps65217_bl_of_match);
 320#endif
 321
 322static struct platform_driver tps65217_bl_driver = {
 323        .probe          = tps65217_bl_probe,
 324        .driver         = {
 325                .name   = "tps65217-bl",
 326                .of_match_table = of_match_ptr(tps65217_bl_of_match),
 327        },
 328};
 329
 330module_platform_driver(tps65217_bl_driver);
 331
 332MODULE_DESCRIPTION("TPS65217 Backlight driver");
 333MODULE_LICENSE("GPL v2");
 334MODULE_AUTHOR("Matthias Kaehlcke <matthias@kaehlcke.net>");
 335