Merge branch 'for-3.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/cooloney/linux-leds

Pull LED subsystem update from Bryan Wu.

* 'for-3.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/cooloney/linux-leds: (50 commits)
  leds-lp8788: forgotten unlock at lp8788_led_work
  LEDS: propagate error codes in blinkm_detect()
  LEDS: memory leak in blinkm_led_common_set()
  leds: add new lp8788 led driver
  LEDS: add BlinkM RGB LED driver, documentation and update MAINTAINERS
  leds: max8997: Simplify max8997_led_set_mode implementation
  leds/leds-s3c24xx: use devm_gpio_request
  leds: convert Network Space v2 LED driver to devm_kzalloc() and cleanup error exit path
  leds: convert DAC124S085 LED driver to devm_kzalloc()
  leds: convert LM3530 LED driver to devm_kzalloc() and cleanup error exit path
  leds: convert TCA6507 LED driver to devm_kzalloc()
  leds: convert Freescale MC13783 LED driver to devm_kzalloc() and cleanup error exit path
  leds: convert ADP5520 LED driver to devm_kzalloc() and cleanup error exit path
  leds: convert PCA955x LED driver to devm_kzalloc() and cleanup error exit path
  leds: convert Sun Fire LED driver to devm_kzalloc() and cleanup error exit path
  leds: convert PCA9532 LED driver to devm_kzalloc()
  leds: convert LT3593 LED driver to devm_kzalloc()
  leds: convert Renesas TPU LED driver to devm_kzalloc() and cleanup error exit path
  leds: convert LP5523 LED driver to devm_kzalloc() and cleanup error exit path
  leds: convert PCA9633 LED driver to devm_kzalloc()
  ...
This commit is contained in:
Linus Torvalds 2012-07-26 20:26:27 -07:00
commit aa0b3b2bee
51 changed files with 2376 additions and 413 deletions

View File

@ -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.

View File

@ -0,0 +1,80 @@
The leds-blinkm driver supports the devices of the BlinkM family.
They are RGB-LED modules driven by a (AT)tiny microcontroller and
communicate through I2C. The default address of these modules is
0x09 but this can be changed through a command. By this you could
dasy-chain up to 127 BlinkMs on an I2C bus.
The device accepts RGB and HSB color values through separate commands.
Also you can store blinking sequences as "scripts" in
the controller and run them. Also fading is an option.
The interface this driver provides is 2-fold:
a) LED class interface for use with triggers
############################################
The registration follows the scheme:
blinkm-<i2c-bus-nr>-<i2c-device-nr>-<color>
$ ls -h /sys/class/leds/blinkm-6-*
/sys/class/leds/blinkm-6-9-blue:
brightness device max_brightness power subsystem trigger uevent
/sys/class/leds/blinkm-6-9-green:
brightness device max_brightness power subsystem trigger uevent
/sys/class/leds/blinkm-6-9-red:
brightness device max_brightness power subsystem trigger uevent
(same is /sys/bus/i2c/devices/6-0009/leds)
We can control the colors separated into red, green and blue and
assign triggers on each color.
E.g.:
$ cat blinkm-6-9-blue/brightness
05
$ echo 200 > blinkm-6-9-blue/brightness
$
$ modprobe ledtrig-heartbeat
$ echo heartbeat > blinkm-6-9-green/trigger
$
b) Sysfs group to control rgb, fade, hsb, scripts ...
#####################################################
This extended interface is available as folder blinkm
in the sysfs folder of the I2C device.
E.g. below /sys/bus/i2c/devices/6-0009/blinkm
$ ls -h /sys/bus/i2c/devices/6-0009/blinkm/
blue green red test
Currently supported is just setting red, green, blue
and a test sequence.
E.g.:
$ cat *
00
00
00
#Write into test to start test sequence!#
$ echo 1 > test
$
$ echo 255 > red
$
as of 6/2012
dl9pf <at> gmx <dot> de

View File

@ -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));

View File

@ -0,0 +1,59 @@
One-shot LED Trigger
====================
This is a LED trigger useful for signaling the user of an event where there are
no clear trap points to put standard led-on and led-off settings. Using this
trigger, the application needs only to signal the trigger when an event has
happened, than the trigger turns the LED on and than keeps it off for a
specified amount of time.
This trigger is meant to be usable both for sporadic and dense events. In the
first case, the trigger produces a clear single controlled blink for each
event, while in the latter it keeps blinking at constant rate, as to signal
that the events are arriving continuously.
A one-shot LED only stays in a constant state when there are no events. An
additional "invert" property specifies if the LED has to stay off (normal) or
on (inverted) when not rearmed.
The trigger can be activated from user space on led class devices as shown
below:
echo oneshot > trigger
This adds the following sysfs attributes to the LED:
delay_on - specifies for how many milliseconds the LED has to stay at
LED_FULL brightness after it has been armed.
Default to 100 ms.
delay_off - specifies for how many milliseconds the LED has to stay at
LED_OFF brightness after it has been armed.
Default to 100 ms.
invert - reverse the blink logic. If set to 0 (default) blink on for delay_on
ms, then blink off for delay_off ms, leaving the LED normally off. If
set to 1, blink off for delay_off ms, then blink on for delay_on ms,
leaving the LED normally on.
Setting this value also immediately change the LED state.
shot - write any non-empty string to signal an events, this starts a blink
sequence if not already running.
Example use-case: network devices, initialization:
echo oneshot > trigger # set trigger for this led
echo 33 > delay_on # blink at 1 / (33 + 33) Hz on continuous traffic
echo 33 > delay_off
interface goes up:
echo 1 > invert # set led as normally-on, turn the led on
packet received/transmitted:
echo 1 > shot # led starts blinking, ignored if already blinking
interface goes down
echo 0 > invert # set led as normally-off, turn the led off

View File

@ -1529,6 +1529,11 @@ W: http://blackfin.uclinux.org/
S: Supported
F: drivers/i2c/busses/i2c-bfin-twi.c
BLINKM RGB LED DRIVER
M: Jan-Simon Moeller <jansimon.moeller@gmx.de>
S: Maintained
F: drivers/leds/leds-blinkm.c
BLOCK LAYER
M: Jens Axboe <axboe@kernel.dk>
T: git git://git.kernel.org/pub/scm/linux/kernel/git/axboe/linux-block.git

View File

@ -200,6 +200,13 @@ config LEDS_LP5523
Driver provides direct control via LED class and interface for
programming the engines.
config LEDS_LP8788
tristate "LED support for the TI LP8788 PMIC"
depends on LEDS_CLASS
depends on MFD_LP8788
help
This option enables support for the Keyboard LEDs on the LP8788 PMIC.
config LEDS_CLEVO_MAIL
tristate "Mail LED on Clevo notebook"
depends on LEDS_CLASS
@ -415,6 +422,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
@ -422,6 +437,14 @@ config LEDS_OT200
This option enables support for the LEDs on the Bachmann OT200.
Say Y to enable LEDs on the Bachmann OT200.
config LEDS_BLINKM
tristate "LED support for the BlinkM I2C RGB LED"
depends on LEDS_CLASS
depends on I2C
help
This option enables support for the BlinkM RGB LED connected
through I2C. Say Y to enable support for the BlinkM LED.
config LEDS_TRIGGERS
bool "LED Trigger support"
depends on LEDS_CLASS
@ -443,6 +466,20 @@ config LEDS_TRIGGER_TIMER
If unsure, say Y.
config LEDS_TRIGGER_ONESHOT
tristate "LED One-shot Trigger"
depends on LEDS_TRIGGERS
help
This allows LEDs to blink in one-shot pulses with parameters
controlled via sysfs. It's useful to notify the user on
sporadic events, when there are no clear begin and end trap points,
or on dense events, where this blinks the LED at constant rate if
rearmed continuously.
It also shows how to use the led_blink_set_oneshot() function.
If unsure, say Y.
config LEDS_TRIGGER_IDE_DISK
bool "LED IDE Disk Trigger"
depends on IDE_GD_ATA
@ -497,7 +534,7 @@ config LEDS_TRIGGER_TRANSIENT
depends on LEDS_TRIGGERS
help
This allows one time activation of a transient state on
GPIO/PWM based hadrware.
GPIO/PWM based hardware.
If unsure, say Y.
endif # NEW_LEDS

View File

@ -24,6 +24,7 @@ obj-$(CONFIG_LEDS_GPIO) += leds-gpio.o
obj-$(CONFIG_LEDS_LP3944) += leds-lp3944.o
obj-$(CONFIG_LEDS_LP5521) += leds-lp5521.o
obj-$(CONFIG_LEDS_LP5523) += leds-lp5523.o
obj-$(CONFIG_LEDS_LP8788) += leds-lp8788.o
obj-$(CONFIG_LEDS_TCA6507) += leds-tca6507.o
obj-$(CONFIG_LEDS_CLEVO_MAIL) += leds-clevo-mail.o
obj-$(CONFIG_LEDS_HP6XX) += leds-hp6xx.o
@ -47,12 +48,15 @@ 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_BLINKM) += leds-blinkm.o
# LED SPI Drivers
obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o
# LED Triggers
obj-$(CONFIG_LEDS_TRIGGER_TIMER) += ledtrig-timer.o
obj-$(CONFIG_LEDS_TRIGGER_ONESHOT) += ledtrig-oneshot.o
obj-$(CONFIG_LEDS_TRIGGER_IDE_DISK) += ledtrig-ide-disk.o
obj-$(CONFIG_LEDS_TRIGGER_HEARTBEAT) += ledtrig-heartbeat.o
obj-$(CONFIG_LEDS_TRIGGER_BACKLIGHT) += ledtrig-backlight.o

View File

@ -53,7 +53,7 @@ static ssize_t led_brightness_store(struct device *dev,
if (state == LED_OFF)
led_trigger_remove(led_cdev);
led_set_brightness(led_cdev, state);
__led_set_brightness(led_cdev, state);
return size;
}
@ -82,7 +82,12 @@ static void led_timer_function(unsigned long data)
unsigned long delay;
if (!led_cdev->blink_delay_on || !led_cdev->blink_delay_off) {
led_set_brightness(led_cdev, LED_OFF);
__led_set_brightness(led_cdev, LED_OFF);
return;
}
if (led_cdev->flags & LED_BLINK_ONESHOT_STOP) {
led_cdev->flags &= ~LED_BLINK_ONESHOT_STOP;
return;
}
@ -100,7 +105,21 @@ static void led_timer_function(unsigned long data)
delay = led_cdev->blink_delay_off;
}
led_set_brightness(led_cdev, brightness);
__led_set_brightness(led_cdev, brightness);
/* Return in next iteration if led is in one-shot mode and we are in
* the final blink state so that the led is toggled each delay_on +
* delay_off milliseconds in worst case.
*/
if (led_cdev->flags & LED_BLINK_ONESHOT) {
if (led_cdev->flags & LED_BLINK_INVERT) {
if (brightness)
led_cdev->flags |= LED_BLINK_ONESHOT_STOP;
} else {
if (!brightness)
led_cdev->flags |= LED_BLINK_ONESHOT_STOP;
}
}
mod_timer(&led_cdev->blink_timer, jiffies + msecs_to_jiffies(delay));
}
@ -203,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);

View File

@ -24,14 +24,6 @@ EXPORT_SYMBOL_GPL(leds_list_lock);
LIST_HEAD(leds_list);
EXPORT_SYMBOL_GPL(leds_list);
static void led_stop_software_blink(struct led_classdev *led_cdev)
{
/* deactivate previous settings */
del_timer_sync(&led_cdev->blink_timer);
led_cdev->blink_delay_on = 0;
led_cdev->blink_delay_off = 0;
}
static void led_set_software_blink(struct led_classdev *led_cdev,
unsigned long delay_on,
unsigned long delay_off)
@ -53,7 +45,7 @@ static void led_set_software_blink(struct led_classdev *led_cdev,
/* never off - just set to brightness */
if (!delay_off) {
led_set_brightness(led_cdev, led_cdev->blink_brightness);
__led_set_brightness(led_cdev, led_cdev->blink_brightness);
return;
}
@ -61,13 +53,12 @@ static void led_set_software_blink(struct led_classdev *led_cdev,
}
void led_blink_set(struct led_classdev *led_cdev,
unsigned long *delay_on,
unsigned long *delay_off)
static void led_blink_setup(struct led_classdev *led_cdev,
unsigned long *delay_on,
unsigned long *delay_off)
{
del_timer_sync(&led_cdev->blink_timer);
if (led_cdev->blink_set &&
if (!(led_cdev->flags & LED_BLINK_ONESHOT) &&
led_cdev->blink_set &&
!led_cdev->blink_set(led_cdev, delay_on, delay_off))
return;
@ -77,12 +68,49 @@ void led_blink_set(struct led_classdev *led_cdev,
led_set_software_blink(led_cdev, *delay_on, *delay_off);
}
void led_blink_set(struct led_classdev *led_cdev,
unsigned long *delay_on,
unsigned long *delay_off)
{
del_timer_sync(&led_cdev->blink_timer);
led_cdev->flags &= ~LED_BLINK_ONESHOT;
led_cdev->flags &= ~LED_BLINK_ONESHOT_STOP;
led_blink_setup(led_cdev, delay_on, delay_off);
}
EXPORT_SYMBOL(led_blink_set);
void led_brightness_set(struct led_classdev *led_cdev,
void led_blink_set_oneshot(struct led_classdev *led_cdev,
unsigned long *delay_on,
unsigned long *delay_off,
int invert)
{
if ((led_cdev->flags & LED_BLINK_ONESHOT) &&
timer_pending(&led_cdev->blink_timer))
return;
led_cdev->flags |= LED_BLINK_ONESHOT;
led_cdev->flags &= ~LED_BLINK_ONESHOT_STOP;
if (invert)
led_cdev->flags |= LED_BLINK_INVERT;
else
led_cdev->flags &= ~LED_BLINK_INVERT;
led_blink_setup(led_cdev, delay_on, delay_off);
}
EXPORT_SYMBOL(led_blink_set_oneshot);
void led_set_brightness(struct led_classdev *led_cdev,
enum led_brightness brightness)
{
led_stop_software_blink(led_cdev);
led_cdev->brightness_set(led_cdev, brightness);
/* stop and clear soft-blink timer */
del_timer_sync(&led_cdev->blink_timer);
led_cdev->blink_delay_on = 0;
led_cdev->blink_delay_off = 0;
__led_set_brightness(led_cdev, brightness);
}
EXPORT_SYMBOL(led_brightness_set);
EXPORT_SYMBOL(led_set_brightness);

View File

@ -99,7 +99,7 @@ ssize_t led_trigger_show(struct device *dev, struct device_attribute *attr,
EXPORT_SYMBOL_GPL(led_trigger_show);
/* Caller must ensure led_cdev->trigger_lock held */
void led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trigger)
void led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trig)
{
unsigned long flags;
@ -112,15 +112,15 @@ void led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trigger)
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 (trigger) {
write_lock_irqsave(&trigger->leddev_list_lock, flags);
list_add_tail(&led_cdev->trig_list, &trigger->led_cdevs);
write_unlock_irqrestore(&trigger->leddev_list_lock, flags);
led_cdev->trigger = trigger;
if (trigger->activate)
trigger->activate(led_cdev);
if (trig) {
write_lock_irqsave(&trig->leddev_list_lock, flags);
list_add_tail(&led_cdev->trig_list, &trig->led_cdevs);
write_unlock_irqrestore(&trig->leddev_list_lock, flags);
led_cdev->trigger = trig;
if (trig->activate)
trig->activate(led_cdev);
}
}
EXPORT_SYMBOL_GPL(led_trigger_set);
@ -153,24 +153,24 @@ EXPORT_SYMBOL_GPL(led_trigger_set_default);
/* LED Trigger Interface */
int led_trigger_register(struct led_trigger *trigger)
int led_trigger_register(struct led_trigger *trig)
{
struct led_classdev *led_cdev;
struct led_trigger *trig;
struct led_trigger *_trig;
rwlock_init(&trigger->leddev_list_lock);
INIT_LIST_HEAD(&trigger->led_cdevs);
rwlock_init(&trig->leddev_list_lock);
INIT_LIST_HEAD(&trig->led_cdevs);
down_write(&triggers_list_lock);
/* Make sure the trigger's name isn't already in use */
list_for_each_entry(trig, &trigger_list, next_trig) {
if (!strcmp(trig->name, trigger->name)) {
list_for_each_entry(_trig, &trigger_list, next_trig) {
if (!strcmp(_trig->name, trig->name)) {
up_write(&triggers_list_lock);
return -EEXIST;
}
}
/* Add to the list of led triggers */
list_add_tail(&trigger->next_trig, &trigger_list);
list_add_tail(&trig->next_trig, &trigger_list);
up_write(&triggers_list_lock);
/* Register with any LEDs that have this as a default trigger */
@ -178,8 +178,8 @@ int led_trigger_register(struct led_trigger *trigger)
list_for_each_entry(led_cdev, &leds_list, node) {
down_write(&led_cdev->trigger_lock);
if (!led_cdev->trigger && led_cdev->default_trigger &&
!strcmp(led_cdev->default_trigger, trigger->name))
led_trigger_set(led_cdev, trigger);
!strcmp(led_cdev->default_trigger, trig->name))
led_trigger_set(led_cdev, trig);
up_write(&led_cdev->trigger_lock);
}
up_read(&leds_list_lock);
@ -188,20 +188,20 @@ int led_trigger_register(struct led_trigger *trigger)
}
EXPORT_SYMBOL_GPL(led_trigger_register);
void led_trigger_unregister(struct led_trigger *trigger)
void led_trigger_unregister(struct led_trigger *trig)
{
struct led_classdev *led_cdev;
/* Remove from the list of led triggers */
down_write(&triggers_list_lock);
list_del(&trigger->next_trig);
list_del(&trig->next_trig);
up_write(&triggers_list_lock);
/* Remove anyone actively using this trigger */
down_read(&leds_list_lock);
list_for_each_entry(led_cdev, &leds_list, node) {
down_write(&led_cdev->trigger_lock);
if (led_cdev->trigger == trigger)
if (led_cdev->trigger == trig)
led_trigger_set(led_cdev, NULL);
up_write(&led_cdev->trigger_lock);
}
@ -211,58 +211,80 @@ EXPORT_SYMBOL_GPL(led_trigger_unregister);
/* Simple LED Tigger Interface */
void led_trigger_event(struct led_trigger *trigger,
void led_trigger_event(struct led_trigger *trig,
enum led_brightness brightness)
{
struct list_head *entry;
if (!trigger)
if (!trig)
return;
read_lock(&trigger->leddev_list_lock);
list_for_each(entry, &trigger->led_cdevs) {
read_lock(&trig->leddev_list_lock);
list_for_each(entry, &trig->led_cdevs) {
struct led_classdev *led_cdev;
led_cdev = list_entry(entry, struct led_classdev, trig_list);
led_set_brightness(led_cdev, brightness);
}
read_unlock(&trigger->leddev_list_lock);
read_unlock(&trig->leddev_list_lock);
}
EXPORT_SYMBOL_GPL(led_trigger_event);
void led_trigger_blink(struct led_trigger *trigger,
unsigned long *delay_on,
unsigned long *delay_off)
static void led_trigger_blink_setup(struct led_trigger *trig,
unsigned long *delay_on,
unsigned long *delay_off,
int oneshot,
int invert)
{
struct list_head *entry;
if (!trigger)
if (!trig)
return;
read_lock(&trigger->leddev_list_lock);
list_for_each(entry, &trigger->led_cdevs) {
read_lock(&trig->leddev_list_lock);
list_for_each(entry, &trig->led_cdevs) {
struct led_classdev *led_cdev;
led_cdev = list_entry(entry, struct led_classdev, trig_list);
led_blink_set(led_cdev, delay_on, delay_off);
if (oneshot)
led_blink_set_oneshot(led_cdev, delay_on, delay_off,
invert);
else
led_blink_set(led_cdev, delay_on, delay_off);
}
read_unlock(&trigger->leddev_list_lock);
read_unlock(&trig->leddev_list_lock);
}
void led_trigger_blink(struct led_trigger *trig,
unsigned long *delay_on,
unsigned long *delay_off)
{
led_trigger_blink_setup(trig, delay_on, delay_off, 0, 0);
}
EXPORT_SYMBOL_GPL(led_trigger_blink);
void led_trigger_blink_oneshot(struct led_trigger *trig,
unsigned long *delay_on,
unsigned long *delay_off,
int invert)
{
led_trigger_blink_setup(trig, delay_on, delay_off, 1, invert);
}
EXPORT_SYMBOL_GPL(led_trigger_blink_oneshot);
void led_trigger_register_simple(const char *name, struct led_trigger **tp)
{
struct led_trigger *trigger;
struct led_trigger *trig;
int err;
trigger = kzalloc(sizeof(struct led_trigger), GFP_KERNEL);
trig = kzalloc(sizeof(struct led_trigger), GFP_KERNEL);
if (trigger) {
trigger->name = name;
err = led_trigger_register(trigger);
if (trig) {
trig->name = name;
err = led_trigger_register(trig);
if (err < 0) {
kfree(trigger);
trigger = NULL;
kfree(trig);
trig = NULL;
printk(KERN_WARNING "LED trigger %s failed to register"
" (%d)\n", name, err);
}
@ -270,15 +292,15 @@ void led_trigger_register_simple(const char *name, struct led_trigger **tp)
printk(KERN_WARNING "LED trigger %s failed to register"
" (no memory)\n", name);
*tp = trigger;
*tp = trig;
}
EXPORT_SYMBOL_GPL(led_trigger_register_simple);
void led_trigger_unregister_simple(struct led_trigger *trigger)
void led_trigger_unregister_simple(struct led_trigger *trig)
{
if (trigger)
led_trigger_unregister(trigger);
kfree(trigger);
if (trig)
led_trigger_unregister(trig);
kfree(trig);
}
EXPORT_SYMBOL_GPL(led_trigger_unregister_simple);

View File

@ -209,7 +209,7 @@ static int pm860x_led_probe(struct platform_device *pdev)
return -EINVAL;
}
data = kzalloc(sizeof(struct pm860x_led), GFP_KERNEL);
data = devm_kzalloc(&pdev->dev, sizeof(struct pm860x_led), GFP_KERNEL);
if (data == NULL)
return -ENOMEM;
strncpy(data->name, res->name, MFD_NAME_SIZE - 1);
@ -220,7 +220,6 @@ static int pm860x_led_probe(struct platform_device *pdev)
data->port = pdata->flags;
if (data->port < 0) {
dev_err(&pdev->dev, "check device failed\n");
kfree(data);
return -EINVAL;
}
@ -233,13 +232,10 @@ static int pm860x_led_probe(struct platform_device *pdev)
ret = led_classdev_register(chip->dev, &data->cdev);
if (ret < 0) {
dev_err(&pdev->dev, "Failed to register LED: %d\n", ret);
goto out;
return ret;
}
pm860x_led_set(&data->cdev, 0);
return 0;
out:
kfree(data);
return ret;
}
static int pm860x_led_remove(struct platform_device *pdev)
@ -247,7 +243,6 @@ static int pm860x_led_remove(struct platform_device *pdev)
struct pm860x_led *data = platform_get_drvdata(pdev);
led_classdev_unregister(&data->cdev);
kfree(data);
return 0;
}

View File

@ -119,7 +119,8 @@ static int __devinit adp5520_led_probe(struct platform_device *pdev)
return -EFAULT;
}
led = kzalloc(sizeof(*led) * pdata->num_leds, GFP_KERNEL);
led = devm_kzalloc(&pdev->dev, sizeof(*led) * pdata->num_leds,
GFP_KERNEL);
if (led == NULL) {
dev_err(&pdev->dev, "failed to alloc memory\n");
return -ENOMEM;
@ -129,7 +130,7 @@ static int __devinit adp5520_led_probe(struct platform_device *pdev)
if (ret) {
dev_err(&pdev->dev, "failed to write\n");
goto err_free;
return ret;
}
for (i = 0; i < pdata->num_leds; ++i) {
@ -179,8 +180,6 @@ err:
}
}
err_free:
kfree(led);
return ret;
}
@ -200,7 +199,6 @@ static int __devexit adp5520_led_remove(struct platform_device *pdev)
cancel_work_sync(&led[i].work);
}
kfree(led);
return 0;
}

View File

@ -99,12 +99,13 @@ static int __devinit asic3_led_probe(struct platform_device *pdev)
ret = mfd_cell_enable(pdev);
if (ret < 0)
goto ret0;
return ret;
led->cdev = kzalloc(sizeof(struct led_classdev), GFP_KERNEL);
led->cdev = devm_kzalloc(&pdev->dev, sizeof(struct led_classdev),
GFP_KERNEL);
if (!led->cdev) {
ret = -ENOMEM;
goto ret1;
goto out;
}
led->cdev->name = led->name;
@ -115,15 +116,12 @@ static int __devinit asic3_led_probe(struct platform_device *pdev)
ret = led_classdev_register(&pdev->dev, led->cdev);
if (ret < 0)
goto ret2;
goto out;
return 0;
ret2:
kfree(led->cdev);
ret1:
out:
(void) mfd_cell_disable(pdev);
ret0:
return ret;
}
@ -133,8 +131,6 @@ static int __devexit asic3_led_remove(struct platform_device *pdev)
led_classdev_unregister(led->cdev);
kfree(led->cdev);
return mfd_cell_disable(pdev);
}

View File

@ -46,7 +46,8 @@ static int __devinit pwmled_probe(struct platform_device *pdev)
if (!pdata || pdata->num_leds < 1)
return -ENODEV;
leds = kcalloc(pdata->num_leds, sizeof(*leds), GFP_KERNEL);
leds = devm_kzalloc(&pdev->dev, pdata->num_leds * sizeof(*leds),
GFP_KERNEL);
if (!leds)
return -ENOMEM;
@ -108,7 +109,6 @@ err:
pwm_channel_free(&leds[i].pwmc);
}
}
kfree(leds);
return status;
}
@ -129,7 +129,6 @@ static int __exit pwmled_remove(struct platform_device *pdev)
pwm_channel_free(&led->pwmc);
}
kfree(leds);
platform_set_drvdata(pdev, NULL);
return 0;
}

View File

@ -677,7 +677,7 @@ static int __devinit bd2802_probe(struct i2c_client *client,
struct bd2802_led_platform_data *pdata;
int ret, i;
led = kzalloc(sizeof(struct bd2802_led), GFP_KERNEL);
led = devm_kzalloc(&client->dev, sizeof(struct bd2802_led), GFP_KERNEL);
if (!led) {
dev_err(&client->dev, "failed to allocate driver data\n");
return -ENOMEM;
@ -697,7 +697,7 @@ static int __devinit bd2802_probe(struct i2c_client *client,
ret = bd2802_write_byte(client, BD2802_REG_CLKSETUP, 0x00);
if (ret < 0) {
dev_err(&client->dev, "failed to detect device\n");
goto failed_free;
return ret;
} else
dev_info(&client->dev, "return 0x%02x\n", ret);
@ -729,9 +729,6 @@ static int __devinit bd2802_probe(struct i2c_client *client,
failed_unregister_dev_file:
for (i--; i >= 0; i--)
device_remove_file(&led->client->dev, bd2802_attributes[i]);
failed_free:
kfree(led);
return ret;
}
@ -746,7 +743,6 @@ static int __exit bd2802_remove(struct i2c_client *client)
bd2802_disable_adv_conf(led);
for (i = 0; i < ARRAY_SIZE(bd2802_attributes); i++)
device_remove_file(&led->client->dev, bd2802_attributes[i]);
kfree(led);
return 0;
}

815
drivers/leds/leds-blinkm.c Normal file
View File

@ -0,0 +1,815 @@
/*
* leds-blinkm.c
* (c) Jan-Simon Möller (dl9pf@gmx.de)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
#include <linux/i2c.h>
#include <linux/err.h>
#include <linux/mutex.h>
#include <linux/sysfs.h>
#include <linux/printk.h>
#include <linux/pm_runtime.h>
#include <linux/leds.h>
#include <linux/delay.h>
/* Addresses to scan - BlinkM is on 0x09 by default*/
static const unsigned short normal_i2c[] = { 0x09, I2C_CLIENT_END };
static int blinkm_transfer_hw(struct i2c_client *client, int cmd);
static int blinkm_test_run(struct i2c_client *client);
struct blinkm_led {
struct i2c_client *i2c_client;
struct led_classdev led_cdev;
int id;
atomic_t active;
};
struct blinkm_work {
struct blinkm_led *blinkm_led;
struct work_struct work;
};
#define cdev_to_blmled(c) container_of(c, struct blinkm_led, led_cdev)
#define work_to_blmwork(c) container_of(c, struct blinkm_work, work)
struct blinkm_data {
struct i2c_client *i2c_client;
struct mutex update_lock;
/* used for led class interface */
struct blinkm_led blinkm_leds[3];
/* used for "blinkm" sysfs interface */
u8 red; /* color red */
u8 green; /* color green */
u8 blue; /* color blue */
/* next values to use for transfer */
u8 next_red; /* color red */
u8 next_green; /* color green */
u8 next_blue; /* color blue */
/* internal use */
u8 args[7]; /* set of args for transmission */
u8 i2c_addr; /* i2c addr */
u8 fw_ver; /* firmware version */
/* used, but not from userspace */
u8 hue; /* HSB hue */
u8 saturation; /* HSB saturation */
u8 brightness; /* HSB brightness */
u8 next_hue; /* HSB hue */
u8 next_saturation; /* HSB saturation */
u8 next_brightness; /* HSB brightness */
/* currently unused / todo */
u8 fade_speed; /* fade speed 1 - 255 */
s8 time_adjust; /* time adjust -128 - 127 */
u8 fade:1; /* fade on = 1, off = 0 */
u8 rand:1; /* rand fade mode on = 1 */
u8 script_id; /* script ID */
u8 script_repeats; /* repeats of script */
u8 script_startline; /* line to start */
};
/* Colors */
#define RED 0
#define GREEN 1
#define BLUE 2
/* mapping command names to cmd chars - see datasheet */
#define BLM_GO_RGB 0
#define BLM_FADE_RGB 1
#define BLM_FADE_HSB 2
#define BLM_FADE_RAND_RGB 3
#define BLM_FADE_RAND_HSB 4
#define BLM_PLAY_SCRIPT 5
#define BLM_STOP_SCRIPT 6
#define BLM_SET_FADE_SPEED 7
#define BLM_SET_TIME_ADJ 8
#define BLM_GET_CUR_RGB 9
#define BLM_WRITE_SCRIPT_LINE 10
#define BLM_READ_SCRIPT_LINE 11
#define BLM_SET_SCRIPT_LR 12 /* Length & Repeats */
#define BLM_SET_ADDR 13
#define BLM_GET_ADDR 14
#define BLM_GET_FW_VER 15
#define BLM_SET_STARTUP_PARAM 16
/* BlinkM Commands
* as extracted out of the datasheet:
*
* cmdchar = command (ascii)
* cmdbyte = command in hex
* nr_args = number of arguments (to send)
* nr_ret = number of return values (to read)
* dir = direction (0 = read, 1 = write, 2 = both)
*
*/
static const struct {
char cmdchar;
u8 cmdbyte;
u8 nr_args;
u8 nr_ret;
u8 dir:2;
} blinkm_cmds[17] = {
/* cmdchar, cmdbyte, nr_args, nr_ret, dir */
{ 'n', 0x6e, 3, 0, 1},
{ 'c', 0x63, 3, 0, 1},
{ 'h', 0x68, 3, 0, 1},
{ 'C', 0x43, 3, 0, 1},
{ 'H', 0x48, 3, 0, 1},
{ 'p', 0x70, 3, 0, 1},
{ 'o', 0x6f, 0, 0, 1},
{ 'f', 0x66, 1, 0, 1},
{ 't', 0x74, 1, 0, 1},
{ 'g', 0x67, 0, 3, 0},
{ 'W', 0x57, 7, 0, 1},
{ 'R', 0x52, 2, 5, 2},
{ 'L', 0x4c, 3, 0, 1},
{ 'A', 0x41, 4, 0, 1},
{ 'a', 0x61, 0, 1, 0},
{ 'Z', 0x5a, 0, 1, 0},
{ 'B', 0x42, 5, 0, 1},
};
static ssize_t show_color_common(struct device *dev, char *buf, int color)
{
struct i2c_client *client;
struct blinkm_data *data;
int ret;
client = to_i2c_client(dev);
data = i2c_get_clientdata(client);
ret = blinkm_transfer_hw(client, BLM_GET_CUR_RGB);
if (ret < 0)
return ret;
switch (color) {
case RED:
return scnprintf(buf, PAGE_SIZE, "%02X\n", data->red);
break;
case GREEN:
return scnprintf(buf, PAGE_SIZE, "%02X\n", data->green);
break;
case BLUE:
return scnprintf(buf, PAGE_SIZE, "%02X\n", data->blue);
break;
default:
return -EINVAL;
}
return -EINVAL;
}
static int store_color_common(struct device *dev, const char *buf, int color)
{
struct i2c_client *client;
struct blinkm_data *data;
int ret;
u8 value;
client = to_i2c_client(dev);
data = i2c_get_clientdata(client);
ret = kstrtou8(buf, 10, &value);
if (ret < 0) {
dev_err(dev, "BlinkM: value too large!\n");
return ret;
}
switch (color) {
case RED:
data->next_red = value;
break;
case GREEN:
data->next_green = value;
break;
case BLUE:
data->next_blue = value;
break;
default:
return -EINVAL;
}
dev_dbg(dev, "next_red = %d, next_green = %d, next_blue = %d\n",
data->next_red, data->next_green, data->next_blue);
/* if mode ... */
ret = blinkm_transfer_hw(client, BLM_GO_RGB);
if (ret < 0) {
dev_err(dev, "BlinkM: can't set RGB\n");
return ret;
}
return 0;
}
static ssize_t show_red(struct device *dev, struct device_attribute *attr,
char *buf)
{
return show_color_common(dev, buf, RED);
}
static ssize_t store_red(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
int ret;
ret = store_color_common(dev, buf, RED);
if (ret < 0)
return ret;
return count;
}
static DEVICE_ATTR(red, S_IRUGO | S_IWUSR, show_red, store_red);
static ssize_t show_green(struct device *dev, struct device_attribute *attr,
char *buf)
{
return show_color_common(dev, buf, GREEN);
}
static ssize_t store_green(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
int ret;
ret = store_color_common(dev, buf, GREEN);
if (ret < 0)
return ret;
return count;
}
static DEVICE_ATTR(green, S_IRUGO | S_IWUSR, show_green, store_green);
static ssize_t show_blue(struct device *dev, struct device_attribute *attr,
char *buf)
{
return show_color_common(dev, buf, BLUE);
}
static ssize_t store_blue(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
int ret;
ret = store_color_common(dev, buf, BLUE);
if (ret < 0)
return ret;
return count;
}
static DEVICE_ATTR(blue, S_IRUGO | S_IWUSR, show_blue, store_blue);
static ssize_t show_test(struct device *dev, struct device_attribute *attr,
char *buf)
{
return scnprintf(buf, PAGE_SIZE,
"#Write into test to start test sequence!#\n");
}
static ssize_t store_test(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct i2c_client *client;
int ret;
client = to_i2c_client(dev);
/*test */
ret = blinkm_test_run(client);
if (ret < 0)
return ret;
return count;
}
static DEVICE_ATTR(test, S_IRUGO | S_IWUSR, show_test, store_test);
/* TODO: HSB, fade, timeadj, script ... */
static struct attribute *blinkm_attrs[] = {
&dev_attr_red.attr,
&dev_attr_green.attr,
&dev_attr_blue.attr,
&dev_attr_test.attr,
NULL,
};
static struct attribute_group blinkm_group = {
.name = "blinkm",
.attrs = blinkm_attrs,
};
static int blinkm_write(struct i2c_client *client, int cmd, u8 *arg)
{
int result;
int i;
int arglen = blinkm_cmds[cmd].nr_args;
/* write out cmd to blinkm - always / default step */
result = i2c_smbus_write_byte(client, blinkm_cmds[cmd].cmdbyte);
if (result < 0)
return result;
/* no args to write out */
if (arglen == 0)
return 0;
for (i = 0; i < arglen; i++) {
/* repeat for arglen */
result = i2c_smbus_write_byte(client, arg[i]);
if (result < 0)
return result;
}
return 0;
}
static int blinkm_read(struct i2c_client *client, int cmd, u8 *arg)
{
int result;
int i;
int retlen = blinkm_cmds[cmd].nr_ret;
for (i = 0; i < retlen; i++) {
/* repeat for retlen */
result = i2c_smbus_read_byte(client);
if (result < 0)
return result;
arg[i] = result;
}
return 0;
}
static int blinkm_transfer_hw(struct i2c_client *client, int cmd)
{
/* the protocol is simple but non-standard:
* e.g. cmd 'g' (= 0x67) for "get device address"
* - which defaults to 0x09 - would be the sequence:
* a) write 0x67 to the device (byte write)
* b) read the value (0x09) back right after (byte read)
*
* Watch out for "unfinished" sequences (i.e. not enough reads
* or writes after a command. It will make the blinkM misbehave.
* Sequence is key here.
*/
/* args / return are in private data struct */
struct blinkm_data *data = i2c_get_clientdata(client);
/* We start hardware transfers which are not to be
* mixed with other commands. Aquire a lock now. */
if (mutex_lock_interruptible(&data->update_lock) < 0)
return -EAGAIN;
/* switch cmd - usually write before reads */
switch (cmd) {
case BLM_FADE_RAND_RGB:
case BLM_GO_RGB:
case BLM_FADE_RGB:
data->args[0] = data->next_red;
data->args[1] = data->next_green;
data->args[2] = data->next_blue;
blinkm_write(client, cmd, data->args);
data->red = data->args[0];
data->green = data->args[1];
data->blue = data->args[2];
break;
case BLM_FADE_HSB:
case BLM_FADE_RAND_HSB:
data->args[0] = data->next_hue;
data->args[1] = data->next_saturation;
data->args[2] = data->next_brightness;
blinkm_write(client, cmd, data->args);
data->hue = data->next_hue;
data->saturation = data->next_saturation;
data->brightness = data->next_brightness;
break;
case BLM_PLAY_SCRIPT:
data->args[0] = data->script_id;
data->args[1] = data->script_repeats;
data->args[2] = data->script_startline;
blinkm_write(client, cmd, data->args);
break;
case BLM_STOP_SCRIPT:
blinkm_write(client, cmd, NULL);
break;
case BLM_GET_CUR_RGB:
data->args[0] = data->red;
data->args[1] = data->green;
data->args[2] = data->blue;
blinkm_write(client, cmd, NULL);
blinkm_read(client, cmd, data->args);
data->red = data->args[0];
data->green = data->args[1];
data->blue = data->args[2];
break;
case BLM_GET_ADDR:
data->args[0] = data->i2c_addr;
blinkm_write(client, cmd, NULL);
blinkm_read(client, cmd, data->args);
data->i2c_addr = data->args[0];
break;
case BLM_SET_TIME_ADJ:
case BLM_SET_FADE_SPEED:
case BLM_READ_SCRIPT_LINE:
case BLM_WRITE_SCRIPT_LINE:
case BLM_SET_SCRIPT_LR:
case BLM_SET_ADDR:
case BLM_GET_FW_VER:
case BLM_SET_STARTUP_PARAM:
dev_err(&client->dev,
"BlinkM: cmd %d not implemented yet.\n", cmd);
break;
default:
dev_err(&client->dev, "BlinkM: unknown command %d\n", cmd);
mutex_unlock(&data->update_lock);
return -EINVAL;
} /* end switch(cmd) */
/* transfers done, unlock */
mutex_unlock(&data->update_lock);
return 0;
}
static void led_work(struct work_struct *work)
{
int ret;
struct blinkm_led *led;
struct blinkm_data *data ;
struct blinkm_work *blm_work = work_to_blmwork(work);
led = blm_work->blinkm_led;
data = i2c_get_clientdata(led->i2c_client);
ret = blinkm_transfer_hw(led->i2c_client, BLM_GO_RGB);
atomic_dec(&led->active);
dev_dbg(&led->i2c_client->dev,
"# DONE # next_red = %d, next_green = %d,"
" next_blue = %d, active = %d\n",
data->next_red, data->next_green,
data->next_blue, atomic_read(&led->active));
kfree(blm_work);
}
static int blinkm_led_common_set(struct led_classdev *led_cdev,
enum led_brightness value, int color)
{
/* led_brightness is 0, 127 or 255 - we just use it here as-is */
struct blinkm_led *led = cdev_to_blmled(led_cdev);
struct blinkm_data *data = i2c_get_clientdata(led->i2c_client);
struct blinkm_work *bl_work;
switch (color) {
case RED:
/* bail out if there's no change */
if (data->next_red == (u8) value)
return 0;
/* we assume a quite fast sequence here ([off]->on->off)
* think of network led trigger - we cannot blink that fast, so
* in case we already have a off->on->off transition queued up,
* we refuse to queue up more.
* Revisit: fast-changing brightness. */
if (atomic_read(&led->active) > 1)
return 0;
data->next_red = (u8) value;
break;
case GREEN:
/* bail out if there's no change */
if (data->next_green == (u8) value)
return 0;
/* we assume a quite fast sequence here ([off]->on->off)
* Revisit: fast-changing brightness. */
if (atomic_read(&led->active) > 1)
return 0;
data->next_green = (u8) value;
break;
case BLUE:
/* bail out if there's no change */
if (data->next_blue == (u8) value)
return 0;
/* we assume a quite fast sequence here ([off]->on->off)
* Revisit: fast-changing brightness. */
if (atomic_read(&led->active) > 1)
return 0;
data->next_blue = (u8) value;
break;
default:
dev_err(&led->i2c_client->dev, "BlinkM: unknown color.\n");
return -EINVAL;
}
bl_work = kzalloc(sizeof(*bl_work), GFP_ATOMIC);
if (!bl_work)
return -ENOMEM;
atomic_inc(&led->active);
dev_dbg(&led->i2c_client->dev,
"#TO_SCHED# next_red = %d, next_green = %d,"
" next_blue = %d, active = %d\n",
data->next_red, data->next_green,
data->next_blue, atomic_read(&led->active));
/* a fresh work _item_ for each change */
bl_work->blinkm_led = led;
INIT_WORK(&bl_work->work, led_work);
/* queue work in own queue for easy sync on exit*/
schedule_work(&bl_work->work);
return 0;
}
static void blinkm_led_red_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
blinkm_led_common_set(led_cdev, value, RED);
}
static void blinkm_led_green_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
blinkm_led_common_set(led_cdev, value, GREEN);
}
static void blinkm_led_blue_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
blinkm_led_common_set(led_cdev, value, BLUE);
}
static void blinkm_init_hw(struct i2c_client *client)
{
int ret;
ret = blinkm_transfer_hw(client, BLM_STOP_SCRIPT);
ret = blinkm_transfer_hw(client, BLM_GO_RGB);
}
static int blinkm_test_run(struct i2c_client *client)
{
int ret;
struct blinkm_data *data = i2c_get_clientdata(client);
data->next_red = 0x01;
data->next_green = 0x05;
data->next_blue = 0x10;
ret = blinkm_transfer_hw(client, BLM_GO_RGB);
if (ret < 0)
return ret;
msleep(2000);
data->next_red = 0x25;
data->next_green = 0x10;
data->next_blue = 0x31;
ret = blinkm_transfer_hw(client, BLM_FADE_RGB);
if (ret < 0)
return ret;
msleep(2000);
data->next_hue = 0x50;
data->next_saturation = 0x10;
data->next_brightness = 0x20;
ret = blinkm_transfer_hw(client, BLM_FADE_HSB);
if (ret < 0)
return ret;
msleep(2000);
return 0;
}
/* Return 0 if detection is successful, -ENODEV otherwise */
static int blinkm_detect(struct i2c_client *client, struct i2c_board_info *info)
{
struct i2c_adapter *adapter = client->adapter;
int ret;
int count = 99;
u8 tmpargs[7];
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA
| I2C_FUNC_SMBUS_WORD_DATA
| I2C_FUNC_SMBUS_WRITE_BYTE))
return -ENODEV;
/* Now, we do the remaining detection. Simple for now. */
/* We might need more guards to protect other i2c slaves */
/* make sure the blinkM is balanced (read/writes) */
while (count > 0) {
ret = blinkm_write(client, BLM_GET_ADDR, NULL);
usleep_range(5000, 10000);
ret = blinkm_read(client, BLM_GET_ADDR, tmpargs);
usleep_range(5000, 10000);
if (tmpargs[0] == 0x09)
count = 0;
count--;
}
/* Step 1: Read BlinkM address back - cmd_char 'a' */
ret = blinkm_write(client, BLM_GET_ADDR, NULL);
if (ret < 0)
return ret;
usleep_range(20000, 30000); /* allow a small delay */
ret = blinkm_read(client, BLM_GET_ADDR, tmpargs);
if (ret < 0)
return ret;
if (tmpargs[0] != 0x09) {
dev_err(&client->dev, "enodev DEV ADDR = 0x%02X\n", tmpargs[0]);
return -ENODEV;
}
strlcpy(info->type, "blinkm", I2C_NAME_SIZE);
return 0;
}
static int __devinit blinkm_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct blinkm_data *data;
struct blinkm_led *led[3];
int err, i;
char blinkm_led_name[28];
data = devm_kzalloc(&client->dev,
sizeof(struct blinkm_data), GFP_KERNEL);
if (!data) {
err = -ENOMEM;
goto exit;
}
data->i2c_addr = 0x09;
data->i2c_addr = 0x08;
/* i2c addr - use fake addr of 0x08 initially (real is 0x09) */
data->fw_ver = 0xfe;
/* firmware version - use fake until we read real value
* (currently broken - BlinkM confused!) */
data->script_id = 0x01;
data->i2c_client = client;
i2c_set_clientdata(client, data);
mutex_init(&data->update_lock);
/* Register sysfs hooks */
err = sysfs_create_group(&client->dev.kobj, &blinkm_group);
if (err < 0) {
dev_err(&client->dev, "couldn't register sysfs group\n");
goto exit;
}
for (i = 0; i < 3; i++) {
/* RED = 0, GREEN = 1, BLUE = 2 */
led[i] = &data->blinkm_leds[i];
led[i]->i2c_client = client;
led[i]->id = i;
led[i]->led_cdev.max_brightness = 255;
led[i]->led_cdev.flags = LED_CORE_SUSPENDRESUME;
atomic_set(&led[i]->active, 0);
switch (i) {
case RED:
snprintf(blinkm_led_name, sizeof(blinkm_led_name),
"blinkm-%d-%d-red",
client->adapter->nr,
client->addr);
led[i]->led_cdev.name = blinkm_led_name;
led[i]->led_cdev.brightness_set = blinkm_led_red_set;
err = led_classdev_register(&client->dev,
&led[i]->led_cdev);
if (err < 0) {
dev_err(&client->dev,
"couldn't register LED %s\n",
led[i]->led_cdev.name);
goto failred;
}
break;
case GREEN:
snprintf(blinkm_led_name, sizeof(blinkm_led_name),
"blinkm-%d-%d-green",
client->adapter->nr,
client->addr);
led[i]->led_cdev.name = blinkm_led_name;
led[i]->led_cdev.brightness_set = blinkm_led_green_set;
err = led_classdev_register(&client->dev,
&led[i]->led_cdev);
if (err < 0) {
dev_err(&client->dev,
"couldn't register LED %s\n",
led[i]->led_cdev.name);
goto failgreen;
}
break;
case BLUE:
snprintf(blinkm_led_name, sizeof(blinkm_led_name),
"blinkm-%d-%d-blue",
client->adapter->nr,
client->addr);
led[i]->led_cdev.name = blinkm_led_name;
led[i]->led_cdev.brightness_set = blinkm_led_blue_set;
err = led_classdev_register(&client->dev,
&led[i]->led_cdev);
if (err < 0) {
dev_err(&client->dev,
"couldn't register LED %s\n",
led[i]->led_cdev.name);
goto failblue;
}
break;
} /* end switch */
} /* end for */
/* Initialize the blinkm */
blinkm_init_hw(client);
return 0;
failblue:
led_classdev_unregister(&led[GREEN]->led_cdev);
failgreen:
led_classdev_unregister(&led[RED]->led_cdev);
failred:
sysfs_remove_group(&client->dev.kobj, &blinkm_group);
exit:
return err;
}
static int __devexit blinkm_remove(struct i2c_client *client)
{
struct blinkm_data *data = i2c_get_clientdata(client);
int ret = 0;
int i;
/* make sure no workqueue entries are pending */
for (i = 0; i < 3; i++) {
flush_scheduled_work();
led_classdev_unregister(&data->blinkm_leds[i].led_cdev);
}
/* reset rgb */
data->next_red = 0x00;
data->next_green = 0x00;
data->next_blue = 0x00;
ret = blinkm_transfer_hw(client, BLM_FADE_RGB);
if (ret < 0)
dev_err(&client->dev, "Failure in blinkm_remove ignored. Continuing.\n");
/* reset hsb */
data->next_hue = 0x00;
data->next_saturation = 0x00;
data->next_brightness = 0x00;
ret = blinkm_transfer_hw(client, BLM_FADE_HSB);
if (ret < 0)
dev_err(&client->dev, "Failure in blinkm_remove ignored. Continuing.\n");
/* red fade to off */
data->next_red = 0xff;
ret = blinkm_transfer_hw(client, BLM_GO_RGB);
if (ret < 0)
dev_err(&client->dev, "Failure in blinkm_remove ignored. Continuing.\n");
/* off */
data->next_red = 0x00;
ret = blinkm_transfer_hw(client, BLM_FADE_RGB);
if (ret < 0)
dev_err(&client->dev, "Failure in blinkm_remove ignored. Continuing.\n");
sysfs_remove_group(&client->dev.kobj, &blinkm_group);
return 0;
}
static const struct i2c_device_id blinkm_id[] = {
{"blinkm", 0},
{}
};
MODULE_DEVICE_TABLE(i2c, blinkm_id);
/* This is the driver that will be inserted */
static struct i2c_driver blinkm_driver = {
.class = I2C_CLASS_HWMON,
.driver = {
.name = "blinkm",
},
.probe = blinkm_probe,
.remove = __devexit_p(blinkm_remove),
.id_table = blinkm_id,
.detect = blinkm_detect,
.address_list = normal_i2c,
};
module_i2c_driver(blinkm_driver);
MODULE_AUTHOR("Jan-Simon Moeller <dl9pf@gmx.de>");
MODULE_DESCRIPTION("BlinkM RGB LED driver");
MODULE_LICENSE("GPL");

View File

@ -108,7 +108,7 @@ static int __devinit da903x_led_probe(struct platform_device *pdev)
return -EINVAL;
}
led = kzalloc(sizeof(struct da903x_led), GFP_KERNEL);
led = devm_kzalloc(&pdev->dev, sizeof(struct da903x_led), GFP_KERNEL);
if (led == NULL) {
dev_err(&pdev->dev, "failed to alloc memory for LED%d\n", id);
return -ENOMEM;
@ -129,15 +129,11 @@ static int __devinit da903x_led_probe(struct platform_device *pdev)
ret = led_classdev_register(led->master, &led->cdev);
if (ret) {
dev_err(&pdev->dev, "failed to register LED %d\n", id);
goto err;
return ret;
}
platform_set_drvdata(pdev, led);
return 0;
err:
kfree(led);
return ret;
}
static int __devexit da903x_led_remove(struct platform_device *pdev)
@ -145,7 +141,6 @@ static int __devexit da903x_led_remove(struct platform_device *pdev)
struct da903x_led *led = platform_get_drvdata(pdev);
led_classdev_unregister(&led->cdev);
kfree(led);
return 0;
}

View File

@ -69,7 +69,7 @@ static int dac124s085_probe(struct spi_device *spi)
struct dac124s085_led *led;
int i, ret;
dac = kzalloc(sizeof(*dac), GFP_KERNEL);
dac = devm_kzalloc(&spi->dev, sizeof(*dac), GFP_KERNEL);
if (!dac)
return -ENOMEM;
@ -102,7 +102,6 @@ eledcr:
led_classdev_unregister(&dac->leds[i].ldev);
spi_set_drvdata(spi, NULL);
kfree(dac);
return ret;
}
@ -117,7 +116,6 @@ static int dac124s085_remove(struct spi_device *spi)
}
spi_set_drvdata(spi, NULL);
kfree(dac);
return 0;
}

View File

@ -178,7 +178,8 @@ static struct gpio_leds_priv * __devinit gpio_leds_create_of(struct platform_dev
if (!count)
return NULL;
priv = kzalloc(sizeof_gpio_leds_priv(count), GFP_KERNEL);
priv = devm_kzalloc(&pdev->dev, sizeof_gpio_leds_priv(count),
GFP_KERNEL);
if (!priv)
return NULL;
@ -215,7 +216,6 @@ static struct gpio_leds_priv * __devinit gpio_leds_create_of(struct platform_dev
err:
for (count = priv->num_leds - 2; count >= 0; count--)
delete_gpio_led(&priv->leds[count]);
kfree(priv);
return NULL;
}
@ -239,8 +239,9 @@ static int __devinit gpio_led_probe(struct platform_device *pdev)
int i, ret = 0;
if (pdata && pdata->num_leds) {
priv = kzalloc(sizeof_gpio_leds_priv(pdata->num_leds),
GFP_KERNEL);
priv = devm_kzalloc(&pdev->dev,
sizeof_gpio_leds_priv(pdata->num_leds),
GFP_KERNEL);
if (!priv)
return -ENOMEM;
@ -253,7 +254,6 @@ static int __devinit gpio_led_probe(struct platform_device *pdev)
/* On failure: unwind the led creations */
for (i = i - 1; i >= 0; i--)
delete_gpio_led(&priv->leds[i]);
kfree(priv);
return ret;
}
}
@ -277,7 +277,6 @@ static int __devexit gpio_led_remove(struct platform_device *pdev)
delete_gpio_led(&priv->leds[i]);
dev_set_drvdata(&pdev->dev, NULL);
kfree(priv);
return 0;
}

View File

@ -386,28 +386,24 @@ static int __devinit lm3530_probe(struct i2c_client *client,
if (pdata == NULL) {
dev_err(&client->dev, "platform data required\n");
err = -ENODEV;
goto err_out;
return -ENODEV;
}
/* BL mode */
if (pdata->mode > LM3530_BL_MODE_PWM) {
dev_err(&client->dev, "Illegal Mode request\n");
err = -EINVAL;
goto err_out;
return -EINVAL;
}
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
dev_err(&client->dev, "I2C_FUNC_I2C not supported\n");
err = -EIO;
goto err_out;
return -EIO;
}
drvdata = kzalloc(sizeof(struct lm3530_data), GFP_KERNEL);
if (drvdata == NULL) {
err = -ENOMEM;
goto err_out;
}
drvdata = devm_kzalloc(&client->dev, sizeof(struct lm3530_data),
GFP_KERNEL);
if (drvdata == NULL)
return -ENOMEM;
drvdata->mode = pdata->mode;
drvdata->client = client;
@ -425,7 +421,7 @@ static int __devinit lm3530_probe(struct i2c_client *client,
dev_err(&client->dev, "regulator get failed\n");
err = PTR_ERR(drvdata->regulator);
drvdata->regulator = NULL;
goto err_regulator_get;
return err;
}
if (drvdata->pdata->brt_val) {
@ -458,9 +454,6 @@ err_create_file:
err_class_register:
err_reg_init:
regulator_put(drvdata->regulator);
err_regulator_get:
kfree(drvdata);
err_out:
return err;
}
@ -474,7 +467,6 @@ static int __devexit lm3530_remove(struct i2c_client *client)
regulator_disable(drvdata->regulator);
regulator_put(drvdata->regulator);
led_classdev_unregister(&drvdata->led_dev);
kfree(drvdata);
return 0;
}

512
drivers/leds/leds-lm3556.c Normal file
View File

@ -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 <linux/module.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/leds.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/fs.h>
#include <linux/regmap.h>
#include <linux/platform_data/leds-lm3556.h>
#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, &reg_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, &reg_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 <daniel.jeong@ti.com>");
MODULE_AUTHOR("G.Shark Jeong <gshark.jeong@gmail.com>");
MODULE_LICENSE("GPL v2");

View File

@ -393,7 +393,8 @@ static int __devinit lp3944_probe(struct i2c_client *client,
return -ENODEV;
}
data = kzalloc(sizeof(struct lp3944_data), GFP_KERNEL);
data = devm_kzalloc(&client->dev, sizeof(struct lp3944_data),
GFP_KERNEL);
if (!data)
return -ENOMEM;
@ -403,10 +404,8 @@ static int __devinit lp3944_probe(struct i2c_client *client,
mutex_init(&data->lock);
err = lp3944_configure(client, data, lp3944_pdata);
if (err < 0) {
kfree(data);
if (err < 0)
return err;
}
dev_info(&client->dev, "lp3944 enabled\n");
return 0;
@ -431,8 +430,6 @@ static int __devexit lp3944_remove(struct i2c_client *client)
break;
}
kfree(data);
return 0;
}

View File

@ -744,7 +744,7 @@ static int __devinit lp5521_probe(struct i2c_client *client,
int ret, i, led;
u8 buf;
chip = kzalloc(sizeof(*chip), GFP_KERNEL);
chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
if (!chip)
return -ENOMEM;
@ -755,8 +755,7 @@ static int __devinit lp5521_probe(struct i2c_client *client,
if (!pdata) {
dev_err(&client->dev, "no platform data\n");
ret = -EINVAL;
goto fail1;
return -EINVAL;
}
mutex_init(&chip->lock);
@ -766,7 +765,7 @@ static int __devinit lp5521_probe(struct i2c_client *client,
if (pdata->setup_resources) {
ret = pdata->setup_resources();
if (ret < 0)
goto fail1;
return ret;
}
if (pdata->enable) {
@ -807,7 +806,7 @@ static int __devinit lp5521_probe(struct i2c_client *client,
ret = lp5521_configure(client);
if (ret < 0) {
dev_err(&client->dev, "error configuring chip\n");
goto fail2;
goto fail1;
}
/* Initialize leds */
@ -822,7 +821,7 @@ static int __devinit lp5521_probe(struct i2c_client *client,
ret = lp5521_init_led(&chip->leds[led], client, i, pdata);
if (ret) {
dev_err(&client->dev, "error initializing leds\n");
goto fail3;
goto fail2;
}
chip->num_leds++;
@ -840,21 +839,19 @@ static int __devinit lp5521_probe(struct i2c_client *client,
ret = lp5521_register_sysfs(client);
if (ret) {
dev_err(&client->dev, "registering sysfs failed\n");
goto fail3;
goto fail2;
}
return ret;
fail3:
fail2:
for (i = 0; i < chip->num_leds; i++) {
led_classdev_unregister(&chip->leds[i].cdev);
cancel_work_sync(&chip->leds[i].brightness_work);
}
fail2:
fail1:
if (pdata->enable)
pdata->enable(0);
if (pdata->release_resources)
pdata->release_resources();
fail1:
kfree(chip);
return ret;
}
@ -875,7 +872,6 @@ static int __devexit lp5521_remove(struct i2c_client *client)
chip->pdata->enable(0);
if (chip->pdata->release_resources)
chip->pdata->release_resources();
kfree(chip);
return 0;
}

View File

@ -877,7 +877,7 @@ static int __devinit lp5523_probe(struct i2c_client *client,
struct lp5523_platform_data *pdata;
int ret, i, led;
chip = kzalloc(sizeof(*chip), GFP_KERNEL);
chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
if (!chip)
return -ENOMEM;
@ -888,8 +888,7 @@ static int __devinit lp5523_probe(struct i2c_client *client,
if (!pdata) {
dev_err(&client->dev, "no platform data\n");
ret = -EINVAL;
goto fail1;
return -EINVAL;
}
mutex_init(&chip->lock);
@ -899,7 +898,7 @@ static int __devinit lp5523_probe(struct i2c_client *client,
if (pdata->setup_resources) {
ret = pdata->setup_resources();
if (ret < 0)
goto fail1;
return ret;
}
if (pdata->enable) {
@ -916,7 +915,7 @@ static int __devinit lp5523_probe(struct i2c_client *client,
*/
ret = lp5523_detect(client);
if (ret)
goto fail2;
goto fail1;
dev_info(&client->dev, "LP5523 Programmable led chip found\n");
@ -925,13 +924,13 @@ static int __devinit lp5523_probe(struct i2c_client *client,
ret = lp5523_init_engine(&chip->engines[i], i + 1);
if (ret) {
dev_err(&client->dev, "error initializing engine\n");
goto fail2;
goto fail1;
}
}
ret = lp5523_configure(client);
if (ret < 0) {
dev_err(&client->dev, "error configuring chip\n");
goto fail2;
goto fail1;
}
/* Initialize leds */
@ -943,10 +942,13 @@ static int __devinit lp5523_probe(struct i2c_client *client,
if (pdata->led_config[i].led_current == 0)
continue;
INIT_WORK(&chip->leds[led].brightness_work,
lp5523_led_brightness_work);
ret = lp5523_init_led(&chip->leds[led], &client->dev, i, pdata);
if (ret) {
dev_err(&client->dev, "error initializing leds\n");
goto fail3;
goto fail2;
}
chip->num_leds++;
@ -956,30 +958,25 @@ static int __devinit lp5523_probe(struct i2c_client *client,
LP5523_REG_LED_CURRENT_BASE + chip->leds[led].chan_nr,
chip->leds[led].led_current);
INIT_WORK(&(chip->leds[led].brightness_work),
lp5523_led_brightness_work);
led++;
}
ret = lp5523_register_sysfs(client);
if (ret) {
dev_err(&client->dev, "registering sysfs failed\n");
goto fail3;
goto fail2;
}
return ret;
fail3:
fail2:
for (i = 0; i < chip->num_leds; i++) {
led_classdev_unregister(&chip->leds[i].cdev);
cancel_work_sync(&chip->leds[i].brightness_work);
}
fail2:
fail1:
if (pdata->enable)
pdata->enable(0);
if (pdata->release_resources)
pdata->release_resources();
fail1:
kfree(chip);
return ret;
}
@ -999,7 +996,6 @@ static int lp5523_remove(struct i2c_client *client)
chip->pdata->enable(0);
if (chip->pdata->release_resources)
chip->pdata->release_resources();
kfree(chip);
return 0;
}

193
drivers/leds/leds-lp8788.c Normal file
View File

@ -0,0 +1,193 @@
/*
* TI LP8788 MFD - keyled driver
*
* Copyright 2012 Texas Instruments
*
* Author: Milo(Woogyom) Kim <milo.kim@ti.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/platform_device.h>
#include <linux/leds.h>
#include <linux/mutex.h>
#include <linux/mfd/lp8788.h>
#include <linux/mfd/lp8788-isink.h>
#define MAX_BRIGHTNESS LP8788_ISINK_MAX_PWM
#define DEFAULT_LED_NAME "keyboard-backlight"
struct lp8788_led {
struct lp8788 *lp;
struct mutex lock;
struct work_struct work;
struct led_classdev led_dev;
enum lp8788_isink_number isink_num;
enum led_brightness brightness;
int on;
};
struct lp8788_led_config {
enum lp8788_isink_scale scale;
enum lp8788_isink_number num;
int iout;
};
static struct lp8788_led_config default_led_config = {
.scale = LP8788_ISINK_SCALE_100mA,
.num = LP8788_ISINK_3,
.iout = 0,
};
static int lp8788_led_init_device(struct lp8788_led *led,
struct lp8788_led_platform_data *pdata)
{
struct lp8788_led_config *cfg = &default_led_config;
u8 addr, mask, val;
int ret;
if (pdata) {
cfg->scale = pdata->scale;
cfg->num = pdata->num;
cfg->iout = pdata->iout_code;
}
led->isink_num = cfg->num;
/* scale configuration */
addr = LP8788_ISINK_CTRL;
mask = 1 << (cfg->num + LP8788_ISINK_SCALE_OFFSET);
val = cfg->scale << cfg->num;
ret = lp8788_update_bits(led->lp, addr, mask, val);
if (ret)
return ret;
/* current configuration */
addr = lp8788_iout_addr[cfg->num];
mask = lp8788_iout_mask[cfg->num];
val = cfg->iout;
return lp8788_update_bits(led->lp, addr, mask, val);
}
static void lp8788_led_enable(struct lp8788_led *led,
enum lp8788_isink_number num, int on)
{
u8 mask = 1 << num;
u8 val = on << num;
if (lp8788_update_bits(led->lp, LP8788_ISINK_CTRL, mask, val))
return;
led->on = on;
}
static void lp8788_led_work(struct work_struct *work)
{
struct lp8788_led *led = container_of(work, struct lp8788_led, work);
enum lp8788_isink_number num = led->isink_num;
int enable;
u8 val = led->brightness;
mutex_lock(&led->lock);
switch (num) {
case LP8788_ISINK_1:
case LP8788_ISINK_2:
case LP8788_ISINK_3:
lp8788_write_byte(led->lp, lp8788_pwm_addr[num], val);
break;
default:
mutex_unlock(&led->lock);
return;
}
enable = (val > 0) ? 1 : 0;
if (enable != led->on)
lp8788_led_enable(led, num, enable);
mutex_unlock(&led->lock);
}
static void lp8788_brightness_set(struct led_classdev *led_cdev,
enum led_brightness brt_val)
{
struct lp8788_led *led =
container_of(led_cdev, struct lp8788_led, led_dev);
led->brightness = brt_val;
schedule_work(&led->work);
}
static __devinit int lp8788_led_probe(struct platform_device *pdev)
{
struct lp8788 *lp = dev_get_drvdata(pdev->dev.parent);
struct lp8788_led_platform_data *led_pdata;
struct lp8788_led *led;
int ret;
led = devm_kzalloc(lp->dev, sizeof(struct lp8788_led), GFP_KERNEL);
if (!led)
return -ENOMEM;
led->lp = lp;
led->led_dev.max_brightness = MAX_BRIGHTNESS;
led->led_dev.brightness_set = lp8788_brightness_set;
led_pdata = lp->pdata ? lp->pdata->led_pdata : NULL;
if (!led_pdata || !led_pdata->name)
led->led_dev.name = DEFAULT_LED_NAME;
else
led->led_dev.name = led_pdata->name;
mutex_init(&led->lock);
INIT_WORK(&led->work, lp8788_led_work);
platform_set_drvdata(pdev, led);
ret = lp8788_led_init_device(led, led_pdata);
if (ret) {
dev_err(lp->dev, "led init device err: %d\n", ret);
return ret;
}
ret = led_classdev_register(lp->dev, &led->led_dev);
if (ret) {
dev_err(lp->dev, "led register err: %d\n", ret);
return ret;
}
return 0;
}
static int __devexit lp8788_led_remove(struct platform_device *pdev)
{
struct lp8788_led *led = platform_get_drvdata(pdev);
led_classdev_unregister(&led->led_dev);
flush_work_sync(&led->work);
return 0;
}
static struct platform_driver lp8788_led_driver = {
.probe = lp8788_led_probe,
.remove = __devexit_p(lp8788_led_remove),
.driver = {
.name = LP8788_DEV_KEYLED,
.owner = THIS_MODULE,
},
};
module_platform_driver(lp8788_led_driver);
MODULE_DESCRIPTION("Texas Instruments LP8788 Keyboard LED Driver");
MODULE_AUTHOR("Milo Kim");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:lp8788-keyled");

View File

@ -149,8 +149,9 @@ static int __devinit lt3593_led_probe(struct platform_device *pdev)
if (!pdata)
return -EBUSY;
leds_data = kzalloc(sizeof(struct lt3593_led_data) * pdata->num_leds,
GFP_KERNEL);
leds_data = devm_kzalloc(&pdev->dev,
sizeof(struct lt3593_led_data) * pdata->num_leds,
GFP_KERNEL);
if (!leds_data)
return -ENOMEM;
@ -169,8 +170,6 @@ err:
for (i = i - 1; i >= 0; i--)
delete_lt3593_led(&leds_data[i]);
kfree(leds_data);
return ret;
}
@ -185,8 +184,6 @@ static int __devexit lt3593_led_remove(struct platform_device *pdev)
for (i = 0; i < pdata->num_leds; i++)
delete_lt3593_led(&leds_data[i]);
kfree(leds_data);
return 0;
}

View File

@ -49,71 +49,37 @@ struct max8997_led {
struct mutex mutex;
};
static void max8997_led_clear_mode(struct max8997_led *led,
enum max8997_led_mode mode)
{
struct i2c_client *client = led->iodev->i2c;
u8 val = 0, mask = 0;
int ret;
switch (mode) {
case MAX8997_FLASH_MODE:
mask = led->id ?
MAX8997_LED1_FLASH_MASK : MAX8997_LED0_FLASH_MASK;
break;
case MAX8997_MOVIE_MODE:
mask = led->id ?
MAX8997_LED1_MOVIE_MASK : MAX8997_LED0_MOVIE_MASK;
break;
case MAX8997_FLASH_PIN_CONTROL_MODE:
mask = led->id ?
MAX8997_LED1_FLASH_PIN_MASK : MAX8997_LED0_FLASH_PIN_MASK;
break;
case MAX8997_MOVIE_PIN_CONTROL_MODE:
mask = led->id ?
MAX8997_LED1_MOVIE_PIN_MASK : MAX8997_LED0_MOVIE_PIN_MASK;
break;
default:
break;
}
if (mask) {
ret = max8997_update_reg(client,
MAX8997_REG_LEN_CNTL, val, mask);
if (ret)
dev_err(led->iodev->dev,
"failed to update register(%d)\n", ret);
}
}
static void max8997_led_set_mode(struct max8997_led *led,
enum max8997_led_mode mode)
{
int ret;
struct i2c_client *client = led->iodev->i2c;
u8 mask = 0;
/* First, clear the previous mode */
max8997_led_clear_mode(led, led->led_mode);
u8 mask = 0, val;
switch (mode) {
case MAX8997_FLASH_MODE:
mask = led->id ?
mask = MAX8997_LED1_FLASH_MASK | MAX8997_LED0_FLASH_MASK;
val = led->id ?
MAX8997_LED1_FLASH_MASK : MAX8997_LED0_FLASH_MASK;
led->cdev.max_brightness = MAX8997_LED_FLASH_MAX_BRIGHTNESS;
break;
case MAX8997_MOVIE_MODE:
mask = led->id ?
mask = MAX8997_LED1_MOVIE_MASK | MAX8997_LED0_MOVIE_MASK;
val = led->id ?
MAX8997_LED1_MOVIE_MASK : MAX8997_LED0_MOVIE_MASK;
led->cdev.max_brightness = MAX8997_LED_MOVIE_MAX_BRIGHTNESS;
break;
case MAX8997_FLASH_PIN_CONTROL_MODE:
mask = led->id ?
mask = MAX8997_LED1_FLASH_PIN_MASK |
MAX8997_LED0_FLASH_PIN_MASK;
val = led->id ?
MAX8997_LED1_FLASH_PIN_MASK : MAX8997_LED0_FLASH_PIN_MASK;
led->cdev.max_brightness = MAX8997_LED_FLASH_MAX_BRIGHTNESS;
break;
case MAX8997_MOVIE_PIN_CONTROL_MODE:
mask = led->id ?
mask = MAX8997_LED1_MOVIE_PIN_MASK |
MAX8997_LED0_MOVIE_PIN_MASK;
val = led->id ?
MAX8997_LED1_MOVIE_PIN_MASK : MAX8997_LED0_MOVIE_PIN_MASK;
led->cdev.max_brightness = MAX8997_LED_MOVIE_MAX_BRIGHTNESS;
break;
@ -123,8 +89,8 @@ static void max8997_led_set_mode(struct max8997_led *led,
}
if (mask) {
ret = max8997_update_reg(client,
MAX8997_REG_LEN_CNTL, mask, mask);
ret = max8997_update_reg(client, MAX8997_REG_LEN_CNTL, val,
mask);
if (ret)
dev_err(led->iodev->dev,
"failed to update register(%d)\n", ret);
@ -276,11 +242,9 @@ static int __devinit max8997_led_probe(struct platform_device *pdev)
return -ENODEV;
}
led = kzalloc(sizeof(*led), GFP_KERNEL);
if (led == NULL) {
ret = -ENOMEM;
goto err_mem;
}
led = devm_kzalloc(&pdev->dev, sizeof(*led), GFP_KERNEL);
if (led == NULL)
return -ENOMEM;
led->id = pdev->id;
snprintf(name, sizeof(name), "max8997-led%d", pdev->id);
@ -315,23 +279,17 @@ static int __devinit max8997_led_probe(struct platform_device *pdev)
ret = led_classdev_register(&pdev->dev, &led->cdev);
if (ret < 0)
goto err_led;
return ret;
ret = device_create_file(led->cdev.dev, &dev_attr_mode);
if (ret != 0) {
dev_err(&pdev->dev,
"failed to create file: %d\n", ret);
goto err_file;
led_classdev_unregister(&led->cdev);
return ret;
}
return 0;
err_file:
led_classdev_unregister(&led->cdev);
err_led:
kfree(led);
err_mem:
return ret;
}
static int __devexit max8997_led_remove(struct platform_device *pdev)
@ -340,7 +298,6 @@ static int __devexit max8997_led_remove(struct platform_device *pdev)
device_remove_file(led->cdev.dev, &dev_attr_mode);
led_classdev_unregister(&led->cdev);
kfree(led);
return 0;
}
@ -354,17 +311,7 @@ static struct platform_driver max8997_led_driver = {
.remove = __devexit_p(max8997_led_remove),
};
static int __init max8997_led_init(void)
{
return platform_driver_register(&max8997_led_driver);
}
module_init(max8997_led_init);
static void __exit max8997_led_exit(void)
{
platform_driver_unregister(&max8997_led_driver);
}
module_exit(max8997_led_exit);
module_platform_driver(max8997_led_driver);
MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>");
MODULE_DESCRIPTION("MAX8997 LED driver");

View File

@ -280,7 +280,8 @@ static int __devinit mc13783_led_probe(struct platform_device *pdev)
return -EINVAL;
}
led = kcalloc(pdata->num_leds, sizeof(*led), GFP_KERNEL);
led = devm_kzalloc(&pdev->dev, pdata->num_leds * sizeof(*led),
GFP_KERNEL);
if (led == NULL) {
dev_err(&pdev->dev, "failed to alloc memory\n");
return -ENOMEM;
@ -289,7 +290,7 @@ static int __devinit mc13783_led_probe(struct platform_device *pdev)
ret = mc13783_leds_prepare(pdev);
if (ret) {
dev_err(&pdev->dev, "unable to init led driver\n");
goto err_free;
return ret;
}
for (i = 0; i < pdata->num_leds; i++) {
@ -344,8 +345,6 @@ err_register:
cancel_work_sync(&led[i].work);
}
err_free:
kfree(led);
return ret;
}
@ -372,7 +371,7 @@ static int __devexit mc13783_led_remove(struct platform_device *pdev)
mc13xxx_unlock(dev);
kfree(led);
platform_set_drvdata(pdev, NULL);
return 0;
}

View File

@ -362,14 +362,14 @@ static int __devinit netxbig_led_probe(struct platform_device *pdev)
if (!pdata)
return -EINVAL;
leds_data = kzalloc(sizeof(struct netxbig_led_data) * pdata->num_leds,
GFP_KERNEL);
leds_data = devm_kzalloc(&pdev->dev,
sizeof(struct netxbig_led_data) * pdata->num_leds, GFP_KERNEL);
if (!leds_data)
return -ENOMEM;
ret = gpio_ext_init(pdata->gpio_ext);
if (ret < 0)
goto err_free_data;
return ret;
for (i = 0; i < pdata->num_leds; i++) {
ret = create_netxbig_led(pdev, &leds_data[i], &pdata->leds[i]);
@ -386,9 +386,6 @@ err_free_leds:
delete_netxbig_led(&leds_data[i]);
gpio_ext_free(pdata->gpio_ext);
err_free_data:
kfree(leds_data);
return ret;
}
@ -404,7 +401,6 @@ static int __devexit netxbig_led_remove(struct platform_device *pdev)
delete_netxbig_led(&leds_data[i]);
gpio_ext_free(pdata->gpio_ext);
kfree(leds_data);
return 0;
}

View File

@ -273,29 +273,23 @@ static int __devinit ns2_led_probe(struct platform_device *pdev)
if (!pdata)
return -EINVAL;
leds_data = kzalloc(sizeof(struct ns2_led_data) *
leds_data = devm_kzalloc(&pdev->dev, sizeof(struct ns2_led_data) *
pdata->num_leds, GFP_KERNEL);
if (!leds_data)
return -ENOMEM;
for (i = 0; i < pdata->num_leds; i++) {
ret = create_ns2_led(pdev, &leds_data[i], &pdata->leds[i]);
if (ret < 0)
goto err;
if (ret < 0) {
for (i = i - 1; i >= 0; i--)
delete_ns2_led(&leds_data[i]);
return ret;
}
}
platform_set_drvdata(pdev, leds_data);
return 0;
err:
for (i = i - 1; i >= 0; i--)
delete_ns2_led(&leds_data[i]);
kfree(leds_data);
return ret;
}
static int __devexit ns2_led_remove(struct platform_device *pdev)
@ -309,7 +303,6 @@ static int __devexit ns2_led_remove(struct platform_device *pdev)
for (i = 0; i < pdata->num_leds; i++)
delete_ns2_led(&leds_data[i]);
kfree(leds_data);
platform_set_drvdata(pdev, NULL);
return 0;

View File

@ -449,7 +449,6 @@ static int pca9532_probe(struct i2c_client *client,
{
struct pca9532_data *data = i2c_get_clientdata(client);
struct pca9532_platform_data *pca9532_pdata = client->dev.platform_data;
int err;
if (!pca9532_pdata)
return -EIO;
@ -458,7 +457,7 @@ static int pca9532_probe(struct i2c_client *client,
I2C_FUNC_SMBUS_BYTE_DATA))
return -EIO;
data = kzalloc(sizeof(*data), GFP_KERNEL);
data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
@ -469,11 +468,7 @@ static int pca9532_probe(struct i2c_client *client,
data->client = client;
mutex_init(&data->update_lock);
err = pca9532_configure(client, data, pca9532_pdata);
if (err)
kfree(data);
return err;
return pca9532_configure(client, data, pca9532_pdata);
}
static int pca9532_remove(struct i2c_client *client)
@ -485,7 +480,6 @@ static int pca9532_remove(struct i2c_client *client)
if (err)
return err;
kfree(data);
return 0;
}

View File

@ -293,15 +293,14 @@ static int __devinit pca955x_probe(struct i2c_client *client,
}
}
pca955x = kzalloc(sizeof(*pca955x), GFP_KERNEL);
pca955x = devm_kzalloc(&client->dev, sizeof(*pca955x), GFP_KERNEL);
if (!pca955x)
return -ENOMEM;
pca955x->leds = kzalloc(sizeof(*pca955x_led) * chip->bits, GFP_KERNEL);
if (!pca955x->leds) {
err = -ENOMEM;
goto exit_nomem;
}
pca955x->leds = devm_kzalloc(&client->dev,
sizeof(*pca955x_led) * chip->bits, GFP_KERNEL);
if (!pca955x->leds)
return -ENOMEM;
i2c_set_clientdata(client, pca955x);
@ -361,10 +360,6 @@ exit:
cancel_work_sync(&pca955x->leds[i].work);
}
kfree(pca955x->leds);
exit_nomem:
kfree(pca955x);
return err;
}
@ -378,9 +373,6 @@ static int __devexit pca955x_remove(struct i2c_client *client)
cancel_work_sync(&pca955x->leds[i].work);
}
kfree(pca955x->leds);
kfree(pca955x);
return 0;
}

View File

@ -108,7 +108,7 @@ static int __devinit pca9633_probe(struct i2c_client *client,
}
}
pca9633 = kcalloc(4, sizeof(*pca9633), GFP_KERNEL);
pca9633 = devm_kzalloc(&client->dev, 4 * sizeof(*pca9633), GFP_KERNEL);
if (!pca9633)
return -ENOMEM;
@ -156,8 +156,6 @@ exit:
cancel_work_sync(&pca9633[i].work);
}
kfree(pca9633);
return err;
}
@ -171,8 +169,6 @@ static int __devexit pca9633_remove(struct i2c_client *client)
cancel_work_sync(&pca9633[i].work);
}
kfree(pca9633);
return 0;
}

View File

@ -57,7 +57,8 @@ static int led_pwm_probe(struct platform_device *pdev)
if (!pdata)
return -EBUSY;
leds_data = kzalloc(sizeof(struct led_pwm_data) * pdata->num_leds,
leds_data = devm_kzalloc(&pdev->dev,
sizeof(struct led_pwm_data) * pdata->num_leds,
GFP_KERNEL);
if (!leds_data)
return -ENOMEM;
@ -103,8 +104,6 @@ err:
}
}
kfree(leds_data);
return ret;
}
@ -121,8 +120,6 @@ static int __devexit led_pwm_remove(struct platform_device *pdev)
pwm_free(leds_data[i].pwm);
}
kfree(leds_data);
return 0;
}

View File

@ -158,7 +158,7 @@ static int __devinit regulator_led_probe(struct platform_device *pdev)
return PTR_ERR(vcc);
}
led = kzalloc(sizeof(*led), GFP_KERNEL);
led = devm_kzalloc(&pdev->dev, sizeof(*led), GFP_KERNEL);
if (led == NULL) {
ret = -ENOMEM;
goto err_vcc;
@ -169,7 +169,7 @@ static int __devinit regulator_led_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "Invalid default brightness %d\n",
pdata->brightness);
ret = -EINVAL;
goto err_led;
goto err_vcc;
}
led->value = pdata->brightness;
@ -190,7 +190,7 @@ static int __devinit regulator_led_probe(struct platform_device *pdev)
ret = led_classdev_register(&pdev->dev, &led->cdev);
if (ret < 0) {
cancel_work_sync(&led->work);
goto err_led;
goto err_vcc;
}
/* to expose the default value to userspace */
@ -201,8 +201,6 @@ static int __devinit regulator_led_probe(struct platform_device *pdev)
return 0;
err_led:
kfree(led);
err_vcc:
regulator_put(vcc);
return ret;
@ -216,7 +214,6 @@ static int __devexit regulator_led_remove(struct platform_device *pdev)
cancel_work_sync(&led->work);
regulator_led_disable(led);
regulator_put(led->vcc);
kfree(led);
return 0;
}

View File

@ -243,31 +243,30 @@ static int __devinit r_tpu_probe(struct platform_device *pdev)
struct led_renesas_tpu_config *cfg = pdev->dev.platform_data;
struct r_tpu_priv *p;
struct resource *res;
int ret = -ENXIO;
int ret;
if (!cfg) {
dev_err(&pdev->dev, "missing platform data\n");
goto err0;
}
p = kzalloc(sizeof(*p), GFP_KERNEL);
p = devm_kzalloc(&pdev->dev, sizeof(*p), GFP_KERNEL);
if (p == NULL) {
dev_err(&pdev->dev, "failed to allocate driver data\n");
ret = -ENOMEM;
goto err0;
return -ENOMEM;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&pdev->dev, "failed to get I/O memory\n");
goto err1;
return -ENXIO;
}
/* map memory, let mapbase point to our channel */
p->mapbase = ioremap_nocache(res->start, resource_size(res));
if (p->mapbase == NULL) {
dev_err(&pdev->dev, "failed to remap I/O memory\n");
goto err1;
return -ENXIO;
}
/* get hold of clock */
@ -275,7 +274,7 @@ static int __devinit r_tpu_probe(struct platform_device *pdev)
if (IS_ERR(p->clk)) {
dev_err(&pdev->dev, "cannot get clock\n");
ret = PTR_ERR(p->clk);
goto err2;
goto err0;
}
p->pdev = pdev;
@ -294,7 +293,7 @@ static int __devinit r_tpu_probe(struct platform_device *pdev)
p->ldev.flags |= LED_CORE_SUSPENDRESUME;
ret = led_classdev_register(&pdev->dev, &p->ldev);
if (ret < 0)
goto err3;
goto err1;
/* max_brightness may be updated by the LED core code */
p->min_rate = p->ldev.max_brightness * p->refresh_rate;
@ -302,14 +301,11 @@ static int __devinit r_tpu_probe(struct platform_device *pdev)
pm_runtime_enable(&pdev->dev);
return 0;
err3:
err1:
r_tpu_set_pin(p, R_TPU_PIN_UNUSED, LED_OFF);
clk_put(p->clk);
err2:
iounmap(p->mapbase);
err1:
kfree(p);
err0:
iounmap(p->mapbase);
return ret;
}
@ -327,7 +323,6 @@ static int __devexit r_tpu_remove(struct platform_device *pdev)
clk_put(p->clk);
iounmap(p->mapbase);
kfree(p);
return 0;
}

View File

@ -45,17 +45,19 @@ static void s3c24xx_led_set(struct led_classdev *led_cdev,
{
struct s3c24xx_gpio_led *led = to_gpio(led_cdev);
struct s3c24xx_led_platdata *pd = led->pdata;
int state = (value ? 1 : 0) ^ (pd->flags & S3C24XX_LEDF_ACTLOW);
/* there will be a short delay between setting the output and
* going from output to input when using tristate. */
s3c2410_gpio_setpin(pd->gpio, (value ? 1 : 0) ^
(pd->flags & S3C24XX_LEDF_ACTLOW));
if (pd->flags & S3C24XX_LEDF_TRISTATE)
s3c2410_gpio_cfgpin(pd->gpio,
value ? S3C2410_GPIO_OUTPUT : S3C2410_GPIO_INPUT);
gpio_set_value(pd->gpio, state);
if (pd->flags & S3C24XX_LEDF_TRISTATE) {
if (value)
gpio_direction_output(pd->gpio, state);
else
gpio_direction_input(pd->gpio);
}
}
static int s3c24xx_led_remove(struct platform_device *dev)
@ -63,7 +65,6 @@ static int s3c24xx_led_remove(struct platform_device *dev)
struct s3c24xx_gpio_led *led = pdev_to_gpio(dev);
led_classdev_unregister(&led->cdev);
kfree(led);
return 0;
}
@ -74,7 +75,8 @@ static int s3c24xx_led_probe(struct platform_device *dev)
struct s3c24xx_gpio_led *led;
int ret;
led = kzalloc(sizeof(struct s3c24xx_gpio_led), GFP_KERNEL);
led = devm_kzalloc(&dev->dev, sizeof(struct s3c24xx_gpio_led),
GFP_KERNEL);
if (led == NULL) {
dev_err(&dev->dev, "No memory for device\n");
return -ENOMEM;
@ -89,27 +91,27 @@ static int s3c24xx_led_probe(struct platform_device *dev)
led->pdata = pdata;
ret = devm_gpio_request(&dev->dev, pdata->gpio, "S3C24XX_LED");
if (ret < 0)
return ret;
/* no point in having a pull-up if we are always driving */
if (pdata->flags & S3C24XX_LEDF_TRISTATE) {
s3c2410_gpio_setpin(pdata->gpio, 0);
s3c2410_gpio_cfgpin(pdata->gpio, S3C2410_GPIO_INPUT);
} else {
s3c2410_gpio_pullup(pdata->gpio, 0);
s3c2410_gpio_setpin(pdata->gpio, 0);
s3c2410_gpio_cfgpin(pdata->gpio, S3C2410_GPIO_OUTPUT);
}
s3c_gpio_setpull(pdata->gpio, S3C_GPIO_PULL_NONE);
if (pdata->flags & S3C24XX_LEDF_TRISTATE)
gpio_direction_input(pdata->gpio);
else
gpio_direction_output(pdata->gpio,
pdata->flags & S3C24XX_LEDF_ACTLOW ? 1 : 0);
/* register our new led device */
ret = led_classdev_register(&dev->dev, &led->cdev);
if (ret < 0) {
if (ret < 0)
dev_err(&dev->dev, "led_classdev_register failed\n");
kfree(led);
return ret;
}
return 0;
return ret;
}
static struct platform_driver s3c24xx_led_driver = {

View File

@ -132,15 +132,13 @@ static int __devinit sunfire_led_generic_probe(struct platform_device *pdev,
if (pdev->num_resources != 1) {
printk(KERN_ERR PFX "Wrong number of resources %d, should be 1\n",
pdev->num_resources);
err = -EINVAL;
goto out;
return -EINVAL;
}
p = kzalloc(sizeof(*p), GFP_KERNEL);
p = devm_kzalloc(&pdev->dev, sizeof(*p), GFP_KERNEL);
if (!p) {
printk(KERN_ERR PFX "Could not allocate struct sunfire_drvdata\n");
err = -ENOMEM;
goto out;
return -ENOMEM;
}
for (i = 0; i < NUM_LEDS_PER_BOARD; i++) {
@ -156,20 +154,15 @@ static int __devinit sunfire_led_generic_probe(struct platform_device *pdev,
if (err) {
printk(KERN_ERR PFX "Could not register %s LED\n",
lp->name);
goto out_unregister_led_cdevs;
for (i--; i >= 0; i--)
led_classdev_unregister(&p->leds[i].led_cdev);
return err;
}
}
dev_set_drvdata(&pdev->dev, p);
return 0;
out_unregister_led_cdevs:
for (i--; i >= 0; i--)
led_classdev_unregister(&p->leds[i].led_cdev);
kfree(p);
out:
return err;
}
static int __devexit sunfire_led_generic_remove(struct platform_device *pdev)
@ -180,8 +173,6 @@ static int __devexit sunfire_led_generic_remove(struct platform_device *pdev)
for (i = 0; i < NUM_LEDS_PER_BOARD; i++)
led_classdev_unregister(&p->leds[i].led_cdev);
kfree(p);
return 0;
}

View File

@ -687,7 +687,7 @@ static int __devinit tca6507_probe(struct i2c_client *client,
NUM_LEDS);
return -ENODEV;
}
tca = kzalloc(sizeof(*tca), GFP_KERNEL);
tca = devm_kzalloc(&client->dev, sizeof(*tca), GFP_KERNEL);
if (!tca)
return -ENOMEM;
@ -727,7 +727,6 @@ exit:
if (tca->leds[i].led_cdev.name)
led_classdev_unregister(&tca->leds[i].led_cdev);
}
kfree(tca);
return err;
}
@ -743,7 +742,6 @@ static int __devexit tca6507_remove(struct i2c_client *client)
}
tca6507_remove_gpio(tca);
cancel_work_sync(&tca->work);
kfree(tca);
return 0;
}
@ -758,18 +756,7 @@ static struct i2c_driver tca6507_driver = {
.id_table = tca6507_id,
};
static int __init tca6507_leds_init(void)
{
return i2c_add_driver(&tca6507_driver);
}
static void __exit tca6507_leds_exit(void)
{
i2c_del_driver(&tca6507_driver);
}
module_init(tca6507_leds_init);
module_exit(tca6507_leds_exit);
module_i2c_driver(tca6507_driver);
MODULE_AUTHOR("NeilBrown <neilb@suse.de>");
MODULE_DESCRIPTION("TCA6507 LED/GPO driver");

View File

@ -17,7 +17,7 @@
#include <linux/rwsem.h>
#include <linux/leds.h>
static inline void led_set_brightness(struct led_classdev *led_cdev,
static inline void __led_set_brightness(struct led_classdev *led_cdev,
enum led_brightness value)
{
if (value > led_cdev->max_brightness)

View File

@ -46,9 +46,9 @@ static int fb_notifier_callback(struct notifier_block *p,
if ((n->old_status == UNBLANK) ^ n->invert) {
n->brightness = led->brightness;
led_set_brightness(led, LED_OFF);
__led_set_brightness(led, LED_OFF);
} else {
led_set_brightness(led, n->brightness);
__led_set_brightness(led, n->brightness);
}
n->old_status = new_status;
@ -87,9 +87,9 @@ static ssize_t bl_trig_invert_store(struct device *dev,
/* After inverting, we need to update the LED. */
if ((n->old_status == BLANK) ^ n->invert)
led_set_brightness(led, LED_OFF);
__led_set_brightness(led, LED_OFF);
else
led_set_brightness(led, n->brightness);
__led_set_brightness(led, n->brightness);
return num;
}

View File

@ -19,7 +19,7 @@
static void defon_trig_activate(struct led_classdev *led_cdev)
{
led_set_brightness(led_cdev, led_cdev->max_brightness);
__led_set_brightness(led_cdev, led_cdev->max_brightness);
}
static struct led_trigger defon_led_trigger = {

View File

@ -54,12 +54,12 @@ static void gpio_trig_work(struct work_struct *work)
if (tmp) {
if (gpio_data->desired_brightness)
led_set_brightness(gpio_data->led,
__led_set_brightness(gpio_data->led,
gpio_data->desired_brightness);
else
led_set_brightness(gpio_data->led, LED_FULL);
__led_set_brightness(gpio_data->led, LED_FULL);
} else {
led_set_brightness(gpio_data->led, LED_OFF);
__led_set_brightness(gpio_data->led, LED_OFF);
}
}

View File

@ -74,7 +74,7 @@ static void led_heartbeat_function(unsigned long data)
break;
}
led_set_brightness(led_cdev, brightness);
__led_set_brightness(led_cdev, brightness);
mod_timer(&heartbeat_data->timer, jiffies + delay);
}

View File

@ -12,39 +12,22 @@
*/
#include <linux/module.h>
#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/timer.h>
#include <linux/leds.h>
static void ledtrig_ide_timerfunc(unsigned long data);
#define BLINK_DELAY 30
DEFINE_LED_TRIGGER(ledtrig_ide);
static DEFINE_TIMER(ledtrig_ide_timer, ledtrig_ide_timerfunc, 0, 0);
static int ide_activity;
static int ide_lastactivity;
static unsigned long ide_blink_delay = BLINK_DELAY;
void ledtrig_ide_activity(void)
{
ide_activity++;
if (!timer_pending(&ledtrig_ide_timer))
mod_timer(&ledtrig_ide_timer, jiffies + msecs_to_jiffies(10));
led_trigger_blink_oneshot(ledtrig_ide,
&ide_blink_delay, &ide_blink_delay, 0);
}
EXPORT_SYMBOL(ledtrig_ide_activity);
static void ledtrig_ide_timerfunc(unsigned long data)
{
if (ide_lastactivity != ide_activity) {
ide_lastactivity = ide_activity;
/* INT_MAX will set each LED to its maximum brightness */
led_trigger_event(ledtrig_ide, INT_MAX);
mod_timer(&ledtrig_ide_timer, jiffies + msecs_to_jiffies(10));
} else {
led_trigger_event(ledtrig_ide, LED_OFF);
}
}
static int __init ledtrig_ide_init(void)
{
led_trigger_register_simple("ide-disk", &ledtrig_ide);

View File

@ -0,0 +1,204 @@
/*
* One-shot LED Trigger
*
* Copyright 2012, Fabio Baltieri <fabio.baltieri@gmail.com>
*
* Based on ledtrig-timer.c by Richard Purdie <rpurdie@openedhand.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/ctype.h>
#include <linux/slab.h>
#include <linux/leds.h>
#include "leds.h"
#define DEFAULT_DELAY 100
struct oneshot_trig_data {
unsigned int invert;
};
static ssize_t led_shot(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
struct oneshot_trig_data *oneshot_data = led_cdev->trigger_data;
led_blink_set_oneshot(led_cdev,
&led_cdev->blink_delay_on, &led_cdev->blink_delay_off,
oneshot_data->invert);
/* content is ignored */
return size;
}
static ssize_t led_invert_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
struct oneshot_trig_data *oneshot_data = led_cdev->trigger_data;
return sprintf(buf, "%u\n", oneshot_data->invert);
}
static ssize_t led_invert_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
struct oneshot_trig_data *oneshot_data = led_cdev->trigger_data;
unsigned long state;
int ret;
ret = kstrtoul(buf, 0, &state);
if (ret)
return ret;
oneshot_data->invert = !!state;
if (oneshot_data->invert)
__led_set_brightness(led_cdev, LED_FULL);
else
__led_set_brightness(led_cdev, LED_OFF);
return size;
}
static ssize_t led_delay_on_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
return sprintf(buf, "%lu\n", led_cdev->blink_delay_on);
}
static ssize_t led_delay_on_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
unsigned long state;
int ret;
ret = kstrtoul(buf, 0, &state);
if (ret)
return ret;
led_cdev->blink_delay_on = state;
return size;
}
static ssize_t led_delay_off_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
return sprintf(buf, "%lu\n", led_cdev->blink_delay_off);
}
static ssize_t led_delay_off_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
unsigned long state;
int ret;
ret = kstrtoul(buf, 0, &state);
if (ret)
return ret;
led_cdev->blink_delay_off = state;
return size;
}
static DEVICE_ATTR(delay_on, 0644, led_delay_on_show, led_delay_on_store);
static DEVICE_ATTR(delay_off, 0644, led_delay_off_show, led_delay_off_store);
static DEVICE_ATTR(invert, 0644, led_invert_show, led_invert_store);
static DEVICE_ATTR(shot, 0200, NULL, led_shot);
static void oneshot_trig_activate(struct led_classdev *led_cdev)
{
struct oneshot_trig_data *oneshot_data;
int rc;
oneshot_data = kzalloc(sizeof(*oneshot_data), GFP_KERNEL);
if (!oneshot_data)
return;
led_cdev->trigger_data = oneshot_data;
rc = device_create_file(led_cdev->dev, &dev_attr_delay_on);
if (rc)
goto err_out_trig_data;
rc = device_create_file(led_cdev->dev, &dev_attr_delay_off);
if (rc)
goto err_out_delayon;
rc = device_create_file(led_cdev->dev, &dev_attr_invert);
if (rc)
goto err_out_delayoff;
rc = device_create_file(led_cdev->dev, &dev_attr_shot);
if (rc)
goto err_out_invert;
led_cdev->blink_delay_on = DEFAULT_DELAY;
led_cdev->blink_delay_off = DEFAULT_DELAY;
led_cdev->activated = true;
return;
err_out_invert:
device_remove_file(led_cdev->dev, &dev_attr_invert);
err_out_delayoff:
device_remove_file(led_cdev->dev, &dev_attr_delay_off);
err_out_delayon:
device_remove_file(led_cdev->dev, &dev_attr_delay_on);
err_out_trig_data:
kfree(led_cdev->trigger_data);
}
static void oneshot_trig_deactivate(struct led_classdev *led_cdev)
{
struct oneshot_trig_data *oneshot_data = led_cdev->trigger_data;
if (led_cdev->activated) {
device_remove_file(led_cdev->dev, &dev_attr_delay_on);
device_remove_file(led_cdev->dev, &dev_attr_delay_off);
device_remove_file(led_cdev->dev, &dev_attr_invert);
device_remove_file(led_cdev->dev, &dev_attr_shot);
kfree(oneshot_data);
led_cdev->activated = false;
}
/* Stop blinking */
led_set_brightness(led_cdev, LED_OFF);
}
static struct led_trigger oneshot_led_trigger = {
.name = "oneshot",
.activate = oneshot_trig_activate,
.deactivate = oneshot_trig_deactivate,
};
static int __init oneshot_trig_init(void)
{
return led_trigger_register(&oneshot_led_trigger);
}
static void __exit oneshot_trig_exit(void)
{
led_trigger_unregister(&oneshot_led_trigger);
}
module_init(oneshot_trig_init);
module_exit(oneshot_trig_exit);
MODULE_AUTHOR("Fabio Baltieri <fabio.baltieri@gmail.com>");
MODULE_DESCRIPTION("One-shot LED trigger");
MODULE_LICENSE("GPL");

View File

@ -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 = {

View File

@ -41,7 +41,7 @@ static void transient_timer_function(unsigned long data)
struct transient_trig_data *transient_data = led_cdev->trigger_data;
transient_data->activate = 0;
led_set_brightness(led_cdev, transient_data->restore_state);
__led_set_brightness(led_cdev, transient_data->restore_state);
}
static ssize_t transient_activate_show(struct device *dev,
@ -72,7 +72,7 @@ static ssize_t transient_activate_store(struct device *dev,
if (state == 0 && transient_data->activate == 1) {
del_timer(&transient_data->timer);
transient_data->activate = state;
led_set_brightness(led_cdev, transient_data->restore_state);
__led_set_brightness(led_cdev, transient_data->restore_state);
return size;
}
@ -80,7 +80,7 @@ static ssize_t transient_activate_store(struct device *dev,
if (state == 1 && transient_data->activate == 0 &&
transient_data->duration != 0) {
transient_data->activate = state;
led_set_brightness(led_cdev, transient_data->state);
__led_set_brightness(led_cdev, transient_data->state);
transient_data->restore_state =
(transient_data->state == LED_FULL) ? LED_OFF : LED_FULL;
mod_timer(&transient_data->timer,
@ -203,7 +203,7 @@ static void transient_trig_deactivate(struct led_classdev *led_cdev)
if (led_cdev->activated) {
del_timer_sync(&transient_data->timer);
led_set_brightness(led_cdev, transient_data->restore_state);
__led_set_brightness(led_cdev, transient_data->restore_state);
device_remove_file(led_cdev->dev, &dev_attr_activate);
device_remove_file(led_cdev->dev, &dev_attr_duration);
device_remove_file(led_cdev->dev, &dev_attr_state);

View File

@ -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 */
@ -103,7 +106,25 @@ extern void led_blink_set(struct led_classdev *led_cdev,
unsigned long *delay_on,
unsigned long *delay_off);
/**
* led_brightness_set - set LED brightness
* 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_set_brightness - set LED brightness
* @led_cdev: the LED to set
* @brightness: the brightness to set it to
*
@ -111,7 +132,7 @@ extern void led_blink_set(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);
/*
@ -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

View File

@ -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 */

View File

@ -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);
}