mirror of
https://github.com/torvalds/linux.git
synced 2024-11-11 06:31:49 +00:00
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:
commit
aa0b3b2bee
@ -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.
|
||||
|
80
Documentation/leds/leds-blinkm.txt
Normal file
80
Documentation/leds/leds-blinkm.txt
Normal 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
|
||||
|
85
Documentation/leds/leds-lm3556.txt
Normal file
85
Documentation/leds/leds-lm3556.txt
Normal 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));
|
59
Documentation/leds/ledtrig-oneshot.txt
Normal file
59
Documentation/leds/ledtrig-oneshot.txt
Normal 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
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
815
drivers/leds/leds-blinkm.c
Normal 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");
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
512
drivers/leds/leds-lm3556.c
Normal 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, ®_val);
|
||||
if (ret < 0) {
|
||||
dev_err(chip->dev, "Failed to read REG_CONF Register\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
reg_val &= (~EX_PIN_CONTROL_MASK);
|
||||
reg_val |= ((pdata->torch_pin_polarity & 0x01)
|
||||
<< TORCH_PIN_POLARITY_SHIFT);
|
||||
reg_val |= ((pdata->strobe_usuage & 0x01) << STROBE_USUAGE_SHIFT);
|
||||
reg_val |= ((pdata->strobe_pin_polarity & 0x01)
|
||||
<< STROBE_PIN_POLARITY_SHIFT);
|
||||
reg_val |= ((pdata->tx_pin_polarity & 0x01) << TX_PIN_POLARITY_SHIFT);
|
||||
reg_val |= ((pdata->indicator_mode & 0x01) << INDIC_MODE_SHIFT);
|
||||
|
||||
ret = regmap_write(chip->regmap, REG_CONF, reg_val);
|
||||
if (ret < 0) {
|
||||
dev_err(chip->dev, "Failed to write REG_CONF Regisgter\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* set enable register */
|
||||
ret = regmap_read(chip->regmap, REG_ENABLE, ®_val);
|
||||
if (ret < 0) {
|
||||
dev_err(chip->dev, "Failed to read REG_ENABLE Register\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
reg_val &= (~EX_PIN_ENABLE_MASK);
|
||||
reg_val |= ((pdata->torch_pin_en & 0x01) << TORCH_PIN_EN_SHIFT);
|
||||
reg_val |= ((pdata->strobe_pin_en & 0x01) << STROBE_PIN_EN_SHIFT);
|
||||
reg_val |= ((pdata->tx_pin_en & 0x01) << TX_PIN_EN_SHIFT);
|
||||
|
||||
ret = regmap_write(chip->regmap, REG_ENABLE, reg_val);
|
||||
if (ret < 0) {
|
||||
dev_err(chip->dev, "Failed to write REG_ENABLE Regisgter\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* chip control */
|
||||
static int lm3556_control(struct lm3556_chip_data *chip,
|
||||
u8 brightness, enum lm3556_mode opmode)
|
||||
{
|
||||
int ret;
|
||||
struct lm3556_platform_data *pdata = chip->pdata;
|
||||
|
||||
ret = regmap_read(chip->regmap, REG_FLAG, &chip->last_flag);
|
||||
if (ret < 0) {
|
||||
dev_err(chip->dev, "Failed to read REG_FLAG Register\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (chip->last_flag)
|
||||
dev_info(chip->dev, "Last FLAG is 0x%x\n", chip->last_flag);
|
||||
|
||||
/* brightness 0 means off-state */
|
||||
if (!brightness)
|
||||
opmode = MODES_STASNDBY;
|
||||
|
||||
switch (opmode) {
|
||||
case MODES_TORCH:
|
||||
ret = regmap_update_bits(chip->regmap, REG_I_CTRL,
|
||||
TORCH_I_MASK << TORCH_I_SHIFT,
|
||||
(brightness - 1) << TORCH_I_SHIFT);
|
||||
|
||||
if (pdata->torch_pin_en)
|
||||
opmode |= (TORCH_PIN_EN_MASK << TORCH_PIN_EN_SHIFT);
|
||||
break;
|
||||
|
||||
case MODES_FLASH:
|
||||
ret = regmap_update_bits(chip->regmap, REG_I_CTRL,
|
||||
FLASH_I_MASK << FLASH_I_SHIFT,
|
||||
(brightness - 1) << FLASH_I_SHIFT);
|
||||
break;
|
||||
|
||||
case MODES_INDIC:
|
||||
ret = regmap_update_bits(chip->regmap, REG_I_CTRL,
|
||||
TORCH_I_MASK << TORCH_I_SHIFT,
|
||||
(brightness - 1) << TORCH_I_SHIFT);
|
||||
break;
|
||||
|
||||
case MODES_STASNDBY:
|
||||
if (pdata->torch_pin_en)
|
||||
opmode |= (TORCH_PIN_EN_MASK << TORCH_PIN_EN_SHIFT);
|
||||
break;
|
||||
|
||||
default:
|
||||
return ret;
|
||||
}
|
||||
if (ret < 0) {
|
||||
dev_err(chip->dev, "Failed to write REG_I_CTRL Register\n");
|
||||
goto out;
|
||||
}
|
||||
ret = regmap_update_bits(chip->regmap, REG_ENABLE,
|
||||
MODE_BITS_MASK << MODE_BITS_SHIFT,
|
||||
opmode << MODE_BITS_SHIFT);
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* torch */
|
||||
static void lm3556_torch_brightness_set(struct led_classdev *cdev,
|
||||
enum led_brightness brightness)
|
||||
{
|
||||
struct lm3556_chip_data *chip =
|
||||
container_of(cdev, struct lm3556_chip_data, cdev_torch);
|
||||
|
||||
mutex_lock(&chip->lock);
|
||||
lm3556_control(chip, brightness, MODES_TORCH);
|
||||
mutex_unlock(&chip->lock);
|
||||
}
|
||||
|
||||
/* flash */
|
||||
static void lm3556_strobe_brightness_set(struct led_classdev *cdev,
|
||||
enum led_brightness brightness)
|
||||
{
|
||||
struct lm3556_chip_data *chip =
|
||||
container_of(cdev, struct lm3556_chip_data, cdev_flash);
|
||||
|
||||
mutex_lock(&chip->lock);
|
||||
lm3556_control(chip, brightness, MODES_FLASH);
|
||||
mutex_unlock(&chip->lock);
|
||||
}
|
||||
|
||||
/* indicator */
|
||||
static void lm3556_indicator_brightness_set(struct led_classdev *cdev,
|
||||
enum led_brightness brightness)
|
||||
{
|
||||
struct lm3556_chip_data *chip =
|
||||
container_of(cdev, struct lm3556_chip_data, cdev_indicator);
|
||||
|
||||
mutex_lock(&chip->lock);
|
||||
lm3556_control(chip, brightness, MODES_INDIC);
|
||||
mutex_unlock(&chip->lock);
|
||||
}
|
||||
|
||||
/* indicator pattern */
|
||||
static ssize_t lm3556_indicator_pattern_store(struct device *dev,
|
||||
struct device_attribute *devAttr,
|
||||
const char *buf, size_t size)
|
||||
{
|
||||
ssize_t ret;
|
||||
struct led_classdev *led_cdev = dev_get_drvdata(dev);
|
||||
struct lm3556_chip_data *chip =
|
||||
container_of(led_cdev, struct lm3556_chip_data, cdev_indicator);
|
||||
unsigned int state;
|
||||
|
||||
ret = kstrtouint(buf, 10, &state);
|
||||
if (ret)
|
||||
goto out;
|
||||
if (state > INDIC_PATTERN_SIZE - 1)
|
||||
state = INDIC_PATTERN_SIZE - 1;
|
||||
|
||||
ret = regmap_write(chip->regmap, REG_INDIC_BLINK,
|
||||
indicator_pattern[state].blinking);
|
||||
if (ret < 0) {
|
||||
dev_err(chip->dev, "Failed to write REG_ENABLE Regisgter\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = regmap_write(chip->regmap, REG_INDIC_PERIOD,
|
||||
indicator_pattern[state].period_cnt);
|
||||
if (ret < 0) {
|
||||
dev_err(chip->dev, "Failed to write REG_ENABLE Regisgter\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
return size;
|
||||
out:
|
||||
dev_err(chip->dev, "Indicator pattern doesn't saved\n");
|
||||
return size;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(pattern, 0666, NULL, lm3556_indicator_pattern_store);
|
||||
|
||||
static const struct regmap_config lm3556_regmap = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.max_register = REG_MAX,
|
||||
};
|
||||
|
||||
/* module initialize */
|
||||
static int __devinit lm3556_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct lm3556_platform_data *pdata = client->dev.platform_data;
|
||||
struct lm3556_chip_data *chip;
|
||||
|
||||
int err;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
|
||||
dev_err(&client->dev, "i2c functionality check fail.\n");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
if (pdata == NULL) {
|
||||
dev_err(&client->dev, "Needs Platform Data.\n");
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
chip =
|
||||
devm_kzalloc(&client->dev, sizeof(struct lm3556_chip_data),
|
||||
GFP_KERNEL);
|
||||
if (!chip)
|
||||
return -ENOMEM;
|
||||
|
||||
chip->dev = &client->dev;
|
||||
chip->pdata = pdata;
|
||||
|
||||
chip->regmap = devm_regmap_init_i2c(client, &lm3556_regmap);
|
||||
if (IS_ERR(chip->regmap)) {
|
||||
err = PTR_ERR(chip->regmap);
|
||||
dev_err(&client->dev, "Failed to allocate register map: %d\n",
|
||||
err);
|
||||
return err;
|
||||
}
|
||||
|
||||
mutex_init(&chip->lock);
|
||||
i2c_set_clientdata(client, chip);
|
||||
|
||||
err = lm3556_chip_init(chip);
|
||||
if (err < 0)
|
||||
goto err_out;
|
||||
|
||||
/* flash */
|
||||
chip->cdev_flash.name = "flash";
|
||||
chip->cdev_flash.max_brightness = 16;
|
||||
chip->cdev_flash.brightness_set = lm3556_strobe_brightness_set;
|
||||
err = led_classdev_register((struct device *)
|
||||
&client->dev, &chip->cdev_flash);
|
||||
if (err < 0)
|
||||
goto err_out;
|
||||
/* torch */
|
||||
chip->cdev_torch.name = "torch";
|
||||
chip->cdev_torch.max_brightness = 8;
|
||||
chip->cdev_torch.brightness_set = lm3556_torch_brightness_set;
|
||||
err = led_classdev_register((struct device *)
|
||||
&client->dev, &chip->cdev_torch);
|
||||
if (err < 0)
|
||||
goto err_create_torch_file;
|
||||
/* indicator */
|
||||
chip->cdev_indicator.name = "indicator";
|
||||
chip->cdev_indicator.max_brightness = 8;
|
||||
chip->cdev_indicator.brightness_set = lm3556_indicator_brightness_set;
|
||||
err = led_classdev_register((struct device *)
|
||||
&client->dev, &chip->cdev_indicator);
|
||||
if (err < 0)
|
||||
goto err_create_indicator_file;
|
||||
|
||||
err = device_create_file(chip->cdev_indicator.dev, &dev_attr_pattern);
|
||||
if (err < 0)
|
||||
goto err_create_pattern_file;
|
||||
|
||||
dev_info(&client->dev, "LM3556 is initialized\n");
|
||||
return 0;
|
||||
|
||||
err_create_pattern_file:
|
||||
led_classdev_unregister(&chip->cdev_indicator);
|
||||
err_create_indicator_file:
|
||||
led_classdev_unregister(&chip->cdev_torch);
|
||||
err_create_torch_file:
|
||||
led_classdev_unregister(&chip->cdev_flash);
|
||||
err_out:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int __devexit lm3556_remove(struct i2c_client *client)
|
||||
{
|
||||
struct lm3556_chip_data *chip = i2c_get_clientdata(client);
|
||||
|
||||
device_remove_file(chip->cdev_indicator.dev, &dev_attr_pattern);
|
||||
led_classdev_unregister(&chip->cdev_indicator);
|
||||
led_classdev_unregister(&chip->cdev_torch);
|
||||
led_classdev_unregister(&chip->cdev_flash);
|
||||
regmap_write(chip->regmap, REG_ENABLE, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id lm3556_id[] = {
|
||||
{LM3556_NAME, 0},
|
||||
{}
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(i2c, lm3556_id);
|
||||
|
||||
static struct i2c_driver lm3556_i2c_driver = {
|
||||
.driver = {
|
||||
.name = LM3556_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.pm = NULL,
|
||||
},
|
||||
.probe = lm3556_probe,
|
||||
.remove = __devexit_p(lm3556_remove),
|
||||
.id_table = lm3556_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(lm3556_i2c_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Texas Instruments Flash Lighting driver for LM3556");
|
||||
MODULE_AUTHOR("Daniel Jeong <daniel.jeong@ti.com>");
|
||||
MODULE_AUTHOR("G.Shark Jeong <gshark.jeong@gmail.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
193
drivers/leds/leds-lp8788.c
Normal 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");
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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");
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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 = {
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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");
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 = {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
204
drivers/leds/ledtrig-oneshot.c
Normal file
204
drivers/leds/ledtrig-oneshot.c
Normal 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");
|
@ -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 = {
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
||||
|
50
include/linux/platform_data/leds-lm3556.h
Normal file
50
include/linux/platform_data/leds-lm3556.h
Normal 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 */
|
@ -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);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user