xref: /linux/drivers/leds/flash/leds-sgm3140.c (revision 9a7c066f6a1dfff2f30e697fe494a669428fc9f4)
1*9a7c066fSLinus Walleij // SPDX-License-Identifier: GPL-2.0
2*9a7c066fSLinus Walleij // Copyright (C) 2020 Luca Weiss <luca@z3ntu.xyz>
3*9a7c066fSLinus Walleij 
4*9a7c066fSLinus Walleij #include <linux/gpio/consumer.h>
5*9a7c066fSLinus Walleij #include <linux/led-class-flash.h>
6*9a7c066fSLinus Walleij #include <linux/module.h>
7*9a7c066fSLinus Walleij #include <linux/regulator/consumer.h>
8*9a7c066fSLinus Walleij #include <linux/platform_device.h>
9*9a7c066fSLinus Walleij 
10*9a7c066fSLinus Walleij #include <media/v4l2-flash-led-class.h>
11*9a7c066fSLinus Walleij 
12*9a7c066fSLinus Walleij #define FLASH_TIMEOUT_DEFAULT		250000U /* 250ms */
13*9a7c066fSLinus Walleij #define FLASH_MAX_TIMEOUT_DEFAULT	300000U /* 300ms */
14*9a7c066fSLinus Walleij 
15*9a7c066fSLinus Walleij struct sgm3140 {
16*9a7c066fSLinus Walleij 	struct led_classdev_flash fled_cdev;
17*9a7c066fSLinus Walleij 	struct v4l2_flash *v4l2_flash;
18*9a7c066fSLinus Walleij 
19*9a7c066fSLinus Walleij 	struct timer_list powerdown_timer;
20*9a7c066fSLinus Walleij 
21*9a7c066fSLinus Walleij 	struct gpio_desc *flash_gpio;
22*9a7c066fSLinus Walleij 	struct gpio_desc *enable_gpio;
23*9a7c066fSLinus Walleij 	struct regulator *vin_regulator;
24*9a7c066fSLinus Walleij 
25*9a7c066fSLinus Walleij 	bool enabled;
26*9a7c066fSLinus Walleij 
27*9a7c066fSLinus Walleij 	/* current timeout in us */
28*9a7c066fSLinus Walleij 	u32 timeout;
29*9a7c066fSLinus Walleij 	/* maximum timeout in us */
30*9a7c066fSLinus Walleij 	u32 max_timeout;
31*9a7c066fSLinus Walleij };
32*9a7c066fSLinus Walleij 
33*9a7c066fSLinus Walleij static struct sgm3140 *flcdev_to_sgm3140(struct led_classdev_flash *flcdev)
34*9a7c066fSLinus Walleij {
35*9a7c066fSLinus Walleij 	return container_of(flcdev, struct sgm3140, fled_cdev);
36*9a7c066fSLinus Walleij }
37*9a7c066fSLinus Walleij 
38*9a7c066fSLinus Walleij static int sgm3140_strobe_set(struct led_classdev_flash *fled_cdev, bool state)
39*9a7c066fSLinus Walleij {
40*9a7c066fSLinus Walleij 	struct sgm3140 *priv = flcdev_to_sgm3140(fled_cdev);
41*9a7c066fSLinus Walleij 	int ret;
42*9a7c066fSLinus Walleij 
43*9a7c066fSLinus Walleij 	if (priv->enabled == state)
44*9a7c066fSLinus Walleij 		return 0;
45*9a7c066fSLinus Walleij 
46*9a7c066fSLinus Walleij 	if (state) {
47*9a7c066fSLinus Walleij 		ret = regulator_enable(priv->vin_regulator);
48*9a7c066fSLinus Walleij 		if (ret) {
49*9a7c066fSLinus Walleij 			dev_err(fled_cdev->led_cdev.dev,
50*9a7c066fSLinus Walleij 				"failed to enable regulator: %d\n", ret);
51*9a7c066fSLinus Walleij 			return ret;
52*9a7c066fSLinus Walleij 		}
53*9a7c066fSLinus Walleij 		gpiod_set_value_cansleep(priv->flash_gpio, 1);
54*9a7c066fSLinus Walleij 		gpiod_set_value_cansleep(priv->enable_gpio, 1);
55*9a7c066fSLinus Walleij 		mod_timer(&priv->powerdown_timer,
56*9a7c066fSLinus Walleij 			  jiffies + usecs_to_jiffies(priv->timeout));
57*9a7c066fSLinus Walleij 	} else {
58*9a7c066fSLinus Walleij 		del_timer_sync(&priv->powerdown_timer);
59*9a7c066fSLinus Walleij 		gpiod_set_value_cansleep(priv->enable_gpio, 0);
60*9a7c066fSLinus Walleij 		gpiod_set_value_cansleep(priv->flash_gpio, 0);
61*9a7c066fSLinus Walleij 		ret = regulator_disable(priv->vin_regulator);
62*9a7c066fSLinus Walleij 		if (ret) {
63*9a7c066fSLinus Walleij 			dev_err(fled_cdev->led_cdev.dev,
64*9a7c066fSLinus Walleij 				"failed to disable regulator: %d\n", ret);
65*9a7c066fSLinus Walleij 			return ret;
66*9a7c066fSLinus Walleij 		}
67*9a7c066fSLinus Walleij 	}
68*9a7c066fSLinus Walleij 
69*9a7c066fSLinus Walleij 	priv->enabled = state;
70*9a7c066fSLinus Walleij 
71*9a7c066fSLinus Walleij 	return 0;
72*9a7c066fSLinus Walleij }
73*9a7c066fSLinus Walleij 
74*9a7c066fSLinus Walleij static int sgm3140_strobe_get(struct led_classdev_flash *fled_cdev, bool *state)
75*9a7c066fSLinus Walleij {
76*9a7c066fSLinus Walleij 	struct sgm3140 *priv = flcdev_to_sgm3140(fled_cdev);
77*9a7c066fSLinus Walleij 
78*9a7c066fSLinus Walleij 	*state = timer_pending(&priv->powerdown_timer);
79*9a7c066fSLinus Walleij 
80*9a7c066fSLinus Walleij 	return 0;
81*9a7c066fSLinus Walleij }
82*9a7c066fSLinus Walleij 
83*9a7c066fSLinus Walleij static int sgm3140_timeout_set(struct led_classdev_flash *fled_cdev,
84*9a7c066fSLinus Walleij 			       u32 timeout)
85*9a7c066fSLinus Walleij {
86*9a7c066fSLinus Walleij 	struct sgm3140 *priv = flcdev_to_sgm3140(fled_cdev);
87*9a7c066fSLinus Walleij 
88*9a7c066fSLinus Walleij 	priv->timeout = timeout;
89*9a7c066fSLinus Walleij 
90*9a7c066fSLinus Walleij 	return 0;
91*9a7c066fSLinus Walleij }
92*9a7c066fSLinus Walleij 
93*9a7c066fSLinus Walleij static const struct led_flash_ops sgm3140_flash_ops = {
94*9a7c066fSLinus Walleij 	.strobe_set = sgm3140_strobe_set,
95*9a7c066fSLinus Walleij 	.strobe_get = sgm3140_strobe_get,
96*9a7c066fSLinus Walleij 	.timeout_set = sgm3140_timeout_set,
97*9a7c066fSLinus Walleij };
98*9a7c066fSLinus Walleij 
99*9a7c066fSLinus Walleij static int sgm3140_brightness_set(struct led_classdev *led_cdev,
100*9a7c066fSLinus Walleij 				  enum led_brightness brightness)
101*9a7c066fSLinus Walleij {
102*9a7c066fSLinus Walleij 	struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
103*9a7c066fSLinus Walleij 	struct sgm3140 *priv = flcdev_to_sgm3140(fled_cdev);
104*9a7c066fSLinus Walleij 	bool enable = brightness == LED_ON;
105*9a7c066fSLinus Walleij 	int ret;
106*9a7c066fSLinus Walleij 
107*9a7c066fSLinus Walleij 	if (priv->enabled == enable)
108*9a7c066fSLinus Walleij 		return 0;
109*9a7c066fSLinus Walleij 
110*9a7c066fSLinus Walleij 	if (enable) {
111*9a7c066fSLinus Walleij 		ret = regulator_enable(priv->vin_regulator);
112*9a7c066fSLinus Walleij 		if (ret) {
113*9a7c066fSLinus Walleij 			dev_err(led_cdev->dev,
114*9a7c066fSLinus Walleij 				"failed to enable regulator: %d\n", ret);
115*9a7c066fSLinus Walleij 			return ret;
116*9a7c066fSLinus Walleij 		}
117*9a7c066fSLinus Walleij 		gpiod_set_value_cansleep(priv->enable_gpio, 1);
118*9a7c066fSLinus Walleij 	} else {
119*9a7c066fSLinus Walleij 		gpiod_set_value_cansleep(priv->enable_gpio, 0);
120*9a7c066fSLinus Walleij 		ret = regulator_disable(priv->vin_regulator);
121*9a7c066fSLinus Walleij 		if (ret) {
122*9a7c066fSLinus Walleij 			dev_err(led_cdev->dev,
123*9a7c066fSLinus Walleij 				"failed to disable regulator: %d\n", ret);
124*9a7c066fSLinus Walleij 			return ret;
125*9a7c066fSLinus Walleij 		}
126*9a7c066fSLinus Walleij 	}
127*9a7c066fSLinus Walleij 
128*9a7c066fSLinus Walleij 	priv->enabled = enable;
129*9a7c066fSLinus Walleij 
130*9a7c066fSLinus Walleij 	return 0;
131*9a7c066fSLinus Walleij }
132*9a7c066fSLinus Walleij 
133*9a7c066fSLinus Walleij static void sgm3140_powerdown_timer(struct timer_list *t)
134*9a7c066fSLinus Walleij {
135*9a7c066fSLinus Walleij 	struct sgm3140 *priv = from_timer(priv, t, powerdown_timer);
136*9a7c066fSLinus Walleij 
137*9a7c066fSLinus Walleij 	gpiod_set_value(priv->enable_gpio, 0);
138*9a7c066fSLinus Walleij 	gpiod_set_value(priv->flash_gpio, 0);
139*9a7c066fSLinus Walleij 	regulator_disable(priv->vin_regulator);
140*9a7c066fSLinus Walleij 
141*9a7c066fSLinus Walleij 	priv->enabled = false;
142*9a7c066fSLinus Walleij }
143*9a7c066fSLinus Walleij 
144*9a7c066fSLinus Walleij static void sgm3140_init_flash_timeout(struct sgm3140 *priv)
145*9a7c066fSLinus Walleij {
146*9a7c066fSLinus Walleij 	struct led_classdev_flash *fled_cdev = &priv->fled_cdev;
147*9a7c066fSLinus Walleij 	struct led_flash_setting *s;
148*9a7c066fSLinus Walleij 
149*9a7c066fSLinus Walleij 	/* Init flash timeout setting */
150*9a7c066fSLinus Walleij 	s = &fled_cdev->timeout;
151*9a7c066fSLinus Walleij 	s->min = 1;
152*9a7c066fSLinus Walleij 	s->max = priv->max_timeout;
153*9a7c066fSLinus Walleij 	s->step = 1;
154*9a7c066fSLinus Walleij 	s->val = FLASH_TIMEOUT_DEFAULT;
155*9a7c066fSLinus Walleij }
156*9a7c066fSLinus Walleij 
157*9a7c066fSLinus Walleij #if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS)
158*9a7c066fSLinus Walleij static void sgm3140_init_v4l2_flash_config(struct sgm3140 *priv,
159*9a7c066fSLinus Walleij 					struct v4l2_flash_config *v4l2_sd_cfg)
160*9a7c066fSLinus Walleij {
161*9a7c066fSLinus Walleij 	struct led_classdev *led_cdev = &priv->fled_cdev.led_cdev;
162*9a7c066fSLinus Walleij 	struct led_flash_setting *s;
163*9a7c066fSLinus Walleij 
164*9a7c066fSLinus Walleij 	strscpy(v4l2_sd_cfg->dev_name, led_cdev->dev->kobj.name,
165*9a7c066fSLinus Walleij 		sizeof(v4l2_sd_cfg->dev_name));
166*9a7c066fSLinus Walleij 
167*9a7c066fSLinus Walleij 	/* Init flash intensity setting */
168*9a7c066fSLinus Walleij 	s = &v4l2_sd_cfg->intensity;
169*9a7c066fSLinus Walleij 	s->min = 0;
170*9a7c066fSLinus Walleij 	s->max = 1;
171*9a7c066fSLinus Walleij 	s->step = 1;
172*9a7c066fSLinus Walleij 	s->val = 1;
173*9a7c066fSLinus Walleij }
174*9a7c066fSLinus Walleij 
175*9a7c066fSLinus Walleij #else
176*9a7c066fSLinus Walleij static void sgm3140_init_v4l2_flash_config(struct sgm3140 *priv,
177*9a7c066fSLinus Walleij 					struct v4l2_flash_config *v4l2_sd_cfg)
178*9a7c066fSLinus Walleij {
179*9a7c066fSLinus Walleij }
180*9a7c066fSLinus Walleij #endif
181*9a7c066fSLinus Walleij 
182*9a7c066fSLinus Walleij static int sgm3140_probe(struct platform_device *pdev)
183*9a7c066fSLinus Walleij {
184*9a7c066fSLinus Walleij 	struct sgm3140 *priv;
185*9a7c066fSLinus Walleij 	struct led_classdev *led_cdev;
186*9a7c066fSLinus Walleij 	struct led_classdev_flash *fled_cdev;
187*9a7c066fSLinus Walleij 	struct led_init_data init_data = {};
188*9a7c066fSLinus Walleij 	struct fwnode_handle *child_node;
189*9a7c066fSLinus Walleij 	struct v4l2_flash_config v4l2_sd_cfg = {};
190*9a7c066fSLinus Walleij 	int ret;
191*9a7c066fSLinus Walleij 
192*9a7c066fSLinus Walleij 	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
193*9a7c066fSLinus Walleij 	if (!priv)
194*9a7c066fSLinus Walleij 		return -ENOMEM;
195*9a7c066fSLinus Walleij 
196*9a7c066fSLinus Walleij 	priv->flash_gpio = devm_gpiod_get(&pdev->dev, "flash", GPIOD_OUT_LOW);
197*9a7c066fSLinus Walleij 	ret = PTR_ERR_OR_ZERO(priv->flash_gpio);
198*9a7c066fSLinus Walleij 	if (ret)
199*9a7c066fSLinus Walleij 		return dev_err_probe(&pdev->dev, ret,
200*9a7c066fSLinus Walleij 				     "Failed to request flash gpio\n");
201*9a7c066fSLinus Walleij 
202*9a7c066fSLinus Walleij 	priv->enable_gpio = devm_gpiod_get(&pdev->dev, "enable", GPIOD_OUT_LOW);
203*9a7c066fSLinus Walleij 	ret = PTR_ERR_OR_ZERO(priv->enable_gpio);
204*9a7c066fSLinus Walleij 	if (ret)
205*9a7c066fSLinus Walleij 		return dev_err_probe(&pdev->dev, ret,
206*9a7c066fSLinus Walleij 				     "Failed to request enable gpio\n");
207*9a7c066fSLinus Walleij 
208*9a7c066fSLinus Walleij 	priv->vin_regulator = devm_regulator_get(&pdev->dev, "vin");
209*9a7c066fSLinus Walleij 	ret = PTR_ERR_OR_ZERO(priv->vin_regulator);
210*9a7c066fSLinus Walleij 	if (ret)
211*9a7c066fSLinus Walleij 		return dev_err_probe(&pdev->dev, ret,
212*9a7c066fSLinus Walleij 				     "Failed to request regulator\n");
213*9a7c066fSLinus Walleij 
214*9a7c066fSLinus Walleij 	child_node = fwnode_get_next_available_child_node(pdev->dev.fwnode,
215*9a7c066fSLinus Walleij 							  NULL);
216*9a7c066fSLinus Walleij 	if (!child_node) {
217*9a7c066fSLinus Walleij 		dev_err(&pdev->dev,
218*9a7c066fSLinus Walleij 			"No fwnode child node found for connected LED.\n");
219*9a7c066fSLinus Walleij 		return -EINVAL;
220*9a7c066fSLinus Walleij 	}
221*9a7c066fSLinus Walleij 
222*9a7c066fSLinus Walleij 	ret = fwnode_property_read_u32(child_node, "flash-max-timeout-us",
223*9a7c066fSLinus Walleij 				       &priv->max_timeout);
224*9a7c066fSLinus Walleij 	if (ret) {
225*9a7c066fSLinus Walleij 		priv->max_timeout = FLASH_MAX_TIMEOUT_DEFAULT;
226*9a7c066fSLinus Walleij 		dev_warn(&pdev->dev,
227*9a7c066fSLinus Walleij 			 "flash-max-timeout-us property missing\n");
228*9a7c066fSLinus Walleij 	}
229*9a7c066fSLinus Walleij 
230*9a7c066fSLinus Walleij 	/*
231*9a7c066fSLinus Walleij 	 * Set default timeout to FLASH_DEFAULT_TIMEOUT except if max_timeout
232*9a7c066fSLinus Walleij 	 * from DT is lower.
233*9a7c066fSLinus Walleij 	 */
234*9a7c066fSLinus Walleij 	priv->timeout = min(priv->max_timeout, FLASH_TIMEOUT_DEFAULT);
235*9a7c066fSLinus Walleij 
236*9a7c066fSLinus Walleij 	timer_setup(&priv->powerdown_timer, sgm3140_powerdown_timer, 0);
237*9a7c066fSLinus Walleij 
238*9a7c066fSLinus Walleij 	fled_cdev = &priv->fled_cdev;
239*9a7c066fSLinus Walleij 	led_cdev = &fled_cdev->led_cdev;
240*9a7c066fSLinus Walleij 
241*9a7c066fSLinus Walleij 	fled_cdev->ops = &sgm3140_flash_ops;
242*9a7c066fSLinus Walleij 
243*9a7c066fSLinus Walleij 	led_cdev->brightness_set_blocking = sgm3140_brightness_set;
244*9a7c066fSLinus Walleij 	led_cdev->max_brightness = LED_ON;
245*9a7c066fSLinus Walleij 	led_cdev->flags |= LED_DEV_CAP_FLASH;
246*9a7c066fSLinus Walleij 
247*9a7c066fSLinus Walleij 	sgm3140_init_flash_timeout(priv);
248*9a7c066fSLinus Walleij 
249*9a7c066fSLinus Walleij 	init_data.fwnode = child_node;
250*9a7c066fSLinus Walleij 
251*9a7c066fSLinus Walleij 	platform_set_drvdata(pdev, priv);
252*9a7c066fSLinus Walleij 
253*9a7c066fSLinus Walleij 	/* Register in the LED subsystem */
254*9a7c066fSLinus Walleij 	ret = devm_led_classdev_flash_register_ext(&pdev->dev,
255*9a7c066fSLinus Walleij 						   fled_cdev, &init_data);
256*9a7c066fSLinus Walleij 	if (ret) {
257*9a7c066fSLinus Walleij 		dev_err(&pdev->dev, "Failed to register flash device: %d\n",
258*9a7c066fSLinus Walleij 			ret);
259*9a7c066fSLinus Walleij 		goto err;
260*9a7c066fSLinus Walleij 	}
261*9a7c066fSLinus Walleij 
262*9a7c066fSLinus Walleij 	sgm3140_init_v4l2_flash_config(priv, &v4l2_sd_cfg);
263*9a7c066fSLinus Walleij 
264*9a7c066fSLinus Walleij 	/* Create V4L2 Flash subdev */
265*9a7c066fSLinus Walleij 	priv->v4l2_flash = v4l2_flash_init(&pdev->dev,
266*9a7c066fSLinus Walleij 					   child_node,
267*9a7c066fSLinus Walleij 					   fled_cdev, NULL,
268*9a7c066fSLinus Walleij 					   &v4l2_sd_cfg);
269*9a7c066fSLinus Walleij 	if (IS_ERR(priv->v4l2_flash)) {
270*9a7c066fSLinus Walleij 		ret = PTR_ERR(priv->v4l2_flash);
271*9a7c066fSLinus Walleij 		goto err;
272*9a7c066fSLinus Walleij 	}
273*9a7c066fSLinus Walleij 
274*9a7c066fSLinus Walleij 	return ret;
275*9a7c066fSLinus Walleij 
276*9a7c066fSLinus Walleij err:
277*9a7c066fSLinus Walleij 	fwnode_handle_put(child_node);
278*9a7c066fSLinus Walleij 	return ret;
279*9a7c066fSLinus Walleij }
280*9a7c066fSLinus Walleij 
281*9a7c066fSLinus Walleij static int sgm3140_remove(struct platform_device *pdev)
282*9a7c066fSLinus Walleij {
283*9a7c066fSLinus Walleij 	struct sgm3140 *priv = platform_get_drvdata(pdev);
284*9a7c066fSLinus Walleij 
285*9a7c066fSLinus Walleij 	del_timer_sync(&priv->powerdown_timer);
286*9a7c066fSLinus Walleij 
287*9a7c066fSLinus Walleij 	v4l2_flash_release(priv->v4l2_flash);
288*9a7c066fSLinus Walleij 
289*9a7c066fSLinus Walleij 	return 0;
290*9a7c066fSLinus Walleij }
291*9a7c066fSLinus Walleij 
292*9a7c066fSLinus Walleij static const struct of_device_id sgm3140_dt_match[] = {
293*9a7c066fSLinus Walleij 	{ .compatible = "sgmicro,sgm3140" },
294*9a7c066fSLinus Walleij 	{ /* sentinel */ }
295*9a7c066fSLinus Walleij };
296*9a7c066fSLinus Walleij MODULE_DEVICE_TABLE(of, sgm3140_dt_match);
297*9a7c066fSLinus Walleij 
298*9a7c066fSLinus Walleij static struct platform_driver sgm3140_driver = {
299*9a7c066fSLinus Walleij 	.probe	= sgm3140_probe,
300*9a7c066fSLinus Walleij 	.remove	= sgm3140_remove,
301*9a7c066fSLinus Walleij 	.driver	= {
302*9a7c066fSLinus Walleij 		.name	= "sgm3140",
303*9a7c066fSLinus Walleij 		.of_match_table = sgm3140_dt_match,
304*9a7c066fSLinus Walleij 	},
305*9a7c066fSLinus Walleij };
306*9a7c066fSLinus Walleij 
307*9a7c066fSLinus Walleij module_platform_driver(sgm3140_driver);
308*9a7c066fSLinus Walleij 
309*9a7c066fSLinus Walleij MODULE_AUTHOR("Luca Weiss <luca@z3ntu.xyz>");
310*9a7c066fSLinus Walleij MODULE_DESCRIPTION("SG Micro SGM3140 charge pump LED driver");
311*9a7c066fSLinus Walleij MODULE_LICENSE("GPL v2");
312