leds-sgm3140.c (7643B)
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 15struct 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 33static struct sgm3140 *flcdev_to_sgm3140(struct led_classdev_flash *flcdev) 34{ 35 return container_of(flcdev, struct sgm3140, fled_cdev); 36} 37 38static 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 74static 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 83static 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 93static 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 99static 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 133static 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 144static 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) 158static 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 176static void sgm3140_init_v4l2_flash_config(struct sgm3140 *priv, 177 struct v4l2_flash_config *v4l2_sd_cfg) 178{ 179} 180#endif 181 182static 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 return dev_err_probe(&pdev->dev, ret, 200 "Failed to request flash gpio\n"); 201 202 priv->enable_gpio = devm_gpiod_get(&pdev->dev, "enable", GPIOD_OUT_LOW); 203 ret = PTR_ERR_OR_ZERO(priv->enable_gpio); 204 if (ret) 205 return dev_err_probe(&pdev->dev, ret, 206 "Failed to request enable gpio\n"); 207 208 priv->vin_regulator = devm_regulator_get(&pdev->dev, "vin"); 209 ret = PTR_ERR_OR_ZERO(priv->vin_regulator); 210 if (ret) 211 return dev_err_probe(&pdev->dev, ret, 212 "Failed to request regulator\n"); 213 214 child_node = fwnode_get_next_available_child_node(pdev->dev.fwnode, 215 NULL); 216 if (!child_node) { 217 dev_err(&pdev->dev, 218 "No fwnode child node found for connected LED.\n"); 219 return -EINVAL; 220 } 221 222 ret = fwnode_property_read_u32(child_node, "flash-max-timeout-us", 223 &priv->max_timeout); 224 if (ret) { 225 priv->max_timeout = FLASH_MAX_TIMEOUT_DEFAULT; 226 dev_warn(&pdev->dev, 227 "flash-max-timeout-us property missing\n"); 228 } 229 230 /* 231 * Set default timeout to FLASH_DEFAULT_TIMEOUT except if max_timeout 232 * from DT is lower. 233 */ 234 priv->timeout = min(priv->max_timeout, FLASH_TIMEOUT_DEFAULT); 235 236 timer_setup(&priv->powerdown_timer, sgm3140_powerdown_timer, 0); 237 238 fled_cdev = &priv->fled_cdev; 239 led_cdev = &fled_cdev->led_cdev; 240 241 fled_cdev->ops = &sgm3140_flash_ops; 242 243 led_cdev->brightness_set_blocking = sgm3140_brightness_set; 244 led_cdev->max_brightness = LED_ON; 245 led_cdev->flags |= LED_DEV_CAP_FLASH; 246 247 sgm3140_init_flash_timeout(priv); 248 249 init_data.fwnode = child_node; 250 251 platform_set_drvdata(pdev, priv); 252 253 /* Register in the LED subsystem */ 254 ret = devm_led_classdev_flash_register_ext(&pdev->dev, 255 fled_cdev, &init_data); 256 if (ret) { 257 dev_err(&pdev->dev, "Failed to register flash device: %d\n", 258 ret); 259 goto err; 260 } 261 262 sgm3140_init_v4l2_flash_config(priv, &v4l2_sd_cfg); 263 264 /* Create V4L2 Flash subdev */ 265 priv->v4l2_flash = v4l2_flash_init(&pdev->dev, 266 child_node, 267 fled_cdev, NULL, 268 &v4l2_sd_cfg); 269 if (IS_ERR(priv->v4l2_flash)) { 270 ret = PTR_ERR(priv->v4l2_flash); 271 goto err; 272 } 273 274 return ret; 275 276err: 277 fwnode_handle_put(child_node); 278 return ret; 279} 280 281static int sgm3140_remove(struct platform_device *pdev) 282{ 283 struct sgm3140 *priv = platform_get_drvdata(pdev); 284 285 del_timer_sync(&priv->powerdown_timer); 286 287 v4l2_flash_release(priv->v4l2_flash); 288 289 return 0; 290} 291 292static const struct of_device_id sgm3140_dt_match[] = { 293 { .compatible = "ocs,ocp8110" }, 294 { .compatible = "sgmicro,sgm3140" }, 295 { /* sentinel */ } 296}; 297MODULE_DEVICE_TABLE(of, sgm3140_dt_match); 298 299static struct platform_driver sgm3140_driver = { 300 .probe = sgm3140_probe, 301 .remove = sgm3140_remove, 302 .driver = { 303 .name = "sgm3140", 304 .of_match_table = sgm3140_dt_match, 305 }, 306}; 307 308module_platform_driver(sgm3140_driver); 309 310MODULE_AUTHOR("Luca Weiss <luca@z3ntu.xyz>"); 311MODULE_DESCRIPTION("SG Micro SGM3140 charge pump LED driver"); 312MODULE_LICENSE("GPL v2");