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