1 // SPDX-License-Identifier: GPL-2.0-only
3 #include <linux/bitops.h>
4 #include <linux/delay.h>
5 #include <linux/init.h>
6 #include <linux/interrupt.h>
7 #include <linux/kernel.h>
8 #include <linux/led-class-flash.h>
9 #include <linux/led-class-multicolor.h>
10 #include <linux/module.h>
11 #include <linux/mutex.h>
12 #include <linux/platform_device.h>
13 #include <linux/property.h>
14 #include <linux/regmap.h>
15 #include <media/v4l2-flash-led-class.h>
27 #define MT6360_REG_RGBEN 0x380
28 #define MT6360_REG_ISNK(_led_no) (0x381 + (_led_no))
29 #define MT6360_ISNK_ENMASK(_led_no) BIT(7 - (_led_no))
30 #define MT6360_ISNK_MASK GENMASK(4, 0)
31 #define MT6360_CHRINDSEL_MASK BIT(3)
33 /* Virtual definition for multicolor */
34 #define MT6360_VIRTUAL_MULTICOLOR (MT6360_MAX_LEDS + 1)
35 #define MULTICOLOR_NUM_CHANNELS 3
37 #define MT6360_REG_FLEDEN 0x37E
38 #define MT6360_REG_STRBTO 0x373
39 #define MT6360_REG_FLEDBASE(_id) (0x372 + 4 * (_id - MT6360_LED_FLASH1))
40 #define MT6360_REG_FLEDISTRB(_id) (MT6360_REG_FLEDBASE(_id) + 2)
41 #define MT6360_REG_FLEDITOR(_id) (MT6360_REG_FLEDBASE(_id) + 3)
42 #define MT6360_REG_CHGSTAT2 0x3E1
43 #define MT6360_REG_FLEDSTAT1 0x3E9
44 #define MT6360_ITORCH_MASK GENMASK(4, 0)
45 #define MT6360_ISTROBE_MASK GENMASK(6, 0)
46 #define MT6360_STRBTO_MASK GENMASK(6, 0)
47 #define MT6360_TORCHEN_MASK BIT(3)
48 #define MT6360_STROBEN_MASK BIT(2)
49 #define MT6360_FLCSEN_MASK(_id) BIT(MT6360_LED_FLASH2 - _id)
50 #define MT6360_FLEDCHGVINOVP_MASK BIT(3)
51 #define MT6360_FLED1STRBTO_MASK BIT(11)
52 #define MT6360_FLED2STRBTO_MASK BIT(10)
53 #define MT6360_FLED1STRB_MASK BIT(9)
54 #define MT6360_FLED2STRB_MASK BIT(8)
55 #define MT6360_FLED1SHORT_MASK BIT(7)
56 #define MT6360_FLED2SHORT_MASK BIT(6)
57 #define MT6360_FLEDLVF_MASK BIT(3)
59 #define MT6360_ISNKRGB_STEPUA 2000
60 #define MT6360_ISNKRGB_MAXUA 24000
61 #define MT6360_ISNKML_STEPUA 5000
62 #define MT6360_ISNKML_MAXUA 150000
64 #define MT6360_ITORCH_MINUA 25000
65 #define MT6360_ITORCH_STEPUA 12500
66 #define MT6360_ITORCH_MAXUA 400000
67 #define MT6360_ISTRB_MINUA 50000
68 #define MT6360_ISTRB_STEPUA 12500
69 #define MT6360_ISTRB_MAXUA 1500000
70 #define MT6360_STRBTO_MINUS 64000
71 #define MT6360_STRBTO_STEPUS 32000
72 #define MT6360_STRBTO_MAXUS 2432000
76 struct led_classdev isnk
;
77 struct led_classdev_mc mc
;
78 struct led_classdev_flash flash
;
80 struct v4l2_flash
*v4l2_flash
;
81 struct mt6360_priv
*priv
;
83 enum led_default_state default_state
;
88 struct regmap
*regmap
;
90 unsigned int fled_strobe_used
;
91 unsigned int fled_torch_used
;
92 unsigned int leds_active
;
93 unsigned int leds_count
;
94 struct mt6360_led leds
[] __counted_by(leds_count
);
97 static int mt6360_mc_brightness_set(struct led_classdev
*lcdev
,
98 enum led_brightness level
)
100 struct led_classdev_mc
*mccdev
= lcdev_to_mccdev(lcdev
);
101 struct mt6360_led
*led
= container_of(mccdev
, struct mt6360_led
, mc
);
102 struct mt6360_priv
*priv
= led
->priv
;
103 u32 real_bright
, enable_mask
= 0, enable
= 0;
106 mutex_lock(&priv
->lock
);
108 led_mc_calc_color_components(mccdev
, level
);
110 for (i
= 0; i
< mccdev
->num_colors
; i
++) {
111 struct mc_subled
*subled
= mccdev
->subled_info
+ i
;
113 real_bright
= min(lcdev
->max_brightness
, subled
->brightness
);
114 ret
= regmap_update_bits(priv
->regmap
, MT6360_REG_ISNK(i
),
115 MT6360_ISNK_MASK
, real_bright
);
119 enable_mask
|= MT6360_ISNK_ENMASK(subled
->channel
);
121 enable
|= MT6360_ISNK_ENMASK(subled
->channel
);
124 ret
= regmap_update_bits(priv
->regmap
, MT6360_REG_RGBEN
, enable_mask
,
128 mutex_unlock(&priv
->lock
);
132 static int mt6360_isnk_brightness_set(struct led_classdev
*lcdev
,
133 enum led_brightness level
)
135 struct mt6360_led
*led
= container_of(lcdev
, struct mt6360_led
, isnk
);
136 struct mt6360_priv
*priv
= led
->priv
;
137 u32 enable_mask
= MT6360_ISNK_ENMASK(led
->led_no
);
138 u32 val
= level
? MT6360_ISNK_ENMASK(led
->led_no
) : 0;
141 mutex_lock(&priv
->lock
);
143 ret
= regmap_update_bits(priv
->regmap
, MT6360_REG_ISNK(led
->led_no
),
144 MT6360_ISNK_MASK
, level
);
148 ret
= regmap_update_bits(priv
->regmap
, MT6360_REG_RGBEN
, enable_mask
,
152 mutex_unlock(&priv
->lock
);
156 static int mt6360_torch_brightness_set(struct led_classdev
*lcdev
,
157 enum led_brightness level
)
159 struct mt6360_led
*led
=
160 container_of(lcdev
, struct mt6360_led
, flash
.led_cdev
);
161 struct mt6360_priv
*priv
= led
->priv
;
162 u32 enable_mask
= MT6360_TORCHEN_MASK
| MT6360_FLCSEN_MASK(led
->led_no
);
163 u32 val
= level
? MT6360_FLCSEN_MASK(led
->led_no
) : 0;
164 u32 prev
= priv
->fled_torch_used
, curr
;
167 mutex_lock(&priv
->lock
);
170 * Only one set of flash control logic, use the flag to avoid strobe is
173 if (priv
->fled_strobe_used
) {
174 dev_warn(lcdev
->dev
, "Please disable strobe first [%d]\n",
175 priv
->fled_strobe_used
);
181 curr
= prev
| BIT(led
->led_no
);
183 curr
= prev
& ~BIT(led
->led_no
);
186 val
|= MT6360_TORCHEN_MASK
;
189 ret
= regmap_update_bits(priv
->regmap
,
190 MT6360_REG_FLEDITOR(led
->led_no
),
191 MT6360_ITORCH_MASK
, level
- 1);
196 ret
= regmap_update_bits(priv
->regmap
, MT6360_REG_FLEDEN
, enable_mask
,
201 priv
->fled_torch_used
= curr
;
204 mutex_unlock(&priv
->lock
);
208 static int mt6360_flash_brightness_set(struct led_classdev_flash
*fl_cdev
,
212 * Due to the current spike when turning on flash, let brightness to be
214 * This empty function is used to prevent led_classdev_flash register
220 static int _mt6360_flash_brightness_set(struct led_classdev_flash
*fl_cdev
,
223 struct mt6360_led
*led
=
224 container_of(fl_cdev
, struct mt6360_led
, flash
);
225 struct mt6360_priv
*priv
= led
->priv
;
226 struct led_flash_setting
*s
= &fl_cdev
->brightness
;
227 u32 val
= (brightness
- s
->min
) / s
->step
;
229 return regmap_update_bits(priv
->regmap
,
230 MT6360_REG_FLEDISTRB(led
->led_no
),
231 MT6360_ISTROBE_MASK
, val
);
234 static int mt6360_strobe_set(struct led_classdev_flash
*fl_cdev
, bool state
)
236 struct mt6360_led
*led
=
237 container_of(fl_cdev
, struct mt6360_led
, flash
);
238 struct mt6360_priv
*priv
= led
->priv
;
239 struct led_classdev
*lcdev
= &fl_cdev
->led_cdev
;
240 struct led_flash_setting
*s
= &fl_cdev
->brightness
;
241 u32 enable_mask
= MT6360_STROBEN_MASK
| MT6360_FLCSEN_MASK(led
->led_no
);
242 u32 val
= state
? MT6360_FLCSEN_MASK(led
->led_no
) : 0;
243 u32 prev
= priv
->fled_strobe_used
, curr
;
246 mutex_lock(&priv
->lock
);
249 * If the state of the upcoming change is the same as the current LED
250 * device state, then skip the subsequent code to avoid conflict
251 * with the flow of turning on LED torch mode in V4L2.
253 if (state
== !!(BIT(led
->led_no
) & prev
)) {
254 dev_info(lcdev
->dev
, "No change in strobe state [0x%x]\n", prev
);
259 * Only one set of flash control logic, use the flag to avoid torch is
262 if (priv
->fled_torch_used
) {
263 dev_warn(lcdev
->dev
, "Please disable torch first [0x%x]\n",
264 priv
->fled_torch_used
);
270 curr
= prev
| BIT(led
->led_no
);
272 curr
= prev
& ~BIT(led
->led_no
);
275 val
|= MT6360_STROBEN_MASK
;
277 ret
= regmap_update_bits(priv
->regmap
, MT6360_REG_FLEDEN
, enable_mask
,
280 dev_err(lcdev
->dev
, "[%d] control current source %d fail\n",
286 * If the flash need to be on, config the flash current ramping up to
288 * Else, always recover back to the minimum one
290 ret
= _mt6360_flash_brightness_set(fl_cdev
, state
? s
->val
: s
->min
);
295 * For the flash turn on/off, HW rampping up/down time is 5ms/500us,
299 usleep_range(5000, 6000);
300 else if (prev
&& !curr
)
303 priv
->fled_strobe_used
= curr
;
306 mutex_unlock(&priv
->lock
);
310 static int mt6360_strobe_get(struct led_classdev_flash
*fl_cdev
, bool *state
)
312 struct mt6360_led
*led
=
313 container_of(fl_cdev
, struct mt6360_led
, flash
);
314 struct mt6360_priv
*priv
= led
->priv
;
316 mutex_lock(&priv
->lock
);
317 *state
= !!(priv
->fled_strobe_used
& BIT(led
->led_no
));
318 mutex_unlock(&priv
->lock
);
323 static int mt6360_timeout_set(struct led_classdev_flash
*fl_cdev
, u32 timeout
)
325 struct mt6360_led
*led
=
326 container_of(fl_cdev
, struct mt6360_led
, flash
);
327 struct mt6360_priv
*priv
= led
->priv
;
328 struct led_flash_setting
*s
= &fl_cdev
->timeout
;
329 u32 val
= (timeout
- s
->min
) / s
->step
;
332 mutex_lock(&priv
->lock
);
333 ret
= regmap_update_bits(priv
->regmap
, MT6360_REG_STRBTO
,
334 MT6360_STRBTO_MASK
, val
);
335 mutex_unlock(&priv
->lock
);
340 static int mt6360_fault_get(struct led_classdev_flash
*fl_cdev
, u32
*fault
)
342 struct mt6360_led
*led
=
343 container_of(fl_cdev
, struct mt6360_led
, flash
);
344 struct mt6360_priv
*priv
= led
->priv
;
346 unsigned int chg_stat
, strobe_timeout_mask
, fled_short_mask
;
350 mutex_lock(&priv
->lock
);
351 ret
= regmap_read(priv
->regmap
, MT6360_REG_CHGSTAT2
, &chg_stat
);
355 ret
= regmap_raw_read(priv
->regmap
, MT6360_REG_FLEDSTAT1
, &fled_stat
,
360 if (led
->led_no
== MT6360_LED_FLASH1
) {
361 strobe_timeout_mask
= MT6360_FLED1STRBTO_MASK
;
362 fled_short_mask
= MT6360_FLED1SHORT_MASK
;
364 strobe_timeout_mask
= MT6360_FLED2STRBTO_MASK
;
365 fled_short_mask
= MT6360_FLED2SHORT_MASK
;
368 if (chg_stat
& MT6360_FLEDCHGVINOVP_MASK
)
369 rfault
|= LED_FAULT_INPUT_VOLTAGE
;
371 if (fled_stat
& strobe_timeout_mask
)
372 rfault
|= LED_FAULT_TIMEOUT
;
374 if (fled_stat
& fled_short_mask
)
375 rfault
|= LED_FAULT_SHORT_CIRCUIT
;
377 if (fled_stat
& MT6360_FLEDLVF_MASK
)
378 rfault
|= LED_FAULT_UNDER_VOLTAGE
;
382 mutex_unlock(&priv
->lock
);
386 static const struct led_flash_ops mt6360_flash_ops
= {
387 .flash_brightness_set
= mt6360_flash_brightness_set
,
388 .strobe_set
= mt6360_strobe_set
,
389 .strobe_get
= mt6360_strobe_get
,
390 .timeout_set
= mt6360_timeout_set
,
391 .fault_get
= mt6360_fault_get
,
394 static int mt6360_isnk_init_default_state(struct mt6360_led
*led
)
396 struct mt6360_priv
*priv
= led
->priv
;
401 ret
= regmap_read(priv
->regmap
, MT6360_REG_ISNK(led
->led_no
), ®val
);
404 level
= regval
& MT6360_ISNK_MASK
;
406 ret
= regmap_read(priv
->regmap
, MT6360_REG_RGBEN
, ®val
);
410 if (!(regval
& MT6360_ISNK_ENMASK(led
->led_no
)))
413 switch (led
->default_state
) {
414 case LEDS_DEFSTATE_ON
:
415 led
->isnk
.brightness
= led
->isnk
.max_brightness
;
417 case LEDS_DEFSTATE_KEEP
:
418 led
->isnk
.brightness
= min(level
, led
->isnk
.max_brightness
);
421 led
->isnk
.brightness
= LED_OFF
;
424 return mt6360_isnk_brightness_set(&led
->isnk
, led
->isnk
.brightness
);
427 static int mt6360_flash_init_default_state(struct mt6360_led
*led
)
429 struct led_classdev_flash
*flash
= &led
->flash
;
430 struct mt6360_priv
*priv
= led
->priv
;
431 u32 enable_mask
= MT6360_TORCHEN_MASK
| MT6360_FLCSEN_MASK(led
->led_no
);
436 ret
= regmap_read(priv
->regmap
, MT6360_REG_FLEDITOR(led
->led_no
),
440 level
= regval
& MT6360_ITORCH_MASK
;
442 ret
= regmap_read(priv
->regmap
, MT6360_REG_FLEDEN
, ®val
);
446 if ((regval
& enable_mask
) == enable_mask
)
451 switch (led
->default_state
) {
452 case LEDS_DEFSTATE_ON
:
453 flash
->led_cdev
.brightness
= flash
->led_cdev
.max_brightness
;
455 case LEDS_DEFSTATE_KEEP
:
456 flash
->led_cdev
.brightness
=
457 min(level
, flash
->led_cdev
.max_brightness
);
460 flash
->led_cdev
.brightness
= LED_OFF
;
463 return mt6360_torch_brightness_set(&flash
->led_cdev
,
464 flash
->led_cdev
.brightness
);
467 #if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS)
468 static int mt6360_flash_external_strobe_set(struct v4l2_flash
*v4l2_flash
,
471 struct led_classdev_flash
*flash
= v4l2_flash
->fled_cdev
;
472 struct mt6360_led
*led
= container_of(flash
, struct mt6360_led
, flash
);
473 struct mt6360_priv
*priv
= led
->priv
;
474 u32 mask
= MT6360_FLCSEN_MASK(led
->led_no
);
475 u32 val
= enable
? mask
: 0;
478 mutex_lock(&priv
->lock
);
480 ret
= regmap_update_bits(priv
->regmap
, MT6360_REG_FLEDEN
, mask
, val
);
485 priv
->fled_strobe_used
|= BIT(led
->led_no
);
487 priv
->fled_strobe_used
&= ~BIT(led
->led_no
);
490 mutex_unlock(&priv
->lock
);
494 static const struct v4l2_flash_ops v4l2_flash_ops
= {
495 .external_strobe_set
= mt6360_flash_external_strobe_set
,
498 static void mt6360_init_v4l2_flash_config(struct mt6360_led
*led
,
499 struct v4l2_flash_config
*config
)
501 struct led_classdev
*lcdev
;
502 struct led_flash_setting
*s
= &config
->intensity
;
504 lcdev
= &led
->flash
.led_cdev
;
506 s
->min
= MT6360_ITORCH_MINUA
;
507 s
->step
= MT6360_ITORCH_STEPUA
;
508 s
->val
= s
->max
= s
->min
+ (lcdev
->max_brightness
- 1) * s
->step
;
510 config
->has_external_strobe
= 1;
511 strscpy(config
->dev_name
, lcdev
->dev
->kobj
.name
,
512 sizeof(config
->dev_name
));
514 config
->flash_faults
= LED_FAULT_SHORT_CIRCUIT
| LED_FAULT_TIMEOUT
|
515 LED_FAULT_INPUT_VOLTAGE
|
516 LED_FAULT_UNDER_VOLTAGE
;
519 static const struct v4l2_flash_ops v4l2_flash_ops
;
520 static void mt6360_init_v4l2_flash_config(struct mt6360_led
*led
,
521 struct v4l2_flash_config
*config
)
526 static int mt6360_led_register(struct device
*parent
, struct mt6360_led
*led
,
527 struct led_init_data
*init_data
)
529 struct mt6360_priv
*priv
= led
->priv
;
530 struct v4l2_flash_config v4l2_config
= {0};
533 if ((led
->led_no
== MT6360_LED_ISNK1
||
534 led
->led_no
== MT6360_VIRTUAL_MULTICOLOR
) &&
535 (priv
->leds_active
& BIT(MT6360_LED_ISNK1
))) {
537 * Change isink1 to SW control mode, disconnect it with
540 ret
= regmap_update_bits(priv
->regmap
, MT6360_REG_RGBEN
,
541 MT6360_CHRINDSEL_MASK
,
542 MT6360_CHRINDSEL_MASK
);
544 dev_err(parent
, "Failed to config ISNK1 to SW mode\n");
549 switch (led
->led_no
) {
550 case MT6360_VIRTUAL_MULTICOLOR
:
551 ret
= mt6360_mc_brightness_set(&led
->mc
.led_cdev
, LED_OFF
);
554 "Failed to init multicolor brightness\n");
558 ret
= devm_led_classdev_multicolor_register_ext(parent
,
559 &led
->mc
, init_data
);
561 dev_err(parent
, "Couldn't register multicolor\n");
565 case MT6360_LED_ISNK1
... MT6360_LED_ISNKML
:
566 ret
= mt6360_isnk_init_default_state(led
);
568 dev_err(parent
, "Failed to init %d isnk state\n",
573 ret
= devm_led_classdev_register_ext(parent
, &led
->isnk
,
576 dev_err(parent
, "Couldn't register isink %d\n",
582 ret
= mt6360_flash_init_default_state(led
);
584 dev_err(parent
, "Failed to init %d flash state\n",
589 ret
= devm_led_classdev_flash_register_ext(parent
, &led
->flash
,
592 dev_err(parent
, "Couldn't register flash %d\n",
597 mt6360_init_v4l2_flash_config(led
, &v4l2_config
);
598 led
->v4l2_flash
= v4l2_flash_init(parent
, init_data
->fwnode
,
602 if (IS_ERR(led
->v4l2_flash
)) {
603 dev_err(parent
, "Failed to register %d v4l2 sd\n",
605 return PTR_ERR(led
->v4l2_flash
);
612 static u32
clamp_align(u32 val
, u32 min
, u32 max
, u32 step
)
616 retval
= clamp_val(val
, min
, max
);
618 retval
= rounddown(retval
- min
, step
) + min
;
623 static int mt6360_init_isnk_properties(struct mt6360_led
*led
,
624 struct led_init_data
*init_data
)
626 struct led_classdev
*lcdev
;
627 struct mt6360_priv
*priv
= led
->priv
;
628 struct fwnode_handle
*child
;
629 u32 step_uA
= MT6360_ISNKRGB_STEPUA
, max_uA
= MT6360_ISNKRGB_MAXUA
;
631 int num_color
= 0, ret
;
633 if (led
->led_no
== MT6360_VIRTUAL_MULTICOLOR
) {
634 struct mc_subled
*sub_led
;
636 sub_led
= devm_kzalloc(priv
->dev
,
637 sizeof(*sub_led
) * MULTICOLOR_NUM_CHANNELS
, GFP_KERNEL
);
641 fwnode_for_each_child_node(init_data
->fwnode
, child
) {
644 ret
= fwnode_property_read_u32(child
, "reg", ®
);
645 if (ret
|| reg
> MT6360_LED_ISNK3
||
646 priv
->leds_active
& BIT(reg
)) {
647 fwnode_handle_put(child
);
651 ret
= fwnode_property_read_u32(child
, "color", &color
);
654 "led %d, no color specified\n",
656 fwnode_handle_put(child
);
660 priv
->leds_active
|= BIT(reg
);
661 sub_led
[num_color
].color_index
= color
;
662 sub_led
[num_color
].channel
= reg
;
668 "Multicolor must include 2 or more led channel\n");
672 led
->mc
.num_colors
= num_color
;
673 led
->mc
.subled_info
= sub_led
;
675 lcdev
= &led
->mc
.led_cdev
;
676 lcdev
->brightness_set_blocking
= mt6360_mc_brightness_set
;
678 if (led
->led_no
== MT6360_LED_ISNKML
) {
679 step_uA
= MT6360_ISNKML_STEPUA
;
680 max_uA
= MT6360_ISNKML_MAXUA
;
684 lcdev
->brightness_set_blocking
= mt6360_isnk_brightness_set
;
687 ret
= fwnode_property_read_u32(init_data
->fwnode
, "led-max-microamp",
691 "Not specified led-max-microamp, config to the minimum\n");
694 val
= clamp_align(val
, 0, max_uA
, step_uA
);
696 lcdev
->max_brightness
= val
/ step_uA
;
698 fwnode_property_read_string(init_data
->fwnode
, "linux,default-trigger",
699 &lcdev
->default_trigger
);
704 static int mt6360_init_flash_properties(struct mt6360_led
*led
,
705 struct led_init_data
*init_data
)
707 struct led_classdev_flash
*flash
= &led
->flash
;
708 struct led_classdev
*lcdev
= &flash
->led_cdev
;
709 struct mt6360_priv
*priv
= led
->priv
;
710 struct led_flash_setting
*s
;
714 ret
= fwnode_property_read_u32(init_data
->fwnode
, "led-max-microamp",
718 "Not specified led-max-microamp, config to the minimum\n");
719 val
= MT6360_ITORCH_MINUA
;
721 val
= clamp_align(val
, MT6360_ITORCH_MINUA
, MT6360_ITORCH_MAXUA
,
722 MT6360_ITORCH_STEPUA
);
724 lcdev
->max_brightness
=
725 (val
- MT6360_ITORCH_MINUA
) / MT6360_ITORCH_STEPUA
+ 1;
726 lcdev
->brightness_set_blocking
= mt6360_torch_brightness_set
;
727 lcdev
->flags
|= LED_DEV_CAP_FLASH
;
729 ret
= fwnode_property_read_u32(init_data
->fwnode
, "flash-max-microamp",
733 "Not specified flash-max-microamp, config to the minimum\n");
734 val
= MT6360_ISTRB_MINUA
;
736 val
= clamp_align(val
, MT6360_ISTRB_MINUA
, MT6360_ISTRB_MAXUA
,
737 MT6360_ISTRB_STEPUA
);
739 s
= &flash
->brightness
;
740 s
->min
= MT6360_ISTRB_MINUA
;
741 s
->step
= MT6360_ISTRB_STEPUA
;
742 s
->val
= s
->max
= val
;
745 * Always configure as min level when off to prevent flash current
748 ret
= _mt6360_flash_brightness_set(flash
, s
->min
);
752 ret
= fwnode_property_read_u32(init_data
->fwnode
,
753 "flash-max-timeout-us", &val
);
756 "Not specified flash-max-timeout-us, config to the minimum\n");
757 val
= MT6360_STRBTO_MINUS
;
759 val
= clamp_align(val
, MT6360_STRBTO_MINUS
, MT6360_STRBTO_MAXUS
,
760 MT6360_STRBTO_STEPUS
);
763 s
->min
= MT6360_STRBTO_MINUS
;
764 s
->step
= MT6360_STRBTO_STEPUS
;
765 s
->val
= s
->max
= val
;
767 flash
->ops
= &mt6360_flash_ops
;
772 static void mt6360_v4l2_flash_release(struct mt6360_priv
*priv
)
776 for (i
= 0; i
< priv
->leds_count
; i
++) {
777 struct mt6360_led
*led
= priv
->leds
+ i
;
780 v4l2_flash_release(led
->v4l2_flash
);
784 static int mt6360_led_probe(struct platform_device
*pdev
)
786 struct mt6360_priv
*priv
;
790 count
= device_get_child_node_count(&pdev
->dev
);
791 if (!count
|| count
> MT6360_MAX_LEDS
) {
793 "No child node or node count over max led number %zu\n",
798 priv
= devm_kzalloc(&pdev
->dev
,
799 struct_size(priv
, leds
, count
), GFP_KERNEL
);
803 priv
->leds_count
= count
;
804 priv
->dev
= &pdev
->dev
;
805 mutex_init(&priv
->lock
);
807 priv
->regmap
= dev_get_regmap(pdev
->dev
.parent
, NULL
);
809 dev_err(&pdev
->dev
, "Failed to get parent regmap\n");
813 device_for_each_child_node_scoped(&pdev
->dev
, child
) {
814 struct mt6360_led
*led
= priv
->leds
+ i
;
815 struct led_init_data init_data
= { .fwnode
= child
, };
818 ret
= fwnode_property_read_u32(child
, "color", &led_color
);
820 goto out_flash_release
;
822 if (led_color
== LED_COLOR_ID_RGB
||
823 led_color
== LED_COLOR_ID_MULTI
)
824 reg
= MT6360_VIRTUAL_MULTICOLOR
;
826 ret
= fwnode_property_read_u32(child
, "reg", ®
);
828 goto out_flash_release
;
830 if (reg
>= MT6360_MAX_LEDS
) {
832 goto out_flash_release
;
836 if (priv
->leds_active
& BIT(reg
)) {
838 goto out_flash_release
;
840 priv
->leds_active
|= BIT(reg
);
844 led
->default_state
= led_init_default_state_get(child
);
846 if (reg
== MT6360_VIRTUAL_MULTICOLOR
||
847 reg
<= MT6360_LED_ISNKML
)
848 ret
= mt6360_init_isnk_properties(led
, &init_data
);
850 ret
= mt6360_init_flash_properties(led
, &init_data
);
853 goto out_flash_release
;
855 ret
= mt6360_led_register(&pdev
->dev
, led
, &init_data
);
857 goto out_flash_release
;
862 platform_set_drvdata(pdev
, priv
);
866 mt6360_v4l2_flash_release(priv
);
870 static void mt6360_led_remove(struct platform_device
*pdev
)
872 struct mt6360_priv
*priv
= platform_get_drvdata(pdev
);
874 mt6360_v4l2_flash_release(priv
);
877 static const struct of_device_id __maybe_unused mt6360_led_of_id
[] = {
878 { .compatible
= "mediatek,mt6360-led", },
881 MODULE_DEVICE_TABLE(of
, mt6360_led_of_id
);
883 static struct platform_driver mt6360_led_driver
= {
885 .name
= "mt6360-led",
886 .of_match_table
= mt6360_led_of_id
,
888 .probe
= mt6360_led_probe
,
889 .remove
= mt6360_led_remove
,
891 module_platform_driver(mt6360_led_driver
);
893 MODULE_AUTHOR("Gene Chen <gene_chen@richtek.com>");
894 MODULE_DESCRIPTION("MT6360 LED Driver");
895 MODULE_LICENSE("GPL v2");