diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2012-07-26 20:26:27 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-07-26 20:26:27 -0700 |
commit | aa0b3b2bee1d3ca8355de76caebf65b836c9bb6e (patch) | |
tree | e1ba9eb2e96b55af1008162ec662b03ea24f9989 | |
parent | 6ee127b7dd63afe4d6d0a58293786bf4bf336850 (diff) | |
parent | d45bb11616c94c76c6e40960a120c0687b708a2e (diff) |
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()
...
51 files changed, 2376 insertions, 413 deletions
diff --git a/Documentation/leds/00-INDEX b/Documentation/leds/00-INDEX index 29f481df32c..5fefe374892 100644 --- a/Documentation/leds/00-INDEX +++ b/Documentation/leds/00-INDEX @@ -6,3 +6,5 @@ leds-lp5521.txt - notes on how to use the leds-lp5521 driver. leds-lp5523.txt - notes on how to use the leds-lp5523 driver. +leds-lm3556.txt + - notes on how to use the leds-lm3556 driver. diff --git a/Documentation/leds/leds-blinkm.txt b/Documentation/leds/leds-blinkm.txt new file mode 100644 index 00000000000..9dd92f4cf4e --- /dev/null +++ b/Documentation/leds/leds-blinkm.txt @@ -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 + diff --git a/Documentation/leds/leds-lm3556.txt b/Documentation/leds/leds-lm3556.txt new file mode 100644 index 00000000000..d9eb91b5191 --- /dev/null +++ b/Documentation/leds/leds-lm3556.txt @@ -0,0 +1,85 @@ +Kernel driver for lm3556 +======================== + +*Texas Instrument: + 1.5 A Synchronous Boost LED Flash Driver w/ High-Side Current Source +* Datasheet: http://www.national.com/ds/LM/LM3556.pdf + +Authors: + Daniel Jeong + Contact:Daniel Jeong(daniel.jeong-at-ti.com, gshark.jeong-at-gmail.com) + +Description +----------- +There are 3 functions in LM3556, Flash, Torch and Indicator. + +FLASH MODE +In Flash Mode, the LED current source(LED) provides 16 target current levels +from 93.75 mA to 1500 mA.The Flash currents are adjusted via the CURRENT +CONTROL REGISTER(0x09).Flash mode is activated by the ENABLE REGISTER(0x0A), +or by pulling the STROBE pin HIGH. +LM3556 Flash can be controlled through sys/class/leds/flash/brightness file +* if STROBE pin is enabled, below example control brightness only, and +ON / OFF will be controlled by STROBE pin. + +Flash Example: +OFF : #echo 0 > sys/class/leds/flash/brightness +93.75 mA: #echo 1 > sys/class/leds/flash/brightness +... ..... +1500 mA: #echo 16 > sys/class/leds/flash/brightness + +TORCH MODE +In Torch Mode, the current source(LED) is programmed via the CURRENT CONTROL +REGISTER(0x09).Torch Mode is activated by the ENABLE REGISTER(0x0A) or by the +hardware TORCH input. +LM3556 torch can be controlled through sys/class/leds/torch/brightness file. +* if TORCH pin is enabled, below example control brightness only, +and ON / OFF will be controlled by TORCH pin. + +Torch Example: +OFF : #echo 0 > sys/class/leds/torch/brightness +46.88 mA: #echo 1 > sys/class/leds/torch/brightness +... ..... +375 mA : #echo 8 > sys/class/leds/torch/brightness + +INDICATOR MODE +Indicator pattern can be set through sys/class/leds/indicator/pattern file, +and 4 patterns are pre-defined in indicator_pattern array. +According to N-lank, Pulse time and N Period values, different pattern wiill +be generated.If you want new patterns for your own device, change +indicator_pattern array with your own values and INDIC_PATTERN_SIZE. +Please refer datasheet for more detail about N-Blank, Pulse time and N Period. + +Indicator pattern example: +pattern 0: #echo 0 > sys/class/leds/indicator/pattern +.... +pattern 3: #echo 3 > sys/class/leds/indicator/pattern + +Indicator brightness can be controlled through +sys/class/leds/indicator/brightness file. + +Example: +OFF : #echo 0 > sys/class/leds/indicator/brightness +5.86 mA : #echo 1 > sys/class/leds/indicator/brightness +........ +46.875mA : #echo 8 > sys/class/leds/indicator/brightness + +Notes +----- +Driver expects it is registered using the i2c_board_info mechanism. +To register the chip at address 0x63 on specific adapter, set the platform data +according to include/linux/platform_data/leds-lm3556.h, set the i2c board info + +Example: + static struct i2c_board_info __initdata board_i2c_ch4[] = { + { + I2C_BOARD_INFO(LM3556_NAME, 0x63), + .platform_data = &lm3556_pdata, + }, + }; + +and register it in the platform init function + +Example: + board_register_i2c_bus(4, 400, + board_i2c_ch4, ARRAY_SIZE(board_i2c_ch4)); diff --git a/Documentation/leds/ledtrig-oneshot.txt b/Documentation/leds/ledtrig-oneshot.txt new file mode 100644 index 00000000000..07cd1fa41a3 --- /dev/null +++ b/Documentation/leds/ledtrig-oneshot.txt @@ -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 diff --git a/MAINTAINERS b/MAINTAINERS index 8f363b00522..3bc49687b65 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -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 diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 12b2b55c519..c96bbaadeeb 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -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 diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index f8958cd6cf6..a4429a9217b 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -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 diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c index e663e6f413e..c599095bc00 100644 --- a/drivers/leds/led-class.c +++ b/drivers/leds/led-class.c @@ -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); diff --git a/drivers/leds/led-core.c b/drivers/leds/led-core.c index d65353d8d3f..2ab05af3de3 100644 --- a/drivers/leds/led-core.c +++ b/drivers/leds/led-core.c @@ -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); diff --git a/drivers/leds/led-triggers.c b/drivers/leds/led-triggers.c index 46b4c766335..6157cbbf411 100644 --- a/drivers/leds/led-triggers.c +++ b/drivers/leds/led-triggers.c @@ -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); diff --git a/drivers/leds/leds-88pm860x.c b/drivers/leds/leds-88pm860x.c index 5b61aaf7ac0..61897cfeeda 100644 --- a/drivers/leds/leds-88pm860x.c +++ b/drivers/leds/leds-88pm860x.c @@ -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; } diff --git a/drivers/leds/leds-adp5520.c b/drivers/leds/leds-adp5520.c index b1400db3f83..aa56a867693 100644 --- a/drivers/leds/leds-adp5520.c +++ b/drivers/leds/leds-adp5520.c @@ -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; } diff --git a/drivers/leds/leds-asic3.c b/drivers/leds/leds-asic3.c index 525a9249283..5de74ff90dc 100644 --- a/drivers/leds/leds-asic3.c +++ b/drivers/leds/leds-asic3.c @@ -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); } diff --git a/drivers/leds/leds-atmel-pwm.c b/drivers/leds/leds-atmel-pwm.c index 64ad702a2ec..45430632faa 100644 --- a/drivers/leds/leds-atmel-pwm.c +++ b/drivers/leds/leds-atmel-pwm.c @@ -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; } diff --git a/drivers/leds/leds-bd2802.c b/drivers/leds/leds-bd2802.c index 591cbdf5a04..89ca6a2a19d 100644 --- a/drivers/leds/leds-bd2802.c +++ b/drivers/leds/leds-bd2802.c @@ -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; } diff --git a/drivers/leds/leds-blinkm.c b/drivers/leds/leds-blinkm.c new file mode 100644 index 00000000000..f7c3d7f1ec5 --- /dev/null +++ b/drivers/leds/leds-blinkm.c @@ -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"); + diff --git a/drivers/leds/leds-da903x.c b/drivers/leds/leds-da903x.c index d9cd73ebd6c..cc77c9d9261 100644 --- a/drivers/leds/leds-da903x.c +++ b/drivers/leds/leds-da903x.c @@ -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; } diff --git a/drivers/leds/leds-dac124s085.c b/drivers/leds/leds-dac124s085.c index d56c14269ff..1f9d8e62d37 100644 --- a/drivers/leds/leds-dac124s085.c +++ b/drivers/leds/leds-dac124s085.c @@ -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; } diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c index f4c470a3bc8..c032b218034 100644 --- a/drivers/leds/leds-gpio.c +++ b/drivers/leds/leds-gpio.c @@ -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; } diff --git a/drivers/leds/leds-lm3530.c b/drivers/leds/leds-lm3530.c index 84ba6de8039..23637bdb275 100644 --- a/drivers/leds/leds-lm3530.c +++ b/drivers/leds/leds-lm3530.c @@ -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; } diff --git a/drivers/leds/leds-lm3556.c b/drivers/leds/leds-lm3556.c new file mode 100644 index 00000000000..3062abd9a53 --- /dev/null +++ b/drivers/leds/leds-lm3556.c @@ -0,0 +1,512 @@ +/* + * Simple driver for Texas Instruments LM3556 LED Flash driver chip (Rev0x03) + * Copyright (C) 2012 Texas Instruments + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Please refer Documentation/leds/leds-lm3556.txt file. + */ +#include <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"); diff --git a/drivers/leds/leds-lp3944.c b/drivers/leds/leds-lp3944.c index b8f9f0a5d43..c298f7d9f53 100644 --- a/drivers/leds/leds-lp3944.c +++ b/drivers/leds/leds-lp3944.c @@ -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; } diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index 23815624f35..2064aefedc0 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -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; } diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index 857a3e15f2d..fbc12acada9 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -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; } diff --git a/drivers/leds/leds-lp8788.c b/drivers/leds/leds-lp8788.c new file mode 100644 index 00000000000..53bd136f1ef --- /dev/null +++ b/drivers/leds/leds-lp8788.c @@ -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"); diff --git a/drivers/leds/leds-lt3593.c b/drivers/leds/leds-lt3593.c index e311a96c446..09a732217f6 100644 --- a/drivers/leds/leds-lt3593.c +++ b/drivers/leds/leds-lt3593.c @@ -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; } diff --git a/drivers/leds/leds-max8997.c b/drivers/leds/leds-max8997.c index f4c0e37fad1..569e36de37d 100644 --- a/drivers/leds/leds-max8997.c +++ b/drivers/leds/leds-max8997.c @@ -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"); diff --git a/drivers/leds/leds-mc13783.c b/drivers/leds/leds-mc13783.c index 4cc6a2e3df3..2a5d4340067 100644 --- a/drivers/leds/leds-mc13783.c +++ b/drivers/leds/leds-mc13783.c @@ -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; } diff --git a/drivers/leds/leds-netxbig.c b/drivers/leds/leds-netxbig.c index 73973fdbd8b..e37618e363c 100644 --- a/drivers/leds/leds-netxbig.c +++ b/drivers/leds/leds-netxbig.c @@ -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; } diff --git a/drivers/leds/leds-ns2.c b/drivers/leds/leds-ns2.c index 01cf89ec694..10528dafb04 100644 --- a/drivers/leds/leds-ns2.c +++ b/drivers/leds/leds-ns2.c @@ -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; diff --git a/drivers/leds/leds-pca9532.c b/drivers/leds/leds-pca9532.c index ceccab44b5b..cee8a5b483a 100644 --- a/drivers/leds/leds-pca9532.c +++ b/drivers/leds/leds-pca9532.c @@ -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; } diff --git a/drivers/leds/leds-pca955x.c b/drivers/leds/leds-pca955x.c index 5f462dbf0db..aef3cf0432f 100644 --- a/drivers/leds/leds-pca955x.c +++ b/drivers/leds/leds-pca955x.c @@ -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; } diff --git a/drivers/leds/leds-pca9633.c b/drivers/leds/leds-pca9633.c index d8926fd031a..edcd706c563 100644 --- a/drivers/leds/leds-pca9633.c +++ b/drivers/leds/leds-pca9633.c @@ -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; } diff --git a/drivers/leds/leds-pwm.c b/drivers/leds/leds-pwm.c index 3ed92f34bd4..f2e44c71943 100644 --- a/drivers/leds/leds-pwm.c +++ b/drivers/leds/leds-pwm.c @@ -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; } diff --git a/drivers/leds/leds-regulator.c b/drivers/leds/leds-regulator.c index df7e963bddd..25d382d60fa 100644 --- a/drivers/leds/leds-regulator.c +++ b/drivers/leds/leds-regulator.c @@ -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; } diff --git a/drivers/leds/leds-renesas-tpu.c b/drivers/leds/leds-renesas-tpu.c index 32fe337d5c6..9ee12c28059 100644 --- a/drivers/leds/leds-renesas-tpu.c +++ b/drivers/leds/leds-renesas-tpu.c @@ -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; } diff --git a/drivers/leds/leds-s3c24xx.c b/drivers/leds/leds-s3c24xx.c index bd0a5ed49c4..942f0ea1817 100644 --- a/drivers/leds/leds-s3c24xx.c +++ b/drivers/leds/leds-s3c24xx.c @@ -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 = { diff --git a/drivers/leds/leds-sunfire.c b/drivers/leds/leds-sunfire.c index 1757396b20b..134d9a4b34f 100644 --- a/drivers/leds/leds-sunfire.c +++ b/drivers/leds/leds-sunfire.c @@ -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; } diff --git a/drivers/leds/leds-tca6507.c b/drivers/leds/leds-tca6507.c index 6c1c14f3163..dabcf7ae8d0 100644 --- a/drivers/leds/leds-tca6507.c +++ b/drivers/leds/leds-tca6507.c @@ -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"); diff --git a/drivers/leds/leds.h b/drivers/leds/leds.h index e77c7f8dcdd..d02acd49612 100644 --- a/drivers/leds/leds.h +++ b/drivers/leds/leds.h @@ -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) diff --git a/drivers/leds/ledtrig-backlight.c b/drivers/leds/ledtrig-backlight.c index e2726867c5d..b941685f222 100644 --- a/drivers/leds/ledtrig-backlight.c +++ b/drivers/leds/ledtrig-backlight.c @@ -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; } diff --git a/drivers/leds/ledtrig-default-on.c b/drivers/leds/ledtrig-default-on.c index a4ef54b9d50..eac1f1b1ada 100644 --- a/drivers/leds/ledtrig-default-on.c +++ b/drivers/leds/ledtrig-default-on.c @@ -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 = { diff --git a/drivers/leds/ledtrig-gpio.c b/drivers/leds/ledtrig-gpio.c index f057c101b89..ba215dc42f9 100644 --- a/drivers/leds/ledtrig-gpio.c +++ b/drivers/leds/ledtrig-gpio.c @@ -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); } } diff --git a/drivers/leds/ledtrig-heartbeat.c b/drivers/leds/ledtrig-heartbeat.c index a019fbb7088..1edc7463ce8 100644 --- a/drivers/leds/ledtrig-heartbeat.c +++ b/drivers/leds/ledtrig-heartbeat.c @@ -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); } diff --git a/drivers/leds/ledtrig-ide-disk.c b/drivers/leds/ledtrig-ide-disk.c index ec099fcbcb0..2cd7c0cf592 100644 --- a/drivers/leds/ledtrig-ide-disk.c +++ b/drivers/leds/ledtrig-ide-disk.c @@ -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); diff --git a/drivers/leds/ledtrig-oneshot.c b/drivers/leds/ledtrig-oneshot.c new file mode 100644 index 00000000000..2c029aa5c4f --- /dev/null +++ b/drivers/leds/ledtrig-oneshot.c @@ -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"); diff --git a/drivers/leds/ledtrig-timer.c b/drivers/leds/ledtrig-timer.c index 9010f7abaf2..f774d059220 100644 --- a/drivers/leds/ledtrig-timer.c +++ b/drivers/leds/ledtrig-timer.c @@ -104,7 +104,7 @@ static void timer_trig_deactivate(struct led_classdev *led_cdev) } /* Stop blinking */ - led_brightness_set(led_cdev, LED_OFF); + led_set_brightness(led_cdev, LED_OFF); } static struct led_trigger timer_led_trigger = { diff --git a/drivers/leds/ledtrig-transient.c b/drivers/leds/ledtrig-transient.c index 83179f435e1..398f1042c43 100644 --- a/drivers/leds/ledtrig-transient.c +++ b/drivers/leds/ledtrig-transient.c @@ -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); diff --git a/include/linux/leds.h b/include/linux/leds.h index 39eee41d8c6..3aade1d8f41 100644 --- a/include/linux/leds.h +++ b/include/linux/leds.h @@ -38,6 +38,9 @@ struct led_classdev { #define LED_SUSPENDED (1 << 0) /* Upper 16 bits reflect control information */ #define LED_CORE_SUSPENDRESUME (1 << 16) +#define LED_BLINK_ONESHOT (1 << 17) +#define LED_BLINK_ONESHOT_STOP (1 << 18) +#define LED_BLINK_INVERT (1 << 19) /* Set LED brightness level */ /* Must not sleep, use a workqueue if needed */ @@ -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 diff --git a/include/linux/platform_data/leds-lm3556.h b/include/linux/platform_data/leds-lm3556.h new file mode 100644 index 00000000000..4b4e7d6b052 --- /dev/null +++ b/include/linux/platform_data/leds-lm3556.h @@ -0,0 +1,50 @@ +/* + * Simple driver for Texas Instruments LM3556 LED Flash driver chip (Rev0x03) + * Copyright (C) 2012 Texas Instruments + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#ifndef __LINUX_LM3556_H +#define __LINUX_LM3556_H + +#define LM3556_NAME "leds-lm3556" + +enum lm3556_pin_polarity { + PIN_LOW_ACTIVE = 0, + PIN_HIGH_ACTIVE, +}; + +enum lm3556_pin_enable { + PIN_DISABLED = 0, + PIN_ENABLED, +}; + +enum lm3556_strobe_usuage { + STROBE_EDGE_DETECT = 0, + STROBE_LEVEL_DETECT, +}; + +enum lm3556_indic_mode { + INDIC_MODE_INTERNAL = 0, + INDIC_MODE_EXTERNAL, +}; + +struct lm3556_platform_data { + enum lm3556_pin_enable torch_pin_en; + enum lm3556_pin_polarity torch_pin_polarity; + + enum lm3556_strobe_usuage strobe_usuage; + enum lm3556_pin_enable strobe_pin_en; + enum lm3556_pin_polarity strobe_pin_polarity; + + enum lm3556_pin_enable tx_pin_en; + enum lm3556_pin_polarity tx_pin_polarity; + + enum lm3556_indic_mode indicator_mode; +}; + +#endif /* __LINUX_LM3556_H */ diff --git a/net/mac80211/led.c b/net/mac80211/led.c index 1bf7903496f..bcffa690312 100644 --- a/net/mac80211/led.c +++ b/net/mac80211/led.c @@ -276,7 +276,7 @@ static void ieee80211_stop_tpt_led_trig(struct ieee80211_local *local) read_lock(&tpt_trig->trig.leddev_list_lock); list_for_each_entry(led_cdev, &tpt_trig->trig.led_cdevs, trig_list) - led_brightness_set(led_cdev, LED_OFF); + led_set_brightness(led_cdev, LED_OFF); read_unlock(&tpt_trig->trig.leddev_list_lock); } |