151f3b2c3SLinus Walleij // SPDX-License-Identifier: GPL-2.0-only 251f3b2c3SLinus Walleij /* 351f3b2c3SLinus Walleij * LED driver : leds-ktd2692.c 451f3b2c3SLinus Walleij * 551f3b2c3SLinus Walleij * Copyright (C) 2015 Samsung Electronics 651f3b2c3SLinus Walleij * Ingi Kim <ingi2.kim@samsung.com> 751f3b2c3SLinus Walleij */ 851f3b2c3SLinus Walleij 951f3b2c3SLinus Walleij #include <linux/delay.h> 1051f3b2c3SLinus Walleij #include <linux/err.h> 1151f3b2c3SLinus Walleij #include <linux/gpio/consumer.h> 1251f3b2c3SLinus Walleij #include <linux/led-class-flash.h> 1351f3b2c3SLinus Walleij #include <linux/module.h> 1451f3b2c3SLinus Walleij #include <linux/mutex.h> 1551f3b2c3SLinus Walleij #include <linux/of.h> 1651f3b2c3SLinus Walleij #include <linux/platform_device.h> 1751f3b2c3SLinus Walleij #include <linux/regulator/consumer.h> 1851f3b2c3SLinus Walleij 1951f3b2c3SLinus Walleij /* Value related the movie mode */ 2051f3b2c3SLinus Walleij #define KTD2692_MOVIE_MODE_CURRENT_LEVELS 16 2151f3b2c3SLinus Walleij #define KTD2692_MM_TO_FL_RATIO(x) ((x) / 3) 2251f3b2c3SLinus Walleij #define KTD2692_MM_MIN_CURR_THRESHOLD_SCALE 8 2351f3b2c3SLinus Walleij 2451f3b2c3SLinus Walleij /* Value related the flash mode */ 2551f3b2c3SLinus Walleij #define KTD2692_FLASH_MODE_TIMEOUT_LEVELS 8 2651f3b2c3SLinus Walleij #define KTD2692_FLASH_MODE_TIMEOUT_DISABLE 0 2751f3b2c3SLinus Walleij #define KTD2692_FLASH_MODE_CURR_PERCENT(x) (((x) * 16) / 100) 2851f3b2c3SLinus Walleij 2951f3b2c3SLinus Walleij /* Macro for getting offset of flash timeout */ 3051f3b2c3SLinus Walleij #define GET_TIMEOUT_OFFSET(timeout, step) ((timeout) / (step)) 3151f3b2c3SLinus Walleij 3251f3b2c3SLinus Walleij /* Base register address */ 3351f3b2c3SLinus Walleij #define KTD2692_REG_LVP_BASE 0x00 3451f3b2c3SLinus Walleij #define KTD2692_REG_FLASH_TIMEOUT_BASE 0x20 3551f3b2c3SLinus Walleij #define KTD2692_REG_MM_MIN_CURR_THRESHOLD_BASE 0x40 3651f3b2c3SLinus Walleij #define KTD2692_REG_MOVIE_CURRENT_BASE 0x60 3751f3b2c3SLinus Walleij #define KTD2692_REG_FLASH_CURRENT_BASE 0x80 3851f3b2c3SLinus Walleij #define KTD2692_REG_MODE_BASE 0xA0 3951f3b2c3SLinus Walleij 4051f3b2c3SLinus Walleij /* Set bit coding time for expresswire interface */ 4151f3b2c3SLinus Walleij #define KTD2692_TIME_RESET_US 700 4251f3b2c3SLinus Walleij #define KTD2692_TIME_DATA_START_TIME_US 10 4351f3b2c3SLinus Walleij #define KTD2692_TIME_HIGH_END_OF_DATA_US 350 4451f3b2c3SLinus Walleij #define KTD2692_TIME_LOW_END_OF_DATA_US 10 4551f3b2c3SLinus Walleij #define KTD2692_TIME_SHORT_BITSET_US 4 4651f3b2c3SLinus Walleij #define KTD2692_TIME_LONG_BITSET_US 12 4751f3b2c3SLinus Walleij 4851f3b2c3SLinus Walleij /* KTD2692 default length of name */ 4951f3b2c3SLinus Walleij #define KTD2692_NAME_LENGTH 20 5051f3b2c3SLinus Walleij 5151f3b2c3SLinus Walleij enum ktd2692_bitset { 5251f3b2c3SLinus Walleij KTD2692_LOW = 0, 5351f3b2c3SLinus Walleij KTD2692_HIGH, 5451f3b2c3SLinus Walleij }; 5551f3b2c3SLinus Walleij 5651f3b2c3SLinus Walleij /* Movie / Flash Mode Control */ 5751f3b2c3SLinus Walleij enum ktd2692_led_mode { 5851f3b2c3SLinus Walleij KTD2692_MODE_DISABLE = 0, /* default */ 5951f3b2c3SLinus Walleij KTD2692_MODE_MOVIE, 6051f3b2c3SLinus Walleij KTD2692_MODE_FLASH, 6151f3b2c3SLinus Walleij }; 6251f3b2c3SLinus Walleij 6351f3b2c3SLinus Walleij struct ktd2692_led_config_data { 6451f3b2c3SLinus Walleij /* maximum LED current in movie mode */ 6551f3b2c3SLinus Walleij u32 movie_max_microamp; 6651f3b2c3SLinus Walleij /* maximum LED current in flash mode */ 6751f3b2c3SLinus Walleij u32 flash_max_microamp; 6851f3b2c3SLinus Walleij /* maximum flash timeout */ 6951f3b2c3SLinus Walleij u32 flash_max_timeout; 7051f3b2c3SLinus Walleij /* max LED brightness level */ 7151f3b2c3SLinus Walleij enum led_brightness max_brightness; 7251f3b2c3SLinus Walleij }; 7351f3b2c3SLinus Walleij 7451f3b2c3SLinus Walleij struct ktd2692_context { 7551f3b2c3SLinus Walleij /* Related LED Flash class device */ 7651f3b2c3SLinus Walleij struct led_classdev_flash fled_cdev; 7751f3b2c3SLinus Walleij 7851f3b2c3SLinus Walleij /* secures access to the device */ 7951f3b2c3SLinus Walleij struct mutex lock; 8051f3b2c3SLinus Walleij struct regulator *regulator; 8151f3b2c3SLinus Walleij 8251f3b2c3SLinus Walleij struct gpio_desc *aux_gpio; 8351f3b2c3SLinus Walleij struct gpio_desc *ctrl_gpio; 8451f3b2c3SLinus Walleij 8551f3b2c3SLinus Walleij enum ktd2692_led_mode mode; 8651f3b2c3SLinus Walleij enum led_brightness torch_brightness; 8751f3b2c3SLinus Walleij }; 8851f3b2c3SLinus Walleij 8951f3b2c3SLinus Walleij static struct ktd2692_context *fled_cdev_to_led( 9051f3b2c3SLinus Walleij struct led_classdev_flash *fled_cdev) 9151f3b2c3SLinus Walleij { 9251f3b2c3SLinus Walleij return container_of(fled_cdev, struct ktd2692_context, fled_cdev); 9351f3b2c3SLinus Walleij } 9451f3b2c3SLinus Walleij 9551f3b2c3SLinus Walleij static void ktd2692_expresswire_start(struct ktd2692_context *led) 9651f3b2c3SLinus Walleij { 9751f3b2c3SLinus Walleij gpiod_direction_output(led->ctrl_gpio, KTD2692_HIGH); 9851f3b2c3SLinus Walleij udelay(KTD2692_TIME_DATA_START_TIME_US); 9951f3b2c3SLinus Walleij } 10051f3b2c3SLinus Walleij 10151f3b2c3SLinus Walleij static void ktd2692_expresswire_reset(struct ktd2692_context *led) 10251f3b2c3SLinus Walleij { 10351f3b2c3SLinus Walleij gpiod_direction_output(led->ctrl_gpio, KTD2692_LOW); 10451f3b2c3SLinus Walleij udelay(KTD2692_TIME_RESET_US); 10551f3b2c3SLinus Walleij } 10651f3b2c3SLinus Walleij 10751f3b2c3SLinus Walleij static void ktd2692_expresswire_end(struct ktd2692_context *led) 10851f3b2c3SLinus Walleij { 10951f3b2c3SLinus Walleij gpiod_direction_output(led->ctrl_gpio, KTD2692_LOW); 11051f3b2c3SLinus Walleij udelay(KTD2692_TIME_LOW_END_OF_DATA_US); 11151f3b2c3SLinus Walleij gpiod_direction_output(led->ctrl_gpio, KTD2692_HIGH); 11251f3b2c3SLinus Walleij udelay(KTD2692_TIME_HIGH_END_OF_DATA_US); 11351f3b2c3SLinus Walleij } 11451f3b2c3SLinus Walleij 11551f3b2c3SLinus Walleij static void ktd2692_expresswire_set_bit(struct ktd2692_context *led, bool bit) 11651f3b2c3SLinus Walleij { 11751f3b2c3SLinus Walleij /* 11851f3b2c3SLinus Walleij * The Low Bit(0) and High Bit(1) is based on a time detection 11951f3b2c3SLinus Walleij * algorithm between time low and time high 12051f3b2c3SLinus Walleij * Time_(L_LB) : Low time of the Low Bit(0) 12151f3b2c3SLinus Walleij * Time_(H_LB) : High time of the LOW Bit(0) 12251f3b2c3SLinus Walleij * Time_(L_HB) : Low time of the High Bit(1) 12351f3b2c3SLinus Walleij * Time_(H_HB) : High time of the High Bit(1) 12451f3b2c3SLinus Walleij * 12551f3b2c3SLinus Walleij * It can be simplified to: 12651f3b2c3SLinus Walleij * Low Bit(0) : 2 * Time_(H_LB) < Time_(L_LB) 12751f3b2c3SLinus Walleij * High Bit(1) : 2 * Time_(L_HB) < Time_(H_HB) 12851f3b2c3SLinus Walleij * HIGH ___ ____ _.. _________ ___ 12951f3b2c3SLinus Walleij * |_________| |_.. |____| |__| 13051f3b2c3SLinus Walleij * LOW <L_LB> <H_LB> <L_HB> <H_HB> 13151f3b2c3SLinus Walleij * [ Low Bit (0) ] [ High Bit(1) ] 13251f3b2c3SLinus Walleij */ 13351f3b2c3SLinus Walleij if (bit) { 13451f3b2c3SLinus Walleij gpiod_direction_output(led->ctrl_gpio, KTD2692_LOW); 13551f3b2c3SLinus Walleij udelay(KTD2692_TIME_SHORT_BITSET_US); 13651f3b2c3SLinus Walleij gpiod_direction_output(led->ctrl_gpio, KTD2692_HIGH); 13751f3b2c3SLinus Walleij udelay(KTD2692_TIME_LONG_BITSET_US); 13851f3b2c3SLinus Walleij } else { 13951f3b2c3SLinus Walleij gpiod_direction_output(led->ctrl_gpio, KTD2692_LOW); 14051f3b2c3SLinus Walleij udelay(KTD2692_TIME_LONG_BITSET_US); 14151f3b2c3SLinus Walleij gpiod_direction_output(led->ctrl_gpio, KTD2692_HIGH); 14251f3b2c3SLinus Walleij udelay(KTD2692_TIME_SHORT_BITSET_US); 14351f3b2c3SLinus Walleij } 14451f3b2c3SLinus Walleij } 14551f3b2c3SLinus Walleij 14651f3b2c3SLinus Walleij static void ktd2692_expresswire_write(struct ktd2692_context *led, u8 value) 14751f3b2c3SLinus Walleij { 14851f3b2c3SLinus Walleij int i; 14951f3b2c3SLinus Walleij 15051f3b2c3SLinus Walleij ktd2692_expresswire_start(led); 15151f3b2c3SLinus Walleij for (i = 7; i >= 0; i--) 15251f3b2c3SLinus Walleij ktd2692_expresswire_set_bit(led, value & BIT(i)); 15351f3b2c3SLinus Walleij ktd2692_expresswire_end(led); 15451f3b2c3SLinus Walleij } 15551f3b2c3SLinus Walleij 15651f3b2c3SLinus Walleij static int ktd2692_led_brightness_set(struct led_classdev *led_cdev, 15751f3b2c3SLinus Walleij enum led_brightness brightness) 15851f3b2c3SLinus Walleij { 15951f3b2c3SLinus Walleij struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev); 16051f3b2c3SLinus Walleij struct ktd2692_context *led = fled_cdev_to_led(fled_cdev); 16151f3b2c3SLinus Walleij 16251f3b2c3SLinus Walleij mutex_lock(&led->lock); 16351f3b2c3SLinus Walleij 16451f3b2c3SLinus Walleij if (brightness == LED_OFF) { 16551f3b2c3SLinus Walleij led->mode = KTD2692_MODE_DISABLE; 16651f3b2c3SLinus Walleij gpiod_direction_output(led->aux_gpio, KTD2692_LOW); 16751f3b2c3SLinus Walleij } else { 16851f3b2c3SLinus Walleij ktd2692_expresswire_write(led, brightness | 16951f3b2c3SLinus Walleij KTD2692_REG_MOVIE_CURRENT_BASE); 17051f3b2c3SLinus Walleij led->mode = KTD2692_MODE_MOVIE; 17151f3b2c3SLinus Walleij } 17251f3b2c3SLinus Walleij 17351f3b2c3SLinus Walleij ktd2692_expresswire_write(led, led->mode | KTD2692_REG_MODE_BASE); 17451f3b2c3SLinus Walleij mutex_unlock(&led->lock); 17551f3b2c3SLinus Walleij 17651f3b2c3SLinus Walleij return 0; 17751f3b2c3SLinus Walleij } 17851f3b2c3SLinus Walleij 17951f3b2c3SLinus Walleij static int ktd2692_led_flash_strobe_set(struct led_classdev_flash *fled_cdev, 18051f3b2c3SLinus Walleij bool state) 18151f3b2c3SLinus Walleij { 18251f3b2c3SLinus Walleij struct ktd2692_context *led = fled_cdev_to_led(fled_cdev); 18351f3b2c3SLinus Walleij struct led_flash_setting *timeout = &fled_cdev->timeout; 18451f3b2c3SLinus Walleij u32 flash_tm_reg; 18551f3b2c3SLinus Walleij 18651f3b2c3SLinus Walleij mutex_lock(&led->lock); 18751f3b2c3SLinus Walleij 18851f3b2c3SLinus Walleij if (state) { 18951f3b2c3SLinus Walleij flash_tm_reg = GET_TIMEOUT_OFFSET(timeout->val, timeout->step); 19051f3b2c3SLinus Walleij ktd2692_expresswire_write(led, flash_tm_reg 19151f3b2c3SLinus Walleij | KTD2692_REG_FLASH_TIMEOUT_BASE); 19251f3b2c3SLinus Walleij 19351f3b2c3SLinus Walleij led->mode = KTD2692_MODE_FLASH; 19451f3b2c3SLinus Walleij gpiod_direction_output(led->aux_gpio, KTD2692_HIGH); 19551f3b2c3SLinus Walleij } else { 19651f3b2c3SLinus Walleij led->mode = KTD2692_MODE_DISABLE; 19751f3b2c3SLinus Walleij gpiod_direction_output(led->aux_gpio, KTD2692_LOW); 19851f3b2c3SLinus Walleij } 19951f3b2c3SLinus Walleij 20051f3b2c3SLinus Walleij ktd2692_expresswire_write(led, led->mode | KTD2692_REG_MODE_BASE); 20151f3b2c3SLinus Walleij 20251f3b2c3SLinus Walleij fled_cdev->led_cdev.brightness = LED_OFF; 20351f3b2c3SLinus Walleij led->mode = KTD2692_MODE_DISABLE; 20451f3b2c3SLinus Walleij 20551f3b2c3SLinus Walleij mutex_unlock(&led->lock); 20651f3b2c3SLinus Walleij 20751f3b2c3SLinus Walleij return 0; 20851f3b2c3SLinus Walleij } 20951f3b2c3SLinus Walleij 21051f3b2c3SLinus Walleij static int ktd2692_led_flash_timeout_set(struct led_classdev_flash *fled_cdev, 21151f3b2c3SLinus Walleij u32 timeout) 21251f3b2c3SLinus Walleij { 21351f3b2c3SLinus Walleij return 0; 21451f3b2c3SLinus Walleij } 21551f3b2c3SLinus Walleij 21651f3b2c3SLinus Walleij static void ktd2692_init_movie_current_max(struct ktd2692_led_config_data *cfg) 21751f3b2c3SLinus Walleij { 21851f3b2c3SLinus Walleij u32 offset, step; 21951f3b2c3SLinus Walleij u32 movie_current_microamp; 22051f3b2c3SLinus Walleij 22151f3b2c3SLinus Walleij offset = KTD2692_MOVIE_MODE_CURRENT_LEVELS; 22251f3b2c3SLinus Walleij step = KTD2692_MM_TO_FL_RATIO(cfg->flash_max_microamp) 22351f3b2c3SLinus Walleij / KTD2692_MOVIE_MODE_CURRENT_LEVELS; 22451f3b2c3SLinus Walleij 22551f3b2c3SLinus Walleij do { 22651f3b2c3SLinus Walleij movie_current_microamp = step * offset; 22751f3b2c3SLinus Walleij offset--; 22851f3b2c3SLinus Walleij } while ((movie_current_microamp > cfg->movie_max_microamp) && 22951f3b2c3SLinus Walleij (offset > 0)); 23051f3b2c3SLinus Walleij 23151f3b2c3SLinus Walleij cfg->max_brightness = offset; 23251f3b2c3SLinus Walleij } 23351f3b2c3SLinus Walleij 23451f3b2c3SLinus Walleij static void ktd2692_init_flash_timeout(struct led_classdev_flash *fled_cdev, 23551f3b2c3SLinus Walleij struct ktd2692_led_config_data *cfg) 23651f3b2c3SLinus Walleij { 23751f3b2c3SLinus Walleij struct led_flash_setting *setting; 23851f3b2c3SLinus Walleij 23951f3b2c3SLinus Walleij setting = &fled_cdev->timeout; 24051f3b2c3SLinus Walleij setting->min = KTD2692_FLASH_MODE_TIMEOUT_DISABLE; 24151f3b2c3SLinus Walleij setting->max = cfg->flash_max_timeout; 24251f3b2c3SLinus Walleij setting->step = cfg->flash_max_timeout 24351f3b2c3SLinus Walleij / (KTD2692_FLASH_MODE_TIMEOUT_LEVELS - 1); 24451f3b2c3SLinus Walleij setting->val = cfg->flash_max_timeout; 24551f3b2c3SLinus Walleij } 24651f3b2c3SLinus Walleij 24751f3b2c3SLinus Walleij static void ktd2692_setup(struct ktd2692_context *led) 24851f3b2c3SLinus Walleij { 24951f3b2c3SLinus Walleij led->mode = KTD2692_MODE_DISABLE; 25051f3b2c3SLinus Walleij ktd2692_expresswire_reset(led); 25151f3b2c3SLinus Walleij gpiod_direction_output(led->aux_gpio, KTD2692_LOW); 25251f3b2c3SLinus Walleij 25351f3b2c3SLinus Walleij ktd2692_expresswire_write(led, (KTD2692_MM_MIN_CURR_THRESHOLD_SCALE - 1) 25451f3b2c3SLinus Walleij | KTD2692_REG_MM_MIN_CURR_THRESHOLD_BASE); 25551f3b2c3SLinus Walleij ktd2692_expresswire_write(led, KTD2692_FLASH_MODE_CURR_PERCENT(45) 25651f3b2c3SLinus Walleij | KTD2692_REG_FLASH_CURRENT_BASE); 25751f3b2c3SLinus Walleij } 25851f3b2c3SLinus Walleij 25951f3b2c3SLinus Walleij static void regulator_disable_action(void *_data) 26051f3b2c3SLinus Walleij { 26151f3b2c3SLinus Walleij struct device *dev = _data; 26251f3b2c3SLinus Walleij struct ktd2692_context *led = dev_get_drvdata(dev); 26351f3b2c3SLinus Walleij int ret; 26451f3b2c3SLinus Walleij 26551f3b2c3SLinus Walleij ret = regulator_disable(led->regulator); 26651f3b2c3SLinus Walleij if (ret) 26751f3b2c3SLinus Walleij dev_err(dev, "Failed to disable supply: %d\n", ret); 26851f3b2c3SLinus Walleij } 26951f3b2c3SLinus Walleij 27051f3b2c3SLinus Walleij static int ktd2692_parse_dt(struct ktd2692_context *led, struct device *dev, 27151f3b2c3SLinus Walleij struct ktd2692_led_config_data *cfg) 27251f3b2c3SLinus Walleij { 27351f3b2c3SLinus Walleij struct device_node *np = dev_of_node(dev); 27451f3b2c3SLinus Walleij struct device_node *child_node; 27551f3b2c3SLinus Walleij int ret; 27651f3b2c3SLinus Walleij 277a05f5d0eSLad Prabhakar if (!np) 27851f3b2c3SLinus Walleij return -ENXIO; 27951f3b2c3SLinus Walleij 28051f3b2c3SLinus Walleij led->ctrl_gpio = devm_gpiod_get(dev, "ctrl", GPIOD_ASIS); 28151f3b2c3SLinus Walleij ret = PTR_ERR_OR_ZERO(led->ctrl_gpio); 282*1d3b5aaaSMarkuss Broks if (ret) 283*1d3b5aaaSMarkuss Broks return dev_err_probe(dev, ret, "cannot get ctrl-gpios\n"); 28451f3b2c3SLinus Walleij 28551f3b2c3SLinus Walleij led->aux_gpio = devm_gpiod_get(dev, "aux", GPIOD_ASIS); 28651f3b2c3SLinus Walleij ret = PTR_ERR_OR_ZERO(led->aux_gpio); 287*1d3b5aaaSMarkuss Broks if (ret) 288*1d3b5aaaSMarkuss Broks return dev_err_probe(dev, PTR_ERR(led->aux_gpio), "cannot get aux-gpios\n"); 28951f3b2c3SLinus Walleij 29051f3b2c3SLinus Walleij led->regulator = devm_regulator_get(dev, "vin"); 29151f3b2c3SLinus Walleij if (IS_ERR(led->regulator)) 29251f3b2c3SLinus Walleij led->regulator = NULL; 29351f3b2c3SLinus Walleij 29451f3b2c3SLinus Walleij if (led->regulator) { 29551f3b2c3SLinus Walleij ret = regulator_enable(led->regulator); 29651f3b2c3SLinus Walleij if (ret) { 29751f3b2c3SLinus Walleij dev_err(dev, "Failed to enable supply: %d\n", ret); 29851f3b2c3SLinus Walleij } else { 29951f3b2c3SLinus Walleij ret = devm_add_action_or_reset(dev, 30051f3b2c3SLinus Walleij regulator_disable_action, dev); 30151f3b2c3SLinus Walleij if (ret) 30251f3b2c3SLinus Walleij return ret; 30351f3b2c3SLinus Walleij } 30451f3b2c3SLinus Walleij } 30551f3b2c3SLinus Walleij 30651f3b2c3SLinus Walleij child_node = of_get_next_available_child(np, NULL); 30751f3b2c3SLinus Walleij if (!child_node) { 30851f3b2c3SLinus Walleij dev_err(dev, "No DT child node found for connected LED.\n"); 30951f3b2c3SLinus Walleij return -EINVAL; 31051f3b2c3SLinus Walleij } 31151f3b2c3SLinus Walleij 31251f3b2c3SLinus Walleij led->fled_cdev.led_cdev.name = 31351f3b2c3SLinus Walleij of_get_property(child_node, "label", NULL) ? : child_node->name; 31451f3b2c3SLinus Walleij 31551f3b2c3SLinus Walleij ret = of_property_read_u32(child_node, "led-max-microamp", 31651f3b2c3SLinus Walleij &cfg->movie_max_microamp); 31751f3b2c3SLinus Walleij if (ret) { 31851f3b2c3SLinus Walleij dev_err(dev, "failed to parse led-max-microamp\n"); 31951f3b2c3SLinus Walleij goto err_parse_dt; 32051f3b2c3SLinus Walleij } 32151f3b2c3SLinus Walleij 32251f3b2c3SLinus Walleij ret = of_property_read_u32(child_node, "flash-max-microamp", 32351f3b2c3SLinus Walleij &cfg->flash_max_microamp); 32451f3b2c3SLinus Walleij if (ret) { 32551f3b2c3SLinus Walleij dev_err(dev, "failed to parse flash-max-microamp\n"); 32651f3b2c3SLinus Walleij goto err_parse_dt; 32751f3b2c3SLinus Walleij } 32851f3b2c3SLinus Walleij 32951f3b2c3SLinus Walleij ret = of_property_read_u32(child_node, "flash-max-timeout-us", 33051f3b2c3SLinus Walleij &cfg->flash_max_timeout); 33151f3b2c3SLinus Walleij if (ret) { 33251f3b2c3SLinus Walleij dev_err(dev, "failed to parse flash-max-timeout-us\n"); 33351f3b2c3SLinus Walleij goto err_parse_dt; 33451f3b2c3SLinus Walleij } 33551f3b2c3SLinus Walleij 33651f3b2c3SLinus Walleij err_parse_dt: 33751f3b2c3SLinus Walleij of_node_put(child_node); 33851f3b2c3SLinus Walleij return ret; 33951f3b2c3SLinus Walleij } 34051f3b2c3SLinus Walleij 34151f3b2c3SLinus Walleij static const struct led_flash_ops flash_ops = { 34251f3b2c3SLinus Walleij .strobe_set = ktd2692_led_flash_strobe_set, 34351f3b2c3SLinus Walleij .timeout_set = ktd2692_led_flash_timeout_set, 34451f3b2c3SLinus Walleij }; 34551f3b2c3SLinus Walleij 34651f3b2c3SLinus Walleij static int ktd2692_probe(struct platform_device *pdev) 34751f3b2c3SLinus Walleij { 34851f3b2c3SLinus Walleij struct ktd2692_context *led; 34951f3b2c3SLinus Walleij struct led_classdev *led_cdev; 35051f3b2c3SLinus Walleij struct led_classdev_flash *fled_cdev; 35151f3b2c3SLinus Walleij struct ktd2692_led_config_data led_cfg; 35251f3b2c3SLinus Walleij int ret; 35351f3b2c3SLinus Walleij 35451f3b2c3SLinus Walleij led = devm_kzalloc(&pdev->dev, sizeof(*led), GFP_KERNEL); 35551f3b2c3SLinus Walleij if (!led) 35651f3b2c3SLinus Walleij return -ENOMEM; 35751f3b2c3SLinus Walleij 35851f3b2c3SLinus Walleij fled_cdev = &led->fled_cdev; 35951f3b2c3SLinus Walleij led_cdev = &fled_cdev->led_cdev; 36051f3b2c3SLinus Walleij 36151f3b2c3SLinus Walleij ret = ktd2692_parse_dt(led, &pdev->dev, &led_cfg); 36251f3b2c3SLinus Walleij if (ret) 36351f3b2c3SLinus Walleij return ret; 36451f3b2c3SLinus Walleij 36551f3b2c3SLinus Walleij ktd2692_init_flash_timeout(fled_cdev, &led_cfg); 36651f3b2c3SLinus Walleij ktd2692_init_movie_current_max(&led_cfg); 36751f3b2c3SLinus Walleij 36851f3b2c3SLinus Walleij fled_cdev->ops = &flash_ops; 36951f3b2c3SLinus Walleij 37051f3b2c3SLinus Walleij led_cdev->max_brightness = led_cfg.max_brightness; 37151f3b2c3SLinus Walleij led_cdev->brightness_set_blocking = ktd2692_led_brightness_set; 37251f3b2c3SLinus Walleij led_cdev->flags |= LED_CORE_SUSPENDRESUME | LED_DEV_CAP_FLASH; 37351f3b2c3SLinus Walleij 37451f3b2c3SLinus Walleij mutex_init(&led->lock); 37551f3b2c3SLinus Walleij 37651f3b2c3SLinus Walleij platform_set_drvdata(pdev, led); 37751f3b2c3SLinus Walleij 37851f3b2c3SLinus Walleij ret = led_classdev_flash_register(&pdev->dev, fled_cdev); 37951f3b2c3SLinus Walleij if (ret) { 38051f3b2c3SLinus Walleij dev_err(&pdev->dev, "can't register LED %s\n", led_cdev->name); 38151f3b2c3SLinus Walleij mutex_destroy(&led->lock); 38251f3b2c3SLinus Walleij return ret; 38351f3b2c3SLinus Walleij } 38451f3b2c3SLinus Walleij 38551f3b2c3SLinus Walleij ktd2692_setup(led); 38651f3b2c3SLinus Walleij 38751f3b2c3SLinus Walleij return 0; 38851f3b2c3SLinus Walleij } 38951f3b2c3SLinus Walleij 39051f3b2c3SLinus Walleij static int ktd2692_remove(struct platform_device *pdev) 39151f3b2c3SLinus Walleij { 39251f3b2c3SLinus Walleij struct ktd2692_context *led = platform_get_drvdata(pdev); 39351f3b2c3SLinus Walleij 39451f3b2c3SLinus Walleij led_classdev_flash_unregister(&led->fled_cdev); 39551f3b2c3SLinus Walleij 39651f3b2c3SLinus Walleij mutex_destroy(&led->lock); 39751f3b2c3SLinus Walleij 39851f3b2c3SLinus Walleij return 0; 39951f3b2c3SLinus Walleij } 40051f3b2c3SLinus Walleij 40151f3b2c3SLinus Walleij static const struct of_device_id ktd2692_match[] = { 40251f3b2c3SLinus Walleij { .compatible = "kinetic,ktd2692", }, 40351f3b2c3SLinus Walleij { /* sentinel */ }, 40451f3b2c3SLinus Walleij }; 40551f3b2c3SLinus Walleij MODULE_DEVICE_TABLE(of, ktd2692_match); 40651f3b2c3SLinus Walleij 40751f3b2c3SLinus Walleij static struct platform_driver ktd2692_driver = { 40851f3b2c3SLinus Walleij .driver = { 40951f3b2c3SLinus Walleij .name = "ktd2692", 41051f3b2c3SLinus Walleij .of_match_table = ktd2692_match, 41151f3b2c3SLinus Walleij }, 41251f3b2c3SLinus Walleij .probe = ktd2692_probe, 41351f3b2c3SLinus Walleij .remove = ktd2692_remove, 41451f3b2c3SLinus Walleij }; 41551f3b2c3SLinus Walleij 41651f3b2c3SLinus Walleij module_platform_driver(ktd2692_driver); 41751f3b2c3SLinus Walleij 41851f3b2c3SLinus Walleij MODULE_AUTHOR("Ingi Kim <ingi2.kim@samsung.com>"); 41951f3b2c3SLinus Walleij MODULE_DESCRIPTION("Kinetic KTD2692 LED driver"); 42051f3b2c3SLinus Walleij MODULE_LICENSE("GPL v2"); 421