blob: beb53040e09e4801e18f7d8da8a9cbeca7a0ae26 [file] [log] [blame]
Dan Murphybc1b8492019-03-21 09:28:38 -05001// SPDX-License-Identifier: GPL-2.0
2// TI LM3532 LED driver
Alexander A. Klimovc5437332020-07-13 16:51:15 +02003// Copyright (C) 2019 Texas Instruments Incorporated - https://www.ti.com/
4// https://www.ti.com/lit/ds/symlink/lm3532.pdf
Dan Murphybc1b8492019-03-21 09:28:38 -05005
6#include <linux/i2c.h>
7#include <linux/leds.h>
8#include <linux/slab.h>
9#include <linux/regmap.h>
10#include <linux/types.h>
11#include <linux/regulator/consumer.h>
12#include <linux/module.h>
13#include <uapi/linux/uleds.h>
14#include <linux/gpio/consumer.h>
15
16#define LM3532_NAME "lm3532-led"
17#define LM3532_BL_MODE_MANUAL 0x00
18#define LM3532_BL_MODE_ALS 0x01
19
20#define LM3532_REG_OUTPUT_CFG 0x10
21#define LM3532_REG_STARTSHUT_RAMP 0x11
22#define LM3532_REG_RT_RAMP 0x12
23#define LM3532_REG_PWM_A_CFG 0x13
24#define LM3532_REG_PWM_B_CFG 0x14
25#define LM3532_REG_PWM_C_CFG 0x15
26#define LM3532_REG_ZONE_CFG_A 0x16
Dan Murphy4c905452019-08-20 14:53:04 -050027#define LM3532_REG_CTRL_A_FS_CURR 0x17
Dan Murphybc1b8492019-03-21 09:28:38 -050028#define LM3532_REG_ZONE_CFG_B 0x18
Dan Murphy4c905452019-08-20 14:53:04 -050029#define LM3532_REG_CTRL_B_FS_CURR 0x19
Dan Murphybc1b8492019-03-21 09:28:38 -050030#define LM3532_REG_ZONE_CFG_C 0x1a
Dan Murphy4c905452019-08-20 14:53:04 -050031#define LM3532_REG_CTRL_C_FS_CURR 0x1b
Dan Murphybc1b8492019-03-21 09:28:38 -050032#define LM3532_REG_ENABLE 0x1d
33#define LM3532_ALS_CONFIG 0x23
34#define LM3532_REG_ZN_0_HI 0x60
35#define LM3532_REG_ZN_0_LO 0x61
36#define LM3532_REG_ZN_1_HI 0x62
37#define LM3532_REG_ZN_1_LO 0x63
38#define LM3532_REG_ZN_2_HI 0x64
39#define LM3532_REG_ZN_2_LO 0x65
40#define LM3532_REG_ZN_3_HI 0x66
41#define LM3532_REG_ZN_3_LO 0x67
Dan Murphy13123942019-08-20 14:53:03 -050042#define LM3532_REG_ZONE_TRGT_A 0x70
43#define LM3532_REG_ZONE_TRGT_B 0x75
44#define LM3532_REG_ZONE_TRGT_C 0x7a
Dan Murphybc1b8492019-03-21 09:28:38 -050045#define LM3532_REG_MAX 0x7e
46
Dan Murphy6559ac32019-08-20 14:53:05 -050047/* Control Enable */
Dan Murphybc1b8492019-03-21 09:28:38 -050048#define LM3532_CTRL_A_ENABLE BIT(0)
49#define LM3532_CTRL_B_ENABLE BIT(1)
50#define LM3532_CTRL_C_ENABLE BIT(2)
51
52/* PWM Zone Control */
53#define LM3532_PWM_ZONE_MASK 0x7c
54#define LM3532_PWM_ZONE_0_EN BIT(2)
55#define LM3532_PWM_ZONE_1_EN BIT(3)
56#define LM3532_PWM_ZONE_2_EN BIT(4)
57#define LM3532_PWM_ZONE_3_EN BIT(5)
58#define LM3532_PWM_ZONE_4_EN BIT(6)
59
60/* Brightness Configuration */
61#define LM3532_I2C_CTRL BIT(0)
62#define LM3532_ALS_CTRL 0
63#define LM3532_LINEAR_MAP BIT(1)
64#define LM3532_ZONE_MASK (BIT(2) | BIT(3) | BIT(4))
65#define LM3532_ZONE_0 0
66#define LM3532_ZONE_1 BIT(2)
67#define LM3532_ZONE_2 BIT(3)
68#define LM3532_ZONE_3 (BIT(2) | BIT(3))
69#define LM3532_ZONE_4 BIT(4)
70
71#define LM3532_ENABLE_ALS BIT(3)
72#define LM3532_ALS_SEL_SHIFT 6
73
74/* Zone Boundary Register */
75#define LM3532_ALS_WINDOW_mV 2000
76#define LM3532_ALS_ZB_MAX 4
77#define LM3532_ALS_OFFSET_mV 2
78
79#define LM3532_CONTROL_A 0
80#define LM3532_CONTROL_B 1
81#define LM3532_CONTROL_C 2
82#define LM3532_MAX_CONTROL_BANKS 3
83#define LM3532_MAX_LED_STRINGS 3
84
85#define LM3532_OUTPUT_CFG_MASK 0x3
86#define LM3532_BRT_VAL_ADJUST 8
87#define LM3532_RAMP_DOWN_SHIFT 3
88
89#define LM3532_NUM_RAMP_VALS 8
90#define LM3532_NUM_AVG_VALS 8
91#define LM3532_NUM_IMP_VALS 32
92
Dan Murphy517ea492019-08-20 14:53:07 -050093#define LM3532_FS_CURR_MIN 5000
94#define LM3532_FS_CURR_MAX 29800
95#define LM3532_FS_CURR_STEP 800
96
Dan Murphybc1b8492019-03-21 09:28:38 -050097/*
98 * struct lm3532_als_data
Dan Murphy9e955a42020-09-22 14:06:37 -050099 * @config: value of ALS configuration register
100 * @als1_imp_sel: value of ALS1 resistor select register
101 * @als2_imp_sel: value of ALS2 resistor select register
102 * @als_avrg_time: ALS averaging time
103 * @als_input_mode: ALS input mode for brightness control
104 * @als_vmin: Minimum ALS voltage
105 * @als_vmax: Maximum ALS voltage
106 * @zone_lo: values of ALS lo ZB(Zone Boundary) registers
107 * @zone_hi: values of ALS hi ZB(Zone Boundary) registers
Dan Murphybc1b8492019-03-21 09:28:38 -0500108 */
109struct lm3532_als_data {
110 u8 config;
111 u8 als1_imp_sel;
112 u8 als2_imp_sel;
113 u8 als_avrg_time;
114 u8 als_input_mode;
115 u32 als_vmin;
116 u32 als_vmax;
117 u8 zones_lo[LM3532_ALS_ZB_MAX];
118 u8 zones_hi[LM3532_ALS_ZB_MAX];
119};
120
121/**
122 * struct lm3532_led
123 * @led_dev: led class device
Dan Murphy9e955a42020-09-22 14:06:37 -0500124 * @priv: Pointer the device data structure
125 * @control_bank: Control bank the LED is associated to
126 * @mode: Mode of the LED string
127 * @ctrl_brt_pointer: Zone target register that controls the sink
128 * @num_leds: Number of LED strings are supported in this array
129 * @full_scale_current: The full-scale current setting for the current sink.
130 * @led_strings: The LED strings supported in this array
131 * @enabled: Enabled status
Dan Murphybc1b8492019-03-21 09:28:38 -0500132 */
133struct lm3532_led {
134 struct led_classdev led_dev;
135 struct lm3532_data *priv;
136
137 int control_bank;
138 int mode;
Dan Murphy13123942019-08-20 14:53:03 -0500139 int ctrl_brt_pointer;
Dan Murphybc1b8492019-03-21 09:28:38 -0500140 int num_leds;
Dan Murphy517ea492019-08-20 14:53:07 -0500141 int full_scale_current;
Colin Ian King28799272020-03-13 17:19:37 +0000142 unsigned int enabled:1;
Dan Murphybc1b8492019-03-21 09:28:38 -0500143 u32 led_strings[LM3532_MAX_CONTROL_BANKS];
Dan Murphybc1b8492019-03-21 09:28:38 -0500144};
145
146/**
147 * struct lm3532_data
Dan Murphy9e955a42020-09-22 14:06:37 -0500148 * @enable_gpio: Hardware enable gpio
Dan Murphybc1b8492019-03-21 09:28:38 -0500149 * @regulator: regulator
150 * @client: i2c client
Dan Murphy9e955a42020-09-22 14:06:37 -0500151 * @regmap: Devices register map
152 * @dev: Pointer to the devices device struct
153 * @lock: Lock for reading/writing the device
154 * @als_data: Pointer to the als data struct
155 * @runtime_ramp_up: Runtime ramp up setting
156 * @runtime_ramp_down: Runtime ramp down setting
157 * @leds: Array of LED strings
Dan Murphybc1b8492019-03-21 09:28:38 -0500158 */
159struct lm3532_data {
160 struct gpio_desc *enable_gpio;
161 struct regulator *regulator;
162 struct i2c_client *client;
163 struct regmap *regmap;
164 struct device *dev;
165 struct mutex lock;
166
167 struct lm3532_als_data *als_data;
168
169 u32 runtime_ramp_up;
170 u32 runtime_ramp_down;
171
172 struct lm3532_led leds[];
173};
174
175static const struct reg_default lm3532_reg_defs[] = {
176 {LM3532_REG_OUTPUT_CFG, 0xe4},
177 {LM3532_REG_STARTSHUT_RAMP, 0xc0},
178 {LM3532_REG_RT_RAMP, 0xc0},
179 {LM3532_REG_PWM_A_CFG, 0x82},
180 {LM3532_REG_PWM_B_CFG, 0x82},
181 {LM3532_REG_PWM_C_CFG, 0x82},
182 {LM3532_REG_ZONE_CFG_A, 0xf1},
Dan Murphy4c905452019-08-20 14:53:04 -0500183 {LM3532_REG_CTRL_A_FS_CURR, 0xf3},
Dan Murphybc1b8492019-03-21 09:28:38 -0500184 {LM3532_REG_ZONE_CFG_B, 0xf1},
Dan Murphy4c905452019-08-20 14:53:04 -0500185 {LM3532_REG_CTRL_B_FS_CURR, 0xf3},
Dan Murphybc1b8492019-03-21 09:28:38 -0500186 {LM3532_REG_ZONE_CFG_C, 0xf1},
Dan Murphy4c905452019-08-20 14:53:04 -0500187 {LM3532_REG_CTRL_C_FS_CURR, 0xf3},
Dan Murphybc1b8492019-03-21 09:28:38 -0500188 {LM3532_REG_ENABLE, 0xf8},
189 {LM3532_ALS_CONFIG, 0x44},
190 {LM3532_REG_ZN_0_HI, 0x35},
191 {LM3532_REG_ZN_0_LO, 0x33},
192 {LM3532_REG_ZN_1_HI, 0x6a},
193 {LM3532_REG_ZN_1_LO, 0x66},
194 {LM3532_REG_ZN_2_HI, 0xa1},
195 {LM3532_REG_ZN_2_LO, 0x99},
196 {LM3532_REG_ZN_3_HI, 0xdc},
197 {LM3532_REG_ZN_3_LO, 0xcc},
198};
199
200static const struct regmap_config lm3532_regmap_config = {
201 .reg_bits = 8,
202 .val_bits = 8,
203
204 .max_register = LM3532_REG_MAX,
205 .reg_defaults = lm3532_reg_defs,
206 .num_reg_defaults = ARRAY_SIZE(lm3532_reg_defs),
207 .cache_type = REGCACHE_FLAT,
208};
209
Krzysztof Wilczynski536129cc2019-08-30 20:11:03 +0200210static const int als_imp_table[LM3532_NUM_IMP_VALS] = {37000, 18500, 12330,
Dan Murphybc1b8492019-03-21 09:28:38 -0500211 92500, 7400, 6170, 5290,
212 4630, 4110, 3700, 3360,
213 3080, 2850, 2640, 2440,
214 2310, 2180, 2060, 1950,
215 1850, 1760, 1680, 1610,
216 1540, 1480, 1420, 1370,
217 1320, 1280, 1230, 1190};
218static int lm3532_get_als_imp_index(int als_imped)
219{
220 int i;
221
222 if (als_imped > als_imp_table[1])
223 return 0;
224
225 if (als_imped < als_imp_table[LM3532_NUM_IMP_VALS - 1])
226 return LM3532_NUM_IMP_VALS - 1;
227
228 for (i = 1; i < LM3532_NUM_IMP_VALS; i++) {
229 if (als_imped == als_imp_table[i])
230 return i;
231
232 /* Find an approximate index by looking up the table */
233 if (als_imped < als_imp_table[i - 1] &&
234 als_imped > als_imp_table[i]) {
235 if (als_imped - als_imp_table[i - 1] <
236 als_imp_table[i] - als_imped)
237 return i + 1;
238 else
239 return i;
240 }
241 }
242
243 return -EINVAL;
244}
245
246static int lm3532_get_index(const int table[], int size, int value)
247{
248 int i;
249
250 for (i = 1; i < size; i++) {
251 if (value == table[i])
252 return i;
253
254 /* Find an approximate index by looking up the table */
255 if (value > table[i - 1] &&
256 value < table[i]) {
257 if (value - table[i - 1] < table[i] - value)
258 return i - 1;
259 else
260 return i;
261 }
262 }
263
264 return -EINVAL;
265}
266
Krzysztof Wilczynski536129cc2019-08-30 20:11:03 +0200267static const int als_avrg_table[LM3532_NUM_AVG_VALS] = {17920, 35840, 71680,
Dan Murphybc1b8492019-03-21 09:28:38 -0500268 1433360, 286720, 573440,
269 1146880, 2293760};
270static int lm3532_get_als_avg_index(int avg_time)
271{
272 if (avg_time <= als_avrg_table[0])
273 return 0;
274
275 if (avg_time > als_avrg_table[LM3532_NUM_AVG_VALS - 1])
276 return LM3532_NUM_AVG_VALS - 1;
277
278 return lm3532_get_index(&als_avrg_table[0], LM3532_NUM_AVG_VALS,
279 avg_time);
280}
281
Krzysztof Wilczynski536129cc2019-08-30 20:11:03 +0200282static const int ramp_table[LM3532_NUM_RAMP_VALS] = { 8, 1024, 2048, 4096, 8192,
Dan Murphybc1b8492019-03-21 09:28:38 -0500283 16384, 32768, 65536};
284static int lm3532_get_ramp_index(int ramp_time)
285{
286 if (ramp_time <= ramp_table[0])
287 return 0;
288
289 if (ramp_time > ramp_table[LM3532_NUM_RAMP_VALS - 1])
290 return LM3532_NUM_RAMP_VALS - 1;
291
292 return lm3532_get_index(&ramp_table[0], LM3532_NUM_RAMP_VALS,
293 ramp_time);
294}
295
Tony Lindgren070a0ee2019-08-27 14:52:05 -0700296/* Caller must take care of locking */
Dan Murphybc1b8492019-03-21 09:28:38 -0500297static int lm3532_led_enable(struct lm3532_led *led_data)
298{
299 int ctrl_en_val = BIT(led_data->control_bank);
300 int ret;
301
Tony Lindgren070a0ee2019-08-27 14:52:05 -0700302 if (led_data->enabled)
303 return 0;
304
Dan Murphybc1b8492019-03-21 09:28:38 -0500305 ret = regmap_update_bits(led_data->priv->regmap, LM3532_REG_ENABLE,
306 ctrl_en_val, ctrl_en_val);
307 if (ret) {
308 dev_err(led_data->priv->dev, "Failed to set ctrl:%d\n", ret);
309 return ret;
310 }
311
Tony Lindgren070a0ee2019-08-27 14:52:05 -0700312 ret = regulator_enable(led_data->priv->regulator);
313 if (ret < 0)
314 return ret;
315
316 led_data->enabled = 1;
317
318 return 0;
Dan Murphybc1b8492019-03-21 09:28:38 -0500319}
320
Tony Lindgren070a0ee2019-08-27 14:52:05 -0700321/* Caller must take care of locking */
Dan Murphybc1b8492019-03-21 09:28:38 -0500322static int lm3532_led_disable(struct lm3532_led *led_data)
323{
324 int ctrl_en_val = BIT(led_data->control_bank);
325 int ret;
326
Tony Lindgren070a0ee2019-08-27 14:52:05 -0700327 if (!led_data->enabled)
328 return 0;
329
Dan Murphybc1b8492019-03-21 09:28:38 -0500330 ret = regmap_update_bits(led_data->priv->regmap, LM3532_REG_ENABLE,
Dan Murphy6559ac32019-08-20 14:53:05 -0500331 ctrl_en_val, 0);
Dan Murphybc1b8492019-03-21 09:28:38 -0500332 if (ret) {
333 dev_err(led_data->priv->dev, "Failed to set ctrl:%d\n", ret);
334 return ret;
335 }
336
Tony Lindgren070a0ee2019-08-27 14:52:05 -0700337 ret = regulator_disable(led_data->priv->regulator);
338 if (ret < 0)
339 return ret;
340
341 led_data->enabled = 0;
342
343 return 0;
Dan Murphybc1b8492019-03-21 09:28:38 -0500344}
345
346static int lm3532_brightness_set(struct led_classdev *led_cdev,
347 enum led_brightness brt_val)
348{
349 struct lm3532_led *led =
350 container_of(led_cdev, struct lm3532_led, led_dev);
351 u8 brightness_reg;
352 int ret;
353
354 mutex_lock(&led->priv->lock);
355
Dan Murphy6559ac32019-08-20 14:53:05 -0500356 if (led->mode == LM3532_ALS_CTRL) {
Dan Murphybc1b8492019-03-21 09:28:38 -0500357 if (brt_val > LED_OFF)
358 ret = lm3532_led_enable(led);
359 else
360 ret = lm3532_led_disable(led);
361
362 goto unlock;
363 }
364
365 if (brt_val == LED_OFF) {
366 ret = lm3532_led_disable(led);
367 goto unlock;
368 }
369
370 ret = lm3532_led_enable(led);
371 if (ret)
372 goto unlock;
373
Dan Murphy13123942019-08-20 14:53:03 -0500374 brightness_reg = LM3532_REG_ZONE_TRGT_A + led->control_bank * 5 +
375 (led->ctrl_brt_pointer >> 2);
Dan Murphybc1b8492019-03-21 09:28:38 -0500376
377 ret = regmap_write(led->priv->regmap, brightness_reg, brt_val);
378
379unlock:
380 mutex_unlock(&led->priv->lock);
381 return ret;
382}
383
384static int lm3532_init_registers(struct lm3532_led *led)
385{
386 struct lm3532_data *drvdata = led->priv;
387 unsigned int runtime_ramp_val;
388 unsigned int output_cfg_val = 0;
389 unsigned int output_cfg_shift = 0;
390 unsigned int output_cfg_mask = 0;
Dan Murphy13123942019-08-20 14:53:03 -0500391 unsigned int brightness_config_reg;
392 unsigned int brightness_config_val;
Dan Murphy517ea492019-08-20 14:53:07 -0500393 int fs_current_reg;
394 int fs_current_val;
Dan Murphybc1b8492019-03-21 09:28:38 -0500395 int ret, i;
396
Dan Murphy13123942019-08-20 14:53:03 -0500397 if (drvdata->enable_gpio)
398 gpiod_direction_output(drvdata->enable_gpio, 1);
399
400 brightness_config_reg = LM3532_REG_ZONE_CFG_A + led->control_bank * 2;
401 /*
402 * This could be hard coded to the default value but the control
403 * brightness register may have changed during boot.
404 */
405 ret = regmap_read(drvdata->regmap, brightness_config_reg,
406 &led->ctrl_brt_pointer);
407 if (ret)
408 return ret;
409
410 led->ctrl_brt_pointer &= LM3532_ZONE_MASK;
411 brightness_config_val = led->ctrl_brt_pointer | led->mode;
412 ret = regmap_write(drvdata->regmap, brightness_config_reg,
413 brightness_config_val);
414 if (ret)
415 return ret;
416
Dan Murphy517ea492019-08-20 14:53:07 -0500417 if (led->full_scale_current) {
418 fs_current_reg = LM3532_REG_CTRL_A_FS_CURR + led->control_bank * 2;
419 fs_current_val = (led->full_scale_current - LM3532_FS_CURR_MIN) /
420 LM3532_FS_CURR_STEP;
421
422 ret = regmap_write(drvdata->regmap, fs_current_reg,
423 fs_current_val);
424 if (ret)
425 return ret;
426 }
427
Dan Murphybc1b8492019-03-21 09:28:38 -0500428 for (i = 0; i < led->num_leds; i++) {
429 output_cfg_shift = led->led_strings[i] * 2;
430 output_cfg_val |= (led->control_bank << output_cfg_shift);
431 output_cfg_mask |= LM3532_OUTPUT_CFG_MASK << output_cfg_shift;
432 }
433
434 ret = regmap_update_bits(drvdata->regmap, LM3532_REG_OUTPUT_CFG,
435 output_cfg_mask, output_cfg_val);
436 if (ret)
437 return ret;
438
439 runtime_ramp_val = drvdata->runtime_ramp_up |
440 (drvdata->runtime_ramp_down << LM3532_RAMP_DOWN_SHIFT);
441
442 return regmap_write(drvdata->regmap, LM3532_REG_RT_RAMP,
443 runtime_ramp_val);
444}
445
446static int lm3532_als_configure(struct lm3532_data *priv,
447 struct lm3532_led *led)
448{
449 struct lm3532_als_data *als = priv->als_data;
450 u32 als_vmin, als_vmax, als_vstep;
451 int zone_reg = LM3532_REG_ZN_0_HI;
Dan Murphybc1b8492019-03-21 09:28:38 -0500452 int ret;
453 int i;
454
455 als_vmin = als->als_vmin;
456 als_vmax = als->als_vmax;
457
458 als_vstep = (als_vmax - als_vmin) / ((LM3532_ALS_ZB_MAX + 1) * 2);
459
460 for (i = 0; i < LM3532_ALS_ZB_MAX; i++) {
461 als->zones_lo[i] = ((als_vmin + als_vstep + (i * als_vstep)) *
462 LED_FULL) / 1000;
463 als->zones_hi[i] = ((als_vmin + LM3532_ALS_OFFSET_mV +
464 als_vstep + (i * als_vstep)) * LED_FULL) / 1000;
465
466 zone_reg = LM3532_REG_ZN_0_HI + i * 2;
467 ret = regmap_write(priv->regmap, zone_reg, als->zones_lo[i]);
468 if (ret)
469 return ret;
470
471 zone_reg += 1;
472 ret = regmap_write(priv->regmap, zone_reg, als->zones_hi[i]);
473 if (ret)
474 return ret;
475 }
476
477 als->config = (als->als_avrg_time | (LM3532_ENABLE_ALS) |
478 (als->als_input_mode << LM3532_ALS_SEL_SHIFT));
479
Dan Murphy13123942019-08-20 14:53:03 -0500480 return regmap_write(priv->regmap, LM3532_ALS_CONFIG, als->config);
Dan Murphybc1b8492019-03-21 09:28:38 -0500481}
482
483static int lm3532_parse_als(struct lm3532_data *priv)
484{
485 struct lm3532_als_data *als;
486 int als_avg_time;
487 int als_impedance;
488 int ret;
489
490 als = devm_kzalloc(priv->dev, sizeof(*als), GFP_KERNEL);
491 if (als == NULL)
492 return -ENOMEM;
493
494 ret = device_property_read_u32(&priv->client->dev, "ti,als-vmin",
495 &als->als_vmin);
496 if (ret)
497 als->als_vmin = 0;
498
499 ret = device_property_read_u32(&priv->client->dev, "ti,als-vmax",
500 &als->als_vmax);
501 if (ret)
502 als->als_vmax = LM3532_ALS_WINDOW_mV;
503
504 if (als->als_vmax > LM3532_ALS_WINDOW_mV) {
505 ret = -EINVAL;
506 return ret;
507 }
508
509 ret = device_property_read_u32(&priv->client->dev, "ti,als1-imp-sel",
510 &als_impedance);
511 if (ret)
512 als->als1_imp_sel = 0;
513 else
514 als->als1_imp_sel = lm3532_get_als_imp_index(als_impedance);
515
516 ret = device_property_read_u32(&priv->client->dev, "ti,als2-imp-sel",
517 &als_impedance);
518 if (ret)
519 als->als2_imp_sel = 0;
520 else
521 als->als2_imp_sel = lm3532_get_als_imp_index(als_impedance);
522
523 ret = device_property_read_u32(&priv->client->dev, "ti,als-avrg-time-us",
524 &als_avg_time);
525 if (ret)
526 als->als_avrg_time = 0;
527 else
528 als->als_avrg_time = lm3532_get_als_avg_index(als_avg_time);
529
530 ret = device_property_read_u8(&priv->client->dev, "ti,als-input-mode",
531 &als->als_input_mode);
532 if (ret)
533 als->als_input_mode = 0;
534
535 if (als->als_input_mode > LM3532_BL_MODE_ALS) {
536 ret = -EINVAL;
537 return ret;
538 }
539
540 priv->als_data = als;
541
542 return ret;
543}
544
545static int lm3532_parse_node(struct lm3532_data *priv)
546{
547 struct fwnode_handle *child = NULL;
548 struct lm3532_led *led;
Dan Murphybc1b8492019-03-21 09:28:38 -0500549 int control_bank;
550 u32 ramp_time;
551 size_t i = 0;
552 int ret;
553
554 priv->enable_gpio = devm_gpiod_get_optional(&priv->client->dev,
555 "enable", GPIOD_OUT_LOW);
556 if (IS_ERR(priv->enable_gpio))
557 priv->enable_gpio = NULL;
558
559 priv->regulator = devm_regulator_get(&priv->client->dev, "vin");
560 if (IS_ERR(priv->regulator))
561 priv->regulator = NULL;
562
563 ret = device_property_read_u32(&priv->client->dev, "ramp-up-us",
564 &ramp_time);
565 if (ret)
566 dev_info(&priv->client->dev, "ramp-up-ms property missing\n");
567 else
568 priv->runtime_ramp_up = lm3532_get_ramp_index(ramp_time);
569
570 ret = device_property_read_u32(&priv->client->dev, "ramp-down-us",
571 &ramp_time);
572 if (ret)
573 dev_info(&priv->client->dev, "ramp-down-ms property missing\n");
574 else
575 priv->runtime_ramp_down = lm3532_get_ramp_index(ramp_time);
576
577 device_for_each_child_node(priv->dev, child) {
Pavelcf6eb522019-08-07 12:43:52 +0200578 struct led_init_data idata = {
579 .fwnode = child,
580 .default_label = ":",
581 .devicename = priv->client->name,
582 };
583
Dan Murphybc1b8492019-03-21 09:28:38 -0500584 led = &priv->leds[i];
585
586 ret = fwnode_property_read_u32(child, "reg", &control_bank);
587 if (ret) {
588 dev_err(&priv->client->dev, "reg property missing\n");
Dan Murphybc1b8492019-03-21 09:28:38 -0500589 goto child_out;
590 }
591
592 if (control_bank > LM3532_CONTROL_C) {
593 dev_err(&priv->client->dev, "Control bank invalid\n");
594 continue;
595 }
596
597 led->control_bank = control_bank;
598
599 ret = fwnode_property_read_u32(child, "ti,led-mode",
600 &led->mode);
601 if (ret) {
602 dev_err(&priv->client->dev, "ti,led-mode property missing\n");
Dan Murphybc1b8492019-03-21 09:28:38 -0500603 goto child_out;
604 }
605
Dan Murphy6d4faf32019-09-11 13:27:30 -0500606 if (fwnode_property_present(child, "led-max-microamp") &&
607 fwnode_property_read_u32(child, "led-max-microamp",
608 &led->full_scale_current))
609 dev_err(&priv->client->dev,
610 "Failed getting led-max-microamp\n");
611 else
612 led->full_scale_current = min(led->full_scale_current,
613 LM3532_FS_CURR_MAX);
Dan Murphy517ea492019-08-20 14:53:07 -0500614
Dan Murphybc1b8492019-03-21 09:28:38 -0500615 if (led->mode == LM3532_BL_MODE_ALS) {
Dan Murphy6559ac32019-08-20 14:53:05 -0500616 led->mode = LM3532_ALS_CTRL;
Dan Murphybc1b8492019-03-21 09:28:38 -0500617 ret = lm3532_parse_als(priv);
618 if (ret)
619 dev_err(&priv->client->dev, "Failed to parse als\n");
620 else
621 lm3532_als_configure(priv, led);
Dan Murphy6559ac32019-08-20 14:53:05 -0500622 } else {
623 led->mode = LM3532_I2C_CTRL;
Dan Murphybc1b8492019-03-21 09:28:38 -0500624 }
625
Andy Shevchenkocc93c862019-07-23 23:14:57 +0300626 led->num_leds = fwnode_property_count_u32(child, "led-sources");
Dan Murphybc1b8492019-03-21 09:28:38 -0500627 if (led->num_leds > LM3532_MAX_LED_STRINGS) {
Pavel31e065c2020-01-05 23:36:58 +0100628 dev_err(&priv->client->dev, "Too many LED string defined\n");
Dan Murphybc1b8492019-03-21 09:28:38 -0500629 continue;
630 }
631
632 ret = fwnode_property_read_u32_array(child, "led-sources",
633 led->led_strings,
634 led->num_leds);
635 if (ret) {
636 dev_err(&priv->client->dev, "led-sources property missing\n");
Dan Murphybc1b8492019-03-21 09:28:38 -0500637 goto child_out;
638 }
639
Dan Murphybc1b8492019-03-21 09:28:38 -0500640 led->priv = priv;
Dan Murphybc1b8492019-03-21 09:28:38 -0500641 led->led_dev.brightness_set_blocking = lm3532_brightness_set;
642
Pavelcf6eb522019-08-07 12:43:52 +0200643 ret = devm_led_classdev_register_ext(priv->dev, &led->led_dev, &idata);
Dan Murphybc1b8492019-03-21 09:28:38 -0500644 if (ret) {
645 dev_err(&priv->client->dev, "led register err: %d\n",
646 ret);
Dan Murphybc1b8492019-03-21 09:28:38 -0500647 goto child_out;
648 }
649
Dan Murphy6559ac32019-08-20 14:53:05 -0500650 ret = lm3532_init_registers(led);
651 if (ret) {
652 dev_err(&priv->client->dev, "register init err: %d\n",
653 ret);
Dan Murphy6559ac32019-08-20 14:53:05 -0500654 goto child_out;
655 }
Dan Murphybc1b8492019-03-21 09:28:38 -0500656
657 i++;
658 }
Andy Shevchenko2f39f682021-05-10 12:50:32 +0300659 return 0;
Dan Murphybc1b8492019-03-21 09:28:38 -0500660
661child_out:
Andy Shevchenko2f39f682021-05-10 12:50:32 +0300662 fwnode_handle_put(child);
Dan Murphybc1b8492019-03-21 09:28:38 -0500663 return ret;
664}
665
666static int lm3532_probe(struct i2c_client *client,
667 const struct i2c_device_id *id)
668{
669 struct lm3532_data *drvdata;
670 int ret = 0;
671 int count;
672
673 count = device_get_child_node_count(&client->dev);
674 if (!count) {
675 dev_err(&client->dev, "LEDs are not defined in device tree!");
676 return -ENODEV;
677 }
678
679 drvdata = devm_kzalloc(&client->dev, struct_size(drvdata, leds, count),
680 GFP_KERNEL);
681 if (drvdata == NULL)
682 return -ENOMEM;
683
684 drvdata->client = client;
685 drvdata->dev = &client->dev;
686
687 drvdata->regmap = devm_regmap_init_i2c(client, &lm3532_regmap_config);
688 if (IS_ERR(drvdata->regmap)) {
689 ret = PTR_ERR(drvdata->regmap);
690 dev_err(&client->dev, "Failed to allocate register map: %d\n",
691 ret);
692 return ret;
693 }
694
695 mutex_init(&drvdata->lock);
696 i2c_set_clientdata(client, drvdata);
697
698 ret = lm3532_parse_node(drvdata);
699 if (ret) {
700 dev_err(&client->dev, "Failed to parse node\n");
701 return ret;
702 }
703
Dan Murphybc1b8492019-03-21 09:28:38 -0500704 return ret;
705}
706
707static int lm3532_remove(struct i2c_client *client)
708{
709 struct lm3532_data *drvdata = i2c_get_clientdata(client);
710
711 mutex_destroy(&drvdata->lock);
712
713 if (drvdata->enable_gpio)
714 gpiod_direction_output(drvdata->enable_gpio, 0);
715
716 return 0;
717}
718
719static const struct of_device_id of_lm3532_leds_match[] = {
720 { .compatible = "ti,lm3532", },
721 {},
722};
723MODULE_DEVICE_TABLE(of, of_lm3532_leds_match);
724
725static const struct i2c_device_id lm3532_id[] = {
726 {LM3532_NAME, 0},
727 {}
728};
729MODULE_DEVICE_TABLE(i2c, lm3532_id);
730
731static struct i2c_driver lm3532_i2c_driver = {
732 .probe = lm3532_probe,
733 .remove = lm3532_remove,
734 .id_table = lm3532_id,
735 .driver = {
736 .name = LM3532_NAME,
737 .of_match_table = of_lm3532_leds_match,
738 },
739};
740module_i2c_driver(lm3532_i2c_driver);
741
742MODULE_DESCRIPTION("Back Light driver for LM3532");
743MODULE_LICENSE("GPL v2");
744MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");