TI LMU backlight driver provides common driver features.
Chip specific configuration is handled by each backlight driver such like
LM3532, LM3631, LM3633, LM3695 and LM3697.

It supports common features as below.
  - Consistent device control flow
  - Control bank assignment from the platform data
  - Backlight subsystem control
  - PWM brightness control
  - Shared device tree node

Cc: Jingoo Han <jg1....@samsung.com>
Cc: Bryan Wu <coolo...@gmail.com>
Cc: Lee Jones <lee.jo...@linaro.org>
Signed-off-by: Milo Kim <milo....@ti.com>
---
 drivers/video/backlight/Kconfig            |    7 +
 drivers/video/backlight/Makefile           |    1 +
 drivers/video/backlight/ti-lmu-backlight.c |  369 ++++++++++++++++++++++++++++
 drivers/video/backlight/ti-lmu-backlight.h |   78 ++++++
 4 files changed, 455 insertions(+)
 create mode 100644 drivers/video/backlight/ti-lmu-backlight.c
 create mode 100644 drivers/video/backlight/ti-lmu-backlight.h

diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig
index 5a3eb2e..3641698 100644
--- a/drivers/video/backlight/Kconfig
+++ b/drivers/video/backlight/Kconfig
@@ -384,6 +384,13 @@ config BACKLIGHT_LM3639
        help
          This supports TI LM3639 Backlight + 1.5A Flash LED Driver
 
+config TI_LMU_BACKLIGHT
+       tristate "Backlight driver for TI LMU"
+       depends on BACKLIGHT_LM3532 || BACKLIGHT_LM3631 || BACKLIGHT_LM3633 || 
BACKLIGHT_LM3695 || BACKLIGHT_LM3697
+       help
+         TI LMU backlight driver provides common driver features.
+         Chip specific configuration is handled by each backlight driver.
+
 config BACKLIGHT_LP855X
        tristate "Backlight driver for TI LP855X"
        depends on BACKLIGHT_CLASS_DEVICE && I2C
diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile
index bb82002..f80e046 100644
--- a/drivers/video/backlight/Makefile
+++ b/drivers/video/backlight/Makefile
@@ -39,6 +39,7 @@ obj-$(CONFIG_BACKLIGHT_HP700)         += jornada720_bl.o
 obj-$(CONFIG_BACKLIGHT_LM3533)         += lm3533_bl.o
 obj-$(CONFIG_BACKLIGHT_LM3630A)                += lm3630a_bl.o
 obj-$(CONFIG_BACKLIGHT_LM3639)         += lm3639_bl.o
+obj-$(CONFIG_TI_LMU_BACKLIGHT)         += ti-lmu-backlight.o
 obj-$(CONFIG_BACKLIGHT_LOCOMO)         += locomolcd.o
 obj-$(CONFIG_BACKLIGHT_LP855X)         += lp855x_bl.o
 obj-$(CONFIG_BACKLIGHT_LP8788)         += lp8788_bl.o
diff --git a/drivers/video/backlight/ti-lmu-backlight.c 
b/drivers/video/backlight/ti-lmu-backlight.c
new file mode 100644
index 0000000..5ceb5e8
--- /dev/null
+++ b/drivers/video/backlight/ti-lmu-backlight.c
@@ -0,0 +1,369 @@
+/*
+ * TI LMU Backlight Common Driver
+ *
+ * Copyright 2014 Texas Instruments
+ *
+ * Author: Milo Kim <milo....@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.
+ *
+ * LMU backlight driver supports common features as below.
+ *
+ *   - Consistent device control flow by using ti_lmu_bl_ops
+ *   - Control bank assignment from the platform data
+ *   - Backlight subsystem control
+ *   - PWM brightness control
+ *   - Shared device tree node
+ *
+ * Sequence of LMU backlight control
+ *
+ *   (Chip dependent backlight driver)            (TI LMU Backlight Common)
+ *
+ *     Operation configuration
+ *     ti_lmu_backlight_init_device()   --->
+ *     Initialization                   <---        ops->init()
+ *
+ *     ti_lmu_backlight_register()      --->
+ *     Backlight configuration          <---        ops->configure()
+ *
+ *                                                  Runtime brightness control
+ *     Enable register control          <---        ops->bl_enable()
+ *     Brightness register control      <---        ops->update_brightness()
+ *
+ */
+
+#include <linux/backlight.h>
+#include <linux/err.h>
+#include <linux/mfd/ti-lmu.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/slab.h>
+
+#include "ti-lmu-backlight.h"
+
+#define DEFAULT_BL_NAME                        "lcd-backlight"
+
+static int ti_lmu_backlight_enable(struct ti_lmu_bl *lmu_bl, int enable)
+{
+       const struct ti_lmu_bl_ops *ops = lmu_bl->chip->ops;
+
+       if (ops->bl_enable)
+               return ops->bl_enable(lmu_bl, enable);
+
+       return 0;
+}
+
+static void ti_lmu_backlight_pwm_ctrl(struct ti_lmu_bl *lmu_bl, int br,
+                                     int max_br)
+{
+       struct pwm_device *pwm;
+       unsigned int duty, period;
+
+       /* Request a PWM device with the consumer name */
+       if (!lmu_bl->pwm) {
+               pwm = devm_pwm_get(lmu_bl->chip->dev, lmu_bl->pwm_name);
+               if (IS_ERR(pwm)) {
+                       dev_err(lmu_bl->chip->dev,
+                               "Can not get PWM device: %s\n",
+                               lmu_bl->pwm_name);
+                       return;
+               }
+               lmu_bl->pwm = pwm;
+       }
+
+       period = lmu_bl->bl_pdata->pwm_period;
+       duty = br * period / max_br;
+
+       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_status(struct backlight_device *bl_dev)
+{
+       struct ti_lmu_bl *lmu_bl = bl_get_data(bl_dev);
+       const struct ti_lmu_bl_ops *ops = lmu_bl->chip->ops;
+       int ret = 0;
+       int brt;
+
+       if (bl_dev->props.state & BL_CORE_SUSPENDED)
+               bl_dev->props.brightness = 0;
+
+       brt = bl_dev->props.brightness;
+       if (brt > 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, brt,
+                                         bl_dev->props.max_brightness);
+
+       /*
+        * In some devices, additional handling is required after PWM control.
+        * So, just call device-specific brightness function.
+        */
+       if (ops->update_brightness)
+               return ops->update_brightness(lmu_bl, brt);
+
+       return 0;
+}
+
+static int ti_lmu_backlight_get_brightness(struct backlight_device *bl_dev)
+{
+       return bl_dev->props.brightness;
+}
+
+static const struct backlight_ops lmu_bl_common_ops = {
+       .options = BL_CORE_SUSPENDRESUME,
+       .update_status = ti_lmu_backlight_update_status,
+       .get_brightness = ti_lmu_backlight_get_brightness,
+};
+
+static int ti_lmu_backlight_parse_dt(struct device *dev, struct ti_lmu *lmu)
+{
+       struct ti_lmu_backlight_platform_data *pdata;
+       struct device_node *node = dev->of_node;
+       struct device_node *child;
+       int num_backlights;
+       int i = 0;
+       u8 imax_mA;
+
+       if (!node) {
+               dev_err(dev, "No device node exists\n");
+               return -ENODEV;
+       }
+
+       num_backlights = of_get_child_count(node);
+       if (num_backlights == 0) {
+               dev_err(dev, "No backlight strings\n");
+               return -EINVAL;
+       }
+
+       pdata = devm_kzalloc(dev, sizeof(*pdata) * num_backlights, GFP_KERNEL);
+       if (!pdata)
+               return -ENOMEM;
+
+       for_each_child_of_node(node, child) {
+               of_property_read_string(child, "bl-name", &pdata[i].name);
+
+               /* Make backlight strings */
+               pdata[i].bl_string = 0;
+               if (of_find_property(child, "hvled1-used", NULL))
+                       pdata[i].bl_string |= LMU_HVLED1;
+               if (of_find_property(child, "hvled2-used", NULL))
+                       pdata[i].bl_string |= LMU_HVLED2;
+               if (of_find_property(child, "hvled3-used", NULL))
+                       pdata[i].bl_string |= LMU_HVLED3;
+
+               of_property_read_u8(child, "max-current-milliamp", &imax_mA);
+               pdata[i].imax = ti_lmu_get_current_code(imax_mA);
+
+               of_property_read_u8(child, "initial-brightness",
+                                   &pdata[i].init_brightness);
+
+               /* Light effect */
+               of_property_read_u32(child, "ramp-up", &pdata[i].ramp_up_ms);
+               of_property_read_u32(child, "ramp-down",
+                                    &pdata[i].ramp_down_ms);
+
+               /* PWM mode */
+               of_property_read_u32(child, "pwm-period", &pdata[i].pwm_period);
+
+               i++;
+       }
+
+       lmu->pdata->bl_pdata = pdata;
+       lmu->pdata->num_backlights = num_backlights;
+
+       return 0;
+}
+
+struct ti_lmu_bl_chip *
+ti_lmu_backlight_init_device(struct device *dev, struct ti_lmu *lmu,
+                            const struct ti_lmu_bl_ops *ops)
+{
+       struct ti_lmu_bl_chip *chip;
+       int ret;
+
+       chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
+       if (!chip)
+               return ERR_PTR(-ENOMEM);
+
+       chip->dev = dev;
+       chip->lmu = lmu;
+       chip->ops = ops;
+
+       if (!lmu->pdata->bl_pdata) {
+               if (IS_ENABLED(CONFIG_OF))
+                       ret = ti_lmu_backlight_parse_dt(dev, lmu);
+               else
+                       return ERR_PTR(-ENODEV);
+
+               if (ret)
+                       return ERR_PTR(ret);
+       }
+
+       if (chip->ops->init) {
+               ret = chip->ops->init(chip);
+               if (ret)
+                       return ERR_PTR(ret);
+       }
+
+       return chip;
+}
+EXPORT_SYMBOL_GPL(ti_lmu_backlight_init_device);
+
+static void ti_lmu_backlight_set_ctrl_mode(struct ti_lmu_bl *lmu_bl)
+{
+       struct ti_lmu_backlight_platform_data *pdata = lmu_bl->bl_pdata;
+
+       if (pdata->pwm_period > 0)
+               lmu_bl->mode = BL_PWM_BASED;
+       else
+               lmu_bl->mode = BL_REGISTER_BASED;
+}
+
+static int ti_lmu_backlight_configure(struct ti_lmu_bl *lmu_bl)
+{
+       const struct ti_lmu_bl_ops *ops = lmu_bl->chip->ops;
+
+       if (ops->configure)
+               return ops->configure(lmu_bl);
+
+       return 0;
+}
+
+static int ti_lmu_backlight_add_device(struct ti_lmu_bl *lmu_bl)
+{
+       struct backlight_device *bl_dev;
+       struct backlight_properties props;
+       struct ti_lmu_backlight_platform_data *pdata = lmu_bl->bl_pdata;
+       int max_brightness = lmu_bl->chip->ops->max_brightness;
+       char name[20];
+
+       memset(&props, 0, sizeof(struct backlight_properties));
+       props.type = BACKLIGHT_PLATFORM;
+       props.brightness = pdata ? pdata->init_brightness : 0;
+       props.max_brightness = max_brightness;
+
+       /* Backlight device name */
+       if (!pdata->name)
+               snprintf(name, sizeof(name), "%s:%d", DEFAULT_BL_NAME,
+                        lmu_bl->bank_id);
+       else
+               snprintf(name, sizeof(name), "%s", pdata->name);
+
+       bl_dev = backlight_device_register(name, lmu_bl->chip->dev, lmu_bl,
+                                          &lmu_bl_common_ops, &props);
+       if (IS_ERR(bl_dev))
+               return PTR_ERR(bl_dev);
+
+       lmu_bl->bl_dev = bl_dev;
+
+       return 0;
+}
+
+struct ti_lmu_bl *
+ti_lmu_backlight_register(struct ti_lmu_bl_chip *chip,
+                         struct ti_lmu_backlight_platform_data *pdata,
+                         int num_backlights)
+{
+       struct ti_lmu_bl *lmu_bl, *each;
+       int i, ret;
+
+       lmu_bl = devm_kzalloc(chip->dev, sizeof(*lmu_bl) * num_backlights,
+                             GFP_KERNEL);
+       if (!lmu_bl)
+               return ERR_PTR(-ENOMEM);
+
+       for (i = 0; i < num_backlights; i++) {
+               each = lmu_bl + i;
+               each->bank_id = i;
+               each->chip = chip;
+               each->bl_pdata = pdata + i;
+
+               ti_lmu_backlight_set_ctrl_mode(lmu_bl);
+
+               ret = ti_lmu_backlight_configure(each);
+               if (ret) {
+                       dev_err(chip->dev, "Backlight config err: %d\n", ret);
+                       goto err;
+               }
+
+               ret = ti_lmu_backlight_add_device(each);
+               if (ret) {
+                       dev_err(chip->dev, "Backlight device err: %d\n", ret);
+                       goto cleanup_backlights;
+               }
+
+               backlight_update_status(each->bl_dev);
+       }
+
+       chip->num_backlights = num_backlights;
+
+       return lmu_bl;
+
+cleanup_backlights:
+       while (--i >= 0) {
+               each = lmu_bl + i;
+               backlight_device_unregister(each->bl_dev);
+       }
+err:
+       return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(ti_lmu_backlight_register);
+
+int ti_lmu_backlight_unregister(struct ti_lmu_bl *lmu_bl)
+{
+       struct ti_lmu_bl *each;
+       struct backlight_device *bl_dev;
+       int num_backlights = lmu_bl->chip->num_backlights;
+       int i;
+
+       for (i = 0; i < num_backlights; i++) {
+               each = lmu_bl + i;
+
+               bl_dev = each->bl_dev;
+               bl_dev->props.brightness = 0;
+               backlight_update_status(bl_dev);
+               backlight_device_unregister(bl_dev);
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(ti_lmu_backlight_unregister);
+
+/*
+ * This callback function is invoked in case the LMU effect driver is
+ * requested successfully.
+ */
+void ti_lmu_backlight_effect_callback(struct ti_lmu_effect *lmu_effect,
+                                     int req_id, void *data)
+{
+       struct ti_lmu_bl *lmu_bl = data;
+       unsigned int ramp_time;
+
+       if (req_id == BL_EFFECT_RAMPUP)
+               ramp_time = lmu_bl->bl_pdata->ramp_up_ms;
+       else if (req_id == BL_EFFECT_RAMPDN)
+               ramp_time = lmu_bl->bl_pdata->ramp_down_ms;
+       else
+               return;
+
+       ti_lmu_effect_set_ramp(lmu_effect, ramp_time);
+}
+EXPORT_SYMBOL_GPL(ti_lmu_backlight_effect_callback);
+
+MODULE_DESCRIPTION("TI LMU Backlight Common Driver");
+MODULE_AUTHOR("Milo Kim");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/backlight/ti-lmu-backlight.h 
b/drivers/video/backlight/ti-lmu-backlight.h
new file mode 100644
index 0000000..7dd2fa5d
--- /dev/null
+++ b/drivers/video/backlight/ti-lmu-backlight.h
@@ -0,0 +1,78 @@
+/*
+ * TI LMU Backlight Common Driver
+ *
+ * Copyright 2014 Texas Instruments
+ *
+ * Author: Milo Kim <milo....@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/mfd/ti-lmu.h>
+#include <linux/mfd/ti-lmu-effect.h>
+
+#define LMU_BL_DEFAULT_PWM_NAME                "lmu-backlight"
+
+enum ti_lmu_bl_ctrl_mode {
+       BL_REGISTER_BASED,
+       BL_PWM_BASED,
+};
+
+struct ti_lmu_bl;
+struct ti_lmu_bl_chip;
+
+/*
+ * struct ti_lmu_bl_ops
+ * @init: Device specific initialization
+ * @configure: Device specific string configuration
+ * @update_brightness: Device specific brightness control
+ * @bl_enable: Device specific backlight enable/disable control
+ * @max_brightness: Max brightness value of backlight device
+ */
+struct ti_lmu_bl_ops {
+       int (*init)(struct ti_lmu_bl_chip *lmu_chip);
+       int (*configure)(struct ti_lmu_bl *lmu_bl);
+       int (*update_brightness)(struct ti_lmu_bl *lmu_bl, int brightness);
+       int (*bl_enable)(struct ti_lmu_bl *lmu_bl, int enable);
+       const int max_brightness;
+};
+
+/* One backlight chip can have multiple backlight strings */
+struct ti_lmu_bl_chip {
+       struct device *dev;
+       struct ti_lmu *lmu;
+       const struct ti_lmu_bl_ops *ops;
+       int num_backlights;
+};
+
+/* Backlight string structure */
+struct ti_lmu_bl {
+       int bank_id;
+       struct backlight_device *bl_dev;
+       struct ti_lmu_bl_chip *chip;
+       struct ti_lmu_backlight_platform_data *bl_pdata;
+       enum ti_lmu_bl_ctrl_mode mode;
+       struct pwm_device *pwm;
+       char pwm_name[20];
+};
+
+struct ti_lmu_bl_chip *
+ti_lmu_backlight_init_device(struct device *dev, struct ti_lmu *lmu,
+                            const struct ti_lmu_bl_ops *ops);
+
+struct ti_lmu_bl *
+ti_lmu_backlight_register(struct ti_lmu_bl_chip *chip,
+                         struct ti_lmu_backlight_platform_data *pdata,
+                         int num_backlights);
+
+int ti_lmu_backlight_unregister(struct ti_lmu_bl *lmu_bl);
+
+void ti_lmu_backlight_effect_callback(struct ti_lmu_effect *lmu_effect,
+                                     int req_id, void *data);
+#endif
-- 
1.7.9.5

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to