linux/drivers/leds/leds-tlc591xx.c
<<
>>
Prefs
   1/*
   2 * Copyright 2014 Belkin Inc.
   3 * Copyright 2015 Andrew Lunn <andrew@lunn.ch>
   4 *
   5 * This program is free software; you can redistribute it and/or modify
   6 * it under the terms of the GNU General Public License as published by
   7 * the Free Software Foundation; version 2 of the License.
   8 */
   9
  10#include <linux/i2c.h>
  11#include <linux/leds.h>
  12#include <linux/module.h>
  13#include <linux/of.h>
  14#include <linux/of_device.h>
  15#include <linux/regmap.h>
  16#include <linux/slab.h>
  17
  18#define TLC591XX_MAX_LEDS       16
  19
  20#define TLC591XX_REG_MODE1      0x00
  21#define MODE1_RESPON_ADDR_MASK  0xF0
  22#define MODE1_NORMAL_MODE       (0 << 4)
  23#define MODE1_SPEED_MODE        (1 << 4)
  24
  25#define TLC591XX_REG_MODE2      0x01
  26#define MODE2_DIM               (0 << 5)
  27#define MODE2_BLINK             (1 << 5)
  28#define MODE2_OCH_STOP          (0 << 3)
  29#define MODE2_OCH_ACK           (1 << 3)
  30
  31#define TLC591XX_REG_PWM(x)     (0x02 + (x))
  32
  33#define TLC591XX_REG_GRPPWM     0x12
  34#define TLC591XX_REG_GRPFREQ    0x13
  35
  36/* LED Driver Output State, determine the source that drives LED outputs */
  37#define LEDOUT_OFF              0x0     /* Output LOW */
  38#define LEDOUT_ON               0x1     /* Output HI-Z */
  39#define LEDOUT_DIM              0x2     /* Dimming */
  40#define LEDOUT_BLINK            0x3     /* Blinking */
  41#define LEDOUT_MASK             0x3
  42
  43#define ldev_to_led(c)          container_of(c, struct tlc591xx_led, ldev)
  44
  45struct tlc591xx_led {
  46        bool active;
  47        unsigned int led_no;
  48        struct led_classdev ldev;
  49        struct tlc591xx_priv *priv;
  50};
  51
  52struct tlc591xx_priv {
  53        struct tlc591xx_led leds[TLC591XX_MAX_LEDS];
  54        struct regmap *regmap;
  55        unsigned int reg_ledout_offset;
  56};
  57
  58struct tlc591xx {
  59        unsigned int max_leds;
  60        unsigned int reg_ledout_offset;
  61};
  62
  63static const struct tlc591xx tlc59116 = {
  64        .max_leds = 16,
  65        .reg_ledout_offset = 0x14,
  66};
  67
  68static const struct tlc591xx tlc59108 = {
  69        .max_leds = 8,
  70        .reg_ledout_offset = 0x0c,
  71};
  72
  73static int
  74tlc591xx_set_mode(struct regmap *regmap, u8 mode)
  75{
  76        int err;
  77        u8 val;
  78
  79        err = regmap_write(regmap, TLC591XX_REG_MODE1, MODE1_NORMAL_MODE);
  80        if (err)
  81                return err;
  82
  83        val = MODE2_OCH_STOP | mode;
  84
  85        return regmap_write(regmap, TLC591XX_REG_MODE2, val);
  86}
  87
  88static int
  89tlc591xx_set_ledout(struct tlc591xx_priv *priv, struct tlc591xx_led *led,
  90                    u8 val)
  91{
  92        unsigned int i = (led->led_no % 4) * 2;
  93        unsigned int mask = LEDOUT_MASK << i;
  94        unsigned int addr = priv->reg_ledout_offset + (led->led_no >> 2);
  95
  96        val = val << i;
  97
  98        return regmap_update_bits(priv->regmap, addr, mask, val);
  99}
 100
 101static int
 102tlc591xx_set_pwm(struct tlc591xx_priv *priv, struct tlc591xx_led *led,
 103                 u8 brightness)
 104{
 105        u8 pwm = TLC591XX_REG_PWM(led->led_no);
 106
 107        return regmap_write(priv->regmap, pwm, brightness);
 108}
 109
 110static int
 111tlc591xx_brightness_set(struct led_classdev *led_cdev,
 112                        enum led_brightness brightness)
 113{
 114        struct tlc591xx_led *led = ldev_to_led(led_cdev);
 115        struct tlc591xx_priv *priv = led->priv;
 116        int err;
 117
 118        switch (brightness) {
 119        case 0:
 120                err = tlc591xx_set_ledout(priv, led, LEDOUT_OFF);
 121                break;
 122        case LED_FULL:
 123                err = tlc591xx_set_ledout(priv, led, LEDOUT_ON);
 124                break;
 125        default:
 126                err = tlc591xx_set_ledout(priv, led, LEDOUT_DIM);
 127                if (!err)
 128                        err = tlc591xx_set_pwm(priv, led, brightness);
 129        }
 130
 131        return err;
 132}
 133
 134static void
 135tlc591xx_destroy_devices(struct tlc591xx_priv *priv, unsigned int j)
 136{
 137        int i = j;
 138
 139        while (--i >= 0) {
 140                if (priv->leds[i].active)
 141                        led_classdev_unregister(&priv->leds[i].ldev);
 142        }
 143}
 144
 145static int
 146tlc591xx_configure(struct device *dev,
 147                   struct tlc591xx_priv *priv,
 148                   const struct tlc591xx *tlc591xx)
 149{
 150        unsigned int i;
 151        int err = 0;
 152
 153        tlc591xx_set_mode(priv->regmap, MODE2_DIM);
 154        for (i = 0; i < TLC591XX_MAX_LEDS; i++) {
 155                struct tlc591xx_led *led = &priv->leds[i];
 156
 157                if (!led->active)
 158                        continue;
 159
 160                led->priv = priv;
 161                led->led_no = i;
 162                led->ldev.brightness_set_blocking = tlc591xx_brightness_set;
 163                led->ldev.max_brightness = LED_FULL;
 164                err = led_classdev_register(dev, &led->ldev);
 165                if (err < 0) {
 166                        dev_err(dev, "couldn't register LED %s\n",
 167                                led->ldev.name);
 168                        goto exit;
 169                }
 170        }
 171
 172        return 0;
 173
 174exit:
 175        tlc591xx_destroy_devices(priv, i);
 176        return err;
 177}
 178
 179static const struct regmap_config tlc591xx_regmap = {
 180        .reg_bits = 8,
 181        .val_bits = 8,
 182        .max_register = 0x1e,
 183};
 184
 185static const struct of_device_id of_tlc591xx_leds_match[] = {
 186        { .compatible = "ti,tlc59116",
 187          .data = &tlc59116 },
 188        { .compatible = "ti,tlc59108",
 189          .data = &tlc59108 },
 190        {},
 191};
 192MODULE_DEVICE_TABLE(of, of_tlc591xx_leds_match);
 193
 194static int
 195tlc591xx_probe(struct i2c_client *client,
 196               const struct i2c_device_id *id)
 197{
 198        struct device_node *np = client->dev.of_node, *child;
 199        struct device *dev = &client->dev;
 200        const struct of_device_id *match;
 201        const struct tlc591xx *tlc591xx;
 202        struct tlc591xx_priv *priv;
 203        int err, count, reg;
 204
 205        match = of_match_device(of_tlc591xx_leds_match, dev);
 206        if (!match)
 207                return -ENODEV;
 208
 209        tlc591xx = match->data;
 210        if (!np)
 211                return -ENODEV;
 212
 213        count = of_get_child_count(np);
 214        if (!count || count > tlc591xx->max_leds)
 215                return -EINVAL;
 216
 217        priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
 218        if (!priv)
 219                return -ENOMEM;
 220
 221        priv->regmap = devm_regmap_init_i2c(client, &tlc591xx_regmap);
 222        if (IS_ERR(priv->regmap)) {
 223                err = PTR_ERR(priv->regmap);
 224                dev_err(dev, "Failed to allocate register map: %d\n", err);
 225                return err;
 226        }
 227        priv->reg_ledout_offset = tlc591xx->reg_ledout_offset;
 228
 229        i2c_set_clientdata(client, priv);
 230
 231        for_each_child_of_node(np, child) {
 232                err = of_property_read_u32(child, "reg", &reg);
 233                if (err) {
 234                        of_node_put(child);
 235                        return err;
 236                }
 237                if (reg < 0 || reg >= tlc591xx->max_leds ||
 238                    priv->leds[reg].active) {
 239                        of_node_put(child);
 240                        return -EINVAL;
 241                }
 242                priv->leds[reg].active = true;
 243                priv->leds[reg].ldev.name =
 244                        of_get_property(child, "label", NULL) ? : child->name;
 245                priv->leds[reg].ldev.default_trigger =
 246                        of_get_property(child, "linux,default-trigger", NULL);
 247        }
 248        return tlc591xx_configure(dev, priv, tlc591xx);
 249}
 250
 251static int
 252tlc591xx_remove(struct i2c_client *client)
 253{
 254        struct tlc591xx_priv *priv = i2c_get_clientdata(client);
 255
 256        tlc591xx_destroy_devices(priv, TLC591XX_MAX_LEDS);
 257
 258        return 0;
 259}
 260
 261static const struct i2c_device_id tlc591xx_id[] = {
 262        { "tlc59116" },
 263        { "tlc59108" },
 264        {},
 265};
 266MODULE_DEVICE_TABLE(i2c, tlc591xx_id);
 267
 268static struct i2c_driver tlc591xx_driver = {
 269        .driver = {
 270                .name = "tlc591xx",
 271                .of_match_table = of_match_ptr(of_tlc591xx_leds_match),
 272        },
 273        .probe = tlc591xx_probe,
 274        .remove = tlc591xx_remove,
 275        .id_table = tlc591xx_id,
 276};
 277
 278module_i2c_driver(tlc591xx_driver);
 279
 280MODULE_AUTHOR("Andrew Lunn <andrew@lunn.ch>");
 281MODULE_LICENSE("GPL");
 282MODULE_DESCRIPTION("TLC591XX LED driver");
 283