Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

leds: add sgm3140 driver

Add a driver for the SGMICRO SGM3140 Buck/Boost Charge Pump LED driver.

This device is controlled by two GPIO pins, one for enabling and the
second one for switching between torch and flash mode.

Signed-off-by: Luca Weiss <luca@z3ntu.xyz>
Signed-off-by: Pavel Machek <pavel@ucw.cz>

authored by

Luca Weiss and committed by
Pavel Machek
cef8ec8c ea4407fa

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