xref: /openbmc/linux/drivers/leds/leds-lp50xx.c (revision 2612e3bbc0386368a850140a6c9b990cd496a5ec)
1242b8117SDan Murphy // SPDX-License-Identifier: GPL-2.0
2242b8117SDan Murphy // TI LP50XX LED chip family driver
3242b8117SDan Murphy // Copyright (C) 2018-20 Texas Instruments Incorporated - https://www.ti.com/
4242b8117SDan Murphy 
5242b8117SDan Murphy #include <linux/gpio/consumer.h>
6242b8117SDan Murphy #include <linux/i2c.h>
7242b8117SDan Murphy #include <linux/init.h>
8242b8117SDan Murphy #include <linux/leds.h>
9fb0f236bSAndy Shevchenko #include <linux/mod_devicetable.h>
10242b8117SDan Murphy #include <linux/module.h>
11242b8117SDan Murphy #include <linux/mutex.h>
12242b8117SDan Murphy #include <linux/regmap.h>
13242b8117SDan Murphy #include <linux/regulator/consumer.h>
14242b8117SDan Murphy #include <linux/slab.h>
15242b8117SDan Murphy #include <uapi/linux/uleds.h>
16242b8117SDan Murphy 
17242b8117SDan Murphy #include <linux/led-class-multicolor.h>
18242b8117SDan Murphy 
19242b8117SDan Murphy #include "leds.h"
20242b8117SDan Murphy 
21242b8117SDan Murphy #define LP50XX_DEV_CFG0		0x00
22242b8117SDan Murphy #define LP50XX_DEV_CFG1		0x01
23242b8117SDan Murphy #define LP50XX_LED_CFG0		0x02
24242b8117SDan Murphy 
25242b8117SDan Murphy /* LP5009 and LP5012 registers */
26242b8117SDan Murphy #define LP5012_BNK_BRT		0x03
27242b8117SDan Murphy #define LP5012_BNKA_CLR		0x04
28242b8117SDan Murphy #define LP5012_BNKB_CLR		0x05
29242b8117SDan Murphy #define LP5012_BNKC_CLR		0x06
30242b8117SDan Murphy #define LP5012_LED0_BRT		0x07
31242b8117SDan Murphy #define LP5012_OUT0_CLR		0x0b
32242b8117SDan Murphy #define LP5012_RESET		0x17
33242b8117SDan Murphy 
34242b8117SDan Murphy /* LP5018 and LP5024 registers */
35242b8117SDan Murphy #define LP5024_BNK_BRT		0x03
36242b8117SDan Murphy #define LP5024_BNKA_CLR		0x04
37242b8117SDan Murphy #define LP5024_BNKB_CLR		0x05
38242b8117SDan Murphy #define LP5024_BNKC_CLR		0x06
39242b8117SDan Murphy #define LP5024_LED0_BRT		0x07
40242b8117SDan Murphy #define LP5024_OUT0_CLR		0x0f
41242b8117SDan Murphy #define LP5024_RESET		0x27
42242b8117SDan Murphy 
43242b8117SDan Murphy /* LP5030 and LP5036 registers */
44242b8117SDan Murphy #define LP5036_LED_CFG1		0x03
45242b8117SDan Murphy #define LP5036_BNK_BRT		0x04
46242b8117SDan Murphy #define LP5036_BNKA_CLR		0x05
47242b8117SDan Murphy #define LP5036_BNKB_CLR		0x06
48242b8117SDan Murphy #define LP5036_BNKC_CLR		0x07
49242b8117SDan Murphy #define LP5036_LED0_BRT		0x08
50242b8117SDan Murphy #define LP5036_OUT0_CLR		0x14
51242b8117SDan Murphy #define LP5036_RESET		0x38
52242b8117SDan Murphy 
53242b8117SDan Murphy #define LP50XX_SW_RESET		0xff
54242b8117SDan Murphy #define LP50XX_CHIP_EN		BIT(6)
55242b8117SDan Murphy 
56242b8117SDan Murphy /* There are 3 LED outputs per bank */
57242b8117SDan Murphy #define LP50XX_LEDS_PER_MODULE	3
58242b8117SDan Murphy 
59242b8117SDan Murphy #define LP5009_MAX_LED_MODULES	2
60242b8117SDan Murphy #define LP5012_MAX_LED_MODULES	4
61242b8117SDan Murphy #define LP5018_MAX_LED_MODULES	6
62242b8117SDan Murphy #define LP5024_MAX_LED_MODULES	8
63242b8117SDan Murphy #define LP5030_MAX_LED_MODULES	10
64242b8117SDan Murphy #define LP5036_MAX_LED_MODULES	12
65242b8117SDan Murphy 
66242b8117SDan Murphy static const struct reg_default lp5012_reg_defs[] = {
67242b8117SDan Murphy 	{LP50XX_DEV_CFG0, 0x0},
68242b8117SDan Murphy 	{LP50XX_DEV_CFG1, 0x3c},
69242b8117SDan Murphy 	{LP50XX_LED_CFG0, 0x0},
70242b8117SDan Murphy 	{LP5012_BNK_BRT, 0xff},
71242b8117SDan Murphy 	{LP5012_BNKA_CLR, 0x0f},
72242b8117SDan Murphy 	{LP5012_BNKB_CLR, 0x0f},
73242b8117SDan Murphy 	{LP5012_BNKC_CLR, 0x0f},
74242b8117SDan Murphy 	{LP5012_LED0_BRT, 0x0f},
75242b8117SDan Murphy 	/* LEDX_BRT registers are all 0xff for defaults */
76242b8117SDan Murphy 	{0x08, 0xff}, {0x09, 0xff}, {0x0a, 0xff},
77242b8117SDan Murphy 	{LP5012_OUT0_CLR, 0x0f},
78242b8117SDan Murphy 	/* OUTX_CLR registers are all 0x0 for defaults */
79242b8117SDan Murphy 	{0x0c, 0x00}, {0x0d, 0x00}, {0x0e, 0x00}, {0x0f, 0x00}, {0x10, 0x00},
80242b8117SDan Murphy 	{0x11, 0x00}, {0x12, 0x00}, {0x13, 0x00}, {0x14, 0x00},	{0x15, 0x00},
81242b8117SDan Murphy 	{0x16, 0x00},
82242b8117SDan Murphy 	{LP5012_RESET, 0x00}
83242b8117SDan Murphy };
84242b8117SDan Murphy 
85242b8117SDan Murphy static const struct reg_default lp5024_reg_defs[] = {
86242b8117SDan Murphy 	{LP50XX_DEV_CFG0, 0x0},
87242b8117SDan Murphy 	{LP50XX_DEV_CFG1, 0x3c},
88242b8117SDan Murphy 	{LP50XX_LED_CFG0, 0x0},
89242b8117SDan Murphy 	{LP5024_BNK_BRT, 0xff},
90242b8117SDan Murphy 	{LP5024_BNKA_CLR, 0x0f},
91242b8117SDan Murphy 	{LP5024_BNKB_CLR, 0x0f},
92242b8117SDan Murphy 	{LP5024_BNKC_CLR, 0x0f},
93242b8117SDan Murphy 	{LP5024_LED0_BRT, 0x0f},
94242b8117SDan Murphy 	/* LEDX_BRT registers are all 0xff for defaults */
95242b8117SDan Murphy 	{0x08, 0xff}, {0x09, 0xff}, {0x0a, 0xff}, {0x0b, 0xff}, {0x0c, 0xff},
96242b8117SDan Murphy 	{0x0d, 0xff}, {0x0e, 0xff},
97242b8117SDan Murphy 	{LP5024_OUT0_CLR, 0x0f},
98242b8117SDan Murphy 	/* OUTX_CLR registers are all 0x0 for defaults */
99242b8117SDan Murphy 	{0x10, 0x00}, {0x11, 0x00}, {0x12, 0x00}, {0x13, 0x00}, {0x14, 0x00},
100242b8117SDan Murphy 	{0x15, 0x00}, {0x16, 0x00}, {0x17, 0x00}, {0x18, 0x00}, {0x19, 0x00},
101242b8117SDan Murphy 	{0x1a, 0x00}, {0x1b, 0x00}, {0x1c, 0x00}, {0x1d, 0x00}, {0x1e, 0x00},
102242b8117SDan Murphy 	{0x1f, 0x00}, {0x20, 0x00}, {0x21, 0x00}, {0x22, 0x00}, {0x23, 0x00},
103242b8117SDan Murphy 	{0x24, 0x00}, {0x25, 0x00}, {0x26, 0x00},
104242b8117SDan Murphy 	{LP5024_RESET, 0x00}
105242b8117SDan Murphy };
106242b8117SDan Murphy 
107242b8117SDan Murphy static const struct reg_default lp5036_reg_defs[] = {
108242b8117SDan Murphy 	{LP50XX_DEV_CFG0, 0x0},
109242b8117SDan Murphy 	{LP50XX_DEV_CFG1, 0x3c},
110242b8117SDan Murphy 	{LP50XX_LED_CFG0, 0x0},
111242b8117SDan Murphy 	{LP5036_LED_CFG1, 0x0},
112242b8117SDan Murphy 	{LP5036_BNK_BRT, 0xff},
113242b8117SDan Murphy 	{LP5036_BNKA_CLR, 0x0f},
114242b8117SDan Murphy 	{LP5036_BNKB_CLR, 0x0f},
115242b8117SDan Murphy 	{LP5036_BNKC_CLR, 0x0f},
116242b8117SDan Murphy 	{LP5036_LED0_BRT, 0x0f},
117242b8117SDan Murphy 	/* LEDX_BRT registers are all 0xff for defaults */
118242b8117SDan Murphy 	{0x08, 0xff}, {0x09, 0xff}, {0x0a, 0xff}, {0x0b, 0xff}, {0x0c, 0xff},
119242b8117SDan Murphy 	{0x0d, 0xff}, {0x0e, 0xff}, {0x0f, 0xff}, {0x10, 0xff}, {0x11, 0xff},
120242b8117SDan Murphy 	{0x12, 0xff}, {0x13, 0xff},
121242b8117SDan Murphy 	{LP5036_OUT0_CLR, 0x0f},
122242b8117SDan Murphy 	/* OUTX_CLR registers are all 0x0 for defaults */
123242b8117SDan Murphy 	{0x15, 0x00}, {0x16, 0x00}, {0x17, 0x00}, {0x18, 0x00}, {0x19, 0x00},
124242b8117SDan Murphy 	{0x1a, 0x00}, {0x1b, 0x00}, {0x1c, 0x00}, {0x1d, 0x00}, {0x1e, 0x00},
125242b8117SDan Murphy 	{0x1f, 0x00}, {0x20, 0x00}, {0x21, 0x00}, {0x22, 0x00}, {0x23, 0x00},
126242b8117SDan Murphy 	{0x24, 0x00}, {0x25, 0x00}, {0x26, 0x00}, {0x27, 0x00}, {0x28, 0x00},
127242b8117SDan Murphy 	{0x29, 0x00}, {0x2a, 0x00}, {0x2b, 0x00}, {0x2c, 0x00}, {0x2d, 0x00},
128242b8117SDan Murphy 	{0x2e, 0x00}, {0x2f, 0x00}, {0x30, 0x00}, {0x31, 0x00}, {0x32, 0x00},
129242b8117SDan Murphy 	{0x33, 0x00}, {0x34, 0x00}, {0x35, 0x00}, {0x36, 0x00}, {0x37, 0x00},
130242b8117SDan Murphy 	{LP5036_RESET, 0x00}
131242b8117SDan Murphy };
132242b8117SDan Murphy 
133242b8117SDan Murphy static const struct regmap_config lp5012_regmap_config = {
134242b8117SDan Murphy 	.reg_bits = 8,
135242b8117SDan Murphy 	.val_bits = 8,
136242b8117SDan Murphy 
137242b8117SDan Murphy 	.max_register = LP5012_RESET,
138242b8117SDan Murphy 	.reg_defaults = lp5012_reg_defs,
139242b8117SDan Murphy 	.num_reg_defaults = ARRAY_SIZE(lp5012_reg_defs),
140242b8117SDan Murphy 	.cache_type = REGCACHE_FLAT,
141242b8117SDan Murphy };
142242b8117SDan Murphy 
143242b8117SDan Murphy static const struct regmap_config lp5024_regmap_config = {
144242b8117SDan Murphy 	.reg_bits = 8,
145242b8117SDan Murphy 	.val_bits = 8,
146242b8117SDan Murphy 
147242b8117SDan Murphy 	.max_register = LP5024_RESET,
148242b8117SDan Murphy 	.reg_defaults = lp5024_reg_defs,
149242b8117SDan Murphy 	.num_reg_defaults = ARRAY_SIZE(lp5024_reg_defs),
150242b8117SDan Murphy 	.cache_type = REGCACHE_FLAT,
151242b8117SDan Murphy };
152242b8117SDan Murphy 
153242b8117SDan Murphy static const struct regmap_config lp5036_regmap_config = {
154242b8117SDan Murphy 	.reg_bits = 8,
155242b8117SDan Murphy 	.val_bits = 8,
156242b8117SDan Murphy 
157242b8117SDan Murphy 	.max_register = LP5036_RESET,
158242b8117SDan Murphy 	.reg_defaults = lp5036_reg_defs,
159242b8117SDan Murphy 	.num_reg_defaults = ARRAY_SIZE(lp5036_reg_defs),
160242b8117SDan Murphy 	.cache_type = REGCACHE_FLAT,
161242b8117SDan Murphy };
162242b8117SDan Murphy 
163242b8117SDan Murphy enum lp50xx_model {
164242b8117SDan Murphy 	LP5009,
165242b8117SDan Murphy 	LP5012,
166242b8117SDan Murphy 	LP5018,
167242b8117SDan Murphy 	LP5024,
168242b8117SDan Murphy 	LP5030,
169242b8117SDan Murphy 	LP5036,
170242b8117SDan Murphy };
171242b8117SDan Murphy 
172242b8117SDan Murphy /**
173242b8117SDan Murphy  * struct lp50xx_chip_info -
174242b8117SDan Murphy  * @lp50xx_regmap_config: regmap register configuration
175242b8117SDan Murphy  * @model_id: LED device model
176242b8117SDan Murphy  * @max_modules: total number of supported LED modules
177242b8117SDan Murphy  * @num_leds: number of LED outputs available on the device
178242b8117SDan Murphy  * @led_brightness0_reg: first brightness register of the device
179242b8117SDan Murphy  * @mix_out0_reg: first color mix register of the device
180242b8117SDan Murphy  * @bank_brt_reg: bank brightness register
181242b8117SDan Murphy  * @bank_mix_reg: color mix register
182242b8117SDan Murphy  * @reset_reg: device reset register
183242b8117SDan Murphy  */
184242b8117SDan Murphy struct lp50xx_chip_info {
185242b8117SDan Murphy 	const struct regmap_config *lp50xx_regmap_config;
186242b8117SDan Murphy 	int model_id;
187242b8117SDan Murphy 	u8 max_modules;
188242b8117SDan Murphy 	u8 num_leds;
189242b8117SDan Murphy 	u8 led_brightness0_reg;
190242b8117SDan Murphy 	u8 mix_out0_reg;
191242b8117SDan Murphy 	u8 bank_brt_reg;
192242b8117SDan Murphy 	u8 bank_mix_reg;
193242b8117SDan Murphy 	u8 reset_reg;
194242b8117SDan Murphy };
195242b8117SDan Murphy 
196242b8117SDan Murphy static const struct lp50xx_chip_info lp50xx_chip_info_tbl[] = {
197242b8117SDan Murphy 	[LP5009] = {
198242b8117SDan Murphy 		.model_id = LP5009,
199242b8117SDan Murphy 		.max_modules = LP5009_MAX_LED_MODULES,
200242b8117SDan Murphy 		.num_leds = LP5009_MAX_LED_MODULES * LP50XX_LEDS_PER_MODULE,
201242b8117SDan Murphy 		.led_brightness0_reg = LP5012_LED0_BRT,
202242b8117SDan Murphy 		.mix_out0_reg = LP5012_OUT0_CLR,
203242b8117SDan Murphy 		.bank_brt_reg = LP5012_BNK_BRT,
204242b8117SDan Murphy 		.bank_mix_reg = LP5012_BNKA_CLR,
205242b8117SDan Murphy 		.reset_reg = LP5012_RESET,
206242b8117SDan Murphy 		.lp50xx_regmap_config = &lp5012_regmap_config,
207242b8117SDan Murphy 	},
208242b8117SDan Murphy 	[LP5012] = {
209242b8117SDan Murphy 		.model_id = LP5012,
210242b8117SDan Murphy 		.max_modules = LP5012_MAX_LED_MODULES,
211242b8117SDan Murphy 		.num_leds = LP5012_MAX_LED_MODULES * LP50XX_LEDS_PER_MODULE,
212242b8117SDan Murphy 		.led_brightness0_reg = LP5012_LED0_BRT,
213242b8117SDan Murphy 		.mix_out0_reg = LP5012_OUT0_CLR,
214242b8117SDan Murphy 		.bank_brt_reg = LP5012_BNK_BRT,
215242b8117SDan Murphy 		.bank_mix_reg = LP5012_BNKA_CLR,
216242b8117SDan Murphy 		.reset_reg = LP5012_RESET,
217242b8117SDan Murphy 		.lp50xx_regmap_config = &lp5012_regmap_config,
218242b8117SDan Murphy 	},
219242b8117SDan Murphy 	[LP5018] = {
220242b8117SDan Murphy 		.model_id = LP5018,
221242b8117SDan Murphy 		.max_modules = LP5018_MAX_LED_MODULES,
222242b8117SDan Murphy 		.num_leds = LP5018_MAX_LED_MODULES * LP50XX_LEDS_PER_MODULE,
223242b8117SDan Murphy 		.led_brightness0_reg = LP5024_LED0_BRT,
224242b8117SDan Murphy 		.mix_out0_reg = LP5024_OUT0_CLR,
225242b8117SDan Murphy 		.bank_brt_reg = LP5024_BNK_BRT,
226242b8117SDan Murphy 		.bank_mix_reg = LP5024_BNKA_CLR,
227242b8117SDan Murphy 		.reset_reg = LP5024_RESET,
228242b8117SDan Murphy 		.lp50xx_regmap_config = &lp5024_regmap_config,
229242b8117SDan Murphy 	},
230242b8117SDan Murphy 	[LP5024] = {
231242b8117SDan Murphy 		.model_id = LP5024,
232242b8117SDan Murphy 		.max_modules = LP5024_MAX_LED_MODULES,
233242b8117SDan Murphy 		.num_leds = LP5024_MAX_LED_MODULES * LP50XX_LEDS_PER_MODULE,
234242b8117SDan Murphy 		.led_brightness0_reg = LP5024_LED0_BRT,
235242b8117SDan Murphy 		.mix_out0_reg = LP5024_OUT0_CLR,
236242b8117SDan Murphy 		.bank_brt_reg = LP5024_BNK_BRT,
237242b8117SDan Murphy 		.bank_mix_reg = LP5024_BNKA_CLR,
238242b8117SDan Murphy 		.reset_reg = LP5024_RESET,
239242b8117SDan Murphy 		.lp50xx_regmap_config = &lp5024_regmap_config,
240242b8117SDan Murphy 	},
241242b8117SDan Murphy 	[LP5030] = {
242242b8117SDan Murphy 		.model_id = LP5030,
243242b8117SDan Murphy 		.max_modules = LP5030_MAX_LED_MODULES,
244242b8117SDan Murphy 		.num_leds = LP5030_MAX_LED_MODULES * LP50XX_LEDS_PER_MODULE,
245242b8117SDan Murphy 		.led_brightness0_reg = LP5036_LED0_BRT,
246242b8117SDan Murphy 		.mix_out0_reg = LP5036_OUT0_CLR,
247242b8117SDan Murphy 		.bank_brt_reg = LP5036_BNK_BRT,
248242b8117SDan Murphy 		.bank_mix_reg = LP5036_BNKA_CLR,
249242b8117SDan Murphy 		.reset_reg = LP5036_RESET,
250242b8117SDan Murphy 		.lp50xx_regmap_config = &lp5036_regmap_config,
251242b8117SDan Murphy 	},
252242b8117SDan Murphy 	[LP5036] = {
253242b8117SDan Murphy 		.model_id = LP5036,
254242b8117SDan Murphy 		.max_modules = LP5036_MAX_LED_MODULES,
255242b8117SDan Murphy 		.num_leds = LP5036_MAX_LED_MODULES * LP50XX_LEDS_PER_MODULE,
256242b8117SDan Murphy 		.led_brightness0_reg = LP5036_LED0_BRT,
257242b8117SDan Murphy 		.mix_out0_reg = LP5036_OUT0_CLR,
258242b8117SDan Murphy 		.bank_brt_reg = LP5036_BNK_BRT,
259242b8117SDan Murphy 		.bank_mix_reg = LP5036_BNKA_CLR,
260242b8117SDan Murphy 		.reset_reg = LP5036_RESET,
261242b8117SDan Murphy 		.lp50xx_regmap_config = &lp5036_regmap_config,
262242b8117SDan Murphy 	},
263242b8117SDan Murphy };
264242b8117SDan Murphy 
265242b8117SDan Murphy struct lp50xx_led {
266242b8117SDan Murphy 	struct led_classdev_mc mc_cdev;
267242b8117SDan Murphy 	struct lp50xx *priv;
268242b8117SDan Murphy 	unsigned long bank_modules;
269242b8117SDan Murphy 	u8 ctrl_bank_enabled;
270242b8117SDan Murphy 	int led_number;
271242b8117SDan Murphy };
272242b8117SDan Murphy 
273242b8117SDan Murphy /**
274242b8117SDan Murphy  * struct lp50xx -
275242b8117SDan Murphy  * @enable_gpio: hardware enable gpio
276242b8117SDan Murphy  * @regulator: LED supply regulator pointer
277242b8117SDan Murphy  * @client: pointer to the I2C client
278242b8117SDan Murphy  * @regmap: device register map
279242b8117SDan Murphy  * @dev: pointer to the devices device struct
280242b8117SDan Murphy  * @lock: lock for reading/writing the device
281242b8117SDan Murphy  * @chip_info: chip specific information (ie num_leds)
282242b8117SDan Murphy  * @num_of_banked_leds: holds the number of banked LEDs
283242b8117SDan Murphy  * @leds: array of LED strings
284242b8117SDan Murphy  */
285242b8117SDan Murphy struct lp50xx {
286242b8117SDan Murphy 	struct gpio_desc *enable_gpio;
287242b8117SDan Murphy 	struct regulator *regulator;
288242b8117SDan Murphy 	struct i2c_client *client;
289242b8117SDan Murphy 	struct regmap *regmap;
290242b8117SDan Murphy 	struct device *dev;
291242b8117SDan Murphy 	struct mutex lock;
292242b8117SDan Murphy 	const struct lp50xx_chip_info *chip_info;
293242b8117SDan Murphy 	int num_of_banked_leds;
294242b8117SDan Murphy 
295242b8117SDan Murphy 	/* This needs to be at the end of the struct */
296242b8117SDan Murphy 	struct lp50xx_led leds[];
297242b8117SDan Murphy };
298242b8117SDan Murphy 
mcled_cdev_to_led(struct led_classdev_mc * mc_cdev)299242b8117SDan Murphy static struct lp50xx_led *mcled_cdev_to_led(struct led_classdev_mc *mc_cdev)
300242b8117SDan Murphy {
301242b8117SDan Murphy 	return container_of(mc_cdev, struct lp50xx_led, mc_cdev);
302242b8117SDan Murphy }
303242b8117SDan Murphy 
lp50xx_brightness_set(struct led_classdev * cdev,enum led_brightness brightness)304242b8117SDan Murphy static int lp50xx_brightness_set(struct led_classdev *cdev,
305242b8117SDan Murphy 			     enum led_brightness brightness)
306242b8117SDan Murphy {
307242b8117SDan Murphy 	struct led_classdev_mc *mc_dev = lcdev_to_mccdev(cdev);
308242b8117SDan Murphy 	struct lp50xx_led *led = mcled_cdev_to_led(mc_dev);
309242b8117SDan Murphy 	const struct lp50xx_chip_info *led_chip = led->priv->chip_info;
310242b8117SDan Murphy 	u8 led_offset, reg_val;
311242b8117SDan Murphy 	int ret = 0;
312242b8117SDan Murphy 	int i;
313242b8117SDan Murphy 
314242b8117SDan Murphy 	mutex_lock(&led->priv->lock);
315242b8117SDan Murphy 	if (led->ctrl_bank_enabled)
316242b8117SDan Murphy 		reg_val = led_chip->bank_brt_reg;
317242b8117SDan Murphy 	else
318242b8117SDan Murphy 		reg_val = led_chip->led_brightness0_reg +
319242b8117SDan Murphy 			  led->led_number;
320242b8117SDan Murphy 
321242b8117SDan Murphy 	ret = regmap_write(led->priv->regmap, reg_val, brightness);
322242b8117SDan Murphy 	if (ret) {
323556f15feSAndy Shevchenko 		dev_err(led->priv->dev,
324242b8117SDan Murphy 			"Cannot write brightness value %d\n", ret);
325242b8117SDan Murphy 		goto out;
326242b8117SDan Murphy 	}
327242b8117SDan Murphy 
328242b8117SDan Murphy 	for (i = 0; i < led->mc_cdev.num_colors; i++) {
329242b8117SDan Murphy 		if (led->ctrl_bank_enabled) {
330242b8117SDan Murphy 			reg_val = led_chip->bank_mix_reg + i;
331242b8117SDan Murphy 		} else {
332242b8117SDan Murphy 			led_offset = (led->led_number * 3) + i;
333242b8117SDan Murphy 			reg_val = led_chip->mix_out0_reg + led_offset;
334242b8117SDan Murphy 		}
335242b8117SDan Murphy 
336242b8117SDan Murphy 		ret = regmap_write(led->priv->regmap, reg_val,
337242b8117SDan Murphy 				   mc_dev->subled_info[i].intensity);
338242b8117SDan Murphy 		if (ret) {
339556f15feSAndy Shevchenko 			dev_err(led->priv->dev,
340242b8117SDan Murphy 				"Cannot write intensity value %d\n", ret);
341242b8117SDan Murphy 			goto out;
342242b8117SDan Murphy 		}
343242b8117SDan Murphy 	}
344242b8117SDan Murphy out:
345242b8117SDan Murphy 	mutex_unlock(&led->priv->lock);
346242b8117SDan Murphy 	return ret;
347242b8117SDan Murphy }
348242b8117SDan Murphy 
lp50xx_set_banks(struct lp50xx * priv,u32 led_banks[])349242b8117SDan Murphy static int lp50xx_set_banks(struct lp50xx *priv, u32 led_banks[])
350242b8117SDan Murphy {
351242b8117SDan Murphy 	u8 led_config_lo, led_config_hi;
352242b8117SDan Murphy 	u32 bank_enable_mask = 0;
353242b8117SDan Murphy 	int ret;
354242b8117SDan Murphy 	int i;
355242b8117SDan Murphy 
356242b8117SDan Murphy 	for (i = 0; i < priv->chip_info->max_modules; i++) {
357242b8117SDan Murphy 		if (led_banks[i])
358242b8117SDan Murphy 			bank_enable_mask |= (1 << led_banks[i]);
359242b8117SDan Murphy 	}
360242b8117SDan Murphy 
361b0a82efaSAndy Shevchenko 	led_config_lo = bank_enable_mask;
362b0a82efaSAndy Shevchenko 	led_config_hi = bank_enable_mask >> 8;
363242b8117SDan Murphy 
364242b8117SDan Murphy 	ret = regmap_write(priv->regmap, LP50XX_LED_CFG0, led_config_lo);
365242b8117SDan Murphy 	if (ret)
366242b8117SDan Murphy 		return ret;
367242b8117SDan Murphy 
368242b8117SDan Murphy 	if (priv->chip_info->model_id >= LP5030)
369242b8117SDan Murphy 		ret = regmap_write(priv->regmap, LP5036_LED_CFG1, led_config_hi);
370242b8117SDan Murphy 
371242b8117SDan Murphy 	return ret;
372242b8117SDan Murphy }
373242b8117SDan Murphy 
lp50xx_reset(struct lp50xx * priv)374242b8117SDan Murphy static int lp50xx_reset(struct lp50xx *priv)
375242b8117SDan Murphy {
376242b8117SDan Murphy 	return regmap_write(priv->regmap, priv->chip_info->reset_reg, LP50XX_SW_RESET);
377242b8117SDan Murphy }
378242b8117SDan Murphy 
lp50xx_enable_disable(struct lp50xx * priv,int enable_disable)379242b8117SDan Murphy static int lp50xx_enable_disable(struct lp50xx *priv, int enable_disable)
380242b8117SDan Murphy {
381242b8117SDan Murphy 	int ret;
382242b8117SDan Murphy 
383242b8117SDan Murphy 	ret = gpiod_direction_output(priv->enable_gpio, enable_disable);
384242b8117SDan Murphy 	if (ret)
385242b8117SDan Murphy 		return ret;
386242b8117SDan Murphy 
387242b8117SDan Murphy 	if (enable_disable)
388242b8117SDan Murphy 		return regmap_write(priv->regmap, LP50XX_DEV_CFG0, LP50XX_CHIP_EN);
389242b8117SDan Murphy 	else
390242b8117SDan Murphy 		return regmap_write(priv->regmap, LP50XX_DEV_CFG0, 0);
391242b8117SDan Murphy 
392242b8117SDan Murphy }
393242b8117SDan Murphy 
lp50xx_probe_leds(struct fwnode_handle * child,struct lp50xx * priv,struct lp50xx_led * led,int num_leds)394242b8117SDan Murphy static int lp50xx_probe_leds(struct fwnode_handle *child, struct lp50xx *priv,
395242b8117SDan Murphy 			     struct lp50xx_led *led, int num_leds)
396242b8117SDan Murphy {
397242b8117SDan Murphy 	u32 led_banks[LP5036_MAX_LED_MODULES] = {0};
398242b8117SDan Murphy 	int led_number;
399242b8117SDan Murphy 	int ret;
400242b8117SDan Murphy 
401242b8117SDan Murphy 	if (num_leds > 1) {
402242b8117SDan Murphy 		if (num_leds > priv->chip_info->max_modules) {
403556f15feSAndy Shevchenko 			dev_err(priv->dev, "reg property is invalid\n");
404242b8117SDan Murphy 			return -EINVAL;
405242b8117SDan Murphy 		}
406242b8117SDan Murphy 
407242b8117SDan Murphy 		priv->num_of_banked_leds = num_leds;
408242b8117SDan Murphy 
409242b8117SDan Murphy 		ret = fwnode_property_read_u32_array(child, "reg", led_banks, num_leds);
410242b8117SDan Murphy 		if (ret) {
411556f15feSAndy Shevchenko 			dev_err(priv->dev, "reg property is missing\n");
412242b8117SDan Murphy 			return ret;
413242b8117SDan Murphy 		}
414242b8117SDan Murphy 
415242b8117SDan Murphy 		ret = lp50xx_set_banks(priv, led_banks);
416242b8117SDan Murphy 		if (ret) {
417556f15feSAndy Shevchenko 			dev_err(priv->dev, "Cannot setup banked LEDs\n");
418242b8117SDan Murphy 			return ret;
419242b8117SDan Murphy 		}
420242b8117SDan Murphy 
421242b8117SDan Murphy 		led->ctrl_bank_enabled = 1;
422242b8117SDan Murphy 	} else {
423242b8117SDan Murphy 		ret = fwnode_property_read_u32(child, "reg", &led_number);
424242b8117SDan Murphy 		if (ret) {
425556f15feSAndy Shevchenko 			dev_err(priv->dev, "led reg property missing\n");
426242b8117SDan Murphy 			return ret;
427242b8117SDan Murphy 		}
428242b8117SDan Murphy 
429242b8117SDan Murphy 		if (led_number > priv->chip_info->num_leds) {
430556f15feSAndy Shevchenko 			dev_err(priv->dev, "led-sources property is invalid\n");
431242b8117SDan Murphy 			return -EINVAL;
432242b8117SDan Murphy 		}
433242b8117SDan Murphy 
434242b8117SDan Murphy 		led->led_number = led_number;
435242b8117SDan Murphy 	}
436242b8117SDan Murphy 
437242b8117SDan Murphy 	return 0;
438242b8117SDan Murphy }
439242b8117SDan Murphy 
lp50xx_probe_dt(struct lp50xx * priv)440242b8117SDan Murphy static int lp50xx_probe_dt(struct lp50xx *priv)
441242b8117SDan Murphy {
442242b8117SDan Murphy 	struct fwnode_handle *child = NULL;
443242b8117SDan Murphy 	struct fwnode_handle *led_node = NULL;
444242b8117SDan Murphy 	struct led_init_data init_data = {};
445242b8117SDan Murphy 	struct led_classdev *led_cdev;
446242b8117SDan Murphy 	struct mc_subled *mc_led_info;
447242b8117SDan Murphy 	struct lp50xx_led *led;
448242b8117SDan Murphy 	int ret = -EINVAL;
449242b8117SDan Murphy 	int num_colors;
450242b8117SDan Murphy 	u32 color_id;
451242b8117SDan Murphy 	int i = 0;
452242b8117SDan Murphy 
453242b8117SDan Murphy 	priv->enable_gpio = devm_gpiod_get_optional(priv->dev, "enable", GPIOD_OUT_LOW);
4549a10def9SAndy Shevchenko 	if (IS_ERR(priv->enable_gpio))
4559a10def9SAndy Shevchenko 		return dev_err_probe(priv->dev, PTR_ERR(priv->enable_gpio),
4569a10def9SAndy Shevchenko 				     "Failed to get enable GPIO\n");
457242b8117SDan Murphy 
458242b8117SDan Murphy 	priv->regulator = devm_regulator_get(priv->dev, "vled");
459242b8117SDan Murphy 	if (IS_ERR(priv->regulator))
460242b8117SDan Murphy 		priv->regulator = NULL;
461242b8117SDan Murphy 
462242b8117SDan Murphy 	device_for_each_child_node(priv->dev, child) {
463242b8117SDan Murphy 		led = &priv->leds[i];
464242b8117SDan Murphy 		ret = fwnode_property_count_u32(child, "reg");
465242b8117SDan Murphy 		if (ret < 0) {
466556f15feSAndy Shevchenko 			dev_err(priv->dev, "reg property is invalid\n");
467242b8117SDan Murphy 			goto child_out;
468242b8117SDan Murphy 		}
469242b8117SDan Murphy 
470242b8117SDan Murphy 		ret = lp50xx_probe_leds(child, priv, led, ret);
471242b8117SDan Murphy 		if (ret)
472242b8117SDan Murphy 			goto child_out;
473242b8117SDan Murphy 
474242b8117SDan Murphy 		init_data.fwnode = child;
475242b8117SDan Murphy 		num_colors = 0;
476242b8117SDan Murphy 
477242b8117SDan Murphy 		/*
478242b8117SDan Murphy 		 * There are only 3 LEDs per module otherwise they should be
479242b8117SDan Murphy 		 * banked which also is presented as 3 LEDs.
480242b8117SDan Murphy 		 */
481242b8117SDan Murphy 		mc_led_info = devm_kcalloc(priv->dev, LP50XX_LEDS_PER_MODULE,
482242b8117SDan Murphy 					   sizeof(*mc_led_info), GFP_KERNEL);
4836d8d014cSChristophe JAILLET 		if (!mc_led_info) {
4846d8d014cSChristophe JAILLET 			ret = -ENOMEM;
4856d8d014cSChristophe JAILLET 			goto child_out;
4866d8d014cSChristophe JAILLET 		}
487242b8117SDan Murphy 
488242b8117SDan Murphy 		fwnode_for_each_child_node(child, led_node) {
489242b8117SDan Murphy 			ret = fwnode_property_read_u32(led_node, "color",
490242b8117SDan Murphy 						       &color_id);
491242b8117SDan Murphy 			if (ret) {
492f1e1d532SAndy Shevchenko 				fwnode_handle_put(led_node);
493242b8117SDan Murphy 				dev_err(priv->dev, "Cannot read color\n");
494242b8117SDan Murphy 				goto child_out;
495242b8117SDan Murphy 			}
496242b8117SDan Murphy 
497242b8117SDan Murphy 			mc_led_info[num_colors].color_index = color_id;
498242b8117SDan Murphy 			num_colors++;
499242b8117SDan Murphy 		}
500242b8117SDan Murphy 
501242b8117SDan Murphy 		led->priv = priv;
502242b8117SDan Murphy 		led->mc_cdev.num_colors = num_colors;
503242b8117SDan Murphy 		led->mc_cdev.subled_info = mc_led_info;
504242b8117SDan Murphy 		led_cdev = &led->mc_cdev.led_cdev;
505242b8117SDan Murphy 		led_cdev->brightness_set_blocking = lp50xx_brightness_set;
506242b8117SDan Murphy 
507556f15feSAndy Shevchenko 		ret = devm_led_classdev_multicolor_register_ext(priv->dev,
508242b8117SDan Murphy 						       &led->mc_cdev,
509242b8117SDan Murphy 						       &init_data);
510242b8117SDan Murphy 		if (ret) {
511556f15feSAndy Shevchenko 			dev_err(priv->dev, "led register err: %d\n", ret);
512242b8117SDan Murphy 			goto child_out;
513242b8117SDan Murphy 		}
514242b8117SDan Murphy 		i++;
515242b8117SDan Murphy 	}
516242b8117SDan Murphy 
517242b8117SDan Murphy 	return 0;
518242b8117SDan Murphy 
519242b8117SDan Murphy child_out:
520242b8117SDan Murphy 	fwnode_handle_put(child);
521242b8117SDan Murphy 	return ret;
522242b8117SDan Murphy }
523242b8117SDan Murphy 
lp50xx_probe(struct i2c_client * client)524ea1ff99cSAndy Shevchenko static int lp50xx_probe(struct i2c_client *client)
525242b8117SDan Murphy {
526242b8117SDan Murphy 	struct lp50xx *led;
527242b8117SDan Murphy 	int count;
528242b8117SDan Murphy 	int ret;
529242b8117SDan Murphy 
530242b8117SDan Murphy 	count = device_get_child_node_count(&client->dev);
531242b8117SDan Murphy 	if (!count) {
532242b8117SDan Murphy 		dev_err(&client->dev, "LEDs are not defined in device tree!");
533242b8117SDan Murphy 		return -ENODEV;
534242b8117SDan Murphy 	}
535242b8117SDan Murphy 
536242b8117SDan Murphy 	led = devm_kzalloc(&client->dev, struct_size(led, leds, count),
537242b8117SDan Murphy 			   GFP_KERNEL);
538242b8117SDan Murphy 	if (!led)
539242b8117SDan Murphy 		return -ENOMEM;
540242b8117SDan Murphy 
541242b8117SDan Murphy 	mutex_init(&led->lock);
542242b8117SDan Murphy 	led->client = client;
543242b8117SDan Murphy 	led->dev = &client->dev;
544ea1ff99cSAndy Shevchenko 	led->chip_info = device_get_match_data(&client->dev);
545242b8117SDan Murphy 	i2c_set_clientdata(client, led);
546242b8117SDan Murphy 	led->regmap = devm_regmap_init_i2c(client,
547242b8117SDan Murphy 					led->chip_info->lp50xx_regmap_config);
548242b8117SDan Murphy 	if (IS_ERR(led->regmap)) {
549242b8117SDan Murphy 		ret = PTR_ERR(led->regmap);
550242b8117SDan Murphy 		dev_err(&client->dev, "Failed to allocate register map: %d\n",
551242b8117SDan Murphy 			ret);
552242b8117SDan Murphy 		return ret;
553242b8117SDan Murphy 	}
554242b8117SDan Murphy 
555242b8117SDan Murphy 	ret = lp50xx_reset(led);
556242b8117SDan Murphy 	if (ret)
557242b8117SDan Murphy 		return ret;
558242b8117SDan Murphy 
559242b8117SDan Murphy 	ret = lp50xx_enable_disable(led, 1);
560242b8117SDan Murphy 	if (ret)
561242b8117SDan Murphy 		return ret;
562242b8117SDan Murphy 
563242b8117SDan Murphy 	return lp50xx_probe_dt(led);
564242b8117SDan Murphy }
565242b8117SDan Murphy 
lp50xx_remove(struct i2c_client * client)566ed5c2f5fSUwe Kleine-König static void lp50xx_remove(struct i2c_client *client)
567242b8117SDan Murphy {
568242b8117SDan Murphy 	struct lp50xx *led = i2c_get_clientdata(client);
569242b8117SDan Murphy 	int ret;
570242b8117SDan Murphy 
571242b8117SDan Murphy 	ret = lp50xx_enable_disable(led, 0);
57273bce575SUwe Kleine-König 	if (ret)
573556f15feSAndy Shevchenko 		dev_err(led->dev, "Failed to disable chip\n");
574242b8117SDan Murphy 
575242b8117SDan Murphy 	if (led->regulator) {
576242b8117SDan Murphy 		ret = regulator_disable(led->regulator);
577242b8117SDan Murphy 		if (ret)
578556f15feSAndy Shevchenko 			dev_err(led->dev, "Failed to disable regulator\n");
579242b8117SDan Murphy 	}
580242b8117SDan Murphy 
581242b8117SDan Murphy 	mutex_destroy(&led->lock);
582242b8117SDan Murphy }
583242b8117SDan Murphy 
584242b8117SDan Murphy static const struct i2c_device_id lp50xx_id[] = {
585ea1ff99cSAndy Shevchenko 	{ "lp5009", (kernel_ulong_t)&lp50xx_chip_info_tbl[LP5009] },
586ea1ff99cSAndy Shevchenko 	{ "lp5012", (kernel_ulong_t)&lp50xx_chip_info_tbl[LP5012] },
587ea1ff99cSAndy Shevchenko 	{ "lp5018", (kernel_ulong_t)&lp50xx_chip_info_tbl[LP5018] },
588ea1ff99cSAndy Shevchenko 	{ "lp5024", (kernel_ulong_t)&lp50xx_chip_info_tbl[LP5024] },
589ea1ff99cSAndy Shevchenko 	{ "lp5030", (kernel_ulong_t)&lp50xx_chip_info_tbl[LP5030] },
590ea1ff99cSAndy Shevchenko 	{ "lp5036", (kernel_ulong_t)&lp50xx_chip_info_tbl[LP5036] },
591242b8117SDan Murphy 	{ }
592242b8117SDan Murphy };
593242b8117SDan Murphy MODULE_DEVICE_TABLE(i2c, lp50xx_id);
594242b8117SDan Murphy 
595242b8117SDan Murphy static const struct of_device_id of_lp50xx_leds_match[] = {
596ea1ff99cSAndy Shevchenko 	{ .compatible = "ti,lp5009", .data = &lp50xx_chip_info_tbl[LP5009] },
597ea1ff99cSAndy Shevchenko 	{ .compatible = "ti,lp5012", .data = &lp50xx_chip_info_tbl[LP5012] },
598ea1ff99cSAndy Shevchenko 	{ .compatible = "ti,lp5018", .data = &lp50xx_chip_info_tbl[LP5018] },
599ea1ff99cSAndy Shevchenko 	{ .compatible = "ti,lp5024", .data = &lp50xx_chip_info_tbl[LP5024] },
600ea1ff99cSAndy Shevchenko 	{ .compatible = "ti,lp5030", .data = &lp50xx_chip_info_tbl[LP5030] },
601ea1ff99cSAndy Shevchenko 	{ .compatible = "ti,lp5036", .data = &lp50xx_chip_info_tbl[LP5036] },
602ea1ff99cSAndy Shevchenko 	{}
603242b8117SDan Murphy };
604242b8117SDan Murphy MODULE_DEVICE_TABLE(of, of_lp50xx_leds_match);
605242b8117SDan Murphy 
606242b8117SDan Murphy static struct i2c_driver lp50xx_driver = {
607242b8117SDan Murphy 	.driver = {
608242b8117SDan Murphy 		.name	= "lp50xx",
609242b8117SDan Murphy 		.of_match_table = of_lp50xx_leds_match,
610242b8117SDan Murphy 	},
611*d9ff8a8eSUwe Kleine-König 	.probe		= lp50xx_probe,
612242b8117SDan Murphy 	.remove		= lp50xx_remove,
613242b8117SDan Murphy 	.id_table	= lp50xx_id,
614242b8117SDan Murphy };
615242b8117SDan Murphy module_i2c_driver(lp50xx_driver);
616242b8117SDan Murphy 
617242b8117SDan Murphy MODULE_DESCRIPTION("Texas Instruments LP50XX LED driver");
618242b8117SDan Murphy MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");
619242b8117SDan Murphy MODULE_LICENSE("GPL v2");
620