xref: /linux/drivers/leds/flash/leds-s2m-flash.c (revision 00599d4841790d05401820a96cf7edb193888b00)
1*02149db2SKaustabh Chakraborty // SPDX-License-Identifier: GPL-2.0
2*02149db2SKaustabh Chakraborty /*
3*02149db2SKaustabh Chakraborty  * Flash and Torch LED Driver for Samsung S2M series PMICs.
4*02149db2SKaustabh Chakraborty  *
5*02149db2SKaustabh Chakraborty  * Copyright (c) 2015 Samsung Electronics Co., Ltd
6*02149db2SKaustabh Chakraborty  * Copyright (c) 2026 Kaustabh Chakraborty <kauschluss@disroot.org>
7*02149db2SKaustabh Chakraborty  */
8*02149db2SKaustabh Chakraborty 
9*02149db2SKaustabh Chakraborty #include <linux/container_of.h>
10*02149db2SKaustabh Chakraborty #include <linux/led-class-flash.h>
11*02149db2SKaustabh Chakraborty #include <linux/mfd/samsung/core.h>
12*02149db2SKaustabh Chakraborty #include <linux/mfd/samsung/s2mu005.h>
13*02149db2SKaustabh Chakraborty #include <linux/module.h>
14*02149db2SKaustabh Chakraborty #include <linux/of.h>
15*02149db2SKaustabh Chakraborty #include <linux/platform_device.h>
16*02149db2SKaustabh Chakraborty #include <linux/regmap.h>
17*02149db2SKaustabh Chakraborty #include <media/v4l2-flash-led-class.h>
18*02149db2SKaustabh Chakraborty 
19*02149db2SKaustabh Chakraborty #define MAX_CHANNELS	2
20*02149db2SKaustabh Chakraborty 
21*02149db2SKaustabh Chakraborty struct s2m_led {
22*02149db2SKaustabh Chakraborty 	struct regmap *regmap;
23*02149db2SKaustabh Chakraborty 	struct led_classdev_flash fled;
24*02149db2SKaustabh Chakraborty 	struct v4l2_flash *v4l2_flash;
25*02149db2SKaustabh Chakraborty 	/*
26*02149db2SKaustabh Chakraborty 	 * The mutex object prevents the concurrent access of flash control
27*02149db2SKaustabh Chakraborty 	 * registers by the LED and V4L2 subsystems.
28*02149db2SKaustabh Chakraborty 	 */
29*02149db2SKaustabh Chakraborty 	struct mutex lock;
30*02149db2SKaustabh Chakraborty 	unsigned int reg_enable;
31*02149db2SKaustabh Chakraborty 	u8 channel;
32*02149db2SKaustabh Chakraborty 	u8 flash_brightness;
33*02149db2SKaustabh Chakraborty 	u8 flash_timeout;
34*02149db2SKaustabh Chakraborty };
35*02149db2SKaustabh Chakraborty 
36*02149db2SKaustabh Chakraborty static struct s2m_led *to_s2m_led(struct led_classdev_flash *fled)
37*02149db2SKaustabh Chakraborty {
38*02149db2SKaustabh Chakraborty 	return container_of(fled, struct s2m_led, fled);
39*02149db2SKaustabh Chakraborty }
40*02149db2SKaustabh Chakraborty 
41*02149db2SKaustabh Chakraborty static struct led_classdev_flash *to_s2m_fled(struct led_classdev *cdev)
42*02149db2SKaustabh Chakraborty {
43*02149db2SKaustabh Chakraborty 	return container_of(cdev, struct led_classdev_flash, led_cdev);
44*02149db2SKaustabh Chakraborty }
45*02149db2SKaustabh Chakraborty 
46*02149db2SKaustabh Chakraborty static int s2m_fled_flash_brightness_set(struct led_classdev_flash *fled, u32 brightness)
47*02149db2SKaustabh Chakraborty {
48*02149db2SKaustabh Chakraborty 	struct s2m_led *led = to_s2m_led(fled);
49*02149db2SKaustabh Chakraborty 	struct led_flash_setting *setting = &fled->brightness;
50*02149db2SKaustabh Chakraborty 
51*02149db2SKaustabh Chakraborty 	mutex_lock(&led->lock);
52*02149db2SKaustabh Chakraborty 	led->flash_brightness = (brightness - setting->min) / setting->step;
53*02149db2SKaustabh Chakraborty 	mutex_unlock(&led->lock);
54*02149db2SKaustabh Chakraborty 
55*02149db2SKaustabh Chakraborty 	return 0;
56*02149db2SKaustabh Chakraborty }
57*02149db2SKaustabh Chakraborty 
58*02149db2SKaustabh Chakraborty static int s2m_fled_flash_timeout_set(struct led_classdev_flash *fled, u32 timeout)
59*02149db2SKaustabh Chakraborty {
60*02149db2SKaustabh Chakraborty 	struct s2m_led *led = to_s2m_led(fled);
61*02149db2SKaustabh Chakraborty 	struct led_flash_setting *setting = &fled->timeout;
62*02149db2SKaustabh Chakraborty 
63*02149db2SKaustabh Chakraborty 	mutex_lock(&led->lock);
64*02149db2SKaustabh Chakraborty 	led->flash_timeout = (timeout - setting->min) / setting->step;
65*02149db2SKaustabh Chakraborty 	mutex_unlock(&led->lock);
66*02149db2SKaustabh Chakraborty 
67*02149db2SKaustabh Chakraborty 	return 0;
68*02149db2SKaustabh Chakraborty }
69*02149db2SKaustabh Chakraborty 
70*02149db2SKaustabh Chakraborty #if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS)
71*02149db2SKaustabh Chakraborty static int s2m_fled_flash_external_strobe_set(struct v4l2_flash *v4l2_flash, bool enable)
72*02149db2SKaustabh Chakraborty {
73*02149db2SKaustabh Chakraborty 	struct s2m_led *led = to_s2m_led(v4l2_flash->fled_cdev);
74*02149db2SKaustabh Chakraborty 
75*02149db2SKaustabh Chakraborty 	return led->fled.ops->strobe_set(&led->fled, enable);
76*02149db2SKaustabh Chakraborty }
77*02149db2SKaustabh Chakraborty 
78*02149db2SKaustabh Chakraborty static const struct v4l2_flash_ops s2m_fled_v4l2_flash_ops = {
79*02149db2SKaustabh Chakraborty 	.external_strobe_set = s2m_fled_flash_external_strobe_set,
80*02149db2SKaustabh Chakraborty };
81*02149db2SKaustabh Chakraborty #else
82*02149db2SKaustabh Chakraborty static const struct v4l2_flash_ops s2m_fled_v4l2_flash_ops;
83*02149db2SKaustabh Chakraborty #endif
84*02149db2SKaustabh Chakraborty 
85*02149db2SKaustabh Chakraborty static void s2m_fled_v4l2_flash_release(void *v4l2_flash)
86*02149db2SKaustabh Chakraborty {
87*02149db2SKaustabh Chakraborty 	v4l2_flash_release(v4l2_flash);
88*02149db2SKaustabh Chakraborty }
89*02149db2SKaustabh Chakraborty 
90*02149db2SKaustabh Chakraborty static int s2mu005_fled_torch_brightness_set(struct led_classdev *cdev, enum led_brightness value)
91*02149db2SKaustabh Chakraborty {
92*02149db2SKaustabh Chakraborty 	struct s2m_led *led = to_s2m_led(to_s2m_fled(cdev));
93*02149db2SKaustabh Chakraborty 	int ret;
94*02149db2SKaustabh Chakraborty 
95*02149db2SKaustabh Chakraborty 	mutex_lock(&led->lock);
96*02149db2SKaustabh Chakraborty 
97*02149db2SKaustabh Chakraborty 	if (!value) {
98*02149db2SKaustabh Chakraborty 		ret = regmap_clear_bits(led->regmap, led->reg_enable,
99*02149db2SKaustabh Chakraborty 					S2MU005_FLED_TORCH_EN(led->channel));
100*02149db2SKaustabh Chakraborty 		if (ret)
101*02149db2SKaustabh Chakraborty 			dev_err(cdev->dev, "failed to disable torch LED\n");
102*02149db2SKaustabh Chakraborty 		goto unlock;
103*02149db2SKaustabh Chakraborty 	}
104*02149db2SKaustabh Chakraborty 
105*02149db2SKaustabh Chakraborty 	ret = regmap_update_bits(led->regmap, S2MU005_REG_FLED_CH_CTRL1(led->channel),
106*02149db2SKaustabh Chakraborty 				 S2MU005_FLED_TORCH_IOUT,
107*02149db2SKaustabh Chakraborty 				 FIELD_PREP(S2MU005_FLED_TORCH_IOUT, value - 1));
108*02149db2SKaustabh Chakraborty 	if (ret) {
109*02149db2SKaustabh Chakraborty 		dev_err(cdev->dev, "failed to set torch current\n");
110*02149db2SKaustabh Chakraborty 		goto unlock;
111*02149db2SKaustabh Chakraborty 	}
112*02149db2SKaustabh Chakraborty 
113*02149db2SKaustabh Chakraborty 	ret = regmap_set_bits(led->regmap, led->reg_enable, S2MU005_FLED_TORCH_EN(led->channel));
114*02149db2SKaustabh Chakraborty 	if (ret) {
115*02149db2SKaustabh Chakraborty 		dev_err(cdev->dev, "failed to enable torch LED\n");
116*02149db2SKaustabh Chakraborty 		goto unlock;
117*02149db2SKaustabh Chakraborty 	}
118*02149db2SKaustabh Chakraborty 
119*02149db2SKaustabh Chakraborty unlock:
120*02149db2SKaustabh Chakraborty 	mutex_unlock(&led->lock);
121*02149db2SKaustabh Chakraborty 
122*02149db2SKaustabh Chakraborty 	return ret;
123*02149db2SKaustabh Chakraborty }
124*02149db2SKaustabh Chakraborty 
125*02149db2SKaustabh Chakraborty static int s2mu005_fled_flash_strobe_set(struct led_classdev_flash *fled, bool state)
126*02149db2SKaustabh Chakraborty {
127*02149db2SKaustabh Chakraborty 	struct s2m_led *led = to_s2m_led(fled);
128*02149db2SKaustabh Chakraborty 	int ret;
129*02149db2SKaustabh Chakraborty 
130*02149db2SKaustabh Chakraborty 	mutex_lock(&led->lock);
131*02149db2SKaustabh Chakraborty 
132*02149db2SKaustabh Chakraborty 	ret = regmap_clear_bits(led->regmap, led->reg_enable, S2MU005_FLED_FLASH_EN(led->channel));
133*02149db2SKaustabh Chakraborty 	if (ret) {
134*02149db2SKaustabh Chakraborty 		dev_err(fled->led_cdev.dev, "failed to disable flash LED\n");
135*02149db2SKaustabh Chakraborty 		goto unlock;
136*02149db2SKaustabh Chakraborty 	}
137*02149db2SKaustabh Chakraborty 
138*02149db2SKaustabh Chakraborty 	if (!state)
139*02149db2SKaustabh Chakraborty 		goto unlock;
140*02149db2SKaustabh Chakraborty 
141*02149db2SKaustabh Chakraborty 	ret = regmap_update_bits(led->regmap, S2MU005_REG_FLED_CH_CTRL0(led->channel),
142*02149db2SKaustabh Chakraborty 				 S2MU005_FLED_FLASH_IOUT,
143*02149db2SKaustabh Chakraborty 				 FIELD_PREP(S2MU005_FLED_FLASH_IOUT, led->flash_brightness));
144*02149db2SKaustabh Chakraborty 	if (ret) {
145*02149db2SKaustabh Chakraborty 		dev_err(fled->led_cdev.dev, "failed to set flash brightness\n");
146*02149db2SKaustabh Chakraborty 		goto unlock;
147*02149db2SKaustabh Chakraborty 	}
148*02149db2SKaustabh Chakraborty 
149*02149db2SKaustabh Chakraborty 	ret = regmap_update_bits(led->regmap, S2MU005_REG_FLED_CH_CTRL3(led->channel),
150*02149db2SKaustabh Chakraborty 				 S2MU005_FLED_FLASH_TIMEOUT,
151*02149db2SKaustabh Chakraborty 				 FIELD_PREP(S2MU005_FLED_FLASH_TIMEOUT, led->flash_timeout));
152*02149db2SKaustabh Chakraborty 	if (ret) {
153*02149db2SKaustabh Chakraborty 		dev_err(fled->led_cdev.dev, "failed to set flash timeout\n");
154*02149db2SKaustabh Chakraborty 		goto unlock;
155*02149db2SKaustabh Chakraborty 	}
156*02149db2SKaustabh Chakraborty 
157*02149db2SKaustabh Chakraborty 	ret = regmap_set_bits(led->regmap, led->reg_enable, S2MU005_FLED_FLASH_EN(led->channel));
158*02149db2SKaustabh Chakraborty 	if (ret) {
159*02149db2SKaustabh Chakraborty 		dev_err(fled->led_cdev.dev, "failed to enable flash LED\n");
160*02149db2SKaustabh Chakraborty 		goto unlock;
161*02149db2SKaustabh Chakraborty 	}
162*02149db2SKaustabh Chakraborty 
163*02149db2SKaustabh Chakraborty unlock:
164*02149db2SKaustabh Chakraborty 	mutex_unlock(&led->lock);
165*02149db2SKaustabh Chakraborty 
166*02149db2SKaustabh Chakraborty 	return ret;
167*02149db2SKaustabh Chakraborty }
168*02149db2SKaustabh Chakraborty 
169*02149db2SKaustabh Chakraborty static int s2mu005_fled_flash_strobe_get(struct led_classdev_flash *fled, bool *state)
170*02149db2SKaustabh Chakraborty {
171*02149db2SKaustabh Chakraborty 	struct s2m_led *led = to_s2m_led(fled);
172*02149db2SKaustabh Chakraborty 	u32 val;
173*02149db2SKaustabh Chakraborty 	int ret;
174*02149db2SKaustabh Chakraborty 
175*02149db2SKaustabh Chakraborty 	mutex_lock(&led->lock);
176*02149db2SKaustabh Chakraborty 
177*02149db2SKaustabh Chakraborty 	ret = regmap_read(led->regmap, S2MU005_REG_FLED_STATUS, &val);
178*02149db2SKaustabh Chakraborty 	if (ret) {
179*02149db2SKaustabh Chakraborty 		dev_err(fled->led_cdev.dev, "failed to fetch LED status\n");
180*02149db2SKaustabh Chakraborty 		goto unlock;
181*02149db2SKaustabh Chakraborty 	}
182*02149db2SKaustabh Chakraborty 
183*02149db2SKaustabh Chakraborty 	*state = !!(val & S2MU005_FLED_FLASH_STATUS(led->channel));
184*02149db2SKaustabh Chakraborty 
185*02149db2SKaustabh Chakraborty unlock:
186*02149db2SKaustabh Chakraborty 	mutex_unlock(&led->lock);
187*02149db2SKaustabh Chakraborty 
188*02149db2SKaustabh Chakraborty 	return ret;
189*02149db2SKaustabh Chakraborty }
190*02149db2SKaustabh Chakraborty 
191*02149db2SKaustabh Chakraborty static const struct led_flash_ops s2mu005_fled_flash_ops = {
192*02149db2SKaustabh Chakraborty 	.flash_brightness_set = s2m_fled_flash_brightness_set,
193*02149db2SKaustabh Chakraborty 	.timeout_set = s2m_fled_flash_timeout_set,
194*02149db2SKaustabh Chakraborty 	.strobe_set = s2mu005_fled_flash_strobe_set,
195*02149db2SKaustabh Chakraborty 	.strobe_get = s2mu005_fled_flash_strobe_get,
196*02149db2SKaustabh Chakraborty };
197*02149db2SKaustabh Chakraborty 
198*02149db2SKaustabh Chakraborty static int s2mu005_fled_init(struct s2m_led *led, struct device *dev, struct regmap *regmap,
199*02149db2SKaustabh Chakraborty 			     unsigned int nr_channels)
200*02149db2SKaustabh Chakraborty {
201*02149db2SKaustabh Chakraborty 	unsigned int val;
202*02149db2SKaustabh Chakraborty 	int ret;
203*02149db2SKaustabh Chakraborty 
204*02149db2SKaustabh Chakraborty 	ret = regmap_read(regmap, S2MU005_REG_ID, &val);
205*02149db2SKaustabh Chakraborty 	if (ret)
206*02149db2SKaustabh Chakraborty 		return dev_err_probe(dev, ret, "failed to read revision\n");
207*02149db2SKaustabh Chakraborty 
208*02149db2SKaustabh Chakraborty 	for (int i = 0; i < nr_channels; i++) {
209*02149db2SKaustabh Chakraborty 		/*
210*02149db2SKaustabh Chakraborty 		 * Read the revision register. Revision EVT0 has the register
211*02149db2SKaustabh Chakraborty 		 * at CTRL4, while EVT1 and higher have it at CTRL6.
212*02149db2SKaustabh Chakraborty 		 */
213*02149db2SKaustabh Chakraborty 		if (FIELD_GET(S2MU005_ID_MASK, val) == 0)
214*02149db2SKaustabh Chakraborty 			led[i].reg_enable = S2MU005_REG_FLED_CTRL4;
215*02149db2SKaustabh Chakraborty 		else
216*02149db2SKaustabh Chakraborty 			led[i].reg_enable = S2MU005_REG_FLED_CTRL6;
217*02149db2SKaustabh Chakraborty 	}
218*02149db2SKaustabh Chakraborty 
219*02149db2SKaustabh Chakraborty 	/* Enable the LED channels. */
220*02149db2SKaustabh Chakraborty 	ret = regmap_set_bits(regmap, S2MU005_REG_FLED_CTRL1, S2MU005_FLED_CH_EN);
221*02149db2SKaustabh Chakraborty 	if (ret)
222*02149db2SKaustabh Chakraborty 		return dev_err_probe(dev, ret, "failed to enable LED channels\n");
223*02149db2SKaustabh Chakraborty 
224*02149db2SKaustabh Chakraborty 	return 0;
225*02149db2SKaustabh Chakraborty }
226*02149db2SKaustabh Chakraborty 
227*02149db2SKaustabh Chakraborty static int s2mu005_fled_init_channel(struct s2m_led *led, struct device *dev,
228*02149db2SKaustabh Chakraborty 				     struct fwnode_handle *fwnp)
229*02149db2SKaustabh Chakraborty {
230*02149db2SKaustabh Chakraborty 	struct led_classdev *cdev = &led->fled.led_cdev;
231*02149db2SKaustabh Chakraborty 	struct led_init_data init_data = {};
232*02149db2SKaustabh Chakraborty 	struct v4l2_flash_config v4l2_cfg = {};
233*02149db2SKaustabh Chakraborty 	int ret;
234*02149db2SKaustabh Chakraborty 
235*02149db2SKaustabh Chakraborty 	cdev->max_brightness = 16;
236*02149db2SKaustabh Chakraborty 	cdev->brightness_set_blocking = s2mu005_fled_torch_brightness_set;
237*02149db2SKaustabh Chakraborty 	cdev->flags |= LED_DEV_CAP_FLASH;
238*02149db2SKaustabh Chakraborty 
239*02149db2SKaustabh Chakraborty 	led->fled.timeout.min = 62000;
240*02149db2SKaustabh Chakraborty 	led->fled.timeout.step = 62000;
241*02149db2SKaustabh Chakraborty 	led->fled.timeout.max = 992000;
242*02149db2SKaustabh Chakraborty 	led->fled.timeout.val = 992000;
243*02149db2SKaustabh Chakraborty 
244*02149db2SKaustabh Chakraborty 	led->fled.brightness.min = 25000;
245*02149db2SKaustabh Chakraborty 	led->fled.brightness.step = 25000;
246*02149db2SKaustabh Chakraborty 	led->fled.brightness.max = 375000; /* 400000 causes flickering */
247*02149db2SKaustabh Chakraborty 	led->fled.brightness.val = 375000;
248*02149db2SKaustabh Chakraborty 
249*02149db2SKaustabh Chakraborty 	s2m_fled_flash_timeout_set(&led->fled, led->fled.timeout.val);
250*02149db2SKaustabh Chakraborty 	s2m_fled_flash_brightness_set(&led->fled, led->fled.brightness.val);
251*02149db2SKaustabh Chakraborty 
252*02149db2SKaustabh Chakraborty 	led->fled.ops = &s2mu005_fled_flash_ops;
253*02149db2SKaustabh Chakraborty 
254*02149db2SKaustabh Chakraborty 	init_data.fwnode = fwnp;
255*02149db2SKaustabh Chakraborty 	ret = devm_led_classdev_flash_register_ext(dev, &led->fled, &init_data);
256*02149db2SKaustabh Chakraborty 	if (ret)
257*02149db2SKaustabh Chakraborty 		return dev_err_probe(dev, ret, "failed to create LED flash device\n");
258*02149db2SKaustabh Chakraborty 
259*02149db2SKaustabh Chakraborty 	v4l2_cfg.intensity.min = led->fled.brightness.min;
260*02149db2SKaustabh Chakraborty 	v4l2_cfg.intensity.step = led->fled.brightness.step;
261*02149db2SKaustabh Chakraborty 	v4l2_cfg.intensity.max = led->fled.brightness.max;
262*02149db2SKaustabh Chakraborty 	v4l2_cfg.intensity.val = led->fled.brightness.val;
263*02149db2SKaustabh Chakraborty 
264*02149db2SKaustabh Chakraborty 	v4l2_cfg.has_external_strobe = true;
265*02149db2SKaustabh Chakraborty 
266*02149db2SKaustabh Chakraborty 	led->v4l2_flash = v4l2_flash_init(dev, fwnp, &led->fled, &s2m_fled_v4l2_flash_ops,
267*02149db2SKaustabh Chakraborty 					  &v4l2_cfg);
268*02149db2SKaustabh Chakraborty 	if (IS_ERR(led->v4l2_flash))
269*02149db2SKaustabh Chakraborty 		return dev_err_probe(dev, PTR_ERR(led->v4l2_flash),
270*02149db2SKaustabh Chakraborty 				     "failed to create V4L2 flash device\n");
271*02149db2SKaustabh Chakraborty 
272*02149db2SKaustabh Chakraborty 	ret = devm_add_action_or_reset(dev, s2m_fled_v4l2_flash_release, led->v4l2_flash);
273*02149db2SKaustabh Chakraborty 	if (ret)
274*02149db2SKaustabh Chakraborty 		return dev_err_probe(dev, ret, "failed to add cleanup action\n");
275*02149db2SKaustabh Chakraborty 
276*02149db2SKaustabh Chakraborty 	return 0;
277*02149db2SKaustabh Chakraborty }
278*02149db2SKaustabh Chakraborty 
279*02149db2SKaustabh Chakraborty static int s2m_fled_probe(struct platform_device *pdev)
280*02149db2SKaustabh Chakraborty {
281*02149db2SKaustabh Chakraborty 	struct device *dev = &pdev->dev;
282*02149db2SKaustabh Chakraborty 	struct sec_pmic_dev *ddata = dev_get_drvdata(dev->parent);
283*02149db2SKaustabh Chakraborty 	struct s2m_led *led;
284*02149db2SKaustabh Chakraborty 	int ret;
285*02149db2SKaustabh Chakraborty 
286*02149db2SKaustabh Chakraborty 	led = devm_kzalloc(dev, sizeof(*led) * MAX_CHANNELS, GFP_KERNEL);
287*02149db2SKaustabh Chakraborty 	if (!led)
288*02149db2SKaustabh Chakraborty 		return -ENOMEM;
289*02149db2SKaustabh Chakraborty 
290*02149db2SKaustabh Chakraborty 	/* Initialize LED controller with variant-specific implementation. */
291*02149db2SKaustabh Chakraborty 	ret = s2mu005_fled_init(led, dev, ddata->regmap_pmic, MAX_CHANNELS);
292*02149db2SKaustabh Chakraborty 	if (ret)
293*02149db2SKaustabh Chakraborty 		return ret;
294*02149db2SKaustabh Chakraborty 
295*02149db2SKaustabh Chakraborty 	device_for_each_child_node_scoped(dev, child) {
296*02149db2SKaustabh Chakraborty 		u32 reg;
297*02149db2SKaustabh Chakraborty 
298*02149db2SKaustabh Chakraborty 		if (fwnode_property_read_u32(child, "reg", &reg))
299*02149db2SKaustabh Chakraborty 			continue;
300*02149db2SKaustabh Chakraborty 
301*02149db2SKaustabh Chakraborty 		if (reg >= MAX_CHANNELS) {
302*02149db2SKaustabh Chakraborty 			dev_warn(dev, "channel %d is non-existent\n", reg);
303*02149db2SKaustabh Chakraborty 			continue;
304*02149db2SKaustabh Chakraborty 		}
305*02149db2SKaustabh Chakraborty 
306*02149db2SKaustabh Chakraborty 		if (led[reg].regmap) {
307*02149db2SKaustabh Chakraborty 			dev_warn(dev, "duplicate node for channel %d\n", reg);
308*02149db2SKaustabh Chakraborty 			continue;
309*02149db2SKaustabh Chakraborty 		}
310*02149db2SKaustabh Chakraborty 
311*02149db2SKaustabh Chakraborty 		led[reg].regmap = ddata->regmap_pmic;
312*02149db2SKaustabh Chakraborty 		led[reg].channel = (u8)reg;
313*02149db2SKaustabh Chakraborty 
314*02149db2SKaustabh Chakraborty 		ret = devm_mutex_init(dev, &led[reg].lock);
315*02149db2SKaustabh Chakraborty 		if (ret)
316*02149db2SKaustabh Chakraborty 			return dev_err_probe(dev, ret, "failed to create mutex\n");
317*02149db2SKaustabh Chakraborty 
318*02149db2SKaustabh Chakraborty 		/* Initialize LED channel with variant-specific implementation. */
319*02149db2SKaustabh Chakraborty 		ret = s2mu005_fled_init_channel(led + reg, dev, child);
320*02149db2SKaustabh Chakraborty 		if (ret)
321*02149db2SKaustabh Chakraborty 			return ret;
322*02149db2SKaustabh Chakraborty 	}
323*02149db2SKaustabh Chakraborty 
324*02149db2SKaustabh Chakraborty 	return 0;
325*02149db2SKaustabh Chakraborty }
326*02149db2SKaustabh Chakraborty 
327*02149db2SKaustabh Chakraborty static const struct platform_device_id s2m_fled_id_table[] = {
328*02149db2SKaustabh Chakraborty 	{ "s2mu005-flash", S2MU005 },
329*02149db2SKaustabh Chakraborty 	{ /* sentinel */ },
330*02149db2SKaustabh Chakraborty };
331*02149db2SKaustabh Chakraborty MODULE_DEVICE_TABLE(platform, s2m_fled_id_table);
332*02149db2SKaustabh Chakraborty 
333*02149db2SKaustabh Chakraborty static const struct of_device_id s2m_fled_of_match_table[] = {
334*02149db2SKaustabh Chakraborty 	{ .compatible = "samsung,s2mu005-flash", .data = (void *)S2MU005 },
335*02149db2SKaustabh Chakraborty 	{ /* sentinel */ },
336*02149db2SKaustabh Chakraborty };
337*02149db2SKaustabh Chakraborty MODULE_DEVICE_TABLE(of, s2m_fled_of_match_table);
338*02149db2SKaustabh Chakraborty 
339*02149db2SKaustabh Chakraborty static struct platform_driver s2m_fled_driver = {
340*02149db2SKaustabh Chakraborty 	.driver = {
341*02149db2SKaustabh Chakraborty 		.name = "s2m-flash",
342*02149db2SKaustabh Chakraborty 	},
343*02149db2SKaustabh Chakraborty 	.probe = s2m_fled_probe,
344*02149db2SKaustabh Chakraborty 	.id_table = s2m_fled_id_table,
345*02149db2SKaustabh Chakraborty };
346*02149db2SKaustabh Chakraborty module_platform_driver(s2m_fled_driver);
347*02149db2SKaustabh Chakraborty 
348*02149db2SKaustabh Chakraborty MODULE_DESCRIPTION("Flash/Torch LED Driver for Samsung S2M Series PMICs");
349*02149db2SKaustabh Chakraborty MODULE_AUTHOR("Kaustabh Chakraborty <kauschluss@disroot.org>");
350*02149db2SKaustabh Chakraborty MODULE_LICENSE("GPL");
351