summaryrefslogtreecommitdiff
path: root/drivers/video/backlight/lm3632_bl.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/video/backlight/lm3632_bl.c')
-rwxr-xr-xdrivers/video/backlight/lm3632_bl.c395
1 files changed, 395 insertions, 0 deletions
diff --git a/drivers/video/backlight/lm3632_bl.c b/drivers/video/backlight/lm3632_bl.c
new file mode 100755
index 00000000..6ee1e757
--- /dev/null
+++ b/drivers/video/backlight/lm3632_bl.c
@@ -0,0 +1,395 @@
+/*
+ * TI LM3632 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/delay.h>
+#include <linux/err.h>
+#include <linux/mfd/lm3632.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/slab.h>
+//#include "./lm3632.h"
+
+#define LM3632_MAX_BRIGHTNESS 2047
+
+#define DEFAULT_BL_NAME "lcd-backlight"
+
+enum lm3632_bl_ctrl_mode {
+ LMU_BL_I2C,
+ LMU_BL_PWM,
+};
+struct lm3632_bl {
+ struct device *dev;
+ struct backlight_device *bl_dev;
+
+ struct lm3632 *lm3632;
+ struct lm3632_backlight_platform_data *pdata;
+ enum lm3632_bl_ctrl_mode mode;
+
+ struct pwm_device *pwm;
+};
+
+static int lm3632_bl_string_configure(struct lm3632_bl *lm3632_bl, int enable)
+{
+ u8 val;
+
+ if (enable) {
+ if (lm3632_bl->pdata->is_full_strings)
+ val = LM3632_BL_TWO_STRINGS;
+ else
+ val = LM3632_BL_ONE_STRING;
+ } else {
+ val = 0;
+ }
+
+ return lm3632_update_bits(lm3632_bl->lm3632, LM3632_REG_ENABLE,
+ LM3632_BL_STRING_MASK, val);
+}
+
+static int lm3632_bl_enable(struct lm3632_bl *lm3632_bl, int enable)
+{
+ int ret;
+
+ ret = lm3632_bl_string_configure(lm3632_bl, enable);
+ if (ret) {
+ dev_err(lm3632_bl->dev,
+ "Backlight string config error: %d\n", ret);
+ return ret;
+ }
+
+ return lm3632_update_bits(lm3632_bl->lm3632, LM3632_REG_ENABLE,
+ LM3632_BL_EN_MASK, enable << LM3632_BL_EN_SHIFT);
+}
+
+static void lm3632_bl_pwm_ctrl(struct lm3632_bl *lm3632_bl, int br, int max_br)
+{
+ unsigned int period;
+ unsigned int duty;
+ struct pwm_device *pwm;
+
+ if (!lm3632_bl->pdata)
+ return;
+
+ period = lm3632_bl->pdata->pwm_period;
+ duty = br * period / max_br;
+
+ /* Request a PWM device with the consumer name */
+ if (!lm3632_bl->pwm) {
+ pwm = devm_pwm_get(lm3632_bl->dev, "lm3632-backlight");
+ if (IS_ERR(pwm)) {
+ dev_err(lm3632_bl->dev, "can not get PWM device\n");
+ return;
+ }
+ lm3632_bl->pwm = pwm;
+ }
+
+ pwm_config(lm3632_bl->pwm, duty, period);
+ if (duty)
+ pwm_enable(lm3632_bl->pwm);
+ else
+ pwm_disable(lm3632_bl->pwm);
+}
+
+static int lm3632_bl_set_brightness(struct lm3632_bl *lm3632_bl, int val)
+{
+ u8 data;
+ int ret;
+
+
+ data = val & LM3632_BRT_LSB_MASK;
+ ret = lm3632_update_bits(lm3632_bl->lm3632, LM3632_REG_BRT_LSB,
+ LM3632_BRT_LSB_MASK, data);
+ if (ret)
+ return ret;
+
+ data = (val >> LM3632_BRT_MSB_SHIFT) & 0xFF;
+
+ return lm3632_write_byte(lm3632_bl->lm3632, LM3632_REG_BRT_MSB,
+ data);
+}
+
+static int lm3632_bl_update_status(struct backlight_device *bl_dev)
+{
+ struct lm3632_bl *lm3632_bl = bl_get_data(bl_dev);
+ int brt;
+ int ret;
+
+
+ if (bl_dev->props.state & BL_CORE_SUSPENDED)
+ brt = 0;
+ else
+ brt = bl_dev->props.brightness;
+
+ printk("zyun lm3632 update status=%d\n", brt);
+
+ if (brt > 0)
+ ret = lm3632_bl_enable(lm3632_bl, 1);
+ else
+ ret = lm3632_bl_enable(lm3632_bl, 0);
+
+ if (ret)
+ return ret;
+
+ // we don't want to update brightness here
+ if (lm3632_bl->mode == LMU_BL_PWM)
+ lm3632_bl_pwm_ctrl(lm3632_bl, brt,
+ bl_dev->props.max_brightness);
+ else
+ ret = lm3632_bl_set_brightness(lm3632_bl, brt);
+
+ return ret;
+}
+
+static int lm3632_bl_get_brightness(struct backlight_device *bl_dev)
+{
+ return bl_dev->props.brightness;
+}
+
+static const struct backlight_ops lm3632_bl_ops = {
+ .options = BL_CORE_SUSPENDRESUME,
+ .update_status = lm3632_bl_update_status,
+ .get_brightness = lm3632_bl_get_brightness,
+};
+
+static int lm3632_bl_register(struct lm3632_bl *lm3632_bl)
+{
+ struct backlight_device *bl_dev;
+ struct backlight_properties props;
+ struct lm3632_backlight_platform_data *pdata = lm3632_bl->pdata;
+ char name[20];
+
+ props.type = BACKLIGHT_PLATFORM;
+ props.brightness = 1637;
+ props.max_brightness = LM3632_MAX_BRIGHTNESS;
+
+ if (!pdata || !pdata->name)
+ snprintf(name, sizeof(name), "%s", DEFAULT_BL_NAME);
+ else
+ snprintf(name, sizeof(name), "%s", pdata->name);
+
+ bl_dev = backlight_device_register(name, lm3632_bl->dev, lm3632_bl,
+ &lm3632_bl_ops, &props);
+ if (IS_ERR(bl_dev))
+ return PTR_ERR(bl_dev);
+
+ lm3632_bl->bl_dev = bl_dev;
+
+ return 0;
+}
+
+static void lm3632_bl_unregister(struct lm3632_bl *lm3632_bl)
+{
+ if (lm3632_bl->bl_dev)
+ backlight_device_unregister(lm3632_bl->bl_dev);
+}
+
+static int lm3632_bl_set_ovp(struct lm3632_bl *lm3632_bl)
+{
+ return lm3632_update_bits(lm3632_bl->lm3632, LM3632_REG_CONFIG1,
+ LM3632_OVP_MASK, LM3632_OVP_25V);
+}
+
+static int lm3632_bl_set_swfreq(struct lm3632_bl *lm3632_bl)
+{
+ return lm3632_update_bits(lm3632_bl->lm3632, LM3632_REG_CONFIG2,
+ LM3632_SWFREQ_MASK, LM3632_SWFREQ_1MHZ);
+}
+
+static int lm3632_bl_bias_output(struct lm3632_bl *lm3632_bl)
+{
+ return lm3632_update_bits(lm3632_bl->lm3632, 0x0C,
+ BIT(0), BIT(0));
+}
+
+
+static int lm3632_bl_set_ctrl_mode(struct lm3632_bl *lm3632_bl)
+{
+ struct lm3632_backlight_platform_data *pdata = lm3632_bl->pdata;
+#if 0
+ if (pdata->pwm_period > 0)
+ lm3632_bl->mode = LMU_BL_PWM;
+ else
+#endif
+ lm3632_bl->mode = LMU_BL_I2C;
+
+ return lm3632_update_bits(lm3632_bl->lm3632, LM3632_REG_IO_CTRL,
+ LM3632_PWM_MASK,
+ lm3632_bl->mode << LM3632_PWM_SHIFT);
+}
+
+static int lm3632_bl_configure(struct lm3632_bl *lm3632_bl)
+{
+ int ret;
+
+ /* Select OVP level */
+ ret = lm3632_bl_set_ovp(lm3632_bl);
+ if (ret)
+ return ret;
+
+ /* Select switch frequency */
+ ret = lm3632_bl_set_swfreq(lm3632_bl);
+ if (ret)
+ return ret;
+
+ /* Set vpos/vneg out */
+ ret = lm3632_bl_bias_output(lm3632_bl);
+ if (ret)
+ return ret;
+ /* Backlight control mode - PWM or I2C */
+ return lm3632_bl_set_ctrl_mode(lm3632_bl);
+}
+
+static int lm3632_bl_parse_dt(struct device *dev, struct lm3632_bl *lm3632_bl)
+{
+ struct device_node *node = dev->of_node;
+ struct lm3632_backlight_platform_data *pdata;
+ int brightness = 0;
+ int ret;
+
+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ /* Channel name */
+ of_property_read_string(node, "bl-name", &pdata->name);
+
+ /* String configuration */
+ if (of_find_property(node, "full-strings-used", NULL))
+ pdata->is_full_strings = true;
+
+ /* PWM mode */
+ of_property_read_u32(node, "pwm-period", &pdata->pwm_period);
+ of_property_read_u32(node, "pwm-max-brightness", &brightness);
+
+ if (pdata->pwm_period > 0) {
+ if (brightness == 0) {
+ dev_err(lm3632_bl->dev,
+ "PWM max brightness should be greater than 0\n");
+ return -EINVAL;
+ }
+
+ ret = lm3632_bl_set_brightness(lm3632_bl, brightness);
+ if (ret) {
+ dev_err(lm3632_bl->dev,
+ "PWM max brightness set error: %d\n", ret);
+ return ret;
+ }
+ }
+
+ lm3632_bl->pdata = pdata;
+
+ return 0;
+}
+
+static int lm3632_bl_probe(struct platform_device *pdev)
+{
+ struct lm3632 *lm3632 = dev_get_drvdata(pdev->dev.parent);
+ struct lm3632_backlight_platform_data *pdata = lm3632->pdata->bl_pdata;
+ struct lm3632_bl *lm3632_bl;
+ int ret;
+ printk("zyun lm3632_bl_probe\n");
+ printk("zyun get lm3632 adress=0x%x\n",(int)lm3632);
+ lm3632_bl = devm_kzalloc(&pdev->dev, sizeof(*lm3632_bl), GFP_KERNEL);
+ if (!lm3632_bl)
+ return -ENOMEM;
+
+ lm3632_bl->dev = &pdev->dev;
+ lm3632_bl->lm3632 = lm3632;
+ lm3632_bl->pdata = pdata;
+
+ if (!lm3632_bl->pdata) {
+ if (IS_ENABLED(CONFIG_OF))
+ ret = lm3632_bl_parse_dt(&pdev->dev, lm3632_bl);
+ else
+ return -ENODEV;
+
+ if (ret)
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, lm3632_bl);
+
+ /* Backlight configuration */
+ ret = lm3632_bl_configure(lm3632_bl);
+ if (ret) {
+ dev_err(&pdev->dev, "backlight config err: %d\n", ret);
+ return ret;
+ }
+
+ if (gpio_request(52, "LCD_3V3")) {
+ printk("Failed ro request GPIO_%d \n",52);
+ return -ENODEV;
+ }
+ if (gpio_request(53, "CABC_EN1")) {
+ printk("Failed ro request GPIO_%d \n",53);
+ return -ENODEV;
+ }
+
+ gpio_direction_output(53, 0);
+ gpio_direction_output(52, 0);
+ gpio_set_value(53,0);
+ gpio_set_value(52,0);
+
+ gpio_set_value(53,1);
+ mdelay(5);
+ gpio_set_value(52,1);
+
+ /* Register backlight subsystem */
+ ret = lm3632_bl_register(lm3632_bl);
+ if (ret) {
+ dev_err(&pdev->dev, "register backlight err: %d\n", ret);
+ return ret;
+ }
+
+ backlight_update_status(lm3632_bl->bl_dev);
+
+ return 0;
+}
+
+static int lm3632_bl_remove(struct platform_device *pdev)
+{
+ struct lm3632_bl *lm3632_bl = platform_get_drvdata(pdev);
+ struct backlight_device *bl_dev = lm3632_bl->bl_dev;
+
+ bl_dev->props.brightness = 0;
+ backlight_update_status(bl_dev);
+ lm3632_bl_unregister(lm3632_bl);
+
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id lm3632_bl_of_match[] = {
+ { .compatible = "ti,lm3632-backlight", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, lm3632_bl_of_match);
+#endif
+
+static struct platform_driver lm3632_bl_driver = {
+ .probe = lm3632_bl_probe,
+ .remove = lm3632_bl_remove,
+ .driver = {
+ .name = "lm3632-backlight",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(lm3632_bl_of_match),
+ },
+};
+module_platform_driver(lm3632_bl_driver);
+
+MODULE_DESCRIPTION("TI LM3632 Backlight Driver");
+MODULE_AUTHOR("Milo Kim");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:lm3632-backlight");