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@xxxxxxxxxx> > Cc: Jacek Anaszewski <j.anaszewski@xxxxxxxxxxx> > Cc: Mark Brown <broonie@xxxxxxxxxx> > Cc: Rob Herring <robh+dt@xxxxxxxxxx> > Cc: devicetree@xxxxxxxxxxxxxxx > Cc: linux-leds@xxxxxxxxxxxxxxx > Cc: linux-kernel@xxxxxxxxxxxxxxx > Signed-off-by: Milo Kim <milo.kim@xxxxxx> > --- > 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@xxxxxx> > + * > + * 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@xxxxxx> > + * > + * 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@xxxxxx> > + * > + * 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 -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html