linux/drivers/leds/flash/leds-ktd2692.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * LED driver : leds-ktd2692.c
   4 *
   5 * Copyright (C) 2015 Samsung Electronics
   6 * Ingi Kim <ingi2.kim@samsung.com>
   7 */
   8
   9#include <linux/delay.h>
  10#include <linux/err.h>
  11#include <linux/gpio/consumer.h>
  12#include <linux/led-class-flash.h>
  13#include <linux/module.h>
  14#include <linux/mutex.h>
  15#include <linux/of.h>
  16#include <linux/platform_device.h>
  17#include <linux/regulator/consumer.h>
  18
  19/* Value related the movie mode */
  20#define KTD2692_MOVIE_MODE_CURRENT_LEVELS       16
  21#define KTD2692_MM_TO_FL_RATIO(x)               ((x) / 3)
  22#define KTD2692_MM_MIN_CURR_THRESHOLD_SCALE     8
  23
  24/* Value related the flash mode */
  25#define KTD2692_FLASH_MODE_TIMEOUT_LEVELS       8
  26#define KTD2692_FLASH_MODE_TIMEOUT_DISABLE      0
  27#define KTD2692_FLASH_MODE_CURR_PERCENT(x)      (((x) * 16) / 100)
  28
  29/* Macro for getting offset of flash timeout */
  30#define GET_TIMEOUT_OFFSET(timeout, step)       ((timeout) / (step))
  31
  32/* Base register address */
  33#define KTD2692_REG_LVP_BASE                    0x00
  34#define KTD2692_REG_FLASH_TIMEOUT_BASE          0x20
  35#define KTD2692_REG_MM_MIN_CURR_THRESHOLD_BASE  0x40
  36#define KTD2692_REG_MOVIE_CURRENT_BASE          0x60
  37#define KTD2692_REG_FLASH_CURRENT_BASE          0x80
  38#define KTD2692_REG_MODE_BASE                   0xA0
  39
  40/* Set bit coding time for expresswire interface */
  41#define KTD2692_TIME_RESET_US                   700
  42#define KTD2692_TIME_DATA_START_TIME_US         10
  43#define KTD2692_TIME_HIGH_END_OF_DATA_US        350
  44#define KTD2692_TIME_LOW_END_OF_DATA_US         10
  45#define KTD2692_TIME_SHORT_BITSET_US            4
  46#define KTD2692_TIME_LONG_BITSET_US             12
  47
  48/* KTD2692 default length of name */
  49#define KTD2692_NAME_LENGTH                     20
  50
  51enum ktd2692_bitset {
  52        KTD2692_LOW = 0,
  53        KTD2692_HIGH,
  54};
  55
  56/* Movie / Flash Mode Control */
  57enum ktd2692_led_mode {
  58        KTD2692_MODE_DISABLE = 0,       /* default */
  59        KTD2692_MODE_MOVIE,
  60        KTD2692_MODE_FLASH,
  61};
  62
  63struct ktd2692_led_config_data {
  64        /* maximum LED current in movie mode */
  65        u32 movie_max_microamp;
  66        /* maximum LED current in flash mode */
  67        u32 flash_max_microamp;
  68        /* maximum flash timeout */
  69        u32 flash_max_timeout;
  70        /* max LED brightness level */
  71        enum led_brightness max_brightness;
  72};
  73
  74struct ktd2692_context {
  75        /* Related LED Flash class device */
  76        struct led_classdev_flash fled_cdev;
  77
  78        /* secures access to the device */
  79        struct mutex lock;
  80        struct regulator *regulator;
  81
  82        struct gpio_desc *aux_gpio;
  83        struct gpio_desc *ctrl_gpio;
  84
  85        enum ktd2692_led_mode mode;
  86        enum led_brightness torch_brightness;
  87};
  88
  89static struct ktd2692_context *fled_cdev_to_led(
  90                                struct led_classdev_flash *fled_cdev)
  91{
  92        return container_of(fled_cdev, struct ktd2692_context, fled_cdev);
  93}
  94
  95static void ktd2692_expresswire_start(struct ktd2692_context *led)
  96{
  97        gpiod_direction_output(led->ctrl_gpio, KTD2692_HIGH);
  98        udelay(KTD2692_TIME_DATA_START_TIME_US);
  99}
 100
 101static void ktd2692_expresswire_reset(struct ktd2692_context *led)
 102{
 103        gpiod_direction_output(led->ctrl_gpio, KTD2692_LOW);
 104        udelay(KTD2692_TIME_RESET_US);
 105}
 106
 107static void ktd2692_expresswire_end(struct ktd2692_context *led)
 108{
 109        gpiod_direction_output(led->ctrl_gpio, KTD2692_LOW);
 110        udelay(KTD2692_TIME_LOW_END_OF_DATA_US);
 111        gpiod_direction_output(led->ctrl_gpio, KTD2692_HIGH);
 112        udelay(KTD2692_TIME_HIGH_END_OF_DATA_US);
 113}
 114
 115static void ktd2692_expresswire_set_bit(struct ktd2692_context *led, bool bit)
 116{
 117        /*
 118         * The Low Bit(0) and High Bit(1) is based on a time detection
 119         * algorithm between time low and time high
 120         * Time_(L_LB) : Low time of the Low Bit(0)
 121         * Time_(H_LB) : High time of the LOW Bit(0)
 122         * Time_(L_HB) : Low time of the High Bit(1)
 123         * Time_(H_HB) : High time of the High Bit(1)
 124         *
 125         * It can be simplified to:
 126         * Low Bit(0) : 2 * Time_(H_LB) < Time_(L_LB)
 127         * High Bit(1) : 2 * Time_(L_HB) < Time_(H_HB)
 128         * HIGH  ___           ____    _..     _________    ___
 129         *          |_________|    |_..  |____|         |__|
 130         * LOW        <L_LB>  <H_LB>     <L_HB>  <H_HB>
 131         *          [  Low Bit (0) ]     [  High Bit(1) ]
 132         */
 133        if (bit) {
 134                gpiod_direction_output(led->ctrl_gpio, KTD2692_LOW);
 135                udelay(KTD2692_TIME_SHORT_BITSET_US);
 136                gpiod_direction_output(led->ctrl_gpio, KTD2692_HIGH);
 137                udelay(KTD2692_TIME_LONG_BITSET_US);
 138        } else {
 139                gpiod_direction_output(led->ctrl_gpio, KTD2692_LOW);
 140                udelay(KTD2692_TIME_LONG_BITSET_US);
 141                gpiod_direction_output(led->ctrl_gpio, KTD2692_HIGH);
 142                udelay(KTD2692_TIME_SHORT_BITSET_US);
 143        }
 144}
 145
 146static void ktd2692_expresswire_write(struct ktd2692_context *led, u8 value)
 147{
 148        int i;
 149
 150        ktd2692_expresswire_start(led);
 151        for (i = 7; i >= 0; i--)
 152                ktd2692_expresswire_set_bit(led, value & BIT(i));
 153        ktd2692_expresswire_end(led);
 154}
 155
 156static int ktd2692_led_brightness_set(struct led_classdev *led_cdev,
 157                                       enum led_brightness brightness)
 158{
 159        struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
 160        struct ktd2692_context *led = fled_cdev_to_led(fled_cdev);
 161
 162        mutex_lock(&led->lock);
 163
 164        if (brightness == LED_OFF) {
 165                led->mode = KTD2692_MODE_DISABLE;
 166                gpiod_direction_output(led->aux_gpio, KTD2692_LOW);
 167        } else {
 168                ktd2692_expresswire_write(led, brightness |
 169                                        KTD2692_REG_MOVIE_CURRENT_BASE);
 170                led->mode = KTD2692_MODE_MOVIE;
 171        }
 172
 173        ktd2692_expresswire_write(led, led->mode | KTD2692_REG_MODE_BASE);
 174        mutex_unlock(&led->lock);
 175
 176        return 0;
 177}
 178
 179static int ktd2692_led_flash_strobe_set(struct led_classdev_flash *fled_cdev,
 180                                        bool state)
 181{
 182        struct ktd2692_context *led = fled_cdev_to_led(fled_cdev);
 183        struct led_flash_setting *timeout = &fled_cdev->timeout;
 184        u32 flash_tm_reg;
 185
 186        mutex_lock(&led->lock);
 187
 188        if (state) {
 189                flash_tm_reg = GET_TIMEOUT_OFFSET(timeout->val, timeout->step);
 190                ktd2692_expresswire_write(led, flash_tm_reg
 191                                | KTD2692_REG_FLASH_TIMEOUT_BASE);
 192
 193                led->mode = KTD2692_MODE_FLASH;
 194                gpiod_direction_output(led->aux_gpio, KTD2692_HIGH);
 195        } else {
 196                led->mode = KTD2692_MODE_DISABLE;
 197                gpiod_direction_output(led->aux_gpio, KTD2692_LOW);
 198        }
 199
 200        ktd2692_expresswire_write(led, led->mode | KTD2692_REG_MODE_BASE);
 201
 202        fled_cdev->led_cdev.brightness = LED_OFF;
 203        led->mode = KTD2692_MODE_DISABLE;
 204
 205        mutex_unlock(&led->lock);
 206
 207        return 0;
 208}
 209
 210static int ktd2692_led_flash_timeout_set(struct led_classdev_flash *fled_cdev,
 211                                         u32 timeout)
 212{
 213        return 0;
 214}
 215
 216static void ktd2692_init_movie_current_max(struct ktd2692_led_config_data *cfg)
 217{
 218        u32 offset, step;
 219        u32 movie_current_microamp;
 220
 221        offset = KTD2692_MOVIE_MODE_CURRENT_LEVELS;
 222        step = KTD2692_MM_TO_FL_RATIO(cfg->flash_max_microamp)
 223                / KTD2692_MOVIE_MODE_CURRENT_LEVELS;
 224
 225        do {
 226                movie_current_microamp = step * offset;
 227                offset--;
 228        } while ((movie_current_microamp > cfg->movie_max_microamp) &&
 229                (offset > 0));
 230
 231        cfg->max_brightness = offset;
 232}
 233
 234static void ktd2692_init_flash_timeout(struct led_classdev_flash *fled_cdev,
 235                                       struct ktd2692_led_config_data *cfg)
 236{
 237        struct led_flash_setting *setting;
 238
 239        setting = &fled_cdev->timeout;
 240        setting->min = KTD2692_FLASH_MODE_TIMEOUT_DISABLE;
 241        setting->max = cfg->flash_max_timeout;
 242        setting->step = cfg->flash_max_timeout
 243                        / (KTD2692_FLASH_MODE_TIMEOUT_LEVELS - 1);
 244        setting->val = cfg->flash_max_timeout;
 245}
 246
 247static void ktd2692_setup(struct ktd2692_context *led)
 248{
 249        led->mode = KTD2692_MODE_DISABLE;
 250        ktd2692_expresswire_reset(led);
 251        gpiod_direction_output(led->aux_gpio, KTD2692_LOW);
 252
 253        ktd2692_expresswire_write(led, (KTD2692_MM_MIN_CURR_THRESHOLD_SCALE - 1)
 254                                 | KTD2692_REG_MM_MIN_CURR_THRESHOLD_BASE);
 255        ktd2692_expresswire_write(led, KTD2692_FLASH_MODE_CURR_PERCENT(45)
 256                                 | KTD2692_REG_FLASH_CURRENT_BASE);
 257}
 258
 259static void regulator_disable_action(void *_data)
 260{
 261        struct device *dev = _data;
 262        struct ktd2692_context *led = dev_get_drvdata(dev);
 263        int ret;
 264
 265        ret = regulator_disable(led->regulator);
 266        if (ret)
 267                dev_err(dev, "Failed to disable supply: %d\n", ret);
 268}
 269
 270static int ktd2692_parse_dt(struct ktd2692_context *led, struct device *dev,
 271                            struct ktd2692_led_config_data *cfg)
 272{
 273        struct device_node *np = dev_of_node(dev);
 274        struct device_node *child_node;
 275        int ret;
 276
 277        if (!dev_of_node(dev))
 278                return -ENXIO;
 279
 280        led->ctrl_gpio = devm_gpiod_get(dev, "ctrl", GPIOD_ASIS);
 281        ret = PTR_ERR_OR_ZERO(led->ctrl_gpio);
 282        if (ret) {
 283                dev_err(dev, "cannot get ctrl-gpios %d\n", ret);
 284                return ret;
 285        }
 286
 287        led->aux_gpio = devm_gpiod_get(dev, "aux", GPIOD_ASIS);
 288        ret = PTR_ERR_OR_ZERO(led->aux_gpio);
 289        if (ret) {
 290                dev_err(dev, "cannot get aux-gpios %d\n", ret);
 291                return ret;
 292        }
 293
 294        led->regulator = devm_regulator_get(dev, "vin");
 295        if (IS_ERR(led->regulator))
 296                led->regulator = NULL;
 297
 298        if (led->regulator) {
 299                ret = regulator_enable(led->regulator);
 300                if (ret) {
 301                        dev_err(dev, "Failed to enable supply: %d\n", ret);
 302                } else {
 303                        ret = devm_add_action_or_reset(dev,
 304                                                regulator_disable_action, dev);
 305                        if (ret)
 306                                return ret;
 307                }
 308        }
 309
 310        child_node = of_get_next_available_child(np, NULL);
 311        if (!child_node) {
 312                dev_err(dev, "No DT child node found for connected LED.\n");
 313                return -EINVAL;
 314        }
 315
 316        led->fled_cdev.led_cdev.name =
 317                of_get_property(child_node, "label", NULL) ? : child_node->name;
 318
 319        ret = of_property_read_u32(child_node, "led-max-microamp",
 320                                   &cfg->movie_max_microamp);
 321        if (ret) {
 322                dev_err(dev, "failed to parse led-max-microamp\n");
 323                goto err_parse_dt;
 324        }
 325
 326        ret = of_property_read_u32(child_node, "flash-max-microamp",
 327                                   &cfg->flash_max_microamp);
 328        if (ret) {
 329                dev_err(dev, "failed to parse flash-max-microamp\n");
 330                goto err_parse_dt;
 331        }
 332
 333        ret = of_property_read_u32(child_node, "flash-max-timeout-us",
 334                                   &cfg->flash_max_timeout);
 335        if (ret) {
 336                dev_err(dev, "failed to parse flash-max-timeout-us\n");
 337                goto err_parse_dt;
 338        }
 339
 340err_parse_dt:
 341        of_node_put(child_node);
 342        return ret;
 343}
 344
 345static const struct led_flash_ops flash_ops = {
 346        .strobe_set = ktd2692_led_flash_strobe_set,
 347        .timeout_set = ktd2692_led_flash_timeout_set,
 348};
 349
 350static int ktd2692_probe(struct platform_device *pdev)
 351{
 352        struct ktd2692_context *led;
 353        struct led_classdev *led_cdev;
 354        struct led_classdev_flash *fled_cdev;
 355        struct ktd2692_led_config_data led_cfg;
 356        int ret;
 357
 358        led = devm_kzalloc(&pdev->dev, sizeof(*led), GFP_KERNEL);
 359        if (!led)
 360                return -ENOMEM;
 361
 362        fled_cdev = &led->fled_cdev;
 363        led_cdev = &fled_cdev->led_cdev;
 364
 365        ret = ktd2692_parse_dt(led, &pdev->dev, &led_cfg);
 366        if (ret)
 367                return ret;
 368
 369        ktd2692_init_flash_timeout(fled_cdev, &led_cfg);
 370        ktd2692_init_movie_current_max(&led_cfg);
 371
 372        fled_cdev->ops = &flash_ops;
 373
 374        led_cdev->max_brightness = led_cfg.max_brightness;
 375        led_cdev->brightness_set_blocking = ktd2692_led_brightness_set;
 376        led_cdev->flags |= LED_CORE_SUSPENDRESUME | LED_DEV_CAP_FLASH;
 377
 378        mutex_init(&led->lock);
 379
 380        platform_set_drvdata(pdev, led);
 381
 382        ret = led_classdev_flash_register(&pdev->dev, fled_cdev);
 383        if (ret) {
 384                dev_err(&pdev->dev, "can't register LED %s\n", led_cdev->name);
 385                mutex_destroy(&led->lock);
 386                return ret;
 387        }
 388
 389        ktd2692_setup(led);
 390
 391        return 0;
 392}
 393
 394static int ktd2692_remove(struct platform_device *pdev)
 395{
 396        struct ktd2692_context *led = platform_get_drvdata(pdev);
 397
 398        led_classdev_flash_unregister(&led->fled_cdev);
 399
 400        mutex_destroy(&led->lock);
 401
 402        return 0;
 403}
 404
 405static const struct of_device_id ktd2692_match[] = {
 406        { .compatible = "kinetic,ktd2692", },
 407        { /* sentinel */ },
 408};
 409MODULE_DEVICE_TABLE(of, ktd2692_match);
 410
 411static struct platform_driver ktd2692_driver = {
 412        .driver = {
 413                .name  = "ktd2692",
 414                .of_match_table = ktd2692_match,
 415        },
 416        .probe  = ktd2692_probe,
 417        .remove = ktd2692_remove,
 418};
 419
 420module_platform_driver(ktd2692_driver);
 421
 422MODULE_AUTHOR("Ingi Kim <ingi2.kim@samsung.com>");
 423MODULE_DESCRIPTION("Kinetic KTD2692 LED driver");
 424MODULE_LICENSE("GPL v2");
 425