From 293fee7f607841cc128c3f6df9b464e8abf99fb0 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 28 May 2021 22:23:59 +0200 Subject: leds: aat1290: Move driver to flash subdirectory We created a subdirectory for LED drivers that depend on CONFIG_LEDS_CLASS_FLASH, and this driver does so let's move it there. Signed-off-by: Linus Walleij Acked-by: Jacek Anaszewski Signed-off-by: Pavel Machek --- drivers/leds/Kconfig | 10 - drivers/leds/Makefile | 1 - drivers/leds/flash/Kconfig | 9 + drivers/leds/flash/Makefile | 1 + drivers/leds/flash/leds-aat1290.c | 556 ++++++++++++++++++++++++++++++++++++++ drivers/leds/leds-aat1290.c | 556 -------------------------------------- 6 files changed, 566 insertions(+), 567 deletions(-) create mode 100644 drivers/leds/flash/leds-aat1290.c delete mode 100644 drivers/leds/leds-aat1290.c diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index bdf16180f5ff..a350d8fbb085 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -59,16 +59,6 @@ config LEDS_88PM860X This option enables support for on-chip LED drivers found on Marvell Semiconductor 88PM8606 PMIC. -config LEDS_AAT1290 - tristate "LED support for the AAT1290" - depends on LEDS_CLASS_FLASH - depends on V4L2_FLASH_LED_CLASS || !V4L2_FLASH_LED_CLASS - depends on GPIOLIB || COMPILE_TEST - depends on OF - depends on PINCTRL - help - This option enables support for the LEDs on the AAT1290. - config LEDS_AN30259A tristate "LED support for Panasonic AN30259A" depends on LEDS_CLASS && I2C && OF diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index 7e604d3028c8..c7231975837a 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -9,7 +9,6 @@ obj-$(CONFIG_LEDS_TRIGGERS) += led-triggers.o # LED Platform Drivers (keep this sorted, M-| sort) obj-$(CONFIG_LEDS_88PM860X) += leds-88pm860x.o -obj-$(CONFIG_LEDS_AAT1290) += leds-aat1290.o obj-$(CONFIG_LEDS_ACER_A500) += leds-acer-a500.o obj-$(CONFIG_LEDS_ADP5520) += leds-adp5520.o obj-$(CONFIG_LEDS_AN30259A) += leds-an30259a.o diff --git a/drivers/leds/flash/Kconfig b/drivers/leds/flash/Kconfig index 3f49f3edbffb..736153b0bfd6 100644 --- a/drivers/leds/flash/Kconfig +++ b/drivers/leds/flash/Kconfig @@ -2,6 +2,15 @@ if LEDS_CLASS_FLASH +config LEDS_AAT1290 + tristate "LED support for the AAT1290" + depends on V4L2_FLASH_LED_CLASS || !V4L2_FLASH_LED_CLASS + depends on GPIOLIB || COMPILE_TEST + depends on OF + depends on PINCTRL + help + This option enables support for the LEDs on the AAT1290. + config LEDS_RT4505 tristate "LED support for RT4505 flashlight controller" depends on I2C && OF diff --git a/drivers/leds/flash/Makefile b/drivers/leds/flash/Makefile index 09aee561f769..11bec5881d51 100644 --- a/drivers/leds/flash/Makefile +++ b/drivers/leds/flash/Makefile @@ -1,4 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_LEDS_AAT1290) += leds-aat1290.o obj-$(CONFIG_LEDS_RT4505) += leds-rt4505.o obj-$(CONFIG_LEDS_RT8515) += leds-rt8515.o diff --git a/drivers/leds/flash/leds-aat1290.c b/drivers/leds/flash/leds-aat1290.c new file mode 100644 index 000000000000..589484b22c79 --- /dev/null +++ b/drivers/leds/flash/leds-aat1290.c @@ -0,0 +1,556 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * LED Flash class driver for the AAT1290 + * 1.5A Step-Up Current Regulator for Flash LEDs + * + * Copyright (C) 2015, Samsung Electronics Co., Ltd. + * Author: Jacek Anaszewski + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define AAT1290_MOVIE_MODE_CURRENT_ADDR 17 +#define AAT1290_MAX_MM_CURR_PERCENT_0 16 +#define AAT1290_MAX_MM_CURR_PERCENT_100 1 + +#define AAT1290_FLASH_SAFETY_TIMER_ADDR 18 + +#define AAT1290_MOVIE_MODE_CONFIG_ADDR 19 +#define AAT1290_MOVIE_MODE_OFF 1 +#define AAT1290_MOVIE_MODE_ON 3 + +#define AAT1290_MM_CURRENT_RATIO_ADDR 20 +#define AAT1290_MM_TO_FL_1_92 1 + +#define AAT1290_MM_TO_FL_RATIO 1000 / 1920 +#define AAT1290_MAX_MM_CURRENT(fl_max) (fl_max * AAT1290_MM_TO_FL_RATIO) + +#define AAT1290_LATCH_TIME_MIN_US 500 +#define AAT1290_LATCH_TIME_MAX_US 1000 +#define AAT1290_EN_SET_TICK_TIME_US 1 +#define AAT1290_FLEN_OFF_DELAY_TIME_US 10 +#define AAT1290_FLASH_TM_NUM_LEVELS 16 +#define AAT1290_MM_CURRENT_SCALE_SIZE 15 + +#define AAT1290_NAME "aat1290" + + +struct aat1290_led_config_data { + /* maximum LED current in movie mode */ + u32 max_mm_current; + /* maximum LED current in flash mode */ + u32 max_flash_current; + /* maximum flash timeout */ + u32 max_flash_tm; + /* external strobe capability */ + bool has_external_strobe; + /* max LED brightness level */ + enum led_brightness max_brightness; +}; + +struct aat1290_led { + /* platform device data */ + struct platform_device *pdev; + /* secures access to the device */ + struct mutex lock; + + /* corresponding LED Flash class device */ + struct led_classdev_flash fled_cdev; + /* V4L2 Flash device */ + struct v4l2_flash *v4l2_flash; + + /* FLEN pin */ + struct gpio_desc *gpio_fl_en; + /* EN|SET pin */ + struct gpio_desc *gpio_en_set; + /* movie mode current scale */ + int *mm_current_scale; + /* device mode */ + bool movie_mode; + /* brightness cache */ + unsigned int torch_brightness; +}; + +static struct aat1290_led *fled_cdev_to_led( + struct led_classdev_flash *fled_cdev) +{ + return container_of(fled_cdev, struct aat1290_led, fled_cdev); +} + +static struct led_classdev_flash *led_cdev_to_fled_cdev( + struct led_classdev *led_cdev) +{ + return container_of(led_cdev, struct led_classdev_flash, led_cdev); +} + +static void aat1290_as2cwire_write(struct aat1290_led *led, int addr, int value) +{ + int i; + + gpiod_direction_output(led->gpio_fl_en, 0); + gpiod_direction_output(led->gpio_en_set, 0); + + udelay(AAT1290_FLEN_OFF_DELAY_TIME_US); + + /* write address */ + for (i = 0; i < addr; ++i) { + udelay(AAT1290_EN_SET_TICK_TIME_US); + gpiod_direction_output(led->gpio_en_set, 0); + udelay(AAT1290_EN_SET_TICK_TIME_US); + gpiod_direction_output(led->gpio_en_set, 1); + } + + usleep_range(AAT1290_LATCH_TIME_MIN_US, AAT1290_LATCH_TIME_MAX_US); + + /* write data */ + for (i = 0; i < value; ++i) { + udelay(AAT1290_EN_SET_TICK_TIME_US); + gpiod_direction_output(led->gpio_en_set, 0); + udelay(AAT1290_EN_SET_TICK_TIME_US); + gpiod_direction_output(led->gpio_en_set, 1); + } + + usleep_range(AAT1290_LATCH_TIME_MIN_US, AAT1290_LATCH_TIME_MAX_US); +} + +static void aat1290_set_flash_safety_timer(struct aat1290_led *led, + unsigned int micro_sec) +{ + struct led_classdev_flash *fled_cdev = &led->fled_cdev; + struct led_flash_setting *flash_tm = &fled_cdev->timeout; + int flash_tm_reg = AAT1290_FLASH_TM_NUM_LEVELS - + (micro_sec / flash_tm->step) + 1; + + aat1290_as2cwire_write(led, AAT1290_FLASH_SAFETY_TIMER_ADDR, + flash_tm_reg); +} + +/* LED subsystem callbacks */ + +static int aat1290_led_brightness_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct led_classdev_flash *fled_cdev = led_cdev_to_fled_cdev(led_cdev); + struct aat1290_led *led = fled_cdev_to_led(fled_cdev); + + mutex_lock(&led->lock); + + if (brightness == 0) { + gpiod_direction_output(led->gpio_fl_en, 0); + gpiod_direction_output(led->gpio_en_set, 0); + led->movie_mode = false; + } else { + if (!led->movie_mode) { + aat1290_as2cwire_write(led, + AAT1290_MM_CURRENT_RATIO_ADDR, + AAT1290_MM_TO_FL_1_92); + led->movie_mode = true; + } + + aat1290_as2cwire_write(led, AAT1290_MOVIE_MODE_CURRENT_ADDR, + AAT1290_MAX_MM_CURR_PERCENT_0 - brightness); + aat1290_as2cwire_write(led, AAT1290_MOVIE_MODE_CONFIG_ADDR, + AAT1290_MOVIE_MODE_ON); + } + + mutex_unlock(&led->lock); + + return 0; +} + +static int aat1290_led_flash_strobe_set(struct led_classdev_flash *fled_cdev, + bool state) + +{ + struct aat1290_led *led = fled_cdev_to_led(fled_cdev); + struct led_classdev *led_cdev = &fled_cdev->led_cdev; + struct led_flash_setting *timeout = &fled_cdev->timeout; + + mutex_lock(&led->lock); + + if (state) { + aat1290_set_flash_safety_timer(led, timeout->val); + gpiod_direction_output(led->gpio_fl_en, 1); + } else { + gpiod_direction_output(led->gpio_fl_en, 0); + gpiod_direction_output(led->gpio_en_set, 0); + } + + /* + * To reenter movie mode after a flash event the part must be cycled + * off and back on to reset the movie mode and reprogrammed via the + * AS2Cwire. Therefore the brightness and movie_mode properties needs + * to be updated here to reflect the actual state. + */ + led_cdev->brightness = 0; + led->movie_mode = false; + + mutex_unlock(&led->lock); + + return 0; +} + +static int aat1290_led_flash_timeout_set(struct led_classdev_flash *fled_cdev, + u32 timeout) +{ + /* + * Don't do anything - flash timeout is cached in the led-class-flash + * core and will be applied in the strobe_set op, as writing the + * safety timer register spuriously turns the torch mode on. + */ + + return 0; +} + +static int aat1290_led_parse_dt(struct aat1290_led *led, + struct aat1290_led_config_data *cfg, + struct device_node **sub_node) +{ + struct device *dev = &led->pdev->dev; + struct device_node *child_node; +#if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS) + struct pinctrl *pinctrl; +#endif + int ret = 0; + + led->gpio_fl_en = devm_gpiod_get(dev, "flen", GPIOD_ASIS); + if (IS_ERR(led->gpio_fl_en)) { + ret = PTR_ERR(led->gpio_fl_en); + dev_err(dev, "Unable to claim gpio \"flen\".\n"); + return ret; + } + + led->gpio_en_set = devm_gpiod_get(dev, "enset", GPIOD_ASIS); + if (IS_ERR(led->gpio_en_set)) { + ret = PTR_ERR(led->gpio_en_set); + dev_err(dev, "Unable to claim gpio \"enset\".\n"); + return ret; + } + +#if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS) + pinctrl = devm_pinctrl_get_select_default(&led->pdev->dev); + if (IS_ERR(pinctrl)) { + cfg->has_external_strobe = false; + dev_info(dev, + "No support for external strobe detected.\n"); + } else { + cfg->has_external_strobe = true; + } +#endif + + child_node = of_get_next_available_child(dev_of_node(dev), NULL); + if (!child_node) { + dev_err(dev, "No DT child node found for connected LED.\n"); + return -EINVAL; + } + + ret = of_property_read_u32(child_node, "led-max-microamp", + &cfg->max_mm_current); + /* + * led-max-microamp will default to 1/20 of flash-max-microamp + * in case it is missing. + */ + if (ret < 0) + dev_warn(dev, + "led-max-microamp DT property missing\n"); + + ret = of_property_read_u32(child_node, "flash-max-microamp", + &cfg->max_flash_current); + if (ret < 0) { + dev_err(dev, + "flash-max-microamp DT property missing\n"); + goto err_parse_dt; + } + + ret = of_property_read_u32(child_node, "flash-max-timeout-us", + &cfg->max_flash_tm); + if (ret < 0) { + dev_err(dev, + "flash-max-timeout-us DT property missing\n"); + goto err_parse_dt; + } + + *sub_node = child_node; + +err_parse_dt: + of_node_put(child_node); + + return ret; +} + +static void aat1290_led_validate_mm_current(struct aat1290_led *led, + struct aat1290_led_config_data *cfg) +{ + int i, b = 0, e = AAT1290_MM_CURRENT_SCALE_SIZE; + + while (e - b > 1) { + i = b + (e - b) / 2; + if (cfg->max_mm_current < led->mm_current_scale[i]) + e = i; + else + b = i; + } + + cfg->max_mm_current = led->mm_current_scale[b]; + cfg->max_brightness = b + 1; +} + +static int init_mm_current_scale(struct aat1290_led *led, + struct aat1290_led_config_data *cfg) +{ + static const int max_mm_current_percent[] = { + 20, 22, 25, 28, 32, 36, 40, 45, 50, 56, + 63, 71, 79, 89, 100 + }; + int i, max_mm_current = + AAT1290_MAX_MM_CURRENT(cfg->max_flash_current); + + led->mm_current_scale = devm_kzalloc(&led->pdev->dev, + sizeof(max_mm_current_percent), + GFP_KERNEL); + if (!led->mm_current_scale) + return -ENOMEM; + + for (i = 0; i < AAT1290_MM_CURRENT_SCALE_SIZE; ++i) + led->mm_current_scale[i] = max_mm_current * + max_mm_current_percent[i] / 100; + + return 0; +} + +static int aat1290_led_get_configuration(struct aat1290_led *led, + struct aat1290_led_config_data *cfg, + struct device_node **sub_node) +{ + int ret; + + ret = aat1290_led_parse_dt(led, cfg, sub_node); + if (ret < 0) + return ret; + /* + * Init non-linear movie mode current scale basing + * on the max flash current from led configuration. + */ + ret = init_mm_current_scale(led, cfg); + if (ret < 0) + return ret; + + aat1290_led_validate_mm_current(led, cfg); + +#if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS) +#else + devm_kfree(&led->pdev->dev, led->mm_current_scale); +#endif + + return 0; +} + +static void aat1290_init_flash_timeout(struct aat1290_led *led, + struct aat1290_led_config_data *cfg) +{ + struct led_classdev_flash *fled_cdev = &led->fled_cdev; + struct led_flash_setting *setting; + + /* Init flash timeout setting */ + setting = &fled_cdev->timeout; + setting->min = cfg->max_flash_tm / AAT1290_FLASH_TM_NUM_LEVELS; + setting->max = cfg->max_flash_tm; + setting->step = setting->min; + setting->val = setting->max; +} + +#if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS) +static enum led_brightness aat1290_intensity_to_brightness( + struct v4l2_flash *v4l2_flash, + s32 intensity) +{ + struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev; + struct aat1290_led *led = fled_cdev_to_led(fled_cdev); + int i; + + for (i = AAT1290_MM_CURRENT_SCALE_SIZE - 1; i >= 0; --i) + if (intensity >= led->mm_current_scale[i]) + return i + 1; + + return 1; +} + +static s32 aat1290_brightness_to_intensity(struct v4l2_flash *v4l2_flash, + enum led_brightness brightness) +{ + struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev; + struct aat1290_led *led = fled_cdev_to_led(fled_cdev); + + return led->mm_current_scale[brightness - 1]; +} + +static int aat1290_led_external_strobe_set(struct v4l2_flash *v4l2_flash, + bool enable) +{ + struct aat1290_led *led = fled_cdev_to_led(v4l2_flash->fled_cdev); + struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev; + struct led_classdev *led_cdev = &fled_cdev->led_cdev; + struct pinctrl *pinctrl; + + gpiod_direction_output(led->gpio_fl_en, 0); + gpiod_direction_output(led->gpio_en_set, 0); + + led->movie_mode = false; + led_cdev->brightness = 0; + + pinctrl = devm_pinctrl_get_select(&led->pdev->dev, + enable ? "isp" : "host"); + if (IS_ERR(pinctrl)) { + dev_warn(&led->pdev->dev, "Unable to switch strobe source.\n"); + return PTR_ERR(pinctrl); + } + + return 0; +} + +static void aat1290_init_v4l2_flash_config(struct aat1290_led *led, + struct aat1290_led_config_data *led_cfg, + struct v4l2_flash_config *v4l2_sd_cfg) +{ + struct led_classdev *led_cdev = &led->fled_cdev.led_cdev; + struct led_flash_setting *s; + + strlcpy(v4l2_sd_cfg->dev_name, led_cdev->dev->kobj.name, + sizeof(v4l2_sd_cfg->dev_name)); + + s = &v4l2_sd_cfg->intensity; + s->min = led->mm_current_scale[0]; + s->max = led_cfg->max_mm_current; + s->step = 1; + s->val = s->max; + + v4l2_sd_cfg->has_external_strobe = led_cfg->has_external_strobe; +} + +static const struct v4l2_flash_ops v4l2_flash_ops = { + .external_strobe_set = aat1290_led_external_strobe_set, + .intensity_to_led_brightness = aat1290_intensity_to_brightness, + .led_brightness_to_intensity = aat1290_brightness_to_intensity, +}; +#else +static inline void aat1290_init_v4l2_flash_config(struct aat1290_led *led, + struct aat1290_led_config_data *led_cfg, + struct v4l2_flash_config *v4l2_sd_cfg) +{ +} +static const struct v4l2_flash_ops v4l2_flash_ops; +#endif + +static const struct led_flash_ops flash_ops = { + .strobe_set = aat1290_led_flash_strobe_set, + .timeout_set = aat1290_led_flash_timeout_set, +}; + +static int aat1290_led_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *sub_node = NULL; + struct aat1290_led *led; + struct led_classdev *led_cdev; + struct led_classdev_flash *fled_cdev; + struct led_init_data init_data = {}; + struct aat1290_led_config_data led_cfg = {}; + struct v4l2_flash_config v4l2_sd_cfg = {}; + int ret; + + led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL); + if (!led) + return -ENOMEM; + + led->pdev = pdev; + platform_set_drvdata(pdev, led); + + fled_cdev = &led->fled_cdev; + fled_cdev->ops = &flash_ops; + led_cdev = &fled_cdev->led_cdev; + + ret = aat1290_led_get_configuration(led, &led_cfg, &sub_node); + if (ret < 0) + return ret; + + mutex_init(&led->lock); + + /* Initialize LED Flash class device */ + led_cdev->brightness_set_blocking = aat1290_led_brightness_set; + led_cdev->max_brightness = led_cfg.max_brightness; + led_cdev->flags |= LED_DEV_CAP_FLASH; + + aat1290_init_flash_timeout(led, &led_cfg); + + init_data.fwnode = of_fwnode_handle(sub_node); + init_data.devicename = AAT1290_NAME; + + /* Register LED Flash class device */ + ret = led_classdev_flash_register_ext(&pdev->dev, fled_cdev, + &init_data); + if (ret < 0) + goto err_flash_register; + + aat1290_init_v4l2_flash_config(led, &led_cfg, &v4l2_sd_cfg); + + /* Create V4L2 Flash subdev. */ + led->v4l2_flash = v4l2_flash_init(dev, of_fwnode_handle(sub_node), + fled_cdev, &v4l2_flash_ops, + &v4l2_sd_cfg); + if (IS_ERR(led->v4l2_flash)) { + ret = PTR_ERR(led->v4l2_flash); + goto error_v4l2_flash_init; + } + + return 0; + +error_v4l2_flash_init: + led_classdev_flash_unregister(fled_cdev); +err_flash_register: + mutex_destroy(&led->lock); + + return ret; +} + +static int aat1290_led_remove(struct platform_device *pdev) +{ + struct aat1290_led *led = platform_get_drvdata(pdev); + + v4l2_flash_release(led->v4l2_flash); + led_classdev_flash_unregister(&led->fled_cdev); + + mutex_destroy(&led->lock); + + return 0; +} + +static const struct of_device_id aat1290_led_dt_match[] = { + { .compatible = "skyworks,aat1290" }, + {}, +}; +MODULE_DEVICE_TABLE(of, aat1290_led_dt_match); + +static struct platform_driver aat1290_led_driver = { + .probe = aat1290_led_probe, + .remove = aat1290_led_remove, + .driver = { + .name = "aat1290", + .of_match_table = aat1290_led_dt_match, + }, +}; + +module_platform_driver(aat1290_led_driver); + +MODULE_AUTHOR("Jacek Anaszewski "); +MODULE_DESCRIPTION("Skyworks Current Regulator for Flash LEDs"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/leds/leds-aat1290.c b/drivers/leds/leds-aat1290.c deleted file mode 100644 index 589484b22c79..000000000000 --- a/drivers/leds/leds-aat1290.c +++ /dev/null @@ -1,556 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * LED Flash class driver for the AAT1290 - * 1.5A Step-Up Current Regulator for Flash LEDs - * - * Copyright (C) 2015, Samsung Electronics Co., Ltd. - * Author: Jacek Anaszewski - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define AAT1290_MOVIE_MODE_CURRENT_ADDR 17 -#define AAT1290_MAX_MM_CURR_PERCENT_0 16 -#define AAT1290_MAX_MM_CURR_PERCENT_100 1 - -#define AAT1290_FLASH_SAFETY_TIMER_ADDR 18 - -#define AAT1290_MOVIE_MODE_CONFIG_ADDR 19 -#define AAT1290_MOVIE_MODE_OFF 1 -#define AAT1290_MOVIE_MODE_ON 3 - -#define AAT1290_MM_CURRENT_RATIO_ADDR 20 -#define AAT1290_MM_TO_FL_1_92 1 - -#define AAT1290_MM_TO_FL_RATIO 1000 / 1920 -#define AAT1290_MAX_MM_CURRENT(fl_max) (fl_max * AAT1290_MM_TO_FL_RATIO) - -#define AAT1290_LATCH_TIME_MIN_US 500 -#define AAT1290_LATCH_TIME_MAX_US 1000 -#define AAT1290_EN_SET_TICK_TIME_US 1 -#define AAT1290_FLEN_OFF_DELAY_TIME_US 10 -#define AAT1290_FLASH_TM_NUM_LEVELS 16 -#define AAT1290_MM_CURRENT_SCALE_SIZE 15 - -#define AAT1290_NAME "aat1290" - - -struct aat1290_led_config_data { - /* maximum LED current in movie mode */ - u32 max_mm_current; - /* maximum LED current in flash mode */ - u32 max_flash_current; - /* maximum flash timeout */ - u32 max_flash_tm; - /* external strobe capability */ - bool has_external_strobe; - /* max LED brightness level */ - enum led_brightness max_brightness; -}; - -struct aat1290_led { - /* platform device data */ - struct platform_device *pdev; - /* secures access to the device */ - struct mutex lock; - - /* corresponding LED Flash class device */ - struct led_classdev_flash fled_cdev; - /* V4L2 Flash device */ - struct v4l2_flash *v4l2_flash; - - /* FLEN pin */ - struct gpio_desc *gpio_fl_en; - /* EN|SET pin */ - struct gpio_desc *gpio_en_set; - /* movie mode current scale */ - int *mm_current_scale; - /* device mode */ - bool movie_mode; - /* brightness cache */ - unsigned int torch_brightness; -}; - -static struct aat1290_led *fled_cdev_to_led( - struct led_classdev_flash *fled_cdev) -{ - return container_of(fled_cdev, struct aat1290_led, fled_cdev); -} - -static struct led_classdev_flash *led_cdev_to_fled_cdev( - struct led_classdev *led_cdev) -{ - return container_of(led_cdev, struct led_classdev_flash, led_cdev); -} - -static void aat1290_as2cwire_write(struct aat1290_led *led, int addr, int value) -{ - int i; - - gpiod_direction_output(led->gpio_fl_en, 0); - gpiod_direction_output(led->gpio_en_set, 0); - - udelay(AAT1290_FLEN_OFF_DELAY_TIME_US); - - /* write address */ - for (i = 0; i < addr; ++i) { - udelay(AAT1290_EN_SET_TICK_TIME_US); - gpiod_direction_output(led->gpio_en_set, 0); - udelay(AAT1290_EN_SET_TICK_TIME_US); - gpiod_direction_output(led->gpio_en_set, 1); - } - - usleep_range(AAT1290_LATCH_TIME_MIN_US, AAT1290_LATCH_TIME_MAX_US); - - /* write data */ - for (i = 0; i < value; ++i) { - udelay(AAT1290_EN_SET_TICK_TIME_US); - gpiod_direction_output(led->gpio_en_set, 0); - udelay(AAT1290_EN_SET_TICK_TIME_US); - gpiod_direction_output(led->gpio_en_set, 1); - } - - usleep_range(AAT1290_LATCH_TIME_MIN_US, AAT1290_LATCH_TIME_MAX_US); -} - -static void aat1290_set_flash_safety_timer(struct aat1290_led *led, - unsigned int micro_sec) -{ - struct led_classdev_flash *fled_cdev = &led->fled_cdev; - struct led_flash_setting *flash_tm = &fled_cdev->timeout; - int flash_tm_reg = AAT1290_FLASH_TM_NUM_LEVELS - - (micro_sec / flash_tm->step) + 1; - - aat1290_as2cwire_write(led, AAT1290_FLASH_SAFETY_TIMER_ADDR, - flash_tm_reg); -} - -/* LED subsystem callbacks */ - -static int aat1290_led_brightness_set(struct led_classdev *led_cdev, - enum led_brightness brightness) -{ - struct led_classdev_flash *fled_cdev = led_cdev_to_fled_cdev(led_cdev); - struct aat1290_led *led = fled_cdev_to_led(fled_cdev); - - mutex_lock(&led->lock); - - if (brightness == 0) { - gpiod_direction_output(led->gpio_fl_en, 0); - gpiod_direction_output(led->gpio_en_set, 0); - led->movie_mode = false; - } else { - if (!led->movie_mode) { - aat1290_as2cwire_write(led, - AAT1290_MM_CURRENT_RATIO_ADDR, - AAT1290_MM_TO_FL_1_92); - led->movie_mode = true; - } - - aat1290_as2cwire_write(led, AAT1290_MOVIE_MODE_CURRENT_ADDR, - AAT1290_MAX_MM_CURR_PERCENT_0 - brightness); - aat1290_as2cwire_write(led, AAT1290_MOVIE_MODE_CONFIG_ADDR, - AAT1290_MOVIE_MODE_ON); - } - - mutex_unlock(&led->lock); - - return 0; -} - -static int aat1290_led_flash_strobe_set(struct led_classdev_flash *fled_cdev, - bool state) - -{ - struct aat1290_led *led = fled_cdev_to_led(fled_cdev); - struct led_classdev *led_cdev = &fled_cdev->led_cdev; - struct led_flash_setting *timeout = &fled_cdev->timeout; - - mutex_lock(&led->lock); - - if (state) { - aat1290_set_flash_safety_timer(led, timeout->val); - gpiod_direction_output(led->gpio_fl_en, 1); - } else { - gpiod_direction_output(led->gpio_fl_en, 0); - gpiod_direction_output(led->gpio_en_set, 0); - } - - /* - * To reenter movie mode after a flash event the part must be cycled - * off and back on to reset the movie mode and reprogrammed via the - * AS2Cwire. Therefore the brightness and movie_mode properties needs - * to be updated here to reflect the actual state. - */ - led_cdev->brightness = 0; - led->movie_mode = false; - - mutex_unlock(&led->lock); - - return 0; -} - -static int aat1290_led_flash_timeout_set(struct led_classdev_flash *fled_cdev, - u32 timeout) -{ - /* - * Don't do anything - flash timeout is cached in the led-class-flash - * core and will be applied in the strobe_set op, as writing the - * safety timer register spuriously turns the torch mode on. - */ - - return 0; -} - -static int aat1290_led_parse_dt(struct aat1290_led *led, - struct aat1290_led_config_data *cfg, - struct device_node **sub_node) -{ - struct device *dev = &led->pdev->dev; - struct device_node *child_node; -#if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS) - struct pinctrl *pinctrl; -#endif - int ret = 0; - - led->gpio_fl_en = devm_gpiod_get(dev, "flen", GPIOD_ASIS); - if (IS_ERR(led->gpio_fl_en)) { - ret = PTR_ERR(led->gpio_fl_en); - dev_err(dev, "Unable to claim gpio \"flen\".\n"); - return ret; - } - - led->gpio_en_set = devm_gpiod_get(dev, "enset", GPIOD_ASIS); - if (IS_ERR(led->gpio_en_set)) { - ret = PTR_ERR(led->gpio_en_set); - dev_err(dev, "Unable to claim gpio \"enset\".\n"); - return ret; - } - -#if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS) - pinctrl = devm_pinctrl_get_select_default(&led->pdev->dev); - if (IS_ERR(pinctrl)) { - cfg->has_external_strobe = false; - dev_info(dev, - "No support for external strobe detected.\n"); - } else { - cfg->has_external_strobe = true; - } -#endif - - child_node = of_get_next_available_child(dev_of_node(dev), NULL); - if (!child_node) { - dev_err(dev, "No DT child node found for connected LED.\n"); - return -EINVAL; - } - - ret = of_property_read_u32(child_node, "led-max-microamp", - &cfg->max_mm_current); - /* - * led-max-microamp will default to 1/20 of flash-max-microamp - * in case it is missing. - */ - if (ret < 0) - dev_warn(dev, - "led-max-microamp DT property missing\n"); - - ret = of_property_read_u32(child_node, "flash-max-microamp", - &cfg->max_flash_current); - if (ret < 0) { - dev_err(dev, - "flash-max-microamp DT property missing\n"); - goto err_parse_dt; - } - - ret = of_property_read_u32(child_node, "flash-max-timeout-us", - &cfg->max_flash_tm); - if (ret < 0) { - dev_err(dev, - "flash-max-timeout-us DT property missing\n"); - goto err_parse_dt; - } - - *sub_node = child_node; - -err_parse_dt: - of_node_put(child_node); - - return ret; -} - -static void aat1290_led_validate_mm_current(struct aat1290_led *led, - struct aat1290_led_config_data *cfg) -{ - int i, b = 0, e = AAT1290_MM_CURRENT_SCALE_SIZE; - - while (e - b > 1) { - i = b + (e - b) / 2; - if (cfg->max_mm_current < led->mm_current_scale[i]) - e = i; - else - b = i; - } - - cfg->max_mm_current = led->mm_current_scale[b]; - cfg->max_brightness = b + 1; -} - -static int init_mm_current_scale(struct aat1290_led *led, - struct aat1290_led_config_data *cfg) -{ - static const int max_mm_current_percent[] = { - 20, 22, 25, 28, 32, 36, 40, 45, 50, 56, - 63, 71, 79, 89, 100 - }; - int i, max_mm_current = - AAT1290_MAX_MM_CURRENT(cfg->max_flash_current); - - led->mm_current_scale = devm_kzalloc(&led->pdev->dev, - sizeof(max_mm_current_percent), - GFP_KERNEL); - if (!led->mm_current_scale) - return -ENOMEM; - - for (i = 0; i < AAT1290_MM_CURRENT_SCALE_SIZE; ++i) - led->mm_current_scale[i] = max_mm_current * - max_mm_current_percent[i] / 100; - - return 0; -} - -static int aat1290_led_get_configuration(struct aat1290_led *led, - struct aat1290_led_config_data *cfg, - struct device_node **sub_node) -{ - int ret; - - ret = aat1290_led_parse_dt(led, cfg, sub_node); - if (ret < 0) - return ret; - /* - * Init non-linear movie mode current scale basing - * on the max flash current from led configuration. - */ - ret = init_mm_current_scale(led, cfg); - if (ret < 0) - return ret; - - aat1290_led_validate_mm_current(led, cfg); - -#if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS) -#else - devm_kfree(&led->pdev->dev, led->mm_current_scale); -#endif - - return 0; -} - -static void aat1290_init_flash_timeout(struct aat1290_led *led, - struct aat1290_led_config_data *cfg) -{ - struct led_classdev_flash *fled_cdev = &led->fled_cdev; - struct led_flash_setting *setting; - - /* Init flash timeout setting */ - setting = &fled_cdev->timeout; - setting->min = cfg->max_flash_tm / AAT1290_FLASH_TM_NUM_LEVELS; - setting->max = cfg->max_flash_tm; - setting->step = setting->min; - setting->val = setting->max; -} - -#if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS) -static enum led_brightness aat1290_intensity_to_brightness( - struct v4l2_flash *v4l2_flash, - s32 intensity) -{ - struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev; - struct aat1290_led *led = fled_cdev_to_led(fled_cdev); - int i; - - for (i = AAT1290_MM_CURRENT_SCALE_SIZE - 1; i >= 0; --i) - if (intensity >= led->mm_current_scale[i]) - return i + 1; - - return 1; -} - -static s32 aat1290_brightness_to_intensity(struct v4l2_flash *v4l2_flash, - enum led_brightness brightness) -{ - struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev; - struct aat1290_led *led = fled_cdev_to_led(fled_cdev); - - return led->mm_current_scale[brightness - 1]; -} - -static int aat1290_led_external_strobe_set(struct v4l2_flash *v4l2_flash, - bool enable) -{ - struct aat1290_led *led = fled_cdev_to_led(v4l2_flash->fled_cdev); - struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev; - struct led_classdev *led_cdev = &fled_cdev->led_cdev; - struct pinctrl *pinctrl; - - gpiod_direction_output(led->gpio_fl_en, 0); - gpiod_direction_output(led->gpio_en_set, 0); - - led->movie_mode = false; - led_cdev->brightness = 0; - - pinctrl = devm_pinctrl_get_select(&led->pdev->dev, - enable ? "isp" : "host"); - if (IS_ERR(pinctrl)) { - dev_warn(&led->pdev->dev, "Unable to switch strobe source.\n"); - return PTR_ERR(pinctrl); - } - - return 0; -} - -static void aat1290_init_v4l2_flash_config(struct aat1290_led *led, - struct aat1290_led_config_data *led_cfg, - struct v4l2_flash_config *v4l2_sd_cfg) -{ - struct led_classdev *led_cdev = &led->fled_cdev.led_cdev; - struct led_flash_setting *s; - - strlcpy(v4l2_sd_cfg->dev_name, led_cdev->dev->kobj.name, - sizeof(v4l2_sd_cfg->dev_name)); - - s = &v4l2_sd_cfg->intensity; - s->min = led->mm_current_scale[0]; - s->max = led_cfg->max_mm_current; - s->step = 1; - s->val = s->max; - - v4l2_sd_cfg->has_external_strobe = led_cfg->has_external_strobe; -} - -static const struct v4l2_flash_ops v4l2_flash_ops = { - .external_strobe_set = aat1290_led_external_strobe_set, - .intensity_to_led_brightness = aat1290_intensity_to_brightness, - .led_brightness_to_intensity = aat1290_brightness_to_intensity, -}; -#else -static inline void aat1290_init_v4l2_flash_config(struct aat1290_led *led, - struct aat1290_led_config_data *led_cfg, - struct v4l2_flash_config *v4l2_sd_cfg) -{ -} -static const struct v4l2_flash_ops v4l2_flash_ops; -#endif - -static const struct led_flash_ops flash_ops = { - .strobe_set = aat1290_led_flash_strobe_set, - .timeout_set = aat1290_led_flash_timeout_set, -}; - -static int aat1290_led_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct device_node *sub_node = NULL; - struct aat1290_led *led; - struct led_classdev *led_cdev; - struct led_classdev_flash *fled_cdev; - struct led_init_data init_data = {}; - struct aat1290_led_config_data led_cfg = {}; - struct v4l2_flash_config v4l2_sd_cfg = {}; - int ret; - - led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL); - if (!led) - return -ENOMEM; - - led->pdev = pdev; - platform_set_drvdata(pdev, led); - - fled_cdev = &led->fled_cdev; - fled_cdev->ops = &flash_ops; - led_cdev = &fled_cdev->led_cdev; - - ret = aat1290_led_get_configuration(led, &led_cfg, &sub_node); - if (ret < 0) - return ret; - - mutex_init(&led->lock); - - /* Initialize LED Flash class device */ - led_cdev->brightness_set_blocking = aat1290_led_brightness_set; - led_cdev->max_brightness = led_cfg.max_brightness; - led_cdev->flags |= LED_DEV_CAP_FLASH; - - aat1290_init_flash_timeout(led, &led_cfg); - - init_data.fwnode = of_fwnode_handle(sub_node); - init_data.devicename = AAT1290_NAME; - - /* Register LED Flash class device */ - ret = led_classdev_flash_register_ext(&pdev->dev, fled_cdev, - &init_data); - if (ret < 0) - goto err_flash_register; - - aat1290_init_v4l2_flash_config(led, &led_cfg, &v4l2_sd_cfg); - - /* Create V4L2 Flash subdev. */ - led->v4l2_flash = v4l2_flash_init(dev, of_fwnode_handle(sub_node), - fled_cdev, &v4l2_flash_ops, - &v4l2_sd_cfg); - if (IS_ERR(led->v4l2_flash)) { - ret = PTR_ERR(led->v4l2_flash); - goto error_v4l2_flash_init; - } - - return 0; - -error_v4l2_flash_init: - led_classdev_flash_unregister(fled_cdev); -err_flash_register: - mutex_destroy(&led->lock); - - return ret; -} - -static int aat1290_led_remove(struct platform_device *pdev) -{ - struct aat1290_led *led = platform_get_drvdata(pdev); - - v4l2_flash_release(led->v4l2_flash); - led_classdev_flash_unregister(&led->fled_cdev); - - mutex_destroy(&led->lock); - - return 0; -} - -static const struct of_device_id aat1290_led_dt_match[] = { - { .compatible = "skyworks,aat1290" }, - {}, -}; -MODULE_DEVICE_TABLE(of, aat1290_led_dt_match); - -static struct platform_driver aat1290_led_driver = { - .probe = aat1290_led_probe, - .remove = aat1290_led_remove, - .driver = { - .name = "aat1290", - .of_match_table = aat1290_led_dt_match, - }, -}; - -module_platform_driver(aat1290_led_driver); - -MODULE_AUTHOR("Jacek Anaszewski "); -MODULE_DESCRIPTION("Skyworks Current Regulator for Flash LEDs"); -MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From f5d69f6290a37f5579ef24334d5bf8a59bb13ef4 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 28 May 2021 22:24:00 +0200 Subject: leds: as3645a: Move driver to flash subdirectory We created a subdirectory for LED drivers that depend on CONFIG_LEDS_CLASS_FLASH, and this driver does so let's move it there. Signed-off-by: Linus Walleij Acked-by: Sakari Ailus Signed-off-by: Pavel Machek --- MAINTAINERS | 2 +- drivers/leds/Kconfig | 9 - drivers/leds/Makefile | 1 - drivers/leds/flash/Kconfig | 9 + drivers/leds/flash/Makefile | 1 + drivers/leds/flash/leds-as3645a.c | 774 ++++++++++++++++++++++++++++++++++++++ drivers/leds/leds-as3645a.c | 774 -------------------------------------- 7 files changed, 785 insertions(+), 785 deletions(-) create mode 100644 drivers/leds/flash/leds-as3645a.c delete mode 100644 drivers/leds/leds-as3645a.c diff --git a/MAINTAINERS b/MAINTAINERS index a61f4f3b78a9..7169a6bb8d91 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2841,7 +2841,7 @@ AS3645A LED FLASH CONTROLLER DRIVER M: Sakari Ailus L: linux-leds@vger.kernel.org S: Maintained -F: drivers/leds/leds-as3645a.c +F: drivers/leds/flash/leds-as3645a.c ASAHI KASEI AK7375 LENS VOICE COIL DRIVER M: Tianshu Qiu diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index a350d8fbb085..cfa6b8194b6b 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -94,15 +94,6 @@ config LEDS_ARIEL Say Y to if your machine is a Dell Wyse 3020 thin client. -config LEDS_AS3645A - tristate "AS3645A and LM3555 LED flash controllers support" - depends on I2C && LEDS_CLASS_FLASH - depends on V4L2_FLASH_LED_CLASS || !V4L2_FLASH_LED_CLASS - help - Enable LED flash class support for AS3645A LED flash - controller. V4L2 flash API is provided as well if - CONFIG_V4L2_FLASH_API is enabled. - config LEDS_AW2013 tristate "LED support for Awinic AW2013" depends on LEDS_CLASS && I2C && OF diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index c7231975837a..a3a6fda8ab99 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -14,7 +14,6 @@ obj-$(CONFIG_LEDS_ADP5520) += leds-adp5520.o obj-$(CONFIG_LEDS_AN30259A) += leds-an30259a.o obj-$(CONFIG_LEDS_APU) += leds-apu.o obj-$(CONFIG_LEDS_ARIEL) += leds-ariel.o -obj-$(CONFIG_LEDS_AS3645A) += leds-as3645a.o obj-$(CONFIG_LEDS_ASIC3) += leds-asic3.o obj-$(CONFIG_LEDS_AW2013) += leds-aw2013.o obj-$(CONFIG_LEDS_BCM6328) += leds-bcm6328.o diff --git a/drivers/leds/flash/Kconfig b/drivers/leds/flash/Kconfig index 736153b0bfd6..1126ad3954b6 100644 --- a/drivers/leds/flash/Kconfig +++ b/drivers/leds/flash/Kconfig @@ -11,6 +11,15 @@ config LEDS_AAT1290 help This option enables support for the LEDs on the AAT1290. +config LEDS_AS3645A + tristate "AS3645A and LM3555 LED flash controllers support" + depends on I2C + depends on V4L2_FLASH_LED_CLASS || !V4L2_FLASH_LED_CLASS + help + Enable LED flash class support for AS3645A LED flash + controller. V4L2 flash API is provided as well if + CONFIG_V4L2_FLASH_API is enabled. + config LEDS_RT4505 tristate "LED support for RT4505 flashlight controller" depends on I2C && OF diff --git a/drivers/leds/flash/Makefile b/drivers/leds/flash/Makefile index 11bec5881d51..9bb2fccee047 100644 --- a/drivers/leds/flash/Makefile +++ b/drivers/leds/flash/Makefile @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_LEDS_AAT1290) += leds-aat1290.o +obj-$(CONFIG_LEDS_AS3645A) += leds-as3645a.o obj-$(CONFIG_LEDS_RT4505) += leds-rt4505.o obj-$(CONFIG_LEDS_RT8515) += leds-rt8515.o diff --git a/drivers/leds/flash/leds-as3645a.c b/drivers/leds/flash/leds-as3645a.c new file mode 100644 index 000000000000..aa3f82be0a9c --- /dev/null +++ b/drivers/leds/flash/leds-as3645a.c @@ -0,0 +1,774 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * drivers/leds/leds-as3645a.c - AS3645A and LM3555 flash controllers driver + * + * Copyright (C) 2008-2011 Nokia Corporation + * Copyright (c) 2011, 2017 Intel Corporation. + * + * Based on drivers/media/i2c/as3645a.c. + * + * Contact: Sakari Ailus + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define AS_TIMER_US_TO_CODE(t) (((t) / 1000 - 100) / 50) +#define AS_TIMER_CODE_TO_US(c) ((50 * (c) + 100) * 1000) + +/* Register definitions */ + +/* Read-only Design info register: Reset state: xxxx 0001 */ +#define AS_DESIGN_INFO_REG 0x00 +#define AS_DESIGN_INFO_FACTORY(x) (((x) >> 4)) +#define AS_DESIGN_INFO_MODEL(x) ((x) & 0x0f) + +/* Read-only Version control register: Reset state: 0000 0000 + * for first engineering samples + */ +#define AS_VERSION_CONTROL_REG 0x01 +#define AS_VERSION_CONTROL_RFU(x) (((x) >> 4)) +#define AS_VERSION_CONTROL_VERSION(x) ((x) & 0x0f) + +/* Read / Write (Indicator and timer register): Reset state: 0000 1111 */ +#define AS_INDICATOR_AND_TIMER_REG 0x02 +#define AS_INDICATOR_AND_TIMER_TIMEOUT_SHIFT 0 +#define AS_INDICATOR_AND_TIMER_VREF_SHIFT 4 +#define AS_INDICATOR_AND_TIMER_INDICATOR_SHIFT 6 + +/* Read / Write (Current set register): Reset state: 0110 1001 */ +#define AS_CURRENT_SET_REG 0x03 +#define AS_CURRENT_ASSIST_LIGHT_SHIFT 0 +#define AS_CURRENT_LED_DET_ON (1 << 3) +#define AS_CURRENT_FLASH_CURRENT_SHIFT 4 + +/* Read / Write (Control register): Reset state: 1011 0100 */ +#define AS_CONTROL_REG 0x04 +#define AS_CONTROL_MODE_SETTING_SHIFT 0 +#define AS_CONTROL_STROBE_ON (1 << 2) +#define AS_CONTROL_OUT_ON (1 << 3) +#define AS_CONTROL_EXT_TORCH_ON (1 << 4) +#define AS_CONTROL_STROBE_TYPE_EDGE (0 << 5) +#define AS_CONTROL_STROBE_TYPE_LEVEL (1 << 5) +#define AS_CONTROL_COIL_PEAK_SHIFT 6 + +/* Read only (D3 is read / write) (Fault and info): Reset state: 0000 x000 */ +#define AS_FAULT_INFO_REG 0x05 +#define AS_FAULT_INFO_INDUCTOR_PEAK_LIMIT (1 << 1) +#define AS_FAULT_INFO_INDICATOR_LED (1 << 2) +#define AS_FAULT_INFO_LED_AMOUNT (1 << 3) +#define AS_FAULT_INFO_TIMEOUT (1 << 4) +#define AS_FAULT_INFO_OVER_TEMPERATURE (1 << 5) +#define AS_FAULT_INFO_SHORT_CIRCUIT (1 << 6) +#define AS_FAULT_INFO_OVER_VOLTAGE (1 << 7) + +/* Boost register */ +#define AS_BOOST_REG 0x0d +#define AS_BOOST_CURRENT_DISABLE (0 << 0) +#define AS_BOOST_CURRENT_ENABLE (1 << 0) + +/* Password register is used to unlock boost register writing */ +#define AS_PASSWORD_REG 0x0f +#define AS_PASSWORD_UNLOCK_VALUE 0x55 + +#define AS_NAME "as3645a" +#define AS_I2C_ADDR (0x60 >> 1) /* W:0x60, R:0x61 */ + +#define AS_FLASH_TIMEOUT_MIN 100000 /* us */ +#define AS_FLASH_TIMEOUT_MAX 850000 +#define AS_FLASH_TIMEOUT_STEP 50000 + +#define AS_FLASH_INTENSITY_MIN 200000 /* uA */ +#define AS_FLASH_INTENSITY_MAX_1LED 500000 +#define AS_FLASH_INTENSITY_MAX_2LEDS 400000 +#define AS_FLASH_INTENSITY_STEP 20000 + +#define AS_TORCH_INTENSITY_MIN 20000 /* uA */ +#define AS_TORCH_INTENSITY_MAX 160000 +#define AS_TORCH_INTENSITY_STEP 20000 + +#define AS_INDICATOR_INTENSITY_MIN 0 /* uA */ +#define AS_INDICATOR_INTENSITY_MAX 10000 +#define AS_INDICATOR_INTENSITY_STEP 2500 + +#define AS_PEAK_mA_MAX 2000 +#define AS_PEAK_mA_TO_REG(a) \ + ((min_t(u32, AS_PEAK_mA_MAX, a) - 1250) / 250) + +/* LED numbers for Devicetree */ +#define AS_LED_FLASH 0 +#define AS_LED_INDICATOR 1 + +enum as_mode { + AS_MODE_EXT_TORCH = 0 << AS_CONTROL_MODE_SETTING_SHIFT, + AS_MODE_INDICATOR = 1 << AS_CONTROL_MODE_SETTING_SHIFT, + AS_MODE_ASSIST = 2 << AS_CONTROL_MODE_SETTING_SHIFT, + AS_MODE_FLASH = 3 << AS_CONTROL_MODE_SETTING_SHIFT, +}; + +struct as3645a_config { + u32 flash_timeout_us; + u32 flash_max_ua; + u32 assist_max_ua; + u32 indicator_max_ua; + u32 voltage_reference; + u32 peak; +}; + +struct as3645a { + struct i2c_client *client; + + struct mutex mutex; + + struct led_classdev_flash fled; + struct led_classdev iled_cdev; + + struct v4l2_flash *vf; + struct v4l2_flash *vfind; + + struct fwnode_handle *flash_node; + struct fwnode_handle *indicator_node; + + struct as3645a_config cfg; + + enum as_mode mode; + unsigned int timeout; + unsigned int flash_current; + unsigned int assist_current; + unsigned int indicator_current; + enum v4l2_flash_strobe_source strobe_source; +}; + +#define fled_to_as3645a(__fled) container_of(__fled, struct as3645a, fled) +#define iled_cdev_to_as3645a(__iled_cdev) \ + container_of(__iled_cdev, struct as3645a, iled_cdev) + +/* Return negative errno else zero on success */ +static int as3645a_write(struct as3645a *flash, u8 addr, u8 val) +{ + struct i2c_client *client = flash->client; + int rval; + + rval = i2c_smbus_write_byte_data(client, addr, val); + + dev_dbg(&client->dev, "Write Addr:%02X Val:%02X %s\n", addr, val, + rval < 0 ? "fail" : "ok"); + + return rval; +} + +/* Return negative errno else a data byte received from the device. */ +static int as3645a_read(struct as3645a *flash, u8 addr) +{ + struct i2c_client *client = flash->client; + int rval; + + rval = i2c_smbus_read_byte_data(client, addr); + + dev_dbg(&client->dev, "Read Addr:%02X Val:%02X %s\n", addr, rval, + rval < 0 ? "fail" : "ok"); + + return rval; +} + +/* ----------------------------------------------------------------------------- + * Hardware configuration and trigger + */ + +/** + * as3645a_set_current - Set flash configuration registers + * @flash: The flash + * + * Configure the hardware with flash, assist and indicator currents, as well as + * flash timeout. + * + * Return 0 on success, or a negative error code if an I2C communication error + * occurred. + */ +static int as3645a_set_current(struct as3645a *flash) +{ + u8 val; + + val = (flash->flash_current << AS_CURRENT_FLASH_CURRENT_SHIFT) + | (flash->assist_current << AS_CURRENT_ASSIST_LIGHT_SHIFT) + | AS_CURRENT_LED_DET_ON; + + return as3645a_write(flash, AS_CURRENT_SET_REG, val); +} + +static int as3645a_set_timeout(struct as3645a *flash) +{ + u8 val; + + val = flash->timeout << AS_INDICATOR_AND_TIMER_TIMEOUT_SHIFT; + + val |= (flash->cfg.voltage_reference + << AS_INDICATOR_AND_TIMER_VREF_SHIFT) + | ((flash->indicator_current ? flash->indicator_current - 1 : 0) + << AS_INDICATOR_AND_TIMER_INDICATOR_SHIFT); + + return as3645a_write(flash, AS_INDICATOR_AND_TIMER_REG, val); +} + +/** + * as3645a_set_control - Set flash control register + * @flash: The flash + * @mode: Desired output mode + * @on: Desired output state + * + * Configure the hardware with output mode and state. + * + * Return 0 on success, or a negative error code if an I2C communication error + * occurred. + */ +static int +as3645a_set_control(struct as3645a *flash, enum as_mode mode, bool on) +{ + u8 reg; + + /* Configure output parameters and operation mode. */ + reg = (flash->cfg.peak << AS_CONTROL_COIL_PEAK_SHIFT) + | (on ? AS_CONTROL_OUT_ON : 0) + | mode; + + if (mode == AS_MODE_FLASH && + flash->strobe_source == V4L2_FLASH_STROBE_SOURCE_EXTERNAL) + reg |= AS_CONTROL_STROBE_TYPE_LEVEL + | AS_CONTROL_STROBE_ON; + + return as3645a_write(flash, AS_CONTROL_REG, reg); +} + +static int as3645a_get_fault(struct led_classdev_flash *fled, u32 *fault) +{ + struct as3645a *flash = fled_to_as3645a(fled); + int rval; + + /* NOTE: reading register clears fault status */ + rval = as3645a_read(flash, AS_FAULT_INFO_REG); + if (rval < 0) + return rval; + + if (rval & AS_FAULT_INFO_INDUCTOR_PEAK_LIMIT) + *fault |= LED_FAULT_OVER_CURRENT; + + if (rval & AS_FAULT_INFO_INDICATOR_LED) + *fault |= LED_FAULT_INDICATOR; + + dev_dbg(&flash->client->dev, "%u connected LEDs\n", + rval & AS_FAULT_INFO_LED_AMOUNT ? 2 : 1); + + if (rval & AS_FAULT_INFO_TIMEOUT) + *fault |= LED_FAULT_TIMEOUT; + + if (rval & AS_FAULT_INFO_OVER_TEMPERATURE) + *fault |= LED_FAULT_OVER_TEMPERATURE; + + if (rval & AS_FAULT_INFO_SHORT_CIRCUIT) + *fault |= LED_FAULT_OVER_CURRENT; + + if (rval & AS_FAULT_INFO_OVER_VOLTAGE) + *fault |= LED_FAULT_INPUT_VOLTAGE; + + return rval; +} + +static unsigned int __as3645a_current_to_reg(unsigned int min, unsigned int max, + unsigned int step, + unsigned int val) +{ + if (val < min) + val = min; + + if (val > max) + val = max; + + return (val - min) / step; +} + +static unsigned int as3645a_current_to_reg(struct as3645a *flash, bool is_flash, + unsigned int ua) +{ + if (is_flash) + return __as3645a_current_to_reg(AS_TORCH_INTENSITY_MIN, + flash->cfg.assist_max_ua, + AS_TORCH_INTENSITY_STEP, ua); + else + return __as3645a_current_to_reg(AS_FLASH_INTENSITY_MIN, + flash->cfg.flash_max_ua, + AS_FLASH_INTENSITY_STEP, ua); +} + +static int as3645a_set_indicator_brightness(struct led_classdev *iled_cdev, + enum led_brightness brightness) +{ + struct as3645a *flash = iled_cdev_to_as3645a(iled_cdev); + int rval; + + flash->indicator_current = brightness; + + rval = as3645a_set_timeout(flash); + if (rval) + return rval; + + return as3645a_set_control(flash, AS_MODE_INDICATOR, brightness); +} + +static int as3645a_set_assist_brightness(struct led_classdev *fled_cdev, + enum led_brightness brightness) +{ + struct led_classdev_flash *fled = lcdev_to_flcdev(fled_cdev); + struct as3645a *flash = fled_to_as3645a(fled); + int rval; + + if (brightness) { + /* Register value 0 is 20 mA. */ + flash->assist_current = brightness - 1; + + rval = as3645a_set_current(flash); + if (rval) + return rval; + } + + return as3645a_set_control(flash, AS_MODE_ASSIST, brightness); +} + +static int as3645a_set_flash_brightness(struct led_classdev_flash *fled, + u32 brightness_ua) +{ + struct as3645a *flash = fled_to_as3645a(fled); + + flash->flash_current = as3645a_current_to_reg(flash, true, + brightness_ua); + + return as3645a_set_current(flash); +} + +static int as3645a_set_flash_timeout(struct led_classdev_flash *fled, + u32 timeout_us) +{ + struct as3645a *flash = fled_to_as3645a(fled); + + flash->timeout = AS_TIMER_US_TO_CODE(timeout_us); + + return as3645a_set_timeout(flash); +} + +static int as3645a_set_strobe(struct led_classdev_flash *fled, bool state) +{ + struct as3645a *flash = fled_to_as3645a(fled); + + return as3645a_set_control(flash, AS_MODE_FLASH, state); +} + +static const struct led_flash_ops as3645a_led_flash_ops = { + .flash_brightness_set = as3645a_set_flash_brightness, + .timeout_set = as3645a_set_flash_timeout, + .strobe_set = as3645a_set_strobe, + .fault_get = as3645a_get_fault, +}; + +static int as3645a_setup(struct as3645a *flash) +{ + struct device *dev = &flash->client->dev; + u32 fault = 0; + int rval; + + /* clear errors */ + rval = as3645a_read(flash, AS_FAULT_INFO_REG); + if (rval < 0) + return rval; + + dev_dbg(dev, "Fault info: %02x\n", rval); + + rval = as3645a_set_current(flash); + if (rval < 0) + return rval; + + rval = as3645a_set_timeout(flash); + if (rval < 0) + return rval; + + rval = as3645a_set_control(flash, AS_MODE_INDICATOR, false); + if (rval < 0) + return rval; + + /* read status */ + rval = as3645a_get_fault(&flash->fled, &fault); + if (rval < 0) + return rval; + + dev_dbg(dev, "AS_INDICATOR_AND_TIMER_REG: %02x\n", + as3645a_read(flash, AS_INDICATOR_AND_TIMER_REG)); + dev_dbg(dev, "AS_CURRENT_SET_REG: %02x\n", + as3645a_read(flash, AS_CURRENT_SET_REG)); + dev_dbg(dev, "AS_CONTROL_REG: %02x\n", + as3645a_read(flash, AS_CONTROL_REG)); + + return rval & ~AS_FAULT_INFO_LED_AMOUNT ? -EIO : 0; +} + +static int as3645a_detect(struct as3645a *flash) +{ + struct device *dev = &flash->client->dev; + int rval, man, model, rfu, version; + const char *vendor; + + rval = as3645a_read(flash, AS_DESIGN_INFO_REG); + if (rval < 0) { + dev_err(dev, "can't read design info reg\n"); + return rval; + } + + man = AS_DESIGN_INFO_FACTORY(rval); + model = AS_DESIGN_INFO_MODEL(rval); + + rval = as3645a_read(flash, AS_VERSION_CONTROL_REG); + if (rval < 0) { + dev_err(dev, "can't read version control reg\n"); + return rval; + } + + rfu = AS_VERSION_CONTROL_RFU(rval); + version = AS_VERSION_CONTROL_VERSION(rval); + + /* Verify the chip model and version. */ + if (model != 0x01 || rfu != 0x00) { + dev_err(dev, "AS3645A not detected (model %d rfu %d)\n", + model, rfu); + return -ENODEV; + } + + switch (man) { + case 1: + vendor = "AMS, Austria Micro Systems"; + break; + case 2: + vendor = "ADI, Analog Devices Inc."; + break; + case 3: + vendor = "NSC, National Semiconductor"; + break; + case 4: + vendor = "NXP"; + break; + case 5: + vendor = "TI, Texas Instrument"; + break; + default: + vendor = "Unknown"; + } + + dev_info(dev, "Chip vendor: %s (%d) Version: %d\n", vendor, + man, version); + + rval = as3645a_write(flash, AS_PASSWORD_REG, AS_PASSWORD_UNLOCK_VALUE); + if (rval < 0) + return rval; + + return as3645a_write(flash, AS_BOOST_REG, AS_BOOST_CURRENT_DISABLE); +} + +static int as3645a_parse_node(struct as3645a *flash, + struct fwnode_handle *fwnode) +{ + struct as3645a_config *cfg = &flash->cfg; + struct fwnode_handle *child; + int rval; + + fwnode_for_each_child_node(fwnode, child) { + u32 id = 0; + + fwnode_property_read_u32(child, "reg", &id); + + switch (id) { + case AS_LED_FLASH: + flash->flash_node = child; + fwnode_handle_get(child); + break; + case AS_LED_INDICATOR: + flash->indicator_node = child; + fwnode_handle_get(child); + break; + default: + dev_warn(&flash->client->dev, + "unknown LED %u encountered, ignoring\n", id); + break; + } + } + + if (!flash->flash_node) { + dev_err(&flash->client->dev, "can't find flash node\n"); + return -ENODEV; + } + + rval = fwnode_property_read_u32(flash->flash_node, "flash-timeout-us", + &cfg->flash_timeout_us); + if (rval < 0) { + dev_err(&flash->client->dev, + "can't read flash-timeout-us property for flash\n"); + goto out_err; + } + + rval = fwnode_property_read_u32(flash->flash_node, "flash-max-microamp", + &cfg->flash_max_ua); + if (rval < 0) { + dev_err(&flash->client->dev, + "can't read flash-max-microamp property for flash\n"); + goto out_err; + } + + rval = fwnode_property_read_u32(flash->flash_node, "led-max-microamp", + &cfg->assist_max_ua); + if (rval < 0) { + dev_err(&flash->client->dev, + "can't read led-max-microamp property for flash\n"); + goto out_err; + } + + fwnode_property_read_u32(flash->flash_node, "voltage-reference", + &cfg->voltage_reference); + + fwnode_property_read_u32(flash->flash_node, "ams,input-max-microamp", + &cfg->peak); + cfg->peak = AS_PEAK_mA_TO_REG(cfg->peak); + + if (!flash->indicator_node) { + dev_warn(&flash->client->dev, + "can't find indicator node\n"); + rval = -ENODEV; + goto out_err; + } + + + rval = fwnode_property_read_u32(flash->indicator_node, + "led-max-microamp", + &cfg->indicator_max_ua); + if (rval < 0) { + dev_err(&flash->client->dev, + "can't read led-max-microamp property for indicator\n"); + goto out_err; + } + + return 0; + +out_err: + fwnode_handle_put(flash->flash_node); + fwnode_handle_put(flash->indicator_node); + + return rval; +} + +static int as3645a_led_class_setup(struct as3645a *flash) +{ + struct led_classdev *fled_cdev = &flash->fled.led_cdev; + struct led_classdev *iled_cdev = &flash->iled_cdev; + struct led_init_data init_data = {}; + struct led_flash_setting *cfg; + int rval; + + iled_cdev->brightness_set_blocking = as3645a_set_indicator_brightness; + iled_cdev->max_brightness = + flash->cfg.indicator_max_ua / AS_INDICATOR_INTENSITY_STEP; + iled_cdev->flags = LED_CORE_SUSPENDRESUME; + + init_data.fwnode = flash->indicator_node; + init_data.devicename = AS_NAME; + init_data.default_label = "indicator"; + + rval = led_classdev_register_ext(&flash->client->dev, iled_cdev, + &init_data); + if (rval < 0) + return rval; + + cfg = &flash->fled.brightness; + cfg->min = AS_FLASH_INTENSITY_MIN; + cfg->max = flash->cfg.flash_max_ua; + cfg->step = AS_FLASH_INTENSITY_STEP; + cfg->val = flash->cfg.flash_max_ua; + + cfg = &flash->fled.timeout; + cfg->min = AS_FLASH_TIMEOUT_MIN; + cfg->max = flash->cfg.flash_timeout_us; + cfg->step = AS_FLASH_TIMEOUT_STEP; + cfg->val = flash->cfg.flash_timeout_us; + + flash->fled.ops = &as3645a_led_flash_ops; + + fled_cdev->brightness_set_blocking = as3645a_set_assist_brightness; + /* Value 0 is off in LED class. */ + fled_cdev->max_brightness = + as3645a_current_to_reg(flash, false, + flash->cfg.assist_max_ua) + 1; + fled_cdev->flags = LED_DEV_CAP_FLASH | LED_CORE_SUSPENDRESUME; + + init_data.fwnode = flash->flash_node; + init_data.devicename = AS_NAME; + init_data.default_label = "flash"; + + rval = led_classdev_flash_register_ext(&flash->client->dev, + &flash->fled, &init_data); + if (rval) + goto out_err; + + return rval; + +out_err: + led_classdev_unregister(iled_cdev); + dev_err(&flash->client->dev, + "led_classdev_flash_register() failed, error %d\n", + rval); + return rval; +} + +static int as3645a_v4l2_setup(struct as3645a *flash) +{ + struct led_classdev_flash *fled = &flash->fled; + struct led_classdev *led = &fled->led_cdev; + struct v4l2_flash_config cfg = { + .intensity = { + .min = AS_TORCH_INTENSITY_MIN, + .max = flash->cfg.assist_max_ua, + .step = AS_TORCH_INTENSITY_STEP, + .val = flash->cfg.assist_max_ua, + }, + }; + struct v4l2_flash_config cfgind = { + .intensity = { + .min = AS_INDICATOR_INTENSITY_MIN, + .max = flash->cfg.indicator_max_ua, + .step = AS_INDICATOR_INTENSITY_STEP, + .val = flash->cfg.indicator_max_ua, + }, + }; + + strlcpy(cfg.dev_name, led->dev->kobj.name, sizeof(cfg.dev_name)); + strlcpy(cfgind.dev_name, flash->iled_cdev.dev->kobj.name, + sizeof(cfgind.dev_name)); + + flash->vf = v4l2_flash_init( + &flash->client->dev, flash->flash_node, &flash->fled, NULL, + &cfg); + if (IS_ERR(flash->vf)) + return PTR_ERR(flash->vf); + + flash->vfind = v4l2_flash_indicator_init( + &flash->client->dev, flash->indicator_node, &flash->iled_cdev, + &cfgind); + if (IS_ERR(flash->vfind)) { + v4l2_flash_release(flash->vf); + return PTR_ERR(flash->vfind); + } + + return 0; +} + +static int as3645a_probe(struct i2c_client *client) +{ + struct as3645a *flash; + int rval; + + if (!dev_fwnode(&client->dev)) + return -ENODEV; + + flash = devm_kzalloc(&client->dev, sizeof(*flash), GFP_KERNEL); + if (flash == NULL) + return -ENOMEM; + + flash->client = client; + + rval = as3645a_parse_node(flash, dev_fwnode(&client->dev)); + if (rval < 0) + return rval; + + rval = as3645a_detect(flash); + if (rval < 0) + goto out_put_nodes; + + mutex_init(&flash->mutex); + i2c_set_clientdata(client, flash); + + rval = as3645a_setup(flash); + if (rval) + goto out_mutex_destroy; + + rval = as3645a_led_class_setup(flash); + if (rval) + goto out_mutex_destroy; + + rval = as3645a_v4l2_setup(flash); + if (rval) + goto out_led_classdev_flash_unregister; + + return 0; + +out_led_classdev_flash_unregister: + led_classdev_flash_unregister(&flash->fled); + +out_mutex_destroy: + mutex_destroy(&flash->mutex); + +out_put_nodes: + fwnode_handle_put(flash->flash_node); + fwnode_handle_put(flash->indicator_node); + + return rval; +} + +static int as3645a_remove(struct i2c_client *client) +{ + struct as3645a *flash = i2c_get_clientdata(client); + + as3645a_set_control(flash, AS_MODE_EXT_TORCH, false); + + v4l2_flash_release(flash->vf); + v4l2_flash_release(flash->vfind); + + led_classdev_flash_unregister(&flash->fled); + led_classdev_unregister(&flash->iled_cdev); + + mutex_destroy(&flash->mutex); + + fwnode_handle_put(flash->flash_node); + fwnode_handle_put(flash->indicator_node); + + return 0; +} + +static const struct i2c_device_id as3645a_id_table[] = { + { AS_NAME, 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, as3645a_id_table); + +static const struct of_device_id as3645a_of_table[] = { + { .compatible = "ams,as3645a" }, + { }, +}; +MODULE_DEVICE_TABLE(of, as3645a_of_table); + +static struct i2c_driver as3645a_i2c_driver = { + .driver = { + .of_match_table = as3645a_of_table, + .name = AS_NAME, + }, + .probe_new = as3645a_probe, + .remove = as3645a_remove, + .id_table = as3645a_id_table, +}; + +module_i2c_driver(as3645a_i2c_driver); + +MODULE_AUTHOR("Laurent Pinchart "); +MODULE_AUTHOR("Sakari Ailus "); +MODULE_DESCRIPTION("LED flash driver for AS3645A, LM3555 and their clones"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/leds/leds-as3645a.c b/drivers/leds/leds-as3645a.c deleted file mode 100644 index aa3f82be0a9c..000000000000 --- a/drivers/leds/leds-as3645a.c +++ /dev/null @@ -1,774 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * drivers/leds/leds-as3645a.c - AS3645A and LM3555 flash controllers driver - * - * Copyright (C) 2008-2011 Nokia Corporation - * Copyright (c) 2011, 2017 Intel Corporation. - * - * Based on drivers/media/i2c/as3645a.c. - * - * Contact: Sakari Ailus - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#define AS_TIMER_US_TO_CODE(t) (((t) / 1000 - 100) / 50) -#define AS_TIMER_CODE_TO_US(c) ((50 * (c) + 100) * 1000) - -/* Register definitions */ - -/* Read-only Design info register: Reset state: xxxx 0001 */ -#define AS_DESIGN_INFO_REG 0x00 -#define AS_DESIGN_INFO_FACTORY(x) (((x) >> 4)) -#define AS_DESIGN_INFO_MODEL(x) ((x) & 0x0f) - -/* Read-only Version control register: Reset state: 0000 0000 - * for first engineering samples - */ -#define AS_VERSION_CONTROL_REG 0x01 -#define AS_VERSION_CONTROL_RFU(x) (((x) >> 4)) -#define AS_VERSION_CONTROL_VERSION(x) ((x) & 0x0f) - -/* Read / Write (Indicator and timer register): Reset state: 0000 1111 */ -#define AS_INDICATOR_AND_TIMER_REG 0x02 -#define AS_INDICATOR_AND_TIMER_TIMEOUT_SHIFT 0 -#define AS_INDICATOR_AND_TIMER_VREF_SHIFT 4 -#define AS_INDICATOR_AND_TIMER_INDICATOR_SHIFT 6 - -/* Read / Write (Current set register): Reset state: 0110 1001 */ -#define AS_CURRENT_SET_REG 0x03 -#define AS_CURRENT_ASSIST_LIGHT_SHIFT 0 -#define AS_CURRENT_LED_DET_ON (1 << 3) -#define AS_CURRENT_FLASH_CURRENT_SHIFT 4 - -/* Read / Write (Control register): Reset state: 1011 0100 */ -#define AS_CONTROL_REG 0x04 -#define AS_CONTROL_MODE_SETTING_SHIFT 0 -#define AS_CONTROL_STROBE_ON (1 << 2) -#define AS_CONTROL_OUT_ON (1 << 3) -#define AS_CONTROL_EXT_TORCH_ON (1 << 4) -#define AS_CONTROL_STROBE_TYPE_EDGE (0 << 5) -#define AS_CONTROL_STROBE_TYPE_LEVEL (1 << 5) -#define AS_CONTROL_COIL_PEAK_SHIFT 6 - -/* Read only (D3 is read / write) (Fault and info): Reset state: 0000 x000 */ -#define AS_FAULT_INFO_REG 0x05 -#define AS_FAULT_INFO_INDUCTOR_PEAK_LIMIT (1 << 1) -#define AS_FAULT_INFO_INDICATOR_LED (1 << 2) -#define AS_FAULT_INFO_LED_AMOUNT (1 << 3) -#define AS_FAULT_INFO_TIMEOUT (1 << 4) -#define AS_FAULT_INFO_OVER_TEMPERATURE (1 << 5) -#define AS_FAULT_INFO_SHORT_CIRCUIT (1 << 6) -#define AS_FAULT_INFO_OVER_VOLTAGE (1 << 7) - -/* Boost register */ -#define AS_BOOST_REG 0x0d -#define AS_BOOST_CURRENT_DISABLE (0 << 0) -#define AS_BOOST_CURRENT_ENABLE (1 << 0) - -/* Password register is used to unlock boost register writing */ -#define AS_PASSWORD_REG 0x0f -#define AS_PASSWORD_UNLOCK_VALUE 0x55 - -#define AS_NAME "as3645a" -#define AS_I2C_ADDR (0x60 >> 1) /* W:0x60, R:0x61 */ - -#define AS_FLASH_TIMEOUT_MIN 100000 /* us */ -#define AS_FLASH_TIMEOUT_MAX 850000 -#define AS_FLASH_TIMEOUT_STEP 50000 - -#define AS_FLASH_INTENSITY_MIN 200000 /* uA */ -#define AS_FLASH_INTENSITY_MAX_1LED 500000 -#define AS_FLASH_INTENSITY_MAX_2LEDS 400000 -#define AS_FLASH_INTENSITY_STEP 20000 - -#define AS_TORCH_INTENSITY_MIN 20000 /* uA */ -#define AS_TORCH_INTENSITY_MAX 160000 -#define AS_TORCH_INTENSITY_STEP 20000 - -#define AS_INDICATOR_INTENSITY_MIN 0 /* uA */ -#define AS_INDICATOR_INTENSITY_MAX 10000 -#define AS_INDICATOR_INTENSITY_STEP 2500 - -#define AS_PEAK_mA_MAX 2000 -#define AS_PEAK_mA_TO_REG(a) \ - ((min_t(u32, AS_PEAK_mA_MAX, a) - 1250) / 250) - -/* LED numbers for Devicetree */ -#define AS_LED_FLASH 0 -#define AS_LED_INDICATOR 1 - -enum as_mode { - AS_MODE_EXT_TORCH = 0 << AS_CONTROL_MODE_SETTING_SHIFT, - AS_MODE_INDICATOR = 1 << AS_CONTROL_MODE_SETTING_SHIFT, - AS_MODE_ASSIST = 2 << AS_CONTROL_MODE_SETTING_SHIFT, - AS_MODE_FLASH = 3 << AS_CONTROL_MODE_SETTING_SHIFT, -}; - -struct as3645a_config { - u32 flash_timeout_us; - u32 flash_max_ua; - u32 assist_max_ua; - u32 indicator_max_ua; - u32 voltage_reference; - u32 peak; -}; - -struct as3645a { - struct i2c_client *client; - - struct mutex mutex; - - struct led_classdev_flash fled; - struct led_classdev iled_cdev; - - struct v4l2_flash *vf; - struct v4l2_flash *vfind; - - struct fwnode_handle *flash_node; - struct fwnode_handle *indicator_node; - - struct as3645a_config cfg; - - enum as_mode mode; - unsigned int timeout; - unsigned int flash_current; - unsigned int assist_current; - unsigned int indicator_current; - enum v4l2_flash_strobe_source strobe_source; -}; - -#define fled_to_as3645a(__fled) container_of(__fled, struct as3645a, fled) -#define iled_cdev_to_as3645a(__iled_cdev) \ - container_of(__iled_cdev, struct as3645a, iled_cdev) - -/* Return negative errno else zero on success */ -static int as3645a_write(struct as3645a *flash, u8 addr, u8 val) -{ - struct i2c_client *client = flash->client; - int rval; - - rval = i2c_smbus_write_byte_data(client, addr, val); - - dev_dbg(&client->dev, "Write Addr:%02X Val:%02X %s\n", addr, val, - rval < 0 ? "fail" : "ok"); - - return rval; -} - -/* Return negative errno else a data byte received from the device. */ -static int as3645a_read(struct as3645a *flash, u8 addr) -{ - struct i2c_client *client = flash->client; - int rval; - - rval = i2c_smbus_read_byte_data(client, addr); - - dev_dbg(&client->dev, "Read Addr:%02X Val:%02X %s\n", addr, rval, - rval < 0 ? "fail" : "ok"); - - return rval; -} - -/* ----------------------------------------------------------------------------- - * Hardware configuration and trigger - */ - -/** - * as3645a_set_current - Set flash configuration registers - * @flash: The flash - * - * Configure the hardware with flash, assist and indicator currents, as well as - * flash timeout. - * - * Return 0 on success, or a negative error code if an I2C communication error - * occurred. - */ -static int as3645a_set_current(struct as3645a *flash) -{ - u8 val; - - val = (flash->flash_current << AS_CURRENT_FLASH_CURRENT_SHIFT) - | (flash->assist_current << AS_CURRENT_ASSIST_LIGHT_SHIFT) - | AS_CURRENT_LED_DET_ON; - - return as3645a_write(flash, AS_CURRENT_SET_REG, val); -} - -static int as3645a_set_timeout(struct as3645a *flash) -{ - u8 val; - - val = flash->timeout << AS_INDICATOR_AND_TIMER_TIMEOUT_SHIFT; - - val |= (flash->cfg.voltage_reference - << AS_INDICATOR_AND_TIMER_VREF_SHIFT) - | ((flash->indicator_current ? flash->indicator_current - 1 : 0) - << AS_INDICATOR_AND_TIMER_INDICATOR_SHIFT); - - return as3645a_write(flash, AS_INDICATOR_AND_TIMER_REG, val); -} - -/** - * as3645a_set_control - Set flash control register - * @flash: The flash - * @mode: Desired output mode - * @on: Desired output state - * - * Configure the hardware with output mode and state. - * - * Return 0 on success, or a negative error code if an I2C communication error - * occurred. - */ -static int -as3645a_set_control(struct as3645a *flash, enum as_mode mode, bool on) -{ - u8 reg; - - /* Configure output parameters and operation mode. */ - reg = (flash->cfg.peak << AS_CONTROL_COIL_PEAK_SHIFT) - | (on ? AS_CONTROL_OUT_ON : 0) - | mode; - - if (mode == AS_MODE_FLASH && - flash->strobe_source == V4L2_FLASH_STROBE_SOURCE_EXTERNAL) - reg |= AS_CONTROL_STROBE_TYPE_LEVEL - | AS_CONTROL_STROBE_ON; - - return as3645a_write(flash, AS_CONTROL_REG, reg); -} - -static int as3645a_get_fault(struct led_classdev_flash *fled, u32 *fault) -{ - struct as3645a *flash = fled_to_as3645a(fled); - int rval; - - /* NOTE: reading register clears fault status */ - rval = as3645a_read(flash, AS_FAULT_INFO_REG); - if (rval < 0) - return rval; - - if (rval & AS_FAULT_INFO_INDUCTOR_PEAK_LIMIT) - *fault |= LED_FAULT_OVER_CURRENT; - - if (rval & AS_FAULT_INFO_INDICATOR_LED) - *fault |= LED_FAULT_INDICATOR; - - dev_dbg(&flash->client->dev, "%u connected LEDs\n", - rval & AS_FAULT_INFO_LED_AMOUNT ? 2 : 1); - - if (rval & AS_FAULT_INFO_TIMEOUT) - *fault |= LED_FAULT_TIMEOUT; - - if (rval & AS_FAULT_INFO_OVER_TEMPERATURE) - *fault |= LED_FAULT_OVER_TEMPERATURE; - - if (rval & AS_FAULT_INFO_SHORT_CIRCUIT) - *fault |= LED_FAULT_OVER_CURRENT; - - if (rval & AS_FAULT_INFO_OVER_VOLTAGE) - *fault |= LED_FAULT_INPUT_VOLTAGE; - - return rval; -} - -static unsigned int __as3645a_current_to_reg(unsigned int min, unsigned int max, - unsigned int step, - unsigned int val) -{ - if (val < min) - val = min; - - if (val > max) - val = max; - - return (val - min) / step; -} - -static unsigned int as3645a_current_to_reg(struct as3645a *flash, bool is_flash, - unsigned int ua) -{ - if (is_flash) - return __as3645a_current_to_reg(AS_TORCH_INTENSITY_MIN, - flash->cfg.assist_max_ua, - AS_TORCH_INTENSITY_STEP, ua); - else - return __as3645a_current_to_reg(AS_FLASH_INTENSITY_MIN, - flash->cfg.flash_max_ua, - AS_FLASH_INTENSITY_STEP, ua); -} - -static int as3645a_set_indicator_brightness(struct led_classdev *iled_cdev, - enum led_brightness brightness) -{ - struct as3645a *flash = iled_cdev_to_as3645a(iled_cdev); - int rval; - - flash->indicator_current = brightness; - - rval = as3645a_set_timeout(flash); - if (rval) - return rval; - - return as3645a_set_control(flash, AS_MODE_INDICATOR, brightness); -} - -static int as3645a_set_assist_brightness(struct led_classdev *fled_cdev, - enum led_brightness brightness) -{ - struct led_classdev_flash *fled = lcdev_to_flcdev(fled_cdev); - struct as3645a *flash = fled_to_as3645a(fled); - int rval; - - if (brightness) { - /* Register value 0 is 20 mA. */ - flash->assist_current = brightness - 1; - - rval = as3645a_set_current(flash); - if (rval) - return rval; - } - - return as3645a_set_control(flash, AS_MODE_ASSIST, brightness); -} - -static int as3645a_set_flash_brightness(struct led_classdev_flash *fled, - u32 brightness_ua) -{ - struct as3645a *flash = fled_to_as3645a(fled); - - flash->flash_current = as3645a_current_to_reg(flash, true, - brightness_ua); - - return as3645a_set_current(flash); -} - -static int as3645a_set_flash_timeout(struct led_classdev_flash *fled, - u32 timeout_us) -{ - struct as3645a *flash = fled_to_as3645a(fled); - - flash->timeout = AS_TIMER_US_TO_CODE(timeout_us); - - return as3645a_set_timeout(flash); -} - -static int as3645a_set_strobe(struct led_classdev_flash *fled, bool state) -{ - struct as3645a *flash = fled_to_as3645a(fled); - - return as3645a_set_control(flash, AS_MODE_FLASH, state); -} - -static const struct led_flash_ops as3645a_led_flash_ops = { - .flash_brightness_set = as3645a_set_flash_brightness, - .timeout_set = as3645a_set_flash_timeout, - .strobe_set = as3645a_set_strobe, - .fault_get = as3645a_get_fault, -}; - -static int as3645a_setup(struct as3645a *flash) -{ - struct device *dev = &flash->client->dev; - u32 fault = 0; - int rval; - - /* clear errors */ - rval = as3645a_read(flash, AS_FAULT_INFO_REG); - if (rval < 0) - return rval; - - dev_dbg(dev, "Fault info: %02x\n", rval); - - rval = as3645a_set_current(flash); - if (rval < 0) - return rval; - - rval = as3645a_set_timeout(flash); - if (rval < 0) - return rval; - - rval = as3645a_set_control(flash, AS_MODE_INDICATOR, false); - if (rval < 0) - return rval; - - /* read status */ - rval = as3645a_get_fault(&flash->fled, &fault); - if (rval < 0) - return rval; - - dev_dbg(dev, "AS_INDICATOR_AND_TIMER_REG: %02x\n", - as3645a_read(flash, AS_INDICATOR_AND_TIMER_REG)); - dev_dbg(dev, "AS_CURRENT_SET_REG: %02x\n", - as3645a_read(flash, AS_CURRENT_SET_REG)); - dev_dbg(dev, "AS_CONTROL_REG: %02x\n", - as3645a_read(flash, AS_CONTROL_REG)); - - return rval & ~AS_FAULT_INFO_LED_AMOUNT ? -EIO : 0; -} - -static int as3645a_detect(struct as3645a *flash) -{ - struct device *dev = &flash->client->dev; - int rval, man, model, rfu, version; - const char *vendor; - - rval = as3645a_read(flash, AS_DESIGN_INFO_REG); - if (rval < 0) { - dev_err(dev, "can't read design info reg\n"); - return rval; - } - - man = AS_DESIGN_INFO_FACTORY(rval); - model = AS_DESIGN_INFO_MODEL(rval); - - rval = as3645a_read(flash, AS_VERSION_CONTROL_REG); - if (rval < 0) { - dev_err(dev, "can't read version control reg\n"); - return rval; - } - - rfu = AS_VERSION_CONTROL_RFU(rval); - version = AS_VERSION_CONTROL_VERSION(rval); - - /* Verify the chip model and version. */ - if (model != 0x01 || rfu != 0x00) { - dev_err(dev, "AS3645A not detected (model %d rfu %d)\n", - model, rfu); - return -ENODEV; - } - - switch (man) { - case 1: - vendor = "AMS, Austria Micro Systems"; - break; - case 2: - vendor = "ADI, Analog Devices Inc."; - break; - case 3: - vendor = "NSC, National Semiconductor"; - break; - case 4: - vendor = "NXP"; - break; - case 5: - vendor = "TI, Texas Instrument"; - break; - default: - vendor = "Unknown"; - } - - dev_info(dev, "Chip vendor: %s (%d) Version: %d\n", vendor, - man, version); - - rval = as3645a_write(flash, AS_PASSWORD_REG, AS_PASSWORD_UNLOCK_VALUE); - if (rval < 0) - return rval; - - return as3645a_write(flash, AS_BOOST_REG, AS_BOOST_CURRENT_DISABLE); -} - -static int as3645a_parse_node(struct as3645a *flash, - struct fwnode_handle *fwnode) -{ - struct as3645a_config *cfg = &flash->cfg; - struct fwnode_handle *child; - int rval; - - fwnode_for_each_child_node(fwnode, child) { - u32 id = 0; - - fwnode_property_read_u32(child, "reg", &id); - - switch (id) { - case AS_LED_FLASH: - flash->flash_node = child; - fwnode_handle_get(child); - break; - case AS_LED_INDICATOR: - flash->indicator_node = child; - fwnode_handle_get(child); - break; - default: - dev_warn(&flash->client->dev, - "unknown LED %u encountered, ignoring\n", id); - break; - } - } - - if (!flash->flash_node) { - dev_err(&flash->client->dev, "can't find flash node\n"); - return -ENODEV; - } - - rval = fwnode_property_read_u32(flash->flash_node, "flash-timeout-us", - &cfg->flash_timeout_us); - if (rval < 0) { - dev_err(&flash->client->dev, - "can't read flash-timeout-us property for flash\n"); - goto out_err; - } - - rval = fwnode_property_read_u32(flash->flash_node, "flash-max-microamp", - &cfg->flash_max_ua); - if (rval < 0) { - dev_err(&flash->client->dev, - "can't read flash-max-microamp property for flash\n"); - goto out_err; - } - - rval = fwnode_property_read_u32(flash->flash_node, "led-max-microamp", - &cfg->assist_max_ua); - if (rval < 0) { - dev_err(&flash->client->dev, - "can't read led-max-microamp property for flash\n"); - goto out_err; - } - - fwnode_property_read_u32(flash->flash_node, "voltage-reference", - &cfg->voltage_reference); - - fwnode_property_read_u32(flash->flash_node, "ams,input-max-microamp", - &cfg->peak); - cfg->peak = AS_PEAK_mA_TO_REG(cfg->peak); - - if (!flash->indicator_node) { - dev_warn(&flash->client->dev, - "can't find indicator node\n"); - rval = -ENODEV; - goto out_err; - } - - - rval = fwnode_property_read_u32(flash->indicator_node, - "led-max-microamp", - &cfg->indicator_max_ua); - if (rval < 0) { - dev_err(&flash->client->dev, - "can't read led-max-microamp property for indicator\n"); - goto out_err; - } - - return 0; - -out_err: - fwnode_handle_put(flash->flash_node); - fwnode_handle_put(flash->indicator_node); - - return rval; -} - -static int as3645a_led_class_setup(struct as3645a *flash) -{ - struct led_classdev *fled_cdev = &flash->fled.led_cdev; - struct led_classdev *iled_cdev = &flash->iled_cdev; - struct led_init_data init_data = {}; - struct led_flash_setting *cfg; - int rval; - - iled_cdev->brightness_set_blocking = as3645a_set_indicator_brightness; - iled_cdev->max_brightness = - flash->cfg.indicator_max_ua / AS_INDICATOR_INTENSITY_STEP; - iled_cdev->flags = LED_CORE_SUSPENDRESUME; - - init_data.fwnode = flash->indicator_node; - init_data.devicename = AS_NAME; - init_data.default_label = "indicator"; - - rval = led_classdev_register_ext(&flash->client->dev, iled_cdev, - &init_data); - if (rval < 0) - return rval; - - cfg = &flash->fled.brightness; - cfg->min = AS_FLASH_INTENSITY_MIN; - cfg->max = flash->cfg.flash_max_ua; - cfg->step = AS_FLASH_INTENSITY_STEP; - cfg->val = flash->cfg.flash_max_ua; - - cfg = &flash->fled.timeout; - cfg->min = AS_FLASH_TIMEOUT_MIN; - cfg->max = flash->cfg.flash_timeout_us; - cfg->step = AS_FLASH_TIMEOUT_STEP; - cfg->val = flash->cfg.flash_timeout_us; - - flash->fled.ops = &as3645a_led_flash_ops; - - fled_cdev->brightness_set_blocking = as3645a_set_assist_brightness; - /* Value 0 is off in LED class. */ - fled_cdev->max_brightness = - as3645a_current_to_reg(flash, false, - flash->cfg.assist_max_ua) + 1; - fled_cdev->flags = LED_DEV_CAP_FLASH | LED_CORE_SUSPENDRESUME; - - init_data.fwnode = flash->flash_node; - init_data.devicename = AS_NAME; - init_data.default_label = "flash"; - - rval = led_classdev_flash_register_ext(&flash->client->dev, - &flash->fled, &init_data); - if (rval) - goto out_err; - - return rval; - -out_err: - led_classdev_unregister(iled_cdev); - dev_err(&flash->client->dev, - "led_classdev_flash_register() failed, error %d\n", - rval); - return rval; -} - -static int as3645a_v4l2_setup(struct as3645a *flash) -{ - struct led_classdev_flash *fled = &flash->fled; - struct led_classdev *led = &fled->led_cdev; - struct v4l2_flash_config cfg = { - .intensity = { - .min = AS_TORCH_INTENSITY_MIN, - .max = flash->cfg.assist_max_ua, - .step = AS_TORCH_INTENSITY_STEP, - .val = flash->cfg.assist_max_ua, - }, - }; - struct v4l2_flash_config cfgind = { - .intensity = { - .min = AS_INDICATOR_INTENSITY_MIN, - .max = flash->cfg.indicator_max_ua, - .step = AS_INDICATOR_INTENSITY_STEP, - .val = flash->cfg.indicator_max_ua, - }, - }; - - strlcpy(cfg.dev_name, led->dev->kobj.name, sizeof(cfg.dev_name)); - strlcpy(cfgind.dev_name, flash->iled_cdev.dev->kobj.name, - sizeof(cfgind.dev_name)); - - flash->vf = v4l2_flash_init( - &flash->client->dev, flash->flash_node, &flash->fled, NULL, - &cfg); - if (IS_ERR(flash->vf)) - return PTR_ERR(flash->vf); - - flash->vfind = v4l2_flash_indicator_init( - &flash->client->dev, flash->indicator_node, &flash->iled_cdev, - &cfgind); - if (IS_ERR(flash->vfind)) { - v4l2_flash_release(flash->vf); - return PTR_ERR(flash->vfind); - } - - return 0; -} - -static int as3645a_probe(struct i2c_client *client) -{ - struct as3645a *flash; - int rval; - - if (!dev_fwnode(&client->dev)) - return -ENODEV; - - flash = devm_kzalloc(&client->dev, sizeof(*flash), GFP_KERNEL); - if (flash == NULL) - return -ENOMEM; - - flash->client = client; - - rval = as3645a_parse_node(flash, dev_fwnode(&client->dev)); - if (rval < 0) - return rval; - - rval = as3645a_detect(flash); - if (rval < 0) - goto out_put_nodes; - - mutex_init(&flash->mutex); - i2c_set_clientdata(client, flash); - - rval = as3645a_setup(flash); - if (rval) - goto out_mutex_destroy; - - rval = as3645a_led_class_setup(flash); - if (rval) - goto out_mutex_destroy; - - rval = as3645a_v4l2_setup(flash); - if (rval) - goto out_led_classdev_flash_unregister; - - return 0; - -out_led_classdev_flash_unregister: - led_classdev_flash_unregister(&flash->fled); - -out_mutex_destroy: - mutex_destroy(&flash->mutex); - -out_put_nodes: - fwnode_handle_put(flash->flash_node); - fwnode_handle_put(flash->indicator_node); - - return rval; -} - -static int as3645a_remove(struct i2c_client *client) -{ - struct as3645a *flash = i2c_get_clientdata(client); - - as3645a_set_control(flash, AS_MODE_EXT_TORCH, false); - - v4l2_flash_release(flash->vf); - v4l2_flash_release(flash->vfind); - - led_classdev_flash_unregister(&flash->fled); - led_classdev_unregister(&flash->iled_cdev); - - mutex_destroy(&flash->mutex); - - fwnode_handle_put(flash->flash_node); - fwnode_handle_put(flash->indicator_node); - - return 0; -} - -static const struct i2c_device_id as3645a_id_table[] = { - { AS_NAME, 0 }, - { }, -}; -MODULE_DEVICE_TABLE(i2c, as3645a_id_table); - -static const struct of_device_id as3645a_of_table[] = { - { .compatible = "ams,as3645a" }, - { }, -}; -MODULE_DEVICE_TABLE(of, as3645a_of_table); - -static struct i2c_driver as3645a_i2c_driver = { - .driver = { - .of_match_table = as3645a_of_table, - .name = AS_NAME, - }, - .probe_new = as3645a_probe, - .remove = as3645a_remove, - .id_table = as3645a_id_table, -}; - -module_i2c_driver(as3645a_i2c_driver); - -MODULE_AUTHOR("Laurent Pinchart "); -MODULE_AUTHOR("Sakari Ailus "); -MODULE_DESCRIPTION("LED flash driver for AS3645A, LM3555 and their clones"); -MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From eb5a4422e448a8200ddaafef0cc16db3f45ec1f8 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 28 May 2021 22:24:01 +0200 Subject: leds: max77693: Move driver to flash subdirectory We created a subdirectory for LED drivers that depend on CONFIG_LEDS_CLASS_FLASH, and this driver does so let's move it there. Signed-off-by: Linus Walleij Acked-by: Jacek Anaszewski Signed-off-by: Pavel Machek --- drivers/leds/Kconfig | 11 - drivers/leds/Makefile | 1 - drivers/leds/flash/Kconfig | 10 + drivers/leds/flash/Makefile | 1 + drivers/leds/flash/leds-max77693.c | 1059 ++++++++++++++++++++++++++++++++++++ drivers/leds/leds-max77693.c | 1059 ------------------------------------ 6 files changed, 1070 insertions(+), 1071 deletions(-) create mode 100644 drivers/leds/flash/leds-max77693.c delete mode 100644 drivers/leds/leds-max77693.c diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index cfa6b8194b6b..723de0d30039 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -680,17 +680,6 @@ config LEDS_MAX77650 help LEDs driver for MAX77650 family of PMICs from Maxim Integrated. -config LEDS_MAX77693 - tristate "LED support for MAX77693 Flash" - depends on LEDS_CLASS_FLASH - depends on V4L2_FLASH_LED_CLASS || !V4L2_FLASH_LED_CLASS - depends on MFD_MAX77693 - depends on OF - help - This option enables support for the flash part of the MAX77693 - multifunction device. It has build in control for two leds in flash - and torch mode. - config LEDS_MAX8997 tristate "LED support for MAX8997 PMIC" depends on LEDS_CLASS && MFD_MAX8997 diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index a3a6fda8ab99..6f4aa0e6e355 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -58,7 +58,6 @@ obj-$(CONFIG_LEDS_LP8788) += leds-lp8788.o obj-$(CONFIG_LEDS_LP8860) += leds-lp8860.o obj-$(CONFIG_LEDS_LT3593) += leds-lt3593.o obj-$(CONFIG_LEDS_MAX77650) += leds-max77650.o -obj-$(CONFIG_LEDS_MAX77693) += leds-max77693.o obj-$(CONFIG_LEDS_MAX8997) += leds-max8997.o obj-$(CONFIG_LEDS_MC13783) += leds-mc13783.o obj-$(CONFIG_LEDS_MENF21BMC) += leds-menf21bmc.o diff --git a/drivers/leds/flash/Kconfig b/drivers/leds/flash/Kconfig index 1126ad3954b6..6401af23947f 100644 --- a/drivers/leds/flash/Kconfig +++ b/drivers/leds/flash/Kconfig @@ -20,6 +20,16 @@ config LEDS_AS3645A controller. V4L2 flash API is provided as well if CONFIG_V4L2_FLASH_API is enabled. +config LEDS_MAX77693 + tristate "LED support for MAX77693 Flash" + depends on V4L2_FLASH_LED_CLASS || !V4L2_FLASH_LED_CLASS + depends on MFD_MAX77693 + depends on OF + help + This option enables support for the flash part of the MAX77693 + multifunction device. It has build in control for two leds in flash + and torch mode. + config LEDS_RT4505 tristate "LED support for RT4505 flashlight controller" depends on I2C && OF diff --git a/drivers/leds/flash/Makefile b/drivers/leds/flash/Makefile index 9bb2fccee047..c2a5e530261d 100644 --- a/drivers/leds/flash/Makefile +++ b/drivers/leds/flash/Makefile @@ -2,5 +2,6 @@ obj-$(CONFIG_LEDS_AAT1290) += leds-aat1290.o obj-$(CONFIG_LEDS_AS3645A) += leds-as3645a.o +obj-$(CONFIG_LEDS_MAX77693) += leds-max77693.o obj-$(CONFIG_LEDS_RT4505) += leds-rt4505.o obj-$(CONFIG_LEDS_RT8515) += leds-rt8515.o diff --git a/drivers/leds/flash/leds-max77693.c b/drivers/leds/flash/leds-max77693.c new file mode 100644 index 000000000000..5c1faeb55a31 --- /dev/null +++ b/drivers/leds/flash/leds-max77693.c @@ -0,0 +1,1059 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * LED Flash class driver for the flash cell of max77693 mfd. + * + * Copyright (C) 2015, Samsung Electronics Co., Ltd. + * + * Authors: Jacek Anaszewski + * Andrzej Hajda + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MODE_OFF 0 +#define MODE_FLASH(a) (1 << (a)) +#define MODE_TORCH(a) (1 << (2 + (a))) +#define MODE_FLASH_EXTERNAL(a) (1 << (4 + (a))) + +#define MODE_FLASH_MASK (MODE_FLASH(FLED1) | MODE_FLASH(FLED2) | \ + MODE_FLASH_EXTERNAL(FLED1) | \ + MODE_FLASH_EXTERNAL(FLED2)) +#define MODE_TORCH_MASK (MODE_TORCH(FLED1) | MODE_TORCH(FLED2)) + +#define FLED1_IOUT (1 << 0) +#define FLED2_IOUT (1 << 1) + +enum max77693_fled { + FLED1, + FLED2, +}; + +enum max77693_led_mode { + FLASH, + TORCH, +}; + +struct max77693_led_config_data { + const char *label[2]; + u32 iout_torch_max[2]; + u32 iout_flash_max[2]; + u32 flash_timeout_max[2]; + u32 num_leds; + u32 boost_mode; + u32 boost_vout; + u32 low_vsys; +}; + +struct max77693_sub_led { + /* corresponding FLED output identifier */ + int fled_id; + /* corresponding LED Flash class device */ + struct led_classdev_flash fled_cdev; + /* V4L2 Flash device */ + struct v4l2_flash *v4l2_flash; + + /* brightness cache */ + unsigned int torch_brightness; + /* flash timeout cache */ + unsigned int flash_timeout; + /* flash faults that may have occurred */ + u32 flash_faults; +}; + +struct max77693_led_device { + /* parent mfd regmap */ + struct regmap *regmap; + /* platform device data */ + struct platform_device *pdev; + /* secures access to the device */ + struct mutex lock; + + /* sub led data */ + struct max77693_sub_led sub_leds[2]; + + /* maximum torch current values for FLED outputs */ + u32 iout_torch_max[2]; + /* maximum flash current values for FLED outputs */ + u32 iout_flash_max[2]; + + /* current flash timeout cache */ + unsigned int current_flash_timeout; + /* ITORCH register cache */ + u8 torch_iout_reg; + /* mode of fled outputs */ + unsigned int mode_flags; + /* recently strobed fled */ + int strobing_sub_led_id; + /* bitmask of FLED outputs use state (bit 0. - FLED1, bit 1. - FLED2) */ + u8 fled_mask; + /* FLED modes that can be set */ + u8 allowed_modes; + + /* arrangement of current outputs */ + bool iout_joint; +}; + +static u8 max77693_led_iout_to_reg(u32 ua) +{ + if (ua < FLASH_IOUT_MIN) + ua = FLASH_IOUT_MIN; + return (ua - FLASH_IOUT_MIN) / FLASH_IOUT_STEP; +} + +static u8 max77693_flash_timeout_to_reg(u32 us) +{ + return (us - FLASH_TIMEOUT_MIN) / FLASH_TIMEOUT_STEP; +} + +static inline struct max77693_sub_led *flcdev_to_sub_led( + struct led_classdev_flash *fled_cdev) +{ + return container_of(fled_cdev, struct max77693_sub_led, fled_cdev); +} + +static inline struct max77693_led_device *sub_led_to_led( + struct max77693_sub_led *sub_led) +{ + return container_of(sub_led, struct max77693_led_device, + sub_leds[sub_led->fled_id]); +} + +static inline u8 max77693_led_vsys_to_reg(u32 mv) +{ + return ((mv - MAX_FLASH1_VSYS_MIN) / MAX_FLASH1_VSYS_STEP) << 2; +} + +static inline u8 max77693_led_vout_to_reg(u32 mv) +{ + return (mv - FLASH_VOUT_MIN) / FLASH_VOUT_STEP + FLASH_VOUT_RMIN; +} + +static inline bool max77693_fled_used(struct max77693_led_device *led, + int fled_id) +{ + u8 fled_bit = (fled_id == FLED1) ? FLED1_IOUT : FLED2_IOUT; + + return led->fled_mask & fled_bit; +} + +static int max77693_set_mode_reg(struct max77693_led_device *led, u8 mode) +{ + struct regmap *rmap = led->regmap; + int ret, v = 0, i; + + for (i = FLED1; i <= FLED2; ++i) { + if (mode & MODE_TORCH(i)) + v |= FLASH_EN_ON << TORCH_EN_SHIFT(i); + + if (mode & MODE_FLASH(i)) { + v |= FLASH_EN_ON << FLASH_EN_SHIFT(i); + } else if (mode & MODE_FLASH_EXTERNAL(i)) { + v |= FLASH_EN_FLASH << FLASH_EN_SHIFT(i); + /* + * Enable hw triggering also for torch mode, as some + * camera sensors use torch led to fathom ambient light + * conditions before strobing the flash. + */ + v |= FLASH_EN_TORCH << TORCH_EN_SHIFT(i); + } + } + + /* Reset the register only prior setting flash modes */ + if (mode & ~(MODE_TORCH(FLED1) | MODE_TORCH(FLED2))) { + ret = regmap_write(rmap, MAX77693_LED_REG_FLASH_EN, 0); + if (ret < 0) + return ret; + } + + return regmap_write(rmap, MAX77693_LED_REG_FLASH_EN, v); +} + +static int max77693_add_mode(struct max77693_led_device *led, u8 mode) +{ + u8 new_mode_flags; + int i, ret; + + if (led->iout_joint) + /* Span the mode on FLED2 for joint iouts case */ + mode |= (mode << 1); + + /* + * FLASH_EXTERNAL mode activates FLASHEN and TORCHEN pins in the device. + * Corresponding register bit fields interfere with SW triggered modes, + * thus clear them to ensure proper device configuration. + */ + for (i = FLED1; i <= FLED2; ++i) + if (mode & MODE_FLASH_EXTERNAL(i)) + led->mode_flags &= (~MODE_TORCH(i) & ~MODE_FLASH(i)); + + new_mode_flags = mode | led->mode_flags; + new_mode_flags &= led->allowed_modes; + + if (new_mode_flags ^ led->mode_flags) + led->mode_flags = new_mode_flags; + else + return 0; + + ret = max77693_set_mode_reg(led, led->mode_flags); + if (ret < 0) + return ret; + + /* + * Clear flash mode flag after setting the mode to avoid spurious flash + * strobing on each subsequent torch mode setting. + */ + if (mode & MODE_FLASH_MASK) + led->mode_flags &= ~mode; + + return ret; +} + +static int max77693_clear_mode(struct max77693_led_device *led, + u8 mode) +{ + if (led->iout_joint) + /* Clear mode also on FLED2 for joint iouts case */ + mode |= (mode << 1); + + led->mode_flags &= ~mode; + + return max77693_set_mode_reg(led, led->mode_flags); +} + +static void max77693_add_allowed_modes(struct max77693_led_device *led, + int fled_id, enum max77693_led_mode mode) +{ + if (mode == FLASH) + led->allowed_modes |= (MODE_FLASH(fled_id) | + MODE_FLASH_EXTERNAL(fled_id)); + else + led->allowed_modes |= MODE_TORCH(fled_id); +} + +static void max77693_distribute_currents(struct max77693_led_device *led, + int fled_id, enum max77693_led_mode mode, + u32 micro_amp, u32 iout_max[2], u32 iout[2]) +{ + if (!led->iout_joint) { + iout[fled_id] = micro_amp; + max77693_add_allowed_modes(led, fled_id, mode); + return; + } + + iout[FLED1] = min(micro_amp, iout_max[FLED1]); + iout[FLED2] = micro_amp - iout[FLED1]; + + if (mode == FLASH) + led->allowed_modes &= ~MODE_FLASH_MASK; + else + led->allowed_modes &= ~MODE_TORCH_MASK; + + max77693_add_allowed_modes(led, FLED1, mode); + + if (iout[FLED2]) + max77693_add_allowed_modes(led, FLED2, mode); +} + +static int max77693_set_torch_current(struct max77693_led_device *led, + int fled_id, u32 micro_amp) +{ + struct regmap *rmap = led->regmap; + u8 iout1_reg = 0, iout2_reg = 0; + u32 iout[2]; + + max77693_distribute_currents(led, fled_id, TORCH, micro_amp, + led->iout_torch_max, iout); + + if (fled_id == FLED1 || led->iout_joint) { + iout1_reg = max77693_led_iout_to_reg(iout[FLED1]); + led->torch_iout_reg &= TORCH_IOUT_MASK(TORCH_IOUT2_SHIFT); + } + if (fled_id == FLED2 || led->iout_joint) { + iout2_reg = max77693_led_iout_to_reg(iout[FLED2]); + led->torch_iout_reg &= TORCH_IOUT_MASK(TORCH_IOUT1_SHIFT); + } + + led->torch_iout_reg |= ((iout1_reg << TORCH_IOUT1_SHIFT) | + (iout2_reg << TORCH_IOUT2_SHIFT)); + + return regmap_write(rmap, MAX77693_LED_REG_ITORCH, + led->torch_iout_reg); +} + +static int max77693_set_flash_current(struct max77693_led_device *led, + int fled_id, + u32 micro_amp) +{ + struct regmap *rmap = led->regmap; + u8 iout1_reg, iout2_reg; + u32 iout[2]; + int ret = -EINVAL; + + max77693_distribute_currents(led, fled_id, FLASH, micro_amp, + led->iout_flash_max, iout); + + if (fled_id == FLED1 || led->iout_joint) { + iout1_reg = max77693_led_iout_to_reg(iout[FLED1]); + ret = regmap_write(rmap, MAX77693_LED_REG_IFLASH1, + iout1_reg); + if (ret < 0) + return ret; + } + if (fled_id == FLED2 || led->iout_joint) { + iout2_reg = max77693_led_iout_to_reg(iout[FLED2]); + ret = regmap_write(rmap, MAX77693_LED_REG_IFLASH2, + iout2_reg); + } + + return ret; +} + +static int max77693_set_timeout(struct max77693_led_device *led, u32 microsec) +{ + struct regmap *rmap = led->regmap; + u8 v; + int ret; + + v = max77693_flash_timeout_to_reg(microsec) | FLASH_TMR_LEVEL; + + ret = regmap_write(rmap, MAX77693_LED_REG_FLASH_TIMER, v); + if (ret < 0) + return ret; + + led->current_flash_timeout = microsec; + + return 0; +} + +static int max77693_get_strobe_status(struct max77693_led_device *led, + bool *state) +{ + struct regmap *rmap = led->regmap; + unsigned int v; + int ret; + + ret = regmap_read(rmap, MAX77693_LED_REG_FLASH_STATUS, &v); + if (ret < 0) + return ret; + + *state = v & FLASH_STATUS_FLASH_ON; + + return ret; +} + +static int max77693_get_flash_faults(struct max77693_sub_led *sub_led) +{ + struct max77693_led_device *led = sub_led_to_led(sub_led); + struct regmap *rmap = led->regmap; + unsigned int v; + u8 fault_open_mask, fault_short_mask; + int ret; + + sub_led->flash_faults = 0; + + if (led->iout_joint) { + fault_open_mask = FLASH_INT_FLED1_OPEN | FLASH_INT_FLED2_OPEN; + fault_short_mask = FLASH_INT_FLED1_SHORT | + FLASH_INT_FLED2_SHORT; + } else { + fault_open_mask = (sub_led->fled_id == FLED1) ? + FLASH_INT_FLED1_OPEN : + FLASH_INT_FLED2_OPEN; + fault_short_mask = (sub_led->fled_id == FLED1) ? + FLASH_INT_FLED1_SHORT : + FLASH_INT_FLED2_SHORT; + } + + ret = regmap_read(rmap, MAX77693_LED_REG_FLASH_INT, &v); + if (ret < 0) + return ret; + + if (v & fault_open_mask) + sub_led->flash_faults |= LED_FAULT_OVER_VOLTAGE; + if (v & fault_short_mask) + sub_led->flash_faults |= LED_FAULT_SHORT_CIRCUIT; + if (v & FLASH_INT_OVER_CURRENT) + sub_led->flash_faults |= LED_FAULT_OVER_CURRENT; + + return 0; +} + +static int max77693_setup(struct max77693_led_device *led, + struct max77693_led_config_data *led_cfg) +{ + struct regmap *rmap = led->regmap; + int i, first_led, last_led, ret; + u32 max_flash_curr[2]; + u8 v; + + /* + * Initialize only flash current. Torch current doesn't + * require initialization as ITORCH register is written with + * new value each time brightness_set op is called. + */ + if (led->iout_joint) { + first_led = FLED1; + last_led = FLED1; + max_flash_curr[FLED1] = led_cfg->iout_flash_max[FLED1] + + led_cfg->iout_flash_max[FLED2]; + } else { + first_led = max77693_fled_used(led, FLED1) ? FLED1 : FLED2; + last_led = max77693_fled_used(led, FLED2) ? FLED2 : FLED1; + max_flash_curr[FLED1] = led_cfg->iout_flash_max[FLED1]; + max_flash_curr[FLED2] = led_cfg->iout_flash_max[FLED2]; + } + + for (i = first_led; i <= last_led; ++i) { + ret = max77693_set_flash_current(led, i, + max_flash_curr[i]); + if (ret < 0) + return ret; + } + + v = TORCH_TMR_NO_TIMER | MAX77693_LED_TRIG_TYPE_LEVEL; + ret = regmap_write(rmap, MAX77693_LED_REG_ITORCHTIMER, v); + if (ret < 0) + return ret; + + if (led_cfg->low_vsys > 0) + v = max77693_led_vsys_to_reg(led_cfg->low_vsys) | + MAX_FLASH1_MAX_FL_EN; + else + v = 0; + + ret = regmap_write(rmap, MAX77693_LED_REG_MAX_FLASH1, v); + if (ret < 0) + return ret; + ret = regmap_write(rmap, MAX77693_LED_REG_MAX_FLASH2, 0); + if (ret < 0) + return ret; + + if (led_cfg->boost_mode == MAX77693_LED_BOOST_FIXED) + v = FLASH_BOOST_FIXED; + else + v = led_cfg->boost_mode | led_cfg->boost_mode << 1; + + if (max77693_fled_used(led, FLED1) && max77693_fled_used(led, FLED2)) + v |= FLASH_BOOST_LEDNUM_2; + + ret = regmap_write(rmap, MAX77693_LED_REG_VOUT_CNTL, v); + if (ret < 0) + return ret; + + v = max77693_led_vout_to_reg(led_cfg->boost_vout); + ret = regmap_write(rmap, MAX77693_LED_REG_VOUT_FLASH1, v); + if (ret < 0) + return ret; + + return max77693_set_mode_reg(led, MODE_OFF); +} + +/* LED subsystem callbacks */ +static int max77693_led_brightness_set(struct led_classdev *led_cdev, + enum led_brightness value) +{ + struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev); + struct max77693_sub_led *sub_led = flcdev_to_sub_led(fled_cdev); + struct max77693_led_device *led = sub_led_to_led(sub_led); + int fled_id = sub_led->fled_id, ret; + + mutex_lock(&led->lock); + + if (value == 0) { + ret = max77693_clear_mode(led, MODE_TORCH(fled_id)); + if (ret < 0) + dev_dbg(&led->pdev->dev, + "Failed to clear torch mode (%d)\n", + ret); + goto unlock; + } + + ret = max77693_set_torch_current(led, fled_id, value * TORCH_IOUT_STEP); + if (ret < 0) { + dev_dbg(&led->pdev->dev, + "Failed to set torch current (%d)\n", + ret); + goto unlock; + } + + ret = max77693_add_mode(led, MODE_TORCH(fled_id)); + if (ret < 0) + dev_dbg(&led->pdev->dev, + "Failed to set torch mode (%d)\n", + ret); +unlock: + mutex_unlock(&led->lock); + + return ret; +} + +static int max77693_led_flash_brightness_set( + struct led_classdev_flash *fled_cdev, + u32 brightness) +{ + struct max77693_sub_led *sub_led = flcdev_to_sub_led(fled_cdev); + struct max77693_led_device *led = sub_led_to_led(sub_led); + int ret; + + mutex_lock(&led->lock); + ret = max77693_set_flash_current(led, sub_led->fled_id, brightness); + mutex_unlock(&led->lock); + + return ret; +} + +static int max77693_led_flash_strobe_set( + struct led_classdev_flash *fled_cdev, + bool state) +{ + struct max77693_sub_led *sub_led = flcdev_to_sub_led(fled_cdev); + struct max77693_led_device *led = sub_led_to_led(sub_led); + int fled_id = sub_led->fled_id; + int ret; + + mutex_lock(&led->lock); + + if (!state) { + ret = max77693_clear_mode(led, MODE_FLASH(fled_id)); + goto unlock; + } + + if (sub_led->flash_timeout != led->current_flash_timeout) { + ret = max77693_set_timeout(led, sub_led->flash_timeout); + if (ret < 0) + goto unlock; + } + + led->strobing_sub_led_id = fled_id; + + ret = max77693_add_mode(led, MODE_FLASH(fled_id)); + if (ret < 0) + goto unlock; + + ret = max77693_get_flash_faults(sub_led); + +unlock: + mutex_unlock(&led->lock); + return ret; +} + +static int max77693_led_flash_fault_get( + struct led_classdev_flash *fled_cdev, + u32 *fault) +{ + struct max77693_sub_led *sub_led = flcdev_to_sub_led(fled_cdev); + + *fault = sub_led->flash_faults; + + return 0; +} + +static int max77693_led_flash_strobe_get( + struct led_classdev_flash *fled_cdev, + bool *state) +{ + struct max77693_sub_led *sub_led = flcdev_to_sub_led(fled_cdev); + struct max77693_led_device *led = sub_led_to_led(sub_led); + int ret; + + if (!state) + return -EINVAL; + + mutex_lock(&led->lock); + + ret = max77693_get_strobe_status(led, state); + + *state = !!(*state && (led->strobing_sub_led_id == sub_led->fled_id)); + + mutex_unlock(&led->lock); + + return ret; +} + +static int max77693_led_flash_timeout_set( + struct led_classdev_flash *fled_cdev, + u32 timeout) +{ + struct max77693_sub_led *sub_led = flcdev_to_sub_led(fled_cdev); + struct max77693_led_device *led = sub_led_to_led(sub_led); + + mutex_lock(&led->lock); + sub_led->flash_timeout = timeout; + mutex_unlock(&led->lock); + + return 0; +} + +static int max77693_led_parse_dt(struct max77693_led_device *led, + struct max77693_led_config_data *cfg, + struct device_node **sub_nodes) +{ + struct device *dev = &led->pdev->dev; + struct max77693_sub_led *sub_leds = led->sub_leds; + struct device_node *node = dev_of_node(dev), *child_node; + struct property *prop; + u32 led_sources[2]; + int i, ret, fled_id; + + of_property_read_u32(node, "maxim,boost-mode", &cfg->boost_mode); + of_property_read_u32(node, "maxim,boost-mvout", &cfg->boost_vout); + of_property_read_u32(node, "maxim,mvsys-min", &cfg->low_vsys); + + for_each_available_child_of_node(node, child_node) { + prop = of_find_property(child_node, "led-sources", NULL); + if (prop) { + const __be32 *srcs = NULL; + + for (i = 0; i < ARRAY_SIZE(led_sources); ++i) { + srcs = of_prop_next_u32(prop, srcs, + &led_sources[i]); + if (!srcs) + break; + } + } else { + dev_err(dev, + "led-sources DT property missing\n"); + of_node_put(child_node); + return -EINVAL; + } + + if (i == 2) { + fled_id = FLED1; + led->fled_mask = FLED1_IOUT | FLED2_IOUT; + } else if (led_sources[0] == FLED1) { + fled_id = FLED1; + led->fled_mask |= FLED1_IOUT; + } else if (led_sources[0] == FLED2) { + fled_id = FLED2; + led->fled_mask |= FLED2_IOUT; + } else { + dev_err(dev, + "Wrong led-sources DT property value.\n"); + of_node_put(child_node); + return -EINVAL; + } + + if (sub_nodes[fled_id]) { + dev_err(dev, + "Conflicting \"led-sources\" DT properties\n"); + of_node_put(child_node); + return -EINVAL; + } + + sub_nodes[fled_id] = child_node; + sub_leds[fled_id].fled_id = fled_id; + + cfg->label[fled_id] = + of_get_property(child_node, "label", NULL) ? : + child_node->name; + + ret = of_property_read_u32(child_node, "led-max-microamp", + &cfg->iout_torch_max[fled_id]); + if (ret < 0) { + cfg->iout_torch_max[fled_id] = TORCH_IOUT_MIN; + dev_warn(dev, "led-max-microamp DT property missing\n"); + } + + ret = of_property_read_u32(child_node, "flash-max-microamp", + &cfg->iout_flash_max[fled_id]); + if (ret < 0) { + cfg->iout_flash_max[fled_id] = FLASH_IOUT_MIN; + dev_warn(dev, + "flash-max-microamp DT property missing\n"); + } + + ret = of_property_read_u32(child_node, "flash-max-timeout-us", + &cfg->flash_timeout_max[fled_id]); + if (ret < 0) { + cfg->flash_timeout_max[fled_id] = FLASH_TIMEOUT_MIN; + dev_warn(dev, + "flash-max-timeout-us DT property missing\n"); + } + + if (++cfg->num_leds == 2 || + (max77693_fled_used(led, FLED1) && + max77693_fled_used(led, FLED2))) { + of_node_put(child_node); + break; + } + } + + if (cfg->num_leds == 0) { + dev_err(dev, "No DT child node found for connected LED(s).\n"); + return -EINVAL; + } + + return 0; +} + +static void clamp_align(u32 *v, u32 min, u32 max, u32 step) +{ + *v = clamp_val(*v, min, max); + if (step > 1) + *v = (*v - min) / step * step + min; +} + +static void max77693_align_iout_current(struct max77693_led_device *led, + u32 *iout, u32 min, u32 max, u32 step) +{ + int i; + + if (led->iout_joint) { + if (iout[FLED1] > min) { + iout[FLED1] /= 2; + iout[FLED2] = iout[FLED1]; + } else { + iout[FLED1] = min; + iout[FLED2] = 0; + return; + } + } + + for (i = FLED1; i <= FLED2; ++i) + if (max77693_fled_used(led, i)) + clamp_align(&iout[i], min, max, step); + else + iout[i] = 0; +} + +static void max77693_led_validate_configuration(struct max77693_led_device *led, + struct max77693_led_config_data *cfg) +{ + u32 flash_iout_max = cfg->boost_mode ? FLASH_IOUT_MAX_2LEDS : + FLASH_IOUT_MAX_1LED; + int i; + + if (cfg->num_leds == 1 && + max77693_fled_used(led, FLED1) && max77693_fled_used(led, FLED2)) + led->iout_joint = true; + + cfg->boost_mode = clamp_val(cfg->boost_mode, MAX77693_LED_BOOST_NONE, + MAX77693_LED_BOOST_FIXED); + + /* Boost must be enabled if both current outputs are used */ + if ((cfg->boost_mode == MAX77693_LED_BOOST_NONE) && led->iout_joint) + cfg->boost_mode = MAX77693_LED_BOOST_FIXED; + + max77693_align_iout_current(led, cfg->iout_torch_max, + TORCH_IOUT_MIN, TORCH_IOUT_MAX, TORCH_IOUT_STEP); + + max77693_align_iout_current(led, cfg->iout_flash_max, + FLASH_IOUT_MIN, flash_iout_max, FLASH_IOUT_STEP); + + for (i = 0; i < ARRAY_SIZE(cfg->flash_timeout_max); ++i) + clamp_align(&cfg->flash_timeout_max[i], FLASH_TIMEOUT_MIN, + FLASH_TIMEOUT_MAX, FLASH_TIMEOUT_STEP); + + clamp_align(&cfg->boost_vout, FLASH_VOUT_MIN, FLASH_VOUT_MAX, + FLASH_VOUT_STEP); + + if (cfg->low_vsys) + clamp_align(&cfg->low_vsys, MAX_FLASH1_VSYS_MIN, + MAX_FLASH1_VSYS_MAX, MAX_FLASH1_VSYS_STEP); +} + +static int max77693_led_get_configuration(struct max77693_led_device *led, + struct max77693_led_config_data *cfg, + struct device_node **sub_nodes) +{ + int ret; + + ret = max77693_led_parse_dt(led, cfg, sub_nodes); + if (ret < 0) + return ret; + + max77693_led_validate_configuration(led, cfg); + + memcpy(led->iout_torch_max, cfg->iout_torch_max, + sizeof(led->iout_torch_max)); + memcpy(led->iout_flash_max, cfg->iout_flash_max, + sizeof(led->iout_flash_max)); + + return 0; +} + +static const struct led_flash_ops flash_ops = { + .flash_brightness_set = max77693_led_flash_brightness_set, + .strobe_set = max77693_led_flash_strobe_set, + .strobe_get = max77693_led_flash_strobe_get, + .timeout_set = max77693_led_flash_timeout_set, + .fault_get = max77693_led_flash_fault_get, +}; + +static void max77693_init_flash_settings(struct max77693_sub_led *sub_led, + struct max77693_led_config_data *led_cfg) +{ + struct led_classdev_flash *fled_cdev = &sub_led->fled_cdev; + struct max77693_led_device *led = sub_led_to_led(sub_led); + int fled_id = sub_led->fled_id; + struct led_flash_setting *setting; + + /* Init flash intensity setting */ + setting = &fled_cdev->brightness; + setting->min = FLASH_IOUT_MIN; + setting->max = led->iout_joint ? + led_cfg->iout_flash_max[FLED1] + + led_cfg->iout_flash_max[FLED2] : + led_cfg->iout_flash_max[fled_id]; + setting->step = FLASH_IOUT_STEP; + setting->val = setting->max; + + /* Init flash timeout setting */ + setting = &fled_cdev->timeout; + setting->min = FLASH_TIMEOUT_MIN; + setting->max = led_cfg->flash_timeout_max[fled_id]; + setting->step = FLASH_TIMEOUT_STEP; + setting->val = setting->max; +} + +#if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS) + +static int max77693_led_external_strobe_set( + struct v4l2_flash *v4l2_flash, + bool enable) +{ + struct max77693_sub_led *sub_led = + flcdev_to_sub_led(v4l2_flash->fled_cdev); + struct max77693_led_device *led = sub_led_to_led(sub_led); + int fled_id = sub_led->fled_id; + int ret; + + mutex_lock(&led->lock); + + if (enable) + ret = max77693_add_mode(led, MODE_FLASH_EXTERNAL(fled_id)); + else + ret = max77693_clear_mode(led, MODE_FLASH_EXTERNAL(fled_id)); + + mutex_unlock(&led->lock); + + return ret; +} + +static void max77693_init_v4l2_flash_config(struct max77693_sub_led *sub_led, + struct max77693_led_config_data *led_cfg, + struct v4l2_flash_config *v4l2_sd_cfg) +{ + struct max77693_led_device *led = sub_led_to_led(sub_led); + struct device *dev = &led->pdev->dev; + struct max77693_dev *iodev = dev_get_drvdata(dev->parent); + struct i2c_client *i2c = iodev->i2c; + struct led_flash_setting *s; + + snprintf(v4l2_sd_cfg->dev_name, sizeof(v4l2_sd_cfg->dev_name), + "%s %d-%04x", sub_led->fled_cdev.led_cdev.name, + i2c_adapter_id(i2c->adapter), i2c->addr); + + s = &v4l2_sd_cfg->intensity; + s->min = TORCH_IOUT_MIN; + s->max = sub_led->fled_cdev.led_cdev.max_brightness * TORCH_IOUT_STEP; + s->step = TORCH_IOUT_STEP; + s->val = s->max; + + /* Init flash faults config */ + v4l2_sd_cfg->flash_faults = LED_FAULT_OVER_VOLTAGE | + LED_FAULT_SHORT_CIRCUIT | + LED_FAULT_OVER_CURRENT; + + v4l2_sd_cfg->has_external_strobe = true; +} + +static const struct v4l2_flash_ops v4l2_flash_ops = { + .external_strobe_set = max77693_led_external_strobe_set, +}; +#else +static inline void max77693_init_v4l2_flash_config( + struct max77693_sub_led *sub_led, + struct max77693_led_config_data *led_cfg, + struct v4l2_flash_config *v4l2_sd_cfg) +{ +} +static const struct v4l2_flash_ops v4l2_flash_ops; +#endif + +static void max77693_init_fled_cdev(struct max77693_sub_led *sub_led, + struct max77693_led_config_data *led_cfg) +{ + struct max77693_led_device *led = sub_led_to_led(sub_led); + int fled_id = sub_led->fled_id; + struct led_classdev_flash *fled_cdev; + struct led_classdev *led_cdev; + + /* Initialize LED Flash class device */ + fled_cdev = &sub_led->fled_cdev; + fled_cdev->ops = &flash_ops; + led_cdev = &fled_cdev->led_cdev; + + led_cdev->name = led_cfg->label[fled_id]; + + led_cdev->brightness_set_blocking = max77693_led_brightness_set; + led_cdev->max_brightness = (led->iout_joint ? + led_cfg->iout_torch_max[FLED1] + + led_cfg->iout_torch_max[FLED2] : + led_cfg->iout_torch_max[fled_id]) / + TORCH_IOUT_STEP; + led_cdev->flags |= LED_DEV_CAP_FLASH; + + max77693_init_flash_settings(sub_led, led_cfg); + + /* Init flash timeout cache */ + sub_led->flash_timeout = fled_cdev->timeout.val; +} + +static int max77693_register_led(struct max77693_sub_led *sub_led, + struct max77693_led_config_data *led_cfg, + struct device_node *sub_node) +{ + struct max77693_led_device *led = sub_led_to_led(sub_led); + struct led_classdev_flash *fled_cdev = &sub_led->fled_cdev; + struct device *dev = &led->pdev->dev; + struct v4l2_flash_config v4l2_sd_cfg = {}; + int ret; + + /* Register in the LED subsystem */ + ret = led_classdev_flash_register(dev, fled_cdev); + if (ret < 0) + return ret; + + max77693_init_v4l2_flash_config(sub_led, led_cfg, &v4l2_sd_cfg); + + /* Register in the V4L2 subsystem. */ + sub_led->v4l2_flash = v4l2_flash_init(dev, of_fwnode_handle(sub_node), + fled_cdev, &v4l2_flash_ops, + &v4l2_sd_cfg); + if (IS_ERR(sub_led->v4l2_flash)) { + ret = PTR_ERR(sub_led->v4l2_flash); + goto err_v4l2_flash_init; + } + + return 0; + +err_v4l2_flash_init: + led_classdev_flash_unregister(fled_cdev); + return ret; +} + +static int max77693_led_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct max77693_dev *iodev = dev_get_drvdata(dev->parent); + struct max77693_led_device *led; + struct max77693_sub_led *sub_leds; + struct device_node *sub_nodes[2] = {}; + struct max77693_led_config_data led_cfg = {}; + int init_fled_cdev[2], i, ret; + + led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL); + if (!led) + return -ENOMEM; + + led->pdev = pdev; + led->regmap = iodev->regmap; + led->allowed_modes = MODE_FLASH_MASK; + sub_leds = led->sub_leds; + + platform_set_drvdata(pdev, led); + ret = max77693_led_get_configuration(led, &led_cfg, sub_nodes); + if (ret < 0) + return ret; + + ret = max77693_setup(led, &led_cfg); + if (ret < 0) + return ret; + + mutex_init(&led->lock); + + init_fled_cdev[FLED1] = + led->iout_joint || max77693_fled_used(led, FLED1); + init_fled_cdev[FLED2] = + !led->iout_joint && max77693_fled_used(led, FLED2); + + for (i = FLED1; i <= FLED2; ++i) { + if (!init_fled_cdev[i]) + continue; + + /* Initialize LED Flash class device */ + max77693_init_fled_cdev(&sub_leds[i], &led_cfg); + + /* + * Register LED Flash class device and corresponding + * V4L2 Flash device. + */ + ret = max77693_register_led(&sub_leds[i], &led_cfg, + sub_nodes[i]); + if (ret < 0) { + /* + * At this moment FLED1 might have been already + * registered and it needs to be released. + */ + if (i == FLED2) + goto err_register_led2; + else + goto err_register_led1; + } + } + + return 0; + +err_register_led2: + /* It is possible than only FLED2 was to be registered */ + if (!init_fled_cdev[FLED1]) + goto err_register_led1; + v4l2_flash_release(sub_leds[FLED1].v4l2_flash); + led_classdev_flash_unregister(&sub_leds[FLED1].fled_cdev); +err_register_led1: + mutex_destroy(&led->lock); + + return ret; +} + +static int max77693_led_remove(struct platform_device *pdev) +{ + struct max77693_led_device *led = platform_get_drvdata(pdev); + struct max77693_sub_led *sub_leds = led->sub_leds; + + if (led->iout_joint || max77693_fled_used(led, FLED1)) { + v4l2_flash_release(sub_leds[FLED1].v4l2_flash); + led_classdev_flash_unregister(&sub_leds[FLED1].fled_cdev); + } + + if (!led->iout_joint && max77693_fled_used(led, FLED2)) { + v4l2_flash_release(sub_leds[FLED2].v4l2_flash); + led_classdev_flash_unregister(&sub_leds[FLED2].fled_cdev); + } + + mutex_destroy(&led->lock); + + return 0; +} + +static const struct of_device_id max77693_led_dt_match[] = { + { .compatible = "maxim,max77693-led" }, + {}, +}; +MODULE_DEVICE_TABLE(of, max77693_led_dt_match); + +static struct platform_driver max77693_led_driver = { + .probe = max77693_led_probe, + .remove = max77693_led_remove, + .driver = { + .name = "max77693-led", + .of_match_table = max77693_led_dt_match, + }, +}; + +module_platform_driver(max77693_led_driver); + +MODULE_AUTHOR("Jacek Anaszewski "); +MODULE_AUTHOR("Andrzej Hajda "); +MODULE_DESCRIPTION("Maxim MAX77693 led flash driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/leds/leds-max77693.c b/drivers/leds/leds-max77693.c deleted file mode 100644 index 5c1faeb55a31..000000000000 --- a/drivers/leds/leds-max77693.c +++ /dev/null @@ -1,1059 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * LED Flash class driver for the flash cell of max77693 mfd. - * - * Copyright (C) 2015, Samsung Electronics Co., Ltd. - * - * Authors: Jacek Anaszewski - * Andrzej Hajda - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define MODE_OFF 0 -#define MODE_FLASH(a) (1 << (a)) -#define MODE_TORCH(a) (1 << (2 + (a))) -#define MODE_FLASH_EXTERNAL(a) (1 << (4 + (a))) - -#define MODE_FLASH_MASK (MODE_FLASH(FLED1) | MODE_FLASH(FLED2) | \ - MODE_FLASH_EXTERNAL(FLED1) | \ - MODE_FLASH_EXTERNAL(FLED2)) -#define MODE_TORCH_MASK (MODE_TORCH(FLED1) | MODE_TORCH(FLED2)) - -#define FLED1_IOUT (1 << 0) -#define FLED2_IOUT (1 << 1) - -enum max77693_fled { - FLED1, - FLED2, -}; - -enum max77693_led_mode { - FLASH, - TORCH, -}; - -struct max77693_led_config_data { - const char *label[2]; - u32 iout_torch_max[2]; - u32 iout_flash_max[2]; - u32 flash_timeout_max[2]; - u32 num_leds; - u32 boost_mode; - u32 boost_vout; - u32 low_vsys; -}; - -struct max77693_sub_led { - /* corresponding FLED output identifier */ - int fled_id; - /* corresponding LED Flash class device */ - struct led_classdev_flash fled_cdev; - /* V4L2 Flash device */ - struct v4l2_flash *v4l2_flash; - - /* brightness cache */ - unsigned int torch_brightness; - /* flash timeout cache */ - unsigned int flash_timeout; - /* flash faults that may have occurred */ - u32 flash_faults; -}; - -struct max77693_led_device { - /* parent mfd regmap */ - struct regmap *regmap; - /* platform device data */ - struct platform_device *pdev; - /* secures access to the device */ - struct mutex lock; - - /* sub led data */ - struct max77693_sub_led sub_leds[2]; - - /* maximum torch current values for FLED outputs */ - u32 iout_torch_max[2]; - /* maximum flash current values for FLED outputs */ - u32 iout_flash_max[2]; - - /* current flash timeout cache */ - unsigned int current_flash_timeout; - /* ITORCH register cache */ - u8 torch_iout_reg; - /* mode of fled outputs */ - unsigned int mode_flags; - /* recently strobed fled */ - int strobing_sub_led_id; - /* bitmask of FLED outputs use state (bit 0. - FLED1, bit 1. - FLED2) */ - u8 fled_mask; - /* FLED modes that can be set */ - u8 allowed_modes; - - /* arrangement of current outputs */ - bool iout_joint; -}; - -static u8 max77693_led_iout_to_reg(u32 ua) -{ - if (ua < FLASH_IOUT_MIN) - ua = FLASH_IOUT_MIN; - return (ua - FLASH_IOUT_MIN) / FLASH_IOUT_STEP; -} - -static u8 max77693_flash_timeout_to_reg(u32 us) -{ - return (us - FLASH_TIMEOUT_MIN) / FLASH_TIMEOUT_STEP; -} - -static inline struct max77693_sub_led *flcdev_to_sub_led( - struct led_classdev_flash *fled_cdev) -{ - return container_of(fled_cdev, struct max77693_sub_led, fled_cdev); -} - -static inline struct max77693_led_device *sub_led_to_led( - struct max77693_sub_led *sub_led) -{ - return container_of(sub_led, struct max77693_led_device, - sub_leds[sub_led->fled_id]); -} - -static inline u8 max77693_led_vsys_to_reg(u32 mv) -{ - return ((mv - MAX_FLASH1_VSYS_MIN) / MAX_FLASH1_VSYS_STEP) << 2; -} - -static inline u8 max77693_led_vout_to_reg(u32 mv) -{ - return (mv - FLASH_VOUT_MIN) / FLASH_VOUT_STEP + FLASH_VOUT_RMIN; -} - -static inline bool max77693_fled_used(struct max77693_led_device *led, - int fled_id) -{ - u8 fled_bit = (fled_id == FLED1) ? FLED1_IOUT : FLED2_IOUT; - - return led->fled_mask & fled_bit; -} - -static int max77693_set_mode_reg(struct max77693_led_device *led, u8 mode) -{ - struct regmap *rmap = led->regmap; - int ret, v = 0, i; - - for (i = FLED1; i <= FLED2; ++i) { - if (mode & MODE_TORCH(i)) - v |= FLASH_EN_ON << TORCH_EN_SHIFT(i); - - if (mode & MODE_FLASH(i)) { - v |= FLASH_EN_ON << FLASH_EN_SHIFT(i); - } else if (mode & MODE_FLASH_EXTERNAL(i)) { - v |= FLASH_EN_FLASH << FLASH_EN_SHIFT(i); - /* - * Enable hw triggering also for torch mode, as some - * camera sensors use torch led to fathom ambient light - * conditions before strobing the flash. - */ - v |= FLASH_EN_TORCH << TORCH_EN_SHIFT(i); - } - } - - /* Reset the register only prior setting flash modes */ - if (mode & ~(MODE_TORCH(FLED1) | MODE_TORCH(FLED2))) { - ret = regmap_write(rmap, MAX77693_LED_REG_FLASH_EN, 0); - if (ret < 0) - return ret; - } - - return regmap_write(rmap, MAX77693_LED_REG_FLASH_EN, v); -} - -static int max77693_add_mode(struct max77693_led_device *led, u8 mode) -{ - u8 new_mode_flags; - int i, ret; - - if (led->iout_joint) - /* Span the mode on FLED2 for joint iouts case */ - mode |= (mode << 1); - - /* - * FLASH_EXTERNAL mode activates FLASHEN and TORCHEN pins in the device. - * Corresponding register bit fields interfere with SW triggered modes, - * thus clear them to ensure proper device configuration. - */ - for (i = FLED1; i <= FLED2; ++i) - if (mode & MODE_FLASH_EXTERNAL(i)) - led->mode_flags &= (~MODE_TORCH(i) & ~MODE_FLASH(i)); - - new_mode_flags = mode | led->mode_flags; - new_mode_flags &= led->allowed_modes; - - if (new_mode_flags ^ led->mode_flags) - led->mode_flags = new_mode_flags; - else - return 0; - - ret = max77693_set_mode_reg(led, led->mode_flags); - if (ret < 0) - return ret; - - /* - * Clear flash mode flag after setting the mode to avoid spurious flash - * strobing on each subsequent torch mode setting. - */ - if (mode & MODE_FLASH_MASK) - led->mode_flags &= ~mode; - - return ret; -} - -static int max77693_clear_mode(struct max77693_led_device *led, - u8 mode) -{ - if (led->iout_joint) - /* Clear mode also on FLED2 for joint iouts case */ - mode |= (mode << 1); - - led->mode_flags &= ~mode; - - return max77693_set_mode_reg(led, led->mode_flags); -} - -static void max77693_add_allowed_modes(struct max77693_led_device *led, - int fled_id, enum max77693_led_mode mode) -{ - if (mode == FLASH) - led->allowed_modes |= (MODE_FLASH(fled_id) | - MODE_FLASH_EXTERNAL(fled_id)); - else - led->allowed_modes |= MODE_TORCH(fled_id); -} - -static void max77693_distribute_currents(struct max77693_led_device *led, - int fled_id, enum max77693_led_mode mode, - u32 micro_amp, u32 iout_max[2], u32 iout[2]) -{ - if (!led->iout_joint) { - iout[fled_id] = micro_amp; - max77693_add_allowed_modes(led, fled_id, mode); - return; - } - - iout[FLED1] = min(micro_amp, iout_max[FLED1]); - iout[FLED2] = micro_amp - iout[FLED1]; - - if (mode == FLASH) - led->allowed_modes &= ~MODE_FLASH_MASK; - else - led->allowed_modes &= ~MODE_TORCH_MASK; - - max77693_add_allowed_modes(led, FLED1, mode); - - if (iout[FLED2]) - max77693_add_allowed_modes(led, FLED2, mode); -} - -static int max77693_set_torch_current(struct max77693_led_device *led, - int fled_id, u32 micro_amp) -{ - struct regmap *rmap = led->regmap; - u8 iout1_reg = 0, iout2_reg = 0; - u32 iout[2]; - - max77693_distribute_currents(led, fled_id, TORCH, micro_amp, - led->iout_torch_max, iout); - - if (fled_id == FLED1 || led->iout_joint) { - iout1_reg = max77693_led_iout_to_reg(iout[FLED1]); - led->torch_iout_reg &= TORCH_IOUT_MASK(TORCH_IOUT2_SHIFT); - } - if (fled_id == FLED2 || led->iout_joint) { - iout2_reg = max77693_led_iout_to_reg(iout[FLED2]); - led->torch_iout_reg &= TORCH_IOUT_MASK(TORCH_IOUT1_SHIFT); - } - - led->torch_iout_reg |= ((iout1_reg << TORCH_IOUT1_SHIFT) | - (iout2_reg << TORCH_IOUT2_SHIFT)); - - return regmap_write(rmap, MAX77693_LED_REG_ITORCH, - led->torch_iout_reg); -} - -static int max77693_set_flash_current(struct max77693_led_device *led, - int fled_id, - u32 micro_amp) -{ - struct regmap *rmap = led->regmap; - u8 iout1_reg, iout2_reg; - u32 iout[2]; - int ret = -EINVAL; - - max77693_distribute_currents(led, fled_id, FLASH, micro_amp, - led->iout_flash_max, iout); - - if (fled_id == FLED1 || led->iout_joint) { - iout1_reg = max77693_led_iout_to_reg(iout[FLED1]); - ret = regmap_write(rmap, MAX77693_LED_REG_IFLASH1, - iout1_reg); - if (ret < 0) - return ret; - } - if (fled_id == FLED2 || led->iout_joint) { - iout2_reg = max77693_led_iout_to_reg(iout[FLED2]); - ret = regmap_write(rmap, MAX77693_LED_REG_IFLASH2, - iout2_reg); - } - - return ret; -} - -static int max77693_set_timeout(struct max77693_led_device *led, u32 microsec) -{ - struct regmap *rmap = led->regmap; - u8 v; - int ret; - - v = max77693_flash_timeout_to_reg(microsec) | FLASH_TMR_LEVEL; - - ret = regmap_write(rmap, MAX77693_LED_REG_FLASH_TIMER, v); - if (ret < 0) - return ret; - - led->current_flash_timeout = microsec; - - return 0; -} - -static int max77693_get_strobe_status(struct max77693_led_device *led, - bool *state) -{ - struct regmap *rmap = led->regmap; - unsigned int v; - int ret; - - ret = regmap_read(rmap, MAX77693_LED_REG_FLASH_STATUS, &v); - if (ret < 0) - return ret; - - *state = v & FLASH_STATUS_FLASH_ON; - - return ret; -} - -static int max77693_get_flash_faults(struct max77693_sub_led *sub_led) -{ - struct max77693_led_device *led = sub_led_to_led(sub_led); - struct regmap *rmap = led->regmap; - unsigned int v; - u8 fault_open_mask, fault_short_mask; - int ret; - - sub_led->flash_faults = 0; - - if (led->iout_joint) { - fault_open_mask = FLASH_INT_FLED1_OPEN | FLASH_INT_FLED2_OPEN; - fault_short_mask = FLASH_INT_FLED1_SHORT | - FLASH_INT_FLED2_SHORT; - } else { - fault_open_mask = (sub_led->fled_id == FLED1) ? - FLASH_INT_FLED1_OPEN : - FLASH_INT_FLED2_OPEN; - fault_short_mask = (sub_led->fled_id == FLED1) ? - FLASH_INT_FLED1_SHORT : - FLASH_INT_FLED2_SHORT; - } - - ret = regmap_read(rmap, MAX77693_LED_REG_FLASH_INT, &v); - if (ret < 0) - return ret; - - if (v & fault_open_mask) - sub_led->flash_faults |= LED_FAULT_OVER_VOLTAGE; - if (v & fault_short_mask) - sub_led->flash_faults |= LED_FAULT_SHORT_CIRCUIT; - if (v & FLASH_INT_OVER_CURRENT) - sub_led->flash_faults |= LED_FAULT_OVER_CURRENT; - - return 0; -} - -static int max77693_setup(struct max77693_led_device *led, - struct max77693_led_config_data *led_cfg) -{ - struct regmap *rmap = led->regmap; - int i, first_led, last_led, ret; - u32 max_flash_curr[2]; - u8 v; - - /* - * Initialize only flash current. Torch current doesn't - * require initialization as ITORCH register is written with - * new value each time brightness_set op is called. - */ - if (led->iout_joint) { - first_led = FLED1; - last_led = FLED1; - max_flash_curr[FLED1] = led_cfg->iout_flash_max[FLED1] + - led_cfg->iout_flash_max[FLED2]; - } else { - first_led = max77693_fled_used(led, FLED1) ? FLED1 : FLED2; - last_led = max77693_fled_used(led, FLED2) ? FLED2 : FLED1; - max_flash_curr[FLED1] = led_cfg->iout_flash_max[FLED1]; - max_flash_curr[FLED2] = led_cfg->iout_flash_max[FLED2]; - } - - for (i = first_led; i <= last_led; ++i) { - ret = max77693_set_flash_current(led, i, - max_flash_curr[i]); - if (ret < 0) - return ret; - } - - v = TORCH_TMR_NO_TIMER | MAX77693_LED_TRIG_TYPE_LEVEL; - ret = regmap_write(rmap, MAX77693_LED_REG_ITORCHTIMER, v); - if (ret < 0) - return ret; - - if (led_cfg->low_vsys > 0) - v = max77693_led_vsys_to_reg(led_cfg->low_vsys) | - MAX_FLASH1_MAX_FL_EN; - else - v = 0; - - ret = regmap_write(rmap, MAX77693_LED_REG_MAX_FLASH1, v); - if (ret < 0) - return ret; - ret = regmap_write(rmap, MAX77693_LED_REG_MAX_FLASH2, 0); - if (ret < 0) - return ret; - - if (led_cfg->boost_mode == MAX77693_LED_BOOST_FIXED) - v = FLASH_BOOST_FIXED; - else - v = led_cfg->boost_mode | led_cfg->boost_mode << 1; - - if (max77693_fled_used(led, FLED1) && max77693_fled_used(led, FLED2)) - v |= FLASH_BOOST_LEDNUM_2; - - ret = regmap_write(rmap, MAX77693_LED_REG_VOUT_CNTL, v); - if (ret < 0) - return ret; - - v = max77693_led_vout_to_reg(led_cfg->boost_vout); - ret = regmap_write(rmap, MAX77693_LED_REG_VOUT_FLASH1, v); - if (ret < 0) - return ret; - - return max77693_set_mode_reg(led, MODE_OFF); -} - -/* LED subsystem callbacks */ -static int max77693_led_brightness_set(struct led_classdev *led_cdev, - enum led_brightness value) -{ - struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev); - struct max77693_sub_led *sub_led = flcdev_to_sub_led(fled_cdev); - struct max77693_led_device *led = sub_led_to_led(sub_led); - int fled_id = sub_led->fled_id, ret; - - mutex_lock(&led->lock); - - if (value == 0) { - ret = max77693_clear_mode(led, MODE_TORCH(fled_id)); - if (ret < 0) - dev_dbg(&led->pdev->dev, - "Failed to clear torch mode (%d)\n", - ret); - goto unlock; - } - - ret = max77693_set_torch_current(led, fled_id, value * TORCH_IOUT_STEP); - if (ret < 0) { - dev_dbg(&led->pdev->dev, - "Failed to set torch current (%d)\n", - ret); - goto unlock; - } - - ret = max77693_add_mode(led, MODE_TORCH(fled_id)); - if (ret < 0) - dev_dbg(&led->pdev->dev, - "Failed to set torch mode (%d)\n", - ret); -unlock: - mutex_unlock(&led->lock); - - return ret; -} - -static int max77693_led_flash_brightness_set( - struct led_classdev_flash *fled_cdev, - u32 brightness) -{ - struct max77693_sub_led *sub_led = flcdev_to_sub_led(fled_cdev); - struct max77693_led_device *led = sub_led_to_led(sub_led); - int ret; - - mutex_lock(&led->lock); - ret = max77693_set_flash_current(led, sub_led->fled_id, brightness); - mutex_unlock(&led->lock); - - return ret; -} - -static int max77693_led_flash_strobe_set( - struct led_classdev_flash *fled_cdev, - bool state) -{ - struct max77693_sub_led *sub_led = flcdev_to_sub_led(fled_cdev); - struct max77693_led_device *led = sub_led_to_led(sub_led); - int fled_id = sub_led->fled_id; - int ret; - - mutex_lock(&led->lock); - - if (!state) { - ret = max77693_clear_mode(led, MODE_FLASH(fled_id)); - goto unlock; - } - - if (sub_led->flash_timeout != led->current_flash_timeout) { - ret = max77693_set_timeout(led, sub_led->flash_timeout); - if (ret < 0) - goto unlock; - } - - led->strobing_sub_led_id = fled_id; - - ret = max77693_add_mode(led, MODE_FLASH(fled_id)); - if (ret < 0) - goto unlock; - - ret = max77693_get_flash_faults(sub_led); - -unlock: - mutex_unlock(&led->lock); - return ret; -} - -static int max77693_led_flash_fault_get( - struct led_classdev_flash *fled_cdev, - u32 *fault) -{ - struct max77693_sub_led *sub_led = flcdev_to_sub_led(fled_cdev); - - *fault = sub_led->flash_faults; - - return 0; -} - -static int max77693_led_flash_strobe_get( - struct led_classdev_flash *fled_cdev, - bool *state) -{ - struct max77693_sub_led *sub_led = flcdev_to_sub_led(fled_cdev); - struct max77693_led_device *led = sub_led_to_led(sub_led); - int ret; - - if (!state) - return -EINVAL; - - mutex_lock(&led->lock); - - ret = max77693_get_strobe_status(led, state); - - *state = !!(*state && (led->strobing_sub_led_id == sub_led->fled_id)); - - mutex_unlock(&led->lock); - - return ret; -} - -static int max77693_led_flash_timeout_set( - struct led_classdev_flash *fled_cdev, - u32 timeout) -{ - struct max77693_sub_led *sub_led = flcdev_to_sub_led(fled_cdev); - struct max77693_led_device *led = sub_led_to_led(sub_led); - - mutex_lock(&led->lock); - sub_led->flash_timeout = timeout; - mutex_unlock(&led->lock); - - return 0; -} - -static int max77693_led_parse_dt(struct max77693_led_device *led, - struct max77693_led_config_data *cfg, - struct device_node **sub_nodes) -{ - struct device *dev = &led->pdev->dev; - struct max77693_sub_led *sub_leds = led->sub_leds; - struct device_node *node = dev_of_node(dev), *child_node; - struct property *prop; - u32 led_sources[2]; - int i, ret, fled_id; - - of_property_read_u32(node, "maxim,boost-mode", &cfg->boost_mode); - of_property_read_u32(node, "maxim,boost-mvout", &cfg->boost_vout); - of_property_read_u32(node, "maxim,mvsys-min", &cfg->low_vsys); - - for_each_available_child_of_node(node, child_node) { - prop = of_find_property(child_node, "led-sources", NULL); - if (prop) { - const __be32 *srcs = NULL; - - for (i = 0; i < ARRAY_SIZE(led_sources); ++i) { - srcs = of_prop_next_u32(prop, srcs, - &led_sources[i]); - if (!srcs) - break; - } - } else { - dev_err(dev, - "led-sources DT property missing\n"); - of_node_put(child_node); - return -EINVAL; - } - - if (i == 2) { - fled_id = FLED1; - led->fled_mask = FLED1_IOUT | FLED2_IOUT; - } else if (led_sources[0] == FLED1) { - fled_id = FLED1; - led->fled_mask |= FLED1_IOUT; - } else if (led_sources[0] == FLED2) { - fled_id = FLED2; - led->fled_mask |= FLED2_IOUT; - } else { - dev_err(dev, - "Wrong led-sources DT property value.\n"); - of_node_put(child_node); - return -EINVAL; - } - - if (sub_nodes[fled_id]) { - dev_err(dev, - "Conflicting \"led-sources\" DT properties\n"); - of_node_put(child_node); - return -EINVAL; - } - - sub_nodes[fled_id] = child_node; - sub_leds[fled_id].fled_id = fled_id; - - cfg->label[fled_id] = - of_get_property(child_node, "label", NULL) ? : - child_node->name; - - ret = of_property_read_u32(child_node, "led-max-microamp", - &cfg->iout_torch_max[fled_id]); - if (ret < 0) { - cfg->iout_torch_max[fled_id] = TORCH_IOUT_MIN; - dev_warn(dev, "led-max-microamp DT property missing\n"); - } - - ret = of_property_read_u32(child_node, "flash-max-microamp", - &cfg->iout_flash_max[fled_id]); - if (ret < 0) { - cfg->iout_flash_max[fled_id] = FLASH_IOUT_MIN; - dev_warn(dev, - "flash-max-microamp DT property missing\n"); - } - - ret = of_property_read_u32(child_node, "flash-max-timeout-us", - &cfg->flash_timeout_max[fled_id]); - if (ret < 0) { - cfg->flash_timeout_max[fled_id] = FLASH_TIMEOUT_MIN; - dev_warn(dev, - "flash-max-timeout-us DT property missing\n"); - } - - if (++cfg->num_leds == 2 || - (max77693_fled_used(led, FLED1) && - max77693_fled_used(led, FLED2))) { - of_node_put(child_node); - break; - } - } - - if (cfg->num_leds == 0) { - dev_err(dev, "No DT child node found for connected LED(s).\n"); - return -EINVAL; - } - - return 0; -} - -static void clamp_align(u32 *v, u32 min, u32 max, u32 step) -{ - *v = clamp_val(*v, min, max); - if (step > 1) - *v = (*v - min) / step * step + min; -} - -static void max77693_align_iout_current(struct max77693_led_device *led, - u32 *iout, u32 min, u32 max, u32 step) -{ - int i; - - if (led->iout_joint) { - if (iout[FLED1] > min) { - iout[FLED1] /= 2; - iout[FLED2] = iout[FLED1]; - } else { - iout[FLED1] = min; - iout[FLED2] = 0; - return; - } - } - - for (i = FLED1; i <= FLED2; ++i) - if (max77693_fled_used(led, i)) - clamp_align(&iout[i], min, max, step); - else - iout[i] = 0; -} - -static void max77693_led_validate_configuration(struct max77693_led_device *led, - struct max77693_led_config_data *cfg) -{ - u32 flash_iout_max = cfg->boost_mode ? FLASH_IOUT_MAX_2LEDS : - FLASH_IOUT_MAX_1LED; - int i; - - if (cfg->num_leds == 1 && - max77693_fled_used(led, FLED1) && max77693_fled_used(led, FLED2)) - led->iout_joint = true; - - cfg->boost_mode = clamp_val(cfg->boost_mode, MAX77693_LED_BOOST_NONE, - MAX77693_LED_BOOST_FIXED); - - /* Boost must be enabled if both current outputs are used */ - if ((cfg->boost_mode == MAX77693_LED_BOOST_NONE) && led->iout_joint) - cfg->boost_mode = MAX77693_LED_BOOST_FIXED; - - max77693_align_iout_current(led, cfg->iout_torch_max, - TORCH_IOUT_MIN, TORCH_IOUT_MAX, TORCH_IOUT_STEP); - - max77693_align_iout_current(led, cfg->iout_flash_max, - FLASH_IOUT_MIN, flash_iout_max, FLASH_IOUT_STEP); - - for (i = 0; i < ARRAY_SIZE(cfg->flash_timeout_max); ++i) - clamp_align(&cfg->flash_timeout_max[i], FLASH_TIMEOUT_MIN, - FLASH_TIMEOUT_MAX, FLASH_TIMEOUT_STEP); - - clamp_align(&cfg->boost_vout, FLASH_VOUT_MIN, FLASH_VOUT_MAX, - FLASH_VOUT_STEP); - - if (cfg->low_vsys) - clamp_align(&cfg->low_vsys, MAX_FLASH1_VSYS_MIN, - MAX_FLASH1_VSYS_MAX, MAX_FLASH1_VSYS_STEP); -} - -static int max77693_led_get_configuration(struct max77693_led_device *led, - struct max77693_led_config_data *cfg, - struct device_node **sub_nodes) -{ - int ret; - - ret = max77693_led_parse_dt(led, cfg, sub_nodes); - if (ret < 0) - return ret; - - max77693_led_validate_configuration(led, cfg); - - memcpy(led->iout_torch_max, cfg->iout_torch_max, - sizeof(led->iout_torch_max)); - memcpy(led->iout_flash_max, cfg->iout_flash_max, - sizeof(led->iout_flash_max)); - - return 0; -} - -static const struct led_flash_ops flash_ops = { - .flash_brightness_set = max77693_led_flash_brightness_set, - .strobe_set = max77693_led_flash_strobe_set, - .strobe_get = max77693_led_flash_strobe_get, - .timeout_set = max77693_led_flash_timeout_set, - .fault_get = max77693_led_flash_fault_get, -}; - -static void max77693_init_flash_settings(struct max77693_sub_led *sub_led, - struct max77693_led_config_data *led_cfg) -{ - struct led_classdev_flash *fled_cdev = &sub_led->fled_cdev; - struct max77693_led_device *led = sub_led_to_led(sub_led); - int fled_id = sub_led->fled_id; - struct led_flash_setting *setting; - - /* Init flash intensity setting */ - setting = &fled_cdev->brightness; - setting->min = FLASH_IOUT_MIN; - setting->max = led->iout_joint ? - led_cfg->iout_flash_max[FLED1] + - led_cfg->iout_flash_max[FLED2] : - led_cfg->iout_flash_max[fled_id]; - setting->step = FLASH_IOUT_STEP; - setting->val = setting->max; - - /* Init flash timeout setting */ - setting = &fled_cdev->timeout; - setting->min = FLASH_TIMEOUT_MIN; - setting->max = led_cfg->flash_timeout_max[fled_id]; - setting->step = FLASH_TIMEOUT_STEP; - setting->val = setting->max; -} - -#if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS) - -static int max77693_led_external_strobe_set( - struct v4l2_flash *v4l2_flash, - bool enable) -{ - struct max77693_sub_led *sub_led = - flcdev_to_sub_led(v4l2_flash->fled_cdev); - struct max77693_led_device *led = sub_led_to_led(sub_led); - int fled_id = sub_led->fled_id; - int ret; - - mutex_lock(&led->lock); - - if (enable) - ret = max77693_add_mode(led, MODE_FLASH_EXTERNAL(fled_id)); - else - ret = max77693_clear_mode(led, MODE_FLASH_EXTERNAL(fled_id)); - - mutex_unlock(&led->lock); - - return ret; -} - -static void max77693_init_v4l2_flash_config(struct max77693_sub_led *sub_led, - struct max77693_led_config_data *led_cfg, - struct v4l2_flash_config *v4l2_sd_cfg) -{ - struct max77693_led_device *led = sub_led_to_led(sub_led); - struct device *dev = &led->pdev->dev; - struct max77693_dev *iodev = dev_get_drvdata(dev->parent); - struct i2c_client *i2c = iodev->i2c; - struct led_flash_setting *s; - - snprintf(v4l2_sd_cfg->dev_name, sizeof(v4l2_sd_cfg->dev_name), - "%s %d-%04x", sub_led->fled_cdev.led_cdev.name, - i2c_adapter_id(i2c->adapter), i2c->addr); - - s = &v4l2_sd_cfg->intensity; - s->min = TORCH_IOUT_MIN; - s->max = sub_led->fled_cdev.led_cdev.max_brightness * TORCH_IOUT_STEP; - s->step = TORCH_IOUT_STEP; - s->val = s->max; - - /* Init flash faults config */ - v4l2_sd_cfg->flash_faults = LED_FAULT_OVER_VOLTAGE | - LED_FAULT_SHORT_CIRCUIT | - LED_FAULT_OVER_CURRENT; - - v4l2_sd_cfg->has_external_strobe = true; -} - -static const struct v4l2_flash_ops v4l2_flash_ops = { - .external_strobe_set = max77693_led_external_strobe_set, -}; -#else -static inline void max77693_init_v4l2_flash_config( - struct max77693_sub_led *sub_led, - struct max77693_led_config_data *led_cfg, - struct v4l2_flash_config *v4l2_sd_cfg) -{ -} -static const struct v4l2_flash_ops v4l2_flash_ops; -#endif - -static void max77693_init_fled_cdev(struct max77693_sub_led *sub_led, - struct max77693_led_config_data *led_cfg) -{ - struct max77693_led_device *led = sub_led_to_led(sub_led); - int fled_id = sub_led->fled_id; - struct led_classdev_flash *fled_cdev; - struct led_classdev *led_cdev; - - /* Initialize LED Flash class device */ - fled_cdev = &sub_led->fled_cdev; - fled_cdev->ops = &flash_ops; - led_cdev = &fled_cdev->led_cdev; - - led_cdev->name = led_cfg->label[fled_id]; - - led_cdev->brightness_set_blocking = max77693_led_brightness_set; - led_cdev->max_brightness = (led->iout_joint ? - led_cfg->iout_torch_max[FLED1] + - led_cfg->iout_torch_max[FLED2] : - led_cfg->iout_torch_max[fled_id]) / - TORCH_IOUT_STEP; - led_cdev->flags |= LED_DEV_CAP_FLASH; - - max77693_init_flash_settings(sub_led, led_cfg); - - /* Init flash timeout cache */ - sub_led->flash_timeout = fled_cdev->timeout.val; -} - -static int max77693_register_led(struct max77693_sub_led *sub_led, - struct max77693_led_config_data *led_cfg, - struct device_node *sub_node) -{ - struct max77693_led_device *led = sub_led_to_led(sub_led); - struct led_classdev_flash *fled_cdev = &sub_led->fled_cdev; - struct device *dev = &led->pdev->dev; - struct v4l2_flash_config v4l2_sd_cfg = {}; - int ret; - - /* Register in the LED subsystem */ - ret = led_classdev_flash_register(dev, fled_cdev); - if (ret < 0) - return ret; - - max77693_init_v4l2_flash_config(sub_led, led_cfg, &v4l2_sd_cfg); - - /* Register in the V4L2 subsystem. */ - sub_led->v4l2_flash = v4l2_flash_init(dev, of_fwnode_handle(sub_node), - fled_cdev, &v4l2_flash_ops, - &v4l2_sd_cfg); - if (IS_ERR(sub_led->v4l2_flash)) { - ret = PTR_ERR(sub_led->v4l2_flash); - goto err_v4l2_flash_init; - } - - return 0; - -err_v4l2_flash_init: - led_classdev_flash_unregister(fled_cdev); - return ret; -} - -static int max77693_led_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct max77693_dev *iodev = dev_get_drvdata(dev->parent); - struct max77693_led_device *led; - struct max77693_sub_led *sub_leds; - struct device_node *sub_nodes[2] = {}; - struct max77693_led_config_data led_cfg = {}; - int init_fled_cdev[2], i, ret; - - led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL); - if (!led) - return -ENOMEM; - - led->pdev = pdev; - led->regmap = iodev->regmap; - led->allowed_modes = MODE_FLASH_MASK; - sub_leds = led->sub_leds; - - platform_set_drvdata(pdev, led); - ret = max77693_led_get_configuration(led, &led_cfg, sub_nodes); - if (ret < 0) - return ret; - - ret = max77693_setup(led, &led_cfg); - if (ret < 0) - return ret; - - mutex_init(&led->lock); - - init_fled_cdev[FLED1] = - led->iout_joint || max77693_fled_used(led, FLED1); - init_fled_cdev[FLED2] = - !led->iout_joint && max77693_fled_used(led, FLED2); - - for (i = FLED1; i <= FLED2; ++i) { - if (!init_fled_cdev[i]) - continue; - - /* Initialize LED Flash class device */ - max77693_init_fled_cdev(&sub_leds[i], &led_cfg); - - /* - * Register LED Flash class device and corresponding - * V4L2 Flash device. - */ - ret = max77693_register_led(&sub_leds[i], &led_cfg, - sub_nodes[i]); - if (ret < 0) { - /* - * At this moment FLED1 might have been already - * registered and it needs to be released. - */ - if (i == FLED2) - goto err_register_led2; - else - goto err_register_led1; - } - } - - return 0; - -err_register_led2: - /* It is possible than only FLED2 was to be registered */ - if (!init_fled_cdev[FLED1]) - goto err_register_led1; - v4l2_flash_release(sub_leds[FLED1].v4l2_flash); - led_classdev_flash_unregister(&sub_leds[FLED1].fled_cdev); -err_register_led1: - mutex_destroy(&led->lock); - - return ret; -} - -static int max77693_led_remove(struct platform_device *pdev) -{ - struct max77693_led_device *led = platform_get_drvdata(pdev); - struct max77693_sub_led *sub_leds = led->sub_leds; - - if (led->iout_joint || max77693_fled_used(led, FLED1)) { - v4l2_flash_release(sub_leds[FLED1].v4l2_flash); - led_classdev_flash_unregister(&sub_leds[FLED1].fled_cdev); - } - - if (!led->iout_joint && max77693_fled_used(led, FLED2)) { - v4l2_flash_release(sub_leds[FLED2].v4l2_flash); - led_classdev_flash_unregister(&sub_leds[FLED2].fled_cdev); - } - - mutex_destroy(&led->lock); - - return 0; -} - -static const struct of_device_id max77693_led_dt_match[] = { - { .compatible = "maxim,max77693-led" }, - {}, -}; -MODULE_DEVICE_TABLE(of, max77693_led_dt_match); - -static struct platform_driver max77693_led_driver = { - .probe = max77693_led_probe, - .remove = max77693_led_remove, - .driver = { - .name = "max77693-led", - .of_match_table = max77693_led_dt_match, - }, -}; - -module_platform_driver(max77693_led_driver); - -MODULE_AUTHOR("Jacek Anaszewski "); -MODULE_AUTHOR("Andrzej Hajda "); -MODULE_DESCRIPTION("Maxim MAX77693 led flash driver"); -MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 9a7c066f6a1dfff2f30e697fe494a669428fc9f4 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 28 May 2021 22:24:02 +0200 Subject: leds: sgm3140: Move driver to flash subdirectory We created a subdirectory for LED drivers that depend on CONFIG_LEDS_CLASS_FLASH, and this driver does so let's move it there. Cc: Luca Weiss Signed-off-by: Linus Walleij Signed-off-by: Pavel Machek --- drivers/leds/Kconfig | 8 - drivers/leds/Makefile | 1 - drivers/leds/flash/Kconfig | 7 + drivers/leds/flash/Makefile | 1 + drivers/leds/flash/leds-sgm3140.c | 311 ++++++++++++++++++++++++++++++++++++++ drivers/leds/leds-sgm3140.c | 311 -------------------------------------- 6 files changed, 319 insertions(+), 320 deletions(-) create mode 100644 drivers/leds/flash/leds-sgm3140.c delete mode 100644 drivers/leds/leds-sgm3140.c diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 723de0d30039..171ccfd4de1b 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -883,14 +883,6 @@ config LEDS_IP30 To compile this driver as a module, choose M here: the module will be called leds-ip30. -config LEDS_SGM3140 - tristate "LED support for the SGM3140" - depends on LEDS_CLASS_FLASH - depends on V4L2_FLASH_LED_CLASS || !V4L2_FLASH_LED_CLASS - help - This option enables support for the SGM3140 500mA Buck/Boost Charge - Pump LED Driver. - config LEDS_ACER_A500 tristate "Power button LED support for Acer Iconia Tab A500" depends on LEDS_CLASS && MFD_ACER_A500_EC diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index 6f4aa0e6e355..5e804f72b8e5 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -79,7 +79,6 @@ obj-$(CONFIG_LEDS_PWM) += leds-pwm.o obj-$(CONFIG_LEDS_REGULATOR) += leds-regulator.o obj-$(CONFIG_LEDS_S3C24XX) += leds-s3c24xx.o obj-$(CONFIG_LEDS_SC27XX_BLTC) += leds-sc27xx-bltc.o -obj-$(CONFIG_LEDS_SGM3140) += leds-sgm3140.o obj-$(CONFIG_LEDS_SUNFIRE) += leds-sunfire.o obj-$(CONFIG_LEDS_SYSCON) += leds-syscon.o obj-$(CONFIG_LEDS_TCA6507) += leds-tca6507.o diff --git a/drivers/leds/flash/Kconfig b/drivers/leds/flash/Kconfig index 6401af23947f..788d698587a7 100644 --- a/drivers/leds/flash/Kconfig +++ b/drivers/leds/flash/Kconfig @@ -52,4 +52,11 @@ config LEDS_RT8515 To compile this driver as a module, choose M here: the module will be called leds-rt8515. +config LEDS_SGM3140 + tristate "LED support for the SGM3140" + depends on V4L2_FLASH_LED_CLASS || !V4L2_FLASH_LED_CLASS + help + This option enables support for the SGM3140 500mA Buck/Boost Charge + Pump LED Driver. + endif # LEDS_CLASS_FLASH diff --git a/drivers/leds/flash/Makefile b/drivers/leds/flash/Makefile index c2a5e530261d..2f9153e78717 100644 --- a/drivers/leds/flash/Makefile +++ b/drivers/leds/flash/Makefile @@ -5,3 +5,4 @@ obj-$(CONFIG_LEDS_AS3645A) += leds-as3645a.o obj-$(CONFIG_LEDS_MAX77693) += leds-max77693.o obj-$(CONFIG_LEDS_RT4505) += leds-rt4505.o obj-$(CONFIG_LEDS_RT8515) += leds-rt8515.o +obj-$(CONFIG_LEDS_SGM3140) += leds-sgm3140.o diff --git a/drivers/leds/flash/leds-sgm3140.c b/drivers/leds/flash/leds-sgm3140.c new file mode 100644 index 000000000000..f4f831570f11 --- /dev/null +++ b/drivers/leds/flash/leds-sgm3140.c @@ -0,0 +1,311 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2020 Luca Weiss + +#include +#include +#include +#include +#include + +#include + +#define FLASH_TIMEOUT_DEFAULT 250000U /* 250ms */ +#define FLASH_MAX_TIMEOUT_DEFAULT 300000U /* 300ms */ + +struct sgm3140 { + struct led_classdev_flash fled_cdev; + struct v4l2_flash *v4l2_flash; + + struct timer_list powerdown_timer; + + struct gpio_desc *flash_gpio; + struct gpio_desc *enable_gpio; + struct regulator *vin_regulator; + + bool enabled; + + /* current timeout in us */ + u32 timeout; + /* maximum timeout in us */ + u32 max_timeout; +}; + +static struct sgm3140 *flcdev_to_sgm3140(struct led_classdev_flash *flcdev) +{ + return container_of(flcdev, struct sgm3140, fled_cdev); +} + +static int sgm3140_strobe_set(struct led_classdev_flash *fled_cdev, bool state) +{ + struct sgm3140 *priv = flcdev_to_sgm3140(fled_cdev); + int ret; + + if (priv->enabled == state) + return 0; + + if (state) { + ret = regulator_enable(priv->vin_regulator); + if (ret) { + dev_err(fled_cdev->led_cdev.dev, + "failed to enable regulator: %d\n", ret); + return ret; + } + gpiod_set_value_cansleep(priv->flash_gpio, 1); + gpiod_set_value_cansleep(priv->enable_gpio, 1); + mod_timer(&priv->powerdown_timer, + jiffies + usecs_to_jiffies(priv->timeout)); + } else { + del_timer_sync(&priv->powerdown_timer); + gpiod_set_value_cansleep(priv->enable_gpio, 0); + gpiod_set_value_cansleep(priv->flash_gpio, 0); + ret = regulator_disable(priv->vin_regulator); + if (ret) { + dev_err(fled_cdev->led_cdev.dev, + "failed to disable regulator: %d\n", ret); + return ret; + } + } + + priv->enabled = state; + + return 0; +} + +static int sgm3140_strobe_get(struct led_classdev_flash *fled_cdev, bool *state) +{ + struct sgm3140 *priv = flcdev_to_sgm3140(fled_cdev); + + *state = timer_pending(&priv->powerdown_timer); + + return 0; +} + +static int sgm3140_timeout_set(struct led_classdev_flash *fled_cdev, + u32 timeout) +{ + struct sgm3140 *priv = flcdev_to_sgm3140(fled_cdev); + + priv->timeout = timeout; + + return 0; +} + +static const struct led_flash_ops sgm3140_flash_ops = { + .strobe_set = sgm3140_strobe_set, + .strobe_get = sgm3140_strobe_get, + .timeout_set = sgm3140_timeout_set, +}; + +static int sgm3140_brightness_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev); + struct sgm3140 *priv = flcdev_to_sgm3140(fled_cdev); + bool enable = brightness == LED_ON; + int ret; + + if (priv->enabled == enable) + return 0; + + if (enable) { + ret = regulator_enable(priv->vin_regulator); + if (ret) { + dev_err(led_cdev->dev, + "failed to enable regulator: %d\n", ret); + return ret; + } + gpiod_set_value_cansleep(priv->enable_gpio, 1); + } else { + gpiod_set_value_cansleep(priv->enable_gpio, 0); + ret = regulator_disable(priv->vin_regulator); + if (ret) { + dev_err(led_cdev->dev, + "failed to disable regulator: %d\n", ret); + return ret; + } + } + + priv->enabled = enable; + + return 0; +} + +static void sgm3140_powerdown_timer(struct timer_list *t) +{ + struct sgm3140 *priv = from_timer(priv, t, powerdown_timer); + + gpiod_set_value(priv->enable_gpio, 0); + gpiod_set_value(priv->flash_gpio, 0); + regulator_disable(priv->vin_regulator); + + priv->enabled = false; +} + +static void sgm3140_init_flash_timeout(struct sgm3140 *priv) +{ + struct led_classdev_flash *fled_cdev = &priv->fled_cdev; + struct led_flash_setting *s; + + /* Init flash timeout setting */ + s = &fled_cdev->timeout; + s->min = 1; + s->max = priv->max_timeout; + s->step = 1; + s->val = FLASH_TIMEOUT_DEFAULT; +} + +#if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS) +static void sgm3140_init_v4l2_flash_config(struct sgm3140 *priv, + struct v4l2_flash_config *v4l2_sd_cfg) +{ + struct led_classdev *led_cdev = &priv->fled_cdev.led_cdev; + struct led_flash_setting *s; + + strscpy(v4l2_sd_cfg->dev_name, led_cdev->dev->kobj.name, + sizeof(v4l2_sd_cfg->dev_name)); + + /* Init flash intensity setting */ + s = &v4l2_sd_cfg->intensity; + s->min = 0; + s->max = 1; + s->step = 1; + s->val = 1; +} + +#else +static void sgm3140_init_v4l2_flash_config(struct sgm3140 *priv, + struct v4l2_flash_config *v4l2_sd_cfg) +{ +} +#endif + +static int sgm3140_probe(struct platform_device *pdev) +{ + struct sgm3140 *priv; + struct led_classdev *led_cdev; + struct led_classdev_flash *fled_cdev; + struct led_init_data init_data = {}; + struct fwnode_handle *child_node; + struct v4l2_flash_config v4l2_sd_cfg = {}; + int ret; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->flash_gpio = devm_gpiod_get(&pdev->dev, "flash", GPIOD_OUT_LOW); + ret = PTR_ERR_OR_ZERO(priv->flash_gpio); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "Failed to request flash gpio\n"); + + priv->enable_gpio = devm_gpiod_get(&pdev->dev, "enable", GPIOD_OUT_LOW); + ret = PTR_ERR_OR_ZERO(priv->enable_gpio); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "Failed to request enable gpio\n"); + + priv->vin_regulator = devm_regulator_get(&pdev->dev, "vin"); + ret = PTR_ERR_OR_ZERO(priv->vin_regulator); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "Failed to request regulator\n"); + + child_node = fwnode_get_next_available_child_node(pdev->dev.fwnode, + NULL); + if (!child_node) { + dev_err(&pdev->dev, + "No fwnode child node found for connected LED.\n"); + return -EINVAL; + } + + ret = fwnode_property_read_u32(child_node, "flash-max-timeout-us", + &priv->max_timeout); + if (ret) { + priv->max_timeout = FLASH_MAX_TIMEOUT_DEFAULT; + dev_warn(&pdev->dev, + "flash-max-timeout-us property missing\n"); + } + + /* + * Set default timeout to FLASH_DEFAULT_TIMEOUT except if max_timeout + * from DT is lower. + */ + priv->timeout = min(priv->max_timeout, FLASH_TIMEOUT_DEFAULT); + + timer_setup(&priv->powerdown_timer, sgm3140_powerdown_timer, 0); + + fled_cdev = &priv->fled_cdev; + led_cdev = &fled_cdev->led_cdev; + + fled_cdev->ops = &sgm3140_flash_ops; + + led_cdev->brightness_set_blocking = sgm3140_brightness_set; + led_cdev->max_brightness = LED_ON; + led_cdev->flags |= LED_DEV_CAP_FLASH; + + sgm3140_init_flash_timeout(priv); + + init_data.fwnode = child_node; + + platform_set_drvdata(pdev, priv); + + /* Register in the LED subsystem */ + ret = devm_led_classdev_flash_register_ext(&pdev->dev, + fled_cdev, &init_data); + if (ret) { + dev_err(&pdev->dev, "Failed to register flash device: %d\n", + ret); + goto err; + } + + sgm3140_init_v4l2_flash_config(priv, &v4l2_sd_cfg); + + /* Create V4L2 Flash subdev */ + priv->v4l2_flash = v4l2_flash_init(&pdev->dev, + child_node, + fled_cdev, NULL, + &v4l2_sd_cfg); + if (IS_ERR(priv->v4l2_flash)) { + ret = PTR_ERR(priv->v4l2_flash); + goto err; + } + + return ret; + +err: + fwnode_handle_put(child_node); + return ret; +} + +static int sgm3140_remove(struct platform_device *pdev) +{ + struct sgm3140 *priv = platform_get_drvdata(pdev); + + del_timer_sync(&priv->powerdown_timer); + + v4l2_flash_release(priv->v4l2_flash); + + return 0; +} + +static const struct of_device_id sgm3140_dt_match[] = { + { .compatible = "sgmicro,sgm3140" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, sgm3140_dt_match); + +static struct platform_driver sgm3140_driver = { + .probe = sgm3140_probe, + .remove = sgm3140_remove, + .driver = { + .name = "sgm3140", + .of_match_table = sgm3140_dt_match, + }, +}; + +module_platform_driver(sgm3140_driver); + +MODULE_AUTHOR("Luca Weiss "); +MODULE_DESCRIPTION("SG Micro SGM3140 charge pump LED driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/leds/leds-sgm3140.c b/drivers/leds/leds-sgm3140.c deleted file mode 100644 index f4f831570f11..000000000000 --- a/drivers/leds/leds-sgm3140.c +++ /dev/null @@ -1,311 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -// Copyright (C) 2020 Luca Weiss - -#include -#include -#include -#include -#include - -#include - -#define FLASH_TIMEOUT_DEFAULT 250000U /* 250ms */ -#define FLASH_MAX_TIMEOUT_DEFAULT 300000U /* 300ms */ - -struct sgm3140 { - struct led_classdev_flash fled_cdev; - struct v4l2_flash *v4l2_flash; - - struct timer_list powerdown_timer; - - struct gpio_desc *flash_gpio; - struct gpio_desc *enable_gpio; - struct regulator *vin_regulator; - - bool enabled; - - /* current timeout in us */ - u32 timeout; - /* maximum timeout in us */ - u32 max_timeout; -}; - -static struct sgm3140 *flcdev_to_sgm3140(struct led_classdev_flash *flcdev) -{ - return container_of(flcdev, struct sgm3140, fled_cdev); -} - -static int sgm3140_strobe_set(struct led_classdev_flash *fled_cdev, bool state) -{ - struct sgm3140 *priv = flcdev_to_sgm3140(fled_cdev); - int ret; - - if (priv->enabled == state) - return 0; - - if (state) { - ret = regulator_enable(priv->vin_regulator); - if (ret) { - dev_err(fled_cdev->led_cdev.dev, - "failed to enable regulator: %d\n", ret); - return ret; - } - gpiod_set_value_cansleep(priv->flash_gpio, 1); - gpiod_set_value_cansleep(priv->enable_gpio, 1); - mod_timer(&priv->powerdown_timer, - jiffies + usecs_to_jiffies(priv->timeout)); - } else { - del_timer_sync(&priv->powerdown_timer); - gpiod_set_value_cansleep(priv->enable_gpio, 0); - gpiod_set_value_cansleep(priv->flash_gpio, 0); - ret = regulator_disable(priv->vin_regulator); - if (ret) { - dev_err(fled_cdev->led_cdev.dev, - "failed to disable regulator: %d\n", ret); - return ret; - } - } - - priv->enabled = state; - - return 0; -} - -static int sgm3140_strobe_get(struct led_classdev_flash *fled_cdev, bool *state) -{ - struct sgm3140 *priv = flcdev_to_sgm3140(fled_cdev); - - *state = timer_pending(&priv->powerdown_timer); - - return 0; -} - -static int sgm3140_timeout_set(struct led_classdev_flash *fled_cdev, - u32 timeout) -{ - struct sgm3140 *priv = flcdev_to_sgm3140(fled_cdev); - - priv->timeout = timeout; - - return 0; -} - -static const struct led_flash_ops sgm3140_flash_ops = { - .strobe_set = sgm3140_strobe_set, - .strobe_get = sgm3140_strobe_get, - .timeout_set = sgm3140_timeout_set, -}; - -static int sgm3140_brightness_set(struct led_classdev *led_cdev, - enum led_brightness brightness) -{ - struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev); - struct sgm3140 *priv = flcdev_to_sgm3140(fled_cdev); - bool enable = brightness == LED_ON; - int ret; - - if (priv->enabled == enable) - return 0; - - if (enable) { - ret = regulator_enable(priv->vin_regulator); - if (ret) { - dev_err(led_cdev->dev, - "failed to enable regulator: %d\n", ret); - return ret; - } - gpiod_set_value_cansleep(priv->enable_gpio, 1); - } else { - gpiod_set_value_cansleep(priv->enable_gpio, 0); - ret = regulator_disable(priv->vin_regulator); - if (ret) { - dev_err(led_cdev->dev, - "failed to disable regulator: %d\n", ret); - return ret; - } - } - - priv->enabled = enable; - - return 0; -} - -static void sgm3140_powerdown_timer(struct timer_list *t) -{ - struct sgm3140 *priv = from_timer(priv, t, powerdown_timer); - - gpiod_set_value(priv->enable_gpio, 0); - gpiod_set_value(priv->flash_gpio, 0); - regulator_disable(priv->vin_regulator); - - priv->enabled = false; -} - -static void sgm3140_init_flash_timeout(struct sgm3140 *priv) -{ - struct led_classdev_flash *fled_cdev = &priv->fled_cdev; - struct led_flash_setting *s; - - /* Init flash timeout setting */ - s = &fled_cdev->timeout; - s->min = 1; - s->max = priv->max_timeout; - s->step = 1; - s->val = FLASH_TIMEOUT_DEFAULT; -} - -#if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS) -static void sgm3140_init_v4l2_flash_config(struct sgm3140 *priv, - struct v4l2_flash_config *v4l2_sd_cfg) -{ - struct led_classdev *led_cdev = &priv->fled_cdev.led_cdev; - struct led_flash_setting *s; - - strscpy(v4l2_sd_cfg->dev_name, led_cdev->dev->kobj.name, - sizeof(v4l2_sd_cfg->dev_name)); - - /* Init flash intensity setting */ - s = &v4l2_sd_cfg->intensity; - s->min = 0; - s->max = 1; - s->step = 1; - s->val = 1; -} - -#else -static void sgm3140_init_v4l2_flash_config(struct sgm3140 *priv, - struct v4l2_flash_config *v4l2_sd_cfg) -{ -} -#endif - -static int sgm3140_probe(struct platform_device *pdev) -{ - struct sgm3140 *priv; - struct led_classdev *led_cdev; - struct led_classdev_flash *fled_cdev; - struct led_init_data init_data = {}; - struct fwnode_handle *child_node; - struct v4l2_flash_config v4l2_sd_cfg = {}; - int ret; - - priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - priv->flash_gpio = devm_gpiod_get(&pdev->dev, "flash", GPIOD_OUT_LOW); - ret = PTR_ERR_OR_ZERO(priv->flash_gpio); - if (ret) - return dev_err_probe(&pdev->dev, ret, - "Failed to request flash gpio\n"); - - priv->enable_gpio = devm_gpiod_get(&pdev->dev, "enable", GPIOD_OUT_LOW); - ret = PTR_ERR_OR_ZERO(priv->enable_gpio); - if (ret) - return dev_err_probe(&pdev->dev, ret, - "Failed to request enable gpio\n"); - - priv->vin_regulator = devm_regulator_get(&pdev->dev, "vin"); - ret = PTR_ERR_OR_ZERO(priv->vin_regulator); - if (ret) - return dev_err_probe(&pdev->dev, ret, - "Failed to request regulator\n"); - - child_node = fwnode_get_next_available_child_node(pdev->dev.fwnode, - NULL); - if (!child_node) { - dev_err(&pdev->dev, - "No fwnode child node found for connected LED.\n"); - return -EINVAL; - } - - ret = fwnode_property_read_u32(child_node, "flash-max-timeout-us", - &priv->max_timeout); - if (ret) { - priv->max_timeout = FLASH_MAX_TIMEOUT_DEFAULT; - dev_warn(&pdev->dev, - "flash-max-timeout-us property missing\n"); - } - - /* - * Set default timeout to FLASH_DEFAULT_TIMEOUT except if max_timeout - * from DT is lower. - */ - priv->timeout = min(priv->max_timeout, FLASH_TIMEOUT_DEFAULT); - - timer_setup(&priv->powerdown_timer, sgm3140_powerdown_timer, 0); - - fled_cdev = &priv->fled_cdev; - led_cdev = &fled_cdev->led_cdev; - - fled_cdev->ops = &sgm3140_flash_ops; - - led_cdev->brightness_set_blocking = sgm3140_brightness_set; - led_cdev->max_brightness = LED_ON; - led_cdev->flags |= LED_DEV_CAP_FLASH; - - sgm3140_init_flash_timeout(priv); - - init_data.fwnode = child_node; - - platform_set_drvdata(pdev, priv); - - /* Register in the LED subsystem */ - ret = devm_led_classdev_flash_register_ext(&pdev->dev, - fled_cdev, &init_data); - if (ret) { - dev_err(&pdev->dev, "Failed to register flash device: %d\n", - ret); - goto err; - } - - sgm3140_init_v4l2_flash_config(priv, &v4l2_sd_cfg); - - /* Create V4L2 Flash subdev */ - priv->v4l2_flash = v4l2_flash_init(&pdev->dev, - child_node, - fled_cdev, NULL, - &v4l2_sd_cfg); - if (IS_ERR(priv->v4l2_flash)) { - ret = PTR_ERR(priv->v4l2_flash); - goto err; - } - - return ret; - -err: - fwnode_handle_put(child_node); - return ret; -} - -static int sgm3140_remove(struct platform_device *pdev) -{ - struct sgm3140 *priv = platform_get_drvdata(pdev); - - del_timer_sync(&priv->powerdown_timer); - - v4l2_flash_release(priv->v4l2_flash); - - return 0; -} - -static const struct of_device_id sgm3140_dt_match[] = { - { .compatible = "sgmicro,sgm3140" }, - { /* sentinel */ } -}; -MODULE_DEVICE_TABLE(of, sgm3140_dt_match); - -static struct platform_driver sgm3140_driver = { - .probe = sgm3140_probe, - .remove = sgm3140_remove, - .driver = { - .name = "sgm3140", - .of_match_table = sgm3140_dt_match, - }, -}; - -module_platform_driver(sgm3140_driver); - -MODULE_AUTHOR("Luca Weiss "); -MODULE_DESCRIPTION("SG Micro SGM3140 charge pump LED driver"); -MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 61fa67a4e538f70bb4e6b3e7f3555402c1713654 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 28 May 2021 22:24:03 +0200 Subject: leds: lm3601x: Move driver to flash subdirectory We created a subdirectory for LED drivers that depend on CONFIG_LEDS_CLASS_FLASH, and this driver does so let's move it there. Cc: Dan Murphy Signed-off-by: Linus Walleij Signed-off-by: Pavel Machek --- drivers/leds/Kconfig | 9 - drivers/leds/Makefile | 1 - drivers/leds/flash/Kconfig | 8 + drivers/leds/flash/Makefile | 1 + drivers/leds/flash/leds-lm3601x.c | 481 ++++++++++++++++++++++++++++++++++++++ drivers/leds/leds-lm3601x.c | 481 -------------------------------------- 6 files changed, 490 insertions(+), 491 deletions(-) create mode 100644 drivers/leds/flash/leds-lm3601x.c delete mode 100644 drivers/leds/leds-lm3601x.c diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 171ccfd4de1b..1671aa2f90b5 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -220,15 +220,6 @@ config LEDS_LM3692X This option enables support for the TI LM3692x family of white LED string drivers used for backlighting. -config LEDS_LM3601X - tristate "LED support for LM3601x Chips" - depends on LEDS_CLASS && I2C - depends on LEDS_CLASS_FLASH - select REGMAP_I2C - help - This option enables support for the TI LM3601x family - of flash, torch and indicator classes. - config LEDS_LOCOMO tristate "LED Support for Locomo device" depends on LEDS_CLASS diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index 5e804f72b8e5..6d5c23afaf98 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -40,7 +40,6 @@ obj-$(CONFIG_LEDS_LM3530) += leds-lm3530.o obj-$(CONFIG_LEDS_LM3532) += leds-lm3532.o obj-$(CONFIG_LEDS_LM3533) += leds-lm3533.o obj-$(CONFIG_LEDS_LM355x) += leds-lm355x.o -obj-$(CONFIG_LEDS_LM3601X) += leds-lm3601x.o obj-$(CONFIG_LEDS_LM36274) += leds-lm36274.o obj-$(CONFIG_LEDS_LM3642) += leds-lm3642.o obj-$(CONFIG_LEDS_LM3692X) += leds-lm3692x.o diff --git a/drivers/leds/flash/Kconfig b/drivers/leds/flash/Kconfig index 788d698587a7..6cb6600555f0 100644 --- a/drivers/leds/flash/Kconfig +++ b/drivers/leds/flash/Kconfig @@ -20,6 +20,14 @@ config LEDS_AS3645A controller. V4L2 flash API is provided as well if CONFIG_V4L2_FLASH_API is enabled. +config LEDS_LM3601X + tristate "LED support for LM3601x Chips" + depends on LEDS_CLASS && I2C + select REGMAP_I2C + help + This option enables support for the TI LM3601x family + of flash, torch and indicator classes. + config LEDS_MAX77693 tristate "LED support for MAX77693 Flash" depends on V4L2_FLASH_LED_CLASS || !V4L2_FLASH_LED_CLASS diff --git a/drivers/leds/flash/Makefile b/drivers/leds/flash/Makefile index 2f9153e78717..67556329441e 100644 --- a/drivers/leds/flash/Makefile +++ b/drivers/leds/flash/Makefile @@ -2,6 +2,7 @@ obj-$(CONFIG_LEDS_AAT1290) += leds-aat1290.o obj-$(CONFIG_LEDS_AS3645A) += leds-as3645a.o +obj-$(CONFIG_LEDS_LM3601X) += leds-lm3601x.o obj-$(CONFIG_LEDS_MAX77693) += leds-max77693.o obj-$(CONFIG_LEDS_RT4505) += leds-rt4505.o obj-$(CONFIG_LEDS_RT8515) += leds-rt8515.o diff --git a/drivers/leds/flash/leds-lm3601x.c b/drivers/leds/flash/leds-lm3601x.c new file mode 100644 index 000000000000..d0e1d4814042 --- /dev/null +++ b/drivers/leds/flash/leds-lm3601x.c @@ -0,0 +1,481 @@ +// SPDX-License-Identifier: GPL-2.0 +// Flash and torch driver for Texas Instruments LM3601X LED +// Flash driver chip family +// Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/ + +#include +#include +#include +#include +#include +#include +#include + +#define LM3601X_LED_IR 0x0 +#define LM3601X_LED_TORCH 0x1 + +/* Registers */ +#define LM3601X_ENABLE_REG 0x01 +#define LM3601X_CFG_REG 0x02 +#define LM3601X_LED_FLASH_REG 0x03 +#define LM3601X_LED_TORCH_REG 0x04 +#define LM3601X_FLAGS_REG 0x05 +#define LM3601X_DEV_ID_REG 0x06 + +#define LM3601X_SW_RESET BIT(7) + +/* Enable Mode bits */ +#define LM3601X_MODE_STANDBY 0x00 +#define LM3601X_MODE_IR_DRV BIT(0) +#define LM3601X_MODE_TORCH BIT(1) +#define LM3601X_MODE_STROBE (BIT(0) | BIT(1)) +#define LM3601X_STRB_EN BIT(2) +#define LM3601X_STRB_EDGE_TRIG BIT(3) +#define LM3601X_IVFM_EN BIT(4) + +#define LM36010_BOOST_LIMIT_28 BIT(5) +#define LM36010_BOOST_FREQ_4MHZ BIT(6) +#define LM36010_BOOST_MODE_PASS BIT(7) + +/* Flag Mask */ +#define LM3601X_FLASH_TIME_OUT BIT(0) +#define LM3601X_UVLO_FAULT BIT(1) +#define LM3601X_THERM_SHUTDOWN BIT(2) +#define LM3601X_THERM_CURR BIT(3) +#define LM36010_CURR_LIMIT BIT(4) +#define LM3601X_SHORT_FAULT BIT(5) +#define LM3601X_IVFM_TRIP BIT(6) +#define LM36010_OVP_FAULT BIT(7) + +#define LM3601X_MAX_TORCH_I_UA 376000 +#define LM3601X_MIN_TORCH_I_UA 2400 +#define LM3601X_TORCH_REG_DIV 2965 + +#define LM3601X_MAX_STROBE_I_UA 1500000 +#define LM3601X_MIN_STROBE_I_UA 11000 +#define LM3601X_STROBE_REG_DIV 11800 + +#define LM3601X_TIMEOUT_MASK 0x1e +#define LM3601X_ENABLE_MASK (LM3601X_MODE_IR_DRV | LM3601X_MODE_TORCH) + +#define LM3601X_LOWER_STEP_US 40000 +#define LM3601X_UPPER_STEP_US 200000 +#define LM3601X_MIN_TIMEOUT_US 40000 +#define LM3601X_MAX_TIMEOUT_US 1600000 +#define LM3601X_TIMEOUT_XOVER_US 400000 + +enum lm3601x_type { + CHIP_LM36010 = 0, + CHIP_LM36011, +}; + +/** + * struct lm3601x_led - + * @fled_cdev: flash LED class device pointer + * @client: Pointer to the I2C client + * @regmap: Devices register map + * @lock: Lock for reading/writing the device + * @led_name: LED label for the Torch or IR LED + * @flash_timeout: the timeout for the flash + * @last_flag: last known flags register value + * @torch_current_max: maximum current for the torch + * @flash_current_max: maximum current for the flash + * @max_flash_timeout: maximum timeout for the flash + * @led_mode: The mode to enable either IR or Torch + */ +struct lm3601x_led { + struct led_classdev_flash fled_cdev; + struct i2c_client *client; + struct regmap *regmap; + struct mutex lock; + + unsigned int flash_timeout; + unsigned int last_flag; + + u32 torch_current_max; + u32 flash_current_max; + u32 max_flash_timeout; + + u32 led_mode; +}; + +static const struct reg_default lm3601x_regmap_defs[] = { + { LM3601X_ENABLE_REG, 0x20 }, + { LM3601X_CFG_REG, 0x15 }, + { LM3601X_LED_FLASH_REG, 0x00 }, + { LM3601X_LED_TORCH_REG, 0x00 }, +}; + +static bool lm3601x_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case LM3601X_FLAGS_REG: + return true; + default: + return false; + } +} + +static const struct regmap_config lm3601x_regmap = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = LM3601X_DEV_ID_REG, + .reg_defaults = lm3601x_regmap_defs, + .num_reg_defaults = ARRAY_SIZE(lm3601x_regmap_defs), + .cache_type = REGCACHE_RBTREE, + .volatile_reg = lm3601x_volatile_reg, +}; + +static struct lm3601x_led *fled_cdev_to_led(struct led_classdev_flash *fled_cdev) +{ + return container_of(fled_cdev, struct lm3601x_led, fled_cdev); +} + +static int lm3601x_read_faults(struct lm3601x_led *led) +{ + int flags_val; + int ret; + + ret = regmap_read(led->regmap, LM3601X_FLAGS_REG, &flags_val); + if (ret < 0) + return -EIO; + + led->last_flag = 0; + + if (flags_val & LM36010_OVP_FAULT) + led->last_flag |= LED_FAULT_OVER_VOLTAGE; + + if (flags_val & (LM3601X_THERM_SHUTDOWN | LM3601X_THERM_CURR)) + led->last_flag |= LED_FAULT_OVER_TEMPERATURE; + + if (flags_val & LM3601X_SHORT_FAULT) + led->last_flag |= LED_FAULT_SHORT_CIRCUIT; + + if (flags_val & LM36010_CURR_LIMIT) + led->last_flag |= LED_FAULT_OVER_CURRENT; + + if (flags_val & LM3601X_UVLO_FAULT) + led->last_flag |= LED_FAULT_UNDER_VOLTAGE; + + if (flags_val & LM3601X_IVFM_TRIP) + led->last_flag |= LED_FAULT_INPUT_VOLTAGE; + + if (flags_val & LM3601X_THERM_SHUTDOWN) + led->last_flag |= LED_FAULT_LED_OVER_TEMPERATURE; + + return led->last_flag; +} + +static int lm3601x_brightness_set(struct led_classdev *cdev, + enum led_brightness brightness) +{ + struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(cdev); + struct lm3601x_led *led = fled_cdev_to_led(fled_cdev); + int ret, led_mode_val; + + mutex_lock(&led->lock); + + ret = lm3601x_read_faults(led); + if (ret < 0) + goto out; + + if (led->led_mode == LM3601X_LED_TORCH) + led_mode_val = LM3601X_MODE_TORCH; + else + led_mode_val = LM3601X_MODE_IR_DRV; + + if (brightness == LED_OFF) { + ret = regmap_update_bits(led->regmap, LM3601X_ENABLE_REG, + led_mode_val, LED_OFF); + goto out; + } + + ret = regmap_write(led->regmap, LM3601X_LED_TORCH_REG, brightness); + if (ret < 0) + goto out; + + ret = regmap_update_bits(led->regmap, LM3601X_ENABLE_REG, + LM3601X_MODE_TORCH | LM3601X_MODE_IR_DRV, + led_mode_val); +out: + mutex_unlock(&led->lock); + return ret; +} + +static int lm3601x_strobe_set(struct led_classdev_flash *fled_cdev, + bool state) +{ + struct lm3601x_led *led = fled_cdev_to_led(fled_cdev); + int timeout_reg_val; + int current_timeout; + int ret; + + mutex_lock(&led->lock); + + ret = regmap_read(led->regmap, LM3601X_CFG_REG, ¤t_timeout); + if (ret < 0) + goto out; + + if (led->flash_timeout >= LM3601X_TIMEOUT_XOVER_US) + timeout_reg_val = led->flash_timeout / LM3601X_UPPER_STEP_US + 0x07; + else + timeout_reg_val = led->flash_timeout / LM3601X_LOWER_STEP_US - 0x01; + + if (led->flash_timeout != current_timeout) + ret = regmap_update_bits(led->regmap, LM3601X_CFG_REG, + LM3601X_TIMEOUT_MASK, timeout_reg_val); + + if (state) + ret = regmap_update_bits(led->regmap, LM3601X_ENABLE_REG, + LM3601X_MODE_TORCH | LM3601X_MODE_IR_DRV, + LM3601X_MODE_STROBE); + else + ret = regmap_update_bits(led->regmap, LM3601X_ENABLE_REG, + LM3601X_MODE_STROBE, LED_OFF); + + ret = lm3601x_read_faults(led); +out: + mutex_unlock(&led->lock); + return ret; +} + +static int lm3601x_flash_brightness_set(struct led_classdev_flash *fled_cdev, + u32 brightness) +{ + struct lm3601x_led *led = fled_cdev_to_led(fled_cdev); + u8 brightness_val; + int ret; + + mutex_lock(&led->lock); + ret = lm3601x_read_faults(led); + if (ret < 0) + goto out; + + if (brightness == LED_OFF) { + ret = regmap_update_bits(led->regmap, LM3601X_ENABLE_REG, + LM3601X_MODE_STROBE, LED_OFF); + goto out; + } + + brightness_val = brightness / LM3601X_STROBE_REG_DIV; + + ret = regmap_write(led->regmap, LM3601X_LED_FLASH_REG, brightness_val); +out: + mutex_unlock(&led->lock); + return ret; +} + +static int lm3601x_flash_timeout_set(struct led_classdev_flash *fled_cdev, + u32 timeout) +{ + struct lm3601x_led *led = fled_cdev_to_led(fled_cdev); + + mutex_lock(&led->lock); + + led->flash_timeout = timeout; + + mutex_unlock(&led->lock); + + return 0; +} + +static int lm3601x_strobe_get(struct led_classdev_flash *fled_cdev, bool *state) +{ + struct lm3601x_led *led = fled_cdev_to_led(fled_cdev); + int strobe_state; + int ret; + + mutex_lock(&led->lock); + + ret = regmap_read(led->regmap, LM3601X_ENABLE_REG, &strobe_state); + if (ret < 0) + goto out; + + *state = strobe_state & LM3601X_MODE_STROBE; + +out: + mutex_unlock(&led->lock); + return ret; +} + +static int lm3601x_flash_fault_get(struct led_classdev_flash *fled_cdev, + u32 *fault) +{ + struct lm3601x_led *led = fled_cdev_to_led(fled_cdev); + + lm3601x_read_faults(led); + + *fault = led->last_flag; + + return 0; +} + +static const struct led_flash_ops flash_ops = { + .flash_brightness_set = lm3601x_flash_brightness_set, + .strobe_set = lm3601x_strobe_set, + .strobe_get = lm3601x_strobe_get, + .timeout_set = lm3601x_flash_timeout_set, + .fault_get = lm3601x_flash_fault_get, +}; + +static int lm3601x_register_leds(struct lm3601x_led *led, + struct fwnode_handle *fwnode) +{ + struct led_classdev *led_cdev; + struct led_flash_setting *setting; + struct led_init_data init_data = {}; + + led->fled_cdev.ops = &flash_ops; + + setting = &led->fled_cdev.timeout; + setting->min = LM3601X_MIN_TIMEOUT_US; + setting->max = led->max_flash_timeout; + setting->step = LM3601X_LOWER_STEP_US; + setting->val = led->max_flash_timeout; + + setting = &led->fled_cdev.brightness; + setting->min = LM3601X_MIN_STROBE_I_UA; + setting->max = led->flash_current_max; + setting->step = LM3601X_TORCH_REG_DIV; + setting->val = led->flash_current_max; + + led_cdev = &led->fled_cdev.led_cdev; + led_cdev->brightness_set_blocking = lm3601x_brightness_set; + led_cdev->max_brightness = DIV_ROUND_UP(led->torch_current_max, + LM3601X_TORCH_REG_DIV); + led_cdev->flags |= LED_DEV_CAP_FLASH; + + init_data.fwnode = fwnode; + init_data.devicename = led->client->name; + init_data.default_label = (led->led_mode == LM3601X_LED_TORCH) ? + "torch" : "infrared"; + return devm_led_classdev_flash_register_ext(&led->client->dev, + &led->fled_cdev, &init_data); +} + +static int lm3601x_parse_node(struct lm3601x_led *led, + struct fwnode_handle **fwnode) +{ + struct fwnode_handle *child = NULL; + int ret = -ENODEV; + + child = device_get_next_child_node(&led->client->dev, child); + if (!child) { + dev_err(&led->client->dev, "No LED Child node\n"); + return ret; + } + + ret = fwnode_property_read_u32(child, "reg", &led->led_mode); + if (ret) { + dev_err(&led->client->dev, "reg DT property missing\n"); + goto out_err; + } + + if (led->led_mode > LM3601X_LED_TORCH || + led->led_mode < LM3601X_LED_IR) { + dev_warn(&led->client->dev, "Invalid led mode requested\n"); + ret = -EINVAL; + goto out_err; + } + + ret = fwnode_property_read_u32(child, "led-max-microamp", + &led->torch_current_max); + if (ret) { + dev_warn(&led->client->dev, + "led-max-microamp DT property missing\n"); + goto out_err; + } + + ret = fwnode_property_read_u32(child, "flash-max-microamp", + &led->flash_current_max); + if (ret) { + dev_warn(&led->client->dev, + "flash-max-microamp DT property missing\n"); + goto out_err; + } + + ret = fwnode_property_read_u32(child, "flash-max-timeout-us", + &led->max_flash_timeout); + if (ret) { + dev_warn(&led->client->dev, + "flash-max-timeout-us DT property missing\n"); + goto out_err; + } + + *fwnode = child; + +out_err: + fwnode_handle_put(child); + return ret; +} + +static int lm3601x_probe(struct i2c_client *client) +{ + struct lm3601x_led *led; + struct fwnode_handle *fwnode; + int ret; + + led = devm_kzalloc(&client->dev, sizeof(*led), GFP_KERNEL); + if (!led) + return -ENOMEM; + + led->client = client; + i2c_set_clientdata(client, led); + + ret = lm3601x_parse_node(led, &fwnode); + if (ret) + return -ENODEV; + + led->regmap = devm_regmap_init_i2c(client, &lm3601x_regmap); + if (IS_ERR(led->regmap)) { + ret = PTR_ERR(led->regmap); + dev_err(&client->dev, + "Failed to allocate register map: %d\n", ret); + return ret; + } + + mutex_init(&led->lock); + + return lm3601x_register_leds(led, fwnode); +} + +static int lm3601x_remove(struct i2c_client *client) +{ + struct lm3601x_led *led = i2c_get_clientdata(client); + + mutex_destroy(&led->lock); + + return regmap_update_bits(led->regmap, LM3601X_ENABLE_REG, + LM3601X_ENABLE_MASK, + LM3601X_MODE_STANDBY); +} + +static const struct i2c_device_id lm3601x_id[] = { + { "LM36010", CHIP_LM36010 }, + { "LM36011", CHIP_LM36011 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, lm3601x_id); + +static const struct of_device_id of_lm3601x_leds_match[] = { + { .compatible = "ti,lm36010", }, + { .compatible = "ti,lm36011", }, + { } +}; +MODULE_DEVICE_TABLE(of, of_lm3601x_leds_match); + +static struct i2c_driver lm3601x_i2c_driver = { + .driver = { + .name = "lm3601x", + .of_match_table = of_lm3601x_leds_match, + }, + .probe_new = lm3601x_probe, + .remove = lm3601x_remove, + .id_table = lm3601x_id, +}; +module_i2c_driver(lm3601x_i2c_driver); + +MODULE_DESCRIPTION("Texas Instruments Flash Lighting driver for LM3601X"); +MODULE_AUTHOR("Dan Murphy "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/leds/leds-lm3601x.c b/drivers/leds/leds-lm3601x.c deleted file mode 100644 index d0e1d4814042..000000000000 --- a/drivers/leds/leds-lm3601x.c +++ /dev/null @@ -1,481 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -// Flash and torch driver for Texas Instruments LM3601X LED -// Flash driver chip family -// Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/ - -#include -#include -#include -#include -#include -#include -#include - -#define LM3601X_LED_IR 0x0 -#define LM3601X_LED_TORCH 0x1 - -/* Registers */ -#define LM3601X_ENABLE_REG 0x01 -#define LM3601X_CFG_REG 0x02 -#define LM3601X_LED_FLASH_REG 0x03 -#define LM3601X_LED_TORCH_REG 0x04 -#define LM3601X_FLAGS_REG 0x05 -#define LM3601X_DEV_ID_REG 0x06 - -#define LM3601X_SW_RESET BIT(7) - -/* Enable Mode bits */ -#define LM3601X_MODE_STANDBY 0x00 -#define LM3601X_MODE_IR_DRV BIT(0) -#define LM3601X_MODE_TORCH BIT(1) -#define LM3601X_MODE_STROBE (BIT(0) | BIT(1)) -#define LM3601X_STRB_EN BIT(2) -#define LM3601X_STRB_EDGE_TRIG BIT(3) -#define LM3601X_IVFM_EN BIT(4) - -#define LM36010_BOOST_LIMIT_28 BIT(5) -#define LM36010_BOOST_FREQ_4MHZ BIT(6) -#define LM36010_BOOST_MODE_PASS BIT(7) - -/* Flag Mask */ -#define LM3601X_FLASH_TIME_OUT BIT(0) -#define LM3601X_UVLO_FAULT BIT(1) -#define LM3601X_THERM_SHUTDOWN BIT(2) -#define LM3601X_THERM_CURR BIT(3) -#define LM36010_CURR_LIMIT BIT(4) -#define LM3601X_SHORT_FAULT BIT(5) -#define LM3601X_IVFM_TRIP BIT(6) -#define LM36010_OVP_FAULT BIT(7) - -#define LM3601X_MAX_TORCH_I_UA 376000 -#define LM3601X_MIN_TORCH_I_UA 2400 -#define LM3601X_TORCH_REG_DIV 2965 - -#define LM3601X_MAX_STROBE_I_UA 1500000 -#define LM3601X_MIN_STROBE_I_UA 11000 -#define LM3601X_STROBE_REG_DIV 11800 - -#define LM3601X_TIMEOUT_MASK 0x1e -#define LM3601X_ENABLE_MASK (LM3601X_MODE_IR_DRV | LM3601X_MODE_TORCH) - -#define LM3601X_LOWER_STEP_US 40000 -#define LM3601X_UPPER_STEP_US 200000 -#define LM3601X_MIN_TIMEOUT_US 40000 -#define LM3601X_MAX_TIMEOUT_US 1600000 -#define LM3601X_TIMEOUT_XOVER_US 400000 - -enum lm3601x_type { - CHIP_LM36010 = 0, - CHIP_LM36011, -}; - -/** - * struct lm3601x_led - - * @fled_cdev: flash LED class device pointer - * @client: Pointer to the I2C client - * @regmap: Devices register map - * @lock: Lock for reading/writing the device - * @led_name: LED label for the Torch or IR LED - * @flash_timeout: the timeout for the flash - * @last_flag: last known flags register value - * @torch_current_max: maximum current for the torch - * @flash_current_max: maximum current for the flash - * @max_flash_timeout: maximum timeout for the flash - * @led_mode: The mode to enable either IR or Torch - */ -struct lm3601x_led { - struct led_classdev_flash fled_cdev; - struct i2c_client *client; - struct regmap *regmap; - struct mutex lock; - - unsigned int flash_timeout; - unsigned int last_flag; - - u32 torch_current_max; - u32 flash_current_max; - u32 max_flash_timeout; - - u32 led_mode; -}; - -static const struct reg_default lm3601x_regmap_defs[] = { - { LM3601X_ENABLE_REG, 0x20 }, - { LM3601X_CFG_REG, 0x15 }, - { LM3601X_LED_FLASH_REG, 0x00 }, - { LM3601X_LED_TORCH_REG, 0x00 }, -}; - -static bool lm3601x_volatile_reg(struct device *dev, unsigned int reg) -{ - switch (reg) { - case LM3601X_FLAGS_REG: - return true; - default: - return false; - } -} - -static const struct regmap_config lm3601x_regmap = { - .reg_bits = 8, - .val_bits = 8, - - .max_register = LM3601X_DEV_ID_REG, - .reg_defaults = lm3601x_regmap_defs, - .num_reg_defaults = ARRAY_SIZE(lm3601x_regmap_defs), - .cache_type = REGCACHE_RBTREE, - .volatile_reg = lm3601x_volatile_reg, -}; - -static struct lm3601x_led *fled_cdev_to_led(struct led_classdev_flash *fled_cdev) -{ - return container_of(fled_cdev, struct lm3601x_led, fled_cdev); -} - -static int lm3601x_read_faults(struct lm3601x_led *led) -{ - int flags_val; - int ret; - - ret = regmap_read(led->regmap, LM3601X_FLAGS_REG, &flags_val); - if (ret < 0) - return -EIO; - - led->last_flag = 0; - - if (flags_val & LM36010_OVP_FAULT) - led->last_flag |= LED_FAULT_OVER_VOLTAGE; - - if (flags_val & (LM3601X_THERM_SHUTDOWN | LM3601X_THERM_CURR)) - led->last_flag |= LED_FAULT_OVER_TEMPERATURE; - - if (flags_val & LM3601X_SHORT_FAULT) - led->last_flag |= LED_FAULT_SHORT_CIRCUIT; - - if (flags_val & LM36010_CURR_LIMIT) - led->last_flag |= LED_FAULT_OVER_CURRENT; - - if (flags_val & LM3601X_UVLO_FAULT) - led->last_flag |= LED_FAULT_UNDER_VOLTAGE; - - if (flags_val & LM3601X_IVFM_TRIP) - led->last_flag |= LED_FAULT_INPUT_VOLTAGE; - - if (flags_val & LM3601X_THERM_SHUTDOWN) - led->last_flag |= LED_FAULT_LED_OVER_TEMPERATURE; - - return led->last_flag; -} - -static int lm3601x_brightness_set(struct led_classdev *cdev, - enum led_brightness brightness) -{ - struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(cdev); - struct lm3601x_led *led = fled_cdev_to_led(fled_cdev); - int ret, led_mode_val; - - mutex_lock(&led->lock); - - ret = lm3601x_read_faults(led); - if (ret < 0) - goto out; - - if (led->led_mode == LM3601X_LED_TORCH) - led_mode_val = LM3601X_MODE_TORCH; - else - led_mode_val = LM3601X_MODE_IR_DRV; - - if (brightness == LED_OFF) { - ret = regmap_update_bits(led->regmap, LM3601X_ENABLE_REG, - led_mode_val, LED_OFF); - goto out; - } - - ret = regmap_write(led->regmap, LM3601X_LED_TORCH_REG, brightness); - if (ret < 0) - goto out; - - ret = regmap_update_bits(led->regmap, LM3601X_ENABLE_REG, - LM3601X_MODE_TORCH | LM3601X_MODE_IR_DRV, - led_mode_val); -out: - mutex_unlock(&led->lock); - return ret; -} - -static int lm3601x_strobe_set(struct led_classdev_flash *fled_cdev, - bool state) -{ - struct lm3601x_led *led = fled_cdev_to_led(fled_cdev); - int timeout_reg_val; - int current_timeout; - int ret; - - mutex_lock(&led->lock); - - ret = regmap_read(led->regmap, LM3601X_CFG_REG, ¤t_timeout); - if (ret < 0) - goto out; - - if (led->flash_timeout >= LM3601X_TIMEOUT_XOVER_US) - timeout_reg_val = led->flash_timeout / LM3601X_UPPER_STEP_US + 0x07; - else - timeout_reg_val = led->flash_timeout / LM3601X_LOWER_STEP_US - 0x01; - - if (led->flash_timeout != current_timeout) - ret = regmap_update_bits(led->regmap, LM3601X_CFG_REG, - LM3601X_TIMEOUT_MASK, timeout_reg_val); - - if (state) - ret = regmap_update_bits(led->regmap, LM3601X_ENABLE_REG, - LM3601X_MODE_TORCH | LM3601X_MODE_IR_DRV, - LM3601X_MODE_STROBE); - else - ret = regmap_update_bits(led->regmap, LM3601X_ENABLE_REG, - LM3601X_MODE_STROBE, LED_OFF); - - ret = lm3601x_read_faults(led); -out: - mutex_unlock(&led->lock); - return ret; -} - -static int lm3601x_flash_brightness_set(struct led_classdev_flash *fled_cdev, - u32 brightness) -{ - struct lm3601x_led *led = fled_cdev_to_led(fled_cdev); - u8 brightness_val; - int ret; - - mutex_lock(&led->lock); - ret = lm3601x_read_faults(led); - if (ret < 0) - goto out; - - if (brightness == LED_OFF) { - ret = regmap_update_bits(led->regmap, LM3601X_ENABLE_REG, - LM3601X_MODE_STROBE, LED_OFF); - goto out; - } - - brightness_val = brightness / LM3601X_STROBE_REG_DIV; - - ret = regmap_write(led->regmap, LM3601X_LED_FLASH_REG, brightness_val); -out: - mutex_unlock(&led->lock); - return ret; -} - -static int lm3601x_flash_timeout_set(struct led_classdev_flash *fled_cdev, - u32 timeout) -{ - struct lm3601x_led *led = fled_cdev_to_led(fled_cdev); - - mutex_lock(&led->lock); - - led->flash_timeout = timeout; - - mutex_unlock(&led->lock); - - return 0; -} - -static int lm3601x_strobe_get(struct led_classdev_flash *fled_cdev, bool *state) -{ - struct lm3601x_led *led = fled_cdev_to_led(fled_cdev); - int strobe_state; - int ret; - - mutex_lock(&led->lock); - - ret = regmap_read(led->regmap, LM3601X_ENABLE_REG, &strobe_state); - if (ret < 0) - goto out; - - *state = strobe_state & LM3601X_MODE_STROBE; - -out: - mutex_unlock(&led->lock); - return ret; -} - -static int lm3601x_flash_fault_get(struct led_classdev_flash *fled_cdev, - u32 *fault) -{ - struct lm3601x_led *led = fled_cdev_to_led(fled_cdev); - - lm3601x_read_faults(led); - - *fault = led->last_flag; - - return 0; -} - -static const struct led_flash_ops flash_ops = { - .flash_brightness_set = lm3601x_flash_brightness_set, - .strobe_set = lm3601x_strobe_set, - .strobe_get = lm3601x_strobe_get, - .timeout_set = lm3601x_flash_timeout_set, - .fault_get = lm3601x_flash_fault_get, -}; - -static int lm3601x_register_leds(struct lm3601x_led *led, - struct fwnode_handle *fwnode) -{ - struct led_classdev *led_cdev; - struct led_flash_setting *setting; - struct led_init_data init_data = {}; - - led->fled_cdev.ops = &flash_ops; - - setting = &led->fled_cdev.timeout; - setting->min = LM3601X_MIN_TIMEOUT_US; - setting->max = led->max_flash_timeout; - setting->step = LM3601X_LOWER_STEP_US; - setting->val = led->max_flash_timeout; - - setting = &led->fled_cdev.brightness; - setting->min = LM3601X_MIN_STROBE_I_UA; - setting->max = led->flash_current_max; - setting->step = LM3601X_TORCH_REG_DIV; - setting->val = led->flash_current_max; - - led_cdev = &led->fled_cdev.led_cdev; - led_cdev->brightness_set_blocking = lm3601x_brightness_set; - led_cdev->max_brightness = DIV_ROUND_UP(led->torch_current_max, - LM3601X_TORCH_REG_DIV); - led_cdev->flags |= LED_DEV_CAP_FLASH; - - init_data.fwnode = fwnode; - init_data.devicename = led->client->name; - init_data.default_label = (led->led_mode == LM3601X_LED_TORCH) ? - "torch" : "infrared"; - return devm_led_classdev_flash_register_ext(&led->client->dev, - &led->fled_cdev, &init_data); -} - -static int lm3601x_parse_node(struct lm3601x_led *led, - struct fwnode_handle **fwnode) -{ - struct fwnode_handle *child = NULL; - int ret = -ENODEV; - - child = device_get_next_child_node(&led->client->dev, child); - if (!child) { - dev_err(&led->client->dev, "No LED Child node\n"); - return ret; - } - - ret = fwnode_property_read_u32(child, "reg", &led->led_mode); - if (ret) { - dev_err(&led->client->dev, "reg DT property missing\n"); - goto out_err; - } - - if (led->led_mode > LM3601X_LED_TORCH || - led->led_mode < LM3601X_LED_IR) { - dev_warn(&led->client->dev, "Invalid led mode requested\n"); - ret = -EINVAL; - goto out_err; - } - - ret = fwnode_property_read_u32(child, "led-max-microamp", - &led->torch_current_max); - if (ret) { - dev_warn(&led->client->dev, - "led-max-microamp DT property missing\n"); - goto out_err; - } - - ret = fwnode_property_read_u32(child, "flash-max-microamp", - &led->flash_current_max); - if (ret) { - dev_warn(&led->client->dev, - "flash-max-microamp DT property missing\n"); - goto out_err; - } - - ret = fwnode_property_read_u32(child, "flash-max-timeout-us", - &led->max_flash_timeout); - if (ret) { - dev_warn(&led->client->dev, - "flash-max-timeout-us DT property missing\n"); - goto out_err; - } - - *fwnode = child; - -out_err: - fwnode_handle_put(child); - return ret; -} - -static int lm3601x_probe(struct i2c_client *client) -{ - struct lm3601x_led *led; - struct fwnode_handle *fwnode; - int ret; - - led = devm_kzalloc(&client->dev, sizeof(*led), GFP_KERNEL); - if (!led) - return -ENOMEM; - - led->client = client; - i2c_set_clientdata(client, led); - - ret = lm3601x_parse_node(led, &fwnode); - if (ret) - return -ENODEV; - - led->regmap = devm_regmap_init_i2c(client, &lm3601x_regmap); - if (IS_ERR(led->regmap)) { - ret = PTR_ERR(led->regmap); - dev_err(&client->dev, - "Failed to allocate register map: %d\n", ret); - return ret; - } - - mutex_init(&led->lock); - - return lm3601x_register_leds(led, fwnode); -} - -static int lm3601x_remove(struct i2c_client *client) -{ - struct lm3601x_led *led = i2c_get_clientdata(client); - - mutex_destroy(&led->lock); - - return regmap_update_bits(led->regmap, LM3601X_ENABLE_REG, - LM3601X_ENABLE_MASK, - LM3601X_MODE_STANDBY); -} - -static const struct i2c_device_id lm3601x_id[] = { - { "LM36010", CHIP_LM36010 }, - { "LM36011", CHIP_LM36011 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, lm3601x_id); - -static const struct of_device_id of_lm3601x_leds_match[] = { - { .compatible = "ti,lm36010", }, - { .compatible = "ti,lm36011", }, - { } -}; -MODULE_DEVICE_TABLE(of, of_lm3601x_leds_match); - -static struct i2c_driver lm3601x_i2c_driver = { - .driver = { - .name = "lm3601x", - .of_match_table = of_lm3601x_leds_match, - }, - .probe_new = lm3601x_probe, - .remove = lm3601x_remove, - .id_table = lm3601x_id, -}; -module_i2c_driver(lm3601x_i2c_driver); - -MODULE_DESCRIPTION("Texas Instruments Flash Lighting driver for LM3601X"); -MODULE_AUTHOR("Dan Murphy "); -MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 51f3b2c3d511c7753f039176bbce08e8d53a0845 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 28 May 2021 22:24:04 +0200 Subject: leds: ktd2692: Move driver to flash subdirectory We created a subdirectory for LED drivers that depend on CONFIG_LEDS_CLASS_FLASH, and this driver does so let's move it there. Cc: Ingi Kim Signed-off-by: Linus Walleij Signed-off-by: Pavel Machek --- drivers/leds/Kconfig | 10 - drivers/leds/Makefile | 1 - drivers/leds/flash/Kconfig | 10 + drivers/leds/flash/Makefile | 1 + drivers/leds/flash/leds-ktd2692.c | 424 ++++++++++++++++++++++++++++++++++++++ drivers/leds/leds-ktd2692.c | 424 -------------------------------------- 6 files changed, 435 insertions(+), 435 deletions(-) create mode 100644 drivers/leds/flash/leds-ktd2692.c delete mode 100644 drivers/leds/leds-ktd2692.c diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 1671aa2f90b5..da7773dc8670 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -702,16 +702,6 @@ config LEDS_MENF21BMC This driver can also be built as a module. If so the module will be called leds-menf21bmc. -config LEDS_KTD2692 - tristate "LED support for KTD2692 flash LED controller" - depends on LEDS_CLASS_FLASH && OF - depends on GPIOLIB || COMPILE_TEST - help - This option enables support for KTD2692 LED flash connected - through ExpressWire interface. - - Say Y to enable this driver. - config LEDS_IS31FL319X tristate "LED Support for ISSI IS31FL319x I2C LED controller family" depends on LEDS_CLASS && I2C && OF diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index 6d5c23afaf98..c636ec069612 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -35,7 +35,6 @@ obj-$(CONFIG_LEDS_IP30) += leds-ip30.o obj-$(CONFIG_LEDS_IPAQ_MICRO) += leds-ipaq-micro.o obj-$(CONFIG_LEDS_IS31FL319X) += leds-is31fl319x.o obj-$(CONFIG_LEDS_IS31FL32XX) += leds-is31fl32xx.o -obj-$(CONFIG_LEDS_KTD2692) += leds-ktd2692.o obj-$(CONFIG_LEDS_LM3530) += leds-lm3530.o obj-$(CONFIG_LEDS_LM3532) += leds-lm3532.o obj-$(CONFIG_LEDS_LM3533) += leds-lm3533.o diff --git a/drivers/leds/flash/Kconfig b/drivers/leds/flash/Kconfig index 6cb6600555f0..b230f3d65eb0 100644 --- a/drivers/leds/flash/Kconfig +++ b/drivers/leds/flash/Kconfig @@ -20,6 +20,16 @@ config LEDS_AS3645A controller. V4L2 flash API is provided as well if CONFIG_V4L2_FLASH_API is enabled. +config LEDS_KTD2692 + tristate "LED support for Kinetic KTD2692 flash LED controller" + depends on OF + depends on GPIOLIB || COMPILE_TEST + help + This option enables support for Kinetic KTD2692 LED flash connected + through ExpressWire interface. + + Say Y to enable this driver. + config LEDS_LM3601X tristate "LED support for LM3601x Chips" depends on LEDS_CLASS && I2C diff --git a/drivers/leds/flash/Makefile b/drivers/leds/flash/Makefile index 67556329441e..ebea42f9c37e 100644 --- a/drivers/leds/flash/Makefile +++ b/drivers/leds/flash/Makefile @@ -2,6 +2,7 @@ obj-$(CONFIG_LEDS_AAT1290) += leds-aat1290.o obj-$(CONFIG_LEDS_AS3645A) += leds-as3645a.o +obj-$(CONFIG_LEDS_KTD2692) += leds-ktd2692.o obj-$(CONFIG_LEDS_LM3601X) += leds-lm3601x.o obj-$(CONFIG_LEDS_MAX77693) += leds-max77693.o obj-$(CONFIG_LEDS_RT4505) += leds-rt4505.o diff --git a/drivers/leds/flash/leds-ktd2692.c b/drivers/leds/flash/leds-ktd2692.c new file mode 100644 index 000000000000..f341da1503a4 --- /dev/null +++ b/drivers/leds/flash/leds-ktd2692.c @@ -0,0 +1,424 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * LED driver : leds-ktd2692.c + * + * Copyright (C) 2015 Samsung Electronics + * Ingi Kim + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Value related the movie mode */ +#define KTD2692_MOVIE_MODE_CURRENT_LEVELS 16 +#define KTD2692_MM_TO_FL_RATIO(x) ((x) / 3) +#define KTD2692_MM_MIN_CURR_THRESHOLD_SCALE 8 + +/* Value related the flash mode */ +#define KTD2692_FLASH_MODE_TIMEOUT_LEVELS 8 +#define KTD2692_FLASH_MODE_TIMEOUT_DISABLE 0 +#define KTD2692_FLASH_MODE_CURR_PERCENT(x) (((x) * 16) / 100) + +/* Macro for getting offset of flash timeout */ +#define GET_TIMEOUT_OFFSET(timeout, step) ((timeout) / (step)) + +/* Base register address */ +#define KTD2692_REG_LVP_BASE 0x00 +#define KTD2692_REG_FLASH_TIMEOUT_BASE 0x20 +#define KTD2692_REG_MM_MIN_CURR_THRESHOLD_BASE 0x40 +#define KTD2692_REG_MOVIE_CURRENT_BASE 0x60 +#define KTD2692_REG_FLASH_CURRENT_BASE 0x80 +#define KTD2692_REG_MODE_BASE 0xA0 + +/* Set bit coding time for expresswire interface */ +#define KTD2692_TIME_RESET_US 700 +#define KTD2692_TIME_DATA_START_TIME_US 10 +#define KTD2692_TIME_HIGH_END_OF_DATA_US 350 +#define KTD2692_TIME_LOW_END_OF_DATA_US 10 +#define KTD2692_TIME_SHORT_BITSET_US 4 +#define KTD2692_TIME_LONG_BITSET_US 12 + +/* KTD2692 default length of name */ +#define KTD2692_NAME_LENGTH 20 + +enum ktd2692_bitset { + KTD2692_LOW = 0, + KTD2692_HIGH, +}; + +/* Movie / Flash Mode Control */ +enum ktd2692_led_mode { + KTD2692_MODE_DISABLE = 0, /* default */ + KTD2692_MODE_MOVIE, + KTD2692_MODE_FLASH, +}; + +struct ktd2692_led_config_data { + /* maximum LED current in movie mode */ + u32 movie_max_microamp; + /* maximum LED current in flash mode */ + u32 flash_max_microamp; + /* maximum flash timeout */ + u32 flash_max_timeout; + /* max LED brightness level */ + enum led_brightness max_brightness; +}; + +struct ktd2692_context { + /* Related LED Flash class device */ + struct led_classdev_flash fled_cdev; + + /* secures access to the device */ + struct mutex lock; + struct regulator *regulator; + + struct gpio_desc *aux_gpio; + struct gpio_desc *ctrl_gpio; + + enum ktd2692_led_mode mode; + enum led_brightness torch_brightness; +}; + +static struct ktd2692_context *fled_cdev_to_led( + struct led_classdev_flash *fled_cdev) +{ + return container_of(fled_cdev, struct ktd2692_context, fled_cdev); +} + +static void ktd2692_expresswire_start(struct ktd2692_context *led) +{ + gpiod_direction_output(led->ctrl_gpio, KTD2692_HIGH); + udelay(KTD2692_TIME_DATA_START_TIME_US); +} + +static void ktd2692_expresswire_reset(struct ktd2692_context *led) +{ + gpiod_direction_output(led->ctrl_gpio, KTD2692_LOW); + udelay(KTD2692_TIME_RESET_US); +} + +static void ktd2692_expresswire_end(struct ktd2692_context *led) +{ + gpiod_direction_output(led->ctrl_gpio, KTD2692_LOW); + udelay(KTD2692_TIME_LOW_END_OF_DATA_US); + gpiod_direction_output(led->ctrl_gpio, KTD2692_HIGH); + udelay(KTD2692_TIME_HIGH_END_OF_DATA_US); +} + +static void ktd2692_expresswire_set_bit(struct ktd2692_context *led, bool bit) +{ + /* + * The Low Bit(0) and High Bit(1) is based on a time detection + * algorithm between time low and time high + * Time_(L_LB) : Low time of the Low Bit(0) + * Time_(H_LB) : High time of the LOW Bit(0) + * Time_(L_HB) : Low time of the High Bit(1) + * Time_(H_HB) : High time of the High Bit(1) + * + * It can be simplified to: + * Low Bit(0) : 2 * Time_(H_LB) < Time_(L_LB) + * High Bit(1) : 2 * Time_(L_HB) < Time_(H_HB) + * HIGH ___ ____ _.. _________ ___ + * |_________| |_.. |____| |__| + * LOW + * [ Low Bit (0) ] [ High Bit(1) ] + */ + if (bit) { + gpiod_direction_output(led->ctrl_gpio, KTD2692_LOW); + udelay(KTD2692_TIME_SHORT_BITSET_US); + gpiod_direction_output(led->ctrl_gpio, KTD2692_HIGH); + udelay(KTD2692_TIME_LONG_BITSET_US); + } else { + gpiod_direction_output(led->ctrl_gpio, KTD2692_LOW); + udelay(KTD2692_TIME_LONG_BITSET_US); + gpiod_direction_output(led->ctrl_gpio, KTD2692_HIGH); + udelay(KTD2692_TIME_SHORT_BITSET_US); + } +} + +static void ktd2692_expresswire_write(struct ktd2692_context *led, u8 value) +{ + int i; + + ktd2692_expresswire_start(led); + for (i = 7; i >= 0; i--) + ktd2692_expresswire_set_bit(led, value & BIT(i)); + ktd2692_expresswire_end(led); +} + +static int ktd2692_led_brightness_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev); + struct ktd2692_context *led = fled_cdev_to_led(fled_cdev); + + mutex_lock(&led->lock); + + if (brightness == LED_OFF) { + led->mode = KTD2692_MODE_DISABLE; + gpiod_direction_output(led->aux_gpio, KTD2692_LOW); + } else { + ktd2692_expresswire_write(led, brightness | + KTD2692_REG_MOVIE_CURRENT_BASE); + led->mode = KTD2692_MODE_MOVIE; + } + + ktd2692_expresswire_write(led, led->mode | KTD2692_REG_MODE_BASE); + mutex_unlock(&led->lock); + + return 0; +} + +static int ktd2692_led_flash_strobe_set(struct led_classdev_flash *fled_cdev, + bool state) +{ + struct ktd2692_context *led = fled_cdev_to_led(fled_cdev); + struct led_flash_setting *timeout = &fled_cdev->timeout; + u32 flash_tm_reg; + + mutex_lock(&led->lock); + + if (state) { + flash_tm_reg = GET_TIMEOUT_OFFSET(timeout->val, timeout->step); + ktd2692_expresswire_write(led, flash_tm_reg + | KTD2692_REG_FLASH_TIMEOUT_BASE); + + led->mode = KTD2692_MODE_FLASH; + gpiod_direction_output(led->aux_gpio, KTD2692_HIGH); + } else { + led->mode = KTD2692_MODE_DISABLE; + gpiod_direction_output(led->aux_gpio, KTD2692_LOW); + } + + ktd2692_expresswire_write(led, led->mode | KTD2692_REG_MODE_BASE); + + fled_cdev->led_cdev.brightness = LED_OFF; + led->mode = KTD2692_MODE_DISABLE; + + mutex_unlock(&led->lock); + + return 0; +} + +static int ktd2692_led_flash_timeout_set(struct led_classdev_flash *fled_cdev, + u32 timeout) +{ + return 0; +} + +static void ktd2692_init_movie_current_max(struct ktd2692_led_config_data *cfg) +{ + u32 offset, step; + u32 movie_current_microamp; + + offset = KTD2692_MOVIE_MODE_CURRENT_LEVELS; + step = KTD2692_MM_TO_FL_RATIO(cfg->flash_max_microamp) + / KTD2692_MOVIE_MODE_CURRENT_LEVELS; + + do { + movie_current_microamp = step * offset; + offset--; + } while ((movie_current_microamp > cfg->movie_max_microamp) && + (offset > 0)); + + cfg->max_brightness = offset; +} + +static void ktd2692_init_flash_timeout(struct led_classdev_flash *fled_cdev, + struct ktd2692_led_config_data *cfg) +{ + struct led_flash_setting *setting; + + setting = &fled_cdev->timeout; + setting->min = KTD2692_FLASH_MODE_TIMEOUT_DISABLE; + setting->max = cfg->flash_max_timeout; + setting->step = cfg->flash_max_timeout + / (KTD2692_FLASH_MODE_TIMEOUT_LEVELS - 1); + setting->val = cfg->flash_max_timeout; +} + +static void ktd2692_setup(struct ktd2692_context *led) +{ + led->mode = KTD2692_MODE_DISABLE; + ktd2692_expresswire_reset(led); + gpiod_direction_output(led->aux_gpio, KTD2692_LOW); + + ktd2692_expresswire_write(led, (KTD2692_MM_MIN_CURR_THRESHOLD_SCALE - 1) + | KTD2692_REG_MM_MIN_CURR_THRESHOLD_BASE); + ktd2692_expresswire_write(led, KTD2692_FLASH_MODE_CURR_PERCENT(45) + | KTD2692_REG_FLASH_CURRENT_BASE); +} + +static void regulator_disable_action(void *_data) +{ + struct device *dev = _data; + struct ktd2692_context *led = dev_get_drvdata(dev); + int ret; + + ret = regulator_disable(led->regulator); + if (ret) + dev_err(dev, "Failed to disable supply: %d\n", ret); +} + +static int ktd2692_parse_dt(struct ktd2692_context *led, struct device *dev, + struct ktd2692_led_config_data *cfg) +{ + struct device_node *np = dev_of_node(dev); + struct device_node *child_node; + int ret; + + if (!dev_of_node(dev)) + return -ENXIO; + + led->ctrl_gpio = devm_gpiod_get(dev, "ctrl", GPIOD_ASIS); + ret = PTR_ERR_OR_ZERO(led->ctrl_gpio); + if (ret) { + dev_err(dev, "cannot get ctrl-gpios %d\n", ret); + return ret; + } + + led->aux_gpio = devm_gpiod_get(dev, "aux", GPIOD_ASIS); + ret = PTR_ERR_OR_ZERO(led->aux_gpio); + if (ret) { + dev_err(dev, "cannot get aux-gpios %d\n", ret); + return ret; + } + + led->regulator = devm_regulator_get(dev, "vin"); + if (IS_ERR(led->regulator)) + led->regulator = NULL; + + if (led->regulator) { + ret = regulator_enable(led->regulator); + if (ret) { + dev_err(dev, "Failed to enable supply: %d\n", ret); + } else { + ret = devm_add_action_or_reset(dev, + regulator_disable_action, dev); + if (ret) + return ret; + } + } + + child_node = of_get_next_available_child(np, NULL); + if (!child_node) { + dev_err(dev, "No DT child node found for connected LED.\n"); + return -EINVAL; + } + + led->fled_cdev.led_cdev.name = + of_get_property(child_node, "label", NULL) ? : child_node->name; + + ret = of_property_read_u32(child_node, "led-max-microamp", + &cfg->movie_max_microamp); + if (ret) { + dev_err(dev, "failed to parse led-max-microamp\n"); + goto err_parse_dt; + } + + ret = of_property_read_u32(child_node, "flash-max-microamp", + &cfg->flash_max_microamp); + if (ret) { + dev_err(dev, "failed to parse flash-max-microamp\n"); + goto err_parse_dt; + } + + ret = of_property_read_u32(child_node, "flash-max-timeout-us", + &cfg->flash_max_timeout); + if (ret) { + dev_err(dev, "failed to parse flash-max-timeout-us\n"); + goto err_parse_dt; + } + +err_parse_dt: + of_node_put(child_node); + return ret; +} + +static const struct led_flash_ops flash_ops = { + .strobe_set = ktd2692_led_flash_strobe_set, + .timeout_set = ktd2692_led_flash_timeout_set, +}; + +static int ktd2692_probe(struct platform_device *pdev) +{ + struct ktd2692_context *led; + struct led_classdev *led_cdev; + struct led_classdev_flash *fled_cdev; + struct ktd2692_led_config_data led_cfg; + int ret; + + led = devm_kzalloc(&pdev->dev, sizeof(*led), GFP_KERNEL); + if (!led) + return -ENOMEM; + + fled_cdev = &led->fled_cdev; + led_cdev = &fled_cdev->led_cdev; + + ret = ktd2692_parse_dt(led, &pdev->dev, &led_cfg); + if (ret) + return ret; + + ktd2692_init_flash_timeout(fled_cdev, &led_cfg); + ktd2692_init_movie_current_max(&led_cfg); + + fled_cdev->ops = &flash_ops; + + led_cdev->max_brightness = led_cfg.max_brightness; + led_cdev->brightness_set_blocking = ktd2692_led_brightness_set; + led_cdev->flags |= LED_CORE_SUSPENDRESUME | LED_DEV_CAP_FLASH; + + mutex_init(&led->lock); + + platform_set_drvdata(pdev, led); + + ret = led_classdev_flash_register(&pdev->dev, fled_cdev); + if (ret) { + dev_err(&pdev->dev, "can't register LED %s\n", led_cdev->name); + mutex_destroy(&led->lock); + return ret; + } + + ktd2692_setup(led); + + return 0; +} + +static int ktd2692_remove(struct platform_device *pdev) +{ + struct ktd2692_context *led = platform_get_drvdata(pdev); + + led_classdev_flash_unregister(&led->fled_cdev); + + mutex_destroy(&led->lock); + + return 0; +} + +static const struct of_device_id ktd2692_match[] = { + { .compatible = "kinetic,ktd2692", }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, ktd2692_match); + +static struct platform_driver ktd2692_driver = { + .driver = { + .name = "ktd2692", + .of_match_table = ktd2692_match, + }, + .probe = ktd2692_probe, + .remove = ktd2692_remove, +}; + +module_platform_driver(ktd2692_driver); + +MODULE_AUTHOR("Ingi Kim "); +MODULE_DESCRIPTION("Kinetic KTD2692 LED driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/leds/leds-ktd2692.c b/drivers/leds/leds-ktd2692.c deleted file mode 100644 index f341da1503a4..000000000000 --- a/drivers/leds/leds-ktd2692.c +++ /dev/null @@ -1,424 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * LED driver : leds-ktd2692.c - * - * Copyright (C) 2015 Samsung Electronics - * Ingi Kim - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* Value related the movie mode */ -#define KTD2692_MOVIE_MODE_CURRENT_LEVELS 16 -#define KTD2692_MM_TO_FL_RATIO(x) ((x) / 3) -#define KTD2692_MM_MIN_CURR_THRESHOLD_SCALE 8 - -/* Value related the flash mode */ -#define KTD2692_FLASH_MODE_TIMEOUT_LEVELS 8 -#define KTD2692_FLASH_MODE_TIMEOUT_DISABLE 0 -#define KTD2692_FLASH_MODE_CURR_PERCENT(x) (((x) * 16) / 100) - -/* Macro for getting offset of flash timeout */ -#define GET_TIMEOUT_OFFSET(timeout, step) ((timeout) / (step)) - -/* Base register address */ -#define KTD2692_REG_LVP_BASE 0x00 -#define KTD2692_REG_FLASH_TIMEOUT_BASE 0x20 -#define KTD2692_REG_MM_MIN_CURR_THRESHOLD_BASE 0x40 -#define KTD2692_REG_MOVIE_CURRENT_BASE 0x60 -#define KTD2692_REG_FLASH_CURRENT_BASE 0x80 -#define KTD2692_REG_MODE_BASE 0xA0 - -/* Set bit coding time for expresswire interface */ -#define KTD2692_TIME_RESET_US 700 -#define KTD2692_TIME_DATA_START_TIME_US 10 -#define KTD2692_TIME_HIGH_END_OF_DATA_US 350 -#define KTD2692_TIME_LOW_END_OF_DATA_US 10 -#define KTD2692_TIME_SHORT_BITSET_US 4 -#define KTD2692_TIME_LONG_BITSET_US 12 - -/* KTD2692 default length of name */ -#define KTD2692_NAME_LENGTH 20 - -enum ktd2692_bitset { - KTD2692_LOW = 0, - KTD2692_HIGH, -}; - -/* Movie / Flash Mode Control */ -enum ktd2692_led_mode { - KTD2692_MODE_DISABLE = 0, /* default */ - KTD2692_MODE_MOVIE, - KTD2692_MODE_FLASH, -}; - -struct ktd2692_led_config_data { - /* maximum LED current in movie mode */ - u32 movie_max_microamp; - /* maximum LED current in flash mode */ - u32 flash_max_microamp; - /* maximum flash timeout */ - u32 flash_max_timeout; - /* max LED brightness level */ - enum led_brightness max_brightness; -}; - -struct ktd2692_context { - /* Related LED Flash class device */ - struct led_classdev_flash fled_cdev; - - /* secures access to the device */ - struct mutex lock; - struct regulator *regulator; - - struct gpio_desc *aux_gpio; - struct gpio_desc *ctrl_gpio; - - enum ktd2692_led_mode mode; - enum led_brightness torch_brightness; -}; - -static struct ktd2692_context *fled_cdev_to_led( - struct led_classdev_flash *fled_cdev) -{ - return container_of(fled_cdev, struct ktd2692_context, fled_cdev); -} - -static void ktd2692_expresswire_start(struct ktd2692_context *led) -{ - gpiod_direction_output(led->ctrl_gpio, KTD2692_HIGH); - udelay(KTD2692_TIME_DATA_START_TIME_US); -} - -static void ktd2692_expresswire_reset(struct ktd2692_context *led) -{ - gpiod_direction_output(led->ctrl_gpio, KTD2692_LOW); - udelay(KTD2692_TIME_RESET_US); -} - -static void ktd2692_expresswire_end(struct ktd2692_context *led) -{ - gpiod_direction_output(led->ctrl_gpio, KTD2692_LOW); - udelay(KTD2692_TIME_LOW_END_OF_DATA_US); - gpiod_direction_output(led->ctrl_gpio, KTD2692_HIGH); - udelay(KTD2692_TIME_HIGH_END_OF_DATA_US); -} - -static void ktd2692_expresswire_set_bit(struct ktd2692_context *led, bool bit) -{ - /* - * The Low Bit(0) and High Bit(1) is based on a time detection - * algorithm between time low and time high - * Time_(L_LB) : Low time of the Low Bit(0) - * Time_(H_LB) : High time of the LOW Bit(0) - * Time_(L_HB) : Low time of the High Bit(1) - * Time_(H_HB) : High time of the High Bit(1) - * - * It can be simplified to: - * Low Bit(0) : 2 * Time_(H_LB) < Time_(L_LB) - * High Bit(1) : 2 * Time_(L_HB) < Time_(H_HB) - * HIGH ___ ____ _.. _________ ___ - * |_________| |_.. |____| |__| - * LOW - * [ Low Bit (0) ] [ High Bit(1) ] - */ - if (bit) { - gpiod_direction_output(led->ctrl_gpio, KTD2692_LOW); - udelay(KTD2692_TIME_SHORT_BITSET_US); - gpiod_direction_output(led->ctrl_gpio, KTD2692_HIGH); - udelay(KTD2692_TIME_LONG_BITSET_US); - } else { - gpiod_direction_output(led->ctrl_gpio, KTD2692_LOW); - udelay(KTD2692_TIME_LONG_BITSET_US); - gpiod_direction_output(led->ctrl_gpio, KTD2692_HIGH); - udelay(KTD2692_TIME_SHORT_BITSET_US); - } -} - -static void ktd2692_expresswire_write(struct ktd2692_context *led, u8 value) -{ - int i; - - ktd2692_expresswire_start(led); - for (i = 7; i >= 0; i--) - ktd2692_expresswire_set_bit(led, value & BIT(i)); - ktd2692_expresswire_end(led); -} - -static int ktd2692_led_brightness_set(struct led_classdev *led_cdev, - enum led_brightness brightness) -{ - struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev); - struct ktd2692_context *led = fled_cdev_to_led(fled_cdev); - - mutex_lock(&led->lock); - - if (brightness == LED_OFF) { - led->mode = KTD2692_MODE_DISABLE; - gpiod_direction_output(led->aux_gpio, KTD2692_LOW); - } else { - ktd2692_expresswire_write(led, brightness | - KTD2692_REG_MOVIE_CURRENT_BASE); - led->mode = KTD2692_MODE_MOVIE; - } - - ktd2692_expresswire_write(led, led->mode | KTD2692_REG_MODE_BASE); - mutex_unlock(&led->lock); - - return 0; -} - -static int ktd2692_led_flash_strobe_set(struct led_classdev_flash *fled_cdev, - bool state) -{ - struct ktd2692_context *led = fled_cdev_to_led(fled_cdev); - struct led_flash_setting *timeout = &fled_cdev->timeout; - u32 flash_tm_reg; - - mutex_lock(&led->lock); - - if (state) { - flash_tm_reg = GET_TIMEOUT_OFFSET(timeout->val, timeout->step); - ktd2692_expresswire_write(led, flash_tm_reg - | KTD2692_REG_FLASH_TIMEOUT_BASE); - - led->mode = KTD2692_MODE_FLASH; - gpiod_direction_output(led->aux_gpio, KTD2692_HIGH); - } else { - led->mode = KTD2692_MODE_DISABLE; - gpiod_direction_output(led->aux_gpio, KTD2692_LOW); - } - - ktd2692_expresswire_write(led, led->mode | KTD2692_REG_MODE_BASE); - - fled_cdev->led_cdev.brightness = LED_OFF; - led->mode = KTD2692_MODE_DISABLE; - - mutex_unlock(&led->lock); - - return 0; -} - -static int ktd2692_led_flash_timeout_set(struct led_classdev_flash *fled_cdev, - u32 timeout) -{ - return 0; -} - -static void ktd2692_init_movie_current_max(struct ktd2692_led_config_data *cfg) -{ - u32 offset, step; - u32 movie_current_microamp; - - offset = KTD2692_MOVIE_MODE_CURRENT_LEVELS; - step = KTD2692_MM_TO_FL_RATIO(cfg->flash_max_microamp) - / KTD2692_MOVIE_MODE_CURRENT_LEVELS; - - do { - movie_current_microamp = step * offset; - offset--; - } while ((movie_current_microamp > cfg->movie_max_microamp) && - (offset > 0)); - - cfg->max_brightness = offset; -} - -static void ktd2692_init_flash_timeout(struct led_classdev_flash *fled_cdev, - struct ktd2692_led_config_data *cfg) -{ - struct led_flash_setting *setting; - - setting = &fled_cdev->timeout; - setting->min = KTD2692_FLASH_MODE_TIMEOUT_DISABLE; - setting->max = cfg->flash_max_timeout; - setting->step = cfg->flash_max_timeout - / (KTD2692_FLASH_MODE_TIMEOUT_LEVELS - 1); - setting->val = cfg->flash_max_timeout; -} - -static void ktd2692_setup(struct ktd2692_context *led) -{ - led->mode = KTD2692_MODE_DISABLE; - ktd2692_expresswire_reset(led); - gpiod_direction_output(led->aux_gpio, KTD2692_LOW); - - ktd2692_expresswire_write(led, (KTD2692_MM_MIN_CURR_THRESHOLD_SCALE - 1) - | KTD2692_REG_MM_MIN_CURR_THRESHOLD_BASE); - ktd2692_expresswire_write(led, KTD2692_FLASH_MODE_CURR_PERCENT(45) - | KTD2692_REG_FLASH_CURRENT_BASE); -} - -static void regulator_disable_action(void *_data) -{ - struct device *dev = _data; - struct ktd2692_context *led = dev_get_drvdata(dev); - int ret; - - ret = regulator_disable(led->regulator); - if (ret) - dev_err(dev, "Failed to disable supply: %d\n", ret); -} - -static int ktd2692_parse_dt(struct ktd2692_context *led, struct device *dev, - struct ktd2692_led_config_data *cfg) -{ - struct device_node *np = dev_of_node(dev); - struct device_node *child_node; - int ret; - - if (!dev_of_node(dev)) - return -ENXIO; - - led->ctrl_gpio = devm_gpiod_get(dev, "ctrl", GPIOD_ASIS); - ret = PTR_ERR_OR_ZERO(led->ctrl_gpio); - if (ret) { - dev_err(dev, "cannot get ctrl-gpios %d\n", ret); - return ret; - } - - led->aux_gpio = devm_gpiod_get(dev, "aux", GPIOD_ASIS); - ret = PTR_ERR_OR_ZERO(led->aux_gpio); - if (ret) { - dev_err(dev, "cannot get aux-gpios %d\n", ret); - return ret; - } - - led->regulator = devm_regulator_get(dev, "vin"); - if (IS_ERR(led->regulator)) - led->regulator = NULL; - - if (led->regulator) { - ret = regulator_enable(led->regulator); - if (ret) { - dev_err(dev, "Failed to enable supply: %d\n", ret); - } else { - ret = devm_add_action_or_reset(dev, - regulator_disable_action, dev); - if (ret) - return ret; - } - } - - child_node = of_get_next_available_child(np, NULL); - if (!child_node) { - dev_err(dev, "No DT child node found for connected LED.\n"); - return -EINVAL; - } - - led->fled_cdev.led_cdev.name = - of_get_property(child_node, "label", NULL) ? : child_node->name; - - ret = of_property_read_u32(child_node, "led-max-microamp", - &cfg->movie_max_microamp); - if (ret) { - dev_err(dev, "failed to parse led-max-microamp\n"); - goto err_parse_dt; - } - - ret = of_property_read_u32(child_node, "flash-max-microamp", - &cfg->flash_max_microamp); - if (ret) { - dev_err(dev, "failed to parse flash-max-microamp\n"); - goto err_parse_dt; - } - - ret = of_property_read_u32(child_node, "flash-max-timeout-us", - &cfg->flash_max_timeout); - if (ret) { - dev_err(dev, "failed to parse flash-max-timeout-us\n"); - goto err_parse_dt; - } - -err_parse_dt: - of_node_put(child_node); - return ret; -} - -static const struct led_flash_ops flash_ops = { - .strobe_set = ktd2692_led_flash_strobe_set, - .timeout_set = ktd2692_led_flash_timeout_set, -}; - -static int ktd2692_probe(struct platform_device *pdev) -{ - struct ktd2692_context *led; - struct led_classdev *led_cdev; - struct led_classdev_flash *fled_cdev; - struct ktd2692_led_config_data led_cfg; - int ret; - - led = devm_kzalloc(&pdev->dev, sizeof(*led), GFP_KERNEL); - if (!led) - return -ENOMEM; - - fled_cdev = &led->fled_cdev; - led_cdev = &fled_cdev->led_cdev; - - ret = ktd2692_parse_dt(led, &pdev->dev, &led_cfg); - if (ret) - return ret; - - ktd2692_init_flash_timeout(fled_cdev, &led_cfg); - ktd2692_init_movie_current_max(&led_cfg); - - fled_cdev->ops = &flash_ops; - - led_cdev->max_brightness = led_cfg.max_brightness; - led_cdev->brightness_set_blocking = ktd2692_led_brightness_set; - led_cdev->flags |= LED_CORE_SUSPENDRESUME | LED_DEV_CAP_FLASH; - - mutex_init(&led->lock); - - platform_set_drvdata(pdev, led); - - ret = led_classdev_flash_register(&pdev->dev, fled_cdev); - if (ret) { - dev_err(&pdev->dev, "can't register LED %s\n", led_cdev->name); - mutex_destroy(&led->lock); - return ret; - } - - ktd2692_setup(led); - - return 0; -} - -static int ktd2692_remove(struct platform_device *pdev) -{ - struct ktd2692_context *led = platform_get_drvdata(pdev); - - led_classdev_flash_unregister(&led->fled_cdev); - - mutex_destroy(&led->lock); - - return 0; -} - -static const struct of_device_id ktd2692_match[] = { - { .compatible = "kinetic,ktd2692", }, - { /* sentinel */ }, -}; -MODULE_DEVICE_TABLE(of, ktd2692_match); - -static struct platform_driver ktd2692_driver = { - .driver = { - .name = "ktd2692", - .of_match_table = ktd2692_match, - }, - .probe = ktd2692_probe, - .remove = ktd2692_remove, -}; - -module_platform_driver(ktd2692_driver); - -MODULE_AUTHOR("Ingi Kim "); -MODULE_DESCRIPTION("Kinetic KTD2692 LED driver"); -MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From e642197562cd9781453f835e1406cfe0feeb917e Mon Sep 17 00:00:00 2001 From: Jiapeng Chong Date: Tue, 1 Jun 2021 19:09:03 +0800 Subject: leds: is31fl32xx: Fix missing error code in is31fl32xx_parse_dt() The error code is missing in this code scenario, add the error code '-EINVAL' to the return value 'ret'. Eliminate the follow smatch warning: drivers/leds/leds-is31fl32xx.c:388 is31fl32xx_parse_dt() warn: missing error code 'ret'. Reported-by: Abaci Robot Signed-off-by: Jiapeng Chong Fixes: 9d7cffaf99f5 ("leds: Add driver for the ISSI IS31FL32xx family of LED controllers") Acked-by: David Rivshin Signed-off-by: Pavel Machek --- drivers/leds/leds-is31fl32xx.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/leds/leds-is31fl32xx.c b/drivers/leds/leds-is31fl32xx.c index 3b55af9a8c58..22c092a4394a 100644 --- a/drivers/leds/leds-is31fl32xx.c +++ b/drivers/leds/leds-is31fl32xx.c @@ -386,6 +386,7 @@ static int is31fl32xx_parse_dt(struct device *dev, dev_err(dev, "Node %pOF 'reg' conflicts with another LED\n", child); + ret = -EINVAL; goto err; } -- cgit v1.2.3 From e06ba23b0518875e56e385500613b8651f541742 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Sat, 29 May 2021 14:19:25 +0300 Subject: leds: el15203000: Correct headers (of*.h -> mod_devicetable.h) There is no user of of*.h headers, but mod_devicetable.h. Update header block accordingly. Signed-off-by: Andy Shevchenko Reviewed-by: Oleh Kravchenko Signed-off-by: Pavel Machek --- drivers/leds/leds-el15203000.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/leds/leds-el15203000.c b/drivers/leds/leds-el15203000.c index 76b455e87574..f9eb59a25570 100644 --- a/drivers/leds/leds-el15203000.c +++ b/drivers/leds/leds-el15203000.c @@ -4,8 +4,9 @@ #include #include +#include #include -#include +#include #include /* -- cgit v1.2.3 From 9999908ca1abee7aa518a4f6a3739517c137acbf Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Sat, 29 May 2021 14:19:26 +0300 Subject: leds: lgm-sso: Put fwnode in any case during ->probe() fwnode_get_next_child_node() bumps a reference counting of a returned variable. We have to balance it whenever we return to the caller. All the same in fwnode_for_each_child_node() case. Fixes: c3987cd2bca3 ("leds: lgm: Add LED controller driver for LGM SoC") Cc: Amireddy Mallikarjuna reddy Signed-off-by: Andy Shevchenko Signed-off-by: Pavel Machek --- drivers/leds/blink/leds-lgm-sso.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/leds/blink/leds-lgm-sso.c b/drivers/leds/blink/leds-lgm-sso.c index 7eb2f44f16be..62ce83cea553 100644 --- a/drivers/leds/blink/leds-lgm-sso.c +++ b/drivers/leds/blink/leds-lgm-sso.c @@ -631,8 +631,10 @@ __sso_led_dt_parse(struct sso_led_priv *priv, struct fwnode_handle *fw_ssoled) fwnode_for_each_child_node(fw_ssoled, fwnode_child) { led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL); - if (!led) - return -ENOMEM; + if (!led) { + ret = -ENOMEM; + goto __dt_err; + } INIT_LIST_HEAD(&led->list); led->priv = priv; @@ -702,11 +704,11 @@ __sso_led_dt_parse(struct sso_led_priv *priv, struct fwnode_handle *fw_ssoled) if (sso_create_led(priv, led, fwnode_child)) goto __dt_err; } - fwnode_handle_put(fw_ssoled); return 0; + __dt_err: - fwnode_handle_put(fw_ssoled); + fwnode_handle_put(fwnode_child); /* unregister leds */ list_for_each(p, &priv->led_list) { led = list_entry(p, struct sso_led, list); @@ -731,6 +733,7 @@ static int sso_led_dt_parse(struct sso_led_priv *priv) fw_ssoled = fwnode_get_named_child_node(fwnode, "ssoled"); if (fw_ssoled) { ret = __sso_led_dt_parse(priv, fw_ssoled); + fwnode_handle_put(fw_ssoled); if (ret) return ret; } -- cgit v1.2.3 From 1ed4d05e0a0b23ba15e0affcff4008dd537ae3ee Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Sat, 29 May 2021 14:19:27 +0300 Subject: leds: lgm-sso: Don't spam logs when probe is deferred When requesting GPIO line the probe can be deferred. In such case don't spam logs with an error message. This can be achieved by switching to dev_err_probe(). Fixes: c3987cd2bca3 ("leds: lgm: Add LED controller driver for LGM SoC") Cc: Amireddy Mallikarjuna reddy Signed-off-by: Andy Shevchenko Signed-off-by: Pavel Machek --- drivers/leds/blink/leds-lgm-sso.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/leds/blink/leds-lgm-sso.c b/drivers/leds/blink/leds-lgm-sso.c index 62ce83cea553..ca9f88996819 100644 --- a/drivers/leds/blink/leds-lgm-sso.c +++ b/drivers/leds/blink/leds-lgm-sso.c @@ -644,7 +644,7 @@ __sso_led_dt_parse(struct sso_led_priv *priv, struct fwnode_handle *fw_ssoled) fwnode_child, GPIOD_ASIS, NULL); if (IS_ERR(led->gpiod)) { - dev_err(dev, "led: get gpio fail!\n"); + dev_err_probe(dev, PTR_ERR(led->gpiod), "led: get gpio fail!\n"); goto __dt_err; } -- cgit v1.2.3 From c31ef7004ee1fd417dd9448ba4a43f42090d27fe Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Sat, 29 May 2021 14:19:28 +0300 Subject: leds: lgm-sso: Remove explicit managed GPIO resource cleanup The idea of managed resources is that they will be cleaned up automatically and in the proper order. Remove explicit GPIO cleanup. Signed-off-by: Andy Shevchenko Signed-off-by: Pavel Machek --- drivers/leds/blink/leds-lgm-sso.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/leds/blink/leds-lgm-sso.c b/drivers/leds/blink/leds-lgm-sso.c index ca9f88996819..5c012ffeeaa8 100644 --- a/drivers/leds/blink/leds-lgm-sso.c +++ b/drivers/leds/blink/leds-lgm-sso.c @@ -611,9 +611,6 @@ static void sso_led_shutdown(struct sso_led *led) if (led->desc.hw_trig) regmap_update_bits(priv->mmap, SSO_CON3, BIT(led->desc.pin), 0); - if (led->gpiod) - devm_gpiod_put(priv->dev, led->gpiod); - led->priv = NULL; } -- cgit v1.2.3 From 3dd34dfb09ae814922dd833cad11b215b238a1ba Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Sat, 29 May 2021 14:19:29 +0300 Subject: leds: lgm-sso: Convert to use list_for_each_entry*() API Convert to use list_for_each_entry*() API insted of open coded variants. It saves few lines of code and makes iteasier to read and maintain. Signed-off-by: Andy Shevchenko Signed-off-by: Pavel Machek --- drivers/leds/blink/leds-lgm-sso.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/drivers/leds/blink/leds-lgm-sso.c b/drivers/leds/blink/leds-lgm-sso.c index 5c012ffeeaa8..e47c47e421d6 100644 --- a/drivers/leds/blink/leds-lgm-sso.c +++ b/drivers/leds/blink/leds-lgm-sso.c @@ -621,7 +621,6 @@ __sso_led_dt_parse(struct sso_led_priv *priv, struct fwnode_handle *fw_ssoled) struct device *dev = priv->dev; struct sso_led_desc *desc; struct sso_led *led; - struct list_head *p; const char *tmp; u32 prop; int ret; @@ -707,10 +706,8 @@ __sso_led_dt_parse(struct sso_led_priv *priv, struct fwnode_handle *fw_ssoled) __dt_err: fwnode_handle_put(fwnode_child); /* unregister leds */ - list_for_each(p, &priv->led_list) { - led = list_entry(p, struct sso_led, list); + list_for_each_entry(led, &priv->led_list, list) sso_led_shutdown(led); - } return -EINVAL; } @@ -841,14 +838,12 @@ static int intel_sso_led_probe(struct platform_device *pdev) static int intel_sso_led_remove(struct platform_device *pdev) { struct sso_led_priv *priv; - struct list_head *pos, *n; - struct sso_led *led; + struct sso_led *led, *n; priv = platform_get_drvdata(pdev); - list_for_each_safe(pos, n, &priv->led_list) { - list_del(pos); - led = list_entry(pos, struct sso_led, list); + list_for_each_entry_safe(led, n, &priv->led_list, list) { + list_del(&led->list); sso_led_shutdown(led); } -- cgit v1.2.3 From 690e4f3ad363ae5ad27222214509855d40d569a7 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Sat, 29 May 2021 14:19:30 +0300 Subject: leds: lm3692x: Correct headers (of*.h -> mod_devicetable.h) There is no user of of*.h headers, but mod_devicetable.h. Update header block accordingly. Signed-off-by: Andy Shevchenko Signed-off-by: Pavel Machek --- drivers/leds/leds-lm3692x.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/leds/leds-lm3692x.c b/drivers/leds/leds-lm3692x.c index a02756d7ed8f..afe6fb297855 100644 --- a/drivers/leds/leds-lm3692x.c +++ b/drivers/leds/leds-lm3692x.c @@ -7,10 +7,9 @@ #include #include #include +#include #include #include -#include -#include #include #include #include -- cgit v1.2.3 From 3a923639d36b1c54866236c2f53d112b797b0101 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Sat, 29 May 2021 14:19:31 +0300 Subject: leds: lm3697: Update header block to reflect reality Currently the headers to be included look rather like a random set. Update them a bit to reflect the reality. While at it, drop unneeded dependcy to OF. Signed-off-by: Andy Shevchenko Signed-off-by: Pavel Machek --- drivers/leds/leds-lm3697.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/leds/leds-lm3697.c b/drivers/leds/leds-lm3697.c index 970a4f34791b..292d64b2eeab 100644 --- a/drivers/leds/leds-lm3697.c +++ b/drivers/leds/leds-lm3697.c @@ -2,11 +2,16 @@ // TI LM3697 LED chip family driver // Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/ +#include #include #include -#include -#include +#include +#include +#include +#include #include +#include + #include #define LM3697_REV 0x0 -- cgit v1.2.3 From d299ae942e0201ce3419501f523fbaac989dd036 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Sat, 29 May 2021 14:19:32 +0300 Subject: leds: lm3697: Make error handling more robust It's easy to miss necessary clean up, e.g. firmware node reference counting, during error path in ->probe(). Make it more robust by moving to a single point of return. Signed-off-by: Andy Shevchenko Signed-off-by: Pavel Machek --- drivers/leds/leds-lm3697.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/leds/leds-lm3697.c b/drivers/leds/leds-lm3697.c index 292d64b2eeab..a8c9322558cc 100644 --- a/drivers/leds/leds-lm3697.c +++ b/drivers/leds/leds-lm3697.c @@ -226,14 +226,12 @@ static int lm3697_probe_dt(struct lm3697 *priv) ret = fwnode_property_read_u32(child, "reg", &control_bank); if (ret) { dev_err(dev, "reg property missing\n"); - fwnode_handle_put(child); goto child_out; } if (control_bank > LM3697_CONTROL_B) { dev_err(dev, "reg property is invalid\n"); ret = -EINVAL; - fwnode_handle_put(child); goto child_out; } @@ -264,7 +262,6 @@ static int lm3697_probe_dt(struct lm3697 *priv) led->num_leds); if (ret) { dev_err(dev, "led-sources property missing\n"); - fwnode_handle_put(child); goto child_out; } @@ -289,14 +286,16 @@ static int lm3697_probe_dt(struct lm3697 *priv) &init_data); if (ret) { dev_err(dev, "led register err: %d\n", ret); - fwnode_handle_put(child); goto child_out; } i++; } + return ret; + child_out: + fwnode_handle_put(child); return ret; } -- cgit v1.2.3 From 7e1baaaa2407a642ea19b58e214fab9a69cda1d7 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Sat, 29 May 2021 14:19:33 +0300 Subject: leds: lt3593: Put fwnode in any case during ->probe() device_get_next_child_node() bumps a reference counting of a returned variable. We have to balance it whenever we return to the caller. Fixes: 8cd7d6daba93 ("leds: lt3593: Add device tree probing glue") Cc: Daniel Mack Signed-off-by: Andy Shevchenko Signed-off-by: Pavel Machek --- drivers/leds/leds-lt3593.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/leds/leds-lt3593.c b/drivers/leds/leds-lt3593.c index 3bb52d3165d9..d0160fde0f94 100644 --- a/drivers/leds/leds-lt3593.c +++ b/drivers/leds/leds-lt3593.c @@ -97,10 +97,9 @@ static int lt3593_led_probe(struct platform_device *pdev) init_data.default_label = ":"; ret = devm_led_classdev_register_ext(dev, &led_data->cdev, &init_data); - if (ret < 0) { - fwnode_handle_put(child); + fwnode_handle_put(child); + if (ret < 0) return ret; - } platform_set_drvdata(pdev, led_data); -- cgit v1.2.3 From 8aa41952ef245449df79100e1942b5e6288b098a Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Sat, 29 May 2021 14:19:34 +0300 Subject: leds: rt8515: Put fwnode in any case during ->probe() fwnode_get_next_available_child_node() bumps a reference counting of a returned variable. We have to balance it whenever we return to the caller. Fixes: e1c6edcbea13 ("leds: rt8515: Add Richtek RT8515 LED driver") Signed-off-by: Andy Shevchenko Signed-off-by: Pavel Machek Reviewed-by: Linus Walleij --- drivers/leds/flash/leds-rt8515.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/leds/flash/leds-rt8515.c b/drivers/leds/flash/leds-rt8515.c index 590bfa180d10..44904fdee3cc 100644 --- a/drivers/leds/flash/leds-rt8515.c +++ b/drivers/leds/flash/leds-rt8515.c @@ -343,8 +343,9 @@ static int rt8515_probe(struct platform_device *pdev) ret = devm_led_classdev_flash_register_ext(dev, fled, &init_data); if (ret) { - dev_err(dev, "can't register LED %s\n", led->name); + fwnode_handle_put(child); mutex_destroy(&rt->lock); + dev_err(dev, "can't register LED %s\n", led->name); return ret; } @@ -362,6 +363,7 @@ static int rt8515_probe(struct platform_device *pdev) */ } + fwnode_handle_put(child); return 0; } -- cgit v1.2.3 From 64f67b5240db79eceb0bd57dae8e591fd3103ba0 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 21 Feb 2021 12:52:08 +0100 Subject: leds: trigger: audio: Add an activate callback to ensure the initial brightness is set MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some 2-in-1s with a detachable (USB) keyboard(dock) have mute-LEDs in the speaker- and/or mic-mute keys on the keyboard. Examples of this are the Lenovo Thinkpad10 tablet (with its USB kbd-dock) and the HP x2 10 series. The detachable nature of these keyboards means that the keyboard and thus the mute LEDs may show up after the user (or userspace restoring old mixer settings) has muted the speaker and/or mic. Current LED-class devices with a default_trigger of "audio-mute" or "audio-micmute" initialize the brightness member of led_classdev with ledtrig_audio_get() before registering the LED. This makes the software state after attaching the keyboard match the actual audio mute state, e.g. cat /sys/class/leds/foo/brightness will show the right value. But before this commit nothing was actually calling the led_classdev's brightness_set[_blocking] callback so the value returned by ledtrig_audio_get() was never actually being sent to the hw, leading to the mute LEDs staying in their default power-on state, after attaching the keyboard, even if ledtrig_audio_get() returned a different state. This could be fixed by having the individual LED drivers call brightness_set[_blocking] themselves after registering the LED, but this really is something which should be done by a led-trigger activate callback. Add an activate callback for this, fixing the issue of the mute LEDs being out of sync after (re)attaching the keyboard. Cc: Takashi Iwai Fixes: faa2541f5b1a ("leds: trigger: Introduce audio mute LED trigger") Reviewed-by: Marek Behún Signed-off-by: Hans de Goede Signed-off-by: Pavel Machek --- drivers/leds/trigger/ledtrig-audio.c | 37 ++++++++++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/drivers/leds/trigger/ledtrig-audio.c b/drivers/leds/trigger/ledtrig-audio.c index f76621e88482..c6b437e6369b 100644 --- a/drivers/leds/trigger/ledtrig-audio.c +++ b/drivers/leds/trigger/ledtrig-audio.c @@ -6,10 +6,33 @@ #include #include #include +#include "../leds.h" -static struct led_trigger *ledtrig_audio[NUM_AUDIO_LEDS]; static enum led_brightness audio_state[NUM_AUDIO_LEDS]; +static int ledtrig_audio_mute_activate(struct led_classdev *led_cdev) +{ + led_set_brightness_nosleep(led_cdev, audio_state[LED_AUDIO_MUTE]); + return 0; +} + +static int ledtrig_audio_micmute_activate(struct led_classdev *led_cdev) +{ + led_set_brightness_nosleep(led_cdev, audio_state[LED_AUDIO_MICMUTE]); + return 0; +} + +static struct led_trigger ledtrig_audio[NUM_AUDIO_LEDS] = { + [LED_AUDIO_MUTE] = { + .name = "audio-mute", + .activate = ledtrig_audio_mute_activate, + }, + [LED_AUDIO_MICMUTE] = { + .name = "audio-micmute", + .activate = ledtrig_audio_micmute_activate, + }, +}; + enum led_brightness ledtrig_audio_get(enum led_audio type) { return audio_state[type]; @@ -19,24 +42,22 @@ EXPORT_SYMBOL_GPL(ledtrig_audio_get); void ledtrig_audio_set(enum led_audio type, enum led_brightness state) { audio_state[type] = state; - led_trigger_event(ledtrig_audio[type], state); + led_trigger_event(&ledtrig_audio[type], state); } EXPORT_SYMBOL_GPL(ledtrig_audio_set); static int __init ledtrig_audio_init(void) { - led_trigger_register_simple("audio-mute", - &ledtrig_audio[LED_AUDIO_MUTE]); - led_trigger_register_simple("audio-micmute", - &ledtrig_audio[LED_AUDIO_MICMUTE]); + led_trigger_register(&ledtrig_audio[LED_AUDIO_MUTE]); + led_trigger_register(&ledtrig_audio[LED_AUDIO_MICMUTE]); return 0; } module_init(ledtrig_audio_init); static void __exit ledtrig_audio_exit(void) { - led_trigger_unregister_simple(ledtrig_audio[LED_AUDIO_MUTE]); - led_trigger_unregister_simple(ledtrig_audio[LED_AUDIO_MICMUTE]); + led_trigger_unregister(&ledtrig_audio[LED_AUDIO_MUTE]); + led_trigger_unregister(&ledtrig_audio[LED_AUDIO_MICMUTE]); } module_exit(ledtrig_audio_exit); -- cgit v1.2.3 From 9cbc861095375793a69858f91f3ac4e817f320f0 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 5 Aug 2021 14:26:19 +0300 Subject: leds: lgm-sso: Propagate error codes from callee to caller The one of the latest change to the driver reveals the problem that the error codes from callee aren't propagated to the caller of __sso_led_dt_parse(). Fix this accordingly. Fixes: 9999908ca1ab ("leds: lgm-sso: Put fwnode in any case during ->probe()") Fixes: c3987cd2bca3 ("leds: lgm: Add LED controller driver for LGM SoC") Reported-by: kernel test robot Signed-off-by: Andy Shevchenko Signed-off-by: Pavel Machek --- drivers/leds/blink/leds-lgm-sso.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/leds/blink/leds-lgm-sso.c b/drivers/leds/blink/leds-lgm-sso.c index e47c47e421d6..fd8b7573285a 100644 --- a/drivers/leds/blink/leds-lgm-sso.c +++ b/drivers/leds/blink/leds-lgm-sso.c @@ -640,7 +640,7 @@ __sso_led_dt_parse(struct sso_led_priv *priv, struct fwnode_handle *fw_ssoled) fwnode_child, GPIOD_ASIS, NULL); if (IS_ERR(led->gpiod)) { - dev_err_probe(dev, PTR_ERR(led->gpiod), "led: get gpio fail!\n"); + ret = dev_err_probe(dev, PTR_ERR(led->gpiod), "led: get gpio fail!\n"); goto __dt_err; } @@ -660,8 +660,11 @@ __sso_led_dt_parse(struct sso_led_priv *priv, struct fwnode_handle *fw_ssoled) desc->panic_indicator = 1; ret = fwnode_property_read_u32(fwnode_child, "reg", &prop); - if (ret != 0 || prop >= SSO_LED_MAX_NUM) { + if (ret) + goto __dt_err; + if (prop >= SSO_LED_MAX_NUM) { dev_err(dev, "invalid LED pin:%u\n", prop); + ret = -EINVAL; goto __dt_err; } desc->pin = prop; @@ -697,7 +700,8 @@ __sso_led_dt_parse(struct sso_led_priv *priv, struct fwnode_handle *fw_ssoled) desc->brightness = LED_FULL; } - if (sso_create_led(priv, led, fwnode_child)) + ret = sso_create_led(priv, led, fwnode_child); + if (ret) goto __dt_err; } @@ -709,7 +713,7 @@ __dt_err: list_for_each_entry(led, &priv->led_list, list) sso_led_shutdown(led); - return -EINVAL; + return ret; } static int sso_led_dt_parse(struct sso_led_priv *priv) -- cgit v1.2.3 From 654933ae7d32f278eecd0bb0f175785574ac4775 Mon Sep 17 00:00:00 2001 From: Pavel Machek Date: Mon, 16 Aug 2021 08:47:08 +0200 Subject: leds: flash: Remove redundant initialization of variable ret Adjust initialization not to trigger Coverity warnings. Reported-by: Colin Ian King Signed-off-by: Pavel Machek --- drivers/leds/led-class-flash.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/leds/led-class-flash.c b/drivers/leds/led-class-flash.c index 6eeb9effcf65..185e17055317 100644 --- a/drivers/leds/led-class-flash.c +++ b/drivers/leds/led-class-flash.c @@ -92,14 +92,12 @@ static ssize_t flash_strobe_store(struct device *dev, struct led_classdev *led_cdev = dev_get_drvdata(dev); struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev); unsigned long state; - ssize_t ret = -EINVAL; + ssize_t ret = -EBUSY; mutex_lock(&led_cdev->led_access); - if (led_sysfs_is_disabled(led_cdev)) { - ret = -EBUSY; + if (led_sysfs_is_disabled(led_cdev)) goto unlock; - } ret = kstrtoul(buf, 10, &state); if (ret) -- cgit v1.2.3 From 791bc41163c51f870972d6c6b82d971ce951096c Mon Sep 17 00:00:00 2001 From: Denis Osterland-Heim Date: Tue, 8 Jun 2021 08:35:53 +0200 Subject: leds: move default_state read from fwnode to core This patch introduces a new function to read initial default_state from fwnode. Suggested-by: Pavel Machek Signed-off-by: Denis Osterland-Heim Signed-off-by: Pavel Machek --- drivers/leds/led-core.c | 15 +++++++++++++++ drivers/leds/leds-gpio.c | 12 ++---------- drivers/leds/leds.h | 1 + include/linux/leds.h | 12 +++++++++--- 4 files changed, 27 insertions(+), 13 deletions(-) diff --git a/drivers/leds/led-core.c b/drivers/leds/led-core.c index 8eb8054ef9c6..4a97cb745788 100644 --- a/drivers/leds/led-core.c +++ b/drivers/leds/led-core.c @@ -477,3 +477,18 @@ int led_compose_name(struct device *dev, struct led_init_data *init_data, return 0; } EXPORT_SYMBOL_GPL(led_compose_name); + +enum led_default_state led_init_default_state_get(struct fwnode_handle *fwnode) +{ + const char *state = NULL; + + if (!fwnode_property_read_string(fwnode, "default-state", &state)) { + if (!strcmp(state, "keep")) + return LEDS_DEFSTATE_KEEP; + if (!strcmp(state, "on")) + return LEDS_DEFSTATE_ON; + } + + return LEDS_DEFSTATE_OFF; +} +EXPORT_SYMBOL_GPL(led_init_default_state_get); diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c index b5d5e22d2d1e..092eb59a7d32 100644 --- a/drivers/leds/leds-gpio.c +++ b/drivers/leds/leds-gpio.c @@ -16,6 +16,7 @@ #include #include #include +#include "leds.h" struct gpio_led_data { struct led_classdev cdev; @@ -144,7 +145,6 @@ static struct gpio_leds_priv *gpio_leds_create(struct platform_device *pdev) device_for_each_child_node(dev, child) { struct gpio_led_data *led_dat = &priv->leds[priv->num_leds]; struct gpio_led led = {}; - const char *state = NULL; /* * Acquire gpiod from DT with uninitialized label, which @@ -161,15 +161,7 @@ static struct gpio_leds_priv *gpio_leds_create(struct platform_device *pdev) led_dat->gpiod = led.gpiod; - if (!fwnode_property_read_string(child, "default-state", - &state)) { - if (!strcmp(state, "keep")) - led.default_state = LEDS_GPIO_DEFSTATE_KEEP; - else if (!strcmp(state, "on")) - led.default_state = LEDS_GPIO_DEFSTATE_ON; - else - led.default_state = LEDS_GPIO_DEFSTATE_OFF; - } + led.default_state = led_init_default_state_get(child); if (fwnode_property_present(child, "retain-state-suspended")) led.retain_state_suspended = 1; diff --git a/drivers/leds/leds.h b/drivers/leds/leds.h index 345062ccabda..aa64757a4d89 100644 --- a/drivers/leds/leds.h +++ b/drivers/leds/leds.h @@ -27,6 +27,7 @@ ssize_t led_trigger_read(struct file *filp, struct kobject *kobj, ssize_t led_trigger_write(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t pos, size_t count); +enum led_default_state led_init_default_state_get(struct fwnode_handle *fwnode); extern struct rw_semaphore leds_list_lock; extern struct list_head leds_list; diff --git a/include/linux/leds.h b/include/linux/leds.h index 329fd914cf24..a0b730be40ad 100644 --- a/include/linux/leds.h +++ b/include/linux/leds.h @@ -33,6 +33,12 @@ enum led_brightness { LED_FULL = 255, }; +enum led_default_state { + LEDS_DEFSTATE_OFF = 0, + LEDS_DEFSTATE_ON = 1, + LEDS_DEFSTATE_KEEP = 2, +}; + struct led_init_data { /* device fwnode handle */ struct fwnode_handle *fwnode; @@ -520,9 +526,9 @@ struct gpio_led { /* default_state should be one of LEDS_GPIO_DEFSTATE_(ON|OFF|KEEP) */ struct gpio_desc *gpiod; }; -#define LEDS_GPIO_DEFSTATE_OFF 0 -#define LEDS_GPIO_DEFSTATE_ON 1 -#define LEDS_GPIO_DEFSTATE_KEEP 2 +#define LEDS_GPIO_DEFSTATE_OFF LEDS_DEFSTATE_OFF +#define LEDS_GPIO_DEFSTATE_ON LEDS_DEFSTATE_ON +#define LEDS_GPIO_DEFSTATE_KEEP LEDS_DEFSTATE_KEEP struct gpio_led_platform_data { int num_leds; -- cgit v1.2.3 From 3d3d65bd27645830a6f23af29d0f9ebe5cc8bcb2 Mon Sep 17 00:00:00 2001 From: Denis Osterland-Heim Date: Tue, 8 Jun 2021 08:35:54 +0200 Subject: leds: pwm: add support for default-state device property This patch adds support for "default-state" devicetree property, which allows to defer pwm init to first use of led. This allows to configure the PWM early in bootloader to let the LED blink until an application in Linux userspace sets something different. Signed-off-by: Denis Osterland-Heim Signed-off-by: Pavel Machek --- drivers/leds/leds-pwm.c | 49 +++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 43 insertions(+), 6 deletions(-) diff --git a/drivers/leds/leds-pwm.c b/drivers/leds/leds-pwm.c index d71e9fa5c8de..6832180c1c54 100644 --- a/drivers/leds/leds-pwm.c +++ b/drivers/leds/leds-pwm.c @@ -17,10 +17,12 @@ #include #include #include +#include "leds.h" struct led_pwm { const char *name; u8 active_low; + u8 default_state; unsigned int max_brightness; }; @@ -77,7 +79,38 @@ static int led_pwm_add(struct device *dev, struct led_pwm_priv *priv, led_data->cdev.brightness_set_blocking = led_pwm_set; - pwm_init_state(led_data->pwm, &led_data->pwmstate); + /* init PWM state */ + switch (led->default_state) { + case LEDS_DEFSTATE_KEEP: + pwm_get_state(led_data->pwm, &led_data->pwmstate); + if (led_data->pwmstate.period) + break; + led->default_state = LEDS_DEFSTATE_OFF; + dev_warn(dev, + "failed to read period for %s, default to off", + led->name); + fallthrough; + default: + pwm_init_state(led_data->pwm, &led_data->pwmstate); + break; + } + + /* set brightness */ + switch (led->default_state) { + case LEDS_DEFSTATE_ON: + led_data->cdev.brightness = led->max_brightness; + break; + case LEDS_DEFSTATE_KEEP: + { + uint64_t brightness; + + brightness = led->max_brightness; + brightness *= led_data->pwmstate.duty_cycle; + do_div(brightness, led_data->pwmstate.period); + led_data->cdev.brightness = brightness; + } + break; + } ret = devm_led_classdev_register_ext(dev, &led_data->cdev, &init_data); if (ret) { @@ -86,11 +119,13 @@ static int led_pwm_add(struct device *dev, struct led_pwm_priv *priv, return ret; } - ret = led_pwm_set(&led_data->cdev, led_data->cdev.brightness); - if (ret) { - dev_err(dev, "failed to set led PWM value for %s: %d", - led->name, ret); - return ret; + if (led->default_state != LEDS_DEFSTATE_KEEP) { + ret = led_pwm_set(&led_data->cdev, led_data->cdev.brightness); + if (ret) { + dev_err(dev, "failed to set led PWM value for %s: %d", + led->name, ret); + return ret; + } } priv->num_leds++; @@ -120,6 +155,8 @@ static int led_pwm_create_fwnode(struct device *dev, struct led_pwm_priv *priv) fwnode_property_read_u32(fwnode, "max-brightness", &led.max_brightness); + led.default_state = led_init_default_state_get(fwnode); + ret = led_pwm_add(dev, priv, &led, fwnode); if (ret) goto err_child_out; -- cgit v1.2.3 From 8b624007e72f448df91e3bd4a9f23ad516599040 Mon Sep 17 00:00:00 2001 From: Jan Kundrát Date: Fri, 30 Jul 2021 23:59:11 +0200 Subject: leds: lp50xx: Fix chip name in KConfig MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 9-channel one is called LP5009, not LP509. Signed-off-by: Jan Kundrát Reviewed-by: Andy Shevchenko Signed-off-by: Pavel Machek --- drivers/leds/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index da7773dc8670..ed800f5da7d8 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -369,7 +369,7 @@ config LEDS_LP3952 module will be called leds-lp3952. config LEDS_LP50XX - tristate "LED Support for TI LP5036/30/24/18/12/9 LED driver chip" + tristate "LED Support for TI LP5036/30/24/18/12/09 LED driver chip" depends on LEDS_CLASS && REGMAP_I2C depends on LEDS_CLASS_MULTICOLOR || !LEDS_CLASS_MULTICOLOR help -- cgit v1.2.3 From 5358680e675744962a8adc99263adf59adfa8960 Mon Sep 17 00:00:00 2001 From: Lukas Bulwahn Date: Wed, 4 Aug 2021 14:34:25 +0200 Subject: leds: trigger: remove reference to obsolete CONFIG_IDE_GD_ATA Commit b7fb14d3ac63 ("ide: remove the legacy ide driver") removes the definition of the config IDE_GD_ATA. So, remove the obsolete reference in ./drivers/leds/trigger/Kconfig. Signed-off-by: Lukas Bulwahn Signed-off-by: Pavel Machek --- drivers/leds/trigger/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/leds/trigger/Kconfig b/drivers/leds/trigger/Kconfig index b77a01bd27f4..1f1d57288085 100644 --- a/drivers/leds/trigger/Kconfig +++ b/drivers/leds/trigger/Kconfig @@ -34,7 +34,7 @@ config LEDS_TRIGGER_ONESHOT config LEDS_TRIGGER_DISK bool "LED Disk Trigger" - depends on IDE_GD_ATA || ATA + depends on ATA help This allows LEDs to be controlled by disk activity. If unsure, say Y. -- cgit v1.2.3 From 09f1273064eea23ec41fb206f6eccc2bf79d1fa1 Mon Sep 17 00:00:00 2001 From: Pavel Machek Date: Fri, 20 Aug 2021 10:26:24 +0200 Subject: Documentation: leds: standartizing LED names We have a list of valid functions, but LED names in sysfs are still far from being consistent. Create list of "well known" LED names so we nudge people towards using same LED names (except color) for same functionality. Signed-off-by: Pavel Machek --- Documentation/leds/well-known-leds.txt | 58 ++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 Documentation/leds/well-known-leds.txt diff --git a/Documentation/leds/well-known-leds.txt b/Documentation/leds/well-known-leds.txt new file mode 100644 index 000000000000..4a8b9dc4bf52 --- /dev/null +++ b/Documentation/leds/well-known-leds.txt @@ -0,0 +1,58 @@ +-*- org -*- + +It is somehow important to provide consistent interface to the +userland. LED devices have one problem there, and that is naming of +directories in /sys/class/leds. It would be nice if userland would +just know right "name" for given LED function, but situation got more +complex. + +Anyway, if backwards compatibility is not an issue, new code should +use one of the "good" names from this list, and you should extend the +list where applicable. + +Legacy names are listed, too; in case you are writing application that +wants to use particular feature, you should probe for good name, first, +but then try the legacy ones, too. + +Notice there's a list of functions in include/dt-bindings/leds/common.h . + +* Keyboards + +Good: "input*:*:capslock" +Good: "input*:*:scrolllock" +Good: "input*:*:numlock" +Legacy: "shift-key-light" (Motorola Droid 4, capslock) + +Set of common keyboard LEDs, going back to PC AT or so. + +Legacy: "tpacpi::thinklight" (IBM/Lenovo Thinkpads) +Legacy: "lp5523:kb{1,2,3,4,5,6}" (Nokia N900) + +Frontlight/backlight of main keyboard. + +Legacy: "button-backlight" (Motorola Droid 4) + +Some phones have touch buttons below screen; it is different from main +keyboard. And this is their backlight. + +* Sound subsystem + +Good: "platform:*:mute" +Good: "platform:*:micmute" + +LEDs on notebook body, indicating that sound input / output is muted. + +* System notification + +Legacy: "status-led:{red,green,blue}" (Motorola Droid 4) +Legacy: "lp5523:{r,g,b}" (Nokia N900) + +Phones usually have multi-color status LED. + +* Power management + +Good: "platform:*:charging" (allwinner sun50i) + +* Screen + +Good: ":backlight" (Motorola Droid 4) -- cgit v1.2.3 From 5d823d6d69853ae07786ddf786f7693b57e76beb Mon Sep 17 00:00:00 2001 From: Eddie James Date: Fri, 16 Jul 2021 17:03:25 -0500 Subject: dt-bindings: leds: Add retain-state-shutdown boolean Document the retain-state-shutdown property that indicates that a LED should not be turned off or changed during system shutdown. Signed-off-by: Eddie James Acked-by: Rob Herring Signed-off-by: Pavel Machek --- Documentation/devicetree/bindings/leds/common.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Documentation/devicetree/bindings/leds/common.yaml b/Documentation/devicetree/bindings/leds/common.yaml index b1f363747a62..697102707703 100644 --- a/Documentation/devicetree/bindings/leds/common.yaml +++ b/Documentation/devicetree/bindings/leds/common.yaml @@ -128,6 +128,12 @@ properties: as a panic indicator. type: boolean + retain-state-shutdown: + description: + This property specifies that the LED should not be turned off or changed + when the system shuts down. + type: boolean + trigger-sources: description: | List of devices which should be used as a source triggering this LED -- cgit v1.2.3 From 419066324e19a47b98cdcf5defda42de555a8957 Mon Sep 17 00:00:00 2001 From: Eddie James Date: Fri, 16 Jul 2021 17:03:26 -0500 Subject: leds: leds-core: Implement the retain-state-shutdown property Read the retain-state-shutdown device tree property to set the existing LED_RETAIN_AT_SHUTDOWN flag. Then check the flag when unregistering, and if set, don't set the brightness to OFF. This is useful for systems that want to keep the HW state of the LED across reboots. Signed-off-by: Eddie James Signed-off-by: Pavel Machek --- drivers/leds/led-class.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c index f704391d57a8..f4bb02f6e042 100644 --- a/drivers/leds/led-class.c +++ b/drivers/leds/led-class.c @@ -350,10 +350,15 @@ int led_classdev_register_ext(struct device *parent, if (ret < 0) return ret; - if (init_data->fwnode) + if (init_data->fwnode) { fwnode_property_read_string(init_data->fwnode, "linux,default-trigger", &led_cdev->default_trigger); + + if (fwnode_property_present(init_data->fwnode, + "retain-state-shutdown")) + led_cdev->flags |= LED_RETAIN_AT_SHUTDOWN; + } } else { proposed_name = led_cdev->name; } @@ -444,7 +449,8 @@ void led_classdev_unregister(struct led_classdev *led_cdev) /* Stop blinking */ led_stop_software_blink(led_cdev); - led_set_brightness(led_cdev, LED_OFF); + if (!(led_cdev->flags & LED_RETAIN_AT_SHUTDOWN)) + led_set_brightness(led_cdev, LED_OFF); flush_work(&led_cdev->set_brightness_work); -- cgit v1.2.3 From 2420ae02ce0a926819ebe18f809a57bff3edeac2 Mon Sep 17 00:00:00 2001 From: Eddie James Date: Fri, 16 Jul 2021 17:03:27 -0500 Subject: leds: pca955x: Clean up code formatting Format the code. Add some variables to help shorten lines. Signed-off-by: Eddie James Signed-off-by: Pavel Machek --- drivers/leds/leds-pca955x.c | 63 +++++++++++++++++++++------------------------ 1 file changed, 30 insertions(+), 33 deletions(-) diff --git a/drivers/leds/leds-pca955x.c b/drivers/leds/leds-pca955x.c index 7087ca4592fc..f0d841cb59fc 100644 --- a/drivers/leds/leds-pca955x.c +++ b/drivers/leds/leds-pca955x.c @@ -166,11 +166,10 @@ static inline u8 pca955x_ledsel(u8 oldval, int led_num, int state) static int pca955x_write_psc(struct i2c_client *client, int n, u8 val) { struct pca955x *pca955x = i2c_get_clientdata(client); + u8 cmd = pca95xx_num_input_regs(pca955x->chipdef->bits) + (2 * n); int ret; - ret = i2c_smbus_write_byte_data(client, - pca95xx_num_input_regs(pca955x->chipdef->bits) + 2*n, - val); + ret = i2c_smbus_write_byte_data(client, cmd, val); if (ret < 0) dev_err(&client->dev, "%s: reg 0x%x, val 0x%x, err %d\n", __func__, n, val, ret); @@ -187,11 +186,10 @@ static int pca955x_write_psc(struct i2c_client *client, int n, u8 val) static int pca955x_write_pwm(struct i2c_client *client, int n, u8 val) { struct pca955x *pca955x = i2c_get_clientdata(client); + u8 cmd = pca95xx_num_input_regs(pca955x->chipdef->bits) + 1 + (2 * n); int ret; - ret = i2c_smbus_write_byte_data(client, - pca95xx_num_input_regs(pca955x->chipdef->bits) + 1 + 2*n, - val); + ret = i2c_smbus_write_byte_data(client, cmd, val); if (ret < 0) dev_err(&client->dev, "%s: reg 0x%x, val 0x%x, err %d\n", __func__, n, val, ret); @@ -205,11 +203,10 @@ static int pca955x_write_pwm(struct i2c_client *client, int n, u8 val) static int pca955x_write_ls(struct i2c_client *client, int n, u8 val) { struct pca955x *pca955x = i2c_get_clientdata(client); + u8 cmd = pca95xx_num_input_regs(pca955x->chipdef->bits) + 4 + n; int ret; - ret = i2c_smbus_write_byte_data(client, - pca95xx_num_input_regs(pca955x->chipdef->bits) + 4 + n, - val); + ret = i2c_smbus_write_byte_data(client, cmd, val); if (ret < 0) dev_err(&client->dev, "%s: reg 0x%x, val 0x%x, err %d\n", __func__, n, val, ret); @@ -223,10 +220,10 @@ static int pca955x_write_ls(struct i2c_client *client, int n, u8 val) static int pca955x_read_ls(struct i2c_client *client, int n, u8 *val) { struct pca955x *pca955x = i2c_get_clientdata(client); + u8 cmd = pca95xx_num_input_regs(pca955x->chipdef->bits) + 4 + n; int ret; - ret = i2c_smbus_read_byte_data(client, - pca95xx_num_input_regs(pca955x->chipdef->bits) + 4 + n); + ret = i2c_smbus_read_byte_data(client, cmd); if (ret < 0) { dev_err(&client->dev, "%s: reg 0x%x, err %d\n", __func__, n, ret); @@ -371,6 +368,7 @@ static struct pca955x_platform_data * pca955x_get_pdata(struct i2c_client *client, struct pca955x_chipdef *chip) { struct pca955x_platform_data *pdata; + struct pca955x_led *led; struct fwnode_handle *child; int count; @@ -401,13 +399,13 @@ pca955x_get_pdata(struct i2c_client *client, struct pca955x_chipdef *chip) if ((res != 0) && is_of_node(child)) name = to_of_node(child)->name; - snprintf(pdata->leds[reg].name, sizeof(pdata->leds[reg].name), - "%s", name); + led = &pdata->leds[reg]; + snprintf(led->name, sizeof(led->name), "%s", name); - pdata->leds[reg].type = PCA955X_TYPE_LED; - fwnode_property_read_u32(child, "type", &pdata->leds[reg].type); + led->type = PCA955X_TYPE_LED; + fwnode_property_read_u32(child, "type", &led->type); fwnode_property_read_string(child, "linux,default-trigger", - &pdata->leds[reg].default_trigger); + &led->default_trigger); } pdata->num_leds = chip->bits; @@ -426,11 +424,12 @@ static const struct of_device_id of_pca955x_match[] = { MODULE_DEVICE_TABLE(of, of_pca955x_match); static int pca955x_probe(struct i2c_client *client, - const struct i2c_device_id *id) + const struct i2c_device_id *id) { struct pca955x *pca955x; struct pca955x_led *pca955x_led; struct pca955x_chipdef *chip; + struct led_classdev *led; struct i2c_adapter *adapter; int i, err; struct pca955x_platform_data *pdata; @@ -449,13 +448,13 @@ static int pca955x_probe(struct i2c_client *client, if ((client->addr & ~((1 << chip->slv_addr_shift) - 1)) != chip->slv_addr) { dev_err(&client->dev, "invalid slave address %02x\n", - client->addr); + client->addr); return -ENODEV; } dev_info(&client->dev, "leds-pca955x: Using %s %d-bit LED driver at " - "slave address 0x%02x\n", - client->name, chip->bits, client->addr); + "slave address 0x%02x\n", client->name, chip->bits, + client->addr); if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -EIO; @@ -471,8 +470,8 @@ static int pca955x_probe(struct i2c_client *client, if (!pca955x) return -ENOMEM; - pca955x->leds = devm_kcalloc(&client->dev, - chip->bits, sizeof(*pca955x_led), GFP_KERNEL); + pca955x->leds = devm_kcalloc(&client->dev, chip->bits, + sizeof(*pca955x_led), GFP_KERNEL); if (!pca955x->leds) return -ENOMEM; @@ -501,27 +500,25 @@ static int pca955x_probe(struct i2c_client *client, */ if (pdata->leds[i].name[0] == '\0') snprintf(pdata->leds[i].name, - sizeof(pdata->leds[i].name), "%d", i); + sizeof(pdata->leds[i].name), "%d", i); - snprintf(pca955x_led->name, - sizeof(pca955x_led->name), "pca955x:%s", - pdata->leds[i].name); + snprintf(pca955x_led->name, sizeof(pca955x_led->name), + "pca955x:%s", pdata->leds[i].name); + led = &pca955x_led->led_cdev; if (pdata->leds[i].default_trigger) - pca955x_led->led_cdev.default_trigger = + led->default_trigger = pdata->leds[i].default_trigger; - pca955x_led->led_cdev.name = pca955x_led->name; - pca955x_led->led_cdev.brightness_set_blocking = - pca955x_led_set; + led->name = pca955x_led->name; + led->brightness_set_blocking = pca955x_led_set; - err = devm_led_classdev_register(&client->dev, - &pca955x_led->led_cdev); + err = devm_led_classdev_register(&client->dev, led); if (err) return err; /* Turn off LED */ - err = pca955x_led_set(&pca955x_led->led_cdev, LED_OFF); + err = pca955x_led_set(led, LED_OFF); if (err) return err; } -- cgit v1.2.3 From 7086625fde6538b2c0623eb767ad23c7ac3d7f3a Mon Sep 17 00:00:00 2001 From: Eddie James Date: Fri, 16 Jul 2021 17:03:28 -0500 Subject: leds: pca955x: Add brightness_get function Add a function to fetch the state of the hardware LED. Signed-off-by: Eddie James Signed-off-by: Pavel Machek --- drivers/leds/leds-pca955x.c | 52 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/drivers/leds/leds-pca955x.c b/drivers/leds/leds-pca955x.c index f0d841cb59fc..e47ba7c3b7c7 100644 --- a/drivers/leds/leds-pca955x.c +++ b/drivers/leds/leds-pca955x.c @@ -233,6 +233,57 @@ static int pca955x_read_ls(struct i2c_client *client, int n, u8 *val) return 0; } +static int pca955x_read_pwm(struct i2c_client *client, int n, u8 *val) +{ + struct pca955x *pca955x = i2c_get_clientdata(client); + u8 cmd = pca95xx_num_input_regs(pca955x->chipdef->bits) + 1 + (2 * n); + int ret; + + ret = i2c_smbus_read_byte_data(client, cmd); + if (ret < 0) { + dev_err(&client->dev, "%s: reg 0x%x, err %d\n", + __func__, n, ret); + return ret; + } + *val = (u8)ret; + return 0; +} + +static enum led_brightness pca955x_led_get(struct led_classdev *led_cdev) +{ + struct pca955x_led *pca955x_led = container_of(led_cdev, + struct pca955x_led, + led_cdev); + struct pca955x *pca955x = pca955x_led->pca955x; + u8 ls, pwm; + int ret; + + ret = pca955x_read_ls(pca955x->client, pca955x_led->led_num / 4, &ls); + if (ret) + return ret; + + ls = (ls >> ((pca955x_led->led_num % 4) << 1)) & 0x3; + switch (ls) { + case PCA955X_LS_LED_ON: + ret = LED_FULL; + break; + case PCA955X_LS_LED_OFF: + ret = LED_OFF; + break; + case PCA955X_LS_BLINK0: + ret = LED_HALF; + break; + case PCA955X_LS_BLINK1: + ret = pca955x_read_pwm(pca955x->client, 1, &pwm); + if (ret) + return ret; + ret = 255 - pwm; + break; + } + + return ret; +} + static int pca955x_led_set(struct led_classdev *led_cdev, enum led_brightness value) { @@ -512,6 +563,7 @@ static int pca955x_probe(struct i2c_client *client, led->name = pca955x_led->name; led->brightness_set_blocking = pca955x_led_set; + led->brightness_get = pca955x_led_get; err = devm_led_classdev_register(&client->dev, led); if (err) -- cgit v1.2.3 From e46cb6d0c760a5b15e38138845fad99628fafcb8 Mon Sep 17 00:00:00 2001 From: Eddie James Date: Fri, 16 Jul 2021 17:03:29 -0500 Subject: leds: pca955x: Implement the default-state property In order to retain the LED state after a system reboot, check the documented default-state device tree property during initialization. Modify the behavior of the probe according to the property. Signed-off-by: Eddie James Signed-off-by: Pavel Machek --- drivers/leds/leds-pca955x.c | 54 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 46 insertions(+), 8 deletions(-) diff --git a/drivers/leds/leds-pca955x.c b/drivers/leds/leds-pca955x.c index e47ba7c3b7c7..fa1d77d86ef6 100644 --- a/drivers/leds/leds-pca955x.c +++ b/drivers/leds/leds-pca955x.c @@ -129,6 +129,7 @@ struct pca955x_led { int led_num; /* 0 .. 15 potentially */ char name[32]; u32 type; + int default_state; const char *default_trigger; }; @@ -439,6 +440,7 @@ pca955x_get_pdata(struct i2c_client *client, struct pca955x_chipdef *chip) device_for_each_child_node(&client->dev, child) { const char *name; + const char *state; u32 reg; int res; @@ -457,6 +459,18 @@ pca955x_get_pdata(struct i2c_client *client, struct pca955x_chipdef *chip) fwnode_property_read_u32(child, "type", &led->type); fwnode_property_read_string(child, "linux,default-trigger", &led->default_trigger); + + if (!fwnode_property_read_string(child, "default-state", + &state)) { + if (!strcmp(state, "keep")) + led->default_state = LEDS_GPIO_DEFSTATE_KEEP; + else if (!strcmp(state, "on")) + led->default_state = LEDS_GPIO_DEFSTATE_ON; + else + led->default_state = LEDS_GPIO_DEFSTATE_OFF; + } else { + led->default_state = LEDS_GPIO_DEFSTATE_OFF; + } } pdata->num_leds = chip->bits; @@ -485,6 +499,7 @@ static int pca955x_probe(struct i2c_client *client, int i, err; struct pca955x_platform_data *pdata; int ngpios = 0; + bool keep_pwm = false; chip = &pca955x_chipdefs[id->driver_data]; adapter = client->adapter; @@ -565,14 +580,35 @@ static int pca955x_probe(struct i2c_client *client, led->brightness_set_blocking = pca955x_led_set; led->brightness_get = pca955x_led_get; + if (pdata->leds[i].default_state == + LEDS_GPIO_DEFSTATE_OFF) { + err = pca955x_led_set(led, LED_OFF); + if (err) + return err; + } else if (pdata->leds[i].default_state == + LEDS_GPIO_DEFSTATE_ON) { + err = pca955x_led_set(led, LED_FULL); + if (err) + return err; + } + err = devm_led_classdev_register(&client->dev, led); if (err) return err; - /* Turn off LED */ - err = pca955x_led_set(led, LED_OFF); - if (err) - return err; + /* + * For default-state == "keep", let the core update the + * brightness from the hardware, then check the + * brightness to see if it's using PWM1. If so, PWM1 + * should not be written below. + */ + if (pdata->leds[i].default_state == + LEDS_GPIO_DEFSTATE_KEEP) { + if (led->brightness != LED_FULL && + led->brightness != LED_OFF && + led->brightness != LED_HALF) + keep_pwm = true; + } } } @@ -581,10 +617,12 @@ static int pca955x_probe(struct i2c_client *client, if (err) return err; - /* PWM1 is used for variable brightness, default to OFF */ - err = pca955x_write_pwm(client, 1, 0); - if (err) - return err; + if (!keep_pwm) { + /* PWM1 is used for variable brightness, default to OFF */ + err = pca955x_write_pwm(client, 1, 0); + if (err) + return err; + } /* Set to fast frequency so we do not see flashing */ err = pca955x_write_psc(client, 0, 0); -- cgit v1.2.3 From 7c4815929276b2e223eb6f2e49afe5071d4294a5 Mon Sep 17 00:00:00 2001 From: Eddie James Date: Fri, 16 Jul 2021 17:03:30 -0500 Subject: leds: pca955x: Let the core process the fwnode Much of the fwnode processing in the PCA955x driver is now in the LEDs core driver, so pass the fwnode in the init data when registering the LED device. In order to preserve the existing naming scheme, check for an empty name and set it to the LED number. Signed-off-by: Eddie James Signed-off-by: Pavel Machek --- drivers/leds/leds-pca955x.c | 58 +++++++++++++++++++++++---------------------- 1 file changed, 30 insertions(+), 28 deletions(-) diff --git a/drivers/leds/leds-pca955x.c b/drivers/leds/leds-pca955x.c index fa1d77d86ef6..a6aa4b9abde8 100644 --- a/drivers/leds/leds-pca955x.c +++ b/drivers/leds/leds-pca955x.c @@ -127,10 +127,9 @@ struct pca955x_led { struct pca955x *pca955x; struct led_classdev led_cdev; int led_num; /* 0 .. 15 potentially */ - char name[32]; u32 type; int default_state; - const char *default_trigger; + struct fwnode_handle *fwnode; }; struct pca955x_platform_data { @@ -439,7 +438,6 @@ pca955x_get_pdata(struct i2c_client *client, struct pca955x_chipdef *chip) return ERR_PTR(-ENOMEM); device_for_each_child_node(&client->dev, child) { - const char *name; const char *state; u32 reg; int res; @@ -448,17 +446,10 @@ pca955x_get_pdata(struct i2c_client *client, struct pca955x_chipdef *chip) if ((res != 0) || (reg >= chip->bits)) continue; - res = fwnode_property_read_string(child, "label", &name); - if ((res != 0) && is_of_node(child)) - name = to_of_node(child)->name; - led = &pdata->leds[reg]; - snprintf(led->name, sizeof(led->name), "%s", name); - led->type = PCA955X_TYPE_LED; + led->fwnode = child; fwnode_property_read_u32(child, "type", &led->type); - fwnode_property_read_string(child, "linux,default-trigger", - &led->default_trigger); if (!fwnode_property_read_string(child, "default-state", &state)) { @@ -495,11 +486,14 @@ static int pca955x_probe(struct i2c_client *client, struct pca955x_led *pca955x_led; struct pca955x_chipdef *chip; struct led_classdev *led; + struct led_init_data init_data; struct i2c_adapter *adapter; int i, err; struct pca955x_platform_data *pdata; int ngpios = 0; + bool set_default_label = false; bool keep_pwm = false; + char default_label[8]; chip = &pca955x_chipdefs[id->driver_data]; adapter = client->adapter; @@ -547,6 +541,9 @@ static int pca955x_probe(struct i2c_client *client, pca955x->client = client; pca955x->chipdef = chip; + init_data.devname_mandatory = false; + init_data.devicename = "pca955x"; + for (i = 0; i < chip->bits; i++) { pca955x_led = &pca955x->leds[i]; pca955x_led->led_num = i; @@ -560,23 +557,7 @@ static int pca955x_probe(struct i2c_client *client, ngpios++; break; case PCA955X_TYPE_LED: - /* - * Platform data can specify LED names and - * default triggers - */ - if (pdata->leds[i].name[0] == '\0') - snprintf(pdata->leds[i].name, - sizeof(pdata->leds[i].name), "%d", i); - - snprintf(pca955x_led->name, sizeof(pca955x_led->name), - "pca955x:%s", pdata->leds[i].name); - led = &pca955x_led->led_cdev; - if (pdata->leds[i].default_trigger) - led->default_trigger = - pdata->leds[i].default_trigger; - - led->name = pca955x_led->name; led->brightness_set_blocking = pca955x_led_set; led->brightness_get = pca955x_led_get; @@ -592,7 +573,28 @@ static int pca955x_probe(struct i2c_client *client, return err; } - err = devm_led_classdev_register(&client->dev, led); + init_data.fwnode = pdata->leds[i].fwnode; + + if (is_of_node(init_data.fwnode)) { + if (to_of_node(init_data.fwnode)->name[0] == + '\0') + set_default_label = true; + else + set_default_label = false; + } else { + set_default_label = true; + } + + if (set_default_label) { + snprintf(default_label, sizeof(default_label), + "%d", i); + init_data.default_label = default_label; + } else { + init_data.default_label = NULL; + } + + err = devm_led_classdev_register_ext(&client->dev, led, + &init_data); if (err) return err; -- cgit v1.2.3 From 239f32b4f161c1584cd4b386d6ab8766432a6ede Mon Sep 17 00:00:00 2001 From: Eddie James Date: Fri, 16 Jul 2021 17:03:31 -0500 Subject: leds: pca955x: Switch to i2c probe_new The deprecated i2c probe functionality doesn't work with OF compatible strings, as it only checks for the i2c device id. Switch to the new way of probing and grab the match data to select the chip type. Signed-off-by: Eddie James Signed-off-by: Pavel Machek --- drivers/leds/leds-pca955x.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/drivers/leds/leds-pca955x.c b/drivers/leds/leds-pca955x.c index a6aa4b9abde8..a6b5699aeae4 100644 --- a/drivers/leds/leds-pca955x.c +++ b/drivers/leds/leds-pca955x.c @@ -479,8 +479,7 @@ static const struct of_device_id of_pca955x_match[] = { }; MODULE_DEVICE_TABLE(of, of_pca955x_match); -static int pca955x_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int pca955x_probe(struct i2c_client *client) { struct pca955x *pca955x; struct pca955x_led *pca955x_led; @@ -494,8 +493,24 @@ static int pca955x_probe(struct i2c_client *client, bool set_default_label = false; bool keep_pwm = false; char default_label[8]; + enum pca955x_type chip_type; + const void *md = device_get_match_data(&client->dev); - chip = &pca955x_chipdefs[id->driver_data]; + if (md) { + chip_type = (enum pca955x_type)md; + } else { + const struct i2c_device_id *id = i2c_match_id(pca955x_id, + client); + + if (id) { + chip_type = (enum pca955x_type)id->driver_data; + } else { + dev_err(&client->dev, "unknown chip\n"); + return -ENODEV; + } + } + + chip = &pca955x_chipdefs[chip_type]; adapter = client->adapter; pdata = dev_get_platdata(&client->dev); if (!pdata) { @@ -670,7 +685,7 @@ static struct i2c_driver pca955x_driver = { .name = "leds-pca955x", .of_match_table = of_pca955x_match, }, - .probe = pca955x_probe, + .probe_new = pca955x_probe, .id_table = pca955x_id, }; -- cgit v1.2.3