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