brintos

brintos / linux-shallow public Read only

0
0
Text · 5.3 KiB · 6605e08 Raw
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", &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