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