xref: /linux/drivers/leds/flash/leds-sgm3140.c (revision 79790b6818e96c58fe2bffee1b418c16e64e7b80)
19a7c066fSLinus Walleij // SPDX-License-Identifier: GPL-2.0
29a7c066fSLinus Walleij // Copyright (C) 2020 Luca Weiss <luca@z3ntu.xyz>
39a7c066fSLinus Walleij 
49a7c066fSLinus Walleij #include <linux/gpio/consumer.h>
59a7c066fSLinus Walleij #include <linux/led-class-flash.h>
69a7c066fSLinus Walleij #include <linux/module.h>
79a7c066fSLinus Walleij #include <linux/regulator/consumer.h>
89a7c066fSLinus Walleij #include <linux/platform_device.h>
99a7c066fSLinus Walleij 
109a7c066fSLinus Walleij #include <media/v4l2-flash-led-class.h>
119a7c066fSLinus Walleij 
129a7c066fSLinus Walleij #define FLASH_TIMEOUT_DEFAULT		250000U /* 250ms */
139a7c066fSLinus Walleij #define FLASH_MAX_TIMEOUT_DEFAULT	300000U /* 300ms */
149a7c066fSLinus Walleij 
159a7c066fSLinus Walleij struct sgm3140 {
169a7c066fSLinus Walleij 	struct led_classdev_flash fled_cdev;
179a7c066fSLinus Walleij 	struct v4l2_flash *v4l2_flash;
189a7c066fSLinus Walleij 
199a7c066fSLinus Walleij 	struct timer_list powerdown_timer;
209a7c066fSLinus Walleij 
219a7c066fSLinus Walleij 	struct gpio_desc *flash_gpio;
229a7c066fSLinus Walleij 	struct gpio_desc *enable_gpio;
239a7c066fSLinus Walleij 	struct regulator *vin_regulator;
249a7c066fSLinus Walleij 
259a7c066fSLinus Walleij 	bool enabled;
269a7c066fSLinus Walleij 
279a7c066fSLinus Walleij 	/* current timeout in us */
289a7c066fSLinus Walleij 	u32 timeout;
299a7c066fSLinus Walleij 	/* maximum timeout in us */
309a7c066fSLinus Walleij 	u32 max_timeout;
319a7c066fSLinus Walleij };
329a7c066fSLinus Walleij 
flcdev_to_sgm3140(struct led_classdev_flash * flcdev)339a7c066fSLinus Walleij static struct sgm3140 *flcdev_to_sgm3140(struct led_classdev_flash *flcdev)
349a7c066fSLinus Walleij {
359a7c066fSLinus Walleij 	return container_of(flcdev, struct sgm3140, fled_cdev);
369a7c066fSLinus Walleij }
379a7c066fSLinus Walleij 
sgm3140_strobe_set(struct led_classdev_flash * fled_cdev,bool state)389a7c066fSLinus Walleij static int sgm3140_strobe_set(struct led_classdev_flash *fled_cdev, bool state)
399a7c066fSLinus Walleij {
409a7c066fSLinus Walleij 	struct sgm3140 *priv = flcdev_to_sgm3140(fled_cdev);
419a7c066fSLinus Walleij 	int ret;
429a7c066fSLinus Walleij 
439a7c066fSLinus Walleij 	if (priv->enabled == state)
449a7c066fSLinus Walleij 		return 0;
459a7c066fSLinus Walleij 
469a7c066fSLinus Walleij 	if (state) {
479a7c066fSLinus Walleij 		ret = regulator_enable(priv->vin_regulator);
489a7c066fSLinus Walleij 		if (ret) {
499a7c066fSLinus Walleij 			dev_err(fled_cdev->led_cdev.dev,
509a7c066fSLinus Walleij 				"failed to enable regulator: %d\n", ret);
519a7c066fSLinus Walleij 			return ret;
529a7c066fSLinus Walleij 		}
539a7c066fSLinus Walleij 		gpiod_set_value_cansleep(priv->flash_gpio, 1);
549a7c066fSLinus Walleij 		gpiod_set_value_cansleep(priv->enable_gpio, 1);
559a7c066fSLinus Walleij 		mod_timer(&priv->powerdown_timer,
569a7c066fSLinus Walleij 			  jiffies + usecs_to_jiffies(priv->timeout));
579a7c066fSLinus Walleij 	} else {
589a7c066fSLinus Walleij 		del_timer_sync(&priv->powerdown_timer);
599a7c066fSLinus Walleij 		gpiod_set_value_cansleep(priv->enable_gpio, 0);
609a7c066fSLinus Walleij 		gpiod_set_value_cansleep(priv->flash_gpio, 0);
619a7c066fSLinus Walleij 		ret = regulator_disable(priv->vin_regulator);
629a7c066fSLinus Walleij 		if (ret) {
639a7c066fSLinus Walleij 			dev_err(fled_cdev->led_cdev.dev,
649a7c066fSLinus Walleij 				"failed to disable regulator: %d\n", ret);
659a7c066fSLinus Walleij 			return ret;
669a7c066fSLinus Walleij 		}
679a7c066fSLinus Walleij 	}
689a7c066fSLinus Walleij 
699a7c066fSLinus Walleij 	priv->enabled = state;
709a7c066fSLinus Walleij 
719a7c066fSLinus Walleij 	return 0;
729a7c066fSLinus Walleij }
739a7c066fSLinus Walleij 
sgm3140_strobe_get(struct led_classdev_flash * fled_cdev,bool * state)749a7c066fSLinus Walleij static int sgm3140_strobe_get(struct led_classdev_flash *fled_cdev, bool *state)
759a7c066fSLinus Walleij {
769a7c066fSLinus Walleij 	struct sgm3140 *priv = flcdev_to_sgm3140(fled_cdev);
779a7c066fSLinus Walleij 
789a7c066fSLinus Walleij 	*state = timer_pending(&priv->powerdown_timer);
799a7c066fSLinus Walleij 
809a7c066fSLinus Walleij 	return 0;
819a7c066fSLinus Walleij }
829a7c066fSLinus Walleij 
sgm3140_timeout_set(struct led_classdev_flash * fled_cdev,u32 timeout)839a7c066fSLinus Walleij static int sgm3140_timeout_set(struct led_classdev_flash *fled_cdev,
849a7c066fSLinus Walleij 			       u32 timeout)
859a7c066fSLinus Walleij {
869a7c066fSLinus Walleij 	struct sgm3140 *priv = flcdev_to_sgm3140(fled_cdev);
879a7c066fSLinus Walleij 
889a7c066fSLinus Walleij 	priv->timeout = timeout;
899a7c066fSLinus Walleij 
909a7c066fSLinus Walleij 	return 0;
919a7c066fSLinus Walleij }
929a7c066fSLinus Walleij 
939a7c066fSLinus Walleij static const struct led_flash_ops sgm3140_flash_ops = {
949a7c066fSLinus Walleij 	.strobe_set = sgm3140_strobe_set,
959a7c066fSLinus Walleij 	.strobe_get = sgm3140_strobe_get,
969a7c066fSLinus Walleij 	.timeout_set = sgm3140_timeout_set,
979a7c066fSLinus Walleij };
989a7c066fSLinus Walleij 
sgm3140_brightness_set(struct led_classdev * led_cdev,enum led_brightness brightness)999a7c066fSLinus Walleij static int sgm3140_brightness_set(struct led_classdev *led_cdev,
1009a7c066fSLinus Walleij 				  enum led_brightness brightness)
1019a7c066fSLinus Walleij {
1029a7c066fSLinus Walleij 	struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
1039a7c066fSLinus Walleij 	struct sgm3140 *priv = flcdev_to_sgm3140(fled_cdev);
1049a7c066fSLinus Walleij 	bool enable = brightness == LED_ON;
1059a7c066fSLinus Walleij 	int ret;
1069a7c066fSLinus Walleij 
1079a7c066fSLinus Walleij 	if (priv->enabled == enable)
1089a7c066fSLinus Walleij 		return 0;
1099a7c066fSLinus Walleij 
1109a7c066fSLinus Walleij 	if (enable) {
1119a7c066fSLinus Walleij 		ret = regulator_enable(priv->vin_regulator);
1129a7c066fSLinus Walleij 		if (ret) {
1139a7c066fSLinus Walleij 			dev_err(led_cdev->dev,
1149a7c066fSLinus Walleij 				"failed to enable regulator: %d\n", ret);
1159a7c066fSLinus Walleij 			return ret;
1169a7c066fSLinus Walleij 		}
117*205c2988SOndrej Jirman 		gpiod_set_value_cansleep(priv->flash_gpio, 0);
1189a7c066fSLinus Walleij 		gpiod_set_value_cansleep(priv->enable_gpio, 1);
1199a7c066fSLinus Walleij 	} else {
120*205c2988SOndrej Jirman 		del_timer_sync(&priv->powerdown_timer);
121*205c2988SOndrej Jirman 		gpiod_set_value_cansleep(priv->flash_gpio, 0);
1229a7c066fSLinus Walleij 		gpiod_set_value_cansleep(priv->enable_gpio, 0);
1239a7c066fSLinus Walleij 		ret = regulator_disable(priv->vin_regulator);
1249a7c066fSLinus Walleij 		if (ret) {
1259a7c066fSLinus Walleij 			dev_err(led_cdev->dev,
1269a7c066fSLinus Walleij 				"failed to disable regulator: %d\n", ret);
1279a7c066fSLinus Walleij 			return ret;
1289a7c066fSLinus Walleij 		}
1299a7c066fSLinus Walleij 	}
1309a7c066fSLinus Walleij 
1319a7c066fSLinus Walleij 	priv->enabled = enable;
1329a7c066fSLinus Walleij 
1339a7c066fSLinus Walleij 	return 0;
1349a7c066fSLinus Walleij }
1359a7c066fSLinus Walleij 
sgm3140_powerdown_timer(struct timer_list * t)1369a7c066fSLinus Walleij static void sgm3140_powerdown_timer(struct timer_list *t)
1379a7c066fSLinus Walleij {
1389a7c066fSLinus Walleij 	struct sgm3140 *priv = from_timer(priv, t, powerdown_timer);
1399a7c066fSLinus Walleij 
1409a7c066fSLinus Walleij 	gpiod_set_value(priv->enable_gpio, 0);
1419a7c066fSLinus Walleij 	gpiod_set_value(priv->flash_gpio, 0);
1429a7c066fSLinus Walleij 	regulator_disable(priv->vin_regulator);
1439a7c066fSLinus Walleij 
1449a7c066fSLinus Walleij 	priv->enabled = false;
1459a7c066fSLinus Walleij }
1469a7c066fSLinus Walleij 
sgm3140_init_flash_timeout(struct sgm3140 * priv)1479a7c066fSLinus Walleij static void sgm3140_init_flash_timeout(struct sgm3140 *priv)
1489a7c066fSLinus Walleij {
1499a7c066fSLinus Walleij 	struct led_classdev_flash *fled_cdev = &priv->fled_cdev;
1509a7c066fSLinus Walleij 	struct led_flash_setting *s;
1519a7c066fSLinus Walleij 
1529a7c066fSLinus Walleij 	/* Init flash timeout setting */
1539a7c066fSLinus Walleij 	s = &fled_cdev->timeout;
1549a7c066fSLinus Walleij 	s->min = 1;
1559a7c066fSLinus Walleij 	s->max = priv->max_timeout;
1569a7c066fSLinus Walleij 	s->step = 1;
1579a7c066fSLinus Walleij 	s->val = FLASH_TIMEOUT_DEFAULT;
1589a7c066fSLinus Walleij }
1599a7c066fSLinus Walleij 
1609a7c066fSLinus Walleij #if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS)
sgm3140_init_v4l2_flash_config(struct sgm3140 * priv,struct v4l2_flash_config * v4l2_sd_cfg)1619a7c066fSLinus Walleij static void sgm3140_init_v4l2_flash_config(struct sgm3140 *priv,
1629a7c066fSLinus Walleij 					struct v4l2_flash_config *v4l2_sd_cfg)
1639a7c066fSLinus Walleij {
1649a7c066fSLinus Walleij 	struct led_classdev *led_cdev = &priv->fled_cdev.led_cdev;
1659a7c066fSLinus Walleij 	struct led_flash_setting *s;
1669a7c066fSLinus Walleij 
1679a7c066fSLinus Walleij 	strscpy(v4l2_sd_cfg->dev_name, led_cdev->dev->kobj.name,
1689a7c066fSLinus Walleij 		sizeof(v4l2_sd_cfg->dev_name));
1699a7c066fSLinus Walleij 
1709a7c066fSLinus Walleij 	/* Init flash intensity setting */
1719a7c066fSLinus Walleij 	s = &v4l2_sd_cfg->intensity;
1729a7c066fSLinus Walleij 	s->min = 0;
1739a7c066fSLinus Walleij 	s->max = 1;
1749a7c066fSLinus Walleij 	s->step = 1;
1759a7c066fSLinus Walleij 	s->val = 1;
1769a7c066fSLinus Walleij }
1779a7c066fSLinus Walleij 
1789a7c066fSLinus Walleij #else
sgm3140_init_v4l2_flash_config(struct sgm3140 * priv,struct v4l2_flash_config * v4l2_sd_cfg)1799a7c066fSLinus Walleij static void sgm3140_init_v4l2_flash_config(struct sgm3140 *priv,
1809a7c066fSLinus Walleij 					struct v4l2_flash_config *v4l2_sd_cfg)
1819a7c066fSLinus Walleij {
1829a7c066fSLinus Walleij }
1839a7c066fSLinus Walleij #endif
1849a7c066fSLinus Walleij 
sgm3140_probe(struct platform_device * pdev)1859a7c066fSLinus Walleij static int sgm3140_probe(struct platform_device *pdev)
1869a7c066fSLinus Walleij {
1879a7c066fSLinus Walleij 	struct sgm3140 *priv;
1889a7c066fSLinus Walleij 	struct led_classdev *led_cdev;
1899a7c066fSLinus Walleij 	struct led_classdev_flash *fled_cdev;
1909a7c066fSLinus Walleij 	struct led_init_data init_data = {};
1919a7c066fSLinus Walleij 	struct fwnode_handle *child_node;
1929a7c066fSLinus Walleij 	struct v4l2_flash_config v4l2_sd_cfg = {};
1939a7c066fSLinus Walleij 	int ret;
1949a7c066fSLinus Walleij 
1959a7c066fSLinus Walleij 	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
1969a7c066fSLinus Walleij 	if (!priv)
1979a7c066fSLinus Walleij 		return -ENOMEM;
1989a7c066fSLinus Walleij 
1999a7c066fSLinus Walleij 	priv->flash_gpio = devm_gpiod_get(&pdev->dev, "flash", GPIOD_OUT_LOW);
2009a7c066fSLinus Walleij 	ret = PTR_ERR_OR_ZERO(priv->flash_gpio);
2019a7c066fSLinus Walleij 	if (ret)
2029a7c066fSLinus Walleij 		return dev_err_probe(&pdev->dev, ret,
2039a7c066fSLinus Walleij 				     "Failed to request flash gpio\n");
2049a7c066fSLinus Walleij 
2059a7c066fSLinus Walleij 	priv->enable_gpio = devm_gpiod_get(&pdev->dev, "enable", GPIOD_OUT_LOW);
2069a7c066fSLinus Walleij 	ret = PTR_ERR_OR_ZERO(priv->enable_gpio);
2079a7c066fSLinus Walleij 	if (ret)
2089a7c066fSLinus Walleij 		return dev_err_probe(&pdev->dev, ret,
2099a7c066fSLinus Walleij 				     "Failed to request enable gpio\n");
2109a7c066fSLinus Walleij 
2119a7c066fSLinus Walleij 	priv->vin_regulator = devm_regulator_get(&pdev->dev, "vin");
2129a7c066fSLinus Walleij 	ret = PTR_ERR_OR_ZERO(priv->vin_regulator);
2139a7c066fSLinus Walleij 	if (ret)
2149a7c066fSLinus Walleij 		return dev_err_probe(&pdev->dev, ret,
2159a7c066fSLinus Walleij 				     "Failed to request regulator\n");
2169a7c066fSLinus Walleij 
2179a7c066fSLinus Walleij 	child_node = fwnode_get_next_available_child_node(pdev->dev.fwnode,
2189a7c066fSLinus Walleij 							  NULL);
2199a7c066fSLinus Walleij 	if (!child_node) {
2209a7c066fSLinus Walleij 		dev_err(&pdev->dev,
2219a7c066fSLinus Walleij 			"No fwnode child node found for connected LED.\n");
2229a7c066fSLinus Walleij 		return -EINVAL;
2239a7c066fSLinus Walleij 	}
2249a7c066fSLinus Walleij 
2259a7c066fSLinus Walleij 	ret = fwnode_property_read_u32(child_node, "flash-max-timeout-us",
2269a7c066fSLinus Walleij 				       &priv->max_timeout);
2279a7c066fSLinus Walleij 	if (ret) {
2289a7c066fSLinus Walleij 		priv->max_timeout = FLASH_MAX_TIMEOUT_DEFAULT;
2299a7c066fSLinus Walleij 		dev_warn(&pdev->dev,
2309a7c066fSLinus Walleij 			 "flash-max-timeout-us property missing\n");
2319a7c066fSLinus Walleij 	}
2329a7c066fSLinus Walleij 
2339a7c066fSLinus Walleij 	/*
2349a7c066fSLinus Walleij 	 * Set default timeout to FLASH_DEFAULT_TIMEOUT except if max_timeout
2359a7c066fSLinus Walleij 	 * from DT is lower.
2369a7c066fSLinus Walleij 	 */
2379a7c066fSLinus Walleij 	priv->timeout = min(priv->max_timeout, FLASH_TIMEOUT_DEFAULT);
2389a7c066fSLinus Walleij 
2399a7c066fSLinus Walleij 	timer_setup(&priv->powerdown_timer, sgm3140_powerdown_timer, 0);
2409a7c066fSLinus Walleij 
2419a7c066fSLinus Walleij 	fled_cdev = &priv->fled_cdev;
2429a7c066fSLinus Walleij 	led_cdev = &fled_cdev->led_cdev;
2439a7c066fSLinus Walleij 
2449a7c066fSLinus Walleij 	fled_cdev->ops = &sgm3140_flash_ops;
2459a7c066fSLinus Walleij 
2469a7c066fSLinus Walleij 	led_cdev->brightness_set_blocking = sgm3140_brightness_set;
2479a7c066fSLinus Walleij 	led_cdev->max_brightness = LED_ON;
2489a7c066fSLinus Walleij 	led_cdev->flags |= LED_DEV_CAP_FLASH;
2499a7c066fSLinus Walleij 
2509a7c066fSLinus Walleij 	sgm3140_init_flash_timeout(priv);
2519a7c066fSLinus Walleij 
2529a7c066fSLinus Walleij 	init_data.fwnode = child_node;
2539a7c066fSLinus Walleij 
2549a7c066fSLinus Walleij 	platform_set_drvdata(pdev, priv);
2559a7c066fSLinus Walleij 
2569a7c066fSLinus Walleij 	/* Register in the LED subsystem */
2579a7c066fSLinus Walleij 	ret = devm_led_classdev_flash_register_ext(&pdev->dev,
2589a7c066fSLinus Walleij 						   fled_cdev, &init_data);
2599a7c066fSLinus Walleij 	if (ret) {
2609a7c066fSLinus Walleij 		dev_err(&pdev->dev, "Failed to register flash device: %d\n",
2619a7c066fSLinus Walleij 			ret);
2629a7c066fSLinus Walleij 		goto err;
2639a7c066fSLinus Walleij 	}
2649a7c066fSLinus Walleij 
2659a7c066fSLinus Walleij 	sgm3140_init_v4l2_flash_config(priv, &v4l2_sd_cfg);
2669a7c066fSLinus Walleij 
2679a7c066fSLinus Walleij 	/* Create V4L2 Flash subdev */
2689a7c066fSLinus Walleij 	priv->v4l2_flash = v4l2_flash_init(&pdev->dev,
2699a7c066fSLinus Walleij 					   child_node,
2709a7c066fSLinus Walleij 					   fled_cdev, NULL,
2719a7c066fSLinus Walleij 					   &v4l2_sd_cfg);
2729a7c066fSLinus Walleij 	if (IS_ERR(priv->v4l2_flash)) {
2739a7c066fSLinus Walleij 		ret = PTR_ERR(priv->v4l2_flash);
2749a7c066fSLinus Walleij 		goto err;
2759a7c066fSLinus Walleij 	}
2769a7c066fSLinus Walleij 
2779a7c066fSLinus Walleij 	return ret;
2789a7c066fSLinus Walleij 
2799a7c066fSLinus Walleij err:
2809a7c066fSLinus Walleij 	fwnode_handle_put(child_node);
2819a7c066fSLinus Walleij 	return ret;
2829a7c066fSLinus Walleij }
2839a7c066fSLinus Walleij 
sgm3140_remove(struct platform_device * pdev)28460613020SUwe Kleine-König static void sgm3140_remove(struct platform_device *pdev)
2859a7c066fSLinus Walleij {
2869a7c066fSLinus Walleij 	struct sgm3140 *priv = platform_get_drvdata(pdev);
2879a7c066fSLinus Walleij 
2889a7c066fSLinus Walleij 	del_timer_sync(&priv->powerdown_timer);
2899a7c066fSLinus Walleij 
2909a7c066fSLinus Walleij 	v4l2_flash_release(priv->v4l2_flash);
2919a7c066fSLinus Walleij }
2929a7c066fSLinus Walleij 
2939a7c066fSLinus Walleij static const struct of_device_id sgm3140_dt_match[] = {
29477d62fccSAndré Apitzsch 	{ .compatible = "ocs,ocp8110" },
2957bd932d9SRaymond Hackley 	{ .compatible = "richtek,rt5033-led" },
2969a7c066fSLinus Walleij 	{ .compatible = "sgmicro,sgm3140" },
2979a7c066fSLinus Walleij 	{ /* sentinel */ }
2989a7c066fSLinus Walleij };
2999a7c066fSLinus Walleij MODULE_DEVICE_TABLE(of, sgm3140_dt_match);
3009a7c066fSLinus Walleij 
3019a7c066fSLinus Walleij static struct platform_driver sgm3140_driver = {
3029a7c066fSLinus Walleij 	.probe	= sgm3140_probe,
30360613020SUwe Kleine-König 	.remove_new = sgm3140_remove,
3049a7c066fSLinus Walleij 	.driver	= {
3059a7c066fSLinus Walleij 		.name	= "sgm3140",
3069a7c066fSLinus Walleij 		.of_match_table = sgm3140_dt_match,
3079a7c066fSLinus Walleij 	},
3089a7c066fSLinus Walleij };
3099a7c066fSLinus Walleij 
3109a7c066fSLinus Walleij module_platform_driver(sgm3140_driver);
3119a7c066fSLinus Walleij 
3129a7c066fSLinus Walleij MODULE_AUTHOR("Luca Weiss <luca@z3ntu.xyz>");
3139a7c066fSLinus Walleij MODULE_DESCRIPTION("SG Micro SGM3140 charge pump LED driver");
3149a7c066fSLinus Walleij MODULE_LICENSE("GPL v2");
315