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