From ca7053216833d6b9cd3faaa0dfd2261ce3b9dffc Mon Sep 17 00:00:00 2001 From: "Kim, Milo" Date: Tue, 4 Sep 2012 15:06:11 +0800 Subject: leds-lp5523: support new LP55231 device New TI LP55231 device has same I2C register layout as LP5523. Therefore, all of LED operations can be shared with the LP5523 driver. To support new LP55231 device explicitly, the device description is added in the configuration file. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/Kconfig | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers/leds/Kconfig') diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index c96bbaadeebd..86ea15a127f7 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -192,11 +192,12 @@ config LEDS_LP5521 programming the engines. config LEDS_LP5523 - tristate "LED Support for N.S. LP5523 LED driver chip" + tristate "LED Support for TI/National LP5523/55231 LED driver chip" depends on LEDS_CLASS && I2C help - If you say yes here you get support for the National Semiconductor - LP5523 LED driver. It is 9 channel chip with programmable engines. + If you say yes here you get support for TI/National Semiconductor + LP5523/55231 LED driver. + It is 9 channel chip with programmable engines. Driver provides direct control via LED class and interface for programming the engines. -- cgit v1.2.3 From b98d13c725920e9ab7696e0d7d19c4db1bdf6737 Mon Sep 17 00:00:00 2001 From: "G.Shark Jeong" Date: Wed, 5 Sep 2012 15:05:58 +0800 Subject: leds: Add new LED driver for lm355x chips This driver is a general version for LM355x,lm3554 and lm3556,led chips of TI. LM3554 : The LM3554 is a 2 MHz fixed-frequency synchronous boost converter with 1.2A dual high side led drivers. Datasheet: www.ti.com/lit/ds/symlink/lm3554.pdf LM3556 : The LM3556 is a 4 MHz fixed-frequency synchronous boost converter plus 1.5A constant current driver for a high-current white LED. Datasheet: www.national.com/ds/LM/LM3556.pdf (bryan.wu@canonical.com: use flush_work() to replace flush_work_sync() which is deprecated) Signed-off-by: G.Shark Jeong Signed-off-by: Bryan Wu --- drivers/leds/Kconfig | 8 +- drivers/leds/Makefile | 2 +- drivers/leds/leds-lm3556.c | 512 -------------------------- drivers/leds/leds-lm355x.c | 572 ++++++++++++++++++++++++++++++ include/linux/platform_data/leds-lm3556.h | 50 --- include/linux/platform_data/leds-lm355x.h | 66 ++++ 6 files changed, 643 insertions(+), 567 deletions(-) delete mode 100644 drivers/leds/leds-lm3556.c create mode 100644 drivers/leds/leds-lm355x.c delete mode 100644 include/linux/platform_data/leds-lm3556.h create mode 100644 include/linux/platform_data/leds-lm355x.h (limited to 'drivers/leds/Kconfig') diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 86ea15a127f7..0dcb794df1fa 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -423,13 +423,13 @@ config LEDS_MAX8997 This option enables support for on-chip LED drivers on MAXIM MAX8997 PMIC. -config LEDS_LM3556 - tristate "LED support for LM3556 Chip" +config LEDS_LM355x + tristate "LED support for LM355x Chips, LM3554 and LM3556" depends on LEDS_CLASS && I2C select REGMAP_I2C help - This option enables support for LEDs connected to LM3556. - LM3556 includes Torch, Flash and Indicator functions. + This option enables support for LEDs connected to LM355x. + LM355x includes Torch, Flash and Indicator functions. config LEDS_OT200 tristate "LED support for the Bachmann OT200" diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index a4429a9217bc..b57a021d1fea 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -48,7 +48,7 @@ obj-$(CONFIG_LEDS_NETXBIG) += leds-netxbig.o obj-$(CONFIG_LEDS_ASIC3) += leds-asic3.o obj-$(CONFIG_LEDS_RENESAS_TPU) += leds-renesas-tpu.o obj-$(CONFIG_LEDS_MAX8997) += leds-max8997.o -obj-$(CONFIG_LEDS_LM3556) += leds-lm3556.o +obj-$(CONFIG_LEDS_LM355x) += leds-lm355x.o obj-$(CONFIG_LEDS_BLINKM) += leds-blinkm.o # LED SPI Drivers diff --git a/drivers/leds/leds-lm3556.c b/drivers/leds/leds-lm3556.c deleted file mode 100644 index 3062abd9a532..000000000000 --- a/drivers/leds/leds-lm3556.c +++ /dev/null @@ -1,512 +0,0 @@ -/* - * Simple driver for Texas Instruments LM3556 LED Flash driver chip (Rev0x03) - * 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. - * - * Please refer Documentation/leds/leds-lm3556.txt file. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define REG_FILT_TIME (0x0) -#define REG_IVFM_MODE (0x1) -#define REG_NTC (0x2) -#define REG_INDIC_TIME (0x3) -#define REG_INDIC_BLINK (0x4) -#define REG_INDIC_PERIOD (0x5) -#define REG_TORCH_TIME (0x6) -#define REG_CONF (0x7) -#define REG_FLASH (0x8) -#define REG_I_CTRL (0x9) -#define REG_ENABLE (0xA) -#define REG_FLAG (0xB) -#define REG_MAX (0xB) - -#define IVFM_FILTER_TIME_SHIFT (3) -#define UVLO_EN_SHIFT (7) -#define HYSTERSIS_SHIFT (5) -#define IVM_D_TH_SHIFT (2) -#define IVFM_ADJ_MODE_SHIFT (0) -#define NTC_EVENT_LVL_SHIFT (5) -#define NTC_TRIP_TH_SHIFT (2) -#define NTC_BIAS_I_LVL_SHIFT (0) -#define INDIC_RAMP_UP_TIME_SHIFT (3) -#define INDIC_RAMP_DN_TIME_SHIFT (0) -#define INDIC_N_BLANK_SHIFT (4) -#define INDIC_PULSE_TIME_SHIFT (0) -#define INDIC_N_PERIOD_SHIFT (0) -#define TORCH_RAMP_UP_TIME_SHIFT (3) -#define TORCH_RAMP_DN_TIME_SHIFT (0) -#define STROBE_USUAGE_SHIFT (7) -#define STROBE_PIN_POLARITY_SHIFT (6) -#define TORCH_PIN_POLARITY_SHIFT (5) -#define TX_PIN_POLARITY_SHIFT (4) -#define TX_EVENT_LVL_SHIFT (3) -#define IVFM_EN_SHIFT (2) -#define NTC_MODE_SHIFT (1) -#define INDIC_MODE_SHIFT (0) -#define INDUCTOR_I_LIMIT_SHIFT (6) -#define FLASH_RAMP_TIME_SHIFT (3) -#define FLASH_TOUT_TIME_SHIFT (0) -#define TORCH_I_SHIFT (4) -#define FLASH_I_SHIFT (0) -#define NTC_EN_SHIFT (7) -#define TX_PIN_EN_SHIFT (6) -#define STROBE_PIN_EN_SHIFT (5) -#define TORCH_PIN_EN_SHIFT (4) -#define PRECHG_MODE_EN_SHIFT (3) -#define PASS_MODE_ONLY_EN_SHIFT (2) -#define MODE_BITS_SHIFT (0) - -#define IVFM_FILTER_TIME_MASK (0x3) -#define UVLO_EN_MASK (0x1) -#define HYSTERSIS_MASK (0x3) -#define IVM_D_TH_MASK (0x7) -#define IVFM_ADJ_MODE_MASK (0x3) -#define NTC_EVENT_LVL_MASK (0x1) -#define NTC_TRIP_TH_MASK (0x7) -#define NTC_BIAS_I_LVL_MASK (0x3) -#define INDIC_RAMP_UP_TIME_MASK (0x7) -#define INDIC_RAMP_DN_TIME_MASK (0x7) -#define INDIC_N_BLANK_MASK (0x7) -#define INDIC_PULSE_TIME_MASK (0x7) -#define INDIC_N_PERIOD_MASK (0x7) -#define TORCH_RAMP_UP_TIME_MASK (0x7) -#define TORCH_RAMP_DN_TIME_MASK (0x7) -#define STROBE_USUAGE_MASK (0x1) -#define STROBE_PIN_POLARITY_MASK (0x1) -#define TORCH_PIN_POLARITY_MASK (0x1) -#define TX_PIN_POLARITY_MASK (0x1) -#define TX_EVENT_LVL_MASK (0x1) -#define IVFM_EN_MASK (0x1) -#define NTC_MODE_MASK (0x1) -#define INDIC_MODE_MASK (0x1) -#define INDUCTOR_I_LIMIT_MASK (0x3) -#define FLASH_RAMP_TIME_MASK (0x7) -#define FLASH_TOUT_TIME_MASK (0x7) -#define TORCH_I_MASK (0x7) -#define FLASH_I_MASK (0xF) -#define NTC_EN_MASK (0x1) -#define TX_PIN_EN_MASK (0x1) -#define STROBE_PIN_EN_MASK (0x1) -#define TORCH_PIN_EN_MASK (0x1) -#define PRECHG_MODE_EN_MASK (0x1) -#define PASS_MODE_ONLY_EN_MASK (0x1) -#define MODE_BITS_MASK (0x13) -#define EX_PIN_CONTROL_MASK (0xF1) -#define EX_PIN_ENABLE_MASK (0x70) - -enum lm3556_indic_pulse_time { - PULSE_TIME_0_MS = 0, - PULSE_TIME_32_MS, - PULSE_TIME_64_MS, - PULSE_TIME_92_MS, - PULSE_TIME_128_MS, - PULSE_TIME_160_MS, - PULSE_TIME_196_MS, - PULSE_TIME_224_MS, - PULSE_TIME_256_MS, - PULSE_TIME_288_MS, - PULSE_TIME_320_MS, - PULSE_TIME_352_MS, - PULSE_TIME_384_MS, - PULSE_TIME_416_MS, - PULSE_TIME_448_MS, - PULSE_TIME_480_MS, -}; - -enum lm3556_indic_n_blank { - INDIC_N_BLANK_0 = 0, - INDIC_N_BLANK_1, - INDIC_N_BLANK_2, - INDIC_N_BLANK_3, - INDIC_N_BLANK_4, - INDIC_N_BLANK_5, - INDIC_N_BLANK_6, - INDIC_N_BLANK_7, - INDIC_N_BLANK_8, - INDIC_N_BLANK_9, - INDIC_N_BLANK_10, - INDIC_N_BLANK_11, - INDIC_N_BLANK_12, - INDIC_N_BLANK_13, - INDIC_N_BLANK_14, - INDIC_N_BLANK_15, -}; - -enum lm3556_indic_period { - INDIC_PERIOD_0 = 0, - INDIC_PERIOD_1, - INDIC_PERIOD_2, - INDIC_PERIOD_3, - INDIC_PERIOD_4, - INDIC_PERIOD_5, - INDIC_PERIOD_6, - INDIC_PERIOD_7, -}; - -enum lm3556_mode { - MODES_STASNDBY = 0, - MODES_INDIC, - MODES_TORCH, - MODES_FLASH -}; - -#define INDIC_PATTERN_SIZE 4 - -struct indicator { - u8 blinking; - u8 period_cnt; -}; - -struct lm3556_chip_data { - struct device *dev; - - struct led_classdev cdev_flash; - struct led_classdev cdev_torch; - struct led_classdev cdev_indicator; - - struct lm3556_platform_data *pdata; - struct regmap *regmap; - struct mutex lock; - - unsigned int last_flag; -}; - -/* indicator pattern */ -static struct indicator indicator_pattern[INDIC_PATTERN_SIZE] = { - [0] = {(INDIC_N_BLANK_1 << INDIC_N_BLANK_SHIFT) - | PULSE_TIME_32_MS, INDIC_PERIOD_1}, - [1] = {(INDIC_N_BLANK_15 << INDIC_N_BLANK_SHIFT) - | PULSE_TIME_32_MS, INDIC_PERIOD_2}, - [2] = {(INDIC_N_BLANK_10 << INDIC_N_BLANK_SHIFT) - | PULSE_TIME_32_MS, INDIC_PERIOD_4}, - [3] = {(INDIC_N_BLANK_5 << INDIC_N_BLANK_SHIFT) - | PULSE_TIME_32_MS, INDIC_PERIOD_7}, -}; - -/* chip initialize */ -static int __devinit lm3556_chip_init(struct lm3556_chip_data *chip) -{ - unsigned int reg_val; - int ret; - struct lm3556_platform_data *pdata = chip->pdata; - - /* set config register */ - ret = regmap_read(chip->regmap, REG_CONF, ®_val); - if (ret < 0) { - dev_err(chip->dev, "Failed to read REG_CONF Register\n"); - goto out; - } - - reg_val &= (~EX_PIN_CONTROL_MASK); - reg_val |= ((pdata->torch_pin_polarity & 0x01) - << TORCH_PIN_POLARITY_SHIFT); - reg_val |= ((pdata->strobe_usuage & 0x01) << STROBE_USUAGE_SHIFT); - reg_val |= ((pdata->strobe_pin_polarity & 0x01) - << STROBE_PIN_POLARITY_SHIFT); - reg_val |= ((pdata->tx_pin_polarity & 0x01) << TX_PIN_POLARITY_SHIFT); - reg_val |= ((pdata->indicator_mode & 0x01) << INDIC_MODE_SHIFT); - - ret = regmap_write(chip->regmap, REG_CONF, reg_val); - if (ret < 0) { - dev_err(chip->dev, "Failed to write REG_CONF Regisgter\n"); - goto out; - } - - /* set enable register */ - ret = regmap_read(chip->regmap, REG_ENABLE, ®_val); - if (ret < 0) { - dev_err(chip->dev, "Failed to read REG_ENABLE Register\n"); - goto out; - } - - reg_val &= (~EX_PIN_ENABLE_MASK); - reg_val |= ((pdata->torch_pin_en & 0x01) << TORCH_PIN_EN_SHIFT); - reg_val |= ((pdata->strobe_pin_en & 0x01) << STROBE_PIN_EN_SHIFT); - reg_val |= ((pdata->tx_pin_en & 0x01) << TX_PIN_EN_SHIFT); - - ret = regmap_write(chip->regmap, REG_ENABLE, reg_val); - if (ret < 0) { - dev_err(chip->dev, "Failed to write REG_ENABLE Regisgter\n"); - goto out; - } - -out: - return ret; -} - -/* chip control */ -static int lm3556_control(struct lm3556_chip_data *chip, - u8 brightness, enum lm3556_mode opmode) -{ - int ret; - struct lm3556_platform_data *pdata = chip->pdata; - - ret = regmap_read(chip->regmap, REG_FLAG, &chip->last_flag); - if (ret < 0) { - dev_err(chip->dev, "Failed to read REG_FLAG Register\n"); - goto out; - } - - if (chip->last_flag) - dev_info(chip->dev, "Last FLAG is 0x%x\n", chip->last_flag); - - /* brightness 0 means off-state */ - if (!brightness) - opmode = MODES_STASNDBY; - - switch (opmode) { - case MODES_TORCH: - ret = regmap_update_bits(chip->regmap, REG_I_CTRL, - TORCH_I_MASK << TORCH_I_SHIFT, - (brightness - 1) << TORCH_I_SHIFT); - - if (pdata->torch_pin_en) - opmode |= (TORCH_PIN_EN_MASK << TORCH_PIN_EN_SHIFT); - break; - - case MODES_FLASH: - ret = regmap_update_bits(chip->regmap, REG_I_CTRL, - FLASH_I_MASK << FLASH_I_SHIFT, - (brightness - 1) << FLASH_I_SHIFT); - break; - - case MODES_INDIC: - ret = regmap_update_bits(chip->regmap, REG_I_CTRL, - TORCH_I_MASK << TORCH_I_SHIFT, - (brightness - 1) << TORCH_I_SHIFT); - break; - - case MODES_STASNDBY: - if (pdata->torch_pin_en) - opmode |= (TORCH_PIN_EN_MASK << TORCH_PIN_EN_SHIFT); - break; - - default: - return ret; - } - if (ret < 0) { - dev_err(chip->dev, "Failed to write REG_I_CTRL Register\n"); - goto out; - } - ret = regmap_update_bits(chip->regmap, REG_ENABLE, - MODE_BITS_MASK << MODE_BITS_SHIFT, - opmode << MODE_BITS_SHIFT); - -out: - return ret; -} - -/* torch */ -static void lm3556_torch_brightness_set(struct led_classdev *cdev, - enum led_brightness brightness) -{ - struct lm3556_chip_data *chip = - container_of(cdev, struct lm3556_chip_data, cdev_torch); - - mutex_lock(&chip->lock); - lm3556_control(chip, brightness, MODES_TORCH); - mutex_unlock(&chip->lock); -} - -/* flash */ -static void lm3556_strobe_brightness_set(struct led_classdev *cdev, - enum led_brightness brightness) -{ - struct lm3556_chip_data *chip = - container_of(cdev, struct lm3556_chip_data, cdev_flash); - - mutex_lock(&chip->lock); - lm3556_control(chip, brightness, MODES_FLASH); - mutex_unlock(&chip->lock); -} - -/* indicator */ -static void lm3556_indicator_brightness_set(struct led_classdev *cdev, - enum led_brightness brightness) -{ - struct lm3556_chip_data *chip = - container_of(cdev, struct lm3556_chip_data, cdev_indicator); - - mutex_lock(&chip->lock); - lm3556_control(chip, brightness, MODES_INDIC); - mutex_unlock(&chip->lock); -} - -/* indicator pattern */ -static ssize_t lm3556_indicator_pattern_store(struct device *dev, - struct device_attribute *devAttr, - const char *buf, size_t size) -{ - ssize_t ret; - struct led_classdev *led_cdev = dev_get_drvdata(dev); - struct lm3556_chip_data *chip = - container_of(led_cdev, struct lm3556_chip_data, cdev_indicator); - unsigned int state; - - ret = kstrtouint(buf, 10, &state); - if (ret) - goto out; - if (state > INDIC_PATTERN_SIZE - 1) - state = INDIC_PATTERN_SIZE - 1; - - ret = regmap_write(chip->regmap, REG_INDIC_BLINK, - indicator_pattern[state].blinking); - if (ret < 0) { - dev_err(chip->dev, "Failed to write REG_ENABLE Regisgter\n"); - goto out; - } - - ret = regmap_write(chip->regmap, REG_INDIC_PERIOD, - indicator_pattern[state].period_cnt); - if (ret < 0) { - dev_err(chip->dev, "Failed to write REG_ENABLE Regisgter\n"); - goto out; - } - - return size; -out: - dev_err(chip->dev, "Indicator pattern doesn't saved\n"); - return size; -} - -static DEVICE_ATTR(pattern, 0666, NULL, lm3556_indicator_pattern_store); - -static const struct regmap_config lm3556_regmap = { - .reg_bits = 8, - .val_bits = 8, - .max_register = REG_MAX, -}; - -/* module initialize */ -static int __devinit lm3556_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct lm3556_platform_data *pdata = client->dev.platform_data; - struct lm3556_chip_data *chip; - - int err; - - if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { - dev_err(&client->dev, "i2c functionality check fail.\n"); - return -EOPNOTSUPP; - } - - if (pdata == NULL) { - dev_err(&client->dev, "Needs Platform Data.\n"); - return -ENODATA; - } - - chip = - devm_kzalloc(&client->dev, sizeof(struct lm3556_chip_data), - GFP_KERNEL); - if (!chip) - return -ENOMEM; - - chip->dev = &client->dev; - chip->pdata = pdata; - - chip->regmap = devm_regmap_init_i2c(client, &lm3556_regmap); - if (IS_ERR(chip->regmap)) { - err = PTR_ERR(chip->regmap); - dev_err(&client->dev, "Failed to allocate register map: %d\n", - err); - return err; - } - - mutex_init(&chip->lock); - i2c_set_clientdata(client, chip); - - err = lm3556_chip_init(chip); - if (err < 0) - goto err_out; - - /* flash */ - chip->cdev_flash.name = "flash"; - chip->cdev_flash.max_brightness = 16; - chip->cdev_flash.brightness_set = lm3556_strobe_brightness_set; - err = led_classdev_register((struct device *) - &client->dev, &chip->cdev_flash); - if (err < 0) - goto err_out; - /* torch */ - chip->cdev_torch.name = "torch"; - chip->cdev_torch.max_brightness = 8; - chip->cdev_torch.brightness_set = lm3556_torch_brightness_set; - err = led_classdev_register((struct device *) - &client->dev, &chip->cdev_torch); - if (err < 0) - goto err_create_torch_file; - /* indicator */ - chip->cdev_indicator.name = "indicator"; - chip->cdev_indicator.max_brightness = 8; - chip->cdev_indicator.brightness_set = lm3556_indicator_brightness_set; - err = led_classdev_register((struct device *) - &client->dev, &chip->cdev_indicator); - if (err < 0) - goto err_create_indicator_file; - - err = device_create_file(chip->cdev_indicator.dev, &dev_attr_pattern); - if (err < 0) - goto err_create_pattern_file; - - dev_info(&client->dev, "LM3556 is initialized\n"); - return 0; - -err_create_pattern_file: - led_classdev_unregister(&chip->cdev_indicator); -err_create_indicator_file: - led_classdev_unregister(&chip->cdev_torch); -err_create_torch_file: - led_classdev_unregister(&chip->cdev_flash); -err_out: - return err; -} - -static int __devexit lm3556_remove(struct i2c_client *client) -{ - struct lm3556_chip_data *chip = i2c_get_clientdata(client); - - device_remove_file(chip->cdev_indicator.dev, &dev_attr_pattern); - led_classdev_unregister(&chip->cdev_indicator); - led_classdev_unregister(&chip->cdev_torch); - led_classdev_unregister(&chip->cdev_flash); - regmap_write(chip->regmap, REG_ENABLE, 0); - return 0; -} - -static const struct i2c_device_id lm3556_id[] = { - {LM3556_NAME, 0}, - {} -}; - -MODULE_DEVICE_TABLE(i2c, lm3556_id); - -static struct i2c_driver lm3556_i2c_driver = { - .driver = { - .name = LM3556_NAME, - .owner = THIS_MODULE, - .pm = NULL, - }, - .probe = lm3556_probe, - .remove = __devexit_p(lm3556_remove), - .id_table = lm3556_id, -}; - -module_i2c_driver(lm3556_i2c_driver); - -MODULE_DESCRIPTION("Texas Instruments Flash Lighting driver for LM3556"); -MODULE_AUTHOR("Daniel Jeong "); -MODULE_AUTHOR("G.Shark Jeong "); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/leds/leds-lm355x.c b/drivers/leds/leds-lm355x.c new file mode 100644 index 000000000000..065ec015d67a --- /dev/null +++ b/drivers/leds/leds-lm355x.c @@ -0,0 +1,572 @@ +/* +* Simple driver for Texas Instruments LM355x LED Flash 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +enum lm355x_type { + CHIP_LM3554 = 0, + CHIP_LM3556, +}; + +enum lm355x_regs { + REG_FLAG = 0, + REG_TORCH_CFG, + REG_TORCH_CTRL, + REG_STROBE_CFG, + REG_FLASH_CTRL, + REG_INDI_CFG, + REG_INDI_CTRL, + REG_OPMODE, + REG_MAX, +}; + +/* operation mode */ +enum lm355x_mode { + MODE_SHDN = 0, + MODE_INDIC, + MODE_TORCH, + MODE_FLASH +}; + +/* register map info. */ +struct lm355x_reg_data { + u8 regno; + u8 mask; + u8 shift; +}; + +struct lm355x_chip_data { + struct device *dev; + enum lm355x_type type; + + struct led_classdev cdev_flash; + struct led_classdev cdev_torch; + struct led_classdev cdev_indicator; + + struct work_struct work_flash; + struct work_struct work_torch; + struct work_struct work_indicator; + + u8 br_flash; + u8 br_torch; + u8 br_indicator; + + struct lm355x_platform_data *pdata; + struct regmap *regmap; + struct mutex lock; + + unsigned int last_flag; + struct lm355x_reg_data *regs; +}; + +/* specific indicator function for lm3556 */ +enum lm3556_indic_pulse_time { + PULSE_TIME_0_MS = 0, + PULSE_TIME_32_MS, + PULSE_TIME_64_MS, + PULSE_TIME_92_MS, + PULSE_TIME_128_MS, + PULSE_TIME_160_MS, + PULSE_TIME_196_MS, + PULSE_TIME_224_MS, + PULSE_TIME_256_MS, + PULSE_TIME_288_MS, + PULSE_TIME_320_MS, + PULSE_TIME_352_MS, + PULSE_TIME_384_MS, + PULSE_TIME_416_MS, + PULSE_TIME_448_MS, + PULSE_TIME_480_MS, +}; + +enum lm3556_indic_n_blank { + INDIC_N_BLANK_0 = 0, + INDIC_N_BLANK_1, + INDIC_N_BLANK_2, + INDIC_N_BLANK_3, + INDIC_N_BLANK_4, + INDIC_N_BLANK_5, + INDIC_N_BLANK_6, + INDIC_N_BLANK_7, + INDIC_N_BLANK_8, + INDIC_N_BLANK_9, + INDIC_N_BLANK_10, + INDIC_N_BLANK_11, + INDIC_N_BLANK_12, + INDIC_N_BLANK_13, + INDIC_N_BLANK_14, + INDIC_N_BLANK_15, +}; + +enum lm3556_indic_period { + INDIC_PERIOD_0 = 0, + INDIC_PERIOD_1, + INDIC_PERIOD_2, + INDIC_PERIOD_3, + INDIC_PERIOD_4, + INDIC_PERIOD_5, + INDIC_PERIOD_6, + INDIC_PERIOD_7, +}; + +#define INDIC_PATTERN_SIZE 4 + +struct indicator { + u8 blinking; + u8 period_cnt; +}; + +/* indicator pattern data only for lm3556 */ +static struct indicator indicator_pattern[INDIC_PATTERN_SIZE] = { + [0] = {(INDIC_N_BLANK_1 << 4) | PULSE_TIME_32_MS, INDIC_PERIOD_1}, + [1] = {(INDIC_N_BLANK_15 << 4) | PULSE_TIME_32_MS, INDIC_PERIOD_2}, + [2] = {(INDIC_N_BLANK_10 << 4) | PULSE_TIME_32_MS, INDIC_PERIOD_4}, + [3] = {(INDIC_N_BLANK_5 << 4) | PULSE_TIME_32_MS, INDIC_PERIOD_7}, +}; + +static struct lm355x_reg_data lm3554_regs[REG_MAX] = { + [REG_FLAG] = {0xD0, 0xBF, 0}, + [REG_TORCH_CFG] = {0xE0, 0x80, 7}, + [REG_TORCH_CTRL] = {0xA0, 0x38, 3}, + [REG_STROBE_CFG] = {0xE0, 0x04, 2}, + [REG_FLASH_CTRL] = {0xB0, 0x78, 3}, + [REG_INDI_CFG] = {0xE0, 0x08, 3}, + [REG_INDI_CTRL] = {0xA0, 0xC0, 6}, + [REG_OPMODE] = {0xA0, 0x03, 0}, +}; + +static struct lm355x_reg_data lm3556_regs[REG_MAX] = { + [REG_FLAG] = {0x0B, 0xFF, 0}, + [REG_TORCH_CFG] = {0x0A, 0x10, 4}, + [REG_TORCH_CTRL] = {0x09, 0x70, 4}, + [REG_STROBE_CFG] = {0x0A, 0x20, 5}, + [REG_FLASH_CTRL] = {0x09, 0x0F, 0}, + [REG_INDI_CFG] = {0xFF, 0xFF, 0}, + [REG_INDI_CTRL] = {0x09, 0x70, 4}, + [REG_OPMODE] = {0x0A, 0x03, 0}, +}; + +static char lm355x_name[][I2C_NAME_SIZE] = { + [CHIP_LM3554] = LM3554_NAME, + [CHIP_LM3556] = LM3556_NAME, +}; + +/* chip initialize */ +static int __devinit lm355x_chip_init(struct lm355x_chip_data *chip) +{ + int ret; + unsigned int reg_val; + struct lm355x_platform_data *pdata = chip->pdata; + + /* input and output pins configuration */ + switch (chip->type) { + case CHIP_LM3554: + reg_val = pdata->pin_tx2 | pdata->ntc_pin; + ret = regmap_update_bits(chip->regmap, 0xE0, 0x28, reg_val); + if (ret < 0) + goto out; + reg_val = pdata->pass_mode; + ret = regmap_update_bits(chip->regmap, 0xA0, 0x04, reg_val); + if (ret < 0) + goto out; + break; + + case CHIP_LM3556: + reg_val = pdata->pin_tx2 | pdata->ntc_pin | pdata->pass_mode; + ret = regmap_update_bits(chip->regmap, 0x0A, 0xC4, reg_val); + if (ret < 0) + goto out; + break; + default: + return -ENODATA; + } + + return ret; +out: + dev_err(chip->dev, "%s:i2c access fail to register\n", __func__); + return ret; +} + +/* chip control */ +static void lm355x_control(struct lm355x_chip_data *chip, + u8 brightness, enum lm355x_mode opmode) +{ + int ret; + unsigned int reg_val; + struct lm355x_platform_data *pdata = chip->pdata; + struct lm355x_reg_data *preg = chip->regs; + + ret = regmap_read(chip->regmap, preg[REG_FLAG].regno, &chip->last_flag); + if (ret < 0) + goto out; + if (chip->last_flag & preg[REG_FLAG].mask) + dev_info(chip->dev, "%s Last FLAG is 0x%x\n", + lm355x_name[chip->type], + chip->last_flag & preg[REG_FLAG].mask); + /* brightness 0 means shutdown */ + if (!brightness) + opmode = MODE_SHDN; + + switch (opmode) { + case MODE_TORCH: + ret = + regmap_update_bits(chip->regmap, preg[REG_TORCH_CTRL].regno, + preg[REG_TORCH_CTRL].mask, + (brightness - 1) + << preg[REG_TORCH_CTRL].shift); + if (ret < 0) + goto out; + + if (pdata->pin_tx1 != LM355x_PIN_TORCH_DISABLE) { + ret = + regmap_update_bits(chip->regmap, + preg[REG_TORCH_CFG].regno, + preg[REG_TORCH_CFG].mask, + 0x01 << + preg[REG_TORCH_CFG].shift); + if (ret < 0) + goto out; + opmode = MODE_SHDN; + dev_info(chip->dev, + "torch brt is set - ext. torch pin mode\n"); + } + break; + + case MODE_FLASH: + + ret = + regmap_update_bits(chip->regmap, preg[REG_FLASH_CTRL].regno, + preg[REG_FLASH_CTRL].mask, + (brightness - 1) + << preg[REG_FLASH_CTRL].shift); + if (ret < 0) + goto out; + + if (pdata->pin_strobe != LM355x_PIN_STROBE_DISABLE) { + if (chip->type == CHIP_LM3554) + reg_val = 0x00; + else + reg_val = 0x01; + ret = + regmap_update_bits(chip->regmap, + preg[REG_STROBE_CFG].regno, + preg[REG_STROBE_CFG].mask, + reg_val << + preg[REG_STROBE_CFG].shift); + if (ret < 0) + goto out; + opmode = MODE_SHDN; + dev_info(chip->dev, + "flash brt is set - ext. strobe pin mode\n"); + } + break; + + case MODE_INDIC: + ret = + regmap_update_bits(chip->regmap, preg[REG_INDI_CTRL].regno, + preg[REG_INDI_CTRL].mask, + (brightness - 1) + << preg[REG_INDI_CTRL].shift); + if (ret < 0) + goto out; + + if (pdata->pin_tx2 != LM355x_PIN_TX_DISABLE) { + ret = + regmap_update_bits(chip->regmap, + preg[REG_INDI_CFG].regno, + preg[REG_INDI_CFG].mask, + 0x01 << + preg[REG_INDI_CFG].shift); + if (ret < 0) + goto out; + opmode = MODE_SHDN; + } + break; + case MODE_SHDN: + break; + default: + return; + } + /* operation mode control */ + ret = regmap_update_bits(chip->regmap, preg[REG_OPMODE].regno, + preg[REG_OPMODE].mask, + opmode << preg[REG_OPMODE].shift); + if (ret < 0) + goto out; + return; +out: + dev_err(chip->dev, "%s:i2c access fail to register\n", __func__); + return; +} + +/* torch */ +static void lm355x_deferred_torch_brightness_set(struct work_struct *work) +{ + struct lm355x_chip_data *chip = + container_of(work, struct lm355x_chip_data, work_torch); + + mutex_lock(&chip->lock); + lm355x_control(chip, chip->br_torch, MODE_TORCH); + mutex_unlock(&chip->lock); +} + +static void lm355x_torch_brightness_set(struct led_classdev *cdev, + enum led_brightness brightness) +{ + struct lm355x_chip_data *chip = + container_of(cdev, struct lm355x_chip_data, cdev_torch); + + chip->br_torch = brightness; + schedule_work(&chip->work_torch); +} + +/* flash */ +static void lm355x_deferred_strobe_brightness_set(struct work_struct *work) +{ + struct lm355x_chip_data *chip = + container_of(work, struct lm355x_chip_data, work_flash); + + mutex_lock(&chip->lock); + lm355x_control(chip, chip->br_flash, MODE_FLASH); + mutex_unlock(&chip->lock); +} + +static void lm355x_strobe_brightness_set(struct led_classdev *cdev, + enum led_brightness brightness) +{ + struct lm355x_chip_data *chip = + container_of(cdev, struct lm355x_chip_data, cdev_flash); + + chip->br_flash = brightness; + schedule_work(&chip->work_flash); +} + +/* indicator */ +static void lm355x_deferred_indicator_brightness_set(struct work_struct *work) +{ + struct lm355x_chip_data *chip = + container_of(work, struct lm355x_chip_data, work_indicator); + + mutex_lock(&chip->lock); + lm355x_control(chip, chip->br_indicator, MODE_INDIC); + mutex_unlock(&chip->lock); +} + +static void lm355x_indicator_brightness_set(struct led_classdev *cdev, + enum led_brightness brightness) +{ + struct lm355x_chip_data *chip = + container_of(cdev, struct lm355x_chip_data, cdev_indicator); + + chip->br_indicator = brightness; + schedule_work(&chip->work_indicator); +} + +/* indicator pattern only for lm3556*/ +static ssize_t lm3556_indicator_pattern_store(struct device *dev, + struct device_attribute *devAttr, + const char *buf, size_t size) +{ + ssize_t ret; + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct lm355x_chip_data *chip = + container_of(led_cdev, struct lm355x_chip_data, cdev_indicator); + unsigned int state; + + ret = kstrtouint(buf, 10, &state); + if (ret) + goto out; + if (state > INDIC_PATTERN_SIZE - 1) + state = INDIC_PATTERN_SIZE - 1; + + ret = regmap_write(chip->regmap, 0x04, + indicator_pattern[state].blinking); + if (ret < 0) + goto out; + + ret = regmap_write(chip->regmap, 0x05, + indicator_pattern[state].period_cnt); + if (ret < 0) + goto out; + + return size; +out: + dev_err(chip->dev, "%s:i2c access fail to register\n", __func__); + return size; +} + +static DEVICE_ATTR(pattern, 0666, NULL, lm3556_indicator_pattern_store); + +static const struct regmap_config lm355x_regmap = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0xFF, +}; + +/* module initialize */ +static int __devinit lm355x_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct lm355x_platform_data *pdata = client->dev.platform_data; + struct lm355x_chip_data *chip; + + int err; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_err(&client->dev, "i2c functionality check fail.\n"); + return -EOPNOTSUPP; + } + + if (pdata == NULL) { + dev_err(&client->dev, "needs Platform Data.\n"); + return -ENODATA; + } + + chip = devm_kzalloc(&client->dev, + sizeof(struct lm355x_chip_data), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->dev = &client->dev; + chip->type = id->driver_data; + switch (id->driver_data) { + case CHIP_LM3554: + chip->regs = lm3554_regs; + break; + case CHIP_LM3556: + chip->regs = lm3556_regs; + break; + default: + return -ENOSYS; + } + chip->pdata = pdata; + + chip->regmap = devm_regmap_init_i2c(client, &lm355x_regmap); + if (IS_ERR(chip->regmap)) { + err = PTR_ERR(chip->regmap); + dev_err(&client->dev, + "Failed to allocate register map: %d\n", err); + return err; + } + + mutex_init(&chip->lock); + i2c_set_clientdata(client, chip); + + err = lm355x_chip_init(chip); + if (err < 0) + goto err_out; + + /* flash */ + INIT_WORK(&chip->work_flash, lm355x_deferred_strobe_brightness_set); + chip->cdev_flash.name = "flash"; + chip->cdev_flash.max_brightness = 16; + chip->cdev_flash.brightness_set = lm355x_strobe_brightness_set; + err = led_classdev_register((struct device *) + &client->dev, &chip->cdev_flash); + if (err < 0) + goto err_out; + /* torch */ + INIT_WORK(&chip->work_torch, lm355x_deferred_torch_brightness_set); + chip->cdev_torch.name = "torch"; + chip->cdev_torch.max_brightness = 8; + chip->cdev_torch.brightness_set = lm355x_torch_brightness_set; + err = led_classdev_register((struct device *) + &client->dev, &chip->cdev_torch); + if (err < 0) + goto err_create_torch_file; + /* indicator */ + INIT_WORK(&chip->work_indicator, + lm355x_deferred_indicator_brightness_set); + chip->cdev_indicator.name = "indicator"; + if (id->driver_data == CHIP_LM3554) + chip->cdev_indicator.max_brightness = 4; + else + chip->cdev_indicator.max_brightness = 8; + chip->cdev_indicator.brightness_set = lm355x_indicator_brightness_set; + err = led_classdev_register((struct device *) + &client->dev, &chip->cdev_indicator); + if (err < 0) + goto err_create_indicator_file; + /* indicator pattern control only for LM3554 */ + if (id->driver_data == CHIP_LM3556) { + err = + device_create_file(chip->cdev_indicator.dev, + &dev_attr_pattern); + if (err < 0) + goto err_create_pattern_file; + } + + dev_info(&client->dev, "%s is initialized\n", + lm355x_name[id->driver_data]); + return 0; + +err_create_pattern_file: + led_classdev_unregister(&chip->cdev_indicator); +err_create_indicator_file: + led_classdev_unregister(&chip->cdev_torch); +err_create_torch_file: + led_classdev_unregister(&chip->cdev_flash); +err_out: + return err; +} + +static int __devexit lm355x_remove(struct i2c_client *client) +{ + struct lm355x_chip_data *chip = i2c_get_clientdata(client); + struct lm355x_reg_data *preg = chip->regs; + + regmap_write(chip->regmap, preg[REG_OPMODE].regno, 0); + if (chip->type == CHIP_LM3556) + device_remove_file(chip->cdev_indicator.dev, &dev_attr_pattern); + led_classdev_unregister(&chip->cdev_indicator); + flush_work(&chip->work_indicator); + led_classdev_unregister(&chip->cdev_torch); + flush_work(&chip->work_torch); + led_classdev_unregister(&chip->cdev_flash); + flush_work(&chip->work_flash); + dev_info(&client->dev, "%s is removed\n", lm355x_name[chip->type]); + + return 0; +} + +static const struct i2c_device_id lm355x_id[] = { + {LM3554_NAME, CHIP_LM3554}, + {LM3556_NAME, CHIP_LM3556}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, lm355x_id); + +static struct i2c_driver lm355x_i2c_driver = { + .driver = { + .name = LM355x_NAME, + .owner = THIS_MODULE, + .pm = NULL, + }, + .probe = lm355x_probe, + .remove = __devexit_p(lm355x_remove), + .id_table = lm355x_id, +}; + +module_i2c_driver(lm355x_i2c_driver); + +MODULE_DESCRIPTION("Texas Instruments Flash Lighting driver for LM355x"); +MODULE_AUTHOR("Daniel Jeong "); +MODULE_AUTHOR("G.Shark Jeong "); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/platform_data/leds-lm3556.h b/include/linux/platform_data/leds-lm3556.h deleted file mode 100644 index 4b4e7d6b0527..000000000000 --- a/include/linux/platform_data/leds-lm3556.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Simple driver for Texas Instruments LM3556 LED Flash driver chip (Rev0x03) - * 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. - * - */ - -#ifndef __LINUX_LM3556_H -#define __LINUX_LM3556_H - -#define LM3556_NAME "leds-lm3556" - -enum lm3556_pin_polarity { - PIN_LOW_ACTIVE = 0, - PIN_HIGH_ACTIVE, -}; - -enum lm3556_pin_enable { - PIN_DISABLED = 0, - PIN_ENABLED, -}; - -enum lm3556_strobe_usuage { - STROBE_EDGE_DETECT = 0, - STROBE_LEVEL_DETECT, -}; - -enum lm3556_indic_mode { - INDIC_MODE_INTERNAL = 0, - INDIC_MODE_EXTERNAL, -}; - -struct lm3556_platform_data { - enum lm3556_pin_enable torch_pin_en; - enum lm3556_pin_polarity torch_pin_polarity; - - enum lm3556_strobe_usuage strobe_usuage; - enum lm3556_pin_enable strobe_pin_en; - enum lm3556_pin_polarity strobe_pin_polarity; - - enum lm3556_pin_enable tx_pin_en; - enum lm3556_pin_polarity tx_pin_polarity; - - enum lm3556_indic_mode indicator_mode; -}; - -#endif /* __LINUX_LM3556_H */ diff --git a/include/linux/platform_data/leds-lm355x.h b/include/linux/platform_data/leds-lm355x.h new file mode 100644 index 000000000000..b88724bb0b46 --- /dev/null +++ b/include/linux/platform_data/leds-lm355x.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2012 Texas Instruments + * + * License Terms: GNU General Public License v2 + * + * Simple driver for Texas Instruments LM355x LED driver chip + * + * Author: G.Shark Jeong + * Daniel Jeong + */ + +#define LM355x_NAME "leds-lm355x" +#define LM3554_NAME "leds-lm3554" +#define LM3556_NAME "leds-lm3556" + +/* lm3554 : strobe def. on */ +enum lm355x_strobe { + LM355x_PIN_STROBE_DISABLE = 0x00, + LM355x_PIN_STROBE_ENABLE = 0x01, +}; + +enum lm355x_torch { + LM355x_PIN_TORCH_DISABLE = 0, + LM3554_PIN_TORCH_ENABLE = 0x80, + LM3556_PIN_TORCH_ENABLE = 0x10, +}; + +enum lm355x_tx2 { + LM355x_PIN_TX_DISABLE = 0, + LM3554_PIN_TX_ENABLE = 0x20, + LM3556_PIN_TX_ENABLE = 0x40, +}; + +enum lm355x_ntc { + LM355x_PIN_NTC_DISABLE = 0, + LM3554_PIN_NTC_ENABLE = 0x08, + LM3556_PIN_NTC_ENABLE = 0x80, +}; + +enum lm355x_pmode { + LM355x_PMODE_DISABLE = 0, + LM355x_PMODE_ENABLE = 0x04, +}; + +/* + * struct lm3554_platform_data + * @pin_strobe: strobe input + * @pin_torch : input pin + * lm3554-tx1/torch/gpio1 + * lm3556-torch + * @pin_tx2 : input pin + * lm3554-envm/tx2/gpio2 + * lm3556-tx pin + * @ntc_pin : output pin + * lm3554-ledi/ntc + * lm3556-temp pin + * @pass_mode : pass mode + */ +struct lm355x_platform_data { + enum lm355x_strobe pin_strobe; + enum lm355x_torch pin_tx1; + enum lm355x_tx2 pin_tx2; + enum lm355x_ntc ntc_pin; + + enum lm355x_pmode pass_mode; +}; -- cgit v1.2.3 From 8b7cfbec052d3a0b8e8c7de24a6b5f00d340e193 Mon Sep 17 00:00:00 2001 From: "G.Shark Jeong" Date: Wed, 12 Sep 2012 20:05:50 +0800 Subject: leds: Add new LED driver for lm3642 chips This driver is a general version for LM642 led chip of TI. LM3642 : The LM3642 is a 4MHz fixed-frequency synchronous boost converter plus 1.5A constant current driver for a high-current white LED. The LM3642 is controlled via an I2C-compatible interface. Signed-off-by: G.Shark Jeong Signed-off-by: Bryan Wu --- drivers/leds/Kconfig | 11 + drivers/leds/Makefile | 1 + drivers/leds/leds-lm3642.c | 471 ++++++++++++++++++++++++++++++ include/linux/platform_data/leds-lm3642.h | 38 +++ 4 files changed, 521 insertions(+) create mode 100644 drivers/leds/leds-lm3642.c create mode 100644 include/linux/platform_data/leds-lm3642.h (limited to 'drivers/leds/Kconfig') diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 0dcb794df1fa..7cfa4d1555d9 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -63,6 +63,17 @@ config LEDS_LM3533 hardware-accelerated blinking with maximum on and off periods of 9.8 and 77 seconds respectively. +config LEDS_LM3642 + tristate "LED support for LM3642 Chip" + depends on LEDS_CLASS && I2C + select REGMAP_I2C + help + This option enables support for LEDs connected to LM3642. + The LM3642 is a 4MHz fixed-frequency synchronous boost + converter plus 1.5A constant current driver for a high-current + white LED. + + config LEDS_LOCOMO tristate "LED Support for Locomo device" depends on LEDS_CLASS diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index b57a021d1fea..2ad9871becde 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_LEDS_BD2802) += leds-bd2802.o obj-$(CONFIG_LEDS_LOCOMO) += leds-locomo.o obj-$(CONFIG_LEDS_LM3530) += leds-lm3530.o obj-$(CONFIG_LEDS_LM3533) += leds-lm3533.o +obj-$(CONFIG_LEDS_LM3642) += leds-lm3642.o obj-$(CONFIG_LEDS_MIKROTIK_RB532) += leds-rb532.o obj-$(CONFIG_LEDS_S3C24XX) += leds-s3c24xx.o obj-$(CONFIG_LEDS_NET48XX) += leds-net48xx.o diff --git a/drivers/leds/leds-lm3642.c b/drivers/leds/leds-lm3642.c new file mode 100644 index 000000000000..924853b7922c --- /dev/null +++ b/drivers/leds/leds-lm3642.c @@ -0,0 +1,471 @@ +/* +* Simple driver for Texas Instruments LM3642 LED Flash 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define REG_FILT_TIME (0x0) +#define REG_IVFM_MODE (0x1) +#define REG_TORCH_TIME (0x6) +#define REG_FLASH (0x8) +#define REG_I_CTRL (0x9) +#define REG_ENABLE (0xA) +#define REG_FLAG (0xB) +#define REG_MAX (0xB) + +#define UVLO_EN_SHIFT (7) +#define IVM_D_TH_SHIFT (2) +#define TORCH_RAMP_UP_TIME_SHIFT (3) +#define TORCH_RAMP_DN_TIME_SHIFT (0) +#define INDUCTOR_I_LIMIT_SHIFT (6) +#define FLASH_RAMP_TIME_SHIFT (3) +#define FLASH_TOUT_TIME_SHIFT (0) +#define TORCH_I_SHIFT (4) +#define FLASH_I_SHIFT (0) +#define IVFM_SHIFT (7) +#define TX_PIN_EN_SHIFT (6) +#define STROBE_PIN_EN_SHIFT (5) +#define TORCH_PIN_EN_SHIFT (4) +#define MODE_BITS_SHIFT (0) + +#define UVLO_EN_MASK (0x1) +#define IVM_D_TH_MASK (0x7) +#define TORCH_RAMP_UP_TIME_MASK (0x7) +#define TORCH_RAMP_DN_TIME_MASK (0x7) +#define INDUCTOR_I_LIMIT_MASK (0x1) +#define FLASH_RAMP_TIME_MASK (0x7) +#define FLASH_TOUT_TIME_MASK (0x7) +#define TORCH_I_MASK (0x7) +#define FLASH_I_MASK (0xF) +#define IVFM_MASK (0x1) +#define TX_PIN_EN_MASK (0x1) +#define STROBE_PIN_EN_MASK (0x1) +#define TORCH_PIN_EN_MASK (0x1) +#define MODE_BITS_MASK (0x73) +#define EX_PIN_CONTROL_MASK (0x71) +#define EX_PIN_ENABLE_MASK (0x70) + +enum lm3642_mode { + MODES_STASNDBY = 0, + MODES_INDIC, + MODES_TORCH, + MODES_FLASH +}; + +struct lm3642_chip_data { + struct device *dev; + + struct led_classdev cdev_flash; + struct led_classdev cdev_torch; + struct led_classdev cdev_indicator; + + struct work_struct work_flash; + struct work_struct work_torch; + struct work_struct work_indicator; + + u8 br_flash; + u8 br_torch; + u8 br_indicator; + + enum lm3642_torch_pin_enable torch_pin; + enum lm3642_strobe_pin_enable strobe_pin; + enum lm3642_tx_pin_enable tx_pin; + + struct lm3642_platform_data *pdata; + struct regmap *regmap; + struct mutex lock; + + unsigned int last_flag; +}; + +/* chip initialize */ +static int __devinit lm3642_chip_init(struct lm3642_chip_data *chip) +{ + unsigned int reg_val; + int ret; + struct lm3642_platform_data *pdata = chip->pdata; + + /* set enable register */ + ret = regmap_read(chip->regmap, REG_ENABLE, ®_val); + if (ret < 0) + goto out; + + reg_val &= (~EX_PIN_ENABLE_MASK); + reg_val |= pdata->tx_pin; + ret = regmap_write(chip->regmap, REG_ENABLE, reg_val); + if (ret < 0) + goto out; + +out: + dev_err(chip->dev, "Failed to read REG_ENABLE Register\n"); + return ret; +} + +/* chip control */ +static int lm3642_control(struct lm3642_chip_data *chip, + u8 brightness, enum lm3642_mode opmode) +{ + int ret; + + ret = regmap_read(chip->regmap, REG_FLAG, &chip->last_flag); + if (ret < 0) { + dev_err(chip->dev, "Failed to read REG_FLAG Register\n"); + goto out; + } + + if (chip->last_flag) + dev_info(chip->dev, "Last FLAG is 0x%x\n", chip->last_flag); + + /* brightness 0 means off-state */ + if (!brightness) + opmode = MODES_STASNDBY; + + switch (opmode) { + case MODES_TORCH: + ret = regmap_update_bits(chip->regmap, REG_I_CTRL, + TORCH_I_MASK << TORCH_I_SHIFT, + (brightness - 1) << TORCH_I_SHIFT); + + if (chip->torch_pin) + opmode |= (TORCH_PIN_EN_MASK << TORCH_PIN_EN_SHIFT); + break; + + case MODES_FLASH: + ret = regmap_update_bits(chip->regmap, REG_I_CTRL, + FLASH_I_MASK << FLASH_I_SHIFT, + (brightness - 1) << FLASH_I_SHIFT); + + if (chip->strobe_pin) + opmode |= (STROBE_PIN_EN_MASK << STROBE_PIN_EN_SHIFT); + break; + + case MODES_INDIC: + ret = regmap_update_bits(chip->regmap, REG_I_CTRL, + TORCH_I_MASK << TORCH_I_SHIFT, + (brightness - 1) << TORCH_I_SHIFT); + break; + + case MODES_STASNDBY: + + break; + + default: + return ret; + } + if (ret < 0) { + dev_err(chip->dev, "Failed to write REG_I_CTRL Register\n"); + goto out; + } + + if (chip->tx_pin) + opmode |= (TX_PIN_EN_MASK << TX_PIN_EN_SHIFT); + + ret = regmap_update_bits(chip->regmap, REG_ENABLE, + MODE_BITS_MASK << MODE_BITS_SHIFT, + opmode << MODE_BITS_SHIFT); +out: + return ret; +} + +/* torch */ + +/* torch pin config for lm3642*/ +static ssize_t lm3642_torch_pin_store(struct device *dev, + struct device_attribute *devAttr, + const char *buf, size_t size) +{ + ssize_t ret; + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct lm3642_chip_data *chip = + container_of(led_cdev, struct lm3642_chip_data, cdev_indicator); + unsigned int state; + + ret = kstrtouint(buf, 10, &state); + if (ret) + goto out_strtoint; + if (state != 0) + state = 0x01 << TORCH_PIN_EN_SHIFT; + + chip->torch_pin = state; + ret = regmap_update_bits(chip->regmap, REG_ENABLE, + TORCH_PIN_EN_MASK << TORCH_PIN_EN_SHIFT, + state); + if (ret < 0) + goto out; + + return size; +out: + dev_err(chip->dev, "%s:i2c access fail to register\n", __func__); + return size; +out_strtoint: + dev_err(chip->dev, "%s: fail to change str to int\n", __func__); + return size; +} + +static DEVICE_ATTR(torch_pin, 0666, NULL, lm3642_torch_pin_store); + +static void lm3642_deferred_torch_brightness_set(struct work_struct *work) +{ + struct lm3642_chip_data *chip = + container_of(work, struct lm3642_chip_data, work_torch); + + mutex_lock(&chip->lock); + lm3642_control(chip, chip->br_torch, MODES_TORCH); + mutex_unlock(&chip->lock); +} + +static void lm3642_torch_brightness_set(struct led_classdev *cdev, + enum led_brightness brightness) +{ + struct lm3642_chip_data *chip = + container_of(cdev, struct lm3642_chip_data, cdev_torch); + + chip->br_torch = brightness; + schedule_work(&chip->work_torch); +} + +/* flash */ + +/* strobe pin config for lm3642*/ +static ssize_t lm3642_strobe_pin_store(struct device *dev, + struct device_attribute *devAttr, + const char *buf, size_t size) +{ + ssize_t ret; + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct lm3642_chip_data *chip = + container_of(led_cdev, struct lm3642_chip_data, cdev_indicator); + unsigned int state; + + ret = kstrtouint(buf, 10, &state); + if (ret) + goto out_strtoint; + if (state != 0) + state = 0x01 << STROBE_PIN_EN_SHIFT; + + chip->strobe_pin = state; + ret = regmap_update_bits(chip->regmap, REG_ENABLE, + STROBE_PIN_EN_MASK << STROBE_PIN_EN_SHIFT, + state); + if (ret < 0) + goto out; + + return size; +out: + dev_err(chip->dev, "%s:i2c access fail to register\n", __func__); + return size; +out_strtoint: + dev_err(chip->dev, "%s: fail to change str to int\n", __func__); + return size; +} + +static DEVICE_ATTR(strobe_pin, 0666, NULL, lm3642_strobe_pin_store); + +static void lm3642_deferred_strobe_brightness_set(struct work_struct *work) +{ + struct lm3642_chip_data *chip = + container_of(work, struct lm3642_chip_data, work_flash); + + mutex_lock(&chip->lock); + lm3642_control(chip, chip->br_flash, MODES_FLASH); + mutex_unlock(&chip->lock); +} + +static void lm3642_strobe_brightness_set(struct led_classdev *cdev, + enum led_brightness brightness) +{ + struct lm3642_chip_data *chip = + container_of(cdev, struct lm3642_chip_data, cdev_flash); + + chip->br_flash = brightness; + schedule_work(&chip->work_flash); +} + +/* indicator */ +static void lm3642_deferred_indicator_brightness_set(struct work_struct *work) +{ + struct lm3642_chip_data *chip = + container_of(work, struct lm3642_chip_data, work_indicator); + + mutex_lock(&chip->lock); + lm3642_control(chip, chip->br_indicator, MODES_INDIC); + mutex_unlock(&chip->lock); +} + +static void lm3642_indicator_brightness_set(struct led_classdev *cdev, + enum led_brightness brightness) +{ + struct lm3642_chip_data *chip = + container_of(cdev, struct lm3642_chip_data, cdev_indicator); + + chip->br_indicator = brightness; + schedule_work(&chip->work_indicator); +} + +static const struct regmap_config lm3642_regmap = { + .reg_bits = 8, + .val_bits = 8, + .max_register = REG_MAX, +}; + +static int __devinit lm3642_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct lm3642_platform_data *pdata = client->dev.platform_data; + struct lm3642_chip_data *chip; + + int err; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_err(&client->dev, "i2c functionality check fail.\n"); + return -EOPNOTSUPP; + } + + if (pdata == NULL) { + dev_err(&client->dev, "needs Platform Data.\n"); + return -ENODATA; + } + + chip = devm_kzalloc(&client->dev, + sizeof(struct lm3642_chip_data), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->dev = &client->dev; + chip->pdata = pdata; + + chip->tx_pin = pdata->tx_pin; + chip->torch_pin = pdata->torch_pin; + chip->strobe_pin = pdata->strobe_pin; + + chip->regmap = devm_regmap_init_i2c(client, &lm3642_regmap); + if (IS_ERR(chip->regmap)) { + err = PTR_ERR(chip->regmap); + dev_err(&client->dev, "Failed to allocate register map: %d\n", + err); + return err; + } + + mutex_init(&chip->lock); + i2c_set_clientdata(client, chip); + + err = lm3642_chip_init(chip); + if (err < 0) + goto err_out; + + /* flash */ + INIT_WORK(&chip->work_flash, lm3642_deferred_strobe_brightness_set); + chip->cdev_flash.name = "flash"; + chip->cdev_flash.max_brightness = 16; + chip->cdev_flash.brightness_set = lm3642_strobe_brightness_set; + err = led_classdev_register((struct device *) + &client->dev, &chip->cdev_flash); + if (err < 0) { + dev_err(chip->dev, "failed to register flash\n"); + goto err_out; + } + err = device_create_file(chip->cdev_flash.dev, &dev_attr_strobe_pin); + if (err < 0) { + dev_err(chip->dev, "failed to create strobe-pin file\n"); + goto err_create_flash_pin_file; + } + + /* torch */ + INIT_WORK(&chip->work_torch, lm3642_deferred_torch_brightness_set); + chip->cdev_torch.name = "torch"; + chip->cdev_torch.max_brightness = 8; + chip->cdev_torch.brightness_set = lm3642_torch_brightness_set; + err = led_classdev_register((struct device *) + &client->dev, &chip->cdev_torch); + if (err < 0) { + dev_err(chip->dev, "failed to register torch\n"); + goto err_create_torch_file; + } + err = device_create_file(chip->cdev_torch.dev, &dev_attr_torch_pin); + if (err < 0) { + dev_err(chip->dev, "failed to create torch-pin file\n"); + goto err_create_torch_pin_file; + } + + /* indicator */ + INIT_WORK(&chip->work_indicator, + lm3642_deferred_indicator_brightness_set); + chip->cdev_indicator.name = "indicator"; + chip->cdev_indicator.max_brightness = 8; + chip->cdev_indicator.brightness_set = lm3642_indicator_brightness_set; + err = led_classdev_register((struct device *) + &client->dev, &chip->cdev_indicator); + if (err < 0) { + dev_err(chip->dev, "failed to register indicator\n"); + goto err_create_indicator_file; + } + + dev_info(&client->dev, "LM3642 is initialized\n"); + return 0; + +err_create_indicator_file: + device_remove_file(chip->cdev_torch.dev, &dev_attr_torch_pin); +err_create_torch_pin_file: + led_classdev_unregister(&chip->cdev_torch); +err_create_torch_file: + device_remove_file(chip->cdev_flash.dev, &dev_attr_strobe_pin); +err_create_flash_pin_file: + led_classdev_unregister(&chip->cdev_flash); +err_out: + return err; +} + +static int __devexit lm3642_remove(struct i2c_client *client) +{ + struct lm3642_chip_data *chip = i2c_get_clientdata(client); + + led_classdev_unregister(&chip->cdev_indicator); + flush_work(&chip->work_indicator); + device_remove_file(chip->cdev_torch.dev, &dev_attr_torch_pin); + led_classdev_unregister(&chip->cdev_torch); + flush_work(&chip->work_torch); + device_remove_file(chip->cdev_flash.dev, &dev_attr_strobe_pin); + led_classdev_unregister(&chip->cdev_flash); + flush_work(&chip->work_flash); + regmap_write(chip->regmap, REG_ENABLE, 0); + return 0; +} + +static const struct i2c_device_id lm3642_id[] = { + {LM3642_NAME, 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, lm3642_id); + +static struct i2c_driver lm3642_i2c_driver = { + .driver = { + .name = LM3642_NAME, + .owner = THIS_MODULE, + .pm = NULL, + }, + .probe = lm3642_probe, + .remove = __devexit_p(lm3642_remove), + .id_table = lm3642_id, +}; + +module_i2c_driver(lm3642_i2c_driver); + +MODULE_DESCRIPTION("Texas Instruments Flash Lighting driver for LM3642"); +MODULE_AUTHOR("Daniel Jeong "); +MODULE_AUTHOR("G.Shark Jeong "); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/platform_data/leds-lm3642.h b/include/linux/platform_data/leds-lm3642.h new file mode 100644 index 000000000000..72d6ee6ade57 --- /dev/null +++ b/include/linux/platform_data/leds-lm3642.h @@ -0,0 +1,38 @@ +/* +* Copyright (C) 2012 Texas Instruments +* +* License Terms: GNU General Public License v2 +* +* Simple driver for Texas Instruments LM3642 LED driver chip +* +* Author: G.Shark Jeong +* Daniel Jeong +*/ + +#ifndef __LINUX_LM3642_H +#define __LINUX_LM3642_H + +#define LM3642_NAME "leds-lm3642" + +enum lm3642_torch_pin_enable { + LM3642_TORCH_PIN_DISABLE = 0x00, + LM3642_TORCH_PIN_ENABLE = 0x10, +}; + +enum lm3642_strobe_pin_enable { + LM3642_STROBE_PIN_DISABLE = 0x00, + LM3642_STROBE_PIN_ENABLE = 0x20, +}; + +enum lm3642_tx_pin_enable { + LM3642_TX_PIN_DISABLE = 0x00, + LM3642_TX_PIN_ENABLE = 0x40, +}; + +struct lm3642_platform_data { + enum lm3642_torch_pin_enable torch_pin; + enum lm3642_strobe_pin_enable strobe_pin; + enum lm3642_tx_pin_enable tx_pin; +}; + +#endif /* __LINUX_LM3642_H */ -- cgit v1.2.3