blob: aa9bf8cda673b279f94fa6269a01e13e5d3f0dec [file] [log] [blame]
Dan Murphybc1b8492019-03-21 09:28:38 -05001// SPDX-License-Identifier: GPL-2.0
2// TI LM3532 LED driver
3// Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/
Pavel31e065c2020-01-05 23:36:58 +01004// http://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
99 * @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
108 */
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
124 * @priv - Pointer the device data structure
125 * @control_bank - Control bank the LED is associated to
126 * @mode - Mode of the LED string
Dan Murphy13123942019-08-20 14:53:03 -0500127 * @ctrl_brt_pointer - Zone target register that controls the sink
Dan Murphybc1b8492019-03-21 09:28:38 -0500128 * @num_leds - Number of LED strings are supported in this array
Dan Murphy517ea492019-08-20 14:53:07 -0500129 * @full_scale_current - The full-scale current setting for the current sink.
Dan Murphybc1b8492019-03-21 09:28:38 -0500130 * @led_strings - The LED strings supported in this array
Tony Lindgren070a0ee2019-08-27 14:52:05 -0700131 * @enabled - Enabled status
Dan Murphybc1b8492019-03-21 09:28:38 -0500132 * @label - LED label
133 */
134struct lm3532_led {
135 struct led_classdev led_dev;
136 struct lm3532_data *priv;
137
138 int control_bank;
139 int mode;
Dan Murphy13123942019-08-20 14:53:03 -0500140 int ctrl_brt_pointer;
Dan Murphybc1b8492019-03-21 09:28:38 -0500141 int num_leds;
Dan Murphy517ea492019-08-20 14:53:07 -0500142 int full_scale_current;
Colin Ian King28799272020-03-13 17:19:37 +0000143 unsigned int enabled:1;
Dan Murphybc1b8492019-03-21 09:28:38 -0500144 u32 led_strings[LM3532_MAX_CONTROL_BANKS];
145 char label[LED_MAX_NAME_SIZE];
146};
147
148/**
149 * struct lm3532_data
150 * @enable_gpio - Hardware enable gpio
151 * @regulator: regulator
152 * @client: i2c client
153 * @regmap - Devices register map
154 * @dev - Pointer to the devices device struct
155 * @lock - Lock for reading/writing the device
156 * @als_data - Pointer to the als data struct
157 * @runtime_ramp_up - Runtime ramp up setting
158 * @runtime_ramp_down - Runtime ramp down setting
159 * @leds - Array of LED strings
160 */
161struct lm3532_data {
162 struct gpio_desc *enable_gpio;
163 struct regulator *regulator;
164 struct i2c_client *client;
165 struct regmap *regmap;
166 struct device *dev;
167 struct mutex lock;
168
169 struct lm3532_als_data *als_data;
170
171 u32 runtime_ramp_up;
172 u32 runtime_ramp_down;
173
174 struct lm3532_led leds[];
175};
176
177static const struct reg_default lm3532_reg_defs[] = {
178 {LM3532_REG_OUTPUT_CFG, 0xe4},
179 {LM3532_REG_STARTSHUT_RAMP, 0xc0},
180 {LM3532_REG_RT_RAMP, 0xc0},
181 {LM3532_REG_PWM_A_CFG, 0x82},
182 {LM3532_REG_PWM_B_CFG, 0x82},
183 {LM3532_REG_PWM_C_CFG, 0x82},
184 {LM3532_REG_ZONE_CFG_A, 0xf1},
Dan Murphy4c905452019-08-20 14:53:04 -0500185 {LM3532_REG_CTRL_A_FS_CURR, 0xf3},
Dan Murphybc1b8492019-03-21 09:28:38 -0500186 {LM3532_REG_ZONE_CFG_B, 0xf1},
Dan Murphy4c905452019-08-20 14:53:04 -0500187 {LM3532_REG_CTRL_B_FS_CURR, 0xf3},
Dan Murphybc1b8492019-03-21 09:28:38 -0500188 {LM3532_REG_ZONE_CFG_C, 0xf1},
Dan Murphy4c905452019-08-20 14:53:04 -0500189 {LM3532_REG_CTRL_C_FS_CURR, 0xf3},
Dan Murphybc1b8492019-03-21 09:28:38 -0500190 {LM3532_REG_ENABLE, 0xf8},
191 {LM3532_ALS_CONFIG, 0x44},
192 {LM3532_REG_ZN_0_HI, 0x35},
193 {LM3532_REG_ZN_0_LO, 0x33},
194 {LM3532_REG_ZN_1_HI, 0x6a},
195 {LM3532_REG_ZN_1_LO, 0x66},
196 {LM3532_REG_ZN_2_HI, 0xa1},
197 {LM3532_REG_ZN_2_LO, 0x99},
198 {LM3532_REG_ZN_3_HI, 0xdc},
199 {LM3532_REG_ZN_3_LO, 0xcc},
200};
201
202static const struct regmap_config lm3532_regmap_config = {
203 .reg_bits = 8,
204 .val_bits = 8,
205
206 .max_register = LM3532_REG_MAX,
207 .reg_defaults = lm3532_reg_defs,
208 .num_reg_defaults = ARRAY_SIZE(lm3532_reg_defs),
209 .cache_type = REGCACHE_FLAT,
210};
211
Krzysztof Wilczynski536129cc2019-08-30 20:11:03 +0200212static const int als_imp_table[LM3532_NUM_IMP_VALS] = {37000, 18500, 12330,
Dan Murphybc1b8492019-03-21 09:28:38 -0500213 92500, 7400, 6170, 5290,
214 4630, 4110, 3700, 3360,
215 3080, 2850, 2640, 2440,
216 2310, 2180, 2060, 1950,
217 1850, 1760, 1680, 1610,
218 1540, 1480, 1420, 1370,
219 1320, 1280, 1230, 1190};
220static int lm3532_get_als_imp_index(int als_imped)
221{
222 int i;
223
224 if (als_imped > als_imp_table[1])
225 return 0;
226
227 if (als_imped < als_imp_table[LM3532_NUM_IMP_VALS - 1])
228 return LM3532_NUM_IMP_VALS - 1;
229
230 for (i = 1; i < LM3532_NUM_IMP_VALS; i++) {
231 if (als_imped == als_imp_table[i])
232 return i;
233
234 /* Find an approximate index by looking up the table */
235 if (als_imped < als_imp_table[i - 1] &&
236 als_imped > als_imp_table[i]) {
237 if (als_imped - als_imp_table[i - 1] <
238 als_imp_table[i] - als_imped)
239 return i + 1;
240 else
241 return i;
242 }
243 }
244
245 return -EINVAL;
246}
247
248static int lm3532_get_index(const int table[], int size, int value)
249{
250 int i;
251
252 for (i = 1; i < size; i++) {
253 if (value == table[i])
254 return i;
255
256 /* Find an approximate index by looking up the table */
257 if (value > table[i - 1] &&
258 value < table[i]) {
259 if (value - table[i - 1] < table[i] - value)
260 return i - 1;
261 else
262 return i;
263 }
264 }
265
266 return -EINVAL;
267}
268
Krzysztof Wilczynski536129cc2019-08-30 20:11:03 +0200269static const int als_avrg_table[LM3532_NUM_AVG_VALS] = {17920, 35840, 71680,
Dan Murphybc1b8492019-03-21 09:28:38 -0500270 1433360, 286720, 573440,
271 1146880, 2293760};
272static int lm3532_get_als_avg_index(int avg_time)
273{
274 if (avg_time <= als_avrg_table[0])
275 return 0;
276
277 if (avg_time > als_avrg_table[LM3532_NUM_AVG_VALS - 1])
278 return LM3532_NUM_AVG_VALS - 1;
279
280 return lm3532_get_index(&als_avrg_table[0], LM3532_NUM_AVG_VALS,
281 avg_time);
282}
283
Krzysztof Wilczynski536129cc2019-08-30 20:11:03 +0200284static const int ramp_table[LM3532_NUM_RAMP_VALS] = { 8, 1024, 2048, 4096, 8192,
Dan Murphybc1b8492019-03-21 09:28:38 -0500285 16384, 32768, 65536};
286static int lm3532_get_ramp_index(int ramp_time)
287{
288 if (ramp_time <= ramp_table[0])
289 return 0;
290
291 if (ramp_time > ramp_table[LM3532_NUM_RAMP_VALS - 1])
292 return LM3532_NUM_RAMP_VALS - 1;
293
294 return lm3532_get_index(&ramp_table[0], LM3532_NUM_RAMP_VALS,
295 ramp_time);
296}
297
Tony Lindgren070a0ee2019-08-27 14:52:05 -0700298/* Caller must take care of locking */
Dan Murphybc1b8492019-03-21 09:28:38 -0500299static int lm3532_led_enable(struct lm3532_led *led_data)
300{
301 int ctrl_en_val = BIT(led_data->control_bank);
302 int ret;
303
Tony Lindgren070a0ee2019-08-27 14:52:05 -0700304 if (led_data->enabled)
305 return 0;
306
Dan Murphybc1b8492019-03-21 09:28:38 -0500307 ret = regmap_update_bits(led_data->priv->regmap, LM3532_REG_ENABLE,
308 ctrl_en_val, ctrl_en_val);
309 if (ret) {
310 dev_err(led_data->priv->dev, "Failed to set ctrl:%d\n", ret);
311 return ret;
312 }
313
Tony Lindgren070a0ee2019-08-27 14:52:05 -0700314 ret = regulator_enable(led_data->priv->regulator);
315 if (ret < 0)
316 return ret;
317
318 led_data->enabled = 1;
319
320 return 0;
Dan Murphybc1b8492019-03-21 09:28:38 -0500321}
322
Tony Lindgren070a0ee2019-08-27 14:52:05 -0700323/* Caller must take care of locking */
Dan Murphybc1b8492019-03-21 09:28:38 -0500324static int lm3532_led_disable(struct lm3532_led *led_data)
325{
326 int ctrl_en_val = BIT(led_data->control_bank);
327 int ret;
328
Tony Lindgren070a0ee2019-08-27 14:52:05 -0700329 if (!led_data->enabled)
330 return 0;
331
Dan Murphybc1b8492019-03-21 09:28:38 -0500332 ret = regmap_update_bits(led_data->priv->regmap, LM3532_REG_ENABLE,
Dan Murphy6559ac32019-08-20 14:53:05 -0500333 ctrl_en_val, 0);
Dan Murphybc1b8492019-03-21 09:28:38 -0500334 if (ret) {
335 dev_err(led_data->priv->dev, "Failed to set ctrl:%d\n", ret);
336 return ret;
337 }
338
Tony Lindgren070a0ee2019-08-27 14:52:05 -0700339 ret = regulator_disable(led_data->priv->regulator);
340 if (ret < 0)
341 return ret;
342
343 led_data->enabled = 0;
344
345 return 0;
Dan Murphybc1b8492019-03-21 09:28:38 -0500346}
347
348static int lm3532_brightness_set(struct led_classdev *led_cdev,
349 enum led_brightness brt_val)
350{
351 struct lm3532_led *led =
352 container_of(led_cdev, struct lm3532_led, led_dev);
353 u8 brightness_reg;
354 int ret;
355
356 mutex_lock(&led->priv->lock);
357
Dan Murphy6559ac32019-08-20 14:53:05 -0500358 if (led->mode == LM3532_ALS_CTRL) {
Dan Murphybc1b8492019-03-21 09:28:38 -0500359 if (brt_val > LED_OFF)
360 ret = lm3532_led_enable(led);
361 else
362 ret = lm3532_led_disable(led);
363
364 goto unlock;
365 }
366
367 if (brt_val == LED_OFF) {
368 ret = lm3532_led_disable(led);
369 goto unlock;
370 }
371
372 ret = lm3532_led_enable(led);
373 if (ret)
374 goto unlock;
375
Dan Murphy13123942019-08-20 14:53:03 -0500376 brightness_reg = LM3532_REG_ZONE_TRGT_A + led->control_bank * 5 +
377 (led->ctrl_brt_pointer >> 2);
Dan Murphybc1b8492019-03-21 09:28:38 -0500378
379 ret = regmap_write(led->priv->regmap, brightness_reg, brt_val);
380
381unlock:
382 mutex_unlock(&led->priv->lock);
383 return ret;
384}
385
386static int lm3532_init_registers(struct lm3532_led *led)
387{
388 struct lm3532_data *drvdata = led->priv;
389 unsigned int runtime_ramp_val;
390 unsigned int output_cfg_val = 0;
391 unsigned int output_cfg_shift = 0;
392 unsigned int output_cfg_mask = 0;
Dan Murphy13123942019-08-20 14:53:03 -0500393 unsigned int brightness_config_reg;
394 unsigned int brightness_config_val;
Dan Murphy517ea492019-08-20 14:53:07 -0500395 int fs_current_reg;
396 int fs_current_val;
Dan Murphybc1b8492019-03-21 09:28:38 -0500397 int ret, i;
398
Dan Murphy13123942019-08-20 14:53:03 -0500399 if (drvdata->enable_gpio)
400 gpiod_direction_output(drvdata->enable_gpio, 1);
401
402 brightness_config_reg = LM3532_REG_ZONE_CFG_A + led->control_bank * 2;
403 /*
404 * This could be hard coded to the default value but the control
405 * brightness register may have changed during boot.
406 */
407 ret = regmap_read(drvdata->regmap, brightness_config_reg,
408 &led->ctrl_brt_pointer);
409 if (ret)
410 return ret;
411
412 led->ctrl_brt_pointer &= LM3532_ZONE_MASK;
413 brightness_config_val = led->ctrl_brt_pointer | led->mode;
414 ret = regmap_write(drvdata->regmap, brightness_config_reg,
415 brightness_config_val);
416 if (ret)
417 return ret;
418
Dan Murphy517ea492019-08-20 14:53:07 -0500419 if (led->full_scale_current) {
420 fs_current_reg = LM3532_REG_CTRL_A_FS_CURR + led->control_bank * 2;
421 fs_current_val = (led->full_scale_current - LM3532_FS_CURR_MIN) /
422 LM3532_FS_CURR_STEP;
423
424 ret = regmap_write(drvdata->regmap, fs_current_reg,
425 fs_current_val);
426 if (ret)
427 return ret;
428 }
429
Dan Murphybc1b8492019-03-21 09:28:38 -0500430 for (i = 0; i < led->num_leds; i++) {
431 output_cfg_shift = led->led_strings[i] * 2;
432 output_cfg_val |= (led->control_bank << output_cfg_shift);
433 output_cfg_mask |= LM3532_OUTPUT_CFG_MASK << output_cfg_shift;
434 }
435
436 ret = regmap_update_bits(drvdata->regmap, LM3532_REG_OUTPUT_CFG,
437 output_cfg_mask, output_cfg_val);
438 if (ret)
439 return ret;
440
441 runtime_ramp_val = drvdata->runtime_ramp_up |
442 (drvdata->runtime_ramp_down << LM3532_RAMP_DOWN_SHIFT);
443
444 return regmap_write(drvdata->regmap, LM3532_REG_RT_RAMP,
445 runtime_ramp_val);
446}
447
448static int lm3532_als_configure(struct lm3532_data *priv,
449 struct lm3532_led *led)
450{
451 struct lm3532_als_data *als = priv->als_data;
452 u32 als_vmin, als_vmax, als_vstep;
453 int zone_reg = LM3532_REG_ZN_0_HI;
Dan Murphybc1b8492019-03-21 09:28:38 -0500454 int ret;
455 int i;
456
457 als_vmin = als->als_vmin;
458 als_vmax = als->als_vmax;
459
460 als_vstep = (als_vmax - als_vmin) / ((LM3532_ALS_ZB_MAX + 1) * 2);
461
462 for (i = 0; i < LM3532_ALS_ZB_MAX; i++) {
463 als->zones_lo[i] = ((als_vmin + als_vstep + (i * als_vstep)) *
464 LED_FULL) / 1000;
465 als->zones_hi[i] = ((als_vmin + LM3532_ALS_OFFSET_mV +
466 als_vstep + (i * als_vstep)) * LED_FULL) / 1000;
467
468 zone_reg = LM3532_REG_ZN_0_HI + i * 2;
469 ret = regmap_write(priv->regmap, zone_reg, als->zones_lo[i]);
470 if (ret)
471 return ret;
472
473 zone_reg += 1;
474 ret = regmap_write(priv->regmap, zone_reg, als->zones_hi[i]);
475 if (ret)
476 return ret;
477 }
478
479 als->config = (als->als_avrg_time | (LM3532_ENABLE_ALS) |
480 (als->als_input_mode << LM3532_ALS_SEL_SHIFT));
481
Dan Murphy13123942019-08-20 14:53:03 -0500482 return regmap_write(priv->regmap, LM3532_ALS_CONFIG, als->config);
Dan Murphybc1b8492019-03-21 09:28:38 -0500483}
484
485static int lm3532_parse_als(struct lm3532_data *priv)
486{
487 struct lm3532_als_data *als;
488 int als_avg_time;
489 int als_impedance;
490 int ret;
491
492 als = devm_kzalloc(priv->dev, sizeof(*als), GFP_KERNEL);
493 if (als == NULL)
494 return -ENOMEM;
495
496 ret = device_property_read_u32(&priv->client->dev, "ti,als-vmin",
497 &als->als_vmin);
498 if (ret)
499 als->als_vmin = 0;
500
501 ret = device_property_read_u32(&priv->client->dev, "ti,als-vmax",
502 &als->als_vmax);
503 if (ret)
504 als->als_vmax = LM3532_ALS_WINDOW_mV;
505
506 if (als->als_vmax > LM3532_ALS_WINDOW_mV) {
507 ret = -EINVAL;
508 return ret;
509 }
510
511 ret = device_property_read_u32(&priv->client->dev, "ti,als1-imp-sel",
512 &als_impedance);
513 if (ret)
514 als->als1_imp_sel = 0;
515 else
516 als->als1_imp_sel = lm3532_get_als_imp_index(als_impedance);
517
518 ret = device_property_read_u32(&priv->client->dev, "ti,als2-imp-sel",
519 &als_impedance);
520 if (ret)
521 als->als2_imp_sel = 0;
522 else
523 als->als2_imp_sel = lm3532_get_als_imp_index(als_impedance);
524
525 ret = device_property_read_u32(&priv->client->dev, "ti,als-avrg-time-us",
526 &als_avg_time);
527 if (ret)
528 als->als_avrg_time = 0;
529 else
530 als->als_avrg_time = lm3532_get_als_avg_index(als_avg_time);
531
532 ret = device_property_read_u8(&priv->client->dev, "ti,als-input-mode",
533 &als->als_input_mode);
534 if (ret)
535 als->als_input_mode = 0;
536
537 if (als->als_input_mode > LM3532_BL_MODE_ALS) {
538 ret = -EINVAL;
539 return ret;
540 }
541
542 priv->als_data = als;
543
544 return ret;
545}
546
547static int lm3532_parse_node(struct lm3532_data *priv)
548{
549 struct fwnode_handle *child = NULL;
550 struct lm3532_led *led;
551 const char *name;
552 int control_bank;
553 u32 ramp_time;
554 size_t i = 0;
555 int ret;
556
557 priv->enable_gpio = devm_gpiod_get_optional(&priv->client->dev,
558 "enable", GPIOD_OUT_LOW);
559 if (IS_ERR(priv->enable_gpio))
560 priv->enable_gpio = NULL;
561
562 priv->regulator = devm_regulator_get(&priv->client->dev, "vin");
563 if (IS_ERR(priv->regulator))
564 priv->regulator = NULL;
565
566 ret = device_property_read_u32(&priv->client->dev, "ramp-up-us",
567 &ramp_time);
568 if (ret)
569 dev_info(&priv->client->dev, "ramp-up-ms property missing\n");
570 else
571 priv->runtime_ramp_up = lm3532_get_ramp_index(ramp_time);
572
573 ret = device_property_read_u32(&priv->client->dev, "ramp-down-us",
574 &ramp_time);
575 if (ret)
576 dev_info(&priv->client->dev, "ramp-down-ms property missing\n");
577 else
578 priv->runtime_ramp_down = lm3532_get_ramp_index(ramp_time);
579
580 device_for_each_child_node(priv->dev, child) {
Pavelcf6eb522019-08-07 12:43:52 +0200581 struct led_init_data idata = {
582 .fwnode = child,
583 .default_label = ":",
584 .devicename = priv->client->name,
585 };
586
Dan Murphybc1b8492019-03-21 09:28:38 -0500587 led = &priv->leds[i];
588
589 ret = fwnode_property_read_u32(child, "reg", &control_bank);
590 if (ret) {
591 dev_err(&priv->client->dev, "reg property missing\n");
592 fwnode_handle_put(child);
593 goto child_out;
594 }
595
596 if (control_bank > LM3532_CONTROL_C) {
597 dev_err(&priv->client->dev, "Control bank invalid\n");
598 continue;
599 }
600
601 led->control_bank = control_bank;
602
603 ret = fwnode_property_read_u32(child, "ti,led-mode",
604 &led->mode);
605 if (ret) {
606 dev_err(&priv->client->dev, "ti,led-mode property missing\n");
607 fwnode_handle_put(child);
608 goto child_out;
609 }
610
Dan Murphy6d4faf32019-09-11 13:27:30 -0500611 if (fwnode_property_present(child, "led-max-microamp") &&
612 fwnode_property_read_u32(child, "led-max-microamp",
613 &led->full_scale_current))
614 dev_err(&priv->client->dev,
615 "Failed getting led-max-microamp\n");
616 else
617 led->full_scale_current = min(led->full_scale_current,
618 LM3532_FS_CURR_MAX);
Dan Murphy517ea492019-08-20 14:53:07 -0500619
Dan Murphybc1b8492019-03-21 09:28:38 -0500620 if (led->mode == LM3532_BL_MODE_ALS) {
Dan Murphy6559ac32019-08-20 14:53:05 -0500621 led->mode = LM3532_ALS_CTRL;
Dan Murphybc1b8492019-03-21 09:28:38 -0500622 ret = lm3532_parse_als(priv);
623 if (ret)
624 dev_err(&priv->client->dev, "Failed to parse als\n");
625 else
626 lm3532_als_configure(priv, led);
Dan Murphy6559ac32019-08-20 14:53:05 -0500627 } else {
628 led->mode = LM3532_I2C_CTRL;
Dan Murphybc1b8492019-03-21 09:28:38 -0500629 }
630
Andy Shevchenkocc93c862019-07-23 23:14:57 +0300631 led->num_leds = fwnode_property_count_u32(child, "led-sources");
Dan Murphybc1b8492019-03-21 09:28:38 -0500632 if (led->num_leds > LM3532_MAX_LED_STRINGS) {
Pavel31e065c2020-01-05 23:36:58 +0100633 dev_err(&priv->client->dev, "Too many LED string defined\n");
Dan Murphybc1b8492019-03-21 09:28:38 -0500634 continue;
635 }
636
637 ret = fwnode_property_read_u32_array(child, "led-sources",
638 led->led_strings,
639 led->num_leds);
640 if (ret) {
641 dev_err(&priv->client->dev, "led-sources property missing\n");
642 fwnode_handle_put(child);
643 goto child_out;
644 }
645
646 fwnode_property_read_string(child, "linux,default-trigger",
647 &led->led_dev.default_trigger);
648
649 ret = fwnode_property_read_string(child, "label", &name);
650 if (ret)
651 snprintf(led->label, sizeof(led->label),
652 "%s::", priv->client->name);
653 else
654 snprintf(led->label, sizeof(led->label),
655 "%s:%s", priv->client->name, name);
656
657 led->priv = priv;
658 led->led_dev.name = led->label;
659 led->led_dev.brightness_set_blocking = lm3532_brightness_set;
660
Pavelcf6eb522019-08-07 12:43:52 +0200661 ret = devm_led_classdev_register_ext(priv->dev, &led->led_dev, &idata);
Dan Murphybc1b8492019-03-21 09:28:38 -0500662 if (ret) {
663 dev_err(&priv->client->dev, "led register err: %d\n",
664 ret);
665 fwnode_handle_put(child);
666 goto child_out;
667 }
668
Dan Murphy6559ac32019-08-20 14:53:05 -0500669 ret = lm3532_init_registers(led);
670 if (ret) {
671 dev_err(&priv->client->dev, "register init err: %d\n",
672 ret);
673 fwnode_handle_put(child);
674 goto child_out;
675 }
Dan Murphybc1b8492019-03-21 09:28:38 -0500676
677 i++;
678 }
679
680child_out:
681 return ret;
682}
683
684static int lm3532_probe(struct i2c_client *client,
685 const struct i2c_device_id *id)
686{
687 struct lm3532_data *drvdata;
688 int ret = 0;
689 int count;
690
691 count = device_get_child_node_count(&client->dev);
692 if (!count) {
693 dev_err(&client->dev, "LEDs are not defined in device tree!");
694 return -ENODEV;
695 }
696
697 drvdata = devm_kzalloc(&client->dev, struct_size(drvdata, leds, count),
698 GFP_KERNEL);
699 if (drvdata == NULL)
700 return -ENOMEM;
701
702 drvdata->client = client;
703 drvdata->dev = &client->dev;
704
705 drvdata->regmap = devm_regmap_init_i2c(client, &lm3532_regmap_config);
706 if (IS_ERR(drvdata->regmap)) {
707 ret = PTR_ERR(drvdata->regmap);
708 dev_err(&client->dev, "Failed to allocate register map: %d\n",
709 ret);
710 return ret;
711 }
712
713 mutex_init(&drvdata->lock);
714 i2c_set_clientdata(client, drvdata);
715
716 ret = lm3532_parse_node(drvdata);
717 if (ret) {
718 dev_err(&client->dev, "Failed to parse node\n");
719 return ret;
720 }
721
Dan Murphybc1b8492019-03-21 09:28:38 -0500722 return ret;
723}
724
725static int lm3532_remove(struct i2c_client *client)
726{
727 struct lm3532_data *drvdata = i2c_get_clientdata(client);
728
729 mutex_destroy(&drvdata->lock);
730
731 if (drvdata->enable_gpio)
732 gpiod_direction_output(drvdata->enable_gpio, 0);
733
734 return 0;
735}
736
737static const struct of_device_id of_lm3532_leds_match[] = {
738 { .compatible = "ti,lm3532", },
739 {},
740};
741MODULE_DEVICE_TABLE(of, of_lm3532_leds_match);
742
743static const struct i2c_device_id lm3532_id[] = {
744 {LM3532_NAME, 0},
745 {}
746};
747MODULE_DEVICE_TABLE(i2c, lm3532_id);
748
749static struct i2c_driver lm3532_i2c_driver = {
750 .probe = lm3532_probe,
751 .remove = lm3532_remove,
752 .id_table = lm3532_id,
753 .driver = {
754 .name = LM3532_NAME,
755 .of_match_table = of_lm3532_leds_match,
756 },
757};
758module_i2c_driver(lm3532_i2c_driver);
759
760MODULE_DESCRIPTION("Back Light driver for LM3532");
761MODULE_LICENSE("GPL v2");
762MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");