From 5bb629c504394f4d42c53a25d75ccb02a393f92f Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Sun, 27 May 2012 07:19:22 +0800 Subject: leds: add oneshot blink functions Add two new functions, led_blink_set_oneshot and led_trigger_blink_oneshot, to be used by triggers for one-shot blink of led devices. This is implemented extending the existing software-blink code, and uses the same timer and handler function. The behavior of the code is to do a blink-on, blink-off sequence when the function is called, ignoring other calls until the sequence is completed so that the leds keep blinking at constant rate if the functions are called repeatedly. This is meant to be used by drivers which needs to trigger on sporadic event, but doesn't have clear busy/idle trigger points. After the blink sequence the led remains off. This behavior can be inverted setting the "invert" argument, which blink the led off, than on and leave the led on after the sequence. (bryan.wu@canonical.com: rebase to commit 'leds: don't disable blinking when writing the same value to delay_on or delay_off') Signed-off-by: Fabio Baltieri Acked-by: Shuah Khan Signed-off-by: Bryan Wu --- include/linux/leds.h | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'include') diff --git a/include/linux/leds.h b/include/linux/leds.h index 39eee41d8c6f..dd93a22044bb 100644 --- a/include/linux/leds.h +++ b/include/linux/leds.h @@ -38,6 +38,9 @@ struct led_classdev { #define LED_SUSPENDED (1 << 0) /* Upper 16 bits reflect control information */ #define LED_CORE_SUSPENDRESUME (1 << 16) +#define LED_BLINK_ONESHOT (1 << 17) +#define LED_BLINK_ONESHOT_STOP (1 << 18) +#define LED_BLINK_INVERT (1 << 19) /* Set LED brightness level */ /* Must not sleep, use a workqueue if needed */ @@ -102,6 +105,24 @@ extern void led_classdev_resume(struct led_classdev *led_cdev); extern void led_blink_set(struct led_classdev *led_cdev, unsigned long *delay_on, unsigned long *delay_off); +/** + * led_blink_set_oneshot - do a oneshot software blink + * @led_cdev: the LED to start blinking + * @delay_on: the time it should be on (in ms) + * @delay_off: the time it should ble off (in ms) + * @invert: blink off, then on, leaving the led on + * + * This function makes the LED blink one time for delay_on + + * delay_off time, ignoring the request if another one-shot + * blink is already in progress. + * + * If invert is set, led blinks for delay_off first, then for + * delay_on and leave the led on after the on-off cycle. + */ +extern void led_blink_set_oneshot(struct led_classdev *led_cdev, + unsigned long *delay_on, + unsigned long *delay_off, + int invert); /** * led_brightness_set - set LED brightness * @led_cdev: the LED to set @@ -150,6 +171,10 @@ extern void led_trigger_event(struct led_trigger *trigger, extern void led_trigger_blink(struct led_trigger *trigger, unsigned long *delay_on, unsigned long *delay_off); +extern void led_trigger_blink_oneshot(struct led_trigger *trigger, + unsigned long *delay_on, + unsigned long *delay_off, + int invert); #else -- cgit v1.2.3 From 19cd67e2d51225b164560b54b85f943e07deee8a Mon Sep 17 00:00:00 2001 From: Shuah Khan Date: Thu, 14 Jun 2012 04:34:30 +0800 Subject: leds: Rename led_brightness_set() to led_set_brightness() Rename leds external interface led_brightness_set() to led_set_brightness(). This is the second phase of the change to reduce confusion between the leds internal and external interfaces that set brightness. With this change, now the external interface is led_set_brightness(). The first phase renamed the internal interface led_set_brightness() to __led_set_brightness(). There are no changes to the interface implementations. Signed-off-by: Shuah Khan Signed-off-by: Bryan Wu --- drivers/leds/led-class.c | 2 +- drivers/leds/led-core.c | 4 ++-- drivers/leds/led-triggers.c | 2 +- drivers/leds/ledtrig-oneshot.c | 2 +- drivers/leds/ledtrig-timer.c | 2 +- include/linux/leds.h | 4 ++-- net/mac80211/led.c | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c index cb0a6eb72a2c..c599095bc005 100644 --- a/drivers/leds/led-class.c +++ b/drivers/leds/led-class.c @@ -222,7 +222,7 @@ void led_classdev_unregister(struct led_classdev *led_cdev) #endif /* Stop blinking */ - led_brightness_set(led_cdev, LED_OFF); + led_set_brightness(led_cdev, LED_OFF); device_unregister(led_cdev->dev); diff --git a/drivers/leds/led-core.c b/drivers/leds/led-core.c index 176961b2db6b..8a09c5fc2a6d 100644 --- a/drivers/leds/led-core.c +++ b/drivers/leds/led-core.c @@ -103,7 +103,7 @@ void led_blink_set_oneshot(struct led_classdev *led_cdev, } EXPORT_SYMBOL(led_blink_set_oneshot); -void led_brightness_set(struct led_classdev *led_cdev, +void led_set_brightness(struct led_classdev *led_cdev, enum led_brightness brightness) { /* stop and clear soft-blink timer */ @@ -113,4 +113,4 @@ void led_brightness_set(struct led_classdev *led_cdev, __led_set_brightness(led_cdev, brightness); } -EXPORT_SYMBOL(led_brightness_set); +EXPORT_SYMBOL(led_set_brightness); diff --git a/drivers/leds/led-triggers.c b/drivers/leds/led-triggers.c index f8b14dd8c2b6..57721f25d7cd 100644 --- a/drivers/leds/led-triggers.c +++ b/drivers/leds/led-triggers.c @@ -112,7 +112,7 @@ void led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trig) if (led_cdev->trigger->deactivate) led_cdev->trigger->deactivate(led_cdev); led_cdev->trigger = NULL; - led_brightness_set(led_cdev, LED_OFF); + led_set_brightness(led_cdev, LED_OFF); } if (trig) { write_lock_irqsave(&trig->leddev_list_lock, flags); diff --git a/drivers/leds/ledtrig-oneshot.c b/drivers/leds/ledtrig-oneshot.c index 5cbab41a8da2..2c029aa5c4f1 100644 --- a/drivers/leds/ledtrig-oneshot.c +++ b/drivers/leds/ledtrig-oneshot.c @@ -177,7 +177,7 @@ static void oneshot_trig_deactivate(struct led_classdev *led_cdev) } /* Stop blinking */ - led_brightness_set(led_cdev, LED_OFF); + led_set_brightness(led_cdev, LED_OFF); } static struct led_trigger oneshot_led_trigger = { diff --git a/drivers/leds/ledtrig-timer.c b/drivers/leds/ledtrig-timer.c index 9010f7abaf2c..f774d0592204 100644 --- a/drivers/leds/ledtrig-timer.c +++ b/drivers/leds/ledtrig-timer.c @@ -104,7 +104,7 @@ static void timer_trig_deactivate(struct led_classdev *led_cdev) } /* Stop blinking */ - led_brightness_set(led_cdev, LED_OFF); + led_set_brightness(led_cdev, LED_OFF); } static struct led_trigger timer_led_trigger = { diff --git a/include/linux/leds.h b/include/linux/leds.h index dd93a22044bb..3aade1d8f410 100644 --- a/include/linux/leds.h +++ b/include/linux/leds.h @@ -124,7 +124,7 @@ extern void led_blink_set_oneshot(struct led_classdev *led_cdev, unsigned long *delay_off, int invert); /** - * led_brightness_set - set LED brightness + * led_set_brightness - set LED brightness * @led_cdev: the LED to set * @brightness: the brightness to set it to * @@ -132,7 +132,7 @@ extern void led_blink_set_oneshot(struct led_classdev *led_cdev, * software blink timer that implements blinking when the * hardware doesn't. */ -extern void led_brightness_set(struct led_classdev *led_cdev, +extern void led_set_brightness(struct led_classdev *led_cdev, enum led_brightness brightness); /* diff --git a/net/mac80211/led.c b/net/mac80211/led.c index 1bf7903496f8..bcffa6903129 100644 --- a/net/mac80211/led.c +++ b/net/mac80211/led.c @@ -276,7 +276,7 @@ static void ieee80211_stop_tpt_led_trig(struct ieee80211_local *local) read_lock(&tpt_trig->trig.leddev_list_lock); list_for_each_entry(led_cdev, &tpt_trig->trig.led_cdevs, trig_list) - led_brightness_set(led_cdev, LED_OFF); + led_set_brightness(led_cdev, LED_OFF); read_unlock(&tpt_trig->trig.leddev_list_lock); } -- cgit v1.2.3 From 32abb4788d3fff69fa242c7850e39ec1418df4f4 Mon Sep 17 00:00:00 2001 From: "G.Shark Jeong" Date: Fri, 22 Jun 2012 08:12:06 +0800 Subject: leds: Add LED driver for lm3556 chip 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 Tested on OMAP4430 (bryan.wu@canonical.com: use module_i2c_driver() rather than lm3556_init/lm3556_exit for code simplicity; fixed some typo pointed out by Rob Landley) Signed-off-by: G.Shark Jeong Reviewed-by: Axel Lin Reviewed-by: Kim, Milo Acked-by: Rob Landley Signed-off-by: Bryan Wu --- Documentation/leds/00-INDEX | 2 + Documentation/leds/leds-lm3556.txt | 85 +++++ drivers/leds/Kconfig | 8 + drivers/leds/Makefile | 1 + drivers/leds/leds-lm3556.c | 512 ++++++++++++++++++++++++++++++ include/linux/platform_data/leds-lm3556.h | 50 +++ 6 files changed, 658 insertions(+) create mode 100644 Documentation/leds/leds-lm3556.txt create mode 100644 drivers/leds/leds-lm3556.c create mode 100644 include/linux/platform_data/leds-lm3556.h (limited to 'include') diff --git a/Documentation/leds/00-INDEX b/Documentation/leds/00-INDEX index 29f481df32c7..5fefe374892f 100644 --- a/Documentation/leds/00-INDEX +++ b/Documentation/leds/00-INDEX @@ -6,3 +6,5 @@ leds-lp5521.txt - notes on how to use the leds-lp5521 driver. leds-lp5523.txt - notes on how to use the leds-lp5523 driver. +leds-lm3556.txt + - notes on how to use the leds-lm3556 driver. diff --git a/Documentation/leds/leds-lm3556.txt b/Documentation/leds/leds-lm3556.txt new file mode 100644 index 000000000000..d9eb91b51913 --- /dev/null +++ b/Documentation/leds/leds-lm3556.txt @@ -0,0 +1,85 @@ +Kernel driver for lm3556 +======================== + +*Texas Instrument: + 1.5 A Synchronous Boost LED Flash Driver w/ High-Side Current Source +* Datasheet: http://www.national.com/ds/LM/LM3556.pdf + +Authors: + Daniel Jeong + Contact:Daniel Jeong(daniel.jeong-at-ti.com, gshark.jeong-at-gmail.com) + +Description +----------- +There are 3 functions in LM3556, Flash, Torch and Indicator. + +FLASH MODE +In Flash Mode, the LED current source(LED) provides 16 target current levels +from 93.75 mA to 1500 mA.The Flash currents are adjusted via the CURRENT +CONTROL REGISTER(0x09).Flash mode is activated by the ENABLE REGISTER(0x0A), +or by pulling the STROBE pin HIGH. +LM3556 Flash can be controlled through sys/class/leds/flash/brightness file +* if STROBE pin is enabled, below example control brightness only, and +ON / OFF will be controlled by STROBE pin. + +Flash Example: +OFF : #echo 0 > sys/class/leds/flash/brightness +93.75 mA: #echo 1 > sys/class/leds/flash/brightness +... ..... +1500 mA: #echo 16 > sys/class/leds/flash/brightness + +TORCH MODE +In Torch Mode, the current source(LED) is programmed via the CURRENT CONTROL +REGISTER(0x09).Torch Mode is activated by the ENABLE REGISTER(0x0A) or by the +hardware TORCH input. +LM3556 torch can be controlled through sys/class/leds/torch/brightness file. +* if TORCH pin is enabled, below example control brightness only, +and ON / OFF will be controlled by TORCH pin. + +Torch Example: +OFF : #echo 0 > sys/class/leds/torch/brightness +46.88 mA: #echo 1 > sys/class/leds/torch/brightness +... ..... +375 mA : #echo 8 > sys/class/leds/torch/brightness + +INDICATOR MODE +Indicator pattern can be set through sys/class/leds/indicator/pattern file, +and 4 patterns are pre-defined in indicator_pattern array. +According to N-lank, Pulse time and N Period values, different pattern wiill +be generated.If you want new patterns for your own device, change +indicator_pattern array with your own values and INDIC_PATTERN_SIZE. +Please refer datasheet for more detail about N-Blank, Pulse time and N Period. + +Indicator pattern example: +pattern 0: #echo 0 > sys/class/leds/indicator/pattern +.... +pattern 3: #echo 3 > sys/class/leds/indicator/pattern + +Indicator brightness can be controlled through +sys/class/leds/indicator/brightness file. + +Example: +OFF : #echo 0 > sys/class/leds/indicator/brightness +5.86 mA : #echo 1 > sys/class/leds/indicator/brightness +........ +46.875mA : #echo 8 > sys/class/leds/indicator/brightness + +Notes +----- +Driver expects it is registered using the i2c_board_info mechanism. +To register the chip at address 0x63 on specific adapter, set the platform data +according to include/linux/platform_data/leds-lm3556.h, set the i2c board info + +Example: + static struct i2c_board_info __initdata board_i2c_ch4[] = { + { + I2C_BOARD_INFO(LM3556_NAME, 0x63), + .platform_data = &lm3556_pdata, + }, + }; + +and register it in the platform init function + +Example: + board_register_i2c_bus(4, 400, + board_i2c_ch4, ARRAY_SIZE(board_i2c_ch4)); diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 52f1d456f3d2..f028f0348e83 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -415,6 +415,14 @@ 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" + 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. + config LEDS_OT200 tristate "LED support for the Bachmann OT200" depends on LEDS_CLASS && HAS_IOMEM diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index 9112d518f9d4..5eebd7bce4be 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -47,6 +47,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 # LED SPI Drivers obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o diff --git a/drivers/leds/leds-lm3556.c b/drivers/leds/leds-lm3556.c new file mode 100644 index 000000000000..3062abd9a532 --- /dev/null +++ b/drivers/leds/leds-lm3556.c @@ -0,0 +1,512 @@ +/* + * 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/include/linux/platform_data/leds-lm3556.h b/include/linux/platform_data/leds-lm3556.h new file mode 100644 index 000000000000..4b4e7d6b0527 --- /dev/null +++ b/include/linux/platform_data/leds-lm3556.h @@ -0,0 +1,50 @@ +/* + * 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 */ -- cgit v1.2.3