Message ID | 20190313123249.17258-1-dmurphy@ti.com |
---|---|
State | Not Applicable, archived |
Headers | show |
Series | [v4,1/4] dt: lm3532: Add lm3532 dt doc and update ti_lmu doc | expand |
Context | Check | Description |
---|---|---|
robh/checkpatch | warning | "total: 0 errors, 1 warnings, 145 lines checked" |
Hi! Could you maybe slow down a bit. "Hey, I should review this". "Hmm, there are two new versions in the mailbox in the meantime, good thing I waited." > + ramp-up-ms = <1024>; > + ramp-down-ms = <65536>; This is wrong. > + led@1 { > + reg = <1>; > + led-sources = <1>; > + ti,led-mode = <0>; > + label = "keypad"; "platform::kbd_backlight" Pavel
Hello On 3/14/19 4:39 AM, Pavel Machek wrote: > Hi! > > Could you maybe slow down a bit. > > "Hey, I should review this". "Hmm, there are two new versions in the > mailbox in the meantime, good thing I waited." > Yes. I am going to hold off on v5 until the code gets reviewed. >> + ramp-up-ms = <1024>; >> + ramp-down-ms = <65536>; > > This is wrong. > Ack. Changed in it in the example I deleted. >> + led@1 { >> + reg = <1>; >> + led-sources = <1>; >> + ti,led-mode = <0>; >> + label = "keypad"; > > "platform::kbd_backlight" Being an example does it have to match DT contents? I can change it since I have to touch the file anyway. Dan > Pavel >
On Wed, Mar 13, 2019 at 07:32:46AM -0500, Dan Murphy wrote: > Add the lm3532 device tree documentation. > Remove lm3532 device tree reference from the ti_lmu devicetree > documentation. > > With the addition of the dedicated lm3532 documentation the device > can be removed from the ti_lmu.txt. > > The reason for this is that the lm3532 dt documentation now defines > the ability to control LED output strings against different control > banks or groups multiple strings to be controlled by a single control > bank. > > Another addition was for ALS lighting control and configuration. The > LM3532 has a feature that can take in the ALS reading from 2 separate > ALS devices and adjust the brightness on the strings that are configured > to support this feature. > > Finally the device specific properties were moved to the parent node as these > properties are not control bank configurable. These include the runtime ramp > and the ALS configuration. > > Signed-off-by: Dan Murphy <dmurphy@ti.com> > --- > > v4 - Appended "ti," to TI specific properties, add enable gpio documentation, > removed an example, moved ramp to optional parent properties - https://lore.kernel.org/patchwork/patch/1050122/ > > v3 - No changes - https://lore.kernel.org/patchwork/patch/1049026/ > v2 - Fixed ramp-up and ramp-down properties, removed hard coded property values, > added ranges for variable properties, I did not change the label - https://lore.kernel.org/patchwork/patch/1048805/ > > > .../devicetree/bindings/leds/leds-lm3532.txt | 101 ++++++++++++++++++ > .../devicetree/bindings/mfd/ti-lmu.txt | 20 ---- > 2 files changed, 101 insertions(+), 20 deletions(-) > create mode 100644 Documentation/devicetree/bindings/leds/leds-lm3532.txt > > diff --git a/Documentation/devicetree/bindings/leds/leds-lm3532.txt b/Documentation/devicetree/bindings/leds/leds-lm3532.txt > new file mode 100644 > index 000000000000..ba793ef9b3b6 > --- /dev/null > +++ b/Documentation/devicetree/bindings/leds/leds-lm3532.txt > @@ -0,0 +1,101 @@ > +* Texas Instruments - lm3532 White LED driver with ambient light sensing > +capability. > + > +The LM3532 provides the 3 high-voltage, low-side current sinks. The device is > +programmable over an I2C-compatible interface and has independent > +current control for all three channels. The adaptive current regulation > +method allows for different LED currents in each current sink thus allowing > +for a wide variety of backlight and keypad applications. > + > +The main features of the LM3532 include dual ambient light sensor inputs > +each with 32 internal voltage setting resistors, 8-bit logarithmic and linear > +brightness control, dual external PWM brightness control inputs, and up to > +1000:1 dimming ratio with programmable fade in and fade out settings. > + > +Required properties: > + - compatible : "ti,lm3532" > + - reg : I2C slave address > + - #address-cells : 1 > + - #size-cells : 0 > + > +Optional properties: > + - enable-gpios : gpio pin to enable (active high)/disable the device. > + Range for ramp settings: 8us - 65536us This should be after the 2 ramp properties. With that, Reviewed-by: Rob Herring <robh@kernel.org> > + - ramp-up-us - The Run time ramp rates/step are from one current > + set-point to another after the device has reached its > + initial target set point from turn-on > + - ramp-down-us - The Run time ramp rates/step are from one current > + set-point to another after the device has reached its > + initial target set point from turn-on
Hello On 3/13/19 7:32 AM, Dan Murphy wrote: > Introduce the Texas Instruments LM3532 White LED driver. > The driver supports ALS configurability or manual brightness > control. > > The driver also supports associating LED strings with specific > control banks in a group or as individually controlled strings. > I have a v5 series ready for posting and only the DT was reviewed. Do we have any comments on the code? Dan > Signed-off-by: Dan Murphy <dmurphy@ti.com> > --- > > v4 - Updated code to DT doc changes appending "ti," to TI specific properties - > https://lore.kernel.org/patchwork/patch/1050124/ > > v3 - Removed switch case for register setting in favor of an algorithim (v1 comment), > https://lore.kernel.org/patchwork/patch/1049024/ > v2 - Added look up tables for als_avg, als_imp and ramp times, fixed brightness > control when ALS is in control, added property value checks to ensure the values > are not out of bounds - https://lore.kernel.org/patchwork/patch/1048807/ > > drivers/leds/Kconfig | 10 + > drivers/leds/Makefile | 1 + > drivers/leds/leds-lm3532.c | 681 +++++++++++++++++++++++++++++++++++++ > 3 files changed, 692 insertions(+) > create mode 100644 drivers/leds/leds-lm3532.c > > diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig > index a72f97fca57b..da00b9ed5a5c 100644 > --- a/drivers/leds/Kconfig > +++ b/drivers/leds/Kconfig > @@ -138,6 +138,16 @@ config LEDS_LM3530 > controlled manually or using PWM input or using ambient > light automatically. > > +config LEDS_LM3532 > + tristate "LCD Backlight driver for LM3532" > + depends on LEDS_CLASS > + depends on I2C > + help > + This option enables support for the LCD backlight using > + LM3532 ambient light sensor chip. This ALS chip can be > + controlled manually or using PWM input or using ambient > + light automatically. > + > config LEDS_LM3533 > tristate "LED support for LM3533" > depends on LEDS_CLASS > diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile > index 4c1b0054f379..7a8b1f55d459 100644 > --- a/drivers/leds/Makefile > +++ b/drivers/leds/Makefile > @@ -18,6 +18,7 @@ obj-$(CONFIG_LEDS_BD2802) += leds-bd2802.o > obj-$(CONFIG_LEDS_CPCAP) += leds-cpcap.o > obj-$(CONFIG_LEDS_LOCOMO) += leds-locomo.o > obj-$(CONFIG_LEDS_LM3530) += leds-lm3530.o > +obj-$(CONFIG_LEDS_LM3532) += leds-lm3532.o > obj-$(CONFIG_LEDS_LM3533) += leds-lm3533.o > obj-$(CONFIG_LEDS_LM3642) += leds-lm3642.o > obj-$(CONFIG_LEDS_MIKROTIK_RB532) += leds-rb532.o > diff --git a/drivers/leds/leds-lm3532.c b/drivers/leds/leds-lm3532.c > new file mode 100644 > index 000000000000..e8a614d71469 > --- /dev/null > +++ b/drivers/leds/leds-lm3532.c > @@ -0,0 +1,681 @@ > +// SPDX-License-Identifier: GPL-2.0 > +// TI LM3532 LED driver > +// Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/ > + > +#include <linux/i2c.h> > +#include <linux/leds.h> > +#include <linux/slab.h> > +#include <linux/regmap.h> > +#include <linux/types.h> > +#include <linux/regulator/consumer.h> > +#include <linux/module.h> > +#include <uapi/linux/uleds.h> > +#include <linux/gpio/consumer.h> > + > +#define LM3532_NAME "lm3532-led" > +#define LM3532_BL_MODE_MANUAL 0x00 > +#define LM3532_BL_MODE_ALS 0x01 > + > +#define LM3532_REG_OUTPUT_CFG 0x10 > +#define LM3532_REG_STARTSHUT_RAMP 0x11 > +#define LM3532_REG_RT_RAMP 0x12 > +#define LM3532_REG_PWM_A_CFG 0x13 > +#define LM3532_REG_PWM_B_CFG 0x14 > +#define LM3532_REG_PWM_C_CFG 0x15 > +#define LM3532_REG_ZONE_CFG_A 0x16 > +#define LM3532_REG_CTRL_A_BRT 0x17 > +#define LM3532_REG_ZONE_CFG_B 0x18 > +#define LM3532_REG_CTRL_B_BRT 0x19 > +#define LM3532_REG_ZONE_CFG_C 0x1a > +#define LM3532_REG_CTRL_C_BRT 0x1b > +#define LM3532_REG_ENABLE 0x1d > +#define LM3532_ALS_CONFIG 0x23 > +#define LM3532_REG_ZN_0_HI 0x60 > +#define LM3532_REG_ZN_0_LO 0x61 > +#define LM3532_REG_ZN_1_HI 0x62 > +#define LM3532_REG_ZN_1_LO 0x63 > +#define LM3532_REG_ZN_2_HI 0x64 > +#define LM3532_REG_ZN_2_LO 0x65 > +#define LM3532_REG_ZN_3_HI 0x66 > +#define LM3532_REG_ZN_3_LO 0x67 > +#define LM3532_REG_MAX 0x7e > + > +/* Contorl Enable */ > +#define LM3532_CTRL_A_ENABLE BIT(0) > +#define LM3532_CTRL_B_ENABLE BIT(1) > +#define LM3532_CTRL_C_ENABLE BIT(2) > + > +/* PWM Zone Control */ > +#define LM3532_PWM_ZONE_MASK 0x7c > +#define LM3532_PWM_ZONE_0_EN BIT(2) > +#define LM3532_PWM_ZONE_1_EN BIT(3) > +#define LM3532_PWM_ZONE_2_EN BIT(4) > +#define LM3532_PWM_ZONE_3_EN BIT(5) > +#define LM3532_PWM_ZONE_4_EN BIT(6) > + > +/* Brightness Configuration */ > +#define LM3532_I2C_CTRL BIT(0) > +#define LM3532_ALS_CTRL 0 > +#define LM3532_LINEAR_MAP BIT(1) > +#define LM3532_ZONE_MASK (BIT(2) | BIT(3) | BIT(4)) > +#define LM3532_ZONE_0 0 > +#define LM3532_ZONE_1 BIT(2) > +#define LM3532_ZONE_2 BIT(3) > +#define LM3532_ZONE_3 (BIT(2) | BIT(3)) > +#define LM3532_ZONE_4 BIT(4) > + > +#define LM3532_ENABLE_ALS BIT(3) > +#define LM3532_ALS_SEL_SHIFT 6 > + > +/* Zone Boundary Register */ > +#define LM3532_ALS_WINDOW_mV 2000 > +#define LM3532_ALS_ZB_MAX 4 > +#define LM3532_ALS_OFFSET_mV 2 > + > +#define LM3532_CONTROL_A 0 > +#define LM3532_CONTROL_B 1 > +#define LM3532_CONTROL_C 2 > +#define LM3532_MAX_CONTROL_BANKS 3 > +#define LM3532_MAX_LED_STRINGS 3 > + > +#define LM3532_OUTPUT_CFG_MASK 0x3 > +#define LM3532_BRT_VAL_ADJUST 8 > +#define LM3532_RAMP_DOWN_SHIFT 3 > + > +#define LM3532_NUM_RAMP_VALS 8 > +#define LM3532_NUM_AVG_VALS 8 > +#define LM3532_NUM_IMP_VALS 32 > + > +/* > + * struct lm3532_als_data > + * @config - value of ALS configuration register > + * @als1_imp_sel - value of ALS1 resistor select register > + * @als2_imp_sel - value of ALS2 resistor select register > + * @als_avrg_time - ALS averaging time > + * @als_input_mode - ALS input mode for brightness control > + * @als_vmin - Minimum ALS voltage > + * @als_vmax - Maximum ALS voltage > + * @zone_lo - values of ALS lo ZB(Zone Boundary) registers > + * @zone_hi - values of ALS hi ZB(Zone Boundary) registers > + */ > +struct lm3532_als_data { > + u8 config; > + u8 als1_imp_sel; > + u8 als2_imp_sel; > + u8 als_avrg_time; > + u8 als_input_mode; > + u32 als_vmin; > + u32 als_vmax; > + u8 zones_lo[LM3532_ALS_ZB_MAX]; > + u8 zones_hi[LM3532_ALS_ZB_MAX]; > +}; > + > +/** > + * struct lm3532_led > + * @led_dev: led class device > + * @priv - Pointer the device data structure > + * @control_bank - Control bank the LED is associated to > + * @mode - Mode of the LED string > + * @num_leds - Number of LED strings are supported in this array > + * @led_strings - The LED strings supported in this array > + * @label - LED label > + */ > +struct lm3532_led { > + struct led_classdev led_dev; > + struct lm3532_data *priv; > + > + int control_bank; > + int mode; > + int num_leds; > + u32 led_strings[LM3532_MAX_CONTROL_BANKS]; > + char label[LED_MAX_NAME_SIZE]; > +}; > + > +/** > + * struct lm3532_data > + * @enable_gpio - Hardware enable gpio > + * @regulator: regulator > + * @client: i2c client > + * @regmap - Devices register map > + * @dev - Pointer to the devices device struct > + * @lock - Lock for reading/writing the device > + * @als_data - Pointer to the als data struct > + * @runtime_ramp_up - Runtime ramp up setting > + * @runtime_ramp_down - Runtime ramp down setting > + * @leds - Array of LED strings > + */ > +struct lm3532_data { > + struct gpio_desc *enable_gpio; > + struct regulator *regulator; > + struct i2c_client *client; > + struct regmap *regmap; > + struct device *dev; > + struct mutex lock; > + > + struct lm3532_als_data *als_data; > + > + u32 runtime_ramp_up; > + u32 runtime_ramp_down; > + > + struct lm3532_led leds[]; > +}; > + > +static const struct reg_default lm3532_reg_defs[] = { > + {LM3532_REG_OUTPUT_CFG, 0xe4}, > + {LM3532_REG_STARTSHUT_RAMP, 0xc0}, > + {LM3532_REG_RT_RAMP, 0xc0}, > + {LM3532_REG_PWM_A_CFG, 0x82}, > + {LM3532_REG_PWM_B_CFG, 0x82}, > + {LM3532_REG_PWM_C_CFG, 0x82}, > + {LM3532_REG_ZONE_CFG_A, 0xf1}, > + {LM3532_REG_CTRL_A_BRT, 0xf3}, > + {LM3532_REG_ZONE_CFG_B, 0xf1}, > + {LM3532_REG_CTRL_B_BRT, 0xf3}, > + {LM3532_REG_ZONE_CFG_C, 0xf1}, > + {LM3532_REG_CTRL_C_BRT, 0xf3}, > + {LM3532_REG_ENABLE, 0xf8}, > + {LM3532_ALS_CONFIG, 0x44}, > + {LM3532_REG_ZN_0_HI, 0x35}, > + {LM3532_REG_ZN_0_LO, 0x33}, > + {LM3532_REG_ZN_1_HI, 0x6a}, > + {LM3532_REG_ZN_1_LO, 0x66}, > + {LM3532_REG_ZN_2_HI, 0xa1}, > + {LM3532_REG_ZN_2_LO, 0x99}, > + {LM3532_REG_ZN_3_HI, 0xdc}, > + {LM3532_REG_ZN_3_LO, 0xcc}, > +}; > + > +static const struct regmap_config lm3532_regmap_config = { > + .reg_bits = 8, > + .val_bits = 8, > + > + .max_register = LM3532_REG_MAX, > + .reg_defaults = lm3532_reg_defs, > + .num_reg_defaults = ARRAY_SIZE(lm3532_reg_defs), > + .cache_type = REGCACHE_FLAT, > +}; > + > +const static int als_imp_table[LM3532_NUM_IMP_VALS] = {37000, 18500, 12330, > + 92500, 7400, 6170, 5290, > + 4630, 4110, 3700, 3360, > + 3080, 2850, 2640, 2440, > + 2310, 2180, 2060, 1950, > + 1850, 1760, 1680, 1610, > + 1540, 1480, 1420, 1370, > + 1320, 1280, 1230, 1190}; > +static int lm3532_get_als_imp_index(int als_imped) > +{ > + int i; > + > + if (als_imped > als_imp_table[1]) > + return 0; > + > + if (als_imped < als_imp_table[LM3532_NUM_IMP_VALS - 1]) > + return LM3532_NUM_IMP_VALS - 1; > + > + for (i = 1; i < LM3532_NUM_IMP_VALS; i++) { > + if (als_imped == als_imp_table[i]) > + return i; > + > + /* Find an approximate index by looking up the table */ > + if (als_imped < als_imp_table[i - 1] && > + als_imped > als_imp_table[i]) { > + if (als_imped - als_imp_table[i - 1] < > + als_imp_table[i] - als_imped) > + return i + 1; > + else > + return i; > + } > + } > + > + return -EINVAL; > +} > + > +static int lm3532_get_index(const int table[], int size, int value) > +{ > + int i; > + > + for (i = 1; i < size; i++) { > + if (value == table[i]) > + return i; > + > + /* Find an approximate index by looking up the table */ > + if (value > table[i - 1] && > + value < table[i]) { > + if (value - table[i - 1] < table[i] - value) > + return i - 1; > + else > + return i; > + } > + } > + > + return -EINVAL; > +} > + > +const static int als_avrg_table[LM3532_NUM_AVG_VALS] = {17920, 35840, 71680, > + 1433360, 286720, 573440, > + 1146880, 2293760}; > +static int lm3532_get_als_avg_index(int avg_time) > +{ > + if (avg_time <= als_avrg_table[0]) > + return 0; > + > + if (avg_time > als_avrg_table[LM3532_NUM_AVG_VALS - 1]) > + return LM3532_NUM_AVG_VALS - 1; > + > + return lm3532_get_index(&als_avrg_table[0], LM3532_NUM_AVG_VALS, > + avg_time); > +} > + > +const static int ramp_table[LM3532_NUM_RAMP_VALS] = { 8, 1024, 2048, 4096, 8192, > + 16384, 32768, 65536}; > +static int lm3532_get_ramp_index(int ramp_time) > +{ > + if (ramp_time <= ramp_table[0]) > + return 0; > + > + if (ramp_time > ramp_table[LM3532_NUM_RAMP_VALS - 1]) > + return LM3532_NUM_RAMP_VALS - 1; > + > + return lm3532_get_index(&ramp_table[0], LM3532_NUM_RAMP_VALS, > + ramp_time); > +} > + > +static int lm3532_led_enable(struct lm3532_led *led_data) > +{ > + int ctrl_en_val = BIT(led_data->control_bank); > + int ret; > + > + ret = regmap_update_bits(led_data->priv->regmap, LM3532_REG_ENABLE, > + ctrl_en_val, ctrl_en_val); > + if (ret) { > + dev_err(led_data->priv->dev, "Failed to set ctrl:%d\n", ret); > + return ret; > + } > + > + return regulator_enable(led_data->priv->regulator); > +} > + > +static int lm3532_led_disable(struct lm3532_led *led_data) > +{ > + int ctrl_en_val = BIT(led_data->control_bank); > + int ret; > + > + ret = regmap_update_bits(led_data->priv->regmap, LM3532_REG_ENABLE, > + ctrl_en_val, ~ctrl_en_val); > + if (ret) { > + dev_err(led_data->priv->dev, "Failed to set ctrl:%d\n", ret); > + return ret; > + } > + > + return regulator_disable(led_data->priv->regulator); > +} > + > +static int lm3532_brightness_set(struct led_classdev *led_cdev, > + enum led_brightness brt_val) > +{ > + struct lm3532_led *led = > + container_of(led_cdev, struct lm3532_led, led_dev); > + u8 brightness_reg; > + int ret; > + > + mutex_lock(&led->priv->lock); > + > + if (led->mode == LM3532_BL_MODE_ALS) { > + if (brt_val > LED_OFF) > + ret = lm3532_led_enable(led); > + else > + ret = lm3532_led_disable(led); > + > + goto unlock; > + } > + > + if (brt_val == LED_OFF) { > + ret = lm3532_led_disable(led); > + goto unlock; > + } > + > + ret = lm3532_led_enable(led); > + if (ret) > + goto unlock; > + > + brightness_reg = LM3532_REG_CTRL_A_BRT + led->control_bank * 2; > + brt_val = brt_val / LM3532_BRT_VAL_ADJUST; > + > + ret = regmap_write(led->priv->regmap, brightness_reg, brt_val); > + > +unlock: > + mutex_unlock(&led->priv->lock); > + return ret; > +} > + > +static int lm3532_init_registers(struct lm3532_led *led) > +{ > + struct lm3532_data *drvdata = led->priv; > + unsigned int runtime_ramp_val; > + unsigned int output_cfg_val = 0; > + unsigned int output_cfg_shift = 0; > + unsigned int output_cfg_mask = 0; > + int ret, i; > + > + for (i = 0; i < led->num_leds; i++) { > + output_cfg_shift = led->led_strings[i] * 2; > + output_cfg_val |= (led->control_bank << output_cfg_shift); > + output_cfg_mask |= LM3532_OUTPUT_CFG_MASK << output_cfg_shift; > + } > + > + ret = regmap_update_bits(drvdata->regmap, LM3532_REG_OUTPUT_CFG, > + output_cfg_mask, output_cfg_val); > + if (ret) > + return ret; > + > + runtime_ramp_val = drvdata->runtime_ramp_up | > + (drvdata->runtime_ramp_down << LM3532_RAMP_DOWN_SHIFT); > + > + return regmap_write(drvdata->regmap, LM3532_REG_RT_RAMP, > + runtime_ramp_val); > +} > + > +static int lm3532_als_configure(struct lm3532_data *priv, > + struct lm3532_led *led) > +{ > + struct lm3532_als_data *als = priv->als_data; > + u32 als_vmin, als_vmax, als_vstep; > + int zone_reg = LM3532_REG_ZN_0_HI; > + int brightnes_config_reg; > + int ret; > + int i; > + > + als_vmin = als->als_vmin; > + als_vmax = als->als_vmax; > + > + als_vstep = (als_vmax - als_vmin) / ((LM3532_ALS_ZB_MAX + 1) * 2); > + > + for (i = 0; i < LM3532_ALS_ZB_MAX; i++) { > + als->zones_lo[i] = ((als_vmin + als_vstep + (i * als_vstep)) * > + LED_FULL) / 1000; > + als->zones_hi[i] = ((als_vmin + LM3532_ALS_OFFSET_mV + > + als_vstep + (i * als_vstep)) * LED_FULL) / 1000; > + > + zone_reg = LM3532_REG_ZN_0_HI + i * 2; > + ret = regmap_write(priv->regmap, zone_reg, als->zones_lo[i]); > + if (ret) > + return ret; > + > + zone_reg += 1; > + ret = regmap_write(priv->regmap, zone_reg, als->zones_hi[i]); > + if (ret) > + return ret; > + } > + > + als->config = (als->als_avrg_time | (LM3532_ENABLE_ALS) | > + (als->als_input_mode << LM3532_ALS_SEL_SHIFT)); > + > + ret = regmap_write(priv->regmap, LM3532_ALS_CONFIG, als->config); > + if (ret) > + return ret; > + > + brightnes_config_reg = LM3532_REG_ZONE_CFG_A + led->control_bank * 2; > + > + return regmap_update_bits(priv->regmap, brightnes_config_reg, > + LM3532_I2C_CTRL, LM3532_ALS_CTRL); > +} > + > +static int lm3532_parse_als(struct lm3532_data *priv) > +{ > + struct lm3532_als_data *als; > + int als_avg_time; > + int als_impedance; > + int ret; > + > + als = devm_kzalloc(priv->dev, sizeof(*als), GFP_KERNEL); > + if (als == NULL) > + return -ENOMEM; > + > + ret = device_property_read_u32(&priv->client->dev, "ti,als-vmin", > + &als->als_vmin); > + if (ret) > + als->als_vmin = 0; > + > + ret = device_property_read_u32(&priv->client->dev, "ti,als-vmax", > + &als->als_vmax); > + if (ret) > + als->als_vmax = LM3532_ALS_WINDOW_mV; > + > + if (als->als_vmax > LM3532_ALS_WINDOW_mV) { > + ret = -EINVAL; > + return ret; > + } > + > + ret = device_property_read_u32(&priv->client->dev, "ti,als1-imp-sel", > + &als_impedance); > + if (ret) > + als->als1_imp_sel = 0; > + else > + als->als1_imp_sel = lm3532_get_als_imp_index(als_impedance); > + > + ret = device_property_read_u32(&priv->client->dev, "ti,als2-imp-sel", > + &als_impedance); > + if (ret) > + als->als2_imp_sel = 0; > + else > + als->als2_imp_sel = lm3532_get_als_imp_index(als_impedance); > + > + ret = device_property_read_u32(&priv->client->dev, "ti,als-avrg-time-us", > + &als_avg_time); > + if (ret) > + als->als_avrg_time = 0; > + else > + als->als_avrg_time = lm3532_get_als_avg_index(als_avg_time); > + > + ret = device_property_read_u8(&priv->client->dev, "ti,als-input-mode", > + &als->als_input_mode); > + if (ret) > + als->als_input_mode = 0; > + > + if (als->als_input_mode > LM3532_BL_MODE_ALS) { > + ret = -EINVAL; > + return ret; > + } > + > + priv->als_data = als; > + > + return ret; > +} > + > +static int lm3532_parse_node(struct lm3532_data *priv) > +{ > + struct fwnode_handle *child = NULL; > + struct lm3532_led *led; > + const char *name; > + int control_bank; > + u32 ramp_time; > + size_t i = 0; > + int ret; > + > + priv->enable_gpio = devm_gpiod_get_optional(&priv->client->dev, > + "enable", GPIOD_OUT_LOW); > + if (IS_ERR(priv->enable_gpio)) > + priv->enable_gpio = NULL; > + > + priv->regulator = devm_regulator_get(&priv->client->dev, "vin"); > + if (IS_ERR(priv->regulator)) > + priv->regulator = NULL; > + > + ret = device_property_read_u32(&priv->client->dev, "ramp-up-us", > + &ramp_time); > + if (ret) > + dev_info(&priv->client->dev, "ramp-up-ms property missing\n"); > + else > + priv->runtime_ramp_up = lm3532_get_ramp_index(ramp_time); > + > + ret = device_property_read_u32(&priv->client->dev, "ramp-down-us", > + &ramp_time); > + if (ret) > + dev_info(&priv->client->dev, "ramp-down-ms property missing\n"); > + else > + priv->runtime_ramp_down = lm3532_get_ramp_index(ramp_time); > + > + device_for_each_child_node(priv->dev, child) { > + led = &priv->leds[i]; > + > + ret = fwnode_property_read_u32(child, "reg", &control_bank); > + if (ret) { > + dev_err(&priv->client->dev, "reg property missing\n"); > + fwnode_handle_put(child); > + goto child_out; > + } > + > + if (control_bank > LM3532_CONTROL_C) { > + dev_err(&priv->client->dev, "Control bank invalid\n"); > + continue; > + } > + > + led->control_bank = control_bank; > + > + ret = fwnode_property_read_u32(child, "ti,led-mode", > + &led->mode); > + if (ret) { > + dev_err(&priv->client->dev, "ti,led-mode property missing\n"); > + fwnode_handle_put(child); > + goto child_out; > + } > + > + if (led->mode == LM3532_BL_MODE_ALS) { > + ret = lm3532_parse_als(priv); > + if (ret) > + dev_err(&priv->client->dev, "Failed to parse als\n"); > + else > + lm3532_als_configure(priv, led); > + } > + > + led->num_leds = fwnode_property_read_u32_array(child, > + "led-sources", > + NULL, 0); > + > + if (led->num_leds > LM3532_MAX_LED_STRINGS) { > + dev_err(&priv->client->dev, "To many LED string defined\n"); > + continue; > + } > + > + ret = fwnode_property_read_u32_array(child, "led-sources", > + led->led_strings, > + led->num_leds); > + if (ret) { > + dev_err(&priv->client->dev, "led-sources property missing\n"); > + fwnode_handle_put(child); > + goto child_out; > + } > + > + fwnode_property_read_string(child, "linux,default-trigger", > + &led->led_dev.default_trigger); > + > + ret = fwnode_property_read_string(child, "label", &name); > + if (ret) > + snprintf(led->label, sizeof(led->label), > + "%s::", priv->client->name); > + else > + snprintf(led->label, sizeof(led->label), > + "%s:%s", priv->client->name, name); > + > + led->priv = priv; > + led->led_dev.name = led->label; > + led->led_dev.brightness_set_blocking = lm3532_brightness_set; > + > + ret = devm_led_classdev_register(priv->dev, &led->led_dev); > + if (ret) { > + dev_err(&priv->client->dev, "led register err: %d\n", > + ret); > + fwnode_handle_put(child); > + goto child_out; > + } > + > + lm3532_init_registers(led); > + > + i++; > + } > + > +child_out: > + return ret; > +} > + > +static int lm3532_probe(struct i2c_client *client, > + const struct i2c_device_id *id) > +{ > + struct lm3532_data *drvdata; > + int ret = 0; > + int count; > + > + count = device_get_child_node_count(&client->dev); > + if (!count) { > + dev_err(&client->dev, "LEDs are not defined in device tree!"); > + return -ENODEV; > + } > + > + drvdata = devm_kzalloc(&client->dev, struct_size(drvdata, leds, count), > + GFP_KERNEL); > + if (drvdata == NULL) > + return -ENOMEM; > + > + drvdata->client = client; > + drvdata->dev = &client->dev; > + > + drvdata->regmap = devm_regmap_init_i2c(client, &lm3532_regmap_config); > + if (IS_ERR(drvdata->regmap)) { > + ret = PTR_ERR(drvdata->regmap); > + dev_err(&client->dev, "Failed to allocate register map: %d\n", > + ret); > + return ret; > + } > + > + mutex_init(&drvdata->lock); > + i2c_set_clientdata(client, drvdata); > + > + ret = lm3532_parse_node(drvdata); > + if (ret) { > + dev_err(&client->dev, "Failed to parse node\n"); > + return ret; > + } > + > + if (drvdata->enable_gpio) > + gpiod_direction_output(drvdata->enable_gpio, 1); > + > + return ret; > +} > + > +static int lm3532_remove(struct i2c_client *client) > +{ > + struct lm3532_data *drvdata = i2c_get_clientdata(client); > + > + if (drvdata->enable_gpio) > + gpiod_direction_output(drvdata->enable_gpio, 0); > + > + return 0; > +} > + > +static const struct of_device_id of_lm3532_leds_match[] = { > + { .compatible = "ti,lm3532", }, > + {}, > +}; > +MODULE_DEVICE_TABLE(of, of_lm3532_leds_match); > + > +static const struct i2c_device_id lm3532_id[] = { > + {LM3532_NAME, 0}, > + {} > +}; > +MODULE_DEVICE_TABLE(i2c, lm3532_id); > + > +static struct i2c_driver lm3532_i2c_driver = { > + .probe = lm3532_probe, > + .remove = lm3532_remove, > + .id_table = lm3532_id, > + .driver = { > + .name = LM3532_NAME, > + .of_match_table = of_lm3532_leds_match, > + }, > +}; > +module_i2c_driver(lm3532_i2c_driver); > + > +MODULE_DESCRIPTION("Back Light driver for LM3532"); > +MODULE_LICENSE("GPL v2"); > +MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>"); >
Hi Dan, I have one minor issue below. On 3/13/19 1:32 PM, Dan Murphy wrote: > Introduce the Texas Instruments LM3532 White LED driver. > The driver supports ALS configurability or manual brightness > control. > > The driver also supports associating LED strings with specific > control banks in a group or as individually controlled strings. > > Signed-off-by: Dan Murphy <dmurphy@ti.com> > --- > > v4 - Updated code to DT doc changes appending "ti," to TI specific properties - > https://lore.kernel.org/patchwork/patch/1050124/ > > v3 - Removed switch case for register setting in favor of an algorithim (v1 comment), > https://lore.kernel.org/patchwork/patch/1049024/ > v2 - Added look up tables for als_avg, als_imp and ramp times, fixed brightness > control when ALS is in control, added property value checks to ensure the values > are not out of bounds - https://lore.kernel.org/patchwork/patch/1048807/ > > drivers/leds/Kconfig | 10 + > drivers/leds/Makefile | 1 + > drivers/leds/leds-lm3532.c | 681 +++++++++++++++++++++++++++++++++++++ > 3 files changed, 692 insertions(+) > create mode 100644 drivers/leds/leds-lm3532.c > > diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig > index a72f97fca57b..da00b9ed5a5c 100644 > --- a/drivers/leds/Kconfig > +++ b/drivers/leds/Kconfig > @@ -138,6 +138,16 @@ config LEDS_LM3530 > controlled manually or using PWM input or using ambient > light automatically. > > +config LEDS_LM3532 > + tristate "LCD Backlight driver for LM3532" > + depends on LEDS_CLASS > + depends on I2C > + help > + This option enables support for the LCD backlight using > + LM3532 ambient light sensor chip. This ALS chip can be > + controlled manually or using PWM input or using ambient > + light automatically. > + > config LEDS_LM3533 > tristate "LED support for LM3533" > depends on LEDS_CLASS > diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile > index 4c1b0054f379..7a8b1f55d459 100644 > --- a/drivers/leds/Makefile > +++ b/drivers/leds/Makefile > @@ -18,6 +18,7 @@ obj-$(CONFIG_LEDS_BD2802) += leds-bd2802.o > obj-$(CONFIG_LEDS_CPCAP) += leds-cpcap.o > obj-$(CONFIG_LEDS_LOCOMO) += leds-locomo.o > obj-$(CONFIG_LEDS_LM3530) += leds-lm3530.o > +obj-$(CONFIG_LEDS_LM3532) += leds-lm3532.o > obj-$(CONFIG_LEDS_LM3533) += leds-lm3533.o > obj-$(CONFIG_LEDS_LM3642) += leds-lm3642.o > obj-$(CONFIG_LEDS_MIKROTIK_RB532) += leds-rb532.o > diff --git a/drivers/leds/leds-lm3532.c b/drivers/leds/leds-lm3532.c > new file mode 100644 > index 000000000000..e8a614d71469 > --- /dev/null > +++ b/drivers/leds/leds-lm3532.c > @@ -0,0 +1,681 @@ > +// SPDX-License-Identifier: GPL-2.0 > +// TI LM3532 LED driver > +// Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/ > + > +#include <linux/i2c.h> > +#include <linux/leds.h> > +#include <linux/slab.h> > +#include <linux/regmap.h> > +#include <linux/types.h> > +#include <linux/regulator/consumer.h> > +#include <linux/module.h> > +#include <uapi/linux/uleds.h> > +#include <linux/gpio/consumer.h> > + > +#define LM3532_NAME "lm3532-led" > +#define LM3532_BL_MODE_MANUAL 0x00 > +#define LM3532_BL_MODE_ALS 0x01 > + > +#define LM3532_REG_OUTPUT_CFG 0x10 > +#define LM3532_REG_STARTSHUT_RAMP 0x11 > +#define LM3532_REG_RT_RAMP 0x12 > +#define LM3532_REG_PWM_A_CFG 0x13 > +#define LM3532_REG_PWM_B_CFG 0x14 > +#define LM3532_REG_PWM_C_CFG 0x15 > +#define LM3532_REG_ZONE_CFG_A 0x16 > +#define LM3532_REG_CTRL_A_BRT 0x17 > +#define LM3532_REG_ZONE_CFG_B 0x18 > +#define LM3532_REG_CTRL_B_BRT 0x19 > +#define LM3532_REG_ZONE_CFG_C 0x1a > +#define LM3532_REG_CTRL_C_BRT 0x1b > +#define LM3532_REG_ENABLE 0x1d > +#define LM3532_ALS_CONFIG 0x23 > +#define LM3532_REG_ZN_0_HI 0x60 > +#define LM3532_REG_ZN_0_LO 0x61 > +#define LM3532_REG_ZN_1_HI 0x62 > +#define LM3532_REG_ZN_1_LO 0x63 > +#define LM3532_REG_ZN_2_HI 0x64 > +#define LM3532_REG_ZN_2_LO 0x65 > +#define LM3532_REG_ZN_3_HI 0x66 > +#define LM3532_REG_ZN_3_LO 0x67 > +#define LM3532_REG_MAX 0x7e > + > +/* Contorl Enable */ > +#define LM3532_CTRL_A_ENABLE BIT(0) > +#define LM3532_CTRL_B_ENABLE BIT(1) > +#define LM3532_CTRL_C_ENABLE BIT(2) > + > +/* PWM Zone Control */ > +#define LM3532_PWM_ZONE_MASK 0x7c > +#define LM3532_PWM_ZONE_0_EN BIT(2) > +#define LM3532_PWM_ZONE_1_EN BIT(3) > +#define LM3532_PWM_ZONE_2_EN BIT(4) > +#define LM3532_PWM_ZONE_3_EN BIT(5) > +#define LM3532_PWM_ZONE_4_EN BIT(6) > + > +/* Brightness Configuration */ > +#define LM3532_I2C_CTRL BIT(0) > +#define LM3532_ALS_CTRL 0 > +#define LM3532_LINEAR_MAP BIT(1) > +#define LM3532_ZONE_MASK (BIT(2) | BIT(3) | BIT(4)) > +#define LM3532_ZONE_0 0 > +#define LM3532_ZONE_1 BIT(2) > +#define LM3532_ZONE_2 BIT(3) > +#define LM3532_ZONE_3 (BIT(2) | BIT(3)) > +#define LM3532_ZONE_4 BIT(4) > + > +#define LM3532_ENABLE_ALS BIT(3) > +#define LM3532_ALS_SEL_SHIFT 6 > + > +/* Zone Boundary Register */ > +#define LM3532_ALS_WINDOW_mV 2000 > +#define LM3532_ALS_ZB_MAX 4 > +#define LM3532_ALS_OFFSET_mV 2 > + > +#define LM3532_CONTROL_A 0 > +#define LM3532_CONTROL_B 1 > +#define LM3532_CONTROL_C 2 > +#define LM3532_MAX_CONTROL_BANKS 3 > +#define LM3532_MAX_LED_STRINGS 3 > + > +#define LM3532_OUTPUT_CFG_MASK 0x3 > +#define LM3532_BRT_VAL_ADJUST 8 > +#define LM3532_RAMP_DOWN_SHIFT 3 > + > +#define LM3532_NUM_RAMP_VALS 8 > +#define LM3532_NUM_AVG_VALS 8 > +#define LM3532_NUM_IMP_VALS 32 > + > +/* > + * struct lm3532_als_data > + * @config - value of ALS configuration register > + * @als1_imp_sel - value of ALS1 resistor select register > + * @als2_imp_sel - value of ALS2 resistor select register > + * @als_avrg_time - ALS averaging time > + * @als_input_mode - ALS input mode for brightness control > + * @als_vmin - Minimum ALS voltage > + * @als_vmax - Maximum ALS voltage > + * @zone_lo - values of ALS lo ZB(Zone Boundary) registers > + * @zone_hi - values of ALS hi ZB(Zone Boundary) registers > + */ > +struct lm3532_als_data { > + u8 config; > + u8 als1_imp_sel; > + u8 als2_imp_sel; > + u8 als_avrg_time; > + u8 als_input_mode; > + u32 als_vmin; > + u32 als_vmax; > + u8 zones_lo[LM3532_ALS_ZB_MAX]; > + u8 zones_hi[LM3532_ALS_ZB_MAX]; > +}; > + > +/** > + * struct lm3532_led > + * @led_dev: led class device > + * @priv - Pointer the device data structure > + * @control_bank - Control bank the LED is associated to > + * @mode - Mode of the LED string > + * @num_leds - Number of LED strings are supported in this array > + * @led_strings - The LED strings supported in this array > + * @label - LED label > + */ > +struct lm3532_led { > + struct led_classdev led_dev; > + struct lm3532_data *priv; > + > + int control_bank; > + int mode; > + int num_leds; > + u32 led_strings[LM3532_MAX_CONTROL_BANKS]; > + char label[LED_MAX_NAME_SIZE]; > +}; > + > +/** > + * struct lm3532_data > + * @enable_gpio - Hardware enable gpio > + * @regulator: regulator > + * @client: i2c client > + * @regmap - Devices register map > + * @dev - Pointer to the devices device struct > + * @lock - Lock for reading/writing the device > + * @als_data - Pointer to the als data struct > + * @runtime_ramp_up - Runtime ramp up setting > + * @runtime_ramp_down - Runtime ramp down setting > + * @leds - Array of LED strings > + */ > +struct lm3532_data { > + struct gpio_desc *enable_gpio; > + struct regulator *regulator; > + struct i2c_client *client; > + struct regmap *regmap; > + struct device *dev; > + struct mutex lock; > + > + struct lm3532_als_data *als_data; > + > + u32 runtime_ramp_up; > + u32 runtime_ramp_down; > + > + struct lm3532_led leds[]; > +}; > + > +static const struct reg_default lm3532_reg_defs[] = { > + {LM3532_REG_OUTPUT_CFG, 0xe4}, > + {LM3532_REG_STARTSHUT_RAMP, 0xc0}, > + {LM3532_REG_RT_RAMP, 0xc0}, > + {LM3532_REG_PWM_A_CFG, 0x82}, > + {LM3532_REG_PWM_B_CFG, 0x82}, > + {LM3532_REG_PWM_C_CFG, 0x82}, > + {LM3532_REG_ZONE_CFG_A, 0xf1}, > + {LM3532_REG_CTRL_A_BRT, 0xf3}, > + {LM3532_REG_ZONE_CFG_B, 0xf1}, > + {LM3532_REG_CTRL_B_BRT, 0xf3}, > + {LM3532_REG_ZONE_CFG_C, 0xf1}, > + {LM3532_REG_CTRL_C_BRT, 0xf3}, > + {LM3532_REG_ENABLE, 0xf8}, > + {LM3532_ALS_CONFIG, 0x44}, > + {LM3532_REG_ZN_0_HI, 0x35}, > + {LM3532_REG_ZN_0_LO, 0x33}, > + {LM3532_REG_ZN_1_HI, 0x6a}, > + {LM3532_REG_ZN_1_LO, 0x66}, > + {LM3532_REG_ZN_2_HI, 0xa1}, > + {LM3532_REG_ZN_2_LO, 0x99}, > + {LM3532_REG_ZN_3_HI, 0xdc}, > + {LM3532_REG_ZN_3_LO, 0xcc}, > +}; > + > +static const struct regmap_config lm3532_regmap_config = { > + .reg_bits = 8, > + .val_bits = 8, > + > + .max_register = LM3532_REG_MAX, > + .reg_defaults = lm3532_reg_defs, > + .num_reg_defaults = ARRAY_SIZE(lm3532_reg_defs), > + .cache_type = REGCACHE_FLAT, > +}; > + > +const static int als_imp_table[LM3532_NUM_IMP_VALS] = {37000, 18500, 12330, > + 92500, 7400, 6170, 5290, > + 4630, 4110, 3700, 3360, > + 3080, 2850, 2640, 2440, > + 2310, 2180, 2060, 1950, > + 1850, 1760, 1680, 1610, > + 1540, 1480, 1420, 1370, > + 1320, 1280, 1230, 1190}; > +static int lm3532_get_als_imp_index(int als_imped) > +{ > + int i; > + > + if (als_imped > als_imp_table[1]) > + return 0; > + > + if (als_imped < als_imp_table[LM3532_NUM_IMP_VALS - 1]) > + return LM3532_NUM_IMP_VALS - 1; > + > + for (i = 1; i < LM3532_NUM_IMP_VALS; i++) { > + if (als_imped == als_imp_table[i]) > + return i; > + > + /* Find an approximate index by looking up the table */ > + if (als_imped < als_imp_table[i - 1] && > + als_imped > als_imp_table[i]) { > + if (als_imped - als_imp_table[i - 1] < > + als_imp_table[i] - als_imped) > + return i + 1; > + else > + return i; > + } > + } > + > + return -EINVAL; > +} > + > +static int lm3532_get_index(const int table[], int size, int value) > +{ > + int i; > + > + for (i = 1; i < size; i++) { > + if (value == table[i]) > + return i; > + > + /* Find an approximate index by looking up the table */ > + if (value > table[i - 1] && > + value < table[i]) { > + if (value - table[i - 1] < table[i] - value) > + return i - 1; > + else > + return i; > + } > + } > + > + return -EINVAL; > +} > + > +const static int als_avrg_table[LM3532_NUM_AVG_VALS] = {17920, 35840, 71680, > + 1433360, 286720, 573440, > + 1146880, 2293760}; > +static int lm3532_get_als_avg_index(int avg_time) > +{ > + if (avg_time <= als_avrg_table[0]) > + return 0; > + > + if (avg_time > als_avrg_table[LM3532_NUM_AVG_VALS - 1]) > + return LM3532_NUM_AVG_VALS - 1; > + > + return lm3532_get_index(&als_avrg_table[0], LM3532_NUM_AVG_VALS, > + avg_time); > +} > + > +const static int ramp_table[LM3532_NUM_RAMP_VALS] = { 8, 1024, 2048, 4096, 8192, > + 16384, 32768, 65536}; > +static int lm3532_get_ramp_index(int ramp_time) > +{ > + if (ramp_time <= ramp_table[0]) > + return 0; > + > + if (ramp_time > ramp_table[LM3532_NUM_RAMP_VALS - 1]) > + return LM3532_NUM_RAMP_VALS - 1; > + > + return lm3532_get_index(&ramp_table[0], LM3532_NUM_RAMP_VALS, > + ramp_time); > +} > + > +static int lm3532_led_enable(struct lm3532_led *led_data) > +{ > + int ctrl_en_val = BIT(led_data->control_bank); > + int ret; > + > + ret = regmap_update_bits(led_data->priv->regmap, LM3532_REG_ENABLE, > + ctrl_en_val, ctrl_en_val); > + if (ret) { > + dev_err(led_data->priv->dev, "Failed to set ctrl:%d\n", ret); > + return ret; > + } > + > + return regulator_enable(led_data->priv->regulator); > +} > + > +static int lm3532_led_disable(struct lm3532_led *led_data) > +{ > + int ctrl_en_val = BIT(led_data->control_bank); > + int ret; > + > + ret = regmap_update_bits(led_data->priv->regmap, LM3532_REG_ENABLE, > + ctrl_en_val, ~ctrl_en_val); > + if (ret) { > + dev_err(led_data->priv->dev, "Failed to set ctrl:%d\n", ret); > + return ret; > + } > + > + return regulator_disable(led_data->priv->regulator); > +} > + > +static int lm3532_brightness_set(struct led_classdev *led_cdev, > + enum led_brightness brt_val) > +{ > + struct lm3532_led *led = > + container_of(led_cdev, struct lm3532_led, led_dev); > + u8 brightness_reg; > + int ret; > + > + mutex_lock(&led->priv->lock); > + > + if (led->mode == LM3532_BL_MODE_ALS) { > + if (brt_val > LED_OFF) > + ret = lm3532_led_enable(led); > + else > + ret = lm3532_led_disable(led); > + > + goto unlock; > + } > + > + if (brt_val == LED_OFF) { > + ret = lm3532_led_disable(led); > + goto unlock; > + } > + > + ret = lm3532_led_enable(led); > + if (ret) > + goto unlock; > + > + brightness_reg = LM3532_REG_CTRL_A_BRT + led->control_bank * 2; > + brt_val = brt_val / LM3532_BRT_VAL_ADJUST; > + > + ret = regmap_write(led->priv->regmap, brightness_reg, brt_val); > + > +unlock: > + mutex_unlock(&led->priv->lock); > + return ret; > +} > + > +static int lm3532_init_registers(struct lm3532_led *led) > +{ > + struct lm3532_data *drvdata = led->priv; > + unsigned int runtime_ramp_val; > + unsigned int output_cfg_val = 0; > + unsigned int output_cfg_shift = 0; > + unsigned int output_cfg_mask = 0; > + int ret, i; > + > + for (i = 0; i < led->num_leds; i++) { > + output_cfg_shift = led->led_strings[i] * 2; > + output_cfg_val |= (led->control_bank << output_cfg_shift); > + output_cfg_mask |= LM3532_OUTPUT_CFG_MASK << output_cfg_shift; > + } > + > + ret = regmap_update_bits(drvdata->regmap, LM3532_REG_OUTPUT_CFG, > + output_cfg_mask, output_cfg_val); > + if (ret) > + return ret; > + > + runtime_ramp_val = drvdata->runtime_ramp_up | > + (drvdata->runtime_ramp_down << LM3532_RAMP_DOWN_SHIFT); > + > + return regmap_write(drvdata->regmap, LM3532_REG_RT_RAMP, > + runtime_ramp_val); > +} > + > +static int lm3532_als_configure(struct lm3532_data *priv, > + struct lm3532_led *led) > +{ > + struct lm3532_als_data *als = priv->als_data; > + u32 als_vmin, als_vmax, als_vstep; > + int zone_reg = LM3532_REG_ZN_0_HI; > + int brightnes_config_reg; > + int ret; > + int i; > + > + als_vmin = als->als_vmin; > + als_vmax = als->als_vmax; > + > + als_vstep = (als_vmax - als_vmin) / ((LM3532_ALS_ZB_MAX + 1) * 2); > + > + for (i = 0; i < LM3532_ALS_ZB_MAX; i++) { > + als->zones_lo[i] = ((als_vmin + als_vstep + (i * als_vstep)) * > + LED_FULL) / 1000; > + als->zones_hi[i] = ((als_vmin + LM3532_ALS_OFFSET_mV + > + als_vstep + (i * als_vstep)) * LED_FULL) / 1000; > + > + zone_reg = LM3532_REG_ZN_0_HI + i * 2; > + ret = regmap_write(priv->regmap, zone_reg, als->zones_lo[i]); > + if (ret) > + return ret; > + > + zone_reg += 1; > + ret = regmap_write(priv->regmap, zone_reg, als->zones_hi[i]); > + if (ret) > + return ret; > + } > + > + als->config = (als->als_avrg_time | (LM3532_ENABLE_ALS) | > + (als->als_input_mode << LM3532_ALS_SEL_SHIFT)); > + > + ret = regmap_write(priv->regmap, LM3532_ALS_CONFIG, als->config); > + if (ret) > + return ret; > + > + brightnes_config_reg = LM3532_REG_ZONE_CFG_A + led->control_bank * 2; > + > + return regmap_update_bits(priv->regmap, brightnes_config_reg, > + LM3532_I2C_CTRL, LM3532_ALS_CTRL); > +} > + > +static int lm3532_parse_als(struct lm3532_data *priv) > +{ > + struct lm3532_als_data *als; > + int als_avg_time; > + int als_impedance; > + int ret; > + > + als = devm_kzalloc(priv->dev, sizeof(*als), GFP_KERNEL); > + if (als == NULL) > + return -ENOMEM; > + > + ret = device_property_read_u32(&priv->client->dev, "ti,als-vmin", > + &als->als_vmin); > + if (ret) > + als->als_vmin = 0; > + > + ret = device_property_read_u32(&priv->client->dev, "ti,als-vmax", > + &als->als_vmax); > + if (ret) > + als->als_vmax = LM3532_ALS_WINDOW_mV; > + > + if (als->als_vmax > LM3532_ALS_WINDOW_mV) { > + ret = -EINVAL; > + return ret; > + } > + > + ret = device_property_read_u32(&priv->client->dev, "ti,als1-imp-sel", > + &als_impedance); > + if (ret) > + als->als1_imp_sel = 0; > + else > + als->als1_imp_sel = lm3532_get_als_imp_index(als_impedance); > + > + ret = device_property_read_u32(&priv->client->dev, "ti,als2-imp-sel", > + &als_impedance); > + if (ret) > + als->als2_imp_sel = 0; > + else > + als->als2_imp_sel = lm3532_get_als_imp_index(als_impedance); > + > + ret = device_property_read_u32(&priv->client->dev, "ti,als-avrg-time-us", > + &als_avg_time); > + if (ret) > + als->als_avrg_time = 0; > + else > + als->als_avrg_time = lm3532_get_als_avg_index(als_avg_time); > + > + ret = device_property_read_u8(&priv->client->dev, "ti,als-input-mode", > + &als->als_input_mode); > + if (ret) > + als->als_input_mode = 0; > + > + if (als->als_input_mode > LM3532_BL_MODE_ALS) { > + ret = -EINVAL; > + return ret; > + } > + > + priv->als_data = als; > + > + return ret; > +} > + > +static int lm3532_parse_node(struct lm3532_data *priv) > +{ > + struct fwnode_handle *child = NULL; > + struct lm3532_led *led; > + const char *name; > + int control_bank; > + u32 ramp_time; > + size_t i = 0; > + int ret; > + > + priv->enable_gpio = devm_gpiod_get_optional(&priv->client->dev, > + "enable", GPIOD_OUT_LOW); > + if (IS_ERR(priv->enable_gpio)) > + priv->enable_gpio = NULL; > + > + priv->regulator = devm_regulator_get(&priv->client->dev, "vin"); > + if (IS_ERR(priv->regulator)) > + priv->regulator = NULL; > + > + ret = device_property_read_u32(&priv->client->dev, "ramp-up-us", > + &ramp_time); > + if (ret) > + dev_info(&priv->client->dev, "ramp-up-ms property missing\n"); > + else > + priv->runtime_ramp_up = lm3532_get_ramp_index(ramp_time); > + > + ret = device_property_read_u32(&priv->client->dev, "ramp-down-us", > + &ramp_time); > + if (ret) > + dev_info(&priv->client->dev, "ramp-down-ms property missing\n"); > + else > + priv->runtime_ramp_down = lm3532_get_ramp_index(ramp_time); > + > + device_for_each_child_node(priv->dev, child) { > + led = &priv->leds[i]; > + > + ret = fwnode_property_read_u32(child, "reg", &control_bank); > + if (ret) { > + dev_err(&priv->client->dev, "reg property missing\n"); > + fwnode_handle_put(child); > + goto child_out; > + } > + > + if (control_bank > LM3532_CONTROL_C) { > + dev_err(&priv->client->dev, "Control bank invalid\n"); > + continue; > + } > + > + led->control_bank = control_bank; > + > + ret = fwnode_property_read_u32(child, "ti,led-mode", > + &led->mode); > + if (ret) { > + dev_err(&priv->client->dev, "ti,led-mode property missing\n"); > + fwnode_handle_put(child); > + goto child_out; > + } > + > + if (led->mode == LM3532_BL_MODE_ALS) { > + ret = lm3532_parse_als(priv); > + if (ret) > + dev_err(&priv->client->dev, "Failed to parse als\n"); > + else > + lm3532_als_configure(priv, led); > + } > + > + led->num_leds = fwnode_property_read_u32_array(child, > + "led-sources", > + NULL, 0); > + > + if (led->num_leds > LM3532_MAX_LED_STRINGS) { > + dev_err(&priv->client->dev, "To many LED string defined\n"); > + continue; > + } > + > + ret = fwnode_property_read_u32_array(child, "led-sources", > + led->led_strings, > + led->num_leds); > + if (ret) { > + dev_err(&priv->client->dev, "led-sources property missing\n"); > + fwnode_handle_put(child); > + goto child_out; > + } > + > + fwnode_property_read_string(child, "linux,default-trigger", > + &led->led_dev.default_trigger); > + > + ret = fwnode_property_read_string(child, "label", &name); > + if (ret) > + snprintf(led->label, sizeof(led->label), > + "%s::", priv->client->name); > + else > + snprintf(led->label, sizeof(led->label), > + "%s:%s", priv->client->name, name); > + > + led->priv = priv; > + led->led_dev.name = led->label; > + led->led_dev.brightness_set_blocking = lm3532_brightness_set; > + > + ret = devm_led_classdev_register(priv->dev, &led->led_dev); > + if (ret) { > + dev_err(&priv->client->dev, "led register err: %d\n", > + ret); > + fwnode_handle_put(child); > + goto child_out; > + } > + > + lm3532_init_registers(led); > + > + i++; > + } > + > +child_out: > + return ret; > +} > + > +static int lm3532_probe(struct i2c_client *client, > + const struct i2c_device_id *id) > +{ > + struct lm3532_data *drvdata; > + int ret = 0; > + int count; > + > + count = device_get_child_node_count(&client->dev); > + if (!count) { > + dev_err(&client->dev, "LEDs are not defined in device tree!"); > + return -ENODEV; > + } > + > + drvdata = devm_kzalloc(&client->dev, struct_size(drvdata, leds, count), > + GFP_KERNEL); > + if (drvdata == NULL) > + return -ENOMEM; > + > + drvdata->client = client; > + drvdata->dev = &client->dev; > + > + drvdata->regmap = devm_regmap_init_i2c(client, &lm3532_regmap_config); > + if (IS_ERR(drvdata->regmap)) { > + ret = PTR_ERR(drvdata->regmap); > + dev_err(&client->dev, "Failed to allocate register map: %d\n", > + ret); > + return ret; > + } > + > + mutex_init(&drvdata->lock); > + i2c_set_clientdata(client, drvdata); > + > + ret = lm3532_parse_node(drvdata); > + if (ret) { > + dev_err(&client->dev, "Failed to parse node\n"); > + return ret; > + } > + > + if (drvdata->enable_gpio) > + gpiod_direction_output(drvdata->enable_gpio, 1); > + > + return ret; > +} > + > +static int lm3532_remove(struct i2c_client *client) > +{ > + struct lm3532_data *drvdata = i2c_get_clientdata(client); mutex_destroy(&drvdata->lock); > + if (drvdata->enable_gpio) > + gpiod_direction_output(drvdata->enable_gpio, 0); > + > + return 0; > +} > + > +static const struct of_device_id of_lm3532_leds_match[] = { > + { .compatible = "ti,lm3532", }, > + {}, > +}; > +MODULE_DEVICE_TABLE(of, of_lm3532_leds_match); > + > +static const struct i2c_device_id lm3532_id[] = { > + {LM3532_NAME, 0}, > + {} > +}; > +MODULE_DEVICE_TABLE(i2c, lm3532_id); > + > +static struct i2c_driver lm3532_i2c_driver = { > + .probe = lm3532_probe, > + .remove = lm3532_remove, > + .id_table = lm3532_id, > + .driver = { > + .name = LM3532_NAME, > + .of_match_table = of_lm3532_leds_match, > + }, > +}; > +module_i2c_driver(lm3532_i2c_driver); > + > +MODULE_DESCRIPTION("Back Light driver for LM3532"); > +MODULE_LICENSE("GPL v2"); > +MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>"); >
Hi Dan, On 3/20/19 1:39 PM, Dan Murphy wrote: > Hello > > On 3/13/19 7:32 AM, Dan Murphy wrote: >> Introduce the Texas Instruments LM3532 White LED driver. >> The driver supports ALS configurability or manual brightness >> control. >> >> The driver also supports associating LED strings with specific >> control banks in a group or as individually controlled strings. >> > > I have a v5 series ready for posting and only the DT was reviewed. > > Do we have any comments on the code? I've just added one comment. After it is addressed please submit v5.
Jacek On 3/20/19 1:59 PM, Jacek Anaszewski wrote: > Hi Dan, > > I have one minor issue below. > > On 3/13/19 1:32 PM, Dan Murphy wrote: >> Introduce the Texas Instruments LM3532 White LED driver. >> The driver supports ALS configurability or manual brightness >> control. >> >> The driver also supports associating LED strings with specific >> control banks in a group or as individually controlled strings. >> >> Signed-off-by: Dan Murphy <dmurphy@ti.com> >> --- >> >> v4 - Updated code to DT doc changes appending "ti," to TI specific properties - >> https://lore.kernel.org/patchwork/patch/1050124/ >> >> v3 - Removed switch case for register setting in favor of an algorithim (v1 comment), >> https://lore.kernel.org/patchwork/patch/1049024/ >> v2 - Added look up tables for als_avg, als_imp and ramp times, fixed brightness >> control when ALS is in control, added property value checks to ensure the values >> are not out of bounds - https://lore.kernel.org/patchwork/patch/1048807/ >> >> drivers/leds/Kconfig | 10 + >> drivers/leds/Makefile | 1 + >> drivers/leds/leds-lm3532.c | 681 +++++++++++++++++++++++++++++++++++++ >> 3 files changed, 692 insertions(+) >> create mode 100644 drivers/leds/leds-lm3532.c >> >> diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig >> index a72f97fca57b..da00b9ed5a5c 100644 >> --- a/drivers/leds/Kconfig >> +++ b/drivers/leds/Kconfig >> @@ -138,6 +138,16 @@ config LEDS_LM3530 >> controlled manually or using PWM input or using ambient >> light automatically. >> +config LEDS_LM3532 >> + tristate "LCD Backlight driver for LM3532" >> + depends on LEDS_CLASS >> + depends on I2C >> + help >> + This option enables support for the LCD backlight using >> + LM3532 ambient light sensor chip. This ALS chip can be >> + controlled manually or using PWM input or using ambient >> + light automatically. >> + >> config LEDS_LM3533 >> tristate "LED support for LM3533" >> depends on LEDS_CLASS >> diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile >> index 4c1b0054f379..7a8b1f55d459 100644 >> --- a/drivers/leds/Makefile >> +++ b/drivers/leds/Makefile >> @@ -18,6 +18,7 @@ obj-$(CONFIG_LEDS_BD2802) += leds-bd2802.o >> obj-$(CONFIG_LEDS_CPCAP) += leds-cpcap.o >> obj-$(CONFIG_LEDS_LOCOMO) += leds-locomo.o >> obj-$(CONFIG_LEDS_LM3530) += leds-lm3530.o >> +obj-$(CONFIG_LEDS_LM3532) += leds-lm3532.o >> obj-$(CONFIG_LEDS_LM3533) += leds-lm3533.o >> obj-$(CONFIG_LEDS_LM3642) += leds-lm3642.o >> obj-$(CONFIG_LEDS_MIKROTIK_RB532) += leds-rb532.o >> diff --git a/drivers/leds/leds-lm3532.c b/drivers/leds/leds-lm3532.c >> new file mode 100644 >> index 000000000000..e8a614d71469 >> --- /dev/null >> +++ b/drivers/leds/leds-lm3532.c >> @@ -0,0 +1,681 @@ >> +// SPDX-License-Identifier: GPL-2.0 >> +// TI LM3532 LED driver >> +// Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/ >> + >> +#include <linux/i2c.h> >> +#include <linux/leds.h> >> +#include <linux/slab.h> >> +#include <linux/regmap.h> >> +#include <linux/types.h> >> +#include <linux/regulator/consumer.h> >> +#include <linux/module.h> >> +#include <uapi/linux/uleds.h> >> +#include <linux/gpio/consumer.h> >> + >> +#define LM3532_NAME "lm3532-led" >> +#define LM3532_BL_MODE_MANUAL 0x00 >> +#define LM3532_BL_MODE_ALS 0x01 >> + >> +#define LM3532_REG_OUTPUT_CFG 0x10 >> +#define LM3532_REG_STARTSHUT_RAMP 0x11 >> +#define LM3532_REG_RT_RAMP 0x12 >> +#define LM3532_REG_PWM_A_CFG 0x13 >> +#define LM3532_REG_PWM_B_CFG 0x14 >> +#define LM3532_REG_PWM_C_CFG 0x15 >> +#define LM3532_REG_ZONE_CFG_A 0x16 >> +#define LM3532_REG_CTRL_A_BRT 0x17 >> +#define LM3532_REG_ZONE_CFG_B 0x18 >> +#define LM3532_REG_CTRL_B_BRT 0x19 >> +#define LM3532_REG_ZONE_CFG_C 0x1a >> +#define LM3532_REG_CTRL_C_BRT 0x1b >> +#define LM3532_REG_ENABLE 0x1d >> +#define LM3532_ALS_CONFIG 0x23 >> +#define LM3532_REG_ZN_0_HI 0x60 >> +#define LM3532_REG_ZN_0_LO 0x61 >> +#define LM3532_REG_ZN_1_HI 0x62 >> +#define LM3532_REG_ZN_1_LO 0x63 >> +#define LM3532_REG_ZN_2_HI 0x64 >> +#define LM3532_REG_ZN_2_LO 0x65 >> +#define LM3532_REG_ZN_3_HI 0x66 >> +#define LM3532_REG_ZN_3_LO 0x67 >> +#define LM3532_REG_MAX 0x7e >> + >> +/* Contorl Enable */ >> +#define LM3532_CTRL_A_ENABLE BIT(0) >> +#define LM3532_CTRL_B_ENABLE BIT(1) >> +#define LM3532_CTRL_C_ENABLE BIT(2) >> + >> +/* PWM Zone Control */ >> +#define LM3532_PWM_ZONE_MASK 0x7c >> +#define LM3532_PWM_ZONE_0_EN BIT(2) >> +#define LM3532_PWM_ZONE_1_EN BIT(3) >> +#define LM3532_PWM_ZONE_2_EN BIT(4) >> +#define LM3532_PWM_ZONE_3_EN BIT(5) >> +#define LM3532_PWM_ZONE_4_EN BIT(6) >> + >> +/* Brightness Configuration */ >> +#define LM3532_I2C_CTRL BIT(0) >> +#define LM3532_ALS_CTRL 0 >> +#define LM3532_LINEAR_MAP BIT(1) >> +#define LM3532_ZONE_MASK (BIT(2) | BIT(3) | BIT(4)) >> +#define LM3532_ZONE_0 0 >> +#define LM3532_ZONE_1 BIT(2) >> +#define LM3532_ZONE_2 BIT(3) >> +#define LM3532_ZONE_3 (BIT(2) | BIT(3)) >> +#define LM3532_ZONE_4 BIT(4) >> + >> +#define LM3532_ENABLE_ALS BIT(3) >> +#define LM3532_ALS_SEL_SHIFT 6 >> + >> +/* Zone Boundary Register */ >> +#define LM3532_ALS_WINDOW_mV 2000 >> +#define LM3532_ALS_ZB_MAX 4 >> +#define LM3532_ALS_OFFSET_mV 2 >> + >> +#define LM3532_CONTROL_A 0 >> +#define LM3532_CONTROL_B 1 >> +#define LM3532_CONTROL_C 2 >> +#define LM3532_MAX_CONTROL_BANKS 3 >> +#define LM3532_MAX_LED_STRINGS 3 >> + >> +#define LM3532_OUTPUT_CFG_MASK 0x3 >> +#define LM3532_BRT_VAL_ADJUST 8 >> +#define LM3532_RAMP_DOWN_SHIFT 3 >> + >> +#define LM3532_NUM_RAMP_VALS 8 >> +#define LM3532_NUM_AVG_VALS 8 >> +#define LM3532_NUM_IMP_VALS 32 >> + >> +/* >> + * struct lm3532_als_data >> + * @config - value of ALS configuration register >> + * @als1_imp_sel - value of ALS1 resistor select register >> + * @als2_imp_sel - value of ALS2 resistor select register >> + * @als_avrg_time - ALS averaging time >> + * @als_input_mode - ALS input mode for brightness control >> + * @als_vmin - Minimum ALS voltage >> + * @als_vmax - Maximum ALS voltage >> + * @zone_lo - values of ALS lo ZB(Zone Boundary) registers >> + * @zone_hi - values of ALS hi ZB(Zone Boundary) registers >> + */ >> +struct lm3532_als_data { >> + u8 config; >> + u8 als1_imp_sel; >> + u8 als2_imp_sel; >> + u8 als_avrg_time; >> + u8 als_input_mode; >> + u32 als_vmin; >> + u32 als_vmax; >> + u8 zones_lo[LM3532_ALS_ZB_MAX]; >> + u8 zones_hi[LM3532_ALS_ZB_MAX]; >> +}; >> + >> +/** >> + * struct lm3532_led >> + * @led_dev: led class device >> + * @priv - Pointer the device data structure >> + * @control_bank - Control bank the LED is associated to >> + * @mode - Mode of the LED string >> + * @num_leds - Number of LED strings are supported in this array >> + * @led_strings - The LED strings supported in this array >> + * @label - LED label >> + */ >> +struct lm3532_led { >> + struct led_classdev led_dev; >> + struct lm3532_data *priv; >> + >> + int control_bank; >> + int mode; >> + int num_leds; >> + u32 led_strings[LM3532_MAX_CONTROL_BANKS]; >> + char label[LED_MAX_NAME_SIZE]; >> +}; >> + >> +/** >> + * struct lm3532_data >> + * @enable_gpio - Hardware enable gpio >> + * @regulator: regulator >> + * @client: i2c client >> + * @regmap - Devices register map >> + * @dev - Pointer to the devices device struct >> + * @lock - Lock for reading/writing the device >> + * @als_data - Pointer to the als data struct >> + * @runtime_ramp_up - Runtime ramp up setting >> + * @runtime_ramp_down - Runtime ramp down setting >> + * @leds - Array of LED strings >> + */ >> +struct lm3532_data { >> + struct gpio_desc *enable_gpio; >> + struct regulator *regulator; >> + struct i2c_client *client; >> + struct regmap *regmap; >> + struct device *dev; >> + struct mutex lock; >> + >> + struct lm3532_als_data *als_data; >> + >> + u32 runtime_ramp_up; >> + u32 runtime_ramp_down; >> + >> + struct lm3532_led leds[]; >> +}; >> + >> +static const struct reg_default lm3532_reg_defs[] = { >> + {LM3532_REG_OUTPUT_CFG, 0xe4}, >> + {LM3532_REG_STARTSHUT_RAMP, 0xc0}, >> + {LM3532_REG_RT_RAMP, 0xc0}, >> + {LM3532_REG_PWM_A_CFG, 0x82}, >> + {LM3532_REG_PWM_B_CFG, 0x82}, >> + {LM3532_REG_PWM_C_CFG, 0x82}, >> + {LM3532_REG_ZONE_CFG_A, 0xf1}, >> + {LM3532_REG_CTRL_A_BRT, 0xf3}, >> + {LM3532_REG_ZONE_CFG_B, 0xf1}, >> + {LM3532_REG_CTRL_B_BRT, 0xf3}, >> + {LM3532_REG_ZONE_CFG_C, 0xf1}, >> + {LM3532_REG_CTRL_C_BRT, 0xf3}, >> + {LM3532_REG_ENABLE, 0xf8}, >> + {LM3532_ALS_CONFIG, 0x44}, >> + {LM3532_REG_ZN_0_HI, 0x35}, >> + {LM3532_REG_ZN_0_LO, 0x33}, >> + {LM3532_REG_ZN_1_HI, 0x6a}, >> + {LM3532_REG_ZN_1_LO, 0x66}, >> + {LM3532_REG_ZN_2_HI, 0xa1}, >> + {LM3532_REG_ZN_2_LO, 0x99}, >> + {LM3532_REG_ZN_3_HI, 0xdc}, >> + {LM3532_REG_ZN_3_LO, 0xcc}, >> +}; >> + >> +static const struct regmap_config lm3532_regmap_config = { >> + .reg_bits = 8, >> + .val_bits = 8, >> + >> + .max_register = LM3532_REG_MAX, >> + .reg_defaults = lm3532_reg_defs, >> + .num_reg_defaults = ARRAY_SIZE(lm3532_reg_defs), >> + .cache_type = REGCACHE_FLAT, >> +}; >> + >> +const static int als_imp_table[LM3532_NUM_IMP_VALS] = {37000, 18500, 12330, >> + 92500, 7400, 6170, 5290, >> + 4630, 4110, 3700, 3360, >> + 3080, 2850, 2640, 2440, >> + 2310, 2180, 2060, 1950, >> + 1850, 1760, 1680, 1610, >> + 1540, 1480, 1420, 1370, >> + 1320, 1280, 1230, 1190}; >> +static int lm3532_get_als_imp_index(int als_imped) >> +{ >> + int i; >> + >> + if (als_imped > als_imp_table[1]) >> + return 0; >> + >> + if (als_imped < als_imp_table[LM3532_NUM_IMP_VALS - 1]) >> + return LM3532_NUM_IMP_VALS - 1; >> + >> + for (i = 1; i < LM3532_NUM_IMP_VALS; i++) { >> + if (als_imped == als_imp_table[i]) >> + return i; >> + >> + /* Find an approximate index by looking up the table */ >> + if (als_imped < als_imp_table[i - 1] && >> + als_imped > als_imp_table[i]) { >> + if (als_imped - als_imp_table[i - 1] < >> + als_imp_table[i] - als_imped) >> + return i + 1; >> + else >> + return i; >> + } >> + } >> + >> + return -EINVAL; >> +} >> + >> +static int lm3532_get_index(const int table[], int size, int value) >> +{ >> + int i; >> + >> + for (i = 1; i < size; i++) { >> + if (value == table[i]) >> + return i; >> + >> + /* Find an approximate index by looking up the table */ >> + if (value > table[i - 1] && >> + value < table[i]) { >> + if (value - table[i - 1] < table[i] - value) >> + return i - 1; >> + else >> + return i; >> + } >> + } >> + >> + return -EINVAL; >> +} >> + >> +const static int als_avrg_table[LM3532_NUM_AVG_VALS] = {17920, 35840, 71680, >> + 1433360, 286720, 573440, >> + 1146880, 2293760}; >> +static int lm3532_get_als_avg_index(int avg_time) >> +{ >> + if (avg_time <= als_avrg_table[0]) >> + return 0; >> + >> + if (avg_time > als_avrg_table[LM3532_NUM_AVG_VALS - 1]) >> + return LM3532_NUM_AVG_VALS - 1; >> + >> + return lm3532_get_index(&als_avrg_table[0], LM3532_NUM_AVG_VALS, >> + avg_time); >> +} >> + >> +const static int ramp_table[LM3532_NUM_RAMP_VALS] = { 8, 1024, 2048, 4096, 8192, >> + 16384, 32768, 65536}; >> +static int lm3532_get_ramp_index(int ramp_time) >> +{ >> + if (ramp_time <= ramp_table[0]) >> + return 0; >> + >> + if (ramp_time > ramp_table[LM3532_NUM_RAMP_VALS - 1]) >> + return LM3532_NUM_RAMP_VALS - 1; >> + >> + return lm3532_get_index(&ramp_table[0], LM3532_NUM_RAMP_VALS, >> + ramp_time); >> +} >> + >> +static int lm3532_led_enable(struct lm3532_led *led_data) >> +{ >> + int ctrl_en_val = BIT(led_data->control_bank); >> + int ret; >> + >> + ret = regmap_update_bits(led_data->priv->regmap, LM3532_REG_ENABLE, >> + ctrl_en_val, ctrl_en_val); >> + if (ret) { >> + dev_err(led_data->priv->dev, "Failed to set ctrl:%d\n", ret); >> + return ret; >> + } >> + >> + return regulator_enable(led_data->priv->regulator); >> +} >> + >> +static int lm3532_led_disable(struct lm3532_led *led_data) >> +{ >> + int ctrl_en_val = BIT(led_data->control_bank); >> + int ret; >> + >> + ret = regmap_update_bits(led_data->priv->regmap, LM3532_REG_ENABLE, >> + ctrl_en_val, ~ctrl_en_val); >> + if (ret) { >> + dev_err(led_data->priv->dev, "Failed to set ctrl:%d\n", ret); >> + return ret; >> + } >> + >> + return regulator_disable(led_data->priv->regulator); >> +} >> + >> +static int lm3532_brightness_set(struct led_classdev *led_cdev, >> + enum led_brightness brt_val) >> +{ >> + struct lm3532_led *led = >> + container_of(led_cdev, struct lm3532_led, led_dev); >> + u8 brightness_reg; >> + int ret; >> + >> + mutex_lock(&led->priv->lock); >> + >> + if (led->mode == LM3532_BL_MODE_ALS) { >> + if (brt_val > LED_OFF) >> + ret = lm3532_led_enable(led); >> + else >> + ret = lm3532_led_disable(led); >> + >> + goto unlock; >> + } >> + >> + if (brt_val == LED_OFF) { >> + ret = lm3532_led_disable(led); >> + goto unlock; >> + } >> + >> + ret = lm3532_led_enable(led); >> + if (ret) >> + goto unlock; >> + >> + brightness_reg = LM3532_REG_CTRL_A_BRT + led->control_bank * 2; >> + brt_val = brt_val / LM3532_BRT_VAL_ADJUST; >> + >> + ret = regmap_write(led->priv->regmap, brightness_reg, brt_val); >> + >> +unlock: >> + mutex_unlock(&led->priv->lock); >> + return ret; >> +} >> + >> +static int lm3532_init_registers(struct lm3532_led *led) >> +{ >> + struct lm3532_data *drvdata = led->priv; >> + unsigned int runtime_ramp_val; >> + unsigned int output_cfg_val = 0; >> + unsigned int output_cfg_shift = 0; >> + unsigned int output_cfg_mask = 0; >> + int ret, i; >> + >> + for (i = 0; i < led->num_leds; i++) { >> + output_cfg_shift = led->led_strings[i] * 2; >> + output_cfg_val |= (led->control_bank << output_cfg_shift); >> + output_cfg_mask |= LM3532_OUTPUT_CFG_MASK << output_cfg_shift; >> + } >> + >> + ret = regmap_update_bits(drvdata->regmap, LM3532_REG_OUTPUT_CFG, >> + output_cfg_mask, output_cfg_val); >> + if (ret) >> + return ret; >> + >> + runtime_ramp_val = drvdata->runtime_ramp_up | >> + (drvdata->runtime_ramp_down << LM3532_RAMP_DOWN_SHIFT); >> + >> + return regmap_write(drvdata->regmap, LM3532_REG_RT_RAMP, >> + runtime_ramp_val); >> +} >> + >> +static int lm3532_als_configure(struct lm3532_data *priv, >> + struct lm3532_led *led) >> +{ >> + struct lm3532_als_data *als = priv->als_data; >> + u32 als_vmin, als_vmax, als_vstep; >> + int zone_reg = LM3532_REG_ZN_0_HI; >> + int brightnes_config_reg; >> + int ret; >> + int i; >> + >> + als_vmin = als->als_vmin; >> + als_vmax = als->als_vmax; >> + >> + als_vstep = (als_vmax - als_vmin) / ((LM3532_ALS_ZB_MAX + 1) * 2); >> + >> + for (i = 0; i < LM3532_ALS_ZB_MAX; i++) { >> + als->zones_lo[i] = ((als_vmin + als_vstep + (i * als_vstep)) * >> + LED_FULL) / 1000; >> + als->zones_hi[i] = ((als_vmin + LM3532_ALS_OFFSET_mV + >> + als_vstep + (i * als_vstep)) * LED_FULL) / 1000; >> + >> + zone_reg = LM3532_REG_ZN_0_HI + i * 2; >> + ret = regmap_write(priv->regmap, zone_reg, als->zones_lo[i]); >> + if (ret) >> + return ret; >> + >> + zone_reg += 1; >> + ret = regmap_write(priv->regmap, zone_reg, als->zones_hi[i]); >> + if (ret) >> + return ret; >> + } >> + >> + als->config = (als->als_avrg_time | (LM3532_ENABLE_ALS) | >> + (als->als_input_mode << LM3532_ALS_SEL_SHIFT)); >> + >> + ret = regmap_write(priv->regmap, LM3532_ALS_CONFIG, als->config); >> + if (ret) >> + return ret; >> + >> + brightnes_config_reg = LM3532_REG_ZONE_CFG_A + led->control_bank * 2; >> + >> + return regmap_update_bits(priv->regmap, brightnes_config_reg, >> + LM3532_I2C_CTRL, LM3532_ALS_CTRL); >> +} >> + >> +static int lm3532_parse_als(struct lm3532_data *priv) >> +{ >> + struct lm3532_als_data *als; >> + int als_avg_time; >> + int als_impedance; >> + int ret; >> + >> + als = devm_kzalloc(priv->dev, sizeof(*als), GFP_KERNEL); >> + if (als == NULL) >> + return -ENOMEM; >> + >> + ret = device_property_read_u32(&priv->client->dev, "ti,als-vmin", >> + &als->als_vmin); >> + if (ret) >> + als->als_vmin = 0; >> + >> + ret = device_property_read_u32(&priv->client->dev, "ti,als-vmax", >> + &als->als_vmax); >> + if (ret) >> + als->als_vmax = LM3532_ALS_WINDOW_mV; >> + >> + if (als->als_vmax > LM3532_ALS_WINDOW_mV) { >> + ret = -EINVAL; >> + return ret; >> + } >> + >> + ret = device_property_read_u32(&priv->client->dev, "ti,als1-imp-sel", >> + &als_impedance); >> + if (ret) >> + als->als1_imp_sel = 0; >> + else >> + als->als1_imp_sel = lm3532_get_als_imp_index(als_impedance); >> + >> + ret = device_property_read_u32(&priv->client->dev, "ti,als2-imp-sel", >> + &als_impedance); >> + if (ret) >> + als->als2_imp_sel = 0; >> + else >> + als->als2_imp_sel = lm3532_get_als_imp_index(als_impedance); >> + >> + ret = device_property_read_u32(&priv->client->dev, "ti,als-avrg-time-us", >> + &als_avg_time); >> + if (ret) >> + als->als_avrg_time = 0; >> + else >> + als->als_avrg_time = lm3532_get_als_avg_index(als_avg_time); >> + >> + ret = device_property_read_u8(&priv->client->dev, "ti,als-input-mode", >> + &als->als_input_mode); >> + if (ret) >> + als->als_input_mode = 0; >> + >> + if (als->als_input_mode > LM3532_BL_MODE_ALS) { >> + ret = -EINVAL; >> + return ret; >> + } >> + >> + priv->als_data = als; >> + >> + return ret; >> +} >> + >> +static int lm3532_parse_node(struct lm3532_data *priv) >> +{ >> + struct fwnode_handle *child = NULL; >> + struct lm3532_led *led; >> + const char *name; >> + int control_bank; >> + u32 ramp_time; >> + size_t i = 0; >> + int ret; >> + >> + priv->enable_gpio = devm_gpiod_get_optional(&priv->client->dev, >> + "enable", GPIOD_OUT_LOW); >> + if (IS_ERR(priv->enable_gpio)) >> + priv->enable_gpio = NULL; >> + >> + priv->regulator = devm_regulator_get(&priv->client->dev, "vin"); >> + if (IS_ERR(priv->regulator)) >> + priv->regulator = NULL; >> + >> + ret = device_property_read_u32(&priv->client->dev, "ramp-up-us", >> + &ramp_time); >> + if (ret) >> + dev_info(&priv->client->dev, "ramp-up-ms property missing\n"); >> + else >> + priv->runtime_ramp_up = lm3532_get_ramp_index(ramp_time); >> + >> + ret = device_property_read_u32(&priv->client->dev, "ramp-down-us", >> + &ramp_time); >> + if (ret) >> + dev_info(&priv->client->dev, "ramp-down-ms property missing\n"); >> + else >> + priv->runtime_ramp_down = lm3532_get_ramp_index(ramp_time); >> + >> + device_for_each_child_node(priv->dev, child) { >> + led = &priv->leds[i]; >> + >> + ret = fwnode_property_read_u32(child, "reg", &control_bank); >> + if (ret) { >> + dev_err(&priv->client->dev, "reg property missing\n"); >> + fwnode_handle_put(child); >> + goto child_out; >> + } >> + >> + if (control_bank > LM3532_CONTROL_C) { >> + dev_err(&priv->client->dev, "Control bank invalid\n"); >> + continue; >> + } >> + >> + led->control_bank = control_bank; >> + >> + ret = fwnode_property_read_u32(child, "ti,led-mode", >> + &led->mode); >> + if (ret) { >> + dev_err(&priv->client->dev, "ti,led-mode property missing\n"); >> + fwnode_handle_put(child); >> + goto child_out; >> + } >> + >> + if (led->mode == LM3532_BL_MODE_ALS) { >> + ret = lm3532_parse_als(priv); >> + if (ret) >> + dev_err(&priv->client->dev, "Failed to parse als\n"); >> + else >> + lm3532_als_configure(priv, led); >> + } >> + >> + led->num_leds = fwnode_property_read_u32_array(child, >> + "led-sources", >> + NULL, 0); >> + >> + if (led->num_leds > LM3532_MAX_LED_STRINGS) { >> + dev_err(&priv->client->dev, "To many LED string defined\n"); >> + continue; >> + } >> + >> + ret = fwnode_property_read_u32_array(child, "led-sources", >> + led->led_strings, >> + led->num_leds); >> + if (ret) { >> + dev_err(&priv->client->dev, "led-sources property missing\n"); >> + fwnode_handle_put(child); >> + goto child_out; >> + } >> + >> + fwnode_property_read_string(child, "linux,default-trigger", >> + &led->led_dev.default_trigger); >> + >> + ret = fwnode_property_read_string(child, "label", &name); >> + if (ret) >> + snprintf(led->label, sizeof(led->label), >> + "%s::", priv->client->name); >> + else >> + snprintf(led->label, sizeof(led->label), >> + "%s:%s", priv->client->name, name); >> + >> + led->priv = priv; >> + led->led_dev.name = led->label; >> + led->led_dev.brightness_set_blocking = lm3532_brightness_set; >> + >> + ret = devm_led_classdev_register(priv->dev, &led->led_dev); >> + if (ret) { >> + dev_err(&priv->client->dev, "led register err: %d\n", >> + ret); >> + fwnode_handle_put(child); >> + goto child_out; >> + } >> + >> + lm3532_init_registers(led); >> + >> + i++; >> + } >> + >> +child_out: >> + return ret; >> +} >> + >> +static int lm3532_probe(struct i2c_client *client, >> + const struct i2c_device_id *id) >> +{ >> + struct lm3532_data *drvdata; >> + int ret = 0; >> + int count; >> + >> + count = device_get_child_node_count(&client->dev); >> + if (!count) { >> + dev_err(&client->dev, "LEDs are not defined in device tree!"); >> + return -ENODEV; >> + } >> + >> + drvdata = devm_kzalloc(&client->dev, struct_size(drvdata, leds, count), >> + GFP_KERNEL); >> + if (drvdata == NULL) >> + return -ENOMEM; >> + >> + drvdata->client = client; >> + drvdata->dev = &client->dev; >> + >> + drvdata->regmap = devm_regmap_init_i2c(client, &lm3532_regmap_config); >> + if (IS_ERR(drvdata->regmap)) { >> + ret = PTR_ERR(drvdata->regmap); >> + dev_err(&client->dev, "Failed to allocate register map: %d\n", >> + ret); >> + return ret; >> + } >> + >> + mutex_init(&drvdata->lock); >> + i2c_set_clientdata(client, drvdata); >> + >> + ret = lm3532_parse_node(drvdata); >> + if (ret) { >> + dev_err(&client->dev, "Failed to parse node\n"); >> + return ret; >> + } >> + >> + if (drvdata->enable_gpio) >> + gpiod_direction_output(drvdata->enable_gpio, 1); >> + >> + return ret; >> +} >> + >> +static int lm3532_remove(struct i2c_client *client) >> +{ >> + struct lm3532_data *drvdata = i2c_get_clientdata(client); > mutex_destroy(&drvdata->lock); > Ack. I give post v5 tomorrow. Dan >> + if (drvdata->enable_gpio) >> + gpiod_direction_output(drvdata->enable_gpio, 0); >> + >> + return 0; >> +} >> + >> +static const struct of_device_id of_lm3532_leds_match[] = { >> + { .compatible = "ti,lm3532", }, >> + {}, >> +}; >> +MODULE_DEVICE_TABLE(of, of_lm3532_leds_match); >> + >> +static const struct i2c_device_id lm3532_id[] = { >> + {LM3532_NAME, 0}, >> + {} >> +}; >> +MODULE_DEVICE_TABLE(i2c, lm3532_id); >> + >> +static struct i2c_driver lm3532_i2c_driver = { >> + .probe = lm3532_probe, >> + .remove = lm3532_remove, >> + .id_table = lm3532_id, >> + .driver = { >> + .name = LM3532_NAME, >> + .of_match_table = of_lm3532_leds_match, >> + }, >> +}; >> +module_i2c_driver(lm3532_i2c_driver); >> + >> +MODULE_DESCRIPTION("Back Light driver for LM3532"); >> +MODULE_LICENSE("GPL v2"); >> +MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>"); >> >
Jacek On 3/20/19 2:00 PM, Jacek Anaszewski wrote: > Hi Dan, > > On 3/20/19 1:39 PM, Dan Murphy wrote: >> Hello >> >> On 3/13/19 7:32 AM, Dan Murphy wrote: >>> Introduce the Texas Instruments LM3532 White LED driver. >>> The driver supports ALS configurability or manual brightness >>> control. >>> >>> The driver also supports associating LED strings with specific >>> control banks in a group or as individually controlled strings. >>> >> >> I have a v5 series ready for posting and only the DT was reviewed. >> >> Do we have any comments on the code? > > I've just added one comment. After it is addressed please submit v5. > Thank you. I will give it through the night for others to comment and I will post v5 tomorrow. Dan
On Wed, 20 Mar 2019, Jacek Anaszewski wrote: > Hi Dan, > > I have one minor issue below. > > On 3/13/19 1:32 PM, Dan Murphy wrote: > > Introduce the Texas Instruments LM3532 White LED driver. > > The driver supports ALS configurability or manual brightness > > control. > > > > The driver also supports associating LED strings with specific > > control banks in a group or as individually controlled strings. > > > > Signed-off-by: Dan Murphy <dmurphy@ti.com> > > --- > > > > v4 - Updated code to DT doc changes appending "ti," to TI specific properties - > > https://lore.kernel.org/patchwork/patch/1050124/ > > > > v3 - Removed switch case for register setting in favor of an algorithim (v1 comment), > > https://lore.kernel.org/patchwork/patch/1049024/ > > v2 - Added look up tables for als_avg, als_imp and ramp times, fixed brightness > > control when ALS is in control, added property value checks to ensure the values > > are not out of bounds - https://lore.kernel.org/patchwork/patch/1048807/ > > > > drivers/leds/Kconfig | 10 + > > drivers/leds/Makefile | 1 + > > drivers/leds/leds-lm3532.c | 681 +++++++++++++++++++++++++++++++++++++ > > 3 files changed, 692 insertions(+) > > create mode 100644 drivers/leds/leds-lm3532.c [snip] > > diff --git a/drivers/leds/leds-lm3532.c b/drivers/leds/leds-lm3532.c > > new file mode 100644 > > index 000000000000..e8a614d71469 > > --- /dev/null > > +++ b/drivers/leds/leds-lm3532.c [snip 700 lines of churn] Jacek could you please trim your replies please? It took way to long to locate your one line comment in this 700 line patch. > > +static int lm3532_remove(struct i2c_client *client) > > +{ > > + struct lm3532_data *drvdata = i2c_get_clientdata(client); > mutex_destroy(&drvdata->lock);
On Wed, 20 Mar 2019, Dan Murphy wrote: > On 3/20/19 1:59 PM, Jacek Anaszewski wrote: > > On 3/13/19 1:32 PM, Dan Murphy wrote: > >> Introduce the Texas Instruments LM3532 White LED driver. > >> The driver supports ALS configurability or manual brightness > >> control. > >> > >> The driver also supports associating LED strings with specific > >> control banks in a group or as individually controlled strings. > >> > >> Signed-off-by: Dan Murphy <dmurphy@ti.com> > >> --- > >> > >> v4 - Updated code to DT doc changes appending "ti," to TI specific properties - > >> https://lore.kernel.org/patchwork/patch/1050124/ > >> > >> v3 - Removed switch case for register setting in favor of an algorithim (v1 comment), > >> https://lore.kernel.org/patchwork/patch/1049024/ > >> v2 - Added look up tables for als_avg, als_imp and ramp times, fixed brightness > >> control when ALS is in control, added property value checks to ensure the values > >> are not out of bounds - https://lore.kernel.org/patchwork/patch/1048807/ > >> > >> drivers/leds/Kconfig | 10 + > >> drivers/leds/Makefile | 1 + > >> drivers/leds/leds-lm3532.c | 681 +++++++++++++++++++++++++++++++++++++ > >> 3 files changed, 692 insertions(+) > >> create mode 100644 drivers/leds/leds-lm3532.c [snip 700 lines of churn] > Ack. I give post v5 tomorrow. Same for you too please Dan.
diff --git a/Documentation/devicetree/bindings/leds/leds-lm3532.txt b/Documentation/devicetree/bindings/leds/leds-lm3532.txt new file mode 100644 index 000000000000..ba793ef9b3b6 --- /dev/null +++ b/Documentation/devicetree/bindings/leds/leds-lm3532.txt @@ -0,0 +1,101 @@ +* Texas Instruments - lm3532 White LED driver with ambient light sensing +capability. + +The LM3532 provides the 3 high-voltage, low-side current sinks. The device is +programmable over an I2C-compatible interface and has independent +current control for all three channels. The adaptive current regulation +method allows for different LED currents in each current sink thus allowing +for a wide variety of backlight and keypad applications. + +The main features of the LM3532 include dual ambient light sensor inputs +each with 32 internal voltage setting resistors, 8-bit logarithmic and linear +brightness control, dual external PWM brightness control inputs, and up to +1000:1 dimming ratio with programmable fade in and fade out settings. + +Required properties: + - compatible : "ti,lm3532" + - reg : I2C slave address + - #address-cells : 1 + - #size-cells : 0 + +Optional properties: + - enable-gpios : gpio pin to enable (active high)/disable the device. + Range for ramp settings: 8us - 65536us + - ramp-up-us - The Run time ramp rates/step are from one current + set-point to another after the device has reached its + initial target set point from turn-on + - ramp-down-us - The Run time ramp rates/step are from one current + set-point to another after the device has reached its + initial target set point from turn-on + +Optional properties if ALS mode is used: + - ti,als-vmin - Minimum ALS voltage defined in Volts + - ti,als-vmax - Maximum ALS voltage defined in Volts + Per the data sheet the max ALS voltage is 2V and the min is 0V + + - ti,als1-imp-sel - ALS1 impedance resistor selection in Ohms + - ti,als2-imp-sel - ALS2 impedance resistor selection in Ohms + Range for impedance select: 37000 Ohms - 1190 Ohms + Values above 37kohms will be set to the "High Impedance" setting + + - ti,als-avrg-time-us - Determines the length of time the device needs to + average the two ALS inputs. This is only used if + the input mode is LM3532_ALS_INPUT_AVRG. + Range: 17920us - 2293760us + - ti,als-input-mode - Determines how the device uses the attached ALS + devices. + 0x00 - ALS1 and ALS2 input average + 0x01 - ALS1 Input + 0x02 - ALS2 Input + 0x03 - Max of ALS1 and ALS2 + +Required child properties: + - reg : Indicates control bank the LED string is controlled by + - led-sources : see Documentation/devicetree/bindings/leds/common.txt + - ti,led-mode : Defines if the LED strings are manually controlled or + if the LED strings are controlled by the ALS. + 0x00 - LED strings are I2C controlled via full scale + brightness control register + 0x01 - LED strings are ALS controlled + +Optional LED child properties: + - label : see Documentation/devicetree/bindings/leds/common.txt + - linux,default-trigger : + see Documentation/devicetree/bindings/leds/common.txt + +Example: +led-controller@38 { + compatible = "ti,lm3532"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x38>; + + enable-gpios = <&gpio6 12 GPIO_ACTIVE_HIGH>; + ramp-up-ms = <1024>; + ramp-down-ms = <65536>; + + ti,als-vmin = <0>; + ti,als-vmax = <2000>; + ti,als1-imp-sel = <4110>; + ti,als2-imp-sel = <2180>; + ti,als-avrg-time-us = <17920>; + ti,als-input-mode = <0x00>; + + lcd_backlight: led@0 { + reg = <0>; + led-sources = <2>; + ti,led-mode = <1>; + label = "backlight"; + linux,default-trigger = "backlight"; + }; + + led@1 { + reg = <1>; + led-sources = <1>; + ti,led-mode = <0>; + label = "keypad"; + }; +}; + +For more product information please see the links below: +http://www.ti.com/product/LM3532 diff --git a/Documentation/devicetree/bindings/mfd/ti-lmu.txt b/Documentation/devicetree/bindings/mfd/ti-lmu.txt index c885cf89b8ce..980394d701a7 100644 --- a/Documentation/devicetree/bindings/mfd/ti-lmu.txt +++ b/Documentation/devicetree/bindings/mfd/ti-lmu.txt @@ -4,7 +4,6 @@ TI LMU driver supports lighting devices below. Name Child nodes ------ --------------------------------- - LM3532 Backlight LM3631 Backlight and regulator LM3632 Backlight and regulator LM3633 Backlight, LED and fault monitor @@ -13,7 +12,6 @@ TI LMU driver supports lighting devices below. Required properties: - compatible: Should be one of: - "ti,lm3532" "ti,lm3631" "ti,lm3632" "ti,lm3633" @@ -23,7 +21,6 @@ Required properties: 0x11 for LM3632 0x29 for LM3631 0x36 for LM3633, LM3697 - 0x38 for LM3532 0x63 for LM3695 Optional property: @@ -47,23 +44,6 @@ Optional nodes: [2] ../leds/leds-lm3633.txt [3] ../regulator/lm363x-regulator.txt -lm3532@38 { - compatible = "ti,lm3532"; - reg = <0x38>; - - enable-gpios = <&pioC 2 GPIO_ACTIVE_HIGH>; - - backlight { - compatible = "ti,lm3532-backlight"; - - lcd { - led-sources = <0 1 2>; - ramp-up-msec = <30>; - ramp-down-msec = <0>; - }; - }; -}; - lm3631@29 { compatible = "ti,lm3631"; reg = <0x29>;
Add the lm3532 device tree documentation. Remove lm3532 device tree reference from the ti_lmu devicetree documentation. With the addition of the dedicated lm3532 documentation the device can be removed from the ti_lmu.txt. The reason for this is that the lm3532 dt documentation now defines the ability to control LED output strings against different control banks or groups multiple strings to be controlled by a single control bank. Another addition was for ALS lighting control and configuration. The LM3532 has a feature that can take in the ALS reading from 2 separate ALS devices and adjust the brightness on the strings that are configured to support this feature. Finally the device specific properties were moved to the parent node as these properties are not control bank configurable. These include the runtime ramp and the ALS configuration. Signed-off-by: Dan Murphy <dmurphy@ti.com> --- v4 - Appended "ti," to TI specific properties, add enable gpio documentation, removed an example, moved ramp to optional parent properties - https://lore.kernel.org/patchwork/patch/1050122/ v3 - No changes - https://lore.kernel.org/patchwork/patch/1049026/ v2 - Fixed ramp-up and ramp-down properties, removed hard coded property values, added ranges for variable properties, I did not change the label - https://lore.kernel.org/patchwork/patch/1048805/ .../devicetree/bindings/leds/leds-lm3532.txt | 101 ++++++++++++++++++ .../devicetree/bindings/mfd/ti-lmu.txt | 20 ---- 2 files changed, 101 insertions(+), 20 deletions(-) create mode 100644 Documentation/devicetree/bindings/leds/leds-lm3532.txt