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