All of lore.kernel.org
 help / color / mirror / Atom feed
From: Lee Jones <lee.jones@linaro.org>
To: Milo Kim <milo.kim@ti.com>
Cc: robh+dt@kernel.org, j.anaszewski@samsung.com, broonie@kernel.org,
	devicetree@vger.kernel.org, linux-leds@vger.kernel.org,
	linux-kernel@vger.kernel.org
Subject: Re: [PATCH v2 7/9] backlight: add TI LMU backlight driver
Date: Mon, 11 Jan 2016 09:57:02 +0000	[thread overview]
Message-ID: <20160111095702.GS14104@x1> (raw)
In-Reply-To: <1448521025-2796-8-git-send-email-milo.kim@ti.com>

Jingoo (not CC'ed) needs to review this.

> This is consolidated driver which supports backlight devices below.
>   LM3532, LM3631, LM3632, LM3633, LM3695 and LM3697.
> 
> Structure
> ---------
>   It consists of two parts - core and data.
> 
>   Core part supports features below.
>     - Backlight subsystem control
>     - Channel configuration from DT properties
>     - Light dimming effect control: ramp up and down.
>     - LMU fault monitor notifier handling
>     - PWM brightness control
> 
>   Data part describes device specific data.
>     - Register value configuration for each LMU device
>       : initialization, channel configuration, control mode, enable and
>         brightness.
>     - PWM action configuration
>     - Light dimming effect table
>     - Option for LMU fault monitor support
> 
> Macros for register data
> ------------------------
>   All LMU devices have 8-bit based registers. LMU_BL_REG() creates 24-bit
>   register value in data part. It consists of address, mask and value.
>   On the other hand, register value should be parsed when the driver
>   reads/writes data from/to I2C registers. Driver uses LMU_BL_GET_ADDR(),
>   LMU_BL_GET_MASK() and LMU_BL_GET_VAL() for this purpose.
> 
> Data structure
> --------------
>   ti_lmu_bl:         Backlight output channel data
>   ti_lmu_bl_chip:    Backlight device data. One device can have multiple
>                      backlight channel data.
>   ti_lmu_bl_reg:     Backlight device register data
>   ti_lmu_bl_cfg:     Backlight configuration data for each LMU device
> 
> Cc: Lee Jones <lee.jones@linaro.org> 
> Cc: Jacek Anaszewski <j.anaszewski@samsung.com>
> Cc: Mark Brown <broonie@kernel.org>
> Cc: Rob Herring <robh+dt@kernel.org>
> Cc: devicetree@vger.kernel.org
> Cc: linux-leds@vger.kernel.org 
> Cc: linux-kernel@vger.kernel.org
> Signed-off-by: Milo Kim <milo.kim@ti.com>
> ---
>  drivers/video/backlight/Kconfig                 |   7 +
>  drivers/video/backlight/Makefile                |   3 +
>  drivers/video/backlight/ti-lmu-backlight-core.c | 649 ++++++++++++++++++++++++
>  drivers/video/backlight/ti-lmu-backlight-data.c | 287 +++++++++++
>  include/linux/mfd/ti-lmu-backlight.h            | 290 +++++++++++
>  5 files changed, 1236 insertions(+)
>  create mode 100644 drivers/video/backlight/ti-lmu-backlight-core.c
>  create mode 100644 drivers/video/backlight/ti-lmu-backlight-data.c
>  create mode 100644 include/linux/mfd/ti-lmu-backlight.h
> 
> diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig
> index 5ffa4b4..451d043 100644
> --- a/drivers/video/backlight/Kconfig
> +++ b/drivers/video/backlight/Kconfig
> @@ -427,6 +427,13 @@ config BACKLIGHT_SKY81452
>  	  To compile this driver as a module, choose M here: the module will
>  	  be called sky81452-backlight
>  
> +config BACKLIGHT_TI_LMU
> +	tristate "Backlight driver for TI LMU"
> +	depends on BACKLIGHT_CLASS_DEVICE && MFD_TI_LMU
> +	help
> +	  Say Y to enable the backlight driver for TI LMU devices.
> +	  This supports LM3532, LM3631, LM3632, LM3633, LM3695 and LM3697.
> +
>  config BACKLIGHT_TPS65217
>  	tristate "TPS65217 Backlight"
>  	depends on BACKLIGHT_CLASS_DEVICE && MFD_TPS65217
> diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile
> index 16ec534..0f74ce7 100644
> --- a/drivers/video/backlight/Makefile
> +++ b/drivers/video/backlight/Makefile
> @@ -52,6 +52,9 @@ obj-$(CONFIG_BACKLIGHT_PM8941_WLED)	+= pm8941-wled.o
>  obj-$(CONFIG_BACKLIGHT_PWM)		+= pwm_bl.o
>  obj-$(CONFIG_BACKLIGHT_SAHARA)		+= kb3886_bl.o
>  obj-$(CONFIG_BACKLIGHT_SKY81452)	+= sky81452-backlight.o
> +ti-lmu-backlight-objs			:= ti-lmu-backlight-core.o \
> +					   ti-lmu-backlight-data.o
> +obj-$(CONFIG_BACKLIGHT_TI_LMU)		+= ti-lmu-backlight.o
>  obj-$(CONFIG_BACKLIGHT_TOSA)		+= tosa_bl.o
>  obj-$(CONFIG_BACKLIGHT_TPS65217)	+= tps65217_bl.o
>  obj-$(CONFIG_BACKLIGHT_WM831X)		+= wm831x_bl.o
> diff --git a/drivers/video/backlight/ti-lmu-backlight-core.c b/drivers/video/backlight/ti-lmu-backlight-core.c
> new file mode 100644
> index 0000000..838e2c2
> --- /dev/null
> +++ b/drivers/video/backlight/ti-lmu-backlight-core.c
> @@ -0,0 +1,649 @@
> +/*
> + * TI LMU (Lighting Management Unit) Backlight Driver
> + *
> + * Copyright 2015 Texas Instruments
> + *
> + * Author: Milo Kim <milo.kim@ti.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#include <linux/backlight.h>
> +#include <linux/bitops.h>
> +#include <linux/delay.h>
> +#include <linux/err.h>
> +#include <linux/kernel.h>
> +#include <linux/mfd/ti-lmu.h>
> +#include <linux/mfd/ti-lmu-backlight.h>
> +#include <linux/mfd/ti-lmu-register.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/platform_device.h>
> +#include <linux/pwm.h>
> +#include <linux/slab.h>
> +
> +#define NUM_DUAL_CHANNEL			2
> +#define LMU_BACKLIGHT_DUAL_CHANNEL_USED		(BIT(0) | BIT(1))
> +#define LMU_BACKLIGHT_11BIT_LSB_MASK		(BIT(0) | BIT(1) | BIT(2))
> +#define LMU_BACKLIGHT_11BIT_MSB_SHIFT		3
> +#define DEFAULT_PWM_NAME			"lmu-backlight"
> +
> +static int ti_lmu_backlight_enable(struct ti_lmu_bl *lmu_bl, int enable)
> +{
> +	struct ti_lmu_bl_chip *chip = lmu_bl->chip;
> +	struct regmap *regmap = chip->lmu->regmap;
> +	unsigned long enable_time = chip->cfg->reginfo->enable_usec;
> +	u8 *reg = chip->cfg->reginfo->enable;
> +	u8 mask = BIT(lmu_bl->bank_id);
> +
> +	if (!reg)
> +		return -EINVAL;
> +
> +	if (enable)
> +		return regmap_update_bits(regmap, *reg, mask, mask);
> +	else
> +		return regmap_update_bits(regmap, *reg, mask, 0);
> +
> +	if (enable_time > 0)
> +		usleep_range(enable_time, enable_time + 100);
> +}
> +
> +static void ti_lmu_backlight_pwm_ctrl(struct ti_lmu_bl *lmu_bl, int brightness,
> +				      int max_brightness)
> +{
> +	struct pwm_device *pwm;
> +	unsigned int duty, period;
> +
> +	if (!lmu_bl->pwm) {
> +		pwm = devm_pwm_get(lmu_bl->chip->dev, DEFAULT_PWM_NAME);
> +		if (IS_ERR(pwm)) {
> +			dev_err(lmu_bl->chip->dev,
> +				"Can not get PWM device, err: %ld\n",
> +				PTR_ERR(pwm));
> +			return;
> +		}
> +
> +		lmu_bl->pwm = pwm;
> +	}
> +
> +	period = lmu_bl->pwm_period;
> +	duty = brightness * period / max_brightness;
> +
> +	pwm_config(lmu_bl->pwm, duty, period);
> +	if (duty)
> +		pwm_enable(lmu_bl->pwm);
> +	else
> +		pwm_disable(lmu_bl->pwm);
> +}
> +
> +static int ti_lmu_backlight_update_brightness_register(struct ti_lmu_bl *lmu_bl,
> +						       int brightness)
> +{
> +	const struct ti_lmu_bl_cfg *cfg = lmu_bl->chip->cfg;
> +	const struct ti_lmu_bl_reg *reginfo = cfg->reginfo;
> +	struct regmap *regmap = lmu_bl->chip->lmu->regmap;
> +	u8 reg, val;
> +	int ret;
> +
> +	if (lmu_bl->mode == BL_PWM_BASED) {
> +		switch (cfg->pwm_action) {
> +		case UPDATE_PWM_ONLY:
> +			/* No register update is required */
> +			return 0;
> +		case UPDATE_MAX_BRT:
> +			/*
> +			 * PWM can start from any non-zero code and dim down
> +			 * to zero. So, brightness register should be updated
> +			 * even in PWM mode.
> +			 */
> +			if (brightness > 0)
> +				brightness = MAX_BRIGHTNESS_11BIT;
> +			else
> +				brightness = 0;
> +			break;
> +		default:
> +			break;
> +		}
> +	}
> +
> +	/*
> +	 * Brightness register update
> +	 *
> +	 * 11 bit dimming: update LSB bits and write MSB byte.
> +	 *		   MSB brightness should be shifted.
> +	 *  8 bit dimming: write MSB byte.
> +	 */
> +
> +	if (!reginfo->brightness_msb)
> +		return -EINVAL;
> +
> +	if (cfg->max_brightness == MAX_BRIGHTNESS_11BIT) {
> +		if (!reginfo->brightness_lsb)
> +			return -EINVAL;
> +
> +		reg = reginfo->brightness_lsb[lmu_bl->bank_id];
> +		ret = regmap_update_bits(regmap, reg,
> +					 LMU_BACKLIGHT_11BIT_LSB_MASK,
> +					 brightness);
> +		if (ret)
> +			return ret;
> +
> +		val = (brightness >> LMU_BACKLIGHT_11BIT_MSB_SHIFT) & 0xFF;
> +	} else {
> +		val = brightness & 0xFF;
> +	}
> +
> +	reg = reginfo->brightness_msb[lmu_bl->bank_id];
> +	return regmap_write(regmap, reg, val);
> +}
> +
> +static int ti_lmu_backlight_update_status(struct backlight_device *bl_dev)
> +{
> +	struct ti_lmu_bl *lmu_bl = bl_get_data(bl_dev);
> +	int brightness = bl_dev->props.brightness;
> +	int ret;
> +
> +	if (bl_dev->props.state & BL_CORE_SUSPENDED)
> +		brightness = 0;
> +
> +	if (brightness > 0)
> +		ret = ti_lmu_backlight_enable(lmu_bl, 1);
> +	else
> +		ret = ti_lmu_backlight_enable(lmu_bl, 0);
> +
> +	if (ret)
> +		return ret;
> +
> +	if (lmu_bl->mode == BL_PWM_BASED)
> +		ti_lmu_backlight_pwm_ctrl(lmu_bl, brightness,
> +					  bl_dev->props.max_brightness);
> +
> +	return ti_lmu_backlight_update_brightness_register(lmu_bl, brightness);
> +}
> +
> +static const struct backlight_ops lmu_backlight_ops = {
> +	.options = BL_CORE_SUSPENDRESUME,
> +	.update_status = ti_lmu_backlight_update_status,
> +};
> +
> +static int ti_lmu_backlight_of_get_ctrl_bank(struct device_node *np,
> +					     struct ti_lmu_bl *lmu_bl)
> +{
> +	const char *name;
> +	u32 *sources;
> +	int num_channels = lmu_bl->chip->cfg->num_channels;
> +	int ret, num_sources;
> +
> +	sources = devm_kzalloc(lmu_bl->chip->dev, num_channels, GFP_KERNEL);
> +	if (!sources)
> +		return -ENOMEM;
> +
> +	if (!of_property_read_string(np, "label", &name))
> +		lmu_bl->name = name;
> +	else
> +		lmu_bl->name = np->name;
> +
> +	ret = of_property_count_u32_elems(np, "led-sources");
> +	if (ret < 0 || ret > num_channels)
> +		return -EINVAL;
> +
> +	num_sources = ret;
> +	ret = of_property_read_u32_array(np, "led-sources", sources,
> +					 num_sources);
> +	if (ret)
> +		return ret;
> +
> +	lmu_bl->led_sources = 0;
> +	while (num_sources--)
> +		set_bit(sources[num_sources], &lmu_bl->led_sources);
> +
> +	return 0;
> +}
> +
> +static void ti_lmu_backlight_of_get_light_properties(struct device_node *np,
> +						     struct ti_lmu_bl *lmu_bl)
> +{
> +	of_property_read_u32(np, "default-brightness-level",
> +			     &lmu_bl->default_brightness);
> +
> +	of_property_read_u32(np, "ramp-up-msec",  &lmu_bl->ramp_up_msec);
> +	of_property_read_u32(np, "ramp-down-msec", &lmu_bl->ramp_down_msec);
> +}
> +
> +static void ti_lmu_backlight_of_get_brightness_mode(struct device_node *np,
> +						    struct ti_lmu_bl *lmu_bl)
> +{
> +	of_property_read_u32(np, "pwm-period", &lmu_bl->pwm_period);
> +
> +	if (lmu_bl->pwm_period > 0)
> +		lmu_bl->mode = BL_PWM_BASED;
> +	else
> +		lmu_bl->mode = BL_REGISTER_BASED;
> +}
> +
> +static int ti_lmu_backlight_of_create(struct ti_lmu_bl_chip *chip,
> +				      struct device_node *np)
> +{
> +	struct device_node *child;
> +	struct ti_lmu_bl *lmu_bl, *each;
> +	int ret, num_backlights;
> +	int i = 0;
> +
> +	num_backlights = of_get_child_count(np);
> +	if (num_backlights == 0) {
> +		dev_err(chip->dev, "No backlight strings\n");
> +		return -ENODEV;
> +	}
> +
> +	/* One chip can have mulitple backlight strings */
> +	lmu_bl = devm_kzalloc(chip->dev, sizeof(*lmu_bl) * num_backlights,
> +			      GFP_KERNEL);
> +	if (!lmu_bl)
> +		return -ENOMEM;
> +
> +	/* Child is mapped to LMU backlight control bank */
> +	for_each_child_of_node(np, child) {
> +		each = lmu_bl + i;
> +		each->bank_id = i;
> +		each->chip = chip;
> +
> +		ret = ti_lmu_backlight_of_get_ctrl_bank(child, each);
> +		if (ret) {
> +			of_node_put(np);
> +			return ret;
> +		}
> +
> +		ti_lmu_backlight_of_get_light_properties(child, each);
> +		ti_lmu_backlight_of_get_brightness_mode(child, each);
> +
> +		i++;
> +	}
> +
> +	chip->lmu_bl = lmu_bl;
> +	chip->num_backlights = num_backlights;
> +
> +	return 0;
> +}
> +
> +static int ti_lmu_backlight_create_channel(struct ti_lmu_bl *lmu_bl)
> +{
> +	struct regmap *regmap = lmu_bl->chip->lmu->regmap;
> +	u32 *reg = lmu_bl->chip->cfg->reginfo->channel;
> +	int num_channels = lmu_bl->chip->cfg->num_channels;
> +	int i, ret;
> +	u8 shift;
> +
> +	/*
> +	 * How to create backlight output channels:
> +	 *   Check 'led_sources' bit and update registers.
> +	 *
> +	 *   1) Dual channel configuration
> +	 *     The 1st register data is used for single channel.
> +	 *     The 2nd register data is used for dual channel.
> +	 *
> +	 *   2) Multiple channel configuration
> +	 *     Each register data is mapped to bank ID.
> +	 *     Bit shift operation is defined in channel registers.
> +	 *
> +	 * Channel register data consists of address, mask, value.
> +	 * Driver can get each data by using LMU_BL_GET_ADDR(),
> +	 * LMU_BL_GET_MASK(), LMU_BL_GET_VAL().
> +	 */
> +
> +	if (num_channels == NUM_DUAL_CHANNEL) {
> +		if (lmu_bl->led_sources == LMU_BACKLIGHT_DUAL_CHANNEL_USED)
> +			++reg;
> +
> +		return regmap_update_bits(regmap, LMU_BL_GET_ADDR(*reg),
> +					  LMU_BL_GET_MASK(*reg),
> +					  LMU_BL_GET_VAL(*reg));
> +	}
> +
> +	for (i = 0; i < num_channels; i++) {
> +		if (!reg)
> +			break;
> +
> +		/*
> +		 * Note that the result of LMU_BL_GET_VAL() is shift bit.
> +		 * The bank_id should be shifted for the channel configuration.
> +		 */
> +		if (test_bit(i, &lmu_bl->led_sources)) {
> +			shift = LMU_BL_GET_VAL(*reg);
> +			ret = regmap_update_bits(regmap, LMU_BL_GET_ADDR(*reg),
> +						 LMU_BL_GET_MASK(*reg),
> +						 lmu_bl->bank_id << shift);
> +			if (ret)
> +				return ret;
> +		}
> +
> +		reg++;
> +	}
> +
> +	return 0;
> +}
> +
> +static int ti_lmu_backlight_update_ctrl_mode(struct ti_lmu_bl *lmu_bl)
> +{
> +	struct regmap *regmap = lmu_bl->chip->lmu->regmap;
> +	u32 *reg = lmu_bl->chip->cfg->reginfo->mode + lmu_bl->bank_id;
> +	u8 val;
> +
> +	if (!reg)
> +		return 0;
> +
> +	/*
> +	 * Update PWM configuration register.
> +	 * If the mode is register based, then clear the bit.
> +	 */
> +	if (lmu_bl->mode == BL_PWM_BASED)
> +		val = LMU_BL_GET_VAL(*reg);
> +	else
> +		val = 0;
> +
> +	return regmap_update_bits(regmap, LMU_BL_GET_ADDR(*reg),
> +				  LMU_BL_GET_MASK(*reg), val);
> +}
> +
> +static int ti_lmu_backlight_convert_ramp_to_index(struct ti_lmu_bl *lmu_bl,
> +						  enum ti_lmu_bl_ramp_mode mode)
> +{
> +	const int *ramp_table = lmu_bl->chip->cfg->ramp_table;
> +	const int size = lmu_bl->chip->cfg->size_ramp;
> +	unsigned int msec;
> +	int i;
> +
> +	if (!ramp_table)
> +		return -EINVAL;
> +
> +	switch (mode) {
> +	case BL_RAMP_UP:
> +		msec = lmu_bl->ramp_up_msec;
> +		break;
> +	case BL_RAMP_DOWN:
> +		msec = lmu_bl->ramp_down_msec;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	if (msec <= ramp_table[0])
> +		return 0;
> +
> +	if (msec > ramp_table[size - 1])
> +		return size - 1;
> +
> +	for (i = 1; i < size; i++) {
> +		if (msec == ramp_table[i])
> +			return i;
> +
> +		/* Find an approximate index by looking up the table */
> +		if (msec > ramp_table[i - 1] && msec < ramp_table[i]) {
> +			if (msec - ramp_table[i - 1] < ramp_table[i] - msec)
> +				return i - 1;
> +			else
> +				return i;
> +		}
> +	}
> +
> +	return -EINVAL;
> +}
> +
> +static int ti_lmu_backlight_set_ramp(struct ti_lmu_bl *lmu_bl)
> +{
> +	struct regmap *regmap = lmu_bl->chip->lmu->regmap;
> +	const struct ti_lmu_bl_reg *reginfo = lmu_bl->chip->cfg->reginfo;
> +	int offset = reginfo->ramp_reg_offset;
> +	int i, ret, index;
> +	u32 reg;
> +
> +	for (i = BL_RAMP_UP; i <= BL_RAMP_DOWN; i++) {
> +		index = ti_lmu_backlight_convert_ramp_to_index(lmu_bl, i);
> +		if (index > 0) {
> +			if (!reginfo->ramp)
> +				break;
> +
> +			if (lmu_bl->bank_id == 0)
> +				reg = reginfo->ramp[i];
> +			else
> +				reg = reginfo->ramp[i] + offset;
> +
> +			/*
> +			 * Note that the result of LMU_BL_GET_VAL() is
> +			 * shift bit. So updated bit is shifted index value.
> +			 */
> +			ret = regmap_update_bits(regmap, LMU_BL_GET_ADDR(reg),
> +						 LMU_BL_GET_MASK(reg),
> +						 index << LMU_BL_GET_VAL(reg));
> +			if (ret)
> +				return ret;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int ti_lmu_backlight_configure(struct ti_lmu_bl *lmu_bl)
> +{
> +	int ret;
> +
> +	ret = ti_lmu_backlight_create_channel(lmu_bl);
> +	if (ret)
> +		return ret;
> +
> +	ret = ti_lmu_backlight_update_ctrl_mode(lmu_bl);
> +	if (ret)
> +		return ret;
> +
> +	return ti_lmu_backlight_set_ramp(lmu_bl);
> +}
> +
> +static int ti_lmu_backlight_init(struct ti_lmu_bl_chip *chip)
> +{
> +	struct regmap *regmap = chip->lmu->regmap;
> +	u32 *reg = chip->cfg->reginfo->init;
> +	int num_init = chip->cfg->reginfo->num_init;
> +	int i, ret;
> +
> +	/*
> +	 * 'init' register data consists of address, mask, value.
> +	 * Driver can get each data by using LMU_BL_GET_ADDR(),
> +	 * LMU_BL_GET_MASK(), LMU_BL_GET_VAL().
> +	 */
> +
> +	for (i = 0; i < num_init; i++) {
> +		if (!reg)
> +			break;
> +
> +		ret = regmap_update_bits(regmap, LMU_BL_GET_ADDR(*reg),
> +					 LMU_BL_GET_MASK(*reg),
> +					 LMU_BL_GET_VAL(*reg));
> +		if (ret)
> +			return ret;
> +
> +		reg++;
> +	}
> +
> +	return 0;
> +}
> +
> +static int ti_lmu_backlight_reload(struct ti_lmu_bl_chip *chip)
> +{
> +	struct ti_lmu_bl *each;
> +	int i, ret;
> +
> +	ret = ti_lmu_backlight_init(chip);
> +	if (ret)
> +		return ret;
> +
> +	for (i = 0; i < chip->num_backlights; i++) {
> +		each = chip->lmu_bl + i;
> +		ret = ti_lmu_backlight_configure(each);
> +		if (ret)
> +			return ret;
> +
> +		backlight_update_status(each->bl_dev);
> +	}
> +
> +	return 0;
> +}
> +
> +static int ti_lmu_backlight_add_device(struct device *dev,
> +				       struct ti_lmu_bl *lmu_bl)
> +{
> +	struct backlight_device *bl_dev;
> +	struct backlight_properties props;
> +
> +	memset(&props, 0, sizeof(struct backlight_properties));
> +	props.type = BACKLIGHT_PLATFORM;
> +	props.brightness = lmu_bl->default_brightness;
> +	props.max_brightness = lmu_bl->chip->cfg->max_brightness;
> +
> +	bl_dev = devm_backlight_device_register(dev, lmu_bl->name,
> +						lmu_bl->chip->dev, lmu_bl,
> +						&lmu_backlight_ops, &props);
> +	if (IS_ERR(bl_dev))
> +		return PTR_ERR(bl_dev);
> +
> +	lmu_bl->bl_dev = bl_dev;
> +
> +	return 0;
> +}
> +
> +static struct ti_lmu_bl_chip *
> +ti_lmu_backlight_register(struct device *dev, struct ti_lmu *lmu,
> +			  const struct ti_lmu_bl_cfg *cfg)
> +{
> +	struct ti_lmu_bl_chip *chip;
> +	struct ti_lmu_bl *each;
> +	int i, ret;
> +
> +	if (!cfg) {
> +		dev_err(dev, "Operation is not configured\n");
> +		return ERR_PTR(-EINVAL);
> +	}
> +
> +	chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
> +	if (!chip)
> +		return ERR_PTR(-ENOMEM);
> +
> +	chip->dev = dev;
> +	chip->lmu = lmu;
> +	chip->cfg = cfg;
> +
> +	ret = ti_lmu_backlight_of_create(chip, dev->of_node);
> +	if (ret)
> +		return ERR_PTR(ret);
> +
> +	ret = ti_lmu_backlight_init(chip);
> +	if (ret) {
> +		dev_err(dev, "Backlight init err: %d\n", ret);
> +		return ERR_PTR(ret);
> +	}
> +
> +	for (i = 0; i < chip->num_backlights; i++) {
> +		each = chip->lmu_bl + i;
> +
> +		ret = ti_lmu_backlight_configure(each);
> +		if (ret) {
> +			dev_err(dev, "Backlight config err: %d\n", ret);
> +			return ERR_PTR(ret);
> +		}
> +
> +		ret = ti_lmu_backlight_add_device(dev, each);
> +		if (ret) {
> +			dev_err(dev, "Backlight device err: %d\n", ret);
> +			return ERR_PTR(ret);
> +		}
> +
> +		backlight_update_status(each->bl_dev);
> +	}
> +
> +	return chip;
> +}
> +
> +static void ti_lmu_backlight_unregister(struct ti_lmu_bl_chip *chip)
> +{
> +	struct ti_lmu_bl *each;
> +	int i;
> +
> +	/* Turn off the brightness */
> +	for (i = 0; i < chip->num_backlights; i++) {
> +		each = chip->lmu_bl + i;
> +		each->bl_dev->props.brightness = 0;
> +		backlight_update_status(each->bl_dev);
> +	}
> +}
> +
> +static int ti_lmu_backlight_monitor_notifier(struct notifier_block *nb,
> +					     unsigned long action, void *unused)
> +{
> +	struct ti_lmu_bl_chip *chip = container_of(nb, struct ti_lmu_bl_chip,
> +						   nb);
> +
> +	if (action == LMU_EVENT_MONITOR_DONE) {
> +		if (ti_lmu_backlight_reload(chip))
> +			return NOTIFY_STOP;
> +	}
> +
> +	return NOTIFY_OK;
> +}
> +
> +static int ti_lmu_backlight_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct ti_lmu *lmu = dev_get_drvdata(dev->parent);
> +	struct ti_lmu_bl_chip *chip;
> +	int ret;
> +
> +	chip = ti_lmu_backlight_register(dev, lmu, &lmu_bl_cfg[pdev->id]);
> +	if (IS_ERR(chip))
> +		return PTR_ERR(chip);
> +
> +	/*
> +	 * Notifier callback is required because backlight device needs
> +	 * reconfiguration after fault detection procedure is done by
> +	 * ti-lmu-fault-monitor driver.
> +	 */
> +	if (chip->cfg->fault_monitor_used) {
> +		chip->nb.notifier_call = ti_lmu_backlight_monitor_notifier;
> +		ret = blocking_notifier_chain_register(&chip->lmu->notifier,
> +						       &chip->nb);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	platform_set_drvdata(pdev, chip);
> +
> +	return 0;
> +}
> +
> +static int ti_lmu_backlight_remove(struct platform_device *pdev)
> +{
> +	struct ti_lmu_bl_chip *chip = platform_get_drvdata(pdev);
> +
> +	if (chip->cfg->fault_monitor_used)
> +		blocking_notifier_chain_unregister(&chip->lmu->notifier,
> +						   &chip->nb);
> +
> +	ti_lmu_backlight_unregister(chip);
> +
> +	return 0;
> +}
> +
> +static struct platform_driver ti_lmu_backlight_driver = {
> +	.probe  = ti_lmu_backlight_probe,
> +	.remove = ti_lmu_backlight_remove,
> +	.driver = {
> +		.name = "ti-lmu-backlight",
> +	},
> +};
> +
> +module_platform_driver(ti_lmu_backlight_driver)
> +
> +MODULE_DESCRIPTION("TI LMU Backlight Driver");
> +MODULE_AUTHOR("Milo Kim");
> +MODULE_LICENSE("GPL v2");
> +MODULE_ALIAS("platform:ti-lmu-backlight");
> diff --git a/drivers/video/backlight/ti-lmu-backlight-data.c b/drivers/video/backlight/ti-lmu-backlight-data.c
> new file mode 100644
> index 0000000..0a486b2
> --- /dev/null
> +++ b/drivers/video/backlight/ti-lmu-backlight-data.c
> @@ -0,0 +1,287 @@
> +/*
> + * TI LMU (Lighting Management Unit) Backlight Device Data
> + *
> + * Copyright 2015 Texas Instruments
> + *
> + * Author: Milo Kim <milo.kim@ti.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/mfd/ti-lmu.h>
> +#include <linux/mfd/ti-lmu-backlight.h>
> +#include <linux/mfd/ti-lmu-register.h>
> +#include <linux/module.h>
> +
> +/* LM3532 */
> +static u32 lm3532_init_regs[] = {
> +	LM3532_INIT_ZONE_0,
> +	LM3532_INIT_ZONE_1,
> +	LM3532_INIT_ZONE_2,
> +};
> +
> +static u32 lm3532_channel_regs[] = {
> +	LM3532_CHANNEL_1,
> +	LM3532_CHANNEL_2,
> +	LM3532_CHANNEL_3,
> +};
> +
> +static u32 lm3532_mode_regs[] = {
> +	LM3532_MODE_PWM_A,
> +	LM3532_MODE_PWM_B,
> +	LM3532_MODE_PWM_C,
> +};
> +
> +static u32 lm3532_ramp_regs[] = {
> +	LM3532_RAMPUP,
> +	LM3532_RAMPDN,
> +};
> +
> +static u8 lm3532_enable_reg = LM3532_REG_ENABLE;
> +
> +static u8 lm3532_brightness_regs[] = {
> +	LM3532_REG_BRT_A,
> +	LM3532_REG_BRT_B,
> +	LM3532_REG_BRT_C,
> +};
> +
> +static const struct ti_lmu_bl_reg lm3532_reg_info = {
> +	.init		= lm3532_init_regs,
> +	.num_init	= ARRAY_SIZE(lm3532_init_regs),
> +	.channel	= lm3532_channel_regs,
> +	.mode		= lm3532_mode_regs,
> +	.ramp		= lm3532_ramp_regs,
> +	.enable		= &lm3532_enable_reg,
> +	.brightness_msb	= lm3532_brightness_regs,
> +};
> +
> +/* LM3631 */
> +static u32 lm3631_init_regs[] = {
> +	LM3631_INIT_BRT_MODE,
> +	LM3631_INIT_DIMMING_MODE,
> +};
> +
> +static u32 lm3631_channel_regs[]  = {
> +	LM3631_SINGLE_CHANNEL,
> +	LM3631_DUAL_CHANNEL,
> +};
> +
> +static u32 lm3631_ramp_reg = LM3631_RAMP;
> +static u8 lm3631_enable_reg = LM3631_REG_DEVCTRL;
> +static u8 lm3631_brightness_msb_reg = LM3631_REG_BRT_MSB;
> +static u8 lm3631_brightness_lsb_reg = LM3631_REG_BRT_LSB;
> +
> +static const struct ti_lmu_bl_reg lm3631_reg_info = {
> +	.init		= lm3631_init_regs,
> +	.num_init	= ARRAY_SIZE(lm3631_init_regs),
> +	.channel	= lm3631_channel_regs,
> +	.ramp		= &lm3631_ramp_reg,
> +	.enable		= &lm3631_enable_reg,
> +	.brightness_msb	= &lm3631_brightness_msb_reg,
> +	.brightness_lsb	= &lm3631_brightness_lsb_reg,
> +};
> +
> +/* LM3632 */
> +static u32 lm3632_init_regs[] = {
> +	LM3632_INIT_OVP_25V,
> +	LM3632_INIT_SWFREQ_1MHZ,
> +};
> +
> +static u32 lm3632_channel_regs[]  = {
> +	LM3632_SINGLE_CHANNEL,
> +	LM3632_DUAL_CHANNEL,
> +};
> +
> +static u32 lm3632_mode_reg = LM3632_MODE_PWM;
> +static u8 lm3632_enable_reg = LM3632_REG_ENABLE;
> +static u8 lm3632_brightness_msb_reg = LM3632_REG_BRT_MSB;
> +static u8 lm3632_brightness_lsb_reg = LM3632_REG_BRT_LSB;
> +
> +static const struct ti_lmu_bl_reg lm3632_reg_info = {
> +	.init		= lm3632_init_regs,
> +	.num_init	= ARRAY_SIZE(lm3632_init_regs),
> +	.channel	= lm3632_channel_regs,
> +	.mode		= &lm3632_mode_reg,
> +	.enable		= &lm3632_enable_reg,
> +	.brightness_msb	= &lm3632_brightness_msb_reg,
> +	.brightness_lsb	= &lm3632_brightness_lsb_reg,
> +};
> +
> +/* LM3633 */
> +static u32 lm3633_init_regs[] = {
> +	LM3633_INIT_OVP_40V,
> +	LM3633_INIT_RAMP_SELECT,
> +};
> +
> +static u32 lm3633_channel_regs[]  = {
> +	LM3633_CHANNEL_HVLED1,
> +	LM3633_CHANNEL_HVLED2,
> +	LM3633_CHANNEL_HVLED3,
> +};
> +
> +static u32 lm3633_mode_regs[] = {
> +	LM3633_MODE_PWM_A,
> +	LM3633_MODE_PWM_B,
> +};
> +
> +static u32 lm3633_ramp_regs[] = {
> +	LM3633_RAMPUP,
> +	LM3633_RAMPDN,
> +};
> +
> +static u8 lm3633_enable_reg = LM3633_REG_ENABLE;
> +
> +static u8 lm3633_brightness_msb_regs[] = {
> +	LM3633_REG_BRT_HVLED_A_MSB,
> +	LM3633_REG_BRT_HVLED_B_MSB,
> +};
> +
> +static u8 lm3633_brightness_lsb_regs[] = {
> +	LM3633_REG_BRT_HVLED_A_LSB,
> +	LM3633_REG_BRT_HVLED_B_LSB,
> +};
> +
> +static const struct ti_lmu_bl_reg lm3633_reg_info = {
> +	.init		 = lm3633_init_regs,
> +	.num_init	 = ARRAY_SIZE(lm3633_init_regs),
> +	.channel	 = lm3633_channel_regs,
> +	.mode		 = lm3633_mode_regs,
> +	.ramp		 = lm3633_ramp_regs,
> +	.ramp_reg_offset = 1, /* For LM3633_REG_BL1_RAMPUP/DN */
> +	.enable		 = &lm3633_enable_reg,
> +	.brightness_msb	 = lm3633_brightness_msb_regs,
> +	.brightness_lsb	 = lm3633_brightness_lsb_regs,
> +};
> +
> +/* LM3695 */
> +static u32 lm3695_init_regs[] = {
> +	LM3695_INIT_BRT_MODE,
> +};
> +
> +static u32 lm3695_channel_regs[]  = {
> +	LM3695_SINGLE_CHANNEL,
> +	LM3695_DUAL_CHANNEL,
> +};
> +
> +static u8 lm3695_enable_reg = LM3695_REG_GP;
> +static u8 lm3695_brightness_msb_reg = LM3695_REG_BRT_MSB;
> +static u8 lm3695_brightness_lsb_reg = LM3695_REG_BRT_LSB;
> +
> +static const struct ti_lmu_bl_reg lm3695_reg_info = {
> +	.init		= lm3695_init_regs,
> +	.num_init	= ARRAY_SIZE(lm3695_init_regs),
> +	.channel	= lm3695_channel_regs,
> +	.enable		= &lm3695_enable_reg,
> +	.enable_usec	= 600,
> +	.brightness_msb	= &lm3695_brightness_msb_reg,
> +	.brightness_lsb	= &lm3695_brightness_lsb_reg,
> +};
> +
> +/* LM3697 */
> +static u32 lm3697_init_regs[] = {
> +	LM3697_INIT_RAMP_SELECT,
> +};
> +
> +static u32 lm3697_channel_regs[]  = {
> +	LM3697_CHANNEL_1,
> +	LM3697_CHANNEL_2,
> +	LM3697_CHANNEL_3,
> +};
> +
> +static u32 lm3697_mode_regs[] = {
> +	LM3697_MODE_PWM_A,
> +	LM3697_MODE_PWM_B,
> +};
> +
> +static u32 lm3697_ramp_regs[] = {
> +	LM3697_RAMPUP,
> +	LM3697_RAMPDN,
> +};
> +
> +static u8 lm3697_enable_reg = LM3697_REG_ENABLE;
> +
> +static u8 lm3697_brightness_msb_regs[] = {
> +	LM3697_REG_BRT_A_MSB,
> +	LM3697_REG_BRT_B_MSB,
> +};
> +
> +static u8 lm3697_brightness_lsb_regs[] = {
> +	LM3697_REG_BRT_A_LSB,
> +	LM3697_REG_BRT_B_LSB,
> +};
> +
> +static const struct ti_lmu_bl_reg lm3697_reg_info = {
> +	.init		 = lm3697_init_regs,
> +	.num_init	 = ARRAY_SIZE(lm3697_init_regs),
> +	.channel	 = lm3697_channel_regs,
> +	.mode		 = lm3697_mode_regs,
> +	.ramp		 = lm3697_ramp_regs,
> +	.ramp_reg_offset = 1, /* For LM3697_REG_BL1_RAMPUP/DN */
> +	.enable		 = &lm3697_enable_reg,
> +	.brightness_msb	 = lm3697_brightness_msb_regs,
> +	.brightness_lsb	 = lm3697_brightness_lsb_regs,
> +};
> +
> +static int lm3532_ramp_table[] = { 0, 1, 2, 4, 8, 16, 32, 65 };
> +
> +static int lm3631_ramp_table[] = {
> +	   0,   1,   2,    5,   10,   20,   50,  100,
> +	 250, 500, 750, 1000, 1500, 2000, 3000, 4000,
> +};
> +
> +static int common_ramp_table[] = {
> +	   2, 250, 500, 1000, 2000, 4000, 8000, 16000,
> +};
> +
> +struct ti_lmu_bl_cfg lmu_bl_cfg[LMU_MAX_ID] = {
> +	{
> +		.reginfo		= &lm3532_reg_info,
> +		.num_channels		= LM3532_MAX_CHANNELS,
> +		.max_brightness		= MAX_BRIGHTNESS_8BIT,
> +		.pwm_action		= UPDATE_PWM_AND_BRT_REGISTER,
> +		.ramp_table		= lm3532_ramp_table,
> +		.size_ramp		= ARRAY_SIZE(lm3532_ramp_table),
> +	},
> +	{
> +		.reginfo		= &lm3631_reg_info,
> +		.num_channels		= LM3631_MAX_CHANNELS,
> +		.max_brightness		= MAX_BRIGHTNESS_11BIT,
> +		.pwm_action		= UPDATE_PWM_ONLY,
> +		.ramp_table		= lm3631_ramp_table,
> +		.size_ramp		= ARRAY_SIZE(lm3631_ramp_table),
> +	},
> +	{
> +		.reginfo		= &lm3632_reg_info,
> +		.num_channels		= LM3632_MAX_CHANNELS,
> +		.max_brightness		= MAX_BRIGHTNESS_11BIT,
> +		.pwm_action		= UPDATE_PWM_ONLY,
> +	},
> +	{
> +		.reginfo		= &lm3633_reg_info,
> +		.num_channels		= LM3633_MAX_CHANNELS,
> +		.max_brightness		= MAX_BRIGHTNESS_11BIT,
> +		.pwm_action		= UPDATE_MAX_BRT,
> +		.ramp_table		= common_ramp_table,
> +		.size_ramp		= ARRAY_SIZE(common_ramp_table),
> +		.fault_monitor_used	= true,
> +	},
> +	{
> +		.reginfo		= &lm3695_reg_info,
> +		.num_channels		= LM3695_MAX_CHANNELS,
> +		.max_brightness		= MAX_BRIGHTNESS_11BIT,
> +		.pwm_action		= UPDATE_PWM_AND_BRT_REGISTER,
> +	},
> +	{
> +		.reginfo		= &lm3697_reg_info,
> +		.num_channels		= LM3697_MAX_CHANNELS,
> +		.max_brightness		= MAX_BRIGHTNESS_11BIT,
> +		.pwm_action		= UPDATE_PWM_AND_BRT_REGISTER,
> +		.ramp_table		= common_ramp_table,
> +		.size_ramp		= ARRAY_SIZE(common_ramp_table),
> +		.fault_monitor_used	= true,
> +	},
> +};
> +EXPORT_SYMBOL_GPL(lmu_bl_cfg);
> diff --git a/include/linux/mfd/ti-lmu-backlight.h b/include/linux/mfd/ti-lmu-backlight.h
> new file mode 100644
> index 0000000..43e5300
> --- /dev/null
> +++ b/include/linux/mfd/ti-lmu-backlight.h
> @@ -0,0 +1,290 @@
> +/*
> + * TI LMU (Lighting Management Unit) Backlight Common Driver
> + *
> + * Copyright 2015 Texas Instruments
> + *
> + * Author: Milo Kim <milo.kim@ti.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#ifndef __TI_LMU_BACKLIGHT_H__
> +#define __TI_LMU_BACKLIGHT_H__
> +
> +#include <linux/backlight.h>
> +#include <linux/device.h>
> +#include <linux/mfd/ti-lmu.h>
> +#include <linux/mfd/ti-lmu-register.h>
> +#include <linux/notifier.h>
> +
> +/**
> + * LMU backlight register data
> + *	value[23:16] | mask[15:8] | address[7:0]
> + */
> +#define LMU_BL_REG(addr, mask, value)				\
> +	((value << 16) | (mask << 8) | addr)
> +
> +#define LMU_BL_GET_ADDR(x)	(x & 0xFF)
> +#define LMU_BL_GET_MASK(x)	((x >> 8) & 0xFF)
> +#define LMU_BL_GET_VAL(x)	((x >> 16) & 0xFF)
> +
> +#define LM3532_INIT_ZONE_0						\
> +	LMU_BL_REG(LM3532_REG_ZONE_CFG_A, LM3532_ZONE_MASK, LM3532_ZONE_0)
> +#define LM3532_INIT_ZONE_1						\
> +	LMU_BL_REG(LM3532_REG_ZONE_CFG_B, LM3532_ZONE_MASK, LM3532_ZONE_1)
> +#define LM3532_INIT_ZONE_2						\
> +	LMU_BL_REG(LM3532_REG_ZONE_CFG_C, LM3532_ZONE_MASK, LM3532_ZONE_2)
> +#define LM3532_CHANNEL_1						\
> +	LMU_BL_REG(LM3532_REG_OUTPUT_CFG, LM3532_ILED1_CFG_MASK,	\
> +	LM3532_ILED1_CFG_SHIFT)
> +#define LM3532_CHANNEL_2						\
> +	LMU_BL_REG(LM3532_REG_OUTPUT_CFG, LM3532_ILED2_CFG_MASK,	\
> +	LM3532_ILED2_CFG_SHIFT)
> +#define LM3532_CHANNEL_3						\
> +	LMU_BL_REG(LM3532_REG_OUTPUT_CFG, LM3532_ILED3_CFG_MASK,	\
> +	LM3532_ILED3_CFG_SHIFT)
> +#define LM3532_MODE_PWM_A						\
> +	LMU_BL_REG(LM3532_REG_PWM_A_CFG, LM3532_PWM_A_MASK, LM3532_PWM_ZONE_0)
> +#define LM3532_MODE_PWM_B						\
> +	LMU_BL_REG(LM3532_REG_PWM_B_CFG, LM3532_PWM_B_MASK, LM3532_PWM_ZONE_1)
> +#define LM3532_MODE_PWM_C						\
> +	LMU_BL_REG(LM3532_REG_PWM_C_CFG, LM3532_PWM_C_MASK, LM3532_PWM_ZONE_2)
> +#define LM3532_RAMPUP							\
> +	LMU_BL_REG(LM3532_REG_RAMPUP, LM3532_RAMPUP_MASK, LM3532_RAMPUP_SHIFT)
> +#define LM3532_RAMPDN							\
> +	LMU_BL_REG(LM3532_REG_RAMPDN, LM3532_RAMPDN_MASK, LM3532_RAMPDN_SHIFT)
> +
> +#define LM3631_INIT_BRT_MODE						\
> +	LMU_BL_REG(LM3631_REG_BRT_MODE, LM3631_MODE_MASK, LM3631_DEFAULT_MODE)
> +#define LM3631_INIT_DIMMING_MODE					\
> +	LMU_BL_REG(LM3631_REG_BL_CFG, LM3631_MAP_MASK, LM3631_EXPONENTIAL_MAP)
> +#define LM3631_SINGLE_CHANNEL						\
> +	LMU_BL_REG(LM3631_REG_BL_CFG, LM3631_BL_CHANNEL_MASK,		\
> +	LM3631_BL_SINGLE_CHANNEL)
> +#define LM3631_DUAL_CHANNEL						\
> +	LMU_BL_REG(LM3631_REG_BL_CFG, LM3631_BL_CHANNEL_MASK,		\
> +	LM3631_BL_DUAL_CHANNEL)
> +#define LM3631_RAMP							\
> +	LMU_BL_REG(LM3631_REG_SLOPE, LM3631_SLOPE_MASK, LM3631_SLOPE_SHIFT)
> +
> +#define LM3632_INIT_OVP_25V						\
> +	LMU_BL_REG(LM3632_REG_CONFIG1, LM3632_OVP_MASK, LM3632_OVP_25V)
> +#define LM3632_INIT_SWFREQ_1MHZ						\
> +	LMU_BL_REG(LM3632_REG_CONFIG2, LM3632_SWFREQ_MASK, LM3632_SWFREQ_1MHZ)
> +#define LM3632_SINGLE_CHANNEL						\
> +	LMU_BL_REG(LM3632_REG_ENABLE, LM3632_BL_CHANNEL_MASK,		\
> +	LM3632_BL_SINGLE_CHANNEL)
> +#define LM3632_DUAL_CHANNEL						\
> +	LMU_BL_REG(LM3632_REG_ENABLE, LM3632_BL_CHANNEL_MASK,		\
> +	LM3632_BL_DUAL_CHANNEL)
> +#define LM3632_MODE_PWM							\
> +	LMU_BL_REG(LM3632_REG_IO_CTRL, LM3632_PWM_MASK, LM3632_PWM_MODE)
> +
> +#define LM3633_INIT_OVP_40V						\
> +	LMU_BL_REG(LM3633_REG_BOOST_CFG, LM3633_OVP_MASK, LM3633_OVP_40V)
> +#define LM3633_INIT_RAMP_SELECT						\
> +	LMU_BL_REG(LM3633_REG_BL_RAMP_CONF, LM3633_BL_RAMP_MASK,	\
> +	LM3633_BL_RAMP_EACH)
> +#define LM3633_CHANNEL_HVLED1						\
> +	LMU_BL_REG(LM3633_REG_HVLED_OUTPUT_CFG, LM3633_HVLED1_CFG_MASK,	\
> +	LM3633_HVLED1_CFG_SHIFT)
> +#define LM3633_CHANNEL_HVLED2						\
> +	LMU_BL_REG(LM3633_REG_HVLED_OUTPUT_CFG, LM3633_HVLED2_CFG_MASK,	\
> +	LM3633_HVLED2_CFG_SHIFT)
> +#define LM3633_CHANNEL_HVLED3						\
> +	LMU_BL_REG(LM3633_REG_HVLED_OUTPUT_CFG, LM3633_HVLED3_CFG_MASK,	\
> +	LM3633_HVLED3_CFG_SHIFT)
> +#define LM3633_MODE_PWM_A						\
> +	LMU_BL_REG(LM3633_REG_PWM_CFG, LM3633_PWM_A_MASK, LM3633_PWM_A_MASK)
> +#define LM3633_MODE_PWM_B						\
> +	LMU_BL_REG(LM3633_REG_PWM_CFG, LM3633_PWM_B_MASK, LM3633_PWM_B_MASK)
> +#define LM3633_RAMPUP							\
> +	LMU_BL_REG(LM3633_REG_BL0_RAMP, LM3633_BL_RAMPUP_MASK,		\
> +	LM3633_BL_RAMPUP_SHIFT)
> +#define LM3633_RAMPDN							\
> +	LMU_BL_REG(LM3633_REG_BL0_RAMP, LM3633_BL_RAMPDN_MASK,		\
> +	LM3633_BL_RAMPDN_SHIFT)
> +
> +#define LM3695_INIT_BRT_MODE						\
> +	LMU_BL_REG(LM3695_REG_GP, LM3695_BRT_RW_MASK, LM3695_BRT_RW_MASK)
> +#define LM3695_SINGLE_CHANNEL						\
> +	LMU_BL_REG(LM3695_REG_GP, LM3695_BL_CHANNEL_MASK,		\
> +	LM3695_BL_SINGLE_CHANNEL)
> +#define LM3695_DUAL_CHANNEL						\
> +	LMU_BL_REG(LM3695_REG_GP, LM3695_BL_CHANNEL_MASK,		\
> +	LM3695_BL_DUAL_CHANNEL)
> +
> +#define LM3697_INIT_RAMP_SELECT						\
> +	LMU_BL_REG(LM3697_REG_RAMP_CONF, LM3697_RAMP_MASK, LM3697_RAMP_EACH)
> +#define LM3697_CHANNEL_1						\
> +	LMU_BL_REG(LM3697_REG_HVLED_OUTPUT_CFG, LM3697_HVLED1_CFG_MASK,	\
> +	LM3697_HVLED1_CFG_SHIFT)
> +#define LM3697_CHANNEL_2						\
> +	LMU_BL_REG(LM3697_REG_HVLED_OUTPUT_CFG, LM3697_HVLED2_CFG_MASK,	\
> +	LM3697_HVLED2_CFG_SHIFT)
> +#define LM3697_CHANNEL_3						\
> +	LMU_BL_REG(LM3697_REG_HVLED_OUTPUT_CFG, LM3697_HVLED3_CFG_MASK,	\
> +	LM3697_HVLED3_CFG_SHIFT)
> +#define LM3697_MODE_PWM_A						\
> +	LMU_BL_REG(LM3697_REG_PWM_CFG, LM3697_PWM_A_MASK, LM3697_PWM_A_MASK)
> +#define LM3697_MODE_PWM_B						\
> +	LMU_BL_REG(LM3697_REG_PWM_CFG, LM3697_PWM_B_MASK, LM3697_PWM_B_MASK)
> +#define LM3697_RAMPUP							\
> +	LMU_BL_REG(LM3697_REG_BL0_RAMP, LM3697_RAMPUP_MASK, LM3697_RAMPUP_SHIFT)
> +#define LM3697_RAMPDN							\
> +	LMU_BL_REG(LM3697_REG_BL0_RAMP, LM3697_RAMPDN_MASK, LM3697_RAMPDN_SHIFT)
> +
> +#define LM3532_MAX_CHANNELS		3
> +#define LM3631_MAX_CHANNELS		2
> +#define LM3632_MAX_CHANNELS		2
> +#define LM3633_MAX_CHANNELS		3
> +#define LM3695_MAX_CHANNELS		2
> +#define LM3697_MAX_CHANNELS		3
> +
> +#define MAX_BRIGHTNESS_8BIT		255
> +#define MAX_BRIGHTNESS_11BIT		2047
> +
> +enum ti_lmu_bl_ctrl_mode {
> +	BL_REGISTER_BASED,
> +	BL_PWM_BASED,
> +};
> +
> +enum ti_lmu_bl_pwm_action {
> +	/* Update PWM duty, no brightness register update is required */
> +	UPDATE_PWM_ONLY,
> +	/* Update not only duty but also brightness register */
> +	UPDATE_PWM_AND_BRT_REGISTER,
> +	/* Update max value in brightness registers */
> +	UPDATE_MAX_BRT,
> +};
> +
> +enum ti_lmu_bl_ramp_mode {
> +	BL_RAMP_UP,
> +	BL_RAMP_DOWN,
> +};
> +
> +struct ti_lmu_bl;
> +struct ti_lmu_bl_chip;
> +
> +/**
> + * struct ti_lmu_bl_reg
> + *
> + * @init:		Device initialization registers
> + * @num_init:		Numbers of initialization registers
> + * @channel:		Backlight channel configuration registers
> + * @mode:		Brightness control mode registers
> + * @ramp:		Ramp registers for lighting effect
> + * @ramp_reg_offset:	Ramp register offset.
> + *			Only used for multiple ramp registers.
> + * @enable:		Enable control register address
> + * @enable_usec:	Delay time for updating enable register.
> + *			Unit is microsecond.
> + * @brightness_msb:	Brightness MSB(Upper 8 bits) registers.
> + *			Concatenated with LSB in 11 bit dimming mode.
> + *			In 8 bit dimming, only MSB is used.
> + * @brightness_lsb:	Brightness LSB(Lower 3 bits) registers.
> + *			Only valid in 11 bit dimming mode.
> + */
> +struct ti_lmu_bl_reg {
> +	u32 *init;
> +	int num_init;
> +	u32 *channel;
> +	u32 *mode;
> +	u32 *ramp;
> +	int ramp_reg_offset;
> +	u8 *enable;
> +	unsigned long enable_usec;
> +	u8 *brightness_msb;
> +	u8 *brightness_lsb;
> +};
> +
> +/**
> + * struct ti_lmu_bl_cfg
> + *
> + * @reginfo:		Device register configuration
> + * @num_channels:	Number of backlight channels
> + * @max_brightness:	Max brightness value of backlight device
> + * @pwm_action:		How to control brightness registers in PWM mode
> + * @ramp_table:		[Optional] Ramp time table for lighting effect.
> + *			It's used for searching approximate register index.
> + * @size_ramp:		[Optional] Size of ramp table
> + * @fault_monitor_used:	[Optional] Set true if the device needs to handle
> + *			LMU fault monitor event.
> + *
> + * This structure is used for device specific data configuration.
> + */
> +struct ti_lmu_bl_cfg {
> +	const struct ti_lmu_bl_reg *reginfo;
> +	int num_channels;
> +	int max_brightness;
> +	enum ti_lmu_bl_pwm_action pwm_action;
> +	int *ramp_table;
> +	int size_ramp;
> +	bool fault_monitor_used;
> +};
> +
> +/**
> + * struct ti_lmu_bl_chip
> + *
> + * @dev:		Parent device pointer
> + * @lmu:		LMU structure.
> + *			Used for register R/W access and notification.
> + * @cfg:		Device configuration data
> + * @lmu_bl:		Multiple backlight channels
> + * @num_backlights:	Number of backlight channels
> + * @nb:			Notifier block for handling LMU fault monitor event
> + *
> + * One backlight chip can have multiple backlight channels, 'ti_lmu_bl'.
> + */
> +struct ti_lmu_bl_chip {
> +	struct device *dev;
> +	struct ti_lmu *lmu;
> +	const struct ti_lmu_bl_cfg *cfg;
> +	struct ti_lmu_bl *lmu_bl;
> +	int num_backlights;
> +	struct notifier_block nb;
> +};
> +
> +/**
> + * struct ti_lmu_bl
> + *
> + * @chip:		Pointer to parent backlight device
> + * @bl_dev:		Backlight subsystem device structure
> + * @bank_id:		Backlight bank ID
> + * @name:		Backlight channel name
> + * @mode:		Backlight control mode
> + * @led_sources:	Backlight output channel configuration.
> + *			Bit mask is set on parsing DT.
> + * @default_brightness:	[Optional] Initial brightness value
> + * @ramp_up_msec:	[Optional] Ramp up time
> + * @ramp_down_msec:	[Optional] Ramp down time
> + * @pwm_period:		[Optional] PWM period
> + * @pwm:		[Optional] PWM subsystem structure
> + *
> + * Each backlight device has its own channel configuration.
> + * For chip control, parent chip data structure is used.
> + */
> +struct ti_lmu_bl {
> +	struct ti_lmu_bl_chip *chip;
> +	struct backlight_device *bl_dev;
> +
> +	int bank_id;
> +	const char *name;
> +	enum ti_lmu_bl_ctrl_mode mode;
> +	unsigned long led_sources;
> +
> +	unsigned int default_brightness;
> +
> +	/* Used for lighting effect */
> +	unsigned int ramp_up_msec;
> +	unsigned int ramp_down_msec;
> +
> +	/* Only valid in PWM mode */
> +	unsigned int pwm_period;
> +	struct pwm_device *pwm;
> +};
> +
> +extern struct ti_lmu_bl_cfg lmu_bl_cfg[LMU_MAX_ID];
> +#endif

-- 
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog

  reply	other threads:[~2016-01-11  9:57 UTC|newest]

Thread overview: 58+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-11-26  6:56 [PATCH v2 0/9] Support TI LMU devices Milo Kim
2015-11-26  6:56 ` Milo Kim
2015-11-26  6:56 ` [PATCH v2 1/9] Documentation: dt-bindings: mfd: add TI LMU device binding information Milo Kim
2015-11-26  6:56   ` Milo Kim
2015-11-27 20:55   ` Rob Herring
2016-01-11  9:46   ` Lee Jones
2015-11-26  6:56 ` [PATCH v2 2/9] Documentation: dt-bindings: leds: backlight: add TI LMU backlight " Milo Kim
2015-11-26  6:56   ` Milo Kim
2016-01-11  9:53   ` Lee Jones
2015-11-26  6:56 ` [PATCH v2 3/9] Documentation: dt-bindings: leds: add LM3633 LED " Milo Kim
2015-11-26  6:56   ` Milo Kim
2015-11-27 11:19   ` Jacek Anaszewski
2015-11-30  8:19     ` Kim, Milo
2015-11-30  8:19       ` Kim, Milo
2015-11-30 12:26       ` Jacek Anaszewski
     [not found]         ` <565C408B.9070703-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
2015-12-07  8:46           ` Kim, Milo
2015-12-07  8:46             ` Kim, Milo
2015-12-07 10:50             ` Jacek Anaszewski
2015-11-26  6:57 ` [PATCH v2 4/9] Documentation: dt-bindings: regulator: add LM363x regulator " Milo Kim
2015-11-26  6:57   ` Milo Kim
2015-11-27 12:37   ` Mark Brown
2015-11-27 20:44     ` Rob Herring
2015-11-27 22:07       ` Mark Brown
2015-11-27 12:55   ` Applied "regulator: lm363x: add LM363x regulator binding information" to the regulator tree Mark Brown
2015-11-27 20:57   ` [PATCH v2 4/9] Documentation: dt-bindings: regulator: add LM363x regulator binding information Rob Herring
2015-11-26  6:57 ` [PATCH v2 5/9] mfd: add TI LMU driver Milo Kim
2015-11-26  6:57   ` Milo Kim
2016-01-11 10:17   ` Lee Jones
2015-11-26  6:57 ` [PATCH v2 6/9] mfd: add TI LMU hardware fault monitoring driver Milo Kim
2015-11-26  6:57   ` Milo Kim
2016-01-11 10:21   ` Lee Jones
2016-01-12  3:36     ` Milo Kim
2016-01-12  3:36       ` Milo Kim
2016-01-12  7:37       ` Lee Jones
2015-11-26  6:57 ` [PATCH v2 7/9] backlight: add TI LMU backlight driver Milo Kim
2015-11-26  6:57   ` Milo Kim
2016-01-11  9:57   ` Lee Jones [this message]
2016-01-11 23:32     ` Milo Kim
2016-01-11 23:32       ` Milo Kim
2015-11-26  6:57 ` [PATCH v2 8/9] leds: add LM3633 driver Milo Kim
2015-11-26  6:57   ` Milo Kim
2015-11-27 11:19   ` Jacek Anaszewski
     [not found]     ` <56583C37.1050307-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
2015-11-28  8:28       ` Jacek Anaszewski
2015-11-28  8:28         ` Jacek Anaszewski
2015-11-30  8:48     ` Kim, Milo
2015-11-30  8:48       ` Kim, Milo
2015-11-30 12:26       ` Jacek Anaszewski
2015-11-26  6:57 ` [PATCH v2 9/9] regulator: add LM363X driver Milo Kim
2015-11-26  6:57   ` Milo Kim
2015-11-27 12:55   ` Applied "regulator: add LM363X driver" to the regulator tree Mark Brown
2016-01-14  7:56   ` [PATCH v2 9/9] regulator: add LM363X driver Milo Kim
2016-01-14  7:56     ` Milo Kim
     [not found]     ` <56975493.7050900-l0cyMroinI0@public.gmane.org>
2016-01-14 10:27       ` Mark Brown
2016-01-14 10:27         ` Mark Brown
2016-01-14 23:41         ` Kim, Milo
2016-01-14 23:41           ` Kim, Milo
2016-01-06  7:20 ` [PATCH v2 0/9] Support TI LMU devices Milo Kim
2016-01-06  7:20   ` Milo Kim

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20160111095702.GS14104@x1 \
    --to=lee.jones@linaro.org \
    --cc=broonie@kernel.org \
    --cc=devicetree@vger.kernel.org \
    --cc=j.anaszewski@samsung.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-leds@vger.kernel.org \
    --cc=milo.kim@ti.com \
    --cc=robh+dt@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.