1// SPDX-License-Identifier: GPL-2.0-only
2
3#include <linux/bitops.h>
4#include <linux/i2c.h>
5#include <linux/kernel.h>
6#include <linux/led-class-flash.h>
7#include <linux/module.h>
8#include <linux/mutex.h>
9#include <linux/property.h>
10#include <linux/regmap.h>
11#include <media/v4l2-flash-led-class.h>
12
13#define RT4505_REG_RESET	0x0
14#define RT4505_REG_CONFIG	0x8
15#define RT4505_REG_ILED		0x9
16#define RT4505_REG_ENABLE	0xA
17#define RT4505_REG_FLAGS	0xB
18
19#define RT4505_RESET_MASK	BIT(7)
20#define RT4505_FLASHTO_MASK	GENMASK(2, 0)
21#define RT4505_ITORCH_MASK	GENMASK(7, 5)
22#define RT4505_ITORCH_SHIFT	5
23#define RT4505_IFLASH_MASK	GENMASK(4, 0)
24#define RT4505_ENABLE_MASK	GENMASK(5, 0)
25#define RT4505_TORCH_SET	(BIT(0) | BIT(4))
26#define RT4505_FLASH_SET	(BIT(0) | BIT(1) | BIT(2) | BIT(4))
27#define RT4505_EXT_FLASH_SET	(BIT(0) | BIT(1) | BIT(4) | BIT(5))
28#define RT4505_FLASH_GET	(BIT(0) | BIT(1) | BIT(4))
29#define RT4505_OVP_MASK		BIT(3)
30#define RT4505_SHORT_MASK	BIT(2)
31#define RT4505_OTP_MASK		BIT(1)
32#define RT4505_TIMEOUT_MASK	BIT(0)
33
34#define RT4505_ITORCH_MINUA	46000
35#define RT4505_ITORCH_MAXUA	375000
36#define RT4505_ITORCH_STPUA	47000
37#define RT4505_IFLASH_MINUA	93750
38#define RT4505_IFLASH_MAXUA	1500000
39#define RT4505_IFLASH_STPUA	93750
40#define RT4505_FLASHTO_MINUS	100000
41#define RT4505_FLASHTO_MAXUS	800000
42#define RT4505_FLASHTO_STPUS	100000
43
44struct rt4505_priv {
45	struct device *dev;
46	struct regmap *regmap;
47	struct mutex lock;
48	struct led_classdev_flash flash;
49	struct v4l2_flash *v4l2_flash;
50};
51
52static int rt4505_torch_brightness_set(struct led_classdev *lcdev,
53				       enum led_brightness level)
54{
55	struct rt4505_priv *priv =
56		container_of(lcdev, struct rt4505_priv, flash.led_cdev);
57	u32 val = 0;
58	int ret;
59
60	mutex_lock(&priv->lock);
61
62	if (level != LED_OFF) {
63		ret = regmap_update_bits(priv->regmap,
64					 RT4505_REG_ILED, RT4505_ITORCH_MASK,
65					 (level - 1) << RT4505_ITORCH_SHIFT);
66		if (ret)
67			goto unlock;
68
69		val = RT4505_TORCH_SET;
70	}
71
72	ret = regmap_update_bits(priv->regmap, RT4505_REG_ENABLE,
73				 RT4505_ENABLE_MASK, val);
74
75unlock:
76	mutex_unlock(&priv->lock);
77	return ret;
78}
79
80static enum led_brightness rt4505_torch_brightness_get(
81						struct led_classdev *lcdev)
82{
83	struct rt4505_priv *priv =
84		container_of(lcdev, struct rt4505_priv, flash.led_cdev);
85	u32 val;
86	int ret;
87
88	mutex_lock(&priv->lock);
89
90	ret = regmap_read(priv->regmap, RT4505_REG_ENABLE, &val);
91	if (ret) {
92		dev_err(lcdev->dev, "Failed to get LED enable\n");
93		ret = LED_OFF;
94		goto unlock;
95	}
96
97	if ((val & RT4505_ENABLE_MASK) != RT4505_TORCH_SET) {
98		ret = LED_OFF;
99		goto unlock;
100	}
101
102	ret = regmap_read(priv->regmap, RT4505_REG_ILED, &val);
103	if (ret) {
104		dev_err(lcdev->dev, "Failed to get LED brightness\n");
105		ret = LED_OFF;
106		goto unlock;
107	}
108
109	ret = ((val & RT4505_ITORCH_MASK) >> RT4505_ITORCH_SHIFT) + 1;
110
111unlock:
112	mutex_unlock(&priv->lock);
113	return ret;
114}
115
116static int rt4505_flash_brightness_set(struct led_classdev_flash *fled_cdev,
117				       u32 brightness)
118{
119	struct rt4505_priv *priv =
120		container_of(fled_cdev, struct rt4505_priv, flash);
121	struct led_flash_setting *s = &fled_cdev->brightness;
122	u32 val = (brightness - s->min) / s->step;
123	int ret;
124
125	mutex_lock(&priv->lock);
126	ret = regmap_update_bits(priv->regmap, RT4505_REG_ILED,
127				 RT4505_IFLASH_MASK, val);
128	mutex_unlock(&priv->lock);
129
130	return ret;
131}
132
133static int rt4505_flash_strobe_set(struct led_classdev_flash *fled_cdev,
134				   bool state)
135{
136	struct rt4505_priv *priv =
137		container_of(fled_cdev, struct rt4505_priv, flash);
138	u32 val = state ? RT4505_FLASH_SET : 0;
139	int ret;
140
141	mutex_lock(&priv->lock);
142	ret = regmap_update_bits(priv->regmap, RT4505_REG_ENABLE,
143				 RT4505_ENABLE_MASK, val);
144	mutex_unlock(&priv->lock);
145
146	return ret;
147}
148
149static int rt4505_flash_strobe_get(struct led_classdev_flash *fled_cdev,
150				   bool *state)
151{
152	struct rt4505_priv *priv =
153		container_of(fled_cdev, struct rt4505_priv, flash);
154	u32 val;
155	int ret;
156
157	mutex_lock(&priv->lock);
158
159	ret = regmap_read(priv->regmap, RT4505_REG_ENABLE, &val);
160	if (ret)
161		goto unlock;
162
163	*state = (val & RT4505_FLASH_GET) == RT4505_FLASH_GET;
164
165unlock:
166	mutex_unlock(&priv->lock);
167	return ret;
168}
169
170static int rt4505_flash_timeout_set(struct led_classdev_flash *fled_cdev,
171				    u32 timeout)
172{
173	struct rt4505_priv *priv =
174		container_of(fled_cdev, struct rt4505_priv, flash);
175	struct led_flash_setting *s = &fled_cdev->timeout;
176	u32 val = (timeout - s->min) / s->step;
177	int ret;
178
179	mutex_lock(&priv->lock);
180	ret = regmap_update_bits(priv->regmap, RT4505_REG_CONFIG,
181				 RT4505_FLASHTO_MASK, val);
182	mutex_unlock(&priv->lock);
183
184	return ret;
185}
186
187static int rt4505_fault_get(struct led_classdev_flash *fled_cdev, u32 *fault)
188{
189	struct rt4505_priv *priv =
190		container_of(fled_cdev, struct rt4505_priv, flash);
191	u32 val, led_faults = 0;
192	int ret;
193
194	ret = regmap_read(priv->regmap, RT4505_REG_FLAGS, &val);
195	if (ret)
196		return ret;
197
198	if (val & RT4505_OVP_MASK)
199		led_faults |= LED_FAULT_OVER_VOLTAGE;
200
201	if (val & RT4505_SHORT_MASK)
202		led_faults |= LED_FAULT_SHORT_CIRCUIT;
203
204	if (val & RT4505_OTP_MASK)
205		led_faults |= LED_FAULT_OVER_TEMPERATURE;
206
207	if (val & RT4505_TIMEOUT_MASK)
208		led_faults |= LED_FAULT_TIMEOUT;
209
210	*fault = led_faults;
211	return 0;
212}
213
214static const struct led_flash_ops rt4505_flash_ops = {
215	.flash_brightness_set = rt4505_flash_brightness_set,
216	.strobe_set = rt4505_flash_strobe_set,
217	.strobe_get = rt4505_flash_strobe_get,
218	.timeout_set = rt4505_flash_timeout_set,
219	.fault_get = rt4505_fault_get,
220};
221
222static bool rt4505_is_accessible_reg(struct device *dev, unsigned int reg)
223{
224	if (reg == RT4505_REG_RESET ||
225		(reg >= RT4505_REG_CONFIG && reg <= RT4505_REG_FLAGS))
226		return true;
227	return false;
228}
229
230static const struct regmap_config rt4505_regmap_config = {
231	.reg_bits = 8,
232	.val_bits = 8,
233	.max_register = RT4505_REG_FLAGS,
234
235	.readable_reg = rt4505_is_accessible_reg,
236	.writeable_reg = rt4505_is_accessible_reg,
237};
238
239#if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS)
240static int rt4505_flash_external_strobe_set(struct v4l2_flash *v4l2_flash,
241					    bool enable)
242{
243	struct led_classdev_flash *flash = v4l2_flash->fled_cdev;
244	struct rt4505_priv *priv =
245		container_of(flash, struct rt4505_priv, flash);
246	u32 val = enable ? RT4505_EXT_FLASH_SET : 0;
247	int ret;
248
249	mutex_lock(&priv->lock);
250	ret = regmap_update_bits(priv->regmap, RT4505_REG_ENABLE,
251				 RT4505_ENABLE_MASK, val);
252	mutex_unlock(&priv->lock);
253
254	return ret;
255}
256
257static const struct v4l2_flash_ops v4l2_flash_ops = {
258	.external_strobe_set = rt4505_flash_external_strobe_set,
259};
260
261static void rt4505_init_v4l2_config(struct rt4505_priv *priv,
262				    struct v4l2_flash_config *config)
263{
264	struct led_classdev_flash *flash = &priv->flash;
265	struct led_classdev *lcdev = &flash->led_cdev;
266	struct led_flash_setting *s;
267
268	strscpy(config->dev_name, lcdev->dev->kobj.name,
269		sizeof(config->dev_name));
270
271	s = &config->intensity;
272	s->min = RT4505_ITORCH_MINUA;
273	s->step = RT4505_ITORCH_STPUA;
274	s->max = s->val = s->min + (lcdev->max_brightness - 1) * s->step;
275
276	config->flash_faults = LED_FAULT_OVER_VOLTAGE |
277			       LED_FAULT_SHORT_CIRCUIT |
278			       LED_FAULT_LED_OVER_TEMPERATURE |
279			       LED_FAULT_TIMEOUT;
280	config->has_external_strobe = 1;
281}
282#else
283static const struct v4l2_flash_ops v4l2_flash_ops;
284static void rt4505_init_v4l2_config(struct rt4505_priv *priv,
285				    struct v4l2_flash_config *config)
286{
287}
288#endif
289
290static void rt4505_init_flash_properties(struct rt4505_priv *priv,
291					 struct fwnode_handle *child)
292{
293	struct led_classdev_flash *flash = &priv->flash;
294	struct led_classdev *lcdev = &flash->led_cdev;
295	struct led_flash_setting *s;
296	u32 val;
297	int ret;
298
299	ret = fwnode_property_read_u32(child, "led-max-microamp", &val);
300	if (ret) {
301		dev_warn(priv->dev, "led-max-microamp DT property missing\n");
302		val = RT4505_ITORCH_MINUA;
303	} else
304		val = clamp_val(val, RT4505_ITORCH_MINUA, RT4505_ITORCH_MAXUA);
305
306	lcdev->max_brightness =
307		(val - RT4505_ITORCH_MINUA) / RT4505_ITORCH_STPUA + 1;
308	lcdev->brightness_set_blocking = rt4505_torch_brightness_set;
309	lcdev->brightness_get = rt4505_torch_brightness_get;
310	lcdev->flags |= LED_DEV_CAP_FLASH;
311
312	ret = fwnode_property_read_u32(child, "flash-max-microamp", &val);
313	if (ret) {
314		dev_warn(priv->dev, "flash-max-microamp DT property missing\n");
315		val = RT4505_IFLASH_MINUA;
316	} else
317		val = clamp_val(val, RT4505_IFLASH_MINUA, RT4505_IFLASH_MAXUA);
318
319	s = &flash->brightness;
320	s->min = RT4505_IFLASH_MINUA;
321	s->step = RT4505_IFLASH_STPUA;
322	s->max = s->val = val;
323
324	ret = fwnode_property_read_u32(child, "flash-max-timeout-us", &val);
325	if (ret) {
326		dev_warn(priv->dev,
327			 "flash-max-timeout-us DT property missing\n");
328		val = RT4505_FLASHTO_MINUS;
329	} else
330		val = clamp_val(val, RT4505_FLASHTO_MINUS,
331				RT4505_FLASHTO_MAXUS);
332
333	s = &flash->timeout;
334	s->min = RT4505_FLASHTO_MINUS;
335	s->step = RT4505_FLASHTO_STPUS;
336	s->max = s->val = val;
337
338	flash->ops = &rt4505_flash_ops;
339}
340
341static int rt4505_probe(struct i2c_client *client)
342{
343	struct rt4505_priv *priv;
344	struct fwnode_handle *child;
345	struct led_init_data init_data = {};
346	struct v4l2_flash_config v4l2_config = {};
347	int ret;
348
349	priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
350	if (!priv)
351		return -ENOMEM;
352
353	priv->dev = &client->dev;
354	mutex_init(&priv->lock);
355
356	priv->regmap = devm_regmap_init_i2c(client, &rt4505_regmap_config);
357	if (IS_ERR(priv->regmap)) {
358		dev_err(priv->dev, "Failed to allocate register map\n");
359		return PTR_ERR(priv->regmap);
360	}
361
362	ret = regmap_write(priv->regmap, RT4505_REG_RESET, RT4505_RESET_MASK);
363	if (ret) {
364		dev_err(priv->dev, "Failed to reset registers\n");
365		return ret;
366	}
367
368	child = fwnode_get_next_available_child_node(client->dev.fwnode, NULL);
369	if (!child) {
370		dev_err(priv->dev, "Failed to get child node\n");
371		return -EINVAL;
372	}
373	init_data.fwnode = child;
374
375	rt4505_init_flash_properties(priv, child);
376	ret = devm_led_classdev_flash_register_ext(priv->dev, &priv->flash,
377						   &init_data);
378	if (ret) {
379		dev_err(priv->dev, "Failed to register flash\n");
380		return ret;
381	}
382
383	rt4505_init_v4l2_config(priv, &v4l2_config);
384	priv->v4l2_flash = v4l2_flash_init(priv->dev, init_data.fwnode,
385					   &priv->flash, &v4l2_flash_ops,
386					   &v4l2_config);
387	if (IS_ERR(priv->v4l2_flash)) {
388		dev_err(priv->dev, "Failed to register v4l2 flash\n");
389		return PTR_ERR(priv->v4l2_flash);
390	}
391
392	i2c_set_clientdata(client, priv);
393	return 0;
394}
395
396static void rt4505_remove(struct i2c_client *client)
397{
398	struct rt4505_priv *priv = i2c_get_clientdata(client);
399
400	v4l2_flash_release(priv->v4l2_flash);
401}
402
403static void rt4505_shutdown(struct i2c_client *client)
404{
405	struct rt4505_priv *priv = i2c_get_clientdata(client);
406
407	/* Reset registers to make sure all off before shutdown */
408	regmap_write(priv->regmap, RT4505_REG_RESET, RT4505_RESET_MASK);
409}
410
411static const struct of_device_id __maybe_unused rt4505_leds_match[] = {
412	{ .compatible = "richtek,rt4505", },
413	{}
414};
415MODULE_DEVICE_TABLE(of, rt4505_leds_match);
416
417static struct i2c_driver rt4505_driver = {
418	.driver = {
419		.name = "rt4505",
420		.of_match_table = of_match_ptr(rt4505_leds_match),
421	},
422	.probe = rt4505_probe,
423	.remove = rt4505_remove,
424	.shutdown = rt4505_shutdown,
425};
426module_i2c_driver(rt4505_driver);
427
428MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>");
429MODULE_LICENSE("GPL v2");
430