xref: /openbmc/linux/drivers/leds/leds-lm3532.c (revision bc1b8492)
1bc1b8492SDan Murphy // SPDX-License-Identifier: GPL-2.0
2bc1b8492SDan Murphy // TI LM3532 LED driver
3bc1b8492SDan Murphy // Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/
4bc1b8492SDan Murphy 
5bc1b8492SDan Murphy #include <linux/i2c.h>
6bc1b8492SDan Murphy #include <linux/leds.h>
7bc1b8492SDan Murphy #include <linux/slab.h>
8bc1b8492SDan Murphy #include <linux/regmap.h>
9bc1b8492SDan Murphy #include <linux/types.h>
10bc1b8492SDan Murphy #include <linux/regulator/consumer.h>
11bc1b8492SDan Murphy #include <linux/module.h>
12bc1b8492SDan Murphy #include <uapi/linux/uleds.h>
13bc1b8492SDan Murphy #include <linux/gpio/consumer.h>
14bc1b8492SDan Murphy 
15bc1b8492SDan Murphy #define LM3532_NAME "lm3532-led"
16bc1b8492SDan Murphy #define LM3532_BL_MODE_MANUAL	0x00
17bc1b8492SDan Murphy #define LM3532_BL_MODE_ALS	0x01
18bc1b8492SDan Murphy 
19bc1b8492SDan Murphy #define LM3532_REG_OUTPUT_CFG	0x10
20bc1b8492SDan Murphy #define LM3532_REG_STARTSHUT_RAMP	0x11
21bc1b8492SDan Murphy #define LM3532_REG_RT_RAMP	0x12
22bc1b8492SDan Murphy #define LM3532_REG_PWM_A_CFG	0x13
23bc1b8492SDan Murphy #define LM3532_REG_PWM_B_CFG	0x14
24bc1b8492SDan Murphy #define LM3532_REG_PWM_C_CFG	0x15
25bc1b8492SDan Murphy #define LM3532_REG_ZONE_CFG_A	0x16
26bc1b8492SDan Murphy #define LM3532_REG_CTRL_A_BRT	0x17
27bc1b8492SDan Murphy #define LM3532_REG_ZONE_CFG_B	0x18
28bc1b8492SDan Murphy #define LM3532_REG_CTRL_B_BRT	0x19
29bc1b8492SDan Murphy #define LM3532_REG_ZONE_CFG_C	0x1a
30bc1b8492SDan Murphy #define LM3532_REG_CTRL_C_BRT	0x1b
31bc1b8492SDan Murphy #define LM3532_REG_ENABLE	0x1d
32bc1b8492SDan Murphy #define LM3532_ALS_CONFIG	0x23
33bc1b8492SDan Murphy #define LM3532_REG_ZN_0_HI	0x60
34bc1b8492SDan Murphy #define LM3532_REG_ZN_0_LO	0x61
35bc1b8492SDan Murphy #define LM3532_REG_ZN_1_HI	0x62
36bc1b8492SDan Murphy #define LM3532_REG_ZN_1_LO	0x63
37bc1b8492SDan Murphy #define LM3532_REG_ZN_2_HI	0x64
38bc1b8492SDan Murphy #define LM3532_REG_ZN_2_LO	0x65
39bc1b8492SDan Murphy #define LM3532_REG_ZN_3_HI	0x66
40bc1b8492SDan Murphy #define LM3532_REG_ZN_3_LO	0x67
41bc1b8492SDan Murphy #define LM3532_REG_MAX		0x7e
42bc1b8492SDan Murphy 
43bc1b8492SDan Murphy /* Contorl Enable */
44bc1b8492SDan Murphy #define LM3532_CTRL_A_ENABLE	BIT(0)
45bc1b8492SDan Murphy #define LM3532_CTRL_B_ENABLE	BIT(1)
46bc1b8492SDan Murphy #define LM3532_CTRL_C_ENABLE	BIT(2)
47bc1b8492SDan Murphy 
48bc1b8492SDan Murphy /* PWM Zone Control */
49bc1b8492SDan Murphy #define LM3532_PWM_ZONE_MASK	0x7c
50bc1b8492SDan Murphy #define LM3532_PWM_ZONE_0_EN	BIT(2)
51bc1b8492SDan Murphy #define LM3532_PWM_ZONE_1_EN	BIT(3)
52bc1b8492SDan Murphy #define LM3532_PWM_ZONE_2_EN	BIT(4)
53bc1b8492SDan Murphy #define LM3532_PWM_ZONE_3_EN	BIT(5)
54bc1b8492SDan Murphy #define LM3532_PWM_ZONE_4_EN	BIT(6)
55bc1b8492SDan Murphy 
56bc1b8492SDan Murphy /* Brightness Configuration */
57bc1b8492SDan Murphy #define LM3532_I2C_CTRL		BIT(0)
58bc1b8492SDan Murphy #define LM3532_ALS_CTRL		0
59bc1b8492SDan Murphy #define LM3532_LINEAR_MAP	BIT(1)
60bc1b8492SDan Murphy #define LM3532_ZONE_MASK	(BIT(2) | BIT(3) | BIT(4))
61bc1b8492SDan Murphy #define LM3532_ZONE_0		0
62bc1b8492SDan Murphy #define LM3532_ZONE_1		BIT(2)
63bc1b8492SDan Murphy #define LM3532_ZONE_2		BIT(3)
64bc1b8492SDan Murphy #define LM3532_ZONE_3		(BIT(2) | BIT(3))
65bc1b8492SDan Murphy #define LM3532_ZONE_4		BIT(4)
66bc1b8492SDan Murphy 
67bc1b8492SDan Murphy #define LM3532_ENABLE_ALS	BIT(3)
68bc1b8492SDan Murphy #define LM3532_ALS_SEL_SHIFT	6
69bc1b8492SDan Murphy 
70bc1b8492SDan Murphy /* Zone Boundary Register */
71bc1b8492SDan Murphy #define LM3532_ALS_WINDOW_mV	2000
72bc1b8492SDan Murphy #define LM3532_ALS_ZB_MAX	4
73bc1b8492SDan Murphy #define LM3532_ALS_OFFSET_mV	2
74bc1b8492SDan Murphy 
75bc1b8492SDan Murphy #define LM3532_CONTROL_A	0
76bc1b8492SDan Murphy #define LM3532_CONTROL_B	1
77bc1b8492SDan Murphy #define LM3532_CONTROL_C	2
78bc1b8492SDan Murphy #define LM3532_MAX_CONTROL_BANKS 3
79bc1b8492SDan Murphy #define LM3532_MAX_LED_STRINGS	3
80bc1b8492SDan Murphy 
81bc1b8492SDan Murphy #define LM3532_OUTPUT_CFG_MASK	0x3
82bc1b8492SDan Murphy #define LM3532_BRT_VAL_ADJUST	8
83bc1b8492SDan Murphy #define LM3532_RAMP_DOWN_SHIFT	3
84bc1b8492SDan Murphy 
85bc1b8492SDan Murphy #define LM3532_NUM_RAMP_VALS	8
86bc1b8492SDan Murphy #define LM3532_NUM_AVG_VALS	8
87bc1b8492SDan Murphy #define LM3532_NUM_IMP_VALS	32
88bc1b8492SDan Murphy 
89bc1b8492SDan Murphy /*
90bc1b8492SDan Murphy  * struct lm3532_als_data
91bc1b8492SDan Murphy  * @config - value of ALS configuration register
92bc1b8492SDan Murphy  * @als1_imp_sel - value of ALS1 resistor select register
93bc1b8492SDan Murphy  * @als2_imp_sel - value of ALS2 resistor select register
94bc1b8492SDan Murphy  * @als_avrg_time - ALS averaging time
95bc1b8492SDan Murphy  * @als_input_mode - ALS input mode for brightness control
96bc1b8492SDan Murphy  * @als_vmin - Minimum ALS voltage
97bc1b8492SDan Murphy  * @als_vmax - Maximum ALS voltage
98bc1b8492SDan Murphy  * @zone_lo - values of ALS lo ZB(Zone Boundary) registers
99bc1b8492SDan Murphy  * @zone_hi - values of ALS hi ZB(Zone Boundary) registers
100bc1b8492SDan Murphy  */
101bc1b8492SDan Murphy struct lm3532_als_data {
102bc1b8492SDan Murphy 	u8 config;
103bc1b8492SDan Murphy 	u8 als1_imp_sel;
104bc1b8492SDan Murphy 	u8 als2_imp_sel;
105bc1b8492SDan Murphy 	u8 als_avrg_time;
106bc1b8492SDan Murphy 	u8 als_input_mode;
107bc1b8492SDan Murphy 	u32 als_vmin;
108bc1b8492SDan Murphy 	u32 als_vmax;
109bc1b8492SDan Murphy 	u8 zones_lo[LM3532_ALS_ZB_MAX];
110bc1b8492SDan Murphy 	u8 zones_hi[LM3532_ALS_ZB_MAX];
111bc1b8492SDan Murphy };
112bc1b8492SDan Murphy 
113bc1b8492SDan Murphy /**
114bc1b8492SDan Murphy  * struct lm3532_led
115bc1b8492SDan Murphy  * @led_dev: led class device
116bc1b8492SDan Murphy  * @priv - Pointer the device data structure
117bc1b8492SDan Murphy  * @control_bank - Control bank the LED is associated to
118bc1b8492SDan Murphy  * @mode - Mode of the LED string
119bc1b8492SDan Murphy  * @num_leds - Number of LED strings are supported in this array
120bc1b8492SDan Murphy  * @led_strings - The LED strings supported in this array
121bc1b8492SDan Murphy  * @label - LED label
122bc1b8492SDan Murphy  */
123bc1b8492SDan Murphy struct lm3532_led {
124bc1b8492SDan Murphy 	struct led_classdev led_dev;
125bc1b8492SDan Murphy 	struct lm3532_data *priv;
126bc1b8492SDan Murphy 
127bc1b8492SDan Murphy 	int control_bank;
128bc1b8492SDan Murphy 	int mode;
129bc1b8492SDan Murphy 	int num_leds;
130bc1b8492SDan Murphy 	u32 led_strings[LM3532_MAX_CONTROL_BANKS];
131bc1b8492SDan Murphy 	char label[LED_MAX_NAME_SIZE];
132bc1b8492SDan Murphy };
133bc1b8492SDan Murphy 
134bc1b8492SDan Murphy /**
135bc1b8492SDan Murphy  * struct lm3532_data
136bc1b8492SDan Murphy  * @enable_gpio - Hardware enable gpio
137bc1b8492SDan Murphy  * @regulator: regulator
138bc1b8492SDan Murphy  * @client: i2c client
139bc1b8492SDan Murphy  * @regmap - Devices register map
140bc1b8492SDan Murphy  * @dev - Pointer to the devices device struct
141bc1b8492SDan Murphy  * @lock - Lock for reading/writing the device
142bc1b8492SDan Murphy  * @als_data - Pointer to the als data struct
143bc1b8492SDan Murphy  * @runtime_ramp_up - Runtime ramp up setting
144bc1b8492SDan Murphy  * @runtime_ramp_down - Runtime ramp down setting
145bc1b8492SDan Murphy  * @leds - Array of LED strings
146bc1b8492SDan Murphy  */
147bc1b8492SDan Murphy struct lm3532_data {
148bc1b8492SDan Murphy 	struct gpio_desc *enable_gpio;
149bc1b8492SDan Murphy 	struct regulator *regulator;
150bc1b8492SDan Murphy 	struct i2c_client *client;
151bc1b8492SDan Murphy 	struct regmap *regmap;
152bc1b8492SDan Murphy 	struct device *dev;
153bc1b8492SDan Murphy 	struct mutex lock;
154bc1b8492SDan Murphy 
155bc1b8492SDan Murphy 	struct lm3532_als_data *als_data;
156bc1b8492SDan Murphy 
157bc1b8492SDan Murphy 	u32 runtime_ramp_up;
158bc1b8492SDan Murphy 	u32 runtime_ramp_down;
159bc1b8492SDan Murphy 
160bc1b8492SDan Murphy 	struct lm3532_led leds[];
161bc1b8492SDan Murphy };
162bc1b8492SDan Murphy 
163bc1b8492SDan Murphy static const struct reg_default lm3532_reg_defs[] = {
164bc1b8492SDan Murphy 	{LM3532_REG_OUTPUT_CFG, 0xe4},
165bc1b8492SDan Murphy 	{LM3532_REG_STARTSHUT_RAMP, 0xc0},
166bc1b8492SDan Murphy 	{LM3532_REG_RT_RAMP, 0xc0},
167bc1b8492SDan Murphy 	{LM3532_REG_PWM_A_CFG, 0x82},
168bc1b8492SDan Murphy 	{LM3532_REG_PWM_B_CFG, 0x82},
169bc1b8492SDan Murphy 	{LM3532_REG_PWM_C_CFG, 0x82},
170bc1b8492SDan Murphy 	{LM3532_REG_ZONE_CFG_A, 0xf1},
171bc1b8492SDan Murphy 	{LM3532_REG_CTRL_A_BRT, 0xf3},
172bc1b8492SDan Murphy 	{LM3532_REG_ZONE_CFG_B, 0xf1},
173bc1b8492SDan Murphy 	{LM3532_REG_CTRL_B_BRT, 0xf3},
174bc1b8492SDan Murphy 	{LM3532_REG_ZONE_CFG_C, 0xf1},
175bc1b8492SDan Murphy 	{LM3532_REG_CTRL_C_BRT, 0xf3},
176bc1b8492SDan Murphy 	{LM3532_REG_ENABLE, 0xf8},
177bc1b8492SDan Murphy 	{LM3532_ALS_CONFIG, 0x44},
178bc1b8492SDan Murphy 	{LM3532_REG_ZN_0_HI, 0x35},
179bc1b8492SDan Murphy 	{LM3532_REG_ZN_0_LO, 0x33},
180bc1b8492SDan Murphy 	{LM3532_REG_ZN_1_HI, 0x6a},
181bc1b8492SDan Murphy 	{LM3532_REG_ZN_1_LO, 0x66},
182bc1b8492SDan Murphy 	{LM3532_REG_ZN_2_HI, 0xa1},
183bc1b8492SDan Murphy 	{LM3532_REG_ZN_2_LO, 0x99},
184bc1b8492SDan Murphy 	{LM3532_REG_ZN_3_HI, 0xdc},
185bc1b8492SDan Murphy 	{LM3532_REG_ZN_3_LO, 0xcc},
186bc1b8492SDan Murphy };
187bc1b8492SDan Murphy 
188bc1b8492SDan Murphy static const struct regmap_config lm3532_regmap_config = {
189bc1b8492SDan Murphy 	.reg_bits = 8,
190bc1b8492SDan Murphy 	.val_bits = 8,
191bc1b8492SDan Murphy 
192bc1b8492SDan Murphy 	.max_register = LM3532_REG_MAX,
193bc1b8492SDan Murphy 	.reg_defaults = lm3532_reg_defs,
194bc1b8492SDan Murphy 	.num_reg_defaults = ARRAY_SIZE(lm3532_reg_defs),
195bc1b8492SDan Murphy 	.cache_type = REGCACHE_FLAT,
196bc1b8492SDan Murphy };
197bc1b8492SDan Murphy 
198bc1b8492SDan Murphy const static int als_imp_table[LM3532_NUM_IMP_VALS] = {37000, 18500, 12330,
199bc1b8492SDan Murphy 						       92500, 7400, 6170, 5290,
200bc1b8492SDan Murphy 						       4630, 4110, 3700, 3360,
201bc1b8492SDan Murphy 						       3080, 2850, 2640, 2440,
202bc1b8492SDan Murphy 						       2310, 2180, 2060, 1950,
203bc1b8492SDan Murphy 						       1850, 1760, 1680, 1610,
204bc1b8492SDan Murphy 						       1540, 1480, 1420, 1370,
205bc1b8492SDan Murphy 						       1320, 1280, 1230, 1190};
206bc1b8492SDan Murphy static int lm3532_get_als_imp_index(int als_imped)
207bc1b8492SDan Murphy {
208bc1b8492SDan Murphy 	int i;
209bc1b8492SDan Murphy 
210bc1b8492SDan Murphy 	if (als_imped > als_imp_table[1])
211bc1b8492SDan Murphy 		return 0;
212bc1b8492SDan Murphy 
213bc1b8492SDan Murphy 	if (als_imped < als_imp_table[LM3532_NUM_IMP_VALS - 1])
214bc1b8492SDan Murphy 		return LM3532_NUM_IMP_VALS - 1;
215bc1b8492SDan Murphy 
216bc1b8492SDan Murphy 	for (i = 1; i < LM3532_NUM_IMP_VALS; i++) {
217bc1b8492SDan Murphy 		if (als_imped == als_imp_table[i])
218bc1b8492SDan Murphy 			return i;
219bc1b8492SDan Murphy 
220bc1b8492SDan Murphy 		/* Find an approximate index by looking up the table */
221bc1b8492SDan Murphy 		if (als_imped < als_imp_table[i - 1] &&
222bc1b8492SDan Murphy 		    als_imped > als_imp_table[i]) {
223bc1b8492SDan Murphy 			if (als_imped - als_imp_table[i - 1] <
224bc1b8492SDan Murphy 			    als_imp_table[i] - als_imped)
225bc1b8492SDan Murphy 				return i + 1;
226bc1b8492SDan Murphy 			else
227bc1b8492SDan Murphy 				return i;
228bc1b8492SDan Murphy 		}
229bc1b8492SDan Murphy 	}
230bc1b8492SDan Murphy 
231bc1b8492SDan Murphy 	return -EINVAL;
232bc1b8492SDan Murphy }
233bc1b8492SDan Murphy 
234bc1b8492SDan Murphy static int lm3532_get_index(const int table[], int size, int value)
235bc1b8492SDan Murphy {
236bc1b8492SDan Murphy 	int i;
237bc1b8492SDan Murphy 
238bc1b8492SDan Murphy 	for (i = 1; i < size; i++) {
239bc1b8492SDan Murphy 		if (value == table[i])
240bc1b8492SDan Murphy 			return i;
241bc1b8492SDan Murphy 
242bc1b8492SDan Murphy 		/* Find an approximate index by looking up the table */
243bc1b8492SDan Murphy 		if (value > table[i - 1] &&
244bc1b8492SDan Murphy 		    value < table[i]) {
245bc1b8492SDan Murphy 			if (value - table[i - 1] < table[i] - value)
246bc1b8492SDan Murphy 				return i - 1;
247bc1b8492SDan Murphy 			else
248bc1b8492SDan Murphy 				return i;
249bc1b8492SDan Murphy 		}
250bc1b8492SDan Murphy 	}
251bc1b8492SDan Murphy 
252bc1b8492SDan Murphy 	return -EINVAL;
253bc1b8492SDan Murphy }
254bc1b8492SDan Murphy 
255bc1b8492SDan Murphy const static int als_avrg_table[LM3532_NUM_AVG_VALS] = {17920, 35840, 71680,
256bc1b8492SDan Murphy 							1433360, 286720, 573440,
257bc1b8492SDan Murphy 							1146880, 2293760};
258bc1b8492SDan Murphy static int lm3532_get_als_avg_index(int avg_time)
259bc1b8492SDan Murphy {
260bc1b8492SDan Murphy 	if (avg_time <= als_avrg_table[0])
261bc1b8492SDan Murphy 		return 0;
262bc1b8492SDan Murphy 
263bc1b8492SDan Murphy 	if (avg_time > als_avrg_table[LM3532_NUM_AVG_VALS - 1])
264bc1b8492SDan Murphy 		return LM3532_NUM_AVG_VALS - 1;
265bc1b8492SDan Murphy 
266bc1b8492SDan Murphy 	return lm3532_get_index(&als_avrg_table[0], LM3532_NUM_AVG_VALS,
267bc1b8492SDan Murphy 				avg_time);
268bc1b8492SDan Murphy }
269bc1b8492SDan Murphy 
270bc1b8492SDan Murphy const static int ramp_table[LM3532_NUM_RAMP_VALS] = { 8, 1024, 2048, 4096, 8192,
271bc1b8492SDan Murphy 						     16384, 32768, 65536};
272bc1b8492SDan Murphy static int lm3532_get_ramp_index(int ramp_time)
273bc1b8492SDan Murphy {
274bc1b8492SDan Murphy 	if (ramp_time <= ramp_table[0])
275bc1b8492SDan Murphy 		return 0;
276bc1b8492SDan Murphy 
277bc1b8492SDan Murphy 	if (ramp_time > ramp_table[LM3532_NUM_RAMP_VALS - 1])
278bc1b8492SDan Murphy 		return LM3532_NUM_RAMP_VALS - 1;
279bc1b8492SDan Murphy 
280bc1b8492SDan Murphy 	return lm3532_get_index(&ramp_table[0], LM3532_NUM_RAMP_VALS,
281bc1b8492SDan Murphy 				ramp_time);
282bc1b8492SDan Murphy }
283bc1b8492SDan Murphy 
284bc1b8492SDan Murphy static int lm3532_led_enable(struct lm3532_led *led_data)
285bc1b8492SDan Murphy {
286bc1b8492SDan Murphy 	int ctrl_en_val = BIT(led_data->control_bank);
287bc1b8492SDan Murphy 	int ret;
288bc1b8492SDan Murphy 
289bc1b8492SDan Murphy 	ret = regmap_update_bits(led_data->priv->regmap, LM3532_REG_ENABLE,
290bc1b8492SDan Murphy 					 ctrl_en_val, ctrl_en_val);
291bc1b8492SDan Murphy 	if (ret) {
292bc1b8492SDan Murphy 		dev_err(led_data->priv->dev, "Failed to set ctrl:%d\n", ret);
293bc1b8492SDan Murphy 		return ret;
294bc1b8492SDan Murphy 	}
295bc1b8492SDan Murphy 
296bc1b8492SDan Murphy 	return regulator_enable(led_data->priv->regulator);
297bc1b8492SDan Murphy }
298bc1b8492SDan Murphy 
299bc1b8492SDan Murphy static int lm3532_led_disable(struct lm3532_led *led_data)
300bc1b8492SDan Murphy {
301bc1b8492SDan Murphy 	int ctrl_en_val = BIT(led_data->control_bank);
302bc1b8492SDan Murphy 	int ret;
303bc1b8492SDan Murphy 
304bc1b8492SDan Murphy 	ret = regmap_update_bits(led_data->priv->regmap, LM3532_REG_ENABLE,
305bc1b8492SDan Murphy 					 ctrl_en_val, ~ctrl_en_val);
306bc1b8492SDan Murphy 	if (ret) {
307bc1b8492SDan Murphy 		dev_err(led_data->priv->dev, "Failed to set ctrl:%d\n", ret);
308bc1b8492SDan Murphy 		return ret;
309bc1b8492SDan Murphy 	}
310bc1b8492SDan Murphy 
311bc1b8492SDan Murphy 	return regulator_disable(led_data->priv->regulator);
312bc1b8492SDan Murphy }
313bc1b8492SDan Murphy 
314bc1b8492SDan Murphy static int lm3532_brightness_set(struct led_classdev *led_cdev,
315bc1b8492SDan Murphy 				 enum led_brightness brt_val)
316bc1b8492SDan Murphy {
317bc1b8492SDan Murphy 	struct lm3532_led *led =
318bc1b8492SDan Murphy 			container_of(led_cdev, struct lm3532_led, led_dev);
319bc1b8492SDan Murphy 	u8 brightness_reg;
320bc1b8492SDan Murphy 	int ret;
321bc1b8492SDan Murphy 
322bc1b8492SDan Murphy 	mutex_lock(&led->priv->lock);
323bc1b8492SDan Murphy 
324bc1b8492SDan Murphy 	if (led->mode == LM3532_BL_MODE_ALS) {
325bc1b8492SDan Murphy 		if (brt_val > LED_OFF)
326bc1b8492SDan Murphy 			ret = lm3532_led_enable(led);
327bc1b8492SDan Murphy 		else
328bc1b8492SDan Murphy 			ret = lm3532_led_disable(led);
329bc1b8492SDan Murphy 
330bc1b8492SDan Murphy 		goto unlock;
331bc1b8492SDan Murphy 	}
332bc1b8492SDan Murphy 
333bc1b8492SDan Murphy 	if (brt_val == LED_OFF) {
334bc1b8492SDan Murphy 		ret = lm3532_led_disable(led);
335bc1b8492SDan Murphy 		goto unlock;
336bc1b8492SDan Murphy 	}
337bc1b8492SDan Murphy 
338bc1b8492SDan Murphy 	ret = lm3532_led_enable(led);
339bc1b8492SDan Murphy 	if (ret)
340bc1b8492SDan Murphy 		goto unlock;
341bc1b8492SDan Murphy 
342bc1b8492SDan Murphy 	brightness_reg = LM3532_REG_CTRL_A_BRT + led->control_bank * 2;
343bc1b8492SDan Murphy 	brt_val = brt_val / LM3532_BRT_VAL_ADJUST;
344bc1b8492SDan Murphy 
345bc1b8492SDan Murphy 	ret = regmap_write(led->priv->regmap, brightness_reg, brt_val);
346bc1b8492SDan Murphy 
347bc1b8492SDan Murphy unlock:
348bc1b8492SDan Murphy 	mutex_unlock(&led->priv->lock);
349bc1b8492SDan Murphy 	return ret;
350bc1b8492SDan Murphy }
351bc1b8492SDan Murphy 
352bc1b8492SDan Murphy static int lm3532_init_registers(struct lm3532_led *led)
353bc1b8492SDan Murphy {
354bc1b8492SDan Murphy 	struct lm3532_data *drvdata = led->priv;
355bc1b8492SDan Murphy 	unsigned int runtime_ramp_val;
356bc1b8492SDan Murphy 	unsigned int output_cfg_val = 0;
357bc1b8492SDan Murphy 	unsigned int output_cfg_shift = 0;
358bc1b8492SDan Murphy 	unsigned int output_cfg_mask = 0;
359bc1b8492SDan Murphy 	int ret, i;
360bc1b8492SDan Murphy 
361bc1b8492SDan Murphy 	for (i = 0; i < led->num_leds; i++) {
362bc1b8492SDan Murphy 		output_cfg_shift = led->led_strings[i] * 2;
363bc1b8492SDan Murphy 		output_cfg_val |= (led->control_bank << output_cfg_shift);
364bc1b8492SDan Murphy 		output_cfg_mask |= LM3532_OUTPUT_CFG_MASK << output_cfg_shift;
365bc1b8492SDan Murphy 	}
366bc1b8492SDan Murphy 
367bc1b8492SDan Murphy 	ret = regmap_update_bits(drvdata->regmap, LM3532_REG_OUTPUT_CFG,
368bc1b8492SDan Murphy 				 output_cfg_mask, output_cfg_val);
369bc1b8492SDan Murphy 	if (ret)
370bc1b8492SDan Murphy 		return ret;
371bc1b8492SDan Murphy 
372bc1b8492SDan Murphy 	runtime_ramp_val = drvdata->runtime_ramp_up |
373bc1b8492SDan Murphy 			 (drvdata->runtime_ramp_down << LM3532_RAMP_DOWN_SHIFT);
374bc1b8492SDan Murphy 
375bc1b8492SDan Murphy 	return regmap_write(drvdata->regmap, LM3532_REG_RT_RAMP,
376bc1b8492SDan Murphy 			    runtime_ramp_val);
377bc1b8492SDan Murphy }
378bc1b8492SDan Murphy 
379bc1b8492SDan Murphy static int lm3532_als_configure(struct lm3532_data *priv,
380bc1b8492SDan Murphy 				struct lm3532_led *led)
381bc1b8492SDan Murphy {
382bc1b8492SDan Murphy 	struct lm3532_als_data *als = priv->als_data;
383bc1b8492SDan Murphy 	u32 als_vmin, als_vmax, als_vstep;
384bc1b8492SDan Murphy 	int zone_reg = LM3532_REG_ZN_0_HI;
385bc1b8492SDan Murphy 	int brightnes_config_reg;
386bc1b8492SDan Murphy 	int ret;
387bc1b8492SDan Murphy 	int i;
388bc1b8492SDan Murphy 
389bc1b8492SDan Murphy 	als_vmin = als->als_vmin;
390bc1b8492SDan Murphy 	als_vmax = als->als_vmax;
391bc1b8492SDan Murphy 
392bc1b8492SDan Murphy 	als_vstep = (als_vmax - als_vmin) / ((LM3532_ALS_ZB_MAX + 1) * 2);
393bc1b8492SDan Murphy 
394bc1b8492SDan Murphy 	for (i = 0; i < LM3532_ALS_ZB_MAX; i++) {
395bc1b8492SDan Murphy 		als->zones_lo[i] = ((als_vmin + als_vstep + (i * als_vstep)) *
396bc1b8492SDan Murphy 				LED_FULL) / 1000;
397bc1b8492SDan Murphy 		als->zones_hi[i] = ((als_vmin + LM3532_ALS_OFFSET_mV +
398bc1b8492SDan Murphy 				als_vstep + (i * als_vstep)) * LED_FULL) / 1000;
399bc1b8492SDan Murphy 
400bc1b8492SDan Murphy 		zone_reg = LM3532_REG_ZN_0_HI + i * 2;
401bc1b8492SDan Murphy 		ret = regmap_write(priv->regmap, zone_reg, als->zones_lo[i]);
402bc1b8492SDan Murphy 		if (ret)
403bc1b8492SDan Murphy 			return ret;
404bc1b8492SDan Murphy 
405bc1b8492SDan Murphy 		zone_reg += 1;
406bc1b8492SDan Murphy 		ret = regmap_write(priv->regmap, zone_reg, als->zones_hi[i]);
407bc1b8492SDan Murphy 		if (ret)
408bc1b8492SDan Murphy 			return ret;
409bc1b8492SDan Murphy 	}
410bc1b8492SDan Murphy 
411bc1b8492SDan Murphy 	als->config = (als->als_avrg_time | (LM3532_ENABLE_ALS) |
412bc1b8492SDan Murphy 		(als->als_input_mode << LM3532_ALS_SEL_SHIFT));
413bc1b8492SDan Murphy 
414bc1b8492SDan Murphy 	ret = regmap_write(priv->regmap, LM3532_ALS_CONFIG, als->config);
415bc1b8492SDan Murphy 	if (ret)
416bc1b8492SDan Murphy 		return ret;
417bc1b8492SDan Murphy 
418bc1b8492SDan Murphy 	brightnes_config_reg = LM3532_REG_ZONE_CFG_A + led->control_bank * 2;
419bc1b8492SDan Murphy 
420bc1b8492SDan Murphy 	return regmap_update_bits(priv->regmap, brightnes_config_reg,
421bc1b8492SDan Murphy 				  LM3532_I2C_CTRL, LM3532_ALS_CTRL);
422bc1b8492SDan Murphy }
423bc1b8492SDan Murphy 
424bc1b8492SDan Murphy static int lm3532_parse_als(struct lm3532_data *priv)
425bc1b8492SDan Murphy {
426bc1b8492SDan Murphy 	struct lm3532_als_data *als;
427bc1b8492SDan Murphy 	int als_avg_time;
428bc1b8492SDan Murphy 	int als_impedance;
429bc1b8492SDan Murphy 	int ret;
430bc1b8492SDan Murphy 
431bc1b8492SDan Murphy 	als = devm_kzalloc(priv->dev, sizeof(*als), GFP_KERNEL);
432bc1b8492SDan Murphy 	if (als == NULL)
433bc1b8492SDan Murphy 		return -ENOMEM;
434bc1b8492SDan Murphy 
435bc1b8492SDan Murphy 	ret = device_property_read_u32(&priv->client->dev, "ti,als-vmin",
436bc1b8492SDan Murphy 				       &als->als_vmin);
437bc1b8492SDan Murphy 	if (ret)
438bc1b8492SDan Murphy 		als->als_vmin = 0;
439bc1b8492SDan Murphy 
440bc1b8492SDan Murphy 	ret = device_property_read_u32(&priv->client->dev, "ti,als-vmax",
441bc1b8492SDan Murphy 				       &als->als_vmax);
442bc1b8492SDan Murphy 	if (ret)
443bc1b8492SDan Murphy 		als->als_vmax = LM3532_ALS_WINDOW_mV;
444bc1b8492SDan Murphy 
445bc1b8492SDan Murphy 	if (als->als_vmax > LM3532_ALS_WINDOW_mV) {
446bc1b8492SDan Murphy 		ret = -EINVAL;
447bc1b8492SDan Murphy 		return ret;
448bc1b8492SDan Murphy 	}
449bc1b8492SDan Murphy 
450bc1b8492SDan Murphy 	ret = device_property_read_u32(&priv->client->dev, "ti,als1-imp-sel",
451bc1b8492SDan Murphy 				      &als_impedance);
452bc1b8492SDan Murphy 	if (ret)
453bc1b8492SDan Murphy 		als->als1_imp_sel = 0;
454bc1b8492SDan Murphy 	else
455bc1b8492SDan Murphy 		als->als1_imp_sel = lm3532_get_als_imp_index(als_impedance);
456bc1b8492SDan Murphy 
457bc1b8492SDan Murphy 	ret = device_property_read_u32(&priv->client->dev, "ti,als2-imp-sel",
458bc1b8492SDan Murphy 				      &als_impedance);
459bc1b8492SDan Murphy 	if (ret)
460bc1b8492SDan Murphy 		als->als2_imp_sel = 0;
461bc1b8492SDan Murphy 	else
462bc1b8492SDan Murphy 		als->als2_imp_sel = lm3532_get_als_imp_index(als_impedance);
463bc1b8492SDan Murphy 
464bc1b8492SDan Murphy 	ret = device_property_read_u32(&priv->client->dev, "ti,als-avrg-time-us",
465bc1b8492SDan Murphy 				      &als_avg_time);
466bc1b8492SDan Murphy 	if (ret)
467bc1b8492SDan Murphy 		als->als_avrg_time = 0;
468bc1b8492SDan Murphy 	else
469bc1b8492SDan Murphy 		als->als_avrg_time = lm3532_get_als_avg_index(als_avg_time);
470bc1b8492SDan Murphy 
471bc1b8492SDan Murphy 	ret = device_property_read_u8(&priv->client->dev, "ti,als-input-mode",
472bc1b8492SDan Murphy 				      &als->als_input_mode);
473bc1b8492SDan Murphy 	if (ret)
474bc1b8492SDan Murphy 		als->als_input_mode = 0;
475bc1b8492SDan Murphy 
476bc1b8492SDan Murphy 	if (als->als_input_mode > LM3532_BL_MODE_ALS) {
477bc1b8492SDan Murphy 		ret = -EINVAL;
478bc1b8492SDan Murphy 		return ret;
479bc1b8492SDan Murphy 	}
480bc1b8492SDan Murphy 
481bc1b8492SDan Murphy 	priv->als_data = als;
482bc1b8492SDan Murphy 
483bc1b8492SDan Murphy 	return ret;
484bc1b8492SDan Murphy }
485bc1b8492SDan Murphy 
486bc1b8492SDan Murphy static int lm3532_parse_node(struct lm3532_data *priv)
487bc1b8492SDan Murphy {
488bc1b8492SDan Murphy 	struct fwnode_handle *child = NULL;
489bc1b8492SDan Murphy 	struct lm3532_led *led;
490bc1b8492SDan Murphy 	const char *name;
491bc1b8492SDan Murphy 	int control_bank;
492bc1b8492SDan Murphy 	u32 ramp_time;
493bc1b8492SDan Murphy 	size_t i = 0;
494bc1b8492SDan Murphy 	int ret;
495bc1b8492SDan Murphy 
496bc1b8492SDan Murphy 	priv->enable_gpio = devm_gpiod_get_optional(&priv->client->dev,
497bc1b8492SDan Murphy 						   "enable", GPIOD_OUT_LOW);
498bc1b8492SDan Murphy 	if (IS_ERR(priv->enable_gpio))
499bc1b8492SDan Murphy 		priv->enable_gpio = NULL;
500bc1b8492SDan Murphy 
501bc1b8492SDan Murphy 	priv->regulator = devm_regulator_get(&priv->client->dev, "vin");
502bc1b8492SDan Murphy 	if (IS_ERR(priv->regulator))
503bc1b8492SDan Murphy 		priv->regulator = NULL;
504bc1b8492SDan Murphy 
505bc1b8492SDan Murphy 	ret = device_property_read_u32(&priv->client->dev, "ramp-up-us",
506bc1b8492SDan Murphy 				       &ramp_time);
507bc1b8492SDan Murphy 	if (ret)
508bc1b8492SDan Murphy 		dev_info(&priv->client->dev, "ramp-up-ms property missing\n");
509bc1b8492SDan Murphy 	else
510bc1b8492SDan Murphy 		priv->runtime_ramp_up = lm3532_get_ramp_index(ramp_time);
511bc1b8492SDan Murphy 
512bc1b8492SDan Murphy 	ret = device_property_read_u32(&priv->client->dev, "ramp-down-us",
513bc1b8492SDan Murphy 				       &ramp_time);
514bc1b8492SDan Murphy 	if (ret)
515bc1b8492SDan Murphy 		dev_info(&priv->client->dev, "ramp-down-ms property missing\n");
516bc1b8492SDan Murphy 	else
517bc1b8492SDan Murphy 		priv->runtime_ramp_down = lm3532_get_ramp_index(ramp_time);
518bc1b8492SDan Murphy 
519bc1b8492SDan Murphy 	device_for_each_child_node(priv->dev, child) {
520bc1b8492SDan Murphy 		led = &priv->leds[i];
521bc1b8492SDan Murphy 
522bc1b8492SDan Murphy 		ret = fwnode_property_read_u32(child, "reg", &control_bank);
523bc1b8492SDan Murphy 		if (ret) {
524bc1b8492SDan Murphy 			dev_err(&priv->client->dev, "reg property missing\n");
525bc1b8492SDan Murphy 			fwnode_handle_put(child);
526bc1b8492SDan Murphy 			goto child_out;
527bc1b8492SDan Murphy 		}
528bc1b8492SDan Murphy 
529bc1b8492SDan Murphy 		if (control_bank > LM3532_CONTROL_C) {
530bc1b8492SDan Murphy 			dev_err(&priv->client->dev, "Control bank invalid\n");
531bc1b8492SDan Murphy 			continue;
532bc1b8492SDan Murphy 		}
533bc1b8492SDan Murphy 
534bc1b8492SDan Murphy 		led->control_bank = control_bank;
535bc1b8492SDan Murphy 
536bc1b8492SDan Murphy 		ret = fwnode_property_read_u32(child, "ti,led-mode",
537bc1b8492SDan Murphy 					       &led->mode);
538bc1b8492SDan Murphy 		if (ret) {
539bc1b8492SDan Murphy 			dev_err(&priv->client->dev, "ti,led-mode property missing\n");
540bc1b8492SDan Murphy 			fwnode_handle_put(child);
541bc1b8492SDan Murphy 			goto child_out;
542bc1b8492SDan Murphy 		}
543bc1b8492SDan Murphy 
544bc1b8492SDan Murphy 		if (led->mode == LM3532_BL_MODE_ALS) {
545bc1b8492SDan Murphy 			ret = lm3532_parse_als(priv);
546bc1b8492SDan Murphy 			if (ret)
547bc1b8492SDan Murphy 				dev_err(&priv->client->dev, "Failed to parse als\n");
548bc1b8492SDan Murphy 			else
549bc1b8492SDan Murphy 				lm3532_als_configure(priv, led);
550bc1b8492SDan Murphy 		}
551bc1b8492SDan Murphy 
552bc1b8492SDan Murphy 		led->num_leds = fwnode_property_read_u32_array(child,
553bc1b8492SDan Murphy 							       "led-sources",
554bc1b8492SDan Murphy 							       NULL, 0);
555bc1b8492SDan Murphy 
556bc1b8492SDan Murphy 		if (led->num_leds > LM3532_MAX_LED_STRINGS) {
557bc1b8492SDan Murphy 			dev_err(&priv->client->dev, "To many LED string defined\n");
558bc1b8492SDan Murphy 			continue;
559bc1b8492SDan Murphy 		}
560bc1b8492SDan Murphy 
561bc1b8492SDan Murphy 		ret = fwnode_property_read_u32_array(child, "led-sources",
562bc1b8492SDan Murphy 						    led->led_strings,
563bc1b8492SDan Murphy 						    led->num_leds);
564bc1b8492SDan Murphy 		if (ret) {
565bc1b8492SDan Murphy 			dev_err(&priv->client->dev, "led-sources property missing\n");
566bc1b8492SDan Murphy 			fwnode_handle_put(child);
567bc1b8492SDan Murphy 			goto child_out;
568bc1b8492SDan Murphy 		}
569bc1b8492SDan Murphy 
570bc1b8492SDan Murphy 		fwnode_property_read_string(child, "linux,default-trigger",
571bc1b8492SDan Murphy 					    &led->led_dev.default_trigger);
572bc1b8492SDan Murphy 
573bc1b8492SDan Murphy 		ret = fwnode_property_read_string(child, "label", &name);
574bc1b8492SDan Murphy 		if (ret)
575bc1b8492SDan Murphy 			snprintf(led->label, sizeof(led->label),
576bc1b8492SDan Murphy 				"%s::", priv->client->name);
577bc1b8492SDan Murphy 		else
578bc1b8492SDan Murphy 			snprintf(led->label, sizeof(led->label),
579bc1b8492SDan Murphy 				 "%s:%s", priv->client->name, name);
580bc1b8492SDan Murphy 
581bc1b8492SDan Murphy 		led->priv = priv;
582bc1b8492SDan Murphy 		led->led_dev.name = led->label;
583bc1b8492SDan Murphy 		led->led_dev.brightness_set_blocking = lm3532_brightness_set;
584bc1b8492SDan Murphy 
585bc1b8492SDan Murphy 		ret = devm_led_classdev_register(priv->dev, &led->led_dev);
586bc1b8492SDan Murphy 		if (ret) {
587bc1b8492SDan Murphy 			dev_err(&priv->client->dev, "led register err: %d\n",
588bc1b8492SDan Murphy 				ret);
589bc1b8492SDan Murphy 			fwnode_handle_put(child);
590bc1b8492SDan Murphy 			goto child_out;
591bc1b8492SDan Murphy 		}
592bc1b8492SDan Murphy 
593bc1b8492SDan Murphy 		lm3532_init_registers(led);
594bc1b8492SDan Murphy 
595bc1b8492SDan Murphy 		i++;
596bc1b8492SDan Murphy 	}
597bc1b8492SDan Murphy 
598bc1b8492SDan Murphy child_out:
599bc1b8492SDan Murphy 	return ret;
600bc1b8492SDan Murphy }
601bc1b8492SDan Murphy 
602bc1b8492SDan Murphy static int lm3532_probe(struct i2c_client *client,
603bc1b8492SDan Murphy 			   const struct i2c_device_id *id)
604bc1b8492SDan Murphy {
605bc1b8492SDan Murphy 	struct lm3532_data *drvdata;
606bc1b8492SDan Murphy 	int ret = 0;
607bc1b8492SDan Murphy 	int count;
608bc1b8492SDan Murphy 
609bc1b8492SDan Murphy 	count = device_get_child_node_count(&client->dev);
610bc1b8492SDan Murphy 	if (!count) {
611bc1b8492SDan Murphy 		dev_err(&client->dev, "LEDs are not defined in device tree!");
612bc1b8492SDan Murphy 		return -ENODEV;
613bc1b8492SDan Murphy 	}
614bc1b8492SDan Murphy 
615bc1b8492SDan Murphy 	drvdata = devm_kzalloc(&client->dev, struct_size(drvdata, leds, count),
616bc1b8492SDan Murphy 			   GFP_KERNEL);
617bc1b8492SDan Murphy 	if (drvdata == NULL)
618bc1b8492SDan Murphy 		return -ENOMEM;
619bc1b8492SDan Murphy 
620bc1b8492SDan Murphy 	drvdata->client = client;
621bc1b8492SDan Murphy 	drvdata->dev = &client->dev;
622bc1b8492SDan Murphy 
623bc1b8492SDan Murphy 	drvdata->regmap = devm_regmap_init_i2c(client, &lm3532_regmap_config);
624bc1b8492SDan Murphy 	if (IS_ERR(drvdata->regmap)) {
625bc1b8492SDan Murphy 		ret = PTR_ERR(drvdata->regmap);
626bc1b8492SDan Murphy 		dev_err(&client->dev, "Failed to allocate register map: %d\n",
627bc1b8492SDan Murphy 			ret);
628bc1b8492SDan Murphy 		return ret;
629bc1b8492SDan Murphy 	}
630bc1b8492SDan Murphy 
631bc1b8492SDan Murphy 	mutex_init(&drvdata->lock);
632bc1b8492SDan Murphy 	i2c_set_clientdata(client, drvdata);
633bc1b8492SDan Murphy 
634bc1b8492SDan Murphy 	ret = lm3532_parse_node(drvdata);
635bc1b8492SDan Murphy 	if (ret) {
636bc1b8492SDan Murphy 		dev_err(&client->dev, "Failed to parse node\n");
637bc1b8492SDan Murphy 		return ret;
638bc1b8492SDan Murphy 	}
639bc1b8492SDan Murphy 
640bc1b8492SDan Murphy 	if (drvdata->enable_gpio)
641bc1b8492SDan Murphy 		gpiod_direction_output(drvdata->enable_gpio, 1);
642bc1b8492SDan Murphy 
643bc1b8492SDan Murphy 	return ret;
644bc1b8492SDan Murphy }
645bc1b8492SDan Murphy 
646bc1b8492SDan Murphy static int lm3532_remove(struct i2c_client *client)
647bc1b8492SDan Murphy {
648bc1b8492SDan Murphy 	struct lm3532_data *drvdata = i2c_get_clientdata(client);
649bc1b8492SDan Murphy 
650bc1b8492SDan Murphy 	mutex_destroy(&drvdata->lock);
651bc1b8492SDan Murphy 
652bc1b8492SDan Murphy 	if (drvdata->enable_gpio)
653bc1b8492SDan Murphy 		gpiod_direction_output(drvdata->enable_gpio, 0);
654bc1b8492SDan Murphy 
655bc1b8492SDan Murphy 	return 0;
656bc1b8492SDan Murphy }
657bc1b8492SDan Murphy 
658bc1b8492SDan Murphy static const struct of_device_id of_lm3532_leds_match[] = {
659bc1b8492SDan Murphy 	{ .compatible = "ti,lm3532", },
660bc1b8492SDan Murphy 	{},
661bc1b8492SDan Murphy };
662bc1b8492SDan Murphy MODULE_DEVICE_TABLE(of, of_lm3532_leds_match);
663bc1b8492SDan Murphy 
664bc1b8492SDan Murphy static const struct i2c_device_id lm3532_id[] = {
665bc1b8492SDan Murphy 	{LM3532_NAME, 0},
666bc1b8492SDan Murphy 	{}
667bc1b8492SDan Murphy };
668bc1b8492SDan Murphy MODULE_DEVICE_TABLE(i2c, lm3532_id);
669bc1b8492SDan Murphy 
670bc1b8492SDan Murphy static struct i2c_driver lm3532_i2c_driver = {
671bc1b8492SDan Murphy 	.probe = lm3532_probe,
672bc1b8492SDan Murphy 	.remove = lm3532_remove,
673bc1b8492SDan Murphy 	.id_table = lm3532_id,
674bc1b8492SDan Murphy 	.driver = {
675bc1b8492SDan Murphy 		.name = LM3532_NAME,
676bc1b8492SDan Murphy 		.of_match_table = of_lm3532_leds_match,
677bc1b8492SDan Murphy 	},
678bc1b8492SDan Murphy };
679bc1b8492SDan Murphy module_i2c_driver(lm3532_i2c_driver);
680bc1b8492SDan Murphy 
681bc1b8492SDan Murphy MODULE_DESCRIPTION("Back Light driver for LM3532");
682bc1b8492SDan Murphy MODULE_LICENSE("GPL v2");
683bc1b8492SDan Murphy MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");
684