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