diff options
author | Daniel Jeong <gshark.jeong@gmail.com> | 2013-11-13 03:08:58 +0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-11-13 07:09:15 +0400 |
commit | 28e64a68a2ef1c48f30e8b6803725199929069fc (patch) | |
tree | fd10ccee7c2758f37d6dd3c99a0701b29dec42cd /drivers | |
parent | 5812c13a4e636da4bd7f7cabbbbc59d9dbf3c86c (diff) | |
download | linux-28e64a68a2ef1c48f30e8b6803725199929069fc.tar.xz |
backlight: lm3630: apply chip revision
The LM3630 chip was revised by TI and chip name was also changed to
LM3630A. And register map, default values and initial sequences are
changed. The files, lm3630_bl.{c,h} are replaced by lm3630a_bl.{c,h} You
can find more information about LM3630A(datasheet, evm etc) at
http://www.ti.com/product/lm3630a
Signed-off-by: Daniel Jeong <gshark.jeong@gmail.com>
Signed-off-by: Jingoo Han <jg1.han@samsung.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/video/backlight/Kconfig | 6 | ||||
-rw-r--r-- | drivers/video/backlight/Makefile | 2 | ||||
-rw-r--r-- | drivers/video/backlight/lm3630_bl.c | 475 | ||||
-rw-r--r-- | drivers/video/backlight/lm3630a_bl.c | 483 |
4 files changed, 487 insertions, 479 deletions
diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig index a65dd063ecad..5a3eb2ecb525 100644 --- a/drivers/video/backlight/Kconfig +++ b/drivers/video/backlight/Kconfig @@ -368,12 +368,12 @@ config BACKLIGHT_AAT2870 If you have a AnalogicTech AAT2870 say Y to enable the backlight driver. -config BACKLIGHT_LM3630 - tristate "Backlight Driver for LM3630" +config BACKLIGHT_LM3630A + tristate "Backlight Driver for LM3630A" depends on BACKLIGHT_CLASS_DEVICE && I2C select REGMAP_I2C help - This supports TI LM3630 Backlight Driver + This supports TI LM3630A Backlight Driver config BACKLIGHT_LM3639 tristate "Backlight Driver for LM3639" diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile index 38e1babb1946..bb820024f346 100644 --- a/drivers/video/backlight/Makefile +++ b/drivers/video/backlight/Makefile @@ -37,7 +37,7 @@ obj-$(CONFIG_BACKLIGHT_GPIO) += gpio_backlight.o obj-$(CONFIG_BACKLIGHT_HP680) += hp680_bl.o obj-$(CONFIG_BACKLIGHT_HP700) += jornada720_bl.o obj-$(CONFIG_BACKLIGHT_LM3533) += lm3533_bl.o -obj-$(CONFIG_BACKLIGHT_LM3630) += lm3630_bl.o +obj-$(CONFIG_BACKLIGHT_LM3630A) += lm3630a_bl.o obj-$(CONFIG_BACKLIGHT_LM3639) += lm3639_bl.o obj-$(CONFIG_BACKLIGHT_LOCOMO) += locomolcd.o obj-$(CONFIG_BACKLIGHT_LP855X) += lp855x_bl.o diff --git a/drivers/video/backlight/lm3630_bl.c b/drivers/video/backlight/lm3630_bl.c deleted file mode 100644 index 76a62e978fc3..000000000000 --- a/drivers/video/backlight/lm3630_bl.c +++ /dev/null @@ -1,475 +0,0 @@ -/* -* Simple driver for Texas Instruments LM3630 Backlight driver chip -* Copyright (C) 2012 Texas Instruments -* -* 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/module.h> -#include <linux/slab.h> -#include <linux/i2c.h> -#include <linux/backlight.h> -#include <linux/err.h> -#include <linux/delay.h> -#include <linux/uaccess.h> -#include <linux/interrupt.h> -#include <linux/regmap.h> -#include <linux/platform_data/lm3630_bl.h> - -#define REG_CTRL 0x00 -#define REG_CONFIG 0x01 -#define REG_BRT_A 0x03 -#define REG_BRT_B 0x04 -#define REG_INT_STATUS 0x09 -#define REG_INT_EN 0x0A -#define REG_FAULT 0x0B -#define REG_PWM_OUTLOW 0x12 -#define REG_PWM_OUTHIGH 0x13 -#define REG_MAX 0x1F - -#define INT_DEBOUNCE_MSEC 10 - -enum lm3630_leds { - BLED_ALL = 0, - BLED_1, - BLED_2 -}; - -static const char * const bled_name[] = { - [BLED_ALL] = "lm3630_bled", /*Bank1 controls all string */ - [BLED_1] = "lm3630_bled1", /*Bank1 controls bled1 */ - [BLED_2] = "lm3630_bled2", /*Bank1 or 2 controls bled2 */ -}; - -struct lm3630_chip_data { - struct device *dev; - struct delayed_work work; - int irq; - struct workqueue_struct *irqthread; - struct lm3630_platform_data *pdata; - struct backlight_device *bled1; - struct backlight_device *bled2; - struct regmap *regmap; -}; - -/* initialize chip */ -static int lm3630_chip_init(struct lm3630_chip_data *pchip) -{ - int ret; - unsigned int reg_val; - struct lm3630_platform_data *pdata = pchip->pdata; - - /*pwm control */ - reg_val = ((pdata->pwm_active & 0x01) << 2) | (pdata->pwm_ctrl & 0x03); - ret = regmap_update_bits(pchip->regmap, REG_CONFIG, 0x07, reg_val); - if (ret < 0) - goto out; - - /* bank control */ - reg_val = ((pdata->bank_b_ctrl & 0x01) << 1) | - (pdata->bank_a_ctrl & 0x07); - ret = regmap_update_bits(pchip->regmap, REG_CTRL, 0x07, reg_val); - if (ret < 0) - goto out; - - ret = regmap_update_bits(pchip->regmap, REG_CTRL, 0x80, 0x00); - if (ret < 0) - goto out; - - /* set initial brightness */ - if (pdata->bank_a_ctrl != BANK_A_CTRL_DISABLE) { - ret = regmap_write(pchip->regmap, - REG_BRT_A, pdata->init_brt_led1); - if (ret < 0) - goto out; - } - - if (pdata->bank_b_ctrl != BANK_B_CTRL_DISABLE) { - ret = regmap_write(pchip->regmap, - REG_BRT_B, pdata->init_brt_led2); - if (ret < 0) - goto out; - } - return ret; - -out: - dev_err(pchip->dev, "i2c failed to access register\n"); - return ret; -} - -/* interrupt handling */ -static void lm3630_delayed_func(struct work_struct *work) -{ - int ret; - unsigned int reg_val; - struct lm3630_chip_data *pchip; - - pchip = container_of(work, struct lm3630_chip_data, work.work); - - ret = regmap_read(pchip->regmap, REG_INT_STATUS, ®_val); - if (ret < 0) { - dev_err(pchip->dev, - "i2c failed to access REG_INT_STATUS Register\n"); - return; - } - - dev_info(pchip->dev, "REG_INT_STATUS Register is 0x%x\n", reg_val); -} - -static irqreturn_t lm3630_isr_func(int irq, void *chip) -{ - int ret; - struct lm3630_chip_data *pchip = chip; - unsigned long delay = msecs_to_jiffies(INT_DEBOUNCE_MSEC); - - queue_delayed_work(pchip->irqthread, &pchip->work, delay); - - ret = regmap_update_bits(pchip->regmap, REG_CTRL, 0x80, 0x00); - if (ret < 0) - goto out; - - return IRQ_HANDLED; -out: - dev_err(pchip->dev, "i2c failed to access register\n"); - return IRQ_HANDLED; -} - -static int lm3630_intr_config(struct lm3630_chip_data *pchip) -{ - INIT_DELAYED_WORK(&pchip->work, lm3630_delayed_func); - pchip->irqthread = create_singlethread_workqueue("lm3630-irqthd"); - if (!pchip->irqthread) { - dev_err(pchip->dev, "create irq thread fail...\n"); - return -1; - } - if (request_threaded_irq - (pchip->irq, NULL, lm3630_isr_func, - IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "lm3630_irq", pchip)) { - dev_err(pchip->dev, "request threaded irq fail..\n"); - return -1; - } - return 0; -} - -static bool -set_intensity(struct backlight_device *bl, struct lm3630_chip_data *pchip) -{ - if (!pchip->pdata->pwm_set_intensity) - return false; - pchip->pdata->pwm_set_intensity(bl->props.brightness - 1, - pchip->pdata->pwm_period); - return true; -} - -/* update and get brightness */ -static int lm3630_bank_a_update_status(struct backlight_device *bl) -{ - int ret; - struct lm3630_chip_data *pchip = bl_get_data(bl); - enum lm3630_pwm_ctrl pwm_ctrl = pchip->pdata->pwm_ctrl; - - /* brightness 0 means disable */ - if (!bl->props.brightness) { - ret = regmap_update_bits(pchip->regmap, REG_CTRL, 0x04, 0x00); - if (ret < 0) - goto out; - return bl->props.brightness; - } - - /* pwm control */ - if (pwm_ctrl == PWM_CTRL_BANK_A || pwm_ctrl == PWM_CTRL_BANK_ALL) { - if (!set_intensity(bl, pchip)) - dev_err(pchip->dev, "No pwm control func. in plat-data\n"); - } else { - - /* i2c control */ - ret = regmap_update_bits(pchip->regmap, REG_CTRL, 0x80, 0x00); - if (ret < 0) - goto out; - mdelay(1); - ret = regmap_write(pchip->regmap, - REG_BRT_A, bl->props.brightness - 1); - if (ret < 0) - goto out; - } - return bl->props.brightness; -out: - dev_err(pchip->dev, "i2c failed to access REG_CTRL\n"); - return bl->props.brightness; -} - -static int lm3630_bank_a_get_brightness(struct backlight_device *bl) -{ - unsigned int reg_val; - int brightness, ret; - struct lm3630_chip_data *pchip = bl_get_data(bl); - enum lm3630_pwm_ctrl pwm_ctrl = pchip->pdata->pwm_ctrl; - - if (pwm_ctrl == PWM_CTRL_BANK_A || pwm_ctrl == PWM_CTRL_BANK_ALL) { - ret = regmap_read(pchip->regmap, REG_PWM_OUTHIGH, ®_val); - if (ret < 0) - goto out; - brightness = reg_val & 0x01; - ret = regmap_read(pchip->regmap, REG_PWM_OUTLOW, ®_val); - if (ret < 0) - goto out; - brightness = ((brightness << 8) | reg_val) + 1; - } else { - ret = regmap_update_bits(pchip->regmap, REG_CTRL, 0x80, 0x00); - if (ret < 0) - goto out; - mdelay(1); - ret = regmap_read(pchip->regmap, REG_BRT_A, ®_val); - if (ret < 0) - goto out; - brightness = reg_val + 1; - } - bl->props.brightness = brightness; - return bl->props.brightness; -out: - dev_err(pchip->dev, "i2c failed to access register\n"); - return 0; -} - -static const struct backlight_ops lm3630_bank_a_ops = { - .options = BL_CORE_SUSPENDRESUME, - .update_status = lm3630_bank_a_update_status, - .get_brightness = lm3630_bank_a_get_brightness, -}; - -static int lm3630_bank_b_update_status(struct backlight_device *bl) -{ - int ret; - struct lm3630_chip_data *pchip = bl_get_data(bl); - enum lm3630_pwm_ctrl pwm_ctrl = pchip->pdata->pwm_ctrl; - - if (pwm_ctrl == PWM_CTRL_BANK_B || pwm_ctrl == PWM_CTRL_BANK_ALL) { - if (!set_intensity(bl, pchip)) - dev_err(pchip->dev, - "no pwm control func. in plat-data\n"); - } else { - ret = regmap_update_bits(pchip->regmap, REG_CTRL, 0x80, 0x00); - if (ret < 0) - goto out; - mdelay(1); - ret = regmap_write(pchip->regmap, - REG_BRT_B, bl->props.brightness - 1); - } - return bl->props.brightness; -out: - dev_err(pchip->dev, "i2c failed to access register\n"); - return bl->props.brightness; -} - -static int lm3630_bank_b_get_brightness(struct backlight_device *bl) -{ - unsigned int reg_val; - int brightness, ret; - struct lm3630_chip_data *pchip = bl_get_data(bl); - enum lm3630_pwm_ctrl pwm_ctrl = pchip->pdata->pwm_ctrl; - - if (pwm_ctrl == PWM_CTRL_BANK_B || pwm_ctrl == PWM_CTRL_BANK_ALL) { - ret = regmap_read(pchip->regmap, REG_PWM_OUTHIGH, ®_val); - if (ret < 0) - goto out; - brightness = reg_val & 0x01; - ret = regmap_read(pchip->regmap, REG_PWM_OUTLOW, ®_val); - if (ret < 0) - goto out; - brightness = ((brightness << 8) | reg_val) + 1; - } else { - ret = regmap_update_bits(pchip->regmap, REG_CTRL, 0x80, 0x00); - if (ret < 0) - goto out; - mdelay(1); - ret = regmap_read(pchip->regmap, REG_BRT_B, ®_val); - if (ret < 0) - goto out; - brightness = reg_val + 1; - } - bl->props.brightness = brightness; - - return bl->props.brightness; -out: - dev_err(pchip->dev, "i2c failed to access register\n"); - return bl->props.brightness; -} - -static const struct backlight_ops lm3630_bank_b_ops = { - .options = BL_CORE_SUSPENDRESUME, - .update_status = lm3630_bank_b_update_status, - .get_brightness = lm3630_bank_b_get_brightness, -}; - -static int lm3630_backlight_register(struct lm3630_chip_data *pchip, - enum lm3630_leds ledno) -{ - const char *name = bled_name[ledno]; - struct backlight_properties props; - struct lm3630_platform_data *pdata = pchip->pdata; - - props.type = BACKLIGHT_RAW; - switch (ledno) { - case BLED_1: - case BLED_ALL: - props.brightness = pdata->init_brt_led1; - props.max_brightness = pdata->max_brt_led1; - pchip->bled1 = - backlight_device_register(name, pchip->dev, pchip, - &lm3630_bank_a_ops, &props); - if (IS_ERR(pchip->bled1)) - return PTR_ERR(pchip->bled1); - break; - case BLED_2: - props.brightness = pdata->init_brt_led2; - props.max_brightness = pdata->max_brt_led2; - pchip->bled2 = - backlight_device_register(name, pchip->dev, pchip, - &lm3630_bank_b_ops, &props); - if (IS_ERR(pchip->bled2)) - return PTR_ERR(pchip->bled2); - break; - } - return 0; -} - -static void lm3630_backlight_unregister(struct lm3630_chip_data *pchip) -{ - if (pchip->bled1) - backlight_device_unregister(pchip->bled1); - if (pchip->bled2) - backlight_device_unregister(pchip->bled2); -} - -static const struct regmap_config lm3630_regmap = { - .reg_bits = 8, - .val_bits = 8, - .max_register = REG_MAX, -}; - -static int lm3630_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct lm3630_platform_data *pdata = client->dev.platform_data; - struct lm3630_chip_data *pchip; - int ret; - - if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { - dev_err(&client->dev, "fail : i2c functionality check...\n"); - return -EOPNOTSUPP; - } - - if (pdata == NULL) { - dev_err(&client->dev, "fail : no platform data.\n"); - return -ENODATA; - } - - pchip = devm_kzalloc(&client->dev, sizeof(struct lm3630_chip_data), - GFP_KERNEL); - if (!pchip) - return -ENOMEM; - pchip->pdata = pdata; - pchip->dev = &client->dev; - - pchip->regmap = devm_regmap_init_i2c(client, &lm3630_regmap); - if (IS_ERR(pchip->regmap)) { - ret = PTR_ERR(pchip->regmap); - dev_err(&client->dev, "fail : allocate register map: %d\n", - ret); - return ret; - } - i2c_set_clientdata(client, pchip); - - /* chip initialize */ - ret = lm3630_chip_init(pchip); - if (ret < 0) { - dev_err(&client->dev, "fail : init chip\n"); - goto err_chip_init; - } - - switch (pdata->bank_a_ctrl) { - case BANK_A_CTRL_ALL: - ret = lm3630_backlight_register(pchip, BLED_ALL); - pdata->bank_b_ctrl = BANK_B_CTRL_DISABLE; - break; - case BANK_A_CTRL_LED1: - ret = lm3630_backlight_register(pchip, BLED_1); - break; - case BANK_A_CTRL_LED2: - ret = lm3630_backlight_register(pchip, BLED_2); - pdata->bank_b_ctrl = BANK_B_CTRL_DISABLE; - break; - default: - break; - } - - if (ret < 0) - goto err_bl_reg; - - if (pdata->bank_b_ctrl && pchip->bled2 == NULL) { - ret = lm3630_backlight_register(pchip, BLED_2); - if (ret < 0) - goto err_bl_reg; - } - - /* interrupt enable : irq 0 is not allowed for lm3630 */ - pchip->irq = client->irq; - if (pchip->irq) - lm3630_intr_config(pchip); - - dev_info(&client->dev, "LM3630 backlight register OK.\n"); - return 0; - -err_bl_reg: - dev_err(&client->dev, "fail : backlight register.\n"); - lm3630_backlight_unregister(pchip); -err_chip_init: - return ret; -} - -static int lm3630_remove(struct i2c_client *client) -{ - int ret; - struct lm3630_chip_data *pchip = i2c_get_clientdata(client); - - ret = regmap_write(pchip->regmap, REG_BRT_A, 0); - if (ret < 0) - dev_err(pchip->dev, "i2c failed to access register\n"); - - ret = regmap_write(pchip->regmap, REG_BRT_B, 0); - if (ret < 0) - dev_err(pchip->dev, "i2c failed to access register\n"); - - lm3630_backlight_unregister(pchip); - if (pchip->irq) { - free_irq(pchip->irq, pchip); - flush_workqueue(pchip->irqthread); - destroy_workqueue(pchip->irqthread); - } - return 0; -} - -static const struct i2c_device_id lm3630_id[] = { - {LM3630_NAME, 0}, - {} -}; - -MODULE_DEVICE_TABLE(i2c, lm3630_id); - -static struct i2c_driver lm3630_i2c_driver = { - .driver = { - .name = LM3630_NAME, - }, - .probe = lm3630_probe, - .remove = lm3630_remove, - .id_table = lm3630_id, -}; - -module_i2c_driver(lm3630_i2c_driver); - -MODULE_DESCRIPTION("Texas Instruments Backlight driver for LM3630"); -MODULE_AUTHOR("G.Shark Jeong <gshark.jeong@gmail.com>"); -MODULE_AUTHOR("Daniel Jeong <daniel.jeong@ti.com>"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/video/backlight/lm3630a_bl.c b/drivers/video/backlight/lm3630a_bl.c new file mode 100644 index 000000000000..cf40cc8e662b --- /dev/null +++ b/drivers/video/backlight/lm3630a_bl.c @@ -0,0 +1,483 @@ +/* +* Simple driver for Texas Instruments LM3630A Backlight driver chip +* Copyright (C) 2012 Texas Instruments +* +* 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/module.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/backlight.h> +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/uaccess.h> +#include <linux/interrupt.h> +#include <linux/regmap.h> +#include <linux/pwm.h> +#include <linux/platform_data/lm3630a_bl.h> + +#define REG_CTRL 0x00 +#define REG_BOOST 0x02 +#define REG_CONFIG 0x01 +#define REG_BRT_A 0x03 +#define REG_BRT_B 0x04 +#define REG_I_A 0x05 +#define REG_I_B 0x06 +#define REG_INT_STATUS 0x09 +#define REG_INT_EN 0x0A +#define REG_FAULT 0x0B +#define REG_PWM_OUTLOW 0x12 +#define REG_PWM_OUTHIGH 0x13 +#define REG_MAX 0x1F + +#define INT_DEBOUNCE_MSEC 10 +struct lm3630a_chip { + struct device *dev; + struct delayed_work work; + + int irq; + struct workqueue_struct *irqthread; + struct lm3630a_platform_data *pdata; + struct backlight_device *bleda; + struct backlight_device *bledb; + struct regmap *regmap; + struct pwm_device *pwmd; +}; + +/* i2c access */ +static int lm3630a_read(struct lm3630a_chip *pchip, unsigned int reg) +{ + int rval; + unsigned int reg_val; + + rval = regmap_read(pchip->regmap, reg, ®_val); + if (rval < 0) + return rval; + return reg_val & 0xFF; +} + +static int lm3630a_write(struct lm3630a_chip *pchip, + unsigned int reg, unsigned int data) +{ + return regmap_write(pchip->regmap, reg, data); +} + +static int lm3630a_update(struct lm3630a_chip *pchip, + unsigned int reg, unsigned int mask, + unsigned int data) +{ + return regmap_update_bits(pchip->regmap, reg, mask, data); +} + +/* initialize chip */ +static int lm3630a_chip_init(struct lm3630a_chip *pchip) +{ + int rval; + struct lm3630a_platform_data *pdata = pchip->pdata; + + usleep_range(1000, 2000); + /* set Filter Strength Register */ + rval = lm3630a_write(pchip, 0x50, 0x03); + /* set Cofig. register */ + rval |= lm3630a_update(pchip, REG_CONFIG, 0x07, pdata->pwm_ctrl); + /* set boost control */ + rval |= lm3630a_write(pchip, REG_BOOST, 0x38); + /* set current A */ + rval |= lm3630a_update(pchip, REG_I_A, 0x1F, 0x1F); + /* set current B */ + rval |= lm3630a_write(pchip, REG_I_B, 0x1F); + /* set control */ + rval |= + lm3630a_write(pchip, REG_CTRL, pdata->leda_ctrl | pdata->ledb_ctrl); + usleep_range(1000, 2000); + /* set brightness A and B */ + rval |= lm3630a_write(pchip, REG_BRT_A, pdata->leda_init_brt); + rval |= lm3630a_write(pchip, REG_BRT_B, pdata->ledb_init_brt); + + if (rval < 0) + dev_err(pchip->dev, "i2c failed to access register\n"); + return rval; +} + +/* interrupt handling */ +static void lm3630a_delayed_func(struct work_struct *work) +{ + unsigned int rval; + struct lm3630a_chip *pchip; + + pchip = container_of(work, struct lm3630a_chip, work.work); + + rval = lm3630a_read(pchip, REG_INT_STATUS); + if (rval < 0) { + dev_err(pchip->dev, + "i2c failed to access REG_INT_STATUS Register\n"); + return; + } + + dev_info(pchip->dev, "REG_INT_STATUS Register is 0x%x\n", rval); +} + +static irqreturn_t lm3630a_isr_func(int irq, void *chip) +{ + int rval; + struct lm3630a_chip *pchip = chip; + unsigned long delay = msecs_to_jiffies(INT_DEBOUNCE_MSEC); + + queue_delayed_work(pchip->irqthread, &pchip->work, delay); + + rval = lm3630a_update(pchip, REG_CTRL, 0x80, 0x00); + if (rval < 0) { + dev_err(pchip->dev, "i2c failed to access register\n"); + return IRQ_NONE; + } + return IRQ_HANDLED; +} + +static int lm3630a_intr_config(struct lm3630a_chip *pchip) +{ + int rval; + + rval = lm3630a_write(pchip, REG_INT_EN, 0x87); + if (rval < 0) + return rval; + + INIT_DELAYED_WORK(&pchip->work, lm3630a_delayed_func); + pchip->irqthread = create_singlethread_workqueue("lm3630a-irqthd"); + if (!pchip->irqthread) { + dev_err(pchip->dev, "create irq thread fail\n"); + return -ENOMEM; + } + if (request_threaded_irq + (pchip->irq, NULL, lm3630a_isr_func, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "lm3630a_irq", pchip)) { + dev_err(pchip->dev, "request threaded irq fail\n"); + return -ENOMEM; + } + return rval; +} + +static void lm3630a_pwm_ctrl(struct lm3630a_chip *pchip, int br, int br_max) +{ + unsigned int period = pwm_get_period(pchip->pwmd); + unsigned int duty = br * period / br_max; + + pwm_config(pchip->pwmd, duty, period); + if (duty) + pwm_enable(pchip->pwmd); + else + pwm_disable(pchip->pwmd); +} + +/* update and get brightness */ +static int lm3630a_bank_a_update_status(struct backlight_device *bl) +{ + int ret; + struct lm3630a_chip *pchip = bl_get_data(bl); + enum lm3630a_pwm_ctrl pwm_ctrl = pchip->pdata->pwm_ctrl; + + /* pwm control */ + if ((pwm_ctrl & LM3630A_PWM_BANK_A) != 0) { + lm3630a_pwm_ctrl(pchip, bl->props.brightness, + bl->props.max_brightness); + return bl->props.brightness; + } + + /* disable sleep */ + ret = lm3630a_update(pchip, REG_CTRL, 0x80, 0x00); + if (ret < 0) + goto out_i2c_err; + usleep_range(1000, 2000); + /* minimum brightness is 0x04 */ + ret = lm3630a_write(pchip, REG_BRT_A, bl->props.brightness); + if (bl->props.brightness < 0x4) + ret |= lm3630a_update(pchip, REG_CTRL, LM3630A_LEDA_ENABLE, 0); + else + ret |= lm3630a_update(pchip, REG_CTRL, + LM3630A_LEDA_ENABLE, LM3630A_LEDA_ENABLE); + if (ret < 0) + goto out_i2c_err; + return bl->props.brightness; + +out_i2c_err: + dev_err(pchip->dev, "i2c failed to access\n"); + return bl->props.brightness; +} + +static int lm3630a_bank_a_get_brightness(struct backlight_device *bl) +{ + int brightness, rval; + struct lm3630a_chip *pchip = bl_get_data(bl); + enum lm3630a_pwm_ctrl pwm_ctrl = pchip->pdata->pwm_ctrl; + + if ((pwm_ctrl & LM3630A_PWM_BANK_A) != 0) { + rval = lm3630a_read(pchip, REG_PWM_OUTHIGH); + if (rval < 0) + goto out_i2c_err; + brightness = (rval & 0x01) << 8; + rval = lm3630a_read(pchip, REG_PWM_OUTLOW); + if (rval < 0) + goto out_i2c_err; + brightness |= rval; + goto out; + } + + /* disable sleep */ + rval = lm3630a_update(pchip, REG_CTRL, 0x80, 0x00); + if (rval < 0) + goto out_i2c_err; + usleep_range(1000, 2000); + rval = lm3630a_read(pchip, REG_BRT_A); + if (rval < 0) + goto out_i2c_err; + brightness = rval; + +out: + bl->props.brightness = brightness; + return bl->props.brightness; +out_i2c_err: + dev_err(pchip->dev, "i2c failed to access register\n"); + return 0; +} + +static const struct backlight_ops lm3630a_bank_a_ops = { + .options = BL_CORE_SUSPENDRESUME, + .update_status = lm3630a_bank_a_update_status, + .get_brightness = lm3630a_bank_a_get_brightness, +}; + +/* update and get brightness */ +static int lm3630a_bank_b_update_status(struct backlight_device *bl) +{ + int ret; + struct lm3630a_chip *pchip = bl_get_data(bl); + enum lm3630a_pwm_ctrl pwm_ctrl = pchip->pdata->pwm_ctrl; + + /* pwm control */ + if ((pwm_ctrl & LM3630A_PWM_BANK_B) != 0) { + lm3630a_pwm_ctrl(pchip, bl->props.brightness, + bl->props.max_brightness); + return bl->props.brightness; + } + + /* disable sleep */ + ret = lm3630a_update(pchip, REG_CTRL, 0x80, 0x00); + if (ret < 0) + goto out_i2c_err; + usleep_range(1000, 2000); + /* minimum brightness is 0x04 */ + ret = lm3630a_write(pchip, REG_BRT_B, bl->props.brightness); + if (bl->props.brightness < 0x4) + ret |= lm3630a_update(pchip, REG_CTRL, LM3630A_LEDB_ENABLE, 0); + else + ret |= lm3630a_update(pchip, REG_CTRL, + LM3630A_LEDB_ENABLE, LM3630A_LEDB_ENABLE); + if (ret < 0) + goto out_i2c_err; + return bl->props.brightness; + +out_i2c_err: + dev_err(pchip->dev, "i2c failed to access REG_CTRL\n"); + return bl->props.brightness; +} + +static int lm3630a_bank_b_get_brightness(struct backlight_device *bl) +{ + int brightness, rval; + struct lm3630a_chip *pchip = bl_get_data(bl); + enum lm3630a_pwm_ctrl pwm_ctrl = pchip->pdata->pwm_ctrl; + + if ((pwm_ctrl & LM3630A_PWM_BANK_B) != 0) { + rval = lm3630a_read(pchip, REG_PWM_OUTHIGH); + if (rval < 0) + goto out_i2c_err; + brightness = (rval & 0x01) << 8; + rval = lm3630a_read(pchip, REG_PWM_OUTLOW); + if (rval < 0) + goto out_i2c_err; + brightness |= rval; + goto out; + } + + /* disable sleep */ + rval = lm3630a_update(pchip, REG_CTRL, 0x80, 0x00); + if (rval < 0) + goto out_i2c_err; + usleep_range(1000, 2000); + rval = lm3630a_read(pchip, REG_BRT_B); + if (rval < 0) + goto out_i2c_err; + brightness = rval; + +out: + bl->props.brightness = brightness; + return bl->props.brightness; +out_i2c_err: + dev_err(pchip->dev, "i2c failed to access register\n"); + return 0; +} + +static const struct backlight_ops lm3630a_bank_b_ops = { + .options = BL_CORE_SUSPENDRESUME, + .update_status = lm3630a_bank_b_update_status, + .get_brightness = lm3630a_bank_b_get_brightness, +}; + +static int lm3630a_backlight_register(struct lm3630a_chip *pchip) +{ + struct backlight_properties props; + struct lm3630a_platform_data *pdata = pchip->pdata; + + props.type = BACKLIGHT_RAW; + if (pdata->leda_ctrl != LM3630A_LEDA_DISABLE) { + props.brightness = pdata->leda_init_brt; + props.max_brightness = pdata->leda_max_brt; + pchip->bleda = + devm_backlight_device_register(pchip->dev, "lm3630a_leda", + pchip->dev, pchip, + &lm3630a_bank_a_ops, &props); + if (IS_ERR(pchip->bleda)) + return PTR_ERR(pchip->bleda); + } + + if ((pdata->ledb_ctrl != LM3630A_LEDB_DISABLE) && + (pdata->ledb_ctrl != LM3630A_LEDB_ON_A)) { + props.brightness = pdata->ledb_init_brt; + props.max_brightness = pdata->ledb_max_brt; + pchip->bledb = + devm_backlight_device_register(pchip->dev, "lm3630a_ledb", + pchip->dev, pchip, + &lm3630a_bank_b_ops, &props); + if (IS_ERR(pchip->bledb)) + return PTR_ERR(pchip->bledb); + } + return 0; +} + +static const struct regmap_config lm3630a_regmap = { + .reg_bits = 8, + .val_bits = 8, + .max_register = REG_MAX, +}; + +static int lm3630a_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct lm3630a_platform_data *pdata = client->dev.platform_data; + struct lm3630a_chip *pchip; + int rval; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_err(&client->dev, "fail : i2c functionality check\n"); + return -EOPNOTSUPP; + } + + pchip = devm_kzalloc(&client->dev, sizeof(struct lm3630a_chip), + GFP_KERNEL); + if (!pchip) + return -ENOMEM; + pchip->dev = &client->dev; + + pchip->regmap = devm_regmap_init_i2c(client, &lm3630a_regmap); + if (IS_ERR(pchip->regmap)) { + rval = PTR_ERR(pchip->regmap); + dev_err(&client->dev, "fail : allocate reg. map: %d\n", rval); + return rval; + } + + i2c_set_clientdata(client, pchip); + if (pdata == NULL) { + pchip->pdata = devm_kzalloc(pchip->dev, + sizeof(struct + lm3630a_platform_data), + GFP_KERNEL); + if (pchip->pdata == NULL) + return -ENOMEM; + /* default values */ + pchip->pdata->leda_ctrl = LM3630A_LEDA_ENABLE; + pchip->pdata->ledb_ctrl = LM3630A_LEDB_ENABLE; + pchip->pdata->leda_max_brt = LM3630A_MAX_BRIGHTNESS; + pchip->pdata->ledb_max_brt = LM3630A_MAX_BRIGHTNESS; + pchip->pdata->leda_init_brt = LM3630A_MAX_BRIGHTNESS; + pchip->pdata->ledb_init_brt = LM3630A_MAX_BRIGHTNESS; + } else { + pchip->pdata = pdata; + } + /* chip initialize */ + rval = lm3630a_chip_init(pchip); + if (rval < 0) { + dev_err(&client->dev, "fail : init chip\n"); + return rval; + } + /* backlight register */ + rval = lm3630a_backlight_register(pchip); + if (rval < 0) { + dev_err(&client->dev, "fail : backlight register.\n"); + return rval; + } + /* pwm */ + if (pdata->pwm_ctrl != LM3630A_PWM_DISABLE) { + pchip->pwmd = devm_pwm_get(pchip->dev, "lm3630a-pwm"); + if (IS_ERR(pchip->pwmd)) { + dev_err(&client->dev, "fail : get pwm device\n"); + return PTR_ERR(pchip->pwmd); + } + } + pchip->pwmd->period = pdata->pwm_period; + + /* interrupt enable : irq 0 is not allowed */ + pchip->irq = client->irq; + if (pchip->irq) { + rval = lm3630a_intr_config(pchip); + if (rval < 0) + return rval; + } + dev_info(&client->dev, "LM3630A backlight register OK.\n"); + return 0; +} + +static int lm3630a_remove(struct i2c_client *client) +{ + int rval; + struct lm3630a_chip *pchip = i2c_get_clientdata(client); + + rval = lm3630a_write(pchip, REG_BRT_A, 0); + if (rval < 0) + dev_err(pchip->dev, "i2c failed to access register\n"); + + rval = lm3630a_write(pchip, REG_BRT_B, 0); + if (rval < 0) + dev_err(pchip->dev, "i2c failed to access register\n"); + + if (pchip->irq) { + free_irq(pchip->irq, pchip); + flush_workqueue(pchip->irqthread); + destroy_workqueue(pchip->irqthread); + } + return 0; +} + +static const struct i2c_device_id lm3630a_id[] = { + {LM3630A_NAME, 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, lm3630a_id); + +static struct i2c_driver lm3630a_i2c_driver = { + .driver = { + .name = LM3630A_NAME, + }, + .probe = lm3630a_probe, + .remove = lm3630a_remove, + .id_table = lm3630a_id, +}; + +module_i2c_driver(lm3630a_i2c_driver); + +MODULE_DESCRIPTION("Texas Instruments Backlight driver for LM3630A"); +MODULE_AUTHOR("Daniel Jeong <gshark.jeong@gmail.com>"); +MODULE_AUTHOR("LDD MLP <ldd-mlp@list.ti.com>"); +MODULE_LICENSE("GPL v2"); |