diff --git a/drivers/leds/rgb/Kconfig b/drivers/leds/rgb/Kconfig index 28ef4c487367..9a4ba6531cf8 100644 --- a/drivers/leds/rgb/Kconfig +++ b/drivers/leds/rgb/Kconfig @@ -39,6 +39,31 @@ config LEDS_LP5812 If unsure, say N. +config LEDS_LP5860_CORE + tristate "Core Driver for TI LP5860" + depends on LEDS_CLASS + depends on OF + select REGMAP + help + This option supports common operations for LP5860 devices. + The LP5860 is a LED matrix driver with 18 constant current + sinks and 11 scan switches for 198 LED dots. Each dot can be + controlled individually and supports 8/16-bit PWM dimming. + The chip supports individual LED open and short detection. + + The device can be used with SPI or I2C bus. + +config LEDS_LP5860_SPI + tristate "LED Support for TI LP5860 SPI LED driver chip" + depends on SPI + select LEDS_LP5860_CORE + help + If you say yes here you get support for the Texas Instruments + LP5860 LED driver for SPI bus connections. + + To compile this driver as a module, choose M here: the + module will be called leds-lp5860-spi. + config LEDS_NCP5623 tristate "LED support for NCP5623" depends on I2C diff --git a/drivers/leds/rgb/Makefile b/drivers/leds/rgb/Makefile index be45991f63f5..f3b365ea082d 100644 --- a/drivers/leds/rgb/Makefile +++ b/drivers/leds/rgb/Makefile @@ -3,6 +3,8 @@ obj-$(CONFIG_LEDS_GROUP_MULTICOLOR) += leds-group-multicolor.o obj-$(CONFIG_LEDS_KTD202X) += leds-ktd202x.o obj-$(CONFIG_LEDS_LP5812) += leds-lp5812.o +obj-$(CONFIG_LEDS_LP5860_CORE) += leds-lp5860-core.o +obj-$(CONFIG_LEDS_LP5860_SPI) += leds-lp5860-spi.o obj-$(CONFIG_LEDS_NCP5623) += leds-ncp5623.o obj-$(CONFIG_LEDS_PWM_MULTICOLOR) += leds-pwm-multicolor.o obj-$(CONFIG_LEDS_QCOM_LPG) += leds-qcom-lpg.o diff --git a/drivers/leds/rgb/leds-lp5860-core.c b/drivers/leds/rgb/leds-lp5860-core.c new file mode 100644 index 000000000000..fd0e2f6e6e0f --- /dev/null +++ b/drivers/leds/rgb/leds-lp5860-core.c @@ -0,0 +1,234 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2025 Pengutronix + * + * Author: Steffen Trumtrar + */ + +#include +#include +#include +#include +#include +#include + +#include "leds-lp5860.h" + +static struct lp5860_led *mcled_cdev_to_led(struct led_classdev_mc *mc_cdev) +{ + return container_of(mc_cdev, struct lp5860_led, mc_cdev); +} + +static int lp5860_set_dot_onoff(struct lp5860_led *led, unsigned int dot, bool enable) +{ + unsigned int offset = dot / LP5860_MAX_DOT_ONOFF_GROUP_NUM; + unsigned int mask = BIT(dot % LP5860_MAX_DOT_ONOFF_GROUP_NUM); + + if (dot > LP5860_MAX_LED) + return -EINVAL; + + return regmap_update_bits(led->chip->regmap, + LP5860_REG_DOT_ONOFF_START + offset, mask, + enable ? LP5860_DOT_ALL_ON : LP5860_DOT_ALL_OFF); +} + +static int lp5860_set_mc_brightness(struct led_classdev *cdev, + enum led_brightness brightness) +{ + struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(cdev); + struct lp5860_led *led = mcled_cdev_to_led(mc_cdev); + + led_mc_calc_color_components(mc_cdev, brightness); + + guard(mutex)(&led->chip->lock); + for (int i = 0; i < led->mc_cdev.num_colors; i++) { + unsigned int channel = mc_cdev->subled_info[i].channel; + unsigned int led_brightness = mc_cdev->subled_info[i].brightness; + int ret; + + ret = lp5860_set_dot_onoff(led, channel, !!led_brightness); + if (ret) + return ret; + + ret = regmap_write(led->chip->regmap, + LP5860_REG_PWM_BRI_START + channel, led_brightness); + if (ret) + return ret; + } + + return 0; +} + +static int lp5860_chip_enable(struct lp5860 *lp, bool enable) +{ + guard(mutex)(&lp->lock); + return regmap_write(lp->regmap, LP5860_REG_CHIP_EN, enable); +} + +static int lp5860_led_init(struct lp5860_led *led, struct fwnode_handle *fwnode, + unsigned int channel) +{ + enum led_default_state default_state; + unsigned int brightness; + int ret; + + guard(mutex)(&led->chip->lock); + ret = regmap_read(led->chip->regmap, LP5860_REG_PWM_BRI_START + channel, &brightness); + if (ret) + return ret; + + default_state = led_init_default_state_get(fwnode); + + switch (default_state) { + case LEDS_DEFSTATE_ON: + led->brightness = LP5860_MAX_BRIGHTNESS; + break; + case LEDS_DEFSTATE_KEEP: + led->brightness = min(brightness, LP5860_MAX_BRIGHTNESS); + break; + default: + led->brightness = 0; + break; + } + + return 0; +} + +static int lp5860_iterate_subleds(struct lp5860_led *led, struct led_init_data *init_data) +{ + struct fwnode_handle *led_node = NULL; + struct fwnode_handle *multi_led = init_data->fwnode; + int subled = 0; + + fwnode_for_each_child_node(multi_led, led_node) { + u32 channel; + u32 color_index; + int ret; + + ret = fwnode_property_read_u32(led_node, "color", &color_index); + if (ret) { + dev_err_probe(led->chip->dev, ret, + "%pfwP: Cannot read 'color' property. Skipping.\n", led_node); + fwnode_handle_put(led_node); + return ret; + } + + ret = fwnode_property_read_u32(led_node, "reg", &channel); + if (ret < 0 || channel > LP5860_MAX_LED) { + dev_err_probe(led->chip->dev, ret, + "%pfwP: 'reg' property is missing. Skipping.\n", led_node); + fwnode_handle_put(led_node); + return ret; + } + + led->mc_cdev.subled_info[subled].color_index = color_index; + led->mc_cdev.subled_info[subled].channel = channel; + ret = lp5860_led_init(led, init_data->fwnode, channel); + if (ret) { + dev_err_probe(led->chip->dev, ret, + "%pfwP: Failed to init LED\n", led_node); + fwnode_handle_put(led_node); + return ret; + } + + subled++; + } + + return 0; +} + +static int lp5860_init_dt(struct lp5860 *lp) +{ + struct led_init_data init_data = {}; + struct led_classdev *led_cdev; + struct mc_subled *mc_led_info; + struct lp5860_led *led; + int led_index = 0; + int chan; + int ret; + + device_for_each_child_node_scoped(lp->dev, multi_led) { + led = &lp->leds[led_index]; + + init_data.fwnode = multi_led; + + /* Count the number of channels in this multi_led */ + chan = fwnode_get_child_node_count(multi_led); + if (!chan || chan > LP5860_MAX_LED_CHANNELS) + return -EINVAL; + + led->mc_cdev.num_colors = chan; + + mc_led_info = devm_kcalloc(lp->dev, chan, sizeof(*mc_led_info), GFP_KERNEL); + if (!mc_led_info) + return -ENOMEM; + + led->chip = lp; + led->mc_cdev.subled_info = mc_led_info; + led_cdev = &led->mc_cdev.led_cdev; + led_cdev->max_brightness = LP5860_MAX_BRIGHTNESS; + led_cdev->brightness_set_blocking = lp5860_set_mc_brightness; + + ret = lp5860_iterate_subleds(led, &init_data); + if (ret) + continue; + + ret = lp5860_set_mc_brightness(&led->mc_cdev.led_cdev, led->brightness); + if (ret) + return dev_err_probe(lp->dev, ret, "%pfwP: Failed to set Multi-Color brightness\n", + multi_led); + + ret = devm_led_classdev_multicolor_register_ext(lp->dev, &led->mc_cdev, &init_data); + if (ret) + return dev_err_probe(lp->dev, ret, "%pfwP: Failed to register Multi-Color LEDs\n", + multi_led); + led_index++; + } + + return 0; +} + +int lp5860_device_init(struct device *dev) +{ + struct lp5860 *lp = dev_get_drvdata(dev); + int ret; + + ret = lp5860_chip_enable(lp, LP5860_CHIP_ENABLE); + if (ret) + return ret; + + /* + * Set to 8-bit PWM data without VSYNC. + * Data is sent out for display instantly after received. + */ + mutex_lock(&lp->lock); + ret = regmap_update_bits(lp->regmap, LP5860_REG_DEV_INITIAL, LP5860_MODE_MASK, + LP5860_MODE_1 << LP5860_MODE_SHIFT); + if (ret) + goto err_disable; + mutex_unlock(&lp->lock); + + ret = lp5860_init_dt(lp); + if (ret) + goto err_disable; + + return 0; + +err_disable: + mutex_unlock(&lp->lock); + lp5860_chip_enable(lp, LP5860_CHIP_DISABLE); + return ret; +} +EXPORT_SYMBOL_GPL(lp5860_device_init); + +void lp5860_device_remove(struct device *dev) +{ + struct lp5860 *lp = dev_get_drvdata(dev); + + lp5860_chip_enable(lp, LP5860_CHIP_DISABLE); +} +EXPORT_SYMBOL_GPL(lp5860_device_remove); + +MODULE_AUTHOR("Steffen Trumtrar "); +MODULE_DESCRIPTION("TI LP5860 RGB LED core driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/leds/rgb/leds-lp5860-spi.c b/drivers/leds/rgb/leds-lp5860-spi.c new file mode 100644 index 000000000000..5e0c44854a68 --- /dev/null +++ b/drivers/leds/rgb/leds-lp5860-spi.c @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2025 Pengutronix + * + * Author: Steffen Trumtrar + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "leds-lp5860.h" + +#define LP5860_SPI_WRITE_FLAG BIT(13) + +/* + * The lp5860 uses a rather uncommon SPI data format: The R/W flag is on BIT(5) in the two address + * bytes; BIT(4) to BIT(0) are don't care. Therefore it has 10 bits for the address and 6 bits for + * padding the address. The address bytes are sent MSB first. Matching the cores registers to regmap + * results in write_flag_mask being BIT(13). + */ +static const struct regmap_config lp5860_regmap_config = { + .name = "lp5860", + .reg_bits = 10, + .pad_bits = 6, + .val_bits = 8, + .write_flag_mask = LP5860_SPI_WRITE_FLAG, + .reg_format_endian = REGMAP_ENDIAN_BIG, + .max_register = LP5860_MAX_REG, +}; + +static int lp5860_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + struct lp5860 *lp5860; + unsigned int multi_leds; + + multi_leds = device_get_child_node_count(dev); + if (!multi_leds) { + dev_err(dev, "LEDs are not defined in Device Tree!"); + return -ENODEV; + } + + if (multi_leds > LP5860_MAX_LED) { + dev_err(dev, "Too many LEDs specified.\n"); + return -EINVAL; + } + + lp5860 = devm_kzalloc(dev, struct_size(lp5860, leds, multi_leds), + GFP_KERNEL); + if (!lp5860) + return -ENOMEM; + + lp5860->regmap = devm_regmap_init_spi(spi, &lp5860_regmap_config); + if (IS_ERR(lp5860->regmap)) + return dev_err_probe(&spi->dev, PTR_ERR(lp5860->regmap), + "Failed to initialise Regmap.\n"); + + lp5860->dev = dev; + mutex_init(&lp5860->lock); + + spi_set_drvdata(spi, lp5860); + + return lp5860_device_init(dev); +} + +static void lp5860_remove(struct spi_device *spi) +{ + struct lp5860 *lp5860 = spi_get_drvdata(spi); + + mutex_destroy(&lp5860->lock); + + lp5860_device_remove(&spi->dev); +} + +static const struct of_device_id lp5860_of_match[] = { + { .compatible = "ti,lp5860" }, + {} +}; +MODULE_DEVICE_TABLE(of, lp5860_of_match); + +static struct spi_driver lp5860_driver = { + .driver = { + .name = "lp5860-spi", + .of_match_table = lp5860_of_match, + }, + .probe = lp5860_probe, + .remove = lp5860_remove, +}; +module_spi_driver(lp5860_driver); + +MODULE_AUTHOR("Steffen Trumtrar "); +MODULE_DESCRIPTION("TI LP5860 RGB LED SPI driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/leds/rgb/leds-lp5860.h b/drivers/leds/rgb/leds-lp5860.h new file mode 100644 index 000000000000..940be0c6e8da --- /dev/null +++ b/drivers/leds/rgb/leds-lp5860.h @@ -0,0 +1,268 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2025 Pengutronix + * + * Author: Steffen Trumtrar + */ + +#ifndef _DRIVERS_LEDS_RGB_LP5860_H +#define _DRIVERS_LEDS_RGB_LP5860_H + +#include +#include + +#define LP5860_REG_CHIP_EN 0x00 +#define LP5860_REG_DEV_INITIAL 0x01 +#define LP5860_REG_DEV_CONFIG1 0x02 +#define LP5860_REG_DEV_CONFIG2 0x03 +#define LP5860_REG_DEV_CONFIG3 0x04 +#define LP5860_REG_GLOBAL_BRI 0x05 +#define LP5860_REG_GROUP0_BRI 0x06 +#define LP5860_REG_GROUP1_BRI 0x07 +#define LP5860_REG_GROUP2_BRI 0x08 +#define LP5860_REG_R_CURRENT_SET 0x09 +#define LP5860_REG_G_CURRENT_SET 0x0A +#define LP5860_REG_B_CURRENT_SET 0x0B +#define LP5860_REG_GRP_SEL_START 0x0C +#define LP5860_REG_DOT_ONOFF_START 0x43 +#define LP5860_REG_DOT_ONOFF_MAX 0x63 +#define LP5860_REG_FAULT_STATE 0x64 +#define LP5860_REG_DOT_LOD_START 0x65 +#define LP5860_REG_DOT_LSD_START 0x86 +#define LP5860_REG_LOD_CLEAR 0xA7 +#define LP5860_REG_LSD_CLEAR 0xA8 +#define LP5860_REG_RESET 0xA9 +#define LP5860_REG_DC_START 0x0100 +#define LP5860_REG_PWM_BRI_START 0x0200 +#define LP5860_MAX_REG 0x038B + +/* Register chip_enable value */ +#define LP5860_CHIP_SHIFT 0 +#define LP5860_CHIP_MASK BIT(0) +#define LP5860_CHIP_DISABLE false +#define LP5860_CHIP_ENABLE true + +/* Register dev_initial value */ +#define LP5860_MAX_LINE_SHIFT 3 +#define LP5860_MAX_LINE_MASK GENMASK(6, 3) +#define LP5860_MAX_LINE_11 0x0B +#define LP5860_MAX_LINE_10 0x0A +#define LP5860_MAX_LINE_9 0x09 +#define LP5860_MAX_LINE_8 0x08 +#define LP5860_MAX_LINE_7 0x07 +#define LP5860_MAX_LINE_6 0x06 +#define LP5860_MAX_LINE_5 0x05 +#define LP5860_MAX_LINE_4 0x04 +#define LP5860_MAX_LINE_3 0x03 +#define LP5860_MAX_LINE_2 0x02 +#define LP5860_MAX_LINE_1 0x01 + +#define LP5860_MODE_SHIFT 1 +#define LP5860_MODE_MASK GENMASK(2, 1) +#define LP5860_MODE_3_1 0x03 +#define LP5860_MODE_3 0x02 +#define LP5860_MODE_2 0x01 +#define LP5860_MODE_1 0x00 + +#define LP5860_PWM_FREQUENCY_SHIFT 0 +#define LP5860_PWM_FREQUENCY_MASK BIT(0) +#define LP5860_PWM_FREQUENCY_62_5K 0x01 +#define LP5860_PWM_FREQUENCY_125K 0x00 + +/* Register dev_config1 value */ +#define LP5860_SW_BLK_SHIFT 3 +#define LP5860_SW_BLK_MASK BIT(3) +#define LP5860_SW_BLK_05US 0x01 +#define LP5860_SW_BLK_1US 0x00 + +#define LP5860_PWM_SCALE_MODE_SHIFT 2 +#define LP5860_PWM_SCALE_MODE_MASK BIT(2) +#define LP5860_PWM_SCALE_EXPONENTIAL 0x01 +#define LP5860_PWM_SCALE_LINEAR 0x00 + +#define LP5860_PWM_PHASESHIFT_SHIFT 1 +#define LP5860_PWM_PHASESHIFT_MASK BIT(1) +#define LP5860_PWM_PHASESHIFT_ON 0x01 +#define LP5860_PWM_PHASESHIFT_OFF 0x00 + +#define LP5860_CS_ON_SHIFT_SHIFT 0 +#define LP5860_CS_ON_SHIFT_MASK BIT(0) +#define LP5860_CS_DELAY_ON 0x01 +#define LP5860_CS_DELAY_OFF 0x00 + +/* Register dev_config2 value */ +#define LP5860_COMP_GROUP3_SHIFT 6 +#define LP5860_COMP_GROUP3_MASK GENMASK(7, 6) +#define LP5860_COMP_GROUP3_3CLOCK 0x03 +#define LP5860_COMP_GROUP3_2CLOCK 0x02 +#define LP5860_COMP_GROUP3_1CLOCK 0x01 +#define LP5860_COMP_GROUP3_OFF 0x00 + +#define LP5860_COMP_GROUP2_SHIFT 4 +#define LP5860_COMP_GROUP2_MASK GENMASK(5, 4) +#define LP5860_COMP_GROUP2_3CLOCK 0x03 +#define LP5860_COMP_GROUP2_2CLOCK 0x02 +#define LP5860_COMP_GROUP2_1CLOCK 0x01 +#define LP5860_COMP_GROUP2_OFF 0x00 + +#define LP5860_COMP_GROUP1_SHIFT 2 +#define LP5860_COMP_GROUP1_MASK GENMASK(3, 2) +#define LP5860_COMP_GROUP1_3CLOCK 0x03 +#define LP5860_COMP_GROUP1_2CLOCK 0x02 +#define LP5860_COMP_GROUP1_1CLOCK 0x01 +#define LP5860_COMP_GROUP1_OFF 0x00 + +#define LP5860_LOD_REMOVAL_SHIFT 1 +#define LP5860_LOD_REMOVAL_MASK BIT(1) +#define LP5860_LOD_REMOVAL_EN 0x01 +#define LP5860_LOD_REMOVAL_OFF 0x00 + +#define LP5860_LSD_REMOVAL_SHIFT 0 +#define LP5860_LSD_REMOVAL_MASK BIT(0) +#define LP5860_LSD_REMOVAL_EN 0x01 +#define LP5860_LSD_REMOVAL_OFF 0x00 + +/* Register dev_config3 value */ +#define LP5860_DOWN_DEGHOST_SHIFT 6 +#define LP5860_DOWN_DEGHOST_MASK GENMASK(7, 6) +#define LP5860_DOWN_DEGHOST_STRONG 0x03 +#define LP5860_DOWN_DEGHOST_MEDIUM 0x02 +#define LP5860_DOWN_DEGHOST_WEAK 0x01 +#define LP5860_DOWN_DEGHOST_OFF 0x00 + +#define LP5860_UP_DEGHOST_SHIFT 4 +#define LP5860_UP_DEGHOST_MASK GENMASK(5, 4) +#define LP5860_UP_DEGHOST_GND 0x03 +#define LP5860_UP_DEGHOST_3 0x02 +#define LP5860_UP_DEGHOST_2_5 0x01 +#define LP5860_UP_DEGHOST_2 0x00 + +#define LP5860_MAXIMUM_CURRENT_SHIFT 1 +#define LP5860_MAXIMUM_CURRENT_MASK GENMASK(3, 1) +#define LP5860_MAXIMUM_CURRENT_50 0x07 +#define LP5860_MAXIMUM_CURRENT_40 0x06 +#define LP5860_MAXIMUM_CURRENT_30 0x05 +#define LP5860_MAXIMUM_CURRENT_20 0x04 +#define LP5860_MAXIMUM_CURRENT_15 0x03 +#define LP5860_MAXIMUM_CURRENT_10 0x02 +#define LP5860_MAXIMUM_CURRENT_5 0x01 +#define LP5860_MAXIMUM_CURRENT_3 0x00 + +#define LP5860_UP_DEGHOST_ENABLE_SHIFT 0 +#define LP5860_UP_DEGHOST_ENABLE_MASK BIT(0) +#define LP5860_UP_DEGHOST_ENABLE_EN 0x01 +#define LP5860_UP_DEGHOST_ENABLE_OFF 0x00 + +/* Register PWM */ +#define LP5860_PWM_GLOBAL_MAX 0xff +#define LP5860_PWM_GROUP_MAX 0xff + +/* Register CC group select */ +#define LP5860_CC_GROUP_MASK GENMASK(7, 0) +#define LP5860_CC_GROUP_MAX 0x7F + +/* Register dot group select */ +#define LP5860_DOT_0_SHIFT 0 +#define LP5860_DOT_1_SHIFT 2 +#define LP5860_DOT_2_SHIFT 4 +#define LP5860_DOT_3_SHIFT 6 + +#define LP5860_DOT_GROUP3 0x03 +#define LP5860_DOT_GROUP2 0x02 +#define LP5860_DOT_GROUP1 0x01 +#define LP5860_DOT_GROUP_NONE 0x00 + +#define LP5860_DOT_ALL_ON 0xff +#define LP5860_DOT_ALL_OFF 0x0 +#define LP5860_PWM_DOT_MAX 0xff +/* Dot onoff value */ +#define LP5860_DOT_CS0_SHIFT 0 +#define LP5860_DOT_CS1_SHIFT 1 +#define LP5860_DOT_CS2_SHIFT 2 +#define LP5860_DOT_CS3_SHIFT 3 +#define LP5860_DOT_CS4_SHIFT 4 +#define LP5860_DOT_CS5_SHIFT 5 +#define LP5860_DOT_CS6_SHIFT 6 +#define LP5860_DOT_CS7_SHIFT 7 + +#define LP5860_DOT_CS_ON 0x01 +#define LP5860_DOT_CS_OFF 0x00 + +/* Dot lod value */ +#define LP5860_DOT_LOD0_SHIFT 0 +#define LP5860_DOT_LOD1_SHIFT 1 +#define LP5860_DOT_LOD2_SHIFT 2 +#define LP5860_DOT_LOD3_SHIFT 3 +#define LP5860_DOT_LOD4_SHIFT 4 +#define LP5860_DOT_LOD5_SHIFT 5 +#define LP5860_DOT_LOD6_SHIFT 6 +#define LP5860_DOT_LOD7_SHIFT 7 + +#define LP5860_DOT_LOD_ON 0x01 +#define LP5860_DOT_LOD_OFF 0x00 + +/* dot lsd value */ +#define LP5860_DOT_LSD0_SHIFT 0 +#define LP5860_DOT_LSD1_SHIFT 1 +#define LP5860_DOT_LSD2_SHIFT 2 +#define LP5860_DOT_LSD3_SHIFT 3 +#define LP5860_DOT_LSD4_SHIFT 4 +#define LP5860_DOT_LSD5_SHIFT 5 +#define LP5860_DOT_LSD6_SHIFT 6 +#define LP5860_DOT_LSD7_SHIFT 7 + +#define LP5860_DOT_LSD_ON 0x01 +#define LP5860_DOT_LSD_OFF 0x00 + +/* Register lod state */ +#define LP5860_GLOBAL_LOD_SHIFT 1 +#define LP5860_GLOBAL_LOD_STATE BIT(1) +#define LP5860_GLOBAL_LSD_SHIFT 0 +#define LP5860_GLOBAL_LSD_STATE BIT(0) + +#define LP5860_FAULT_STATE_ON 0x01 +#define LP5860_FAULT_STATE_OFF 0x00 + +#define LP5860_GLOBAL_LOD_CLEAR 0x00 +#define LP5860_GLOBAL_LSD_CLEAR 0x00 + + +#define LP5860_LOD_CLEAR_EN 0xff +#define LP5860_LSD_CLEAR_EN 0xff +#define LP5860_RESET_EN 0xff + +#define LP5860_MAX_BRIGHTNESS 255 +#define LP5860_REG_R_PWM 0x0 +#define LP5860_REG_G_PWM 0x1 +#define LP5860_REG_B_PWM 0x2 + +#define LP5860_MAX_LED_CONSTANT 18 +#define LP5860_MAX_LED_SCAN 11 +#define LP5860_MAX_LED (LP5860_MAX_LED_CONSTANT * LP5860_MAX_LED_SCAN) + +#define LP5860_MAX_DOT_ONOFF_GROUP_NUM 8 + +/* + * Theoretically, there is no max channel per LED, + * limit this to a reasonable value for RGBW LEDs + */ +#define LP5860_MAX_LED_CHANNELS 4 + +struct lp5860_led { + struct lp5860 *chip; + struct led_classdev_mc mc_cdev; + u8 brightness; +}; + +struct lp5860 { + struct device *dev; + struct regmap *regmap; + struct mutex lock; + + DECLARE_FLEX_ARRAY(struct lp5860_led, leds); +}; + +int lp5860_device_init(struct device *dev); +void lp5860_device_remove(struct device *dev); + +#endif /* _DRIVERS_LEDS_RGB_LP5860_H */