- LED core improvements:

- use EXPORT_SYMBOL_GPL consistently,
         - add two new LED_BLINK_ flags,
         - rename brightness_set_sync op to brightness_set_blocking,
         - add led_set_brightness_nosleep{nopm} functions,
         - use set_brightness_work for the blocking op,
         - drivers shouldn't enforce SYNC/ASYNC brightness setting,
         - turn off the LED and wait for completion on unregistering LED
           class device,
         - add managed version of led_trigger_register,
         - add description of brightness setting API to the LED class doc.
 
 - Remove work queues from drivers: leds-tlc591xx, leds-88pm860x, leds-adp5520,
         leds-bd2802, leds-blinkm, leds-lm3533, leds-lm3642, leds-pca9532,
         leds-lp3944, leds-lp55xx, leds-lp8788, leds-lp8860, leds-pca955x,
         leds-pca963x, leds-wm831x, leds-da903x, leds-da9052, leds-dac124d085,
         leds-lt3593, leds-max8997, leds-mc13783, leds-regulator, leds-wm8350,
         leds-max77693, leds-aat1290, leds-ktd2692, leds-gpio, leds-pwm,
         leds-lm355x, leds-ns2.
 
 - Replace brightness_set op with a new brightness_set_blocking op to make the
   drivers compatible with led triggers: leds-ipaq-micro, leds-powernv.
 
 - Add missing of_node_put: leds-ktd2692, leds-aat1290, leds-max77693.
 
 - Make the driver explicitly non-modular: ledtrig-cpu, ledtrig-ide-disk,
         leds-syscon.
 
 - Improvements to leds-bcm6328:
         - reuse bcm6328_led_set() instead of copying its functionality,
         - swap LED ON and OFF definitions,
         - improve blink support,
         - simplify duplicated unlock in bcm6328_blink_set,
         - add little endian support,
         - remove unneded lock when checking initial LED status,
         - add HAS_IOMEM dependency,
         - code cleaning.
 
 - Improvements to leds-bcm6358:
         - use bcm6358_led_set() in order to get rid of the lock,
         - merge bcm6358_led_mode and bcm6358_led_set,
         - add little endian support,
         - remove unneded lock when checking initial LED status,
         - add HAS_IOMEM dependency,
         - remove unneeded busy status check.
 
 - Call led_pwm_set() in leds-pwm to enforce default LED_OFF.
 
 - Fix duration to be msec instead of jiffies: ledtrig-transient.
 
 - Removing NULL check: leds-powernv.
 
 - Use platform_register/unregister_drivers(): leds-sunfire.
 
 - Fix module license specification: ledtrig-oneshot.
 
 - Fix driver description and make license match the header: leds-pwm.
 
 - Remove checking for state < 1 in flash_strobe_store(): led-class-flash.
 
 - Use led_set_brightness_sync for torch brightness: v4l2-flash-led-class
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQIcBAABAgAGBQJWkpEGAAoJEL1qUBy3i3wmwKgP/inH3kQ6RZgKw5g2sZtyLYbf
 W9eEXsGNIKGPmkgRXi3JauY5RiWK2vGAWmBLZCeOfnl/TKzOVvotUweT68dPpC9/
 sd4R5aVNJ6o8lOWuZHWvql5HPc99ou8YUquoPZuHsG/JecB+7uIJUqTszq12+s7c
 Y6RY5KyGD/rMmw3QBWxIKxOMqDl9TvywUZp1LMmD2NP1hdWRuN4R3pnJxQbtPQMg
 F/OnXcu4UN+dzgFpNlqBOdoTXypNP8uJhVWWRuzBaDI1UXc1cHeNWyQc4aMM0FOm
 +U3csu/eUNClIIliN6rswXVR/Bi5VJGIj9VfnShf0CpW9ZaNUKTYvk0usHMH/tMQ
 8mBVY84+XE5cLrRgohiuh0kcF+NqepYDRrte7XBFOFq4W14/l09cYWhA4N563y3N
 18beRbNULVFHSk4IKBelnWuCwp2eG3K2JzOkaYay2U3Uxq5eJEK4xiNT36Ay30dK
 +vzEXSX998f/3ZRKHtBm6RYd8aaH2iSs0Z3dA9AjXxea58qc8lRu5/9DfSBFz1jo
 We4Wq7HOIMYJ6taNqfik8N7nIeWPVtOnfZxRGaZESezGR023LFCSLqe3wMkMfNVz
 63Jd6RA81jlaCNV2h0IVZvKyvIaJ8Dp2jCEQViuPEbsYBYmIDkAr+BUK0VpZodwl
 t977bJDXp4ss1NXeT3kC
 =MW8D
 -----END PGP SIGNATURE-----

Merge tag 'leds-for-4.5' of git://git.kernel.org/pub/scm/linux/kernel/git/j.anaszewski/linux-leds

Pull LED subsystem updates from Jacek Anaszewski:
 "Besides regular driver updates, we introduce a portion of LED core
  improvements, that allow to avoid the need for using work queues in
  the LED class drivers, that set brightness in a blocking way.

  Affected LED class drivers are being optimized accordingly.

   - LED core improvements:
        - use EXPORT_SYMBOL_GPL consistently,
        - add two new LED_BLINK_ flags,
        - rename brightness_set_sync op to brightness_set_blocking,
        - add led_set_brightness_nosleep{nopm} functions,
        - use set_brightness_work for the blocking op,
        - drivers shouldn't enforce SYNC/ASYNC brightness setting,
        - turn off the LED and wait for completion on unregistering LED
          class device,
        - add managed version of led_trigger_register,
        - add description of brightness setting API to the LED class doc.

   - Remove work queues from drivers: leds-tlc591xx, leds-88pm860x, leds-adp5520,
        leds-bd2802, leds-blinkm, leds-lm3533, leds-lm3642, leds-pca9532,
        leds-lp3944, leds-lp55xx, leds-lp8788, leds-lp8860, leds-pca955x,
        leds-pca963x, leds-wm831x, leds-da903x, leds-da9052, leds-dac124d085,
        leds-lt3593, leds-max8997, leds-mc13783, leds-regulator, leds-wm8350,
        leds-max77693, leds-aat1290, leds-ktd2692, leds-gpio, leds-pwm,
        leds-lm355x, leds-ns2.

   - Replace brightness_set op with a new brightness_set_blocking op to
     make the drivers compatible with led triggers: leds-ipaq-micro,
     leds-powernv.

   - Add missing of_node_put: leds-ktd2692, leds-aat1290, leds-max77693.

   - Make the driver explicitly non-modular: ledtrig-cpu,
     ledtrig-ide-disk, leds-syscon.

   - Improvements to leds-bcm6328:
        - reuse bcm6328_led_set() instead of copying its functionality,
        - swap LED ON and OFF definitions,
        - improve blink support,
        - simplify duplicated unlock in bcm6328_blink_set,
        - add little endian support,
        - remove unneded lock when checking initial LED status,
        - add HAS_IOMEM dependency,
        - code cleaning.

   - Improvements to leds-bcm6358:
        - use bcm6358_led_set() in order to get rid of the lock,
        - merge bcm6358_led_mode and bcm6358_led_set,
        - add little endian support,
        - remove unneded lock when checking initial LED status,
        - add HAS_IOMEM dependency,
        - remove unneeded busy status check.

   - Call led_pwm_set() in leds-pwm to enforce default LED_OFF.

   - Fix duration to be msec instead of jiffies: ledtrig-transient.

   - Removing NULL check: leds-powernv.

   - Use platform_register/unregister_drivers(): leds-sunfire.

   - Fix module license specification: ledtrig-oneshot.

   - Fix driver description and make license match the header: leds-pwm.

   - Remove checking for state < 1 in flash_strobe_store():
     led-class-flash.

   - Use led_set_brightness_sync for torch brightness:
     v4l2-flash-led-class"

* tag 'leds-for-4.5' of git://git.kernel.org/pub/scm/linux/kernel/git/j.anaszewski/linux-leds: (68 commits)
  leds: add HAS_IOMEM dependency to LEDS_BCM6328/LEDS_BCM6358
  leds: core: add managed version of led_trigger_register
  leds: bcm6358: remove unneeded busy status check
  leds: bcm6328: improve blink support
  leds: bcm6358: merge bcm6358_led_mode and bcm6358_led_set
  leds: bcm6328: simplify duplicated unlock in bcm6328_blink_set
  leds: bcm6358: add little endian support
  leds: bcm6328: add little endian support
  leds: bcm6358: remove unneded lock when checking initial LED status
  leds: bcm6358: Use bcm6358_led_set() in order to get rid of the lock
  leds: bcm6328: remove unneded lock when checking initial LED
  leds: bcm6328: code cleaning
  leds: syscon: Make the driver explicitly non-modular
  leds: ledtrig-ide-disk: Make the driver explicitly non-modular
  leds: ledtrig-cpu: Make the driver explicitly non-modular
  leds: sunfire: Use platform_register/unregister_drivers()
  leds: max77693: Add missing of_node_put
  leds: aat1290: Add missing of_node_put
  leds: powernv: Implement brightness_set_blocking op
  leds: ipaq-micro: Implement brightness_set_blocking op
  ...
This commit is contained in:
Linus Torvalds 2016-01-11 20:40:48 -08:00
commit 2c487121e3
59 changed files with 681 additions and 1150 deletions

View File

@ -52,6 +52,19 @@ above leaves scope for further attributes should they be needed. If sections
of the name don't apply, just leave that section blank. of the name don't apply, just leave that section blank.
Brightness setting API
======================
LED subsystem core exposes following API for setting brightness:
- led_set_brightness : it is guaranteed not to sleep, passing LED_OFF stops
blinking,
- led_set_brightness_sync : for use cases when immediate effect is desired -
it can block the caller for the time required for accessing
device registers and can sleep, passing LED_OFF stops hardware
blinking, returns -EBUSY if software blink fallback is enabled.
Hardware accelerated blink of LEDs Hardware accelerated blink of LEDs
================================== ==================================

View File

@ -52,6 +52,7 @@ config LEDS_AAT1290
config LEDS_BCM6328 config LEDS_BCM6328
tristate "LED Support for Broadcom BCM6328" tristate "LED Support for Broadcom BCM6328"
depends on LEDS_CLASS depends on LEDS_CLASS
depends on HAS_IOMEM
depends on OF depends on OF
help help
This option enables support for LEDs connected to the BCM6328 This option enables support for LEDs connected to the BCM6328
@ -60,6 +61,7 @@ config LEDS_BCM6328
config LEDS_BCM6358 config LEDS_BCM6358
tristate "LED Support for Broadcom BCM6358" tristate "LED Support for Broadcom BCM6358"
depends on LEDS_CLASS depends on LEDS_CLASS
depends on HAS_IOMEM
depends on OF depends on OF
help help
This option enables support for LEDs connected to the BCM6358 This option enables support for LEDs connected to the BCM6358

View File

@ -108,7 +108,7 @@ static ssize_t flash_strobe_store(struct device *dev,
if (ret) if (ret)
goto unlock; goto unlock;
if (state < 0 || state > 1) { if (state > 1) {
ret = -EINVAL; ret = -EINVAL;
goto unlock; goto unlock;
} }
@ -298,7 +298,7 @@ int led_classdev_flash_register(struct device *parent,
led_cdev = &fled_cdev->led_cdev; led_cdev = &fled_cdev->led_cdev;
if (led_cdev->flags & LED_DEV_CAP_FLASH) { if (led_cdev->flags & LED_DEV_CAP_FLASH) {
if (!led_cdev->brightness_set_sync) if (!led_cdev->brightness_set_blocking)
return -EINVAL; return -EINVAL;
ops = fled_cdev->ops; ops = fled_cdev->ops;
@ -316,10 +316,6 @@ int led_classdev_flash_register(struct device *parent,
if (ret < 0) if (ret < 0)
return ret; return ret;
/* Setting a torch brightness needs to have immediate effect */
led_cdev->flags &= ~SET_BRIGHTNESS_ASYNC;
led_cdev->flags |= SET_BRIGHTNESS_SYNC;
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(led_classdev_flash_register); EXPORT_SYMBOL_GPL(led_classdev_flash_register);

View File

@ -109,7 +109,7 @@ static const struct attribute_group *led_groups[] = {
void led_classdev_suspend(struct led_classdev *led_cdev) void led_classdev_suspend(struct led_classdev *led_cdev)
{ {
led_cdev->flags |= LED_SUSPENDED; led_cdev->flags |= LED_SUSPENDED;
led_cdev->brightness_set(led_cdev, 0); led_set_brightness_nopm(led_cdev, 0);
} }
EXPORT_SYMBOL_GPL(led_classdev_suspend); EXPORT_SYMBOL_GPL(led_classdev_suspend);
@ -119,7 +119,7 @@ EXPORT_SYMBOL_GPL(led_classdev_suspend);
*/ */
void led_classdev_resume(struct led_classdev *led_cdev) void led_classdev_resume(struct led_classdev *led_cdev)
{ {
led_cdev->brightness_set(led_cdev, led_cdev->brightness); led_set_brightness_nopm(led_cdev, led_cdev->brightness);
if (led_cdev->flash_resume) if (led_cdev->flash_resume)
led_cdev->flash_resume(led_cdev); led_cdev->flash_resume(led_cdev);
@ -215,8 +215,6 @@ int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
if (!led_cdev->max_brightness) if (!led_cdev->max_brightness)
led_cdev->max_brightness = LED_FULL; led_cdev->max_brightness = LED_FULL;
led_cdev->flags |= SET_BRIGHTNESS_ASYNC;
led_update_brightness(led_cdev); led_update_brightness(led_cdev);
led_init_core(led_cdev); led_init_core(led_cdev);
@ -247,12 +245,13 @@ void led_classdev_unregister(struct led_classdev *led_cdev)
up_write(&led_cdev->trigger_lock); up_write(&led_cdev->trigger_lock);
#endif #endif
cancel_work_sync(&led_cdev->set_brightness_work);
/* Stop blinking */ /* Stop blinking */
led_stop_software_blink(led_cdev); led_stop_software_blink(led_cdev);
led_set_brightness(led_cdev, LED_OFF); led_set_brightness(led_cdev, LED_OFF);
flush_work(&led_cdev->set_brightness_work);
device_unregister(led_cdev->dev); device_unregister(led_cdev->dev);
down_write(&leds_list_lock); down_write(&leds_list_lock);

View File

@ -32,7 +32,7 @@ static void led_timer_function(unsigned long data)
unsigned long delay; unsigned long delay;
if (!led_cdev->blink_delay_on || !led_cdev->blink_delay_off) { if (!led_cdev->blink_delay_on || !led_cdev->blink_delay_off) {
led_set_brightness_async(led_cdev, LED_OFF); led_set_brightness_nosleep(led_cdev, LED_OFF);
return; return;
} }
@ -44,23 +44,23 @@ static void led_timer_function(unsigned long data)
brightness = led_get_brightness(led_cdev); brightness = led_get_brightness(led_cdev);
if (!brightness) { if (!brightness) {
/* Time to switch the LED on. */ /* Time to switch the LED on. */
if (led_cdev->delayed_set_value) {
led_cdev->blink_brightness =
led_cdev->delayed_set_value;
led_cdev->delayed_set_value = 0;
}
brightness = led_cdev->blink_brightness; brightness = led_cdev->blink_brightness;
delay = led_cdev->blink_delay_on; delay = led_cdev->blink_delay_on;
} else { } else {
/* Store the current brightness value to be able /* Store the current brightness value to be able
* to restore it when the delay_off period is over. * to restore it when the delay_off period is over.
* Do it only if there is no pending blink brightness
* change, to avoid overwriting the new value.
*/ */
if (!(led_cdev->flags & LED_BLINK_BRIGHTNESS_CHANGE))
led_cdev->blink_brightness = brightness; led_cdev->blink_brightness = brightness;
else
led_cdev->flags &= ~LED_BLINK_BRIGHTNESS_CHANGE;
brightness = LED_OFF; brightness = LED_OFF;
delay = led_cdev->blink_delay_off; delay = led_cdev->blink_delay_off;
} }
led_set_brightness_async(led_cdev, brightness); led_set_brightness_nosleep(led_cdev, brightness);
/* Return in next iteration if led is in one-shot mode and we are in /* 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 + * the final blink state so that the led is toggled each delay_on +
@ -83,10 +83,24 @@ static void set_brightness_delayed(struct work_struct *ws)
{ {
struct led_classdev *led_cdev = struct led_classdev *led_cdev =
container_of(ws, struct led_classdev, set_brightness_work); container_of(ws, struct led_classdev, set_brightness_work);
int ret = 0;
if (led_cdev->flags & LED_BLINK_DISABLE) {
led_cdev->delayed_set_value = LED_OFF;
led_stop_software_blink(led_cdev); led_stop_software_blink(led_cdev);
led_cdev->flags &= ~LED_BLINK_DISABLE;
}
led_set_brightness_async(led_cdev, led_cdev->delayed_set_value); if (led_cdev->brightness_set)
led_cdev->brightness_set(led_cdev, led_cdev->delayed_set_value);
else if (led_cdev->brightness_set_blocking)
ret = led_cdev->brightness_set_blocking(led_cdev,
led_cdev->delayed_set_value);
else
ret = -ENOTSUPP;
if (ret < 0)
dev_err(led_cdev->dev,
"Setting an LED's brightness failed (%d)\n", ret);
} }
static void led_set_software_blink(struct led_classdev *led_cdev, static void led_set_software_blink(struct led_classdev *led_cdev,
@ -106,13 +120,14 @@ static void led_set_software_blink(struct led_classdev *led_cdev,
/* never on - just set to off */ /* never on - just set to off */
if (!delay_on) { if (!delay_on) {
led_set_brightness_async(led_cdev, LED_OFF); led_set_brightness_nosleep(led_cdev, LED_OFF);
return; return;
} }
/* never off - just set to brightness */ /* never off - just set to brightness */
if (!delay_off) { if (!delay_off) {
led_set_brightness_async(led_cdev, led_cdev->blink_brightness); led_set_brightness_nosleep(led_cdev,
led_cdev->blink_brightness);
return; return;
} }
@ -156,7 +171,7 @@ void led_blink_set(struct led_classdev *led_cdev,
led_blink_setup(led_cdev, delay_on, delay_off); led_blink_setup(led_cdev, delay_on, delay_off);
} }
EXPORT_SYMBOL(led_blink_set); EXPORT_SYMBOL_GPL(led_blink_set);
void led_blink_set_oneshot(struct led_classdev *led_cdev, void led_blink_set_oneshot(struct led_classdev *led_cdev,
unsigned long *delay_on, unsigned long *delay_on,
@ -177,7 +192,7 @@ void led_blink_set_oneshot(struct led_classdev *led_cdev,
led_blink_setup(led_cdev, delay_on, delay_off); led_blink_setup(led_cdev, delay_on, delay_off);
} }
EXPORT_SYMBOL(led_blink_set_oneshot); EXPORT_SYMBOL_GPL(led_blink_set_oneshot);
void led_stop_software_blink(struct led_classdev *led_cdev) void led_stop_software_blink(struct led_classdev *led_cdev)
{ {
@ -190,29 +205,74 @@ EXPORT_SYMBOL_GPL(led_stop_software_blink);
void led_set_brightness(struct led_classdev *led_cdev, void led_set_brightness(struct led_classdev *led_cdev,
enum led_brightness brightness) enum led_brightness brightness)
{ {
int ret = 0; /*
* In case blinking is on delay brightness setting
/* delay brightness if soft-blink is active */ * until the next timer tick.
*/
if (led_cdev->blink_delay_on || led_cdev->blink_delay_off) { if (led_cdev->blink_delay_on || led_cdev->blink_delay_off) {
led_cdev->delayed_set_value = brightness; /*
if (brightness == LED_OFF) * If we need to disable soft blinking delegate this to the
* work queue task to avoid problems in case we are called
* from hard irq context.
*/
if (brightness == LED_OFF) {
led_cdev->flags |= LED_BLINK_DISABLE;
schedule_work(&led_cdev->set_brightness_work); schedule_work(&led_cdev->set_brightness_work);
} else {
led_cdev->flags |= LED_BLINK_BRIGHTNESS_CHANGE;
led_cdev->blink_brightness = brightness;
}
return; return;
} }
if (led_cdev->flags & SET_BRIGHTNESS_ASYNC) { led_set_brightness_nosleep(led_cdev, brightness);
led_set_brightness_async(led_cdev, brightness);
return;
} else if (led_cdev->flags & SET_BRIGHTNESS_SYNC)
ret = led_set_brightness_sync(led_cdev, brightness);
else
ret = -EINVAL;
if (ret < 0)
dev_dbg(led_cdev->dev, "Setting LED brightness failed (%d)\n",
ret);
} }
EXPORT_SYMBOL(led_set_brightness); EXPORT_SYMBOL_GPL(led_set_brightness);
void led_set_brightness_nopm(struct led_classdev *led_cdev,
enum led_brightness value)
{
/* Use brightness_set op if available, it is guaranteed not to sleep */
if (led_cdev->brightness_set) {
led_cdev->brightness_set(led_cdev, value);
return;
}
/* If brightness setting can sleep, delegate it to a work queue task */
led_cdev->delayed_set_value = value;
schedule_work(&led_cdev->set_brightness_work);
}
EXPORT_SYMBOL_GPL(led_set_brightness_nopm);
void led_set_brightness_nosleep(struct led_classdev *led_cdev,
enum led_brightness value)
{
led_cdev->brightness = min(value, led_cdev->max_brightness);
if (led_cdev->flags & LED_SUSPENDED)
return;
led_set_brightness_nopm(led_cdev, led_cdev->brightness);
}
EXPORT_SYMBOL_GPL(led_set_brightness_nosleep);
int led_set_brightness_sync(struct led_classdev *led_cdev,
enum led_brightness value)
{
if (led_cdev->blink_delay_on || led_cdev->blink_delay_off)
return -EBUSY;
led_cdev->brightness = min(value, led_cdev->max_brightness);
if (led_cdev->flags & LED_SUSPENDED)
return 0;
if (led_cdev->brightness_set_blocking)
return led_cdev->brightness_set_blocking(led_cdev,
led_cdev->brightness);
return -ENOTSUPP;
}
EXPORT_SYMBOL_GPL(led_set_brightness_sync);
int led_update_brightness(struct led_classdev *led_cdev) int led_update_brightness(struct led_classdev *led_cdev)
{ {
@ -228,7 +288,7 @@ int led_update_brightness(struct led_classdev *led_cdev)
return ret; return ret;
} }
EXPORT_SYMBOL(led_update_brightness); EXPORT_SYMBOL_GPL(led_update_brightness);
/* Caller must ensure led_cdev->led_access held */ /* Caller must ensure led_cdev->led_access held */
void led_sysfs_disable(struct led_classdev *led_cdev) void led_sysfs_disable(struct led_classdev *led_cdev)

View File

@ -249,6 +249,34 @@ void led_trigger_unregister(struct led_trigger *trig)
} }
EXPORT_SYMBOL_GPL(led_trigger_unregister); EXPORT_SYMBOL_GPL(led_trigger_unregister);
static void devm_led_trigger_release(struct device *dev, void *res)
{
led_trigger_unregister(*(struct led_trigger **)res);
}
int devm_led_trigger_register(struct device *dev,
struct led_trigger *trig)
{
struct led_trigger **dr;
int rc;
dr = devres_alloc(devm_led_trigger_release, sizeof(*dr),
GFP_KERNEL);
if (!dr)
return -ENOMEM;
*dr = trig;
rc = led_trigger_register(trig);
if (rc)
devres_free(dr);
else
devres_add(dev, dr);
return rc;
}
EXPORT_SYMBOL_GPL(devm_led_trigger_register);
/* Simple LED Tigger Interface */ /* Simple LED Tigger Interface */
void led_trigger_event(struct led_trigger *trig, void led_trigger_event(struct led_trigger *trig,

View File

@ -16,7 +16,6 @@
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/leds.h> #include <linux/leds.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/workqueue.h>
#include <linux/mfd/88pm860x.h> #include <linux/mfd/88pm860x.h>
#include <linux/module.h> #include <linux/module.h>
@ -33,7 +32,6 @@
struct pm860x_led { struct pm860x_led {
struct led_classdev cdev; struct led_classdev cdev;
struct i2c_client *i2c; struct i2c_client *i2c;
struct work_struct work;
struct pm860x_chip *chip; struct pm860x_chip *chip;
struct mutex lock; struct mutex lock;
char name[MFD_NAME_SIZE]; char name[MFD_NAME_SIZE];
@ -69,17 +67,18 @@ static int led_power_set(struct pm860x_chip *chip, int port, int on)
return ret; return ret;
} }
static void pm860x_led_work(struct work_struct *work) static int pm860x_led_set(struct led_classdev *cdev,
enum led_brightness value)
{ {
struct pm860x_led *led = container_of(cdev, struct pm860x_led, cdev);
struct pm860x_led *led;
struct pm860x_chip *chip; struct pm860x_chip *chip;
unsigned char buf[3]; unsigned char buf[3];
int ret; int ret;
led = container_of(work, struct pm860x_led, work);
chip = led->chip; chip = led->chip;
mutex_lock(&led->lock); mutex_lock(&led->lock);
led->brightness = value >> 3;
if ((led->current_brightness == 0) && led->brightness) { if ((led->current_brightness == 0) && led->brightness) {
led_power_set(chip, led->port, 1); led_power_set(chip, led->port, 1);
if (led->iset) { if (led->iset) {
@ -112,15 +111,8 @@ static void pm860x_led_work(struct work_struct *work)
dev_dbg(chip->dev, "Update LED. (reg:%d, brightness:%d)\n", dev_dbg(chip->dev, "Update LED. (reg:%d, brightness:%d)\n",
led->reg_control, led->brightness); led->reg_control, led->brightness);
mutex_unlock(&led->lock); mutex_unlock(&led->lock);
}
static void pm860x_led_set(struct led_classdev *cdev, return 0;
enum led_brightness value)
{
struct pm860x_led *data = container_of(cdev, struct pm860x_led, cdev);
data->brightness = value >> 3;
schedule_work(&data->work);
} }
#ifdef CONFIG_OF #ifdef CONFIG_OF
@ -213,9 +205,8 @@ static int pm860x_led_probe(struct platform_device *pdev)
data->current_brightness = 0; data->current_brightness = 0;
data->cdev.name = data->name; data->cdev.name = data->name;
data->cdev.brightness_set = pm860x_led_set; data->cdev.brightness_set_blocking = pm860x_led_set;
mutex_init(&data->lock); mutex_init(&data->lock);
INIT_WORK(&data->work, pm860x_led_work);
ret = led_classdev_register(chip->dev, &data->cdev); ret = led_classdev_register(chip->dev, &data->cdev);
if (ret < 0) { if (ret < 0) {

View File

@ -20,7 +20,6 @@
#include <linux/pinctrl/consumer.h> #include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/workqueue.h>
#include <media/v4l2-flash-led-class.h> #include <media/v4l2-flash-led-class.h>
#define AAT1290_MOVIE_MODE_CURRENT_ADDR 17 #define AAT1290_MOVIE_MODE_CURRENT_ADDR 17
@ -82,8 +81,6 @@ struct aat1290_led {
/* brightness cache */ /* brightness cache */
unsigned int torch_brightness; unsigned int torch_brightness;
/* assures led-triggers compatibility */
struct work_struct work_brightness_set;
}; };
static struct aat1290_led *fled_cdev_to_led( static struct aat1290_led *fled_cdev_to_led(
@ -92,6 +89,12 @@ static struct aat1290_led *fled_cdev_to_led(
return container_of(fled_cdev, struct aat1290_led, fled_cdev); return container_of(fled_cdev, struct aat1290_led, fled_cdev);
} }
static struct led_classdev_flash *led_cdev_to_fled_cdev(
struct led_classdev *led_cdev)
{
return container_of(led_cdev, struct led_classdev_flash, led_cdev);
}
static void aat1290_as2cwire_write(struct aat1290_led *led, int addr, int value) static void aat1290_as2cwire_write(struct aat1290_led *led, int addr, int value)
{ {
int i; int i;
@ -134,9 +137,14 @@ static void aat1290_set_flash_safety_timer(struct aat1290_led *led,
flash_tm_reg); flash_tm_reg);
} }
static void aat1290_brightness_set(struct aat1290_led *led, /* LED subsystem callbacks */
static int aat1290_led_brightness_set(struct led_classdev *led_cdev,
enum led_brightness brightness) enum led_brightness brightness)
{ {
struct led_classdev_flash *fled_cdev = led_cdev_to_fled_cdev(led_cdev);
struct aat1290_led *led = fled_cdev_to_led(fled_cdev);
mutex_lock(&led->lock); mutex_lock(&led->lock);
if (brightness == 0) { if (brightness == 0) {
@ -158,35 +166,6 @@ static void aat1290_brightness_set(struct aat1290_led *led,
} }
mutex_unlock(&led->lock); mutex_unlock(&led->lock);
}
/* LED subsystem callbacks */
static void aat1290_brightness_set_work(struct work_struct *work)
{
struct aat1290_led *led =
container_of(work, struct aat1290_led, work_brightness_set);
aat1290_brightness_set(led, led->torch_brightness);
}
static void aat1290_led_brightness_set(struct led_classdev *led_cdev,
enum led_brightness brightness)
{
struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
struct aat1290_led *led = fled_cdev_to_led(fled_cdev);
led->torch_brightness = brightness;
schedule_work(&led->work_brightness_set);
}
static int aat1290_led_brightness_set_sync(struct led_classdev *led_cdev,
enum led_brightness brightness)
{
struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
struct aat1290_led *led = fled_cdev_to_led(fled_cdev);
aat1290_brightness_set(led, brightness);
return 0; return 0;
} }
@ -296,7 +275,7 @@ static int aat1290_led_parse_dt(struct aat1290_led *led,
if (ret < 0) { if (ret < 0) {
dev_err(dev, dev_err(dev,
"flash-max-microamp DT property missing\n"); "flash-max-microamp DT property missing\n");
return ret; goto err_parse_dt;
} }
ret = of_property_read_u32(child_node, "flash-max-timeout-us", ret = of_property_read_u32(child_node, "flash-max-timeout-us",
@ -304,13 +283,14 @@ static int aat1290_led_parse_dt(struct aat1290_led *led,
if (ret < 0) { if (ret < 0) {
dev_err(dev, dev_err(dev,
"flash-max-timeout-us DT property missing\n"); "flash-max-timeout-us DT property missing\n");
return ret; goto err_parse_dt;
} }
of_node_put(child_node);
*sub_node = child_node; *sub_node = child_node;
err_parse_dt:
of_node_put(child_node);
return ret; return ret;
} }
@ -509,11 +489,9 @@ static int aat1290_led_probe(struct platform_device *pdev)
mutex_init(&led->lock); mutex_init(&led->lock);
/* Initialize LED Flash class device */ /* Initialize LED Flash class device */
led_cdev->brightness_set = aat1290_led_brightness_set; led_cdev->brightness_set_blocking = aat1290_led_brightness_set;
led_cdev->brightness_set_sync = aat1290_led_brightness_set_sync;
led_cdev->max_brightness = led_cfg.max_brightness; led_cdev->max_brightness = led_cfg.max_brightness;
led_cdev->flags |= LED_DEV_CAP_FLASH; led_cdev->flags |= LED_DEV_CAP_FLASH;
INIT_WORK(&led->work_brightness_set, aat1290_brightness_set_work);
aat1290_init_flash_timeout(led, &led_cfg); aat1290_init_flash_timeout(led, &led_cfg);
@ -548,7 +526,6 @@ static int aat1290_led_remove(struct platform_device *pdev)
v4l2_flash_release(led->v4l2_flash); v4l2_flash_release(led->v4l2_flash);
led_classdev_flash_unregister(&led->fled_cdev); led_classdev_flash_unregister(&led->fled_cdev);
cancel_work_sync(&led->work_brightness_set);
mutex_destroy(&led->lock); mutex_destroy(&led->lock);

View File

@ -17,34 +17,24 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/leds.h> #include <linux/leds.h>
#include <linux/workqueue.h>
#include <linux/mfd/adp5520.h> #include <linux/mfd/adp5520.h>
#include <linux/slab.h> #include <linux/slab.h>
struct adp5520_led { struct adp5520_led {
struct led_classdev cdev; struct led_classdev cdev;
struct work_struct work;
struct device *master; struct device *master;
enum led_brightness new_brightness;
int id; int id;
int flags; int flags;
}; };
static void adp5520_led_work(struct work_struct *work) static int adp5520_led_set(struct led_classdev *led_cdev,
{
struct adp5520_led *led = container_of(work, struct adp5520_led, work);
adp5520_write(led->master, ADP5520_LED1_CURRENT + led->id - 1,
led->new_brightness >> 2);
}
static void adp5520_led_set(struct led_classdev *led_cdev,
enum led_brightness value) enum led_brightness value)
{ {
struct adp5520_led *led; struct adp5520_led *led;
led = container_of(led_cdev, struct adp5520_led, cdev); led = container_of(led_cdev, struct adp5520_led, cdev);
led->new_brightness = value; return adp5520_write(led->master, ADP5520_LED1_CURRENT + led->id - 1,
schedule_work(&led->work); value >> 2);
} }
static int adp5520_led_setup(struct adp5520_led *led) static int adp5520_led_setup(struct adp5520_led *led)
@ -135,7 +125,7 @@ static int adp5520_led_probe(struct platform_device *pdev)
led_dat->cdev.name = cur_led->name; led_dat->cdev.name = cur_led->name;
led_dat->cdev.default_trigger = cur_led->default_trigger; led_dat->cdev.default_trigger = cur_led->default_trigger;
led_dat->cdev.brightness_set = adp5520_led_set; led_dat->cdev.brightness_set_blocking = adp5520_led_set;
led_dat->cdev.brightness = LED_OFF; led_dat->cdev.brightness = LED_OFF;
if (cur_led->flags & ADP5520_FLAG_LED_MASK) if (cur_led->flags & ADP5520_FLAG_LED_MASK)
@ -146,9 +136,6 @@ static int adp5520_led_probe(struct platform_device *pdev)
led_dat->id = led_dat->flags & ADP5520_FLAG_LED_MASK; led_dat->id = led_dat->flags & ADP5520_FLAG_LED_MASK;
led_dat->master = pdev->dev.parent; led_dat->master = pdev->dev.parent;
led_dat->new_brightness = LED_OFF;
INIT_WORK(&led_dat->work, adp5520_led_work);
ret = led_classdev_register(led_dat->master, &led_dat->cdev); ret = led_classdev_register(led_dat->master, &led_dat->cdev);
if (ret) { if (ret) {
@ -170,10 +157,8 @@ static int adp5520_led_probe(struct platform_device *pdev)
err: err:
if (i > 0) { if (i > 0) {
for (i = i - 1; i >= 0; i--) { for (i = i - 1; i >= 0; i--)
led_classdev_unregister(&led[i].cdev); led_classdev_unregister(&led[i].cdev);
cancel_work_sync(&led[i].work);
}
} }
return ret; return ret;
@ -192,7 +177,6 @@ static int adp5520_led_remove(struct platform_device *pdev)
for (i = 0; i < pdata->num_leds; i++) { for (i = 0; i < pdata->num_leds; i++) {
led_classdev_unregister(&led[i].cdev); led_classdev_unregister(&led[i].cdev);
cancel_work_sync(&led[i].work);
} }
return 0; return 0;

View File

@ -48,10 +48,10 @@
BCM6328_SERIAL_LED_SHIFT_DIR) BCM6328_SERIAL_LED_SHIFT_DIR)
#define BCM6328_LED_MODE_MASK 3 #define BCM6328_LED_MODE_MASK 3
#define BCM6328_LED_MODE_OFF 0 #define BCM6328_LED_MODE_ON 0
#define BCM6328_LED_MODE_FAST 1 #define BCM6328_LED_MODE_FAST 1
#define BCM6328_LED_MODE_BLINK 2 #define BCM6328_LED_MODE_BLINK 2
#define BCM6328_LED_MODE_ON 3 #define BCM6328_LED_MODE_OFF 3
#define BCM6328_LED_SHIFT(X) ((X) << 1) #define BCM6328_LED_SHIFT(X) ((X) << 1)
/** /**
@ -76,12 +76,20 @@ struct bcm6328_led {
static void bcm6328_led_write(void __iomem *reg, unsigned long data) static void bcm6328_led_write(void __iomem *reg, unsigned long data)
{ {
#ifdef CONFIG_CPU_BIG_ENDIAN
iowrite32be(data, reg); iowrite32be(data, reg);
#else
writel(data, reg);
#endif
} }
static unsigned long bcm6328_led_read(void __iomem *reg) static unsigned long bcm6328_led_read(void __iomem *reg)
{ {
#ifdef CONFIG_CPU_BIG_ENDIAN
return ioread32be(reg); return ioread32be(reg);
#else
return readl(reg);
#endif
} }
/** /**
@ -126,34 +134,45 @@ static void bcm6328_led_set(struct led_classdev *led_cdev,
*(led->blink_leds) &= ~BIT(led->pin); *(led->blink_leds) &= ~BIT(led->pin);
if ((led->active_low && value == LED_OFF) || if ((led->active_low && value == LED_OFF) ||
(!led->active_low && value != LED_OFF)) (!led->active_low && value != LED_OFF))
bcm6328_led_mode(led, BCM6328_LED_MODE_OFF);
else
bcm6328_led_mode(led, BCM6328_LED_MODE_ON); bcm6328_led_mode(led, BCM6328_LED_MODE_ON);
else
bcm6328_led_mode(led, BCM6328_LED_MODE_OFF);
spin_unlock_irqrestore(led->lock, flags); spin_unlock_irqrestore(led->lock, flags);
} }
static unsigned long bcm6328_blink_delay(unsigned long delay)
{
unsigned long bcm6328_delay;
bcm6328_delay = delay + BCM6328_LED_INTERVAL_MS / 2;
bcm6328_delay = bcm6328_delay / BCM6328_LED_INTERVAL_MS;
if (bcm6328_delay == 0)
bcm6328_delay = 1;
return bcm6328_delay;
}
static int bcm6328_blink_set(struct led_classdev *led_cdev, static int bcm6328_blink_set(struct led_classdev *led_cdev,
unsigned long *delay_on, unsigned long *delay_off) unsigned long *delay_on, unsigned long *delay_off)
{ {
struct bcm6328_led *led = struct bcm6328_led *led =
container_of(led_cdev, struct bcm6328_led, cdev); container_of(led_cdev, struct bcm6328_led, cdev);
unsigned long delay, flags; unsigned long delay, flags;
int rc;
if (!*delay_on) if (!*delay_on)
*delay_on = BCM6328_LED_DEF_DELAY; *delay_on = BCM6328_LED_DEF_DELAY;
if (!*delay_off) if (!*delay_off)
*delay_off = BCM6328_LED_DEF_DELAY; *delay_off = BCM6328_LED_DEF_DELAY;
if (*delay_on != *delay_off) { delay = bcm6328_blink_delay(*delay_on);
if (delay != bcm6328_blink_delay(*delay_off)) {
dev_dbg(led_cdev->dev, dev_dbg(led_cdev->dev,
"fallback to soft blinking (delay_on != delay_off)\n"); "fallback to soft blinking (delay_on != delay_off)\n");
return -EINVAL; return -EINVAL;
} }
delay = *delay_on / BCM6328_LED_INTERVAL_MS; if (delay > BCM6328_LED_INTV_MASK) {
if (delay == 0)
delay = 1;
else if (delay > BCM6328_LED_INTV_MASK) {
dev_dbg(led_cdev->dev, dev_dbg(led_cdev->dev,
"fallback to soft blinking (delay > %ums)\n", "fallback to soft blinking (delay > %ums)\n",
BCM6328_LED_INTV_MASK * BCM6328_LED_INTERVAL_MS); BCM6328_LED_INTV_MASK * BCM6328_LED_INTERVAL_MS);
@ -175,16 +194,15 @@ static int bcm6328_blink_set(struct led_classdev *led_cdev,
bcm6328_led_write(led->mem + BCM6328_REG_INIT, val); bcm6328_led_write(led->mem + BCM6328_REG_INIT, val);
bcm6328_led_mode(led, BCM6328_LED_MODE_BLINK); bcm6328_led_mode(led, BCM6328_LED_MODE_BLINK);
rc = 0;
spin_unlock_irqrestore(led->lock, flags);
} else { } else {
spin_unlock_irqrestore(led->lock, flags);
dev_dbg(led_cdev->dev, dev_dbg(led_cdev->dev,
"fallback to soft blinking (delay already set)\n"); "fallback to soft blinking (delay already set)\n");
return -EINVAL; rc = -EINVAL;
} }
spin_unlock_irqrestore(led->lock, flags);
return 0; return rc;
} }
static int bcm6328_hwled(struct device *dev, struct device_node *nc, u32 reg, static int bcm6328_hwled(struct device *dev, struct device_node *nc, u32 reg,
@ -264,7 +282,6 @@ static int bcm6328_led(struct device *dev, struct device_node *nc, u32 reg,
unsigned long *blink_leds, unsigned long *blink_delay) unsigned long *blink_leds, unsigned long *blink_delay)
{ {
struct bcm6328_led *led; struct bcm6328_led *led;
unsigned long flags;
const char *state; const char *state;
int rc; int rc;
@ -286,7 +303,6 @@ static int bcm6328_led(struct device *dev, struct device_node *nc, u32 reg,
"linux,default-trigger", "linux,default-trigger",
NULL); NULL);
spin_lock_irqsave(lock, flags);
if (!of_property_read_string(nc, "default-state", &state)) { if (!of_property_read_string(nc, "default-state", &state)) {
if (!strcmp(state, "on")) { if (!strcmp(state, "on")) {
led->cdev.brightness = LED_FULL; led->cdev.brightness = LED_FULL;
@ -303,8 +319,8 @@ static int bcm6328_led(struct device *dev, struct device_node *nc, u32 reg,
val = bcm6328_led_read(mode) >> val = bcm6328_led_read(mode) >>
BCM6328_LED_SHIFT(shift % 16); BCM6328_LED_SHIFT(shift % 16);
val &= BCM6328_LED_MODE_MASK; val &= BCM6328_LED_MODE_MASK;
if ((led->active_low && val == BCM6328_LED_MODE_ON) || if ((led->active_low && val == BCM6328_LED_MODE_OFF) ||
(!led->active_low && val == BCM6328_LED_MODE_OFF)) (!led->active_low && val == BCM6328_LED_MODE_ON))
led->cdev.brightness = LED_FULL; led->cdev.brightness = LED_FULL;
else else
led->cdev.brightness = LED_OFF; led->cdev.brightness = LED_OFF;
@ -315,12 +331,7 @@ static int bcm6328_led(struct device *dev, struct device_node *nc, u32 reg,
led->cdev.brightness = LED_OFF; led->cdev.brightness = LED_OFF;
} }
if ((led->active_low && led->cdev.brightness == LED_FULL) || bcm6328_led_set(&led->cdev, led->cdev.brightness);
(!led->active_low && led->cdev.brightness == LED_OFF))
bcm6328_led_mode(led, BCM6328_LED_MODE_ON);
else
bcm6328_led_mode(led, BCM6328_LED_MODE_OFF);
spin_unlock_irqrestore(lock, flags);
led->cdev.brightness_set = bcm6328_led_set; led->cdev.brightness_set = bcm6328_led_set;
led->cdev.blink_set = bcm6328_blink_set; led->cdev.blink_set = bcm6328_blink_set;
@ -341,7 +352,7 @@ static int bcm6328_leds_probe(struct platform_device *pdev)
struct device_node *child; struct device_node *child;
struct resource *mem_r; struct resource *mem_r;
void __iomem *mem; void __iomem *mem;
spinlock_t *lock; spinlock_t *lock; /* memory lock */
unsigned long val, *blink_leds, *blink_delay; unsigned long val, *blink_leds, *blink_delay;
mem_r = platform_get_resource(pdev, IORESOURCE_MEM, 0); mem_r = platform_get_resource(pdev, IORESOURCE_MEM, 0);

View File

@ -49,12 +49,20 @@ struct bcm6358_led {
static void bcm6358_led_write(void __iomem *reg, unsigned long data) static void bcm6358_led_write(void __iomem *reg, unsigned long data)
{ {
#ifdef CONFIG_CPU_BIG_ENDIAN
iowrite32be(data, reg); iowrite32be(data, reg);
#else
writel(data, reg);
#endif
} }
static unsigned long bcm6358_led_read(void __iomem *reg) static unsigned long bcm6358_led_read(void __iomem *reg)
{ {
#ifdef CONFIG_CPU_BIG_ENDIAN
return ioread32be(reg); return ioread32be(reg);
#else
return readl(reg);
#endif
} }
static unsigned long bcm6358_led_busy(void __iomem *mem) static unsigned long bcm6358_led_busy(void __iomem *mem)
@ -68,12 +76,15 @@ static unsigned long bcm6358_led_busy(void __iomem *mem)
return val; return val;
} }
static void bcm6358_led_mode(struct bcm6358_led *led, unsigned long value) static void bcm6358_led_set(struct led_classdev *led_cdev,
enum led_brightness value)
{ {
unsigned long val; struct bcm6358_led *led =
container_of(led_cdev, struct bcm6358_led, cdev);
unsigned long flags, val;
spin_lock_irqsave(led->lock, flags);
bcm6358_led_busy(led->mem); bcm6358_led_busy(led->mem);
val = bcm6358_led_read(led->mem + BCM6358_REG_MODE); val = bcm6358_led_read(led->mem + BCM6358_REG_MODE);
if ((led->active_low && value == LED_OFF) || if ((led->active_low && value == LED_OFF) ||
(!led->active_low && value != LED_OFF)) (!led->active_low && value != LED_OFF))
@ -81,17 +92,6 @@ static void bcm6358_led_mode(struct bcm6358_led *led, unsigned long value)
else else
val &= ~(BIT(led->pin)); val &= ~(BIT(led->pin));
bcm6358_led_write(led->mem + BCM6358_REG_MODE, val); bcm6358_led_write(led->mem + BCM6358_REG_MODE, val);
}
static void bcm6358_led_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
struct bcm6358_led *led =
container_of(led_cdev, struct bcm6358_led, cdev);
unsigned long flags;
spin_lock_irqsave(led->lock, flags);
bcm6358_led_mode(led, value);
spin_unlock_irqrestore(led->lock, flags); spin_unlock_irqrestore(led->lock, flags);
} }
@ -99,7 +99,6 @@ static int bcm6358_led(struct device *dev, struct device_node *nc, u32 reg,
void __iomem *mem, spinlock_t *lock) void __iomem *mem, spinlock_t *lock)
{ {
struct bcm6358_led *led; struct bcm6358_led *led;
unsigned long flags;
const char *state; const char *state;
int rc; int rc;
@ -119,15 +118,11 @@ static int bcm6358_led(struct device *dev, struct device_node *nc, u32 reg,
"linux,default-trigger", "linux,default-trigger",
NULL); NULL);
spin_lock_irqsave(lock, flags);
if (!of_property_read_string(nc, "default-state", &state)) { if (!of_property_read_string(nc, "default-state", &state)) {
if (!strcmp(state, "on")) { if (!strcmp(state, "on")) {
led->cdev.brightness = LED_FULL; led->cdev.brightness = LED_FULL;
} else if (!strcmp(state, "keep")) { } else if (!strcmp(state, "keep")) {
unsigned long val; unsigned long val;
bcm6358_led_busy(led->mem);
val = bcm6358_led_read(led->mem + BCM6358_REG_MODE); val = bcm6358_led_read(led->mem + BCM6358_REG_MODE);
val &= BIT(led->pin); val &= BIT(led->pin);
if ((led->active_low && !val) || if ((led->active_low && !val) ||
@ -141,8 +136,8 @@ static int bcm6358_led(struct device *dev, struct device_node *nc, u32 reg,
} else { } else {
led->cdev.brightness = LED_OFF; led->cdev.brightness = LED_OFF;
} }
bcm6358_led_mode(led, led->cdev.brightness);
spin_unlock_irqrestore(lock, flags); bcm6358_led_set(&led->cdev, led->cdev.brightness);
led->cdev.brightness_set = bcm6358_led_set; led->cdev.brightness_set = bcm6358_led_set;

View File

@ -72,7 +72,6 @@ struct bd2802_led {
struct bd2802_led_platform_data *pdata; struct bd2802_led_platform_data *pdata;
struct i2c_client *client; struct i2c_client *client;
struct rw_semaphore rwsem; struct rw_semaphore rwsem;
struct work_struct work;
struct led_state led[2]; struct led_state led[2];
@ -518,29 +517,22 @@ static struct device_attribute *bd2802_attributes[] = {
&bd2802_rgb_current_attr, &bd2802_rgb_current_attr,
}; };
static void bd2802_led_work(struct work_struct *work)
{
struct bd2802_led *led = container_of(work, struct bd2802_led, work);
if (led->state)
bd2802_turn_on(led, led->led_id, led->color, led->state);
else
bd2802_turn_off(led, led->led_id, led->color);
}
#define BD2802_CONTROL_RGBS(name, id, clr) \ #define BD2802_CONTROL_RGBS(name, id, clr) \
static void bd2802_set_##name##_brightness(struct led_classdev *led_cdev,\ static int bd2802_set_##name##_brightness(struct led_classdev *led_cdev,\
enum led_brightness value) \ enum led_brightness value) \
{ \ { \
struct bd2802_led *led = \ struct bd2802_led *led = \
container_of(led_cdev, struct bd2802_led, cdev_##name); \ container_of(led_cdev, struct bd2802_led, cdev_##name); \
led->led_id = id; \ led->led_id = id; \
led->color = clr; \ led->color = clr; \
if (value == LED_OFF) \ if (value == LED_OFF) { \
led->state = BD2802_OFF; \ led->state = BD2802_OFF; \
else \ bd2802_turn_off(led, led->led_id, led->color); \
} else { \
led->state = BD2802_ON; \ led->state = BD2802_ON; \
schedule_work(&led->work); \ bd2802_turn_on(led, led->led_id, led->color, BD2802_ON);\
} \
return 0; \
} \ } \
static int bd2802_set_##name##_blink(struct led_classdev *led_cdev, \ static int bd2802_set_##name##_blink(struct led_classdev *led_cdev, \
unsigned long *delay_on, unsigned long *delay_off) \ unsigned long *delay_on, unsigned long *delay_off) \
@ -552,7 +544,7 @@ static int bd2802_set_##name##_blink(struct led_classdev *led_cdev, \
led->led_id = id; \ led->led_id = id; \
led->color = clr; \ led->color = clr; \
led->state = BD2802_BLINK; \ led->state = BD2802_BLINK; \
schedule_work(&led->work); \ bd2802_turn_on(led, led->led_id, led->color, BD2802_BLINK); \
return 0; \ return 0; \
} }
@ -567,11 +559,9 @@ static int bd2802_register_led_classdev(struct bd2802_led *led)
{ {
int ret; int ret;
INIT_WORK(&led->work, bd2802_led_work);
led->cdev_led1r.name = "led1_R"; led->cdev_led1r.name = "led1_R";
led->cdev_led1r.brightness = LED_OFF; led->cdev_led1r.brightness = LED_OFF;
led->cdev_led1r.brightness_set = bd2802_set_led1r_brightness; led->cdev_led1r.brightness_set_blocking = bd2802_set_led1r_brightness;
led->cdev_led1r.blink_set = bd2802_set_led1r_blink; led->cdev_led1r.blink_set = bd2802_set_led1r_blink;
ret = led_classdev_register(&led->client->dev, &led->cdev_led1r); ret = led_classdev_register(&led->client->dev, &led->cdev_led1r);
@ -583,7 +573,7 @@ static int bd2802_register_led_classdev(struct bd2802_led *led)
led->cdev_led1g.name = "led1_G"; led->cdev_led1g.name = "led1_G";
led->cdev_led1g.brightness = LED_OFF; led->cdev_led1g.brightness = LED_OFF;
led->cdev_led1g.brightness_set = bd2802_set_led1g_brightness; led->cdev_led1g.brightness_set_blocking = bd2802_set_led1g_brightness;
led->cdev_led1g.blink_set = bd2802_set_led1g_blink; led->cdev_led1g.blink_set = bd2802_set_led1g_blink;
ret = led_classdev_register(&led->client->dev, &led->cdev_led1g); ret = led_classdev_register(&led->client->dev, &led->cdev_led1g);
@ -595,7 +585,7 @@ static int bd2802_register_led_classdev(struct bd2802_led *led)
led->cdev_led1b.name = "led1_B"; led->cdev_led1b.name = "led1_B";
led->cdev_led1b.brightness = LED_OFF; led->cdev_led1b.brightness = LED_OFF;
led->cdev_led1b.brightness_set = bd2802_set_led1b_brightness; led->cdev_led1b.brightness_set_blocking = bd2802_set_led1b_brightness;
led->cdev_led1b.blink_set = bd2802_set_led1b_blink; led->cdev_led1b.blink_set = bd2802_set_led1b_blink;
ret = led_classdev_register(&led->client->dev, &led->cdev_led1b); ret = led_classdev_register(&led->client->dev, &led->cdev_led1b);
@ -607,7 +597,7 @@ static int bd2802_register_led_classdev(struct bd2802_led *led)
led->cdev_led2r.name = "led2_R"; led->cdev_led2r.name = "led2_R";
led->cdev_led2r.brightness = LED_OFF; led->cdev_led2r.brightness = LED_OFF;
led->cdev_led2r.brightness_set = bd2802_set_led2r_brightness; led->cdev_led2r.brightness_set_blocking = bd2802_set_led2r_brightness;
led->cdev_led2r.blink_set = bd2802_set_led2r_blink; led->cdev_led2r.blink_set = bd2802_set_led2r_blink;
ret = led_classdev_register(&led->client->dev, &led->cdev_led2r); ret = led_classdev_register(&led->client->dev, &led->cdev_led2r);
@ -619,7 +609,7 @@ static int bd2802_register_led_classdev(struct bd2802_led *led)
led->cdev_led2g.name = "led2_G"; led->cdev_led2g.name = "led2_G";
led->cdev_led2g.brightness = LED_OFF; led->cdev_led2g.brightness = LED_OFF;
led->cdev_led2g.brightness_set = bd2802_set_led2g_brightness; led->cdev_led2g.brightness_set_blocking = bd2802_set_led2g_brightness;
led->cdev_led2g.blink_set = bd2802_set_led2g_blink; led->cdev_led2g.blink_set = bd2802_set_led2g_blink;
ret = led_classdev_register(&led->client->dev, &led->cdev_led2g); ret = led_classdev_register(&led->client->dev, &led->cdev_led2g);
@ -631,7 +621,7 @@ static int bd2802_register_led_classdev(struct bd2802_led *led)
led->cdev_led2b.name = "led2_B"; led->cdev_led2b.name = "led2_B";
led->cdev_led2b.brightness = LED_OFF; led->cdev_led2b.brightness = LED_OFF;
led->cdev_led2b.brightness_set = bd2802_set_led2b_brightness; led->cdev_led2b.brightness_set_blocking = bd2802_set_led2b_brightness;
led->cdev_led2b.blink_set = bd2802_set_led2b_blink; led->cdev_led2b.blink_set = bd2802_set_led2b_blink;
led->cdev_led2b.flags |= LED_CORE_SUSPENDRESUME; led->cdev_led2b.flags |= LED_CORE_SUSPENDRESUME;
@ -661,7 +651,6 @@ failed_unregister_led1_R:
static void bd2802_unregister_led_classdev(struct bd2802_led *led) static void bd2802_unregister_led_classdev(struct bd2802_led *led)
{ {
cancel_work_sync(&led->work);
led_classdev_unregister(&led->cdev_led2b); led_classdev_unregister(&led->cdev_led2b);
led_classdev_unregister(&led->cdev_led2g); led_classdev_unregister(&led->cdev_led2g);
led_classdev_unregister(&led->cdev_led2r); led_classdev_unregister(&led->cdev_led2r);

View File

@ -39,16 +39,9 @@ struct blinkm_led {
struct i2c_client *i2c_client; struct i2c_client *i2c_client;
struct led_classdev led_cdev; struct led_classdev led_cdev;
int id; 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 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 blinkm_data {
struct i2c_client *i2c_client; struct i2c_client *i2c_client;
@ -439,65 +432,30 @@ static int blinkm_transfer_hw(struct i2c_client *client, int cmd)
return 0; 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, static int blinkm_led_common_set(struct led_classdev *led_cdev,
enum led_brightness value, int color) enum led_brightness value, int color)
{ {
/* led_brightness is 0, 127 or 255 - we just use it here as-is */ /* 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_led *led = cdev_to_blmled(led_cdev);
struct blinkm_data *data = i2c_get_clientdata(led->i2c_client); struct blinkm_data *data = i2c_get_clientdata(led->i2c_client);
struct blinkm_work *bl_work;
switch (color) { switch (color) {
case RED: case RED:
/* bail out if there's no change */ /* bail out if there's no change */
if (data->next_red == (u8) value) if (data->next_red == (u8) value)
return 0; 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; data->next_red = (u8) value;
break; break;
case GREEN: case GREEN:
/* bail out if there's no change */ /* bail out if there's no change */
if (data->next_green == (u8) value) if (data->next_green == (u8) value)
return 0; 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; data->next_green = (u8) value;
break; break;
case BLUE: case BLUE:
/* bail out if there's no change */ /* bail out if there's no change */
if (data->next_blue == (u8) value) if (data->next_blue == (u8) value)
return 0; 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; data->next_blue = (u8) value;
break; break;
@ -506,42 +464,31 @@ static int blinkm_led_common_set(struct led_classdev *led_cdev,
return -EINVAL; return -EINVAL;
} }
bl_work = kzalloc(sizeof(*bl_work), GFP_ATOMIC); blinkm_transfer_hw(led->i2c_client, BLM_GO_RGB);
if (!bl_work)
return -ENOMEM;
atomic_inc(&led->active);
dev_dbg(&led->i2c_client->dev, dev_dbg(&led->i2c_client->dev,
"#TO_SCHED# next_red = %d, next_green = %d," "# DONE # next_red = %d, next_green = %d,"
" next_blue = %d, active = %d\n", " next_blue = %d\n",
data->next_red, data->next_green, data->next_red, data->next_green,
data->next_blue, atomic_read(&led->active)); data->next_blue);
/* 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; return 0;
} }
static void blinkm_led_red_set(struct led_classdev *led_cdev, static int blinkm_led_red_set(struct led_classdev *led_cdev,
enum led_brightness value) enum led_brightness value)
{ {
blinkm_led_common_set(led_cdev, value, RED); return blinkm_led_common_set(led_cdev, value, RED);
} }
static void blinkm_led_green_set(struct led_classdev *led_cdev, static int blinkm_led_green_set(struct led_classdev *led_cdev,
enum led_brightness value) enum led_brightness value)
{ {
blinkm_led_common_set(led_cdev, value, GREEN); return blinkm_led_common_set(led_cdev, value, GREEN);
} }
static void blinkm_led_blue_set(struct led_classdev *led_cdev, static int blinkm_led_blue_set(struct led_classdev *led_cdev,
enum led_brightness value) enum led_brightness value)
{ {
blinkm_led_common_set(led_cdev, value, BLUE); return blinkm_led_common_set(led_cdev, value, BLUE);
} }
static void blinkm_init_hw(struct i2c_client *client) static void blinkm_init_hw(struct i2c_client *client)
@ -669,7 +616,6 @@ static int blinkm_probe(struct i2c_client *client,
led[i]->id = i; led[i]->id = i;
led[i]->led_cdev.max_brightness = 255; led[i]->led_cdev.max_brightness = 255;
led[i]->led_cdev.flags = LED_CORE_SUSPENDRESUME; led[i]->led_cdev.flags = LED_CORE_SUSPENDRESUME;
atomic_set(&led[i]->active, 0);
switch (i) { switch (i) {
case RED: case RED:
snprintf(blinkm_led_name, sizeof(blinkm_led_name), snprintf(blinkm_led_name, sizeof(blinkm_led_name),
@ -677,7 +623,8 @@ static int blinkm_probe(struct i2c_client *client,
client->adapter->nr, client->adapter->nr,
client->addr); client->addr);
led[i]->led_cdev.name = blinkm_led_name; led[i]->led_cdev.name = blinkm_led_name;
led[i]->led_cdev.brightness_set = blinkm_led_red_set; led[i]->led_cdev.brightness_set_blocking =
blinkm_led_red_set;
err = led_classdev_register(&client->dev, err = led_classdev_register(&client->dev,
&led[i]->led_cdev); &led[i]->led_cdev);
if (err < 0) { if (err < 0) {
@ -693,7 +640,8 @@ static int blinkm_probe(struct i2c_client *client,
client->adapter->nr, client->adapter->nr,
client->addr); client->addr);
led[i]->led_cdev.name = blinkm_led_name; led[i]->led_cdev.name = blinkm_led_name;
led[i]->led_cdev.brightness_set = blinkm_led_green_set; led[i]->led_cdev.brightness_set_blocking =
blinkm_led_green_set;
err = led_classdev_register(&client->dev, err = led_classdev_register(&client->dev,
&led[i]->led_cdev); &led[i]->led_cdev);
if (err < 0) { if (err < 0) {
@ -709,7 +657,8 @@ static int blinkm_probe(struct i2c_client *client,
client->adapter->nr, client->adapter->nr,
client->addr); client->addr);
led[i]->led_cdev.name = blinkm_led_name; led[i]->led_cdev.name = blinkm_led_name;
led[i]->led_cdev.brightness_set = blinkm_led_blue_set; led[i]->led_cdev.brightness_set_blocking =
blinkm_led_blue_set;
err = led_classdev_register(&client->dev, err = led_classdev_register(&client->dev,
&led[i]->led_cdev); &led[i]->led_cdev);
if (err < 0) { if (err < 0) {
@ -746,10 +695,8 @@ static int blinkm_remove(struct i2c_client *client)
int i; int i;
/* make sure no workqueue entries are pending */ /* make sure no workqueue entries are pending */
for (i = 0; i < 3; i++) { for (i = 0; i < 3; i++)
flush_scheduled_work();
led_classdev_unregister(&data->blinkm_leds[i].led_cdev); led_classdev_unregister(&data->blinkm_leds[i].led_cdev);
}
/* reset rgb */ /* reset rgb */
data->next_red = 0x00; data->next_red = 0x00;

View File

@ -16,7 +16,6 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/leds.h> #include <linux/leds.h>
#include <linux/workqueue.h>
#include <linux/mfd/da903x.h> #include <linux/mfd/da903x.h>
#include <linux/slab.h> #include <linux/slab.h>
@ -33,9 +32,7 @@
struct da903x_led { struct da903x_led {
struct led_classdev cdev; struct led_classdev cdev;
struct work_struct work;
struct device *master; struct device *master;
enum led_brightness new_brightness;
int id; int id;
int flags; int flags;
}; };
@ -43,11 +40,13 @@ struct da903x_led {
#define DA9030_LED_OFFSET(id) ((id) - DA9030_ID_LED_1) #define DA9030_LED_OFFSET(id) ((id) - DA9030_ID_LED_1)
#define DA9034_LED_OFFSET(id) ((id) - DA9034_ID_LED_1) #define DA9034_LED_OFFSET(id) ((id) - DA9034_ID_LED_1)
static void da903x_led_work(struct work_struct *work) static int da903x_led_set(struct led_classdev *led_cdev,
enum led_brightness value)
{ {
struct da903x_led *led = container_of(work, struct da903x_led, work); struct da903x_led *led =
container_of(led_cdev, struct da903x_led, cdev);
uint8_t val; uint8_t val;
int offset; int offset, ret = -EINVAL;
switch (led->id) { switch (led->id) {
case DA9030_ID_LED_1: case DA9030_ID_LED_1:
@ -57,37 +56,31 @@ static void da903x_led_work(struct work_struct *work)
case DA9030_ID_LED_PC: case DA9030_ID_LED_PC:
offset = DA9030_LED_OFFSET(led->id); offset = DA9030_LED_OFFSET(led->id);
val = led->flags & ~0x87; val = led->flags & ~0x87;
val |= (led->new_brightness) ? 0x80 : 0; /* EN bit */ val |= value ? 0x80 : 0; /* EN bit */
val |= (0x7 - (led->new_brightness >> 5)) & 0x7; /* PWM<2:0> */ val |= (0x7 - (value >> 5)) & 0x7; /* PWM<2:0> */
da903x_write(led->master, DA9030_LED1_CONTROL + offset, val); ret = da903x_write(led->master, DA9030_LED1_CONTROL + offset,
val);
break; break;
case DA9030_ID_VIBRA: case DA9030_ID_VIBRA:
val = led->flags & ~0x80; val = led->flags & ~0x80;
val |= (led->new_brightness) ? 0x80 : 0; /* EN bit */ val |= value ? 0x80 : 0; /* EN bit */
da903x_write(led->master, DA9030_MISC_CONTROL_A, val); ret = da903x_write(led->master, DA9030_MISC_CONTROL_A, val);
break; break;
case DA9034_ID_LED_1: case DA9034_ID_LED_1:
case DA9034_ID_LED_2: case DA9034_ID_LED_2:
offset = DA9034_LED_OFFSET(led->id); offset = DA9034_LED_OFFSET(led->id);
val = (led->new_brightness * 0x5f / LED_FULL) & 0x7f; val = (value * 0x5f / LED_FULL) & 0x7f;
val |= (led->flags & DA9034_LED_RAMP) ? 0x80 : 0; val |= (led->flags & DA9034_LED_RAMP) ? 0x80 : 0;
da903x_write(led->master, DA9034_LED1_CONTROL + offset, val); ret = da903x_write(led->master, DA9034_LED1_CONTROL + offset,
val);
break; break;
case DA9034_ID_VIBRA: case DA9034_ID_VIBRA:
val = led->new_brightness & 0xfe; val = value & 0xfe;
da903x_write(led->master, DA9034_VIBRA, val); ret = da903x_write(led->master, DA9034_VIBRA, val);
break; break;
} }
}
static void da903x_led_set(struct led_classdev *led_cdev, return ret;
enum led_brightness value)
{
struct da903x_led *led;
led = container_of(led_cdev, struct da903x_led, cdev);
led->new_brightness = value;
schedule_work(&led->work);
} }
static int da903x_led_probe(struct platform_device *pdev) static int da903x_led_probe(struct platform_device *pdev)
@ -113,15 +106,12 @@ static int da903x_led_probe(struct platform_device *pdev)
led->cdev.name = pdata->name; led->cdev.name = pdata->name;
led->cdev.default_trigger = pdata->default_trigger; led->cdev.default_trigger = pdata->default_trigger;
led->cdev.brightness_set = da903x_led_set; led->cdev.brightness_set_blocking = da903x_led_set;
led->cdev.brightness = LED_OFF; led->cdev.brightness = LED_OFF;
led->id = id; led->id = id;
led->flags = pdata->flags; led->flags = pdata->flags;
led->master = pdev->dev.parent; led->master = pdev->dev.parent;
led->new_brightness = LED_OFF;
INIT_WORK(&led->work, da903x_led_work);
ret = led_classdev_register(led->master, &led->cdev); ret = led_classdev_register(led->master, &led->cdev);
if (ret) { if (ret) {

View File

@ -16,7 +16,6 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/leds.h> #include <linux/leds.h>
#include <linux/workqueue.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/mfd/da9052/reg.h> #include <linux/mfd/da9052/reg.h>
@ -32,11 +31,9 @@
struct da9052_led { struct da9052_led {
struct led_classdev cdev; struct led_classdev cdev;
struct work_struct work;
struct da9052 *da9052; struct da9052 *da9052;
unsigned char led_index; unsigned char led_index;
unsigned char id; unsigned char id;
int brightness;
}; };
static unsigned char led_reg[] = { static unsigned char led_reg[] = {
@ -44,12 +41,13 @@ static unsigned char led_reg[] = {
DA9052_LED_CONT_5_REG, DA9052_LED_CONT_5_REG,
}; };
static int da9052_set_led_brightness(struct da9052_led *led) static int da9052_set_led_brightness(struct da9052_led *led,
enum led_brightness brightness)
{ {
u8 val; u8 val;
int error; int error;
val = (led->brightness & 0x7f) | DA9052_LED_CONT_DIM; val = (brightness & 0x7f) | DA9052_LED_CONT_DIM;
error = da9052_reg_write(led->da9052, led_reg[led->led_index], val); error = da9052_reg_write(led->da9052, led_reg[led->led_index], val);
if (error < 0) if (error < 0)
@ -58,21 +56,13 @@ static int da9052_set_led_brightness(struct da9052_led *led)
return error; return error;
} }
static void da9052_led_work(struct work_struct *work) static int da9052_led_set(struct led_classdev *led_cdev,
{
struct da9052_led *led = container_of(work, struct da9052_led, work);
da9052_set_led_brightness(led);
}
static void da9052_led_set(struct led_classdev *led_cdev,
enum led_brightness value) enum led_brightness value)
{ {
struct da9052_led *led; struct da9052_led *led =
container_of(led_cdev, struct da9052_led, cdev);
led = container_of(led_cdev, struct da9052_led, cdev); return da9052_set_led_brightness(led, value);
led->brightness = value;
schedule_work(&led->work);
} }
static int da9052_configure_leds(struct da9052 *da9052) static int da9052_configure_leds(struct da9052 *da9052)
@ -133,13 +123,11 @@ static int da9052_led_probe(struct platform_device *pdev)
for (i = 0; i < pled->num_leds; i++) { for (i = 0; i < pled->num_leds; i++) {
led[i].cdev.name = pled->leds[i].name; led[i].cdev.name = pled->leds[i].name;
led[i].cdev.brightness_set = da9052_led_set; led[i].cdev.brightness_set_blocking = da9052_led_set;
led[i].cdev.brightness = LED_OFF; led[i].cdev.brightness = LED_OFF;
led[i].cdev.max_brightness = DA9052_MAX_BRIGHTNESS; led[i].cdev.max_brightness = DA9052_MAX_BRIGHTNESS;
led[i].brightness = LED_OFF;
led[i].led_index = pled->leds[i].flags; led[i].led_index = pled->leds[i].flags;
led[i].da9052 = dev_get_drvdata(pdev->dev.parent); led[i].da9052 = dev_get_drvdata(pdev->dev.parent);
INIT_WORK(&led[i].work, da9052_led_work);
error = led_classdev_register(pdev->dev.parent, &led[i].cdev); error = led_classdev_register(pdev->dev.parent, &led[i].cdev);
if (error) { if (error) {
@ -148,7 +136,8 @@ static int da9052_led_probe(struct platform_device *pdev)
goto err_register; goto err_register;
} }
error = da9052_set_led_brightness(&led[i]); error = da9052_set_led_brightness(&led[i],
led[i].cdev.brightness);
if (error) { if (error) {
dev_err(&pdev->dev, "Unable to init led %d\n", dev_err(&pdev->dev, "Unable to init led %d\n",
led[i].led_index); led[i].led_index);
@ -166,10 +155,8 @@ static int da9052_led_probe(struct platform_device *pdev)
return 0; return 0;
err_register: err_register:
for (i = i - 1; i >= 0; i--) { for (i = i - 1; i >= 0; i--)
led_classdev_unregister(&led[i].cdev); led_classdev_unregister(&led[i].cdev);
cancel_work_sync(&led[i].work);
}
err: err:
return error; return error;
} }
@ -187,10 +174,8 @@ static int da9052_led_remove(struct platform_device *pdev)
pled = pdata->pled; pled = pdata->pled;
for (i = 0; i < pled->num_leds; i++) { for (i = 0; i < pled->num_leds; i++) {
led[i].brightness = 0; da9052_set_led_brightness(&led[i], LED_OFF);
da9052_set_led_brightness(&led[i]);
led_classdev_unregister(&led[i].cdev); led_classdev_unregister(&led[i].cdev);
cancel_work_sync(&led[i].work);
} }
return 0; return 0;

View File

@ -13,20 +13,15 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/workqueue.h>
#include <linux/spi/spi.h> #include <linux/spi/spi.h>
struct dac124s085_led { struct dac124s085_led {
struct led_classdev ldev; struct led_classdev ldev;
struct spi_device *spi; struct spi_device *spi;
int id; int id;
int brightness;
char name[sizeof("dac124s085-3")]; char name[sizeof("dac124s085-3")];
struct mutex mutex; struct mutex mutex;
struct work_struct work;
spinlock_t lock;
}; };
struct dac124s085 { struct dac124s085 {
@ -38,29 +33,21 @@ struct dac124s085 {
#define ALL_WRITE_UPDATE (2 << 12) #define ALL_WRITE_UPDATE (2 << 12)
#define POWER_DOWN_OUTPUT (3 << 12) #define POWER_DOWN_OUTPUT (3 << 12)
static void dac124s085_led_work(struct work_struct *work) static int dac124s085_set_brightness(struct led_classdev *ldev,
{
struct dac124s085_led *led = container_of(work, struct dac124s085_led,
work);
u16 word;
mutex_lock(&led->mutex);
word = cpu_to_le16(((led->id) << 14) | REG_WRITE_UPDATE |
(led->brightness & 0xfff));
spi_write(led->spi, (const u8 *)&word, sizeof(word));
mutex_unlock(&led->mutex);
}
static void dac124s085_set_brightness(struct led_classdev *ldev,
enum led_brightness brightness) enum led_brightness brightness)
{ {
struct dac124s085_led *led = container_of(ldev, struct dac124s085_led, struct dac124s085_led *led = container_of(ldev, struct dac124s085_led,
ldev); ldev);
u16 word;
int ret;
spin_lock(&led->lock); mutex_lock(&led->mutex);
led->brightness = brightness; word = cpu_to_le16(((led->id) << 14) | REG_WRITE_UPDATE |
schedule_work(&led->work); (brightness & 0xfff));
spin_unlock(&led->lock); ret = spi_write(led->spi, (const u8 *)&word, sizeof(word));
mutex_unlock(&led->mutex);
return ret;
} }
static int dac124s085_probe(struct spi_device *spi) static int dac124s085_probe(struct spi_device *spi)
@ -78,16 +65,13 @@ static int dac124s085_probe(struct spi_device *spi)
for (i = 0; i < ARRAY_SIZE(dac->leds); i++) { for (i = 0; i < ARRAY_SIZE(dac->leds); i++) {
led = dac->leds + i; led = dac->leds + i;
led->id = i; led->id = i;
led->brightness = LED_OFF;
led->spi = spi; led->spi = spi;
snprintf(led->name, sizeof(led->name), "dac124s085-%d", i); snprintf(led->name, sizeof(led->name), "dac124s085-%d", i);
spin_lock_init(&led->lock);
INIT_WORK(&led->work, dac124s085_led_work);
mutex_init(&led->mutex); mutex_init(&led->mutex);
led->ldev.name = led->name; led->ldev.name = led->name;
led->ldev.brightness = LED_OFF; led->ldev.brightness = LED_OFF;
led->ldev.max_brightness = 0xfff; led->ldev.max_brightness = 0xfff;
led->ldev.brightness_set = dac124s085_set_brightness; led->ldev.brightness_set_blocking = dac124s085_set_brightness;
ret = led_classdev_register(&spi->dev, &led->ldev); ret = led_classdev_register(&spi->dev, &led->ldev);
if (ret < 0) if (ret < 0)
goto eledcr; goto eledcr;
@ -109,10 +93,8 @@ static int dac124s085_remove(struct spi_device *spi)
struct dac124s085 *dac = spi_get_drvdata(spi); struct dac124s085 *dac = spi_get_drvdata(spi);
int i; int i;
for (i = 0; i < ARRAY_SIZE(dac->leds); i++) { for (i = 0; i < ARRAY_SIZE(dac->leds); i++)
led_classdev_unregister(&dac->leds[i].ldev); led_classdev_unregister(&dac->leds[i].ldev);
cancel_work_sync(&dac->leds[i].work);
}
return 0; return 0;
} }

View File

@ -20,32 +20,16 @@
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/property.h> #include <linux/property.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/workqueue.h>
struct gpio_led_data { struct gpio_led_data {
struct led_classdev cdev; struct led_classdev cdev;
struct gpio_desc *gpiod; struct gpio_desc *gpiod;
struct work_struct work;
u8 new_level;
u8 can_sleep; u8 can_sleep;
u8 blinking; u8 blinking;
int (*platform_gpio_blink_set)(struct gpio_desc *desc, int state, int (*platform_gpio_blink_set)(struct gpio_desc *desc, int state,
unsigned long *delay_on, unsigned long *delay_off); unsigned long *delay_on, unsigned long *delay_off);
}; };
static void gpio_led_work(struct work_struct *work)
{
struct gpio_led_data *led_dat =
container_of(work, struct gpio_led_data, work);
if (led_dat->blinking) {
led_dat->platform_gpio_blink_set(led_dat->gpiod,
led_dat->new_level, NULL, NULL);
led_dat->blinking = 0;
} else
gpiod_set_value_cansleep(led_dat->gpiod, led_dat->new_level);
}
static void gpio_led_set(struct led_classdev *led_cdev, static void gpio_led_set(struct led_classdev *led_cdev,
enum led_brightness value) enum led_brightness value)
{ {
@ -58,23 +42,25 @@ static void gpio_led_set(struct led_classdev *led_cdev,
else else
level = 1; level = 1;
/* Setting GPIOs with I2C/etc requires a task context, and we don't
* seem to have a reliable way to know if we're already in one; so
* let's just assume the worst.
*/
if (led_dat->can_sleep) {
led_dat->new_level = level;
schedule_work(&led_dat->work);
} else {
if (led_dat->blinking) { if (led_dat->blinking) {
led_dat->platform_gpio_blink_set(led_dat->gpiod, level, led_dat->platform_gpio_blink_set(led_dat->gpiod, level,
NULL, NULL); NULL, NULL);
led_dat->blinking = 0; led_dat->blinking = 0;
} else } else {
if (led_dat->can_sleep)
gpiod_set_value_cansleep(led_dat->gpiod, level);
else
gpiod_set_value(led_dat->gpiod, level); gpiod_set_value(led_dat->gpiod, level);
} }
} }
static int gpio_led_set_blocking(struct led_classdev *led_cdev,
enum led_brightness value)
{
gpio_led_set(led_cdev, value);
return 0;
}
static int gpio_blink_set(struct led_classdev *led_cdev, static int gpio_blink_set(struct led_classdev *led_cdev,
unsigned long *delay_on, unsigned long *delay_off) unsigned long *delay_on, unsigned long *delay_off)
{ {
@ -125,12 +111,15 @@ static int create_gpio_led(const struct gpio_led *template,
led_dat->cdev.name = template->name; led_dat->cdev.name = template->name;
led_dat->cdev.default_trigger = template->default_trigger; led_dat->cdev.default_trigger = template->default_trigger;
led_dat->can_sleep = gpiod_cansleep(led_dat->gpiod); led_dat->can_sleep = gpiod_cansleep(led_dat->gpiod);
if (!led_dat->can_sleep)
led_dat->cdev.brightness_set = gpio_led_set;
else
led_dat->cdev.brightness_set_blocking = gpio_led_set_blocking;
led_dat->blinking = 0; led_dat->blinking = 0;
if (blink_set) { if (blink_set) {
led_dat->platform_gpio_blink_set = blink_set; led_dat->platform_gpio_blink_set = blink_set;
led_dat->cdev.blink_set = gpio_blink_set; led_dat->cdev.blink_set = gpio_blink_set;
} }
led_dat->cdev.brightness_set = gpio_led_set;
if (template->default_state == LEDS_GPIO_DEFSTATE_KEEP) if (template->default_state == LEDS_GPIO_DEFSTATE_KEEP)
state = !!gpiod_get_value_cansleep(led_dat->gpiod); state = !!gpiod_get_value_cansleep(led_dat->gpiod);
else else
@ -143,17 +132,9 @@ static int create_gpio_led(const struct gpio_led *template,
if (ret < 0) if (ret < 0)
return ret; return ret;
INIT_WORK(&led_dat->work, gpio_led_work);
return led_classdev_register(parent, &led_dat->cdev); return led_classdev_register(parent, &led_dat->cdev);
} }
static void delete_gpio_led(struct gpio_led_data *led)
{
led_classdev_unregister(&led->cdev);
cancel_work_sync(&led->work);
}
struct gpio_leds_priv { struct gpio_leds_priv {
int num_leds; int num_leds;
struct gpio_led_data leds[]; struct gpio_led_data leds[];
@ -233,7 +214,7 @@ static struct gpio_leds_priv *gpio_leds_create(struct platform_device *pdev)
err: err:
for (count = priv->num_leds - 1; count >= 0; count--) for (count = priv->num_leds - 1; count >= 0; count--)
delete_gpio_led(&priv->leds[count]); led_classdev_unregister(&priv->leds[count].cdev);
return ERR_PTR(ret); return ERR_PTR(ret);
} }
@ -265,7 +246,8 @@ static int gpio_led_probe(struct platform_device *pdev)
if (ret < 0) { if (ret < 0) {
/* On failure: unwind the led creations */ /* On failure: unwind the led creations */
for (i = i - 1; i >= 0; i--) for (i = i - 1; i >= 0; i--)
delete_gpio_led(&priv->leds[i]); led_classdev_unregister(
&priv->leds[i].cdev);
return ret; return ret;
} }
} }
@ -286,7 +268,7 @@ static int gpio_led_remove(struct platform_device *pdev)
int i; int i;
for (i = 0; i < priv->num_leds; i++) for (i = 0; i < priv->num_leds; i++)
delete_gpio_led(&priv->leds[i]); led_classdev_unregister(&priv->leds[i].cdev);
return 0; return 0;
} }

View File

@ -20,7 +20,7 @@
#define LED_AUTOSTOP (1 << 5) /* LED ON/OFF auto stop set 0:disable, 1:enable */ #define LED_AUTOSTOP (1 << 5) /* LED ON/OFF auto stop set 0:disable, 1:enable */
#define LED_ALWAYS (1 << 6) /* LED Interrupt Mask 0:No mask, 1:mask */ #define LED_ALWAYS (1 << 6) /* LED Interrupt Mask 0:No mask, 1:mask */
static void micro_leds_brightness_set(struct led_classdev *led_cdev, static int micro_leds_brightness_set(struct led_classdev *led_cdev,
enum led_brightness value) enum led_brightness value)
{ {
struct ipaq_micro *micro = dev_get_drvdata(led_cdev->dev->parent->parent); struct ipaq_micro *micro = dev_get_drvdata(led_cdev->dev->parent->parent);
@ -50,7 +50,7 @@ static void micro_leds_brightness_set(struct led_classdev *led_cdev,
msg.tx_data[2] = 1; msg.tx_data[2] = 1;
msg.tx_data[3] = 0; /* Duty cycle 256 */ msg.tx_data[3] = 0; /* Duty cycle 256 */
} }
ipaq_micro_tx_msg_sync(micro, &msg); return ipaq_micro_tx_msg_sync(micro, &msg);
} }
/* Maximum duty cycle in ms 256/10 sec = 25600 ms */ /* Maximum duty cycle in ms 256/10 sec = 25600 ms */
@ -102,7 +102,7 @@ static int micro_leds_blink_set(struct led_classdev *led_cdev,
static struct led_classdev micro_led = { static struct led_classdev micro_led = {
.name = "led-ipaq-micro", .name = "led-ipaq-micro",
.brightness_set = micro_leds_brightness_set, .brightness_set_blocking = micro_leds_brightness_set,
.blink_set = micro_leds_blink_set, .blink_set = micro_leds_blink_set,
.flags = LED_CORE_SUSPENDRESUME, .flags = LED_CORE_SUSPENDRESUME,
}; };

View File

@ -18,7 +18,6 @@
#include <linux/of.h> #include <linux/of.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/regulator/consumer.h> #include <linux/regulator/consumer.h>
#include <linux/workqueue.h>
/* Value related the movie mode */ /* Value related the movie mode */
#define KTD2692_MOVIE_MODE_CURRENT_LEVELS 16 #define KTD2692_MOVIE_MODE_CURRENT_LEVELS 16
@ -82,7 +81,6 @@ struct ktd2692_context {
/* secures access to the device */ /* secures access to the device */
struct mutex lock; struct mutex lock;
struct regulator *regulator; struct regulator *regulator;
struct work_struct work_brightness_set;
struct gpio_desc *aux_gpio; struct gpio_desc *aux_gpio;
struct gpio_desc *ctrl_gpio; struct gpio_desc *ctrl_gpio;
@ -158,9 +156,12 @@ static void ktd2692_expresswire_write(struct ktd2692_context *led, u8 value)
ktd2692_expresswire_end(led); ktd2692_expresswire_end(led);
} }
static void ktd2692_brightness_set(struct ktd2692_context *led, static int ktd2692_led_brightness_set(struct led_classdev *led_cdev,
enum led_brightness brightness) enum led_brightness brightness)
{ {
struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
struct ktd2692_context *led = fled_cdev_to_led(fled_cdev);
mutex_lock(&led->lock); mutex_lock(&led->lock);
if (brightness == LED_OFF) { if (brightness == LED_OFF) {
@ -174,33 +175,6 @@ static void ktd2692_brightness_set(struct ktd2692_context *led,
ktd2692_expresswire_write(led, led->mode | KTD2692_REG_MODE_BASE); ktd2692_expresswire_write(led, led->mode | KTD2692_REG_MODE_BASE);
mutex_unlock(&led->lock); mutex_unlock(&led->lock);
}
static void ktd2692_brightness_set_work(struct work_struct *work)
{
struct ktd2692_context *led =
container_of(work, struct ktd2692_context, work_brightness_set);
ktd2692_brightness_set(led, led->torch_brightness);
}
static void ktd2692_led_brightness_set(struct led_classdev *led_cdev,
enum led_brightness brightness)
{
struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
struct ktd2692_context *led = fled_cdev_to_led(fled_cdev);
led->torch_brightness = brightness;
schedule_work(&led->work_brightness_set);
}
static int ktd2692_led_brightness_set_sync(struct led_classdev *led_cdev,
enum led_brightness brightness)
{
struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
struct ktd2692_context *led = fled_cdev_to_led(fled_cdev);
ktd2692_brightness_set(led, brightness);
return 0; return 0;
} }
@ -332,21 +306,24 @@ static int ktd2692_parse_dt(struct ktd2692_context *led, struct device *dev,
&cfg->movie_max_microamp); &cfg->movie_max_microamp);
if (ret) { if (ret) {
dev_err(dev, "failed to parse led-max-microamp\n"); dev_err(dev, "failed to parse led-max-microamp\n");
return ret; goto err_parse_dt;
} }
ret = of_property_read_u32(child_node, "flash-max-microamp", ret = of_property_read_u32(child_node, "flash-max-microamp",
&cfg->flash_max_microamp); &cfg->flash_max_microamp);
if (ret) { if (ret) {
dev_err(dev, "failed to parse flash-max-microamp\n"); dev_err(dev, "failed to parse flash-max-microamp\n");
return ret; goto err_parse_dt;
} }
ret = of_property_read_u32(child_node, "flash-max-timeout-us", ret = of_property_read_u32(child_node, "flash-max-timeout-us",
&cfg->flash_max_timeout); &cfg->flash_max_timeout);
if (ret) if (ret) {
dev_err(dev, "failed to parse flash-max-timeout-us\n"); dev_err(dev, "failed to parse flash-max-timeout-us\n");
goto err_parse_dt;
}
err_parse_dt:
of_node_put(child_node); of_node_put(child_node);
return ret; return ret;
} }
@ -381,12 +358,10 @@ static int ktd2692_probe(struct platform_device *pdev)
fled_cdev->ops = &flash_ops; fled_cdev->ops = &flash_ops;
led_cdev->max_brightness = led_cfg.max_brightness; led_cdev->max_brightness = led_cfg.max_brightness;
led_cdev->brightness_set = ktd2692_led_brightness_set; led_cdev->brightness_set_blocking = ktd2692_led_brightness_set;
led_cdev->brightness_set_sync = ktd2692_led_brightness_set_sync;
led_cdev->flags |= LED_CORE_SUSPENDRESUME | LED_DEV_CAP_FLASH; led_cdev->flags |= LED_CORE_SUSPENDRESUME | LED_DEV_CAP_FLASH;
mutex_init(&led->lock); mutex_init(&led->lock);
INIT_WORK(&led->work_brightness_set, ktd2692_brightness_set_work);
platform_set_drvdata(pdev, led); platform_set_drvdata(pdev, led);
@ -408,7 +383,6 @@ static int ktd2692_remove(struct platform_device *pdev)
int ret; int ret;
led_classdev_flash_unregister(&led->fled_cdev); led_classdev_flash_unregister(&led->fled_cdev);
cancel_work_sync(&led->work_brightness_set);
if (led->regulator) { if (led->regulator) {
ret = regulator_disable(led->regulator); ret = regulator_disable(led->regulator);

View File

@ -17,7 +17,6 @@
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/workqueue.h>
#include <linux/mfd/lm3533.h> #include <linux/mfd/lm3533.h>
@ -53,9 +52,6 @@ struct lm3533_led {
struct mutex mutex; struct mutex mutex;
unsigned long flags; unsigned long flags;
struct work_struct work;
u8 new_brightness;
}; };
@ -123,27 +119,17 @@ out:
return ret; return ret;
} }
static void lm3533_led_work(struct work_struct *work) static int lm3533_led_set(struct led_classdev *cdev,
{
struct lm3533_led *led = container_of(work, struct lm3533_led, work);
dev_dbg(led->cdev.dev, "%s - %u\n", __func__, led->new_brightness);
if (led->new_brightness == 0)
lm3533_led_pattern_enable(led, 0); /* disable blink */
lm3533_ctrlbank_set_brightness(&led->cb, led->new_brightness);
}
static void lm3533_led_set(struct led_classdev *cdev,
enum led_brightness value) enum led_brightness value)
{ {
struct lm3533_led *led = to_lm3533_led(cdev); struct lm3533_led *led = to_lm3533_led(cdev);
dev_dbg(led->cdev.dev, "%s - %d\n", __func__, value); dev_dbg(led->cdev.dev, "%s - %d\n", __func__, value);
led->new_brightness = value; if (value == 0)
schedule_work(&led->work); lm3533_led_pattern_enable(led, 0); /* disable blink */
return lm3533_ctrlbank_set_brightness(&led->cb, value);
} }
static enum led_brightness lm3533_led_get(struct led_classdev *cdev) static enum led_brightness lm3533_led_get(struct led_classdev *cdev)
@ -693,7 +679,7 @@ static int lm3533_led_probe(struct platform_device *pdev)
led->lm3533 = lm3533; led->lm3533 = lm3533;
led->cdev.name = pdata->name; led->cdev.name = pdata->name;
led->cdev.default_trigger = pdata->default_trigger; led->cdev.default_trigger = pdata->default_trigger;
led->cdev.brightness_set = lm3533_led_set; led->cdev.brightness_set_blocking = lm3533_led_set;
led->cdev.brightness_get = lm3533_led_get; led->cdev.brightness_get = lm3533_led_get;
led->cdev.blink_set = lm3533_led_blink_set; led->cdev.blink_set = lm3533_led_blink_set;
led->cdev.brightness = LED_OFF; led->cdev.brightness = LED_OFF;
@ -701,7 +687,6 @@ static int lm3533_led_probe(struct platform_device *pdev)
led->id = pdev->id; led->id = pdev->id;
mutex_init(&led->mutex); mutex_init(&led->mutex);
INIT_WORK(&led->work, lm3533_led_work);
/* The class framework makes a callback to get brightness during /* The class framework makes a callback to get brightness during
* registration so use parent device (for error reporting) until * registration so use parent device (for error reporting) until
@ -733,7 +718,6 @@ static int lm3533_led_probe(struct platform_device *pdev)
err_unregister: err_unregister:
led_classdev_unregister(&led->cdev); led_classdev_unregister(&led->cdev);
flush_work(&led->work);
return ret; return ret;
} }
@ -746,7 +730,6 @@ static int lm3533_led_remove(struct platform_device *pdev)
lm3533_ctrlbank_disable(&led->cb); lm3533_ctrlbank_disable(&led->cb);
led_classdev_unregister(&led->cdev); led_classdev_unregister(&led->cdev);
flush_work(&led->work);
return 0; return 0;
} }
@ -760,7 +743,6 @@ static void lm3533_led_shutdown(struct platform_device *pdev)
lm3533_ctrlbank_disable(&led->cb); lm3533_ctrlbank_disable(&led->cb);
lm3533_led_set(&led->cdev, LED_OFF); /* disable blink */ lm3533_led_set(&led->cdev, LED_OFF); /* disable blink */
flush_work(&led->work);
} }
static struct platform_driver lm3533_led_driver = { static struct platform_driver lm3533_led_driver = {

View File

@ -16,7 +16,6 @@
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/regmap.h> #include <linux/regmap.h>
#include <linux/workqueue.h>
#include <linux/platform_data/leds-lm355x.h> #include <linux/platform_data/leds-lm355x.h>
enum lm355x_type { enum lm355x_type {
@ -59,14 +58,6 @@ struct lm355x_chip_data {
struct led_classdev cdev_torch; struct led_classdev cdev_torch;
struct led_classdev cdev_indicator; struct led_classdev cdev_indicator;
struct work_struct work_flash;
struct work_struct work_torch;
struct work_struct work_indicator;
u8 br_flash;
u8 br_torch;
u8 br_indicator;
struct lm355x_platform_data *pdata; struct lm355x_platform_data *pdata;
struct regmap *regmap; struct regmap *regmap;
struct mutex lock; struct mutex lock;
@ -204,7 +195,7 @@ out:
} }
/* chip control */ /* chip control */
static void lm355x_control(struct lm355x_chip_data *chip, static int lm355x_control(struct lm355x_chip_data *chip,
u8 brightness, enum lm355x_mode opmode) u8 brightness, enum lm355x_mode opmode)
{ {
int ret; int ret;
@ -301,7 +292,7 @@ static void lm355x_control(struct lm355x_chip_data *chip,
case MODE_SHDN: case MODE_SHDN:
break; break;
default: default:
return; return -EINVAL;
} }
/* operation mode control */ /* operation mode control */
ret = regmap_update_bits(chip->regmap, preg[REG_OPMODE].regno, ret = regmap_update_bits(chip->regmap, preg[REG_OPMODE].regno,
@ -309,73 +300,55 @@ static void lm355x_control(struct lm355x_chip_data *chip,
opmode << preg[REG_OPMODE].shift); opmode << preg[REG_OPMODE].shift);
if (ret < 0) if (ret < 0)
goto out; goto out;
return; return ret;
out: out:
dev_err(chip->dev, "%s:i2c access fail to register\n", __func__); dev_err(chip->dev, "%s:i2c access fail to register\n", __func__);
return; return ret;
} }
/* torch */ /* torch */
static void lm355x_deferred_torch_brightness_set(struct work_struct *work)
{
struct lm355x_chip_data *chip =
container_of(work, struct lm355x_chip_data, work_torch);
mutex_lock(&chip->lock); static int lm355x_torch_brightness_set(struct led_classdev *cdev,
lm355x_control(chip, chip->br_torch, MODE_TORCH);
mutex_unlock(&chip->lock);
}
static void lm355x_torch_brightness_set(struct led_classdev *cdev,
enum led_brightness brightness) enum led_brightness brightness)
{ {
struct lm355x_chip_data *chip = struct lm355x_chip_data *chip =
container_of(cdev, struct lm355x_chip_data, cdev_torch); container_of(cdev, struct lm355x_chip_data, cdev_torch);
int ret;
chip->br_torch = brightness; mutex_lock(&chip->lock);
schedule_work(&chip->work_torch); ret = lm355x_control(chip, brightness, MODE_TORCH);
mutex_unlock(&chip->lock);
return ret;
} }
/* flash */ /* flash */
static void lm355x_deferred_strobe_brightness_set(struct work_struct *work)
{
struct lm355x_chip_data *chip =
container_of(work, struct lm355x_chip_data, work_flash);
mutex_lock(&chip->lock); static int lm355x_strobe_brightness_set(struct led_classdev *cdev,
lm355x_control(chip, chip->br_flash, MODE_FLASH);
mutex_unlock(&chip->lock);
}
static void lm355x_strobe_brightness_set(struct led_classdev *cdev,
enum led_brightness brightness) enum led_brightness brightness)
{ {
struct lm355x_chip_data *chip = struct lm355x_chip_data *chip =
container_of(cdev, struct lm355x_chip_data, cdev_flash); container_of(cdev, struct lm355x_chip_data, cdev_flash);
int ret;
chip->br_flash = brightness; mutex_lock(&chip->lock);
schedule_work(&chip->work_flash); ret = lm355x_control(chip, brightness, MODE_FLASH);
mutex_unlock(&chip->lock);
return ret;
} }
/* indicator */ /* indicator */
static void lm355x_deferred_indicator_brightness_set(struct work_struct *work)
{
struct lm355x_chip_data *chip =
container_of(work, struct lm355x_chip_data, work_indicator);
mutex_lock(&chip->lock); static int lm355x_indicator_brightness_set(struct led_classdev *cdev,
lm355x_control(chip, chip->br_indicator, MODE_INDIC);
mutex_unlock(&chip->lock);
}
static void lm355x_indicator_brightness_set(struct led_classdev *cdev,
enum led_brightness brightness) enum led_brightness brightness)
{ {
struct lm355x_chip_data *chip = struct lm355x_chip_data *chip =
container_of(cdev, struct lm355x_chip_data, cdev_indicator); container_of(cdev, struct lm355x_chip_data, cdev_indicator);
int ret;
chip->br_indicator = brightness; mutex_lock(&chip->lock);
schedule_work(&chip->work_indicator); ret = lm355x_control(chip, brightness, MODE_INDIC);
mutex_unlock(&chip->lock);
return ret;
} }
/* indicator pattern only for lm3556*/ /* indicator pattern only for lm3556*/
@ -479,34 +452,31 @@ static int lm355x_probe(struct i2c_client *client,
goto err_out; goto err_out;
/* flash */ /* flash */
INIT_WORK(&chip->work_flash, lm355x_deferred_strobe_brightness_set);
chip->cdev_flash.name = "flash"; chip->cdev_flash.name = "flash";
chip->cdev_flash.max_brightness = 16; chip->cdev_flash.max_brightness = 16;
chip->cdev_flash.brightness_set = lm355x_strobe_brightness_set; chip->cdev_flash.brightness_set_blocking = lm355x_strobe_brightness_set;
chip->cdev_flash.default_trigger = "flash"; chip->cdev_flash.default_trigger = "flash";
err = led_classdev_register((struct device *) err = led_classdev_register((struct device *)
&client->dev, &chip->cdev_flash); &client->dev, &chip->cdev_flash);
if (err < 0) if (err < 0)
goto err_out; goto err_out;
/* torch */ /* torch */
INIT_WORK(&chip->work_torch, lm355x_deferred_torch_brightness_set);
chip->cdev_torch.name = "torch"; chip->cdev_torch.name = "torch";
chip->cdev_torch.max_brightness = 8; chip->cdev_torch.max_brightness = 8;
chip->cdev_torch.brightness_set = lm355x_torch_brightness_set; chip->cdev_torch.brightness_set_blocking = lm355x_torch_brightness_set;
chip->cdev_torch.default_trigger = "torch"; chip->cdev_torch.default_trigger = "torch";
err = led_classdev_register((struct device *) err = led_classdev_register((struct device *)
&client->dev, &chip->cdev_torch); &client->dev, &chip->cdev_torch);
if (err < 0) if (err < 0)
goto err_create_torch_file; goto err_create_torch_file;
/* indicator */ /* indicator */
INIT_WORK(&chip->work_indicator,
lm355x_deferred_indicator_brightness_set);
chip->cdev_indicator.name = "indicator"; chip->cdev_indicator.name = "indicator";
if (id->driver_data == CHIP_LM3554) if (id->driver_data == CHIP_LM3554)
chip->cdev_indicator.max_brightness = 4; chip->cdev_indicator.max_brightness = 4;
else else
chip->cdev_indicator.max_brightness = 8; chip->cdev_indicator.max_brightness = 8;
chip->cdev_indicator.brightness_set = lm355x_indicator_brightness_set; chip->cdev_indicator.brightness_set_blocking =
lm355x_indicator_brightness_set;
/* indicator pattern control only for LM3556 */ /* indicator pattern control only for LM3556 */
if (id->driver_data == CHIP_LM3556) if (id->driver_data == CHIP_LM3556)
chip->cdev_indicator.groups = lm355x_indicator_groups; chip->cdev_indicator.groups = lm355x_indicator_groups;
@ -534,11 +504,8 @@ static int lm355x_remove(struct i2c_client *client)
regmap_write(chip->regmap, preg[REG_OPMODE].regno, 0); regmap_write(chip->regmap, preg[REG_OPMODE].regno, 0);
led_classdev_unregister(&chip->cdev_indicator); led_classdev_unregister(&chip->cdev_indicator);
flush_work(&chip->work_indicator);
led_classdev_unregister(&chip->cdev_torch); led_classdev_unregister(&chip->cdev_torch);
flush_work(&chip->work_torch);
led_classdev_unregister(&chip->cdev_flash); led_classdev_unregister(&chip->cdev_flash);
flush_work(&chip->work_flash);
dev_info(&client->dev, "%s is removed\n", lm355x_name[chip->type]); dev_info(&client->dev, "%s is removed\n", lm355x_name[chip->type]);
return 0; return 0;

View File

@ -15,7 +15,6 @@
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/regmap.h> #include <linux/regmap.h>
#include <linux/workqueue.h>
#include <linux/platform_data/leds-lm3642.h> #include <linux/platform_data/leds-lm3642.h>
#define REG_FILT_TIME (0x0) #define REG_FILT_TIME (0x0)
@ -73,10 +72,6 @@ struct lm3642_chip_data {
struct led_classdev cdev_torch; struct led_classdev cdev_torch;
struct led_classdev cdev_indicator; struct led_classdev cdev_indicator;
struct work_struct work_flash;
struct work_struct work_torch;
struct work_struct work_indicator;
u8 br_flash; u8 br_flash;
u8 br_torch; u8 br_torch;
u8 br_indicator; u8 br_indicator;
@ -209,24 +204,18 @@ out_strtoint:
static DEVICE_ATTR(torch_pin, S_IWUSR, NULL, lm3642_torch_pin_store); static DEVICE_ATTR(torch_pin, S_IWUSR, NULL, lm3642_torch_pin_store);
static void lm3642_deferred_torch_brightness_set(struct work_struct *work) static int lm3642_torch_brightness_set(struct led_classdev *cdev,
{
struct lm3642_chip_data *chip =
container_of(work, struct lm3642_chip_data, work_torch);
mutex_lock(&chip->lock);
lm3642_control(chip, chip->br_torch, MODES_TORCH);
mutex_unlock(&chip->lock);
}
static void lm3642_torch_brightness_set(struct led_classdev *cdev,
enum led_brightness brightness) enum led_brightness brightness)
{ {
struct lm3642_chip_data *chip = struct lm3642_chip_data *chip =
container_of(cdev, struct lm3642_chip_data, cdev_torch); container_of(cdev, struct lm3642_chip_data, cdev_torch);
int ret;
mutex_lock(&chip->lock);
chip->br_torch = brightness; chip->br_torch = brightness;
schedule_work(&chip->work_torch); ret = lm3642_control(chip, chip->br_torch, MODES_TORCH);
mutex_unlock(&chip->lock);
return ret;
} }
/* flash */ /* flash */
@ -266,45 +255,33 @@ out_strtoint:
static DEVICE_ATTR(strobe_pin, S_IWUSR, NULL, lm3642_strobe_pin_store); static DEVICE_ATTR(strobe_pin, S_IWUSR, NULL, lm3642_strobe_pin_store);
static void lm3642_deferred_strobe_brightness_set(struct work_struct *work) static int lm3642_strobe_brightness_set(struct led_classdev *cdev,
{
struct lm3642_chip_data *chip =
container_of(work, struct lm3642_chip_data, work_flash);
mutex_lock(&chip->lock);
lm3642_control(chip, chip->br_flash, MODES_FLASH);
mutex_unlock(&chip->lock);
}
static void lm3642_strobe_brightness_set(struct led_classdev *cdev,
enum led_brightness brightness) enum led_brightness brightness)
{ {
struct lm3642_chip_data *chip = struct lm3642_chip_data *chip =
container_of(cdev, struct lm3642_chip_data, cdev_flash); container_of(cdev, struct lm3642_chip_data, cdev_flash);
int ret;
mutex_lock(&chip->lock);
chip->br_flash = brightness; chip->br_flash = brightness;
schedule_work(&chip->work_flash); ret = lm3642_control(chip, chip->br_flash, MODES_FLASH);
mutex_unlock(&chip->lock);
return ret;
} }
/* indicator */ /* indicator */
static void lm3642_deferred_indicator_brightness_set(struct work_struct *work) static int lm3642_indicator_brightness_set(struct led_classdev *cdev,
{
struct lm3642_chip_data *chip =
container_of(work, struct lm3642_chip_data, work_indicator);
mutex_lock(&chip->lock);
lm3642_control(chip, chip->br_indicator, MODES_INDIC);
mutex_unlock(&chip->lock);
}
static void lm3642_indicator_brightness_set(struct led_classdev *cdev,
enum led_brightness brightness) enum led_brightness brightness)
{ {
struct lm3642_chip_data *chip = struct lm3642_chip_data *chip =
container_of(cdev, struct lm3642_chip_data, cdev_indicator); container_of(cdev, struct lm3642_chip_data, cdev_indicator);
int ret;
mutex_lock(&chip->lock);
chip->br_indicator = brightness; chip->br_indicator = brightness;
schedule_work(&chip->work_indicator); ret = lm3642_control(chip, chip->br_indicator, MODES_INDIC);
mutex_unlock(&chip->lock);
return ret;
} }
static const struct regmap_config lm3642_regmap = { static const struct regmap_config lm3642_regmap = {
@ -371,10 +348,9 @@ static int lm3642_probe(struct i2c_client *client,
goto err_out; goto err_out;
/* flash */ /* flash */
INIT_WORK(&chip->work_flash, lm3642_deferred_strobe_brightness_set);
chip->cdev_flash.name = "flash"; chip->cdev_flash.name = "flash";
chip->cdev_flash.max_brightness = 16; chip->cdev_flash.max_brightness = 16;
chip->cdev_flash.brightness_set = lm3642_strobe_brightness_set; chip->cdev_flash.brightness_set_blocking = lm3642_strobe_brightness_set;
chip->cdev_flash.default_trigger = "flash"; chip->cdev_flash.default_trigger = "flash";
chip->cdev_flash.groups = lm3642_flash_groups, chip->cdev_flash.groups = lm3642_flash_groups,
err = led_classdev_register((struct device *) err = led_classdev_register((struct device *)
@ -385,10 +361,9 @@ static int lm3642_probe(struct i2c_client *client,
} }
/* torch */ /* torch */
INIT_WORK(&chip->work_torch, lm3642_deferred_torch_brightness_set);
chip->cdev_torch.name = "torch"; chip->cdev_torch.name = "torch";
chip->cdev_torch.max_brightness = 8; chip->cdev_torch.max_brightness = 8;
chip->cdev_torch.brightness_set = lm3642_torch_brightness_set; chip->cdev_torch.brightness_set_blocking = lm3642_torch_brightness_set;
chip->cdev_torch.default_trigger = "torch"; chip->cdev_torch.default_trigger = "torch";
chip->cdev_torch.groups = lm3642_torch_groups, chip->cdev_torch.groups = lm3642_torch_groups,
err = led_classdev_register((struct device *) err = led_classdev_register((struct device *)
@ -399,11 +374,10 @@ static int lm3642_probe(struct i2c_client *client,
} }
/* indicator */ /* indicator */
INIT_WORK(&chip->work_indicator,
lm3642_deferred_indicator_brightness_set);
chip->cdev_indicator.name = "indicator"; chip->cdev_indicator.name = "indicator";
chip->cdev_indicator.max_brightness = 8; chip->cdev_indicator.max_brightness = 8;
chip->cdev_indicator.brightness_set = lm3642_indicator_brightness_set; chip->cdev_indicator.brightness_set_blocking =
lm3642_indicator_brightness_set;
err = led_classdev_register((struct device *) err = led_classdev_register((struct device *)
&client->dev, &chip->cdev_indicator); &client->dev, &chip->cdev_indicator);
if (err < 0) { if (err < 0) {
@ -427,11 +401,8 @@ static int lm3642_remove(struct i2c_client *client)
struct lm3642_chip_data *chip = i2c_get_clientdata(client); struct lm3642_chip_data *chip = i2c_get_clientdata(client);
led_classdev_unregister(&chip->cdev_indicator); led_classdev_unregister(&chip->cdev_indicator);
flush_work(&chip->work_indicator);
led_classdev_unregister(&chip->cdev_torch); led_classdev_unregister(&chip->cdev_torch);
flush_work(&chip->work_torch);
led_classdev_unregister(&chip->cdev_flash); led_classdev_unregister(&chip->cdev_flash);
flush_work(&chip->work_flash);
regmap_write(chip->regmap, REG_ENABLE, 0); regmap_write(chip->regmap, REG_ENABLE, 0);
return 0; return 0;
} }

View File

@ -31,7 +31,6 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/leds.h> #include <linux/leds.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/workqueue.h>
#include <linux/leds-lp3944.h> #include <linux/leds-lp3944.h>
/* Read Only Registers */ /* Read Only Registers */
@ -68,10 +67,8 @@
struct lp3944_led_data { struct lp3944_led_data {
u8 id; u8 id;
enum lp3944_type type; enum lp3944_type type;
enum lp3944_status status;
struct led_classdev ldev; struct led_classdev ldev;
struct i2c_client *client; struct i2c_client *client;
struct work_struct work;
}; };
struct lp3944_data { struct lp3944_data {
@ -275,13 +272,12 @@ static int lp3944_led_set_blink(struct led_classdev *led_cdev,
dev_dbg(&led->client->dev, "%s: OK hardware accelerated blink!\n", dev_dbg(&led->client->dev, "%s: OK hardware accelerated blink!\n",
__func__); __func__);
led->status = LP3944_LED_STATUS_DIM0; lp3944_led_set(led, LP3944_LED_STATUS_DIM0);
schedule_work(&led->work);
return 0; return 0;
} }
static void lp3944_led_set_brightness(struct led_classdev *led_cdev, static int lp3944_led_set_brightness(struct led_classdev *led_cdev,
enum led_brightness brightness) enum led_brightness brightness)
{ {
struct lp3944_led_data *led = ldev_to_led(led_cdev); struct lp3944_led_data *led = ldev_to_led(led_cdev);
@ -289,16 +285,7 @@ static void lp3944_led_set_brightness(struct led_classdev *led_cdev,
dev_dbg(&led->client->dev, "%s: %s, %d\n", dev_dbg(&led->client->dev, "%s: %s, %d\n",
__func__, led_cdev->name, brightness); __func__, led_cdev->name, brightness);
led->status = !!brightness; return lp3944_led_set(led, !!brightness);
schedule_work(&led->work);
}
static void lp3944_led_work(struct work_struct *work)
{
struct lp3944_led_data *led;
led = container_of(work, struct lp3944_led_data, work);
lp3944_led_set(led, led->status);
} }
static int lp3944_configure(struct i2c_client *client, static int lp3944_configure(struct i2c_client *client,
@ -318,14 +305,13 @@ static int lp3944_configure(struct i2c_client *client,
case LP3944_LED_TYPE_LED: case LP3944_LED_TYPE_LED:
case LP3944_LED_TYPE_LED_INVERTED: case LP3944_LED_TYPE_LED_INVERTED:
led->type = pled->type; led->type = pled->type;
led->status = pled->status;
led->ldev.name = pled->name; led->ldev.name = pled->name;
led->ldev.max_brightness = 1; led->ldev.max_brightness = 1;
led->ldev.brightness_set = lp3944_led_set_brightness; led->ldev.brightness_set_blocking =
lp3944_led_set_brightness;
led->ldev.blink_set = lp3944_led_set_blink; led->ldev.blink_set = lp3944_led_set_blink;
led->ldev.flags = LED_CORE_SUSPENDRESUME; led->ldev.flags = LED_CORE_SUSPENDRESUME;
INIT_WORK(&led->work, lp3944_led_work);
err = led_classdev_register(&client->dev, &led->ldev); err = led_classdev_register(&client->dev, &led->ldev);
if (err < 0) { if (err < 0) {
dev_err(&client->dev, dev_err(&client->dev,
@ -336,14 +322,14 @@ static int lp3944_configure(struct i2c_client *client,
/* to expose the default value to userspace */ /* to expose the default value to userspace */
led->ldev.brightness = led->ldev.brightness =
(enum led_brightness) led->status; (enum led_brightness) pled->status;
/* Set the default led status */ /* Set the default led status */
err = lp3944_led_set(led, led->status); err = lp3944_led_set(led, pled->status);
if (err < 0) { if (err < 0) {
dev_err(&client->dev, dev_err(&client->dev,
"%s couldn't set STATUS %d\n", "%s couldn't set STATUS %d\n",
led->ldev.name, led->status); led->ldev.name, pled->status);
goto exit; goto exit;
} }
break; break;
@ -364,7 +350,6 @@ exit:
case LP3944_LED_TYPE_LED: case LP3944_LED_TYPE_LED:
case LP3944_LED_TYPE_LED_INVERTED: case LP3944_LED_TYPE_LED_INVERTED:
led_classdev_unregister(&data->leds[i].ldev); led_classdev_unregister(&data->leds[i].ldev);
cancel_work_sync(&data->leds[i].work);
break; break;
case LP3944_LED_TYPE_NONE: case LP3944_LED_TYPE_NONE:
@ -424,7 +409,6 @@ static int lp3944_remove(struct i2c_client *client)
case LP3944_LED_TYPE_LED: case LP3944_LED_TYPE_LED:
case LP3944_LED_TYPE_LED_INVERTED: case LP3944_LED_TYPE_LED_INVERTED:
led_classdev_unregister(&data->leds[i].ldev); led_classdev_unregister(&data->leds[i].ldev);
cancel_work_sync(&data->leds[i].work);
break; break;
case LP3944_LED_TYPE_NONE: case LP3944_LED_TYPE_NONE:

View File

@ -362,16 +362,17 @@ static int lp5521_run_selftest(struct lp55xx_chip *chip, char *buf)
return 0; return 0;
} }
static void lp5521_led_brightness_work(struct work_struct *work) static int lp5521_led_brightness(struct lp55xx_led *led)
{ {
struct lp55xx_led *led = container_of(work, struct lp55xx_led,
brightness_work);
struct lp55xx_chip *chip = led->chip; struct lp55xx_chip *chip = led->chip;
int ret;
mutex_lock(&chip->lock); mutex_lock(&chip->lock);
lp55xx_write(chip, LP5521_REG_LED_PWM_BASE + led->chan_nr, ret = lp55xx_write(chip, LP5521_REG_LED_PWM_BASE + led->chan_nr,
led->brightness); led->brightness);
mutex_unlock(&chip->lock); mutex_unlock(&chip->lock);
return ret;
} }
static ssize_t show_engine_mode(struct device *dev, static ssize_t show_engine_mode(struct device *dev,
@ -501,7 +502,7 @@ static struct lp55xx_device_config lp5521_cfg = {
}, },
.max_channel = LP5521_MAX_LEDS, .max_channel = LP5521_MAX_LEDS,
.post_init_device = lp5521_post_init_device, .post_init_device = lp5521_post_init_device,
.brightness_work_fn = lp5521_led_brightness_work, .brightness_fn = lp5521_led_brightness,
.set_led_current = lp5521_set_led_current, .set_led_current = lp5521_set_led_current,
.firmware_cb = lp5521_firmware_loaded, .firmware_cb = lp5521_firmware_loaded,
.run_engine = lp5521_run_engine, .run_engine = lp5521_run_engine,

View File

@ -802,16 +802,16 @@ leave:
return ret; return ret;
} }
static void lp5523_led_brightness_work(struct work_struct *work) static int lp5523_led_brightness(struct lp55xx_led *led)
{ {
struct lp55xx_led *led = container_of(work, struct lp55xx_led,
brightness_work);
struct lp55xx_chip *chip = led->chip; struct lp55xx_chip *chip = led->chip;
int ret;
mutex_lock(&chip->lock); mutex_lock(&chip->lock);
lp55xx_write(chip, LP5523_REG_LED_PWM_BASE + led->chan_nr, ret = lp55xx_write(chip, LP5523_REG_LED_PWM_BASE + led->chan_nr,
led->brightness); led->brightness);
mutex_unlock(&chip->lock); mutex_unlock(&chip->lock);
return ret;
} }
static LP55XX_DEV_ATTR_RW(engine1_mode, show_engine1_mode, store_engine1_mode); static LP55XX_DEV_ATTR_RW(engine1_mode, show_engine1_mode, store_engine1_mode);
@ -867,7 +867,7 @@ static struct lp55xx_device_config lp5523_cfg = {
}, },
.max_channel = LP5523_MAX_LEDS, .max_channel = LP5523_MAX_LEDS,
.post_init_device = lp5523_post_init_device, .post_init_device = lp5523_post_init_device,
.brightness_work_fn = lp5523_led_brightness_work, .brightness_fn = lp5523_led_brightness,
.set_led_current = lp5523_set_led_current, .set_led_current = lp5523_set_led_current,
.firmware_cb = lp5523_firmware_loaded, .firmware_cb = lp5523_firmware_loaded,
.run_engine = lp5523_run_engine, .run_engine = lp5523_run_engine,

View File

@ -311,10 +311,8 @@ static int lp5562_post_init_device(struct lp55xx_chip *chip)
return 0; return 0;
} }
static void lp5562_led_brightness_work(struct work_struct *work) static int lp5562_led_brightness(struct lp55xx_led *led)
{ {
struct lp55xx_led *led = container_of(work, struct lp55xx_led,
brightness_work);
struct lp55xx_chip *chip = led->chip; struct lp55xx_chip *chip = led->chip;
u8 addr[] = { u8 addr[] = {
LP5562_REG_R_PWM, LP5562_REG_R_PWM,
@ -322,10 +320,13 @@ static void lp5562_led_brightness_work(struct work_struct *work)
LP5562_REG_B_PWM, LP5562_REG_B_PWM,
LP5562_REG_W_PWM, LP5562_REG_W_PWM,
}; };
int ret;
mutex_lock(&chip->lock); mutex_lock(&chip->lock);
lp55xx_write(chip, addr[led->chan_nr], led->brightness); ret = lp55xx_write(chip, addr[led->chan_nr], led->brightness);
mutex_unlock(&chip->lock); mutex_unlock(&chip->lock);
return ret;
} }
static void lp5562_write_program_memory(struct lp55xx_chip *chip, static void lp5562_write_program_memory(struct lp55xx_chip *chip,
@ -503,7 +504,7 @@ static struct lp55xx_device_config lp5562_cfg = {
}, },
.post_init_device = lp5562_post_init_device, .post_init_device = lp5562_post_init_device,
.set_led_current = lp5562_set_led_current, .set_led_current = lp5562_set_led_current,
.brightness_work_fn = lp5562_led_brightness_work, .brightness_fn = lp5562_led_brightness,
.run_engine = lp5562_run_engine, .run_engine = lp5562_run_engine,
.firmware_cb = lp5562_firmware_loaded, .firmware_cb = lp5562_firmware_loaded,
.dev_attr_group = &lp5562_group, .dev_attr_group = &lp5562_group,

View File

@ -134,13 +134,14 @@ static struct attribute *lp55xx_led_attrs[] = {
}; };
ATTRIBUTE_GROUPS(lp55xx_led); ATTRIBUTE_GROUPS(lp55xx_led);
static void lp55xx_set_brightness(struct led_classdev *cdev, static int lp55xx_set_brightness(struct led_classdev *cdev,
enum led_brightness brightness) enum led_brightness brightness)
{ {
struct lp55xx_led *led = cdev_to_lp55xx_led(cdev); struct lp55xx_led *led = cdev_to_lp55xx_led(cdev);
struct lp55xx_device_config *cfg = led->chip->cfg;
led->brightness = (u8)brightness; led->brightness = (u8)brightness;
schedule_work(&led->brightness_work); return cfg->brightness_fn(led);
} }
static int lp55xx_init_led(struct lp55xx_led *led, static int lp55xx_init_led(struct lp55xx_led *led,
@ -172,7 +173,7 @@ static int lp55xx_init_led(struct lp55xx_led *led,
return -EINVAL; return -EINVAL;
} }
led->cdev.brightness_set = lp55xx_set_brightness; led->cdev.brightness_set_blocking = lp55xx_set_brightness;
led->cdev.groups = lp55xx_led_groups; led->cdev.groups = lp55xx_led_groups;
if (pdata->led_config[chan].name) { if (pdata->led_config[chan].name) {
@ -464,7 +465,7 @@ int lp55xx_register_leds(struct lp55xx_led *led, struct lp55xx_chip *chip)
int ret; int ret;
int i; int i;
if (!cfg->brightness_work_fn) { if (!cfg->brightness_fn) {
dev_err(&chip->cl->dev, "empty brightness configuration\n"); dev_err(&chip->cl->dev, "empty brightness configuration\n");
return -EINVAL; return -EINVAL;
} }
@ -481,8 +482,6 @@ int lp55xx_register_leds(struct lp55xx_led *led, struct lp55xx_chip *chip)
if (ret) if (ret)
goto err_init_led; goto err_init_led;
INIT_WORK(&each->brightness_work, cfg->brightness_work_fn);
chip->num_leds++; chip->num_leds++;
each->chip = chip; each->chip = chip;
@ -507,7 +506,6 @@ void lp55xx_unregister_leds(struct lp55xx_led *led, struct lp55xx_chip *chip)
for (i = 0; i < chip->num_leds; i++) { for (i = 0; i < chip->num_leds; i++) {
each = led + i; each = led + i;
led_classdev_unregister(&each->cdev); led_classdev_unregister(&each->cdev);
flush_work(&each->brightness_work);
} }
} }
EXPORT_SYMBOL_GPL(lp55xx_unregister_leds); EXPORT_SYMBOL_GPL(lp55xx_unregister_leds);

View File

@ -95,7 +95,7 @@ struct lp55xx_reg {
* @enable : Chip specific enable command * @enable : Chip specific enable command
* @max_channel : Maximum number of channels * @max_channel : Maximum number of channels
* @post_init_device : Chip specific initialization code * @post_init_device : Chip specific initialization code
* @brightness_work_fn : Brightness work function * @brightness_fn : Brightness function
* @set_led_current : LED current set function * @set_led_current : LED current set function
* @firmware_cb : Call function when the firmware is loaded * @firmware_cb : Call function when the firmware is loaded
* @run_engine : Run internal engine for pattern * @run_engine : Run internal engine for pattern
@ -110,7 +110,7 @@ struct lp55xx_device_config {
int (*post_init_device) (struct lp55xx_chip *chip); int (*post_init_device) (struct lp55xx_chip *chip);
/* access brightness register */ /* access brightness register */
void (*brightness_work_fn)(struct work_struct *work); int (*brightness_fn)(struct lp55xx_led *led);
/* current setting function */ /* current setting function */
void (*set_led_current) (struct lp55xx_led *led, u8 led_current); void (*set_led_current) (struct lp55xx_led *led, u8 led_current);
@ -164,7 +164,6 @@ struct lp55xx_chip {
* @cdev : LED class device * @cdev : LED class device
* @led_current : Current setting at each led channel * @led_current : Current setting at each led channel
* @max_current : Maximun current at each led channel * @max_current : Maximun current at each led channel
* @brightness_work : Workqueue for brightness control
* @brightness : Brightness value * @brightness : Brightness value
* @chip : The lp55xx chip data * @chip : The lp55xx chip data
*/ */
@ -173,7 +172,6 @@ struct lp55xx_led {
struct led_classdev cdev; struct led_classdev cdev;
u8 led_current; u8 led_current;
u8 max_current; u8 max_current;
struct work_struct brightness_work;
u8 brightness; u8 brightness;
struct lp55xx_chip *chip; struct lp55xx_chip *chip;
}; };

View File

@ -272,16 +272,17 @@ static void lp8501_firmware_loaded(struct lp55xx_chip *chip)
lp8501_update_program_memory(chip, fw->data, fw->size); lp8501_update_program_memory(chip, fw->data, fw->size);
} }
static void lp8501_led_brightness_work(struct work_struct *work) static int lp8501_led_brightness(struct lp55xx_led *led)
{ {
struct lp55xx_led *led = container_of(work, struct lp55xx_led,
brightness_work);
struct lp55xx_chip *chip = led->chip; struct lp55xx_chip *chip = led->chip;
int ret;
mutex_lock(&chip->lock); mutex_lock(&chip->lock);
lp55xx_write(chip, LP8501_REG_LED_PWM_BASE + led->chan_nr, ret = lp55xx_write(chip, LP8501_REG_LED_PWM_BASE + led->chan_nr,
led->brightness); led->brightness);
mutex_unlock(&chip->lock); mutex_unlock(&chip->lock);
return ret;
} }
/* Chip specific configurations */ /* Chip specific configurations */
@ -296,7 +297,7 @@ static struct lp55xx_device_config lp8501_cfg = {
}, },
.max_channel = LP8501_MAX_LEDS, .max_channel = LP8501_MAX_LEDS,
.post_init_device = lp8501_post_init_device, .post_init_device = lp8501_post_init_device,
.brightness_work_fn = lp8501_led_brightness_work, .brightness_fn = lp8501_led_brightness,
.set_led_current = lp8501_set_led_current, .set_led_current = lp8501_set_led_current,
.firmware_cb = lp8501_firmware_loaded, .firmware_cb = lp8501_firmware_loaded,
.run_engine = lp8501_run_engine, .run_engine = lp8501_run_engine,

View File

@ -26,10 +26,8 @@
struct lp8788_led { struct lp8788_led {
struct lp8788 *lp; struct lp8788 *lp;
struct mutex lock; struct mutex lock;
struct work_struct work;
struct led_classdev led_dev; struct led_classdev led_dev;
enum lp8788_isink_number isink_num; enum lp8788_isink_number isink_num;
enum led_brightness brightness;
int on; int on;
}; };
@ -76,24 +74,29 @@ static int lp8788_led_init_device(struct lp8788_led *led,
return lp8788_update_bits(led->lp, addr, mask, val); return lp8788_update_bits(led->lp, addr, mask, val);
} }
static void lp8788_led_enable(struct lp8788_led *led, static int lp8788_led_enable(struct lp8788_led *led,
enum lp8788_isink_number num, int on) enum lp8788_isink_number num, int on)
{ {
int ret;
u8 mask = 1 << num; u8 mask = 1 << num;
u8 val = on << num; u8 val = on << num;
if (lp8788_update_bits(led->lp, LP8788_ISINK_CTRL, mask, val)) ret = lp8788_update_bits(led->lp, LP8788_ISINK_CTRL, mask, val);
return; if (ret == 0)
led->on = on; led->on = on;
return ret;
} }
static void lp8788_led_work(struct work_struct *work) static int lp8788_brightness_set(struct led_classdev *led_cdev,
enum led_brightness val)
{ {
struct lp8788_led *led = container_of(work, struct lp8788_led, work); struct lp8788_led *led =
container_of(led_cdev, struct lp8788_led, led_dev);
enum lp8788_isink_number num = led->isink_num; enum lp8788_isink_number num = led->isink_num;
int enable; int enable, ret;
u8 val = led->brightness;
mutex_lock(&led->lock); mutex_lock(&led->lock);
@ -101,28 +104,21 @@ static void lp8788_led_work(struct work_struct *work)
case LP8788_ISINK_1: case LP8788_ISINK_1:
case LP8788_ISINK_2: case LP8788_ISINK_2:
case LP8788_ISINK_3: case LP8788_ISINK_3:
lp8788_write_byte(led->lp, lp8788_pwm_addr[num], val); ret = lp8788_write_byte(led->lp, lp8788_pwm_addr[num], val);
if (ret < 0)
goto unlock;
break; break;
default: default:
mutex_unlock(&led->lock); mutex_unlock(&led->lock);
return; return -EINVAL;
} }
enable = (val > 0) ? 1 : 0; enable = (val > 0) ? 1 : 0;
if (enable != led->on) if (enable != led->on)
lp8788_led_enable(led, num, enable); ret = lp8788_led_enable(led, num, enable);
unlock:
mutex_unlock(&led->lock); mutex_unlock(&led->lock);
} return ret;
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 int lp8788_led_probe(struct platform_device *pdev) static int lp8788_led_probe(struct platform_device *pdev)
@ -139,7 +135,7 @@ static int lp8788_led_probe(struct platform_device *pdev)
led->lp = lp; led->lp = lp;
led->led_dev.max_brightness = MAX_BRIGHTNESS; led->led_dev.max_brightness = MAX_BRIGHTNESS;
led->led_dev.brightness_set = lp8788_brightness_set; led->led_dev.brightness_set_blocking = lp8788_brightness_set;
led_pdata = lp->pdata ? lp->pdata->led_pdata : NULL; led_pdata = lp->pdata ? lp->pdata->led_pdata : NULL;
@ -149,7 +145,6 @@ static int lp8788_led_probe(struct platform_device *pdev)
led->led_dev.name = led_pdata->name; led->led_dev.name = led_pdata->name;
mutex_init(&led->lock); mutex_init(&led->lock);
INIT_WORK(&led->work, lp8788_led_work);
platform_set_drvdata(pdev, led); platform_set_drvdata(pdev, led);
@ -173,7 +168,6 @@ static int lp8788_led_remove(struct platform_device *pdev)
struct lp8788_led *led = platform_get_drvdata(pdev); struct lp8788_led *led = platform_get_drvdata(pdev);
led_classdev_unregister(&led->led_dev); led_classdev_unregister(&led->led_dev);
flush_work(&led->work);
return 0; return 0;
} }

View File

@ -91,26 +91,22 @@
/** /**
* struct lp8860_led - * struct lp8860_led -
* @lock - Lock for reading/writing the device * @lock - Lock for reading/writing the device
* @work - Work item used to off load the brightness register writes
* @client - Pointer to the I2C client * @client - Pointer to the I2C client
* @led_dev - led class device pointer * @led_dev - led class device pointer
* @regmap - Devices register map * @regmap - Devices register map
* @eeprom_regmap - EEPROM register map * @eeprom_regmap - EEPROM register map
* @enable_gpio - VDDIO/EN gpio to enable communication interface * @enable_gpio - VDDIO/EN gpio to enable communication interface
* @regulator - LED supply regulator pointer * @regulator - LED supply regulator pointer
* @brightness - Current brightness value requested
* @label - LED label * @label - LED label
**/ **/
struct lp8860_led { struct lp8860_led {
struct mutex lock; struct mutex lock;
struct work_struct work;
struct i2c_client *client; struct i2c_client *client;
struct led_classdev led_dev; struct led_classdev led_dev;
struct regmap *regmap; struct regmap *regmap;
struct regmap *eeprom_regmap; struct regmap *eeprom_regmap;
struct gpio_desc *enable_gpio; struct gpio_desc *enable_gpio;
struct regulator *regulator; struct regulator *regulator;
enum led_brightness brightness;
const char *label; const char *label;
}; };
@ -212,11 +208,13 @@ out:
return ret; return ret;
} }
static void lp8860_led_brightness_work(struct work_struct *work) static int lp8860_brightness_set(struct led_classdev *led_cdev,
enum led_brightness brt_val)
{ {
struct lp8860_led *led = container_of(work, struct lp8860_led, work); struct lp8860_led *led =
container_of(led_cdev, struct lp8860_led, led_dev);
int disp_brightness = brt_val * 255;
int ret; int ret;
int disp_brightness = led->brightness * 255;
mutex_lock(&led->lock); mutex_lock(&led->lock);
@ -241,16 +239,7 @@ static void lp8860_led_brightness_work(struct work_struct *work)
} }
out: out:
mutex_unlock(&led->lock); mutex_unlock(&led->lock);
} return ret;
static void lp8860_brightness_set(struct led_classdev *led_cdev,
enum led_brightness brt_val)
{
struct lp8860_led *led =
container_of(led_cdev, struct lp8860_led, led_dev);
led->brightness = brt_val;
schedule_work(&led->work);
} }
static int lp8860_init(struct lp8860_led *led) static int lp8860_init(struct lp8860_led *led)
@ -406,10 +395,9 @@ static int lp8860_probe(struct i2c_client *client,
led->client = client; led->client = client;
led->led_dev.name = led->label; led->led_dev.name = led->label;
led->led_dev.max_brightness = LED_FULL; led->led_dev.max_brightness = LED_FULL;
led->led_dev.brightness_set = lp8860_brightness_set; led->led_dev.brightness_set_blocking = lp8860_brightness_set;
mutex_init(&led->lock); mutex_init(&led->lock);
INIT_WORK(&led->work, lp8860_led_brightness_work);
i2c_set_clientdata(client, led); i2c_set_clientdata(client, led);
@ -448,7 +436,6 @@ static int lp8860_remove(struct i2c_client *client)
int ret; int ret;
led_classdev_unregister(&led->led_dev); led_classdev_unregister(&led->led_dev);
cancel_work_sync(&led->work);
if (led->enable_gpio) if (led->enable_gpio)
gpiod_direction_output(led->enable_gpio, 0); gpiod_direction_output(led->enable_gpio, 0);

View File

@ -19,7 +19,6 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/leds.h> #include <linux/leds.h>
#include <linux/workqueue.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/gpio.h> #include <linux/gpio.h>
#include <linux/slab.h> #include <linux/slab.h>
@ -28,15 +27,14 @@
struct lt3593_led_data { struct lt3593_led_data {
struct led_classdev cdev; struct led_classdev cdev;
unsigned gpio; unsigned gpio;
struct work_struct work;
u8 new_level;
}; };
static void lt3593_led_work(struct work_struct *work) static int lt3593_led_set(struct led_classdev *led_cdev,
enum led_brightness value)
{ {
int pulses;
struct lt3593_led_data *led_dat = struct lt3593_led_data *led_dat =
container_of(work, struct lt3593_led_data, work); container_of(led_cdev, struct lt3593_led_data, cdev);
int pulses;
/* /*
* The LT3593 resets its internal current level register to the maximum * The LT3593 resets its internal current level register to the maximum
@ -47,18 +45,18 @@ static void lt3593_led_work(struct work_struct *work)
* applied is to the output driver. * applied is to the output driver.
*/ */
if (led_dat->new_level == 0) { if (value == 0) {
gpio_set_value_cansleep(led_dat->gpio, 0); gpio_set_value_cansleep(led_dat->gpio, 0);
return; return 0;
} }
pulses = 32 - (led_dat->new_level * 32) / 255; pulses = 32 - (value * 32) / 255;
if (pulses == 0) { if (pulses == 0) {
gpio_set_value_cansleep(led_dat->gpio, 0); gpio_set_value_cansleep(led_dat->gpio, 0);
mdelay(1); mdelay(1);
gpio_set_value_cansleep(led_dat->gpio, 1); gpio_set_value_cansleep(led_dat->gpio, 1);
return; return 0;
} }
gpio_set_value_cansleep(led_dat->gpio, 1); gpio_set_value_cansleep(led_dat->gpio, 1);
@ -69,16 +67,8 @@ static void lt3593_led_work(struct work_struct *work)
gpio_set_value_cansleep(led_dat->gpio, 1); gpio_set_value_cansleep(led_dat->gpio, 1);
udelay(1); udelay(1);
} }
}
static void lt3593_led_set(struct led_classdev *led_cdev, return 0;
enum led_brightness value)
{
struct lt3593_led_data *led_dat =
container_of(led_cdev, struct lt3593_led_data, cdev);
led_dat->new_level = value;
schedule_work(&led_dat->work);
} }
static int create_lt3593_led(const struct gpio_led *template, static int create_lt3593_led(const struct gpio_led *template,
@ -97,7 +87,7 @@ static int create_lt3593_led(const struct gpio_led *template,
led_dat->cdev.default_trigger = template->default_trigger; led_dat->cdev.default_trigger = template->default_trigger;
led_dat->gpio = template->gpio; led_dat->gpio = template->gpio;
led_dat->cdev.brightness_set = lt3593_led_set; led_dat->cdev.brightness_set_blocking = lt3593_led_set;
state = (template->default_state == LEDS_GPIO_DEFSTATE_ON); state = (template->default_state == LEDS_GPIO_DEFSTATE_ON);
led_dat->cdev.brightness = state ? LED_FULL : LED_OFF; led_dat->cdev.brightness = state ? LED_FULL : LED_OFF;
@ -111,8 +101,6 @@ static int create_lt3593_led(const struct gpio_led *template,
if (ret < 0) if (ret < 0)
return ret; return ret;
INIT_WORK(&led_dat->work, lt3593_led_work);
ret = led_classdev_register(parent, &led_dat->cdev); ret = led_classdev_register(parent, &led_dat->cdev);
if (ret < 0) if (ret < 0)
return ret; return ret;
@ -129,7 +117,6 @@ static void delete_lt3593_led(struct lt3593_led_data *led)
return; return;
led_classdev_unregister(&led->cdev); led_classdev_unregister(&led->cdev);
cancel_work_sync(&led->work);
} }
static int lt3593_led_probe(struct platform_device *pdev) static int lt3593_led_probe(struct platform_device *pdev)

View File

@ -20,7 +20,6 @@
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/regmap.h> #include <linux/regmap.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/workqueue.h>
#include <media/v4l2-flash-led-class.h> #include <media/v4l2-flash-led-class.h>
#define MODE_OFF 0 #define MODE_OFF 0
@ -62,8 +61,6 @@ struct max77693_sub_led {
int fled_id; int fled_id;
/* corresponding LED Flash class device */ /* corresponding LED Flash class device */
struct led_classdev_flash fled_cdev; struct led_classdev_flash fled_cdev;
/* assures led-triggers compatibility */
struct work_struct work_brightness_set;
/* V4L2 Flash device */ /* V4L2 Flash device */
struct v4l2_flash *v4l2_flash; struct v4l2_flash *v4l2_flash;
@ -463,10 +460,14 @@ static int max77693_setup(struct max77693_led_device *led,
return max77693_set_mode_reg(led, MODE_OFF); return max77693_set_mode_reg(led, MODE_OFF);
} }
static int __max77693_led_brightness_set(struct max77693_led_device *led, /* LED subsystem callbacks */
int fled_id, enum led_brightness value) static int max77693_led_brightness_set(struct led_classdev *led_cdev,
enum led_brightness value)
{ {
int ret; struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
struct max77693_sub_led *sub_led = flcdev_to_sub_led(fled_cdev);
struct max77693_led_device *led = sub_led_to_led(sub_led);
int fled_id = sub_led->fled_id, ret;
mutex_lock(&led->lock); mutex_lock(&led->lock);
@ -494,45 +495,10 @@ static int __max77693_led_brightness_set(struct max77693_led_device *led,
ret); ret);
unlock: unlock:
mutex_unlock(&led->lock); mutex_unlock(&led->lock);
return ret; return ret;
} }
static void max77693_led_brightness_set_work(
struct work_struct *work)
{
struct max77693_sub_led *sub_led =
container_of(work, struct max77693_sub_led,
work_brightness_set);
struct max77693_led_device *led = sub_led_to_led(sub_led);
__max77693_led_brightness_set(led, sub_led->fled_id,
sub_led->torch_brightness);
}
/* LED subsystem callbacks */
static int max77693_led_brightness_set_sync(
struct led_classdev *led_cdev,
enum led_brightness value)
{
struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
struct max77693_sub_led *sub_led = flcdev_to_sub_led(fled_cdev);
struct max77693_led_device *led = sub_led_to_led(sub_led);
return __max77693_led_brightness_set(led, sub_led->fled_id, value);
}
static void max77693_led_brightness_set(
struct led_classdev *led_cdev,
enum led_brightness value)
{
struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
struct max77693_sub_led *sub_led = flcdev_to_sub_led(fled_cdev);
sub_led->torch_brightness = value;
schedule_work(&sub_led->work_brightness_set);
}
static int max77693_led_flash_brightness_set( static int max77693_led_flash_brightness_set(
struct led_classdev_flash *fled_cdev, struct led_classdev_flash *fled_cdev,
u32 brightness) u32 brightness)
@ -682,6 +648,7 @@ static int max77693_led_parse_dt(struct max77693_led_device *led,
if (sub_nodes[fled_id]) { if (sub_nodes[fled_id]) {
dev_err(dev, dev_err(dev,
"Conflicting \"led-sources\" DT properties\n"); "Conflicting \"led-sources\" DT properties\n");
of_node_put(child_node);
return -EINVAL; return -EINVAL;
} }
@ -931,16 +898,13 @@ static void max77693_init_fled_cdev(struct max77693_sub_led *sub_led,
led_cdev->name = led_cfg->label[fled_id]; led_cdev->name = led_cfg->label[fled_id];
led_cdev->brightness_set = max77693_led_brightness_set; led_cdev->brightness_set_blocking = max77693_led_brightness_set;
led_cdev->brightness_set_sync = max77693_led_brightness_set_sync;
led_cdev->max_brightness = (led->iout_joint ? led_cdev->max_brightness = (led->iout_joint ?
led_cfg->iout_torch_max[FLED1] + led_cfg->iout_torch_max[FLED1] +
led_cfg->iout_torch_max[FLED2] : led_cfg->iout_torch_max[FLED2] :
led_cfg->iout_torch_max[fled_id]) / led_cfg->iout_torch_max[fled_id]) /
TORCH_IOUT_STEP; TORCH_IOUT_STEP;
led_cdev->flags |= LED_DEV_CAP_FLASH; led_cdev->flags |= LED_DEV_CAP_FLASH;
INIT_WORK(&sub_led->work_brightness_set,
max77693_led_brightness_set_work);
max77693_init_flash_settings(sub_led, led_cfg); max77693_init_flash_settings(sub_led, led_cfg);
@ -1062,13 +1026,11 @@ static int max77693_led_remove(struct platform_device *pdev)
if (led->iout_joint || max77693_fled_used(led, FLED1)) { if (led->iout_joint || max77693_fled_used(led, FLED1)) {
v4l2_flash_release(sub_leds[FLED1].v4l2_flash); v4l2_flash_release(sub_leds[FLED1].v4l2_flash);
led_classdev_flash_unregister(&sub_leds[FLED1].fled_cdev); led_classdev_flash_unregister(&sub_leds[FLED1].fled_cdev);
cancel_work_sync(&sub_leds[FLED1].work_brightness_set);
} }
if (!led->iout_joint && max77693_fled_used(led, FLED2)) { if (!led->iout_joint && max77693_fled_used(led, FLED2)) {
v4l2_flash_release(sub_leds[FLED2].v4l2_flash); v4l2_flash_release(sub_leds[FLED2].v4l2_flash);
led_classdev_flash_unregister(&sub_leds[FLED2].fled_cdev); led_classdev_flash_unregister(&sub_leds[FLED2].fled_cdev);
cancel_work_sync(&sub_leds[FLED2].work_brightness_set);
} }
mutex_destroy(&led->lock); mutex_destroy(&led->lock);

View File

@ -13,7 +13,6 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/workqueue.h>
#include <linux/leds.h> #include <linux/leds.h>
#include <linux/mfd/max8997.h> #include <linux/mfd/max8997.h>
#include <linux/mfd/max8997-private.h> #include <linux/mfd/max8997-private.h>

View File

@ -20,7 +20,6 @@
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/leds.h> #include <linux/leds.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/workqueue.h>
#include <linux/mfd/mc13xxx.h> #include <linux/mfd/mc13xxx.h>
struct mc13xxx_led_devtype { struct mc13xxx_led_devtype {
@ -32,8 +31,6 @@ struct mc13xxx_led_devtype {
struct mc13xxx_led { struct mc13xxx_led {
struct led_classdev cdev; struct led_classdev cdev;
struct work_struct work;
enum led_brightness new_brightness;
int id; int id;
struct mc13xxx_leds *leds; struct mc13xxx_leds *leds;
}; };
@ -55,9 +52,11 @@ static unsigned int mc13xxx_max_brightness(int id)
return 0x3f; return 0x3f;
} }
static void mc13xxx_led_work(struct work_struct *work) static int mc13xxx_led_set(struct led_classdev *led_cdev,
enum led_brightness value)
{ {
struct mc13xxx_led *led = container_of(work, struct mc13xxx_led, work); struct mc13xxx_led *led =
container_of(led_cdev, struct mc13xxx_led, cdev);
struct mc13xxx_leds *leds = led->leds; struct mc13xxx_leds *leds = led->leds;
unsigned int reg, bank, off, shift; unsigned int reg, bank, off, shift;
@ -105,19 +104,9 @@ static void mc13xxx_led_work(struct work_struct *work)
BUG(); BUG();
} }
mc13xxx_reg_rmw(leds->master, leds->devtype->ledctrl_base + reg, return mc13xxx_reg_rmw(leds->master, leds->devtype->ledctrl_base + reg,
mc13xxx_max_brightness(led->id) << shift, mc13xxx_max_brightness(led->id) << shift,
led->new_brightness << shift); value << shift);
}
static void mc13xxx_led_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
struct mc13xxx_led *led =
container_of(led_cdev, struct mc13xxx_led, cdev);
led->new_brightness = value;
schedule_work(&led->work);
} }
#ifdef CONFIG_OF #ifdef CONFIG_OF
@ -257,11 +246,9 @@ static int __init mc13xxx_led_probe(struct platform_device *pdev)
leds->led[i].cdev.name = name; leds->led[i].cdev.name = name;
leds->led[i].cdev.default_trigger = trig; leds->led[i].cdev.default_trigger = trig;
leds->led[i].cdev.flags = LED_CORE_SUSPENDRESUME; leds->led[i].cdev.flags = LED_CORE_SUSPENDRESUME;
leds->led[i].cdev.brightness_set = mc13xxx_led_set; leds->led[i].cdev.brightness_set_blocking = mc13xxx_led_set;
leds->led[i].cdev.max_brightness = mc13xxx_max_brightness(id); leds->led[i].cdev.max_brightness = mc13xxx_max_brightness(id);
INIT_WORK(&leds->led[i].work, mc13xxx_led_work);
ret = led_classdev_register(dev->parent, &leds->led[i].cdev); ret = led_classdev_register(dev->parent, &leds->led[i].cdev);
if (ret) { if (ret) {
dev_err(dev, "Failed to register LED %i\n", id); dev_err(dev, "Failed to register LED %i\n", id);
@ -270,10 +257,8 @@ static int __init mc13xxx_led_probe(struct platform_device *pdev)
} }
if (ret) if (ret)
while (--i >= 0) { while (--i >= 0)
led_classdev_unregister(&leds->led[i].cdev); led_classdev_unregister(&leds->led[i].cdev);
cancel_work_sync(&leds->led[i].work);
}
return ret; return ret;
} }
@ -283,10 +268,8 @@ static int mc13xxx_led_remove(struct platform_device *pdev)
struct mc13xxx_leds *leds = platform_get_drvdata(pdev); struct mc13xxx_leds *leds = platform_get_drvdata(pdev);
int i; int i;
for (i = 0; i < leds->num_leds; i++) { for (i = 0; i < leds->num_leds; i++)
led_classdev_unregister(&leds->led[i].cdev); led_classdev_unregister(&leds->led[i].cdev);
cancel_work_sync(&leds->led[i].work);
}
return 0; return 0;
} }

View File

@ -45,24 +45,12 @@ struct ns2_led_data {
unsigned cmd; unsigned cmd;
unsigned slow; unsigned slow;
bool can_sleep; bool can_sleep;
int mode_index;
unsigned char sata; /* True when SATA mode active. */ unsigned char sata; /* True when SATA mode active. */
rwlock_t rw_lock; /* Lock GPIOs. */ rwlock_t rw_lock; /* Lock GPIOs. */
struct work_struct work;
int num_modes; int num_modes;
struct ns2_led_modval *modval; struct ns2_led_modval *modval;
}; };
static void ns2_led_work(struct work_struct *work)
{
struct ns2_led_data *led_dat =
container_of(work, struct ns2_led_data, work);
int i = led_dat->mode_index;
gpio_set_value_cansleep(led_dat->cmd, led_dat->modval[i].cmd_level);
gpio_set_value_cansleep(led_dat->slow, led_dat->modval[i].slow_level);
}
static int ns2_led_get_mode(struct ns2_led_data *led_dat, static int ns2_led_get_mode(struct ns2_led_data *led_dat,
enum ns2_led_modes *mode) enum ns2_led_modes *mode)
{ {
@ -112,8 +100,8 @@ static void ns2_led_set_mode(struct ns2_led_data *led_dat,
goto exit_unlock; goto exit_unlock;
} }
led_dat->mode_index = i; gpio_set_value_cansleep(led_dat->cmd, led_dat->modval[i].cmd_level);
schedule_work(&led_dat->work); gpio_set_value_cansleep(led_dat->slow, led_dat->modval[i].slow_level);
exit_unlock: exit_unlock:
write_unlock_irqrestore(&led_dat->rw_lock, flags); write_unlock_irqrestore(&led_dat->rw_lock, flags);
@ -136,6 +124,13 @@ static void ns2_led_set(struct led_classdev *led_cdev,
ns2_led_set_mode(led_dat, mode); ns2_led_set_mode(led_dat, mode);
} }
static int ns2_led_set_blocking(struct led_classdev *led_cdev,
enum led_brightness value)
{
ns2_led_set(led_cdev, value);
return 0;
}
static ssize_t ns2_led_sata_store(struct device *dev, static ssize_t ns2_led_sata_store(struct device *dev,
struct device_attribute *attr, struct device_attribute *attr,
const char *buff, size_t count) const char *buff, size_t count)
@ -219,13 +214,16 @@ create_ns2_led(struct platform_device *pdev, struct ns2_led_data *led_dat,
led_dat->cdev.name = template->name; led_dat->cdev.name = template->name;
led_dat->cdev.default_trigger = template->default_trigger; led_dat->cdev.default_trigger = template->default_trigger;
led_dat->cdev.blink_set = NULL; led_dat->cdev.blink_set = NULL;
led_dat->cdev.brightness_set = ns2_led_set;
led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME; led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME;
led_dat->cdev.groups = ns2_led_groups; led_dat->cdev.groups = ns2_led_groups;
led_dat->cmd = template->cmd; led_dat->cmd = template->cmd;
led_dat->slow = template->slow; led_dat->slow = template->slow;
led_dat->can_sleep = gpio_cansleep(led_dat->cmd) | led_dat->can_sleep = gpio_cansleep(led_dat->cmd) |
gpio_cansleep(led_dat->slow); gpio_cansleep(led_dat->slow);
if (led_dat->can_sleep)
led_dat->cdev.brightness_set_blocking = ns2_led_set_blocking;
else
led_dat->cdev.brightness_set = ns2_led_set;
led_dat->modval = template->modval; led_dat->modval = template->modval;
led_dat->num_modes = template->num_modes; led_dat->num_modes = template->num_modes;
@ -238,8 +236,6 @@ create_ns2_led(struct platform_device *pdev, struct ns2_led_data *led_dat,
led_dat->cdev.brightness = led_dat->cdev.brightness =
(mode == NS_V2_LED_OFF) ? LED_OFF : LED_FULL; (mode == NS_V2_LED_OFF) ? LED_OFF : LED_FULL;
INIT_WORK(&led_dat->work, ns2_led_work);
ret = led_classdev_register(&pdev->dev, &led_dat->cdev); ret = led_classdev_register(&pdev->dev, &led_dat->cdev);
if (ret < 0) if (ret < 0)
return ret; return ret;
@ -250,7 +246,6 @@ create_ns2_led(struct platform_device *pdev, struct ns2_led_data *led_dat,
static void delete_ns2_led(struct ns2_led_data *led_dat) static void delete_ns2_led(struct ns2_led_data *led_dat)
{ {
led_classdev_unregister(&led_dat->cdev); led_classdev_unregister(&led_dat->cdev);
cancel_work_sync(&led_dat->work);
} }
#ifdef CONFIG_OF_GPIO #ifdef CONFIG_OF_GPIO

View File

@ -158,7 +158,7 @@ static void pca9532_setled(struct pca9532_led *led)
mutex_unlock(&data->update_lock); mutex_unlock(&data->update_lock);
} }
static void pca9532_set_brightness(struct led_classdev *led_cdev, static int pca9532_set_brightness(struct led_classdev *led_cdev,
enum led_brightness value) enum led_brightness value)
{ {
int err = 0; int err = 0;
@ -172,9 +172,12 @@ static void pca9532_set_brightness(struct led_classdev *led_cdev,
led->state = PCA9532_PWM0; /* Thecus: hardcode one pwm */ led->state = PCA9532_PWM0; /* Thecus: hardcode one pwm */
err = pca9532_calcpwm(led->client, 0, 0, value); err = pca9532_calcpwm(led->client, 0, 0, value);
if (err) if (err)
return; /* XXX: led api doesn't allow error code? */ return err;
} }
schedule_work(&led->work); if (led->state == PCA9532_PWM0)
pca9532_setpwm(led->client, 0);
pca9532_setled(led);
return err;
} }
static int pca9532_set_blink(struct led_classdev *led_cdev, static int pca9532_set_blink(struct led_classdev *led_cdev,
@ -198,7 +201,10 @@ static int pca9532_set_blink(struct led_classdev *led_cdev,
err = pca9532_calcpwm(client, 0, psc, led_cdev->brightness); err = pca9532_calcpwm(client, 0, psc, led_cdev->brightness);
if (err) if (err)
return err; return err;
schedule_work(&led->work); if (led->state == PCA9532_PWM0)
pca9532_setpwm(led->client, 0);
pca9532_setled(led);
return 0; return 0;
} }
@ -233,15 +239,6 @@ static void pca9532_input_work(struct work_struct *work)
mutex_unlock(&data->update_lock); mutex_unlock(&data->update_lock);
} }
static void pca9532_led_work(struct work_struct *work)
{
struct pca9532_led *led;
led = container_of(work, struct pca9532_led, work);
if (led->state == PCA9532_PWM0)
pca9532_setpwm(led->client, 0);
pca9532_setled(led);
}
#ifdef CONFIG_LEDS_PCA9532_GPIO #ifdef CONFIG_LEDS_PCA9532_GPIO
static int pca9532_gpio_request_pin(struct gpio_chip *gc, unsigned offset) static int pca9532_gpio_request_pin(struct gpio_chip *gc, unsigned offset)
{ {
@ -307,7 +304,6 @@ static int pca9532_destroy_devices(struct pca9532_data *data, int n_devs)
break; break;
case PCA9532_TYPE_LED: case PCA9532_TYPE_LED:
led_classdev_unregister(&data->leds[i].ldev); led_classdev_unregister(&data->leds[i].ldev);
cancel_work_sync(&data->leds[i].work);
break; break;
case PCA9532_TYPE_N2100_BEEP: case PCA9532_TYPE_N2100_BEEP:
if (data->idev != NULL) { if (data->idev != NULL) {
@ -359,9 +355,9 @@ static int pca9532_configure(struct i2c_client *client,
led->name = pled->name; led->name = pled->name;
led->ldev.name = led->name; led->ldev.name = led->name;
led->ldev.brightness = LED_OFF; led->ldev.brightness = LED_OFF;
led->ldev.brightness_set = pca9532_set_brightness; led->ldev.brightness_set_blocking =
pca9532_set_brightness;
led->ldev.blink_set = pca9532_set_blink; led->ldev.blink_set = pca9532_set_blink;
INIT_WORK(&led->work, pca9532_led_work);
err = led_classdev_register(&client->dev, &led->ldev); err = led_classdev_register(&client->dev, &led->ldev);
if (err < 0) { if (err < 0) {
dev_err(&client->dev, dev_err(&client->dev,

View File

@ -47,7 +47,6 @@
#include <linux/leds.h> #include <linux/leds.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/workqueue.h>
#include <linux/slab.h> #include <linux/slab.h>
/* LED select registers determine the source that drives LED outputs */ /* LED select registers determine the source that drives LED outputs */
@ -110,8 +109,6 @@ struct pca955x {
struct pca955x_led { struct pca955x_led {
struct pca955x *pca955x; struct pca955x *pca955x;
struct work_struct work;
enum led_brightness brightness;
struct led_classdev led_cdev; struct led_classdev led_cdev;
int led_num; /* 0 .. 15 potentially */ int led_num; /* 0 .. 15 potentially */
char name[32]; char name[32];
@ -193,7 +190,8 @@ static u8 pca955x_read_ls(struct i2c_client *client, int n)
pca95xx_num_input_regs(pca955x->chipdef->bits) + 4 + n); pca95xx_num_input_regs(pca955x->chipdef->bits) + 4 + n);
} }
static void pca955x_led_work(struct work_struct *work) static int pca955x_led_set(struct led_classdev *led_cdev,
enum led_brightness value)
{ {
struct pca955x_led *pca955x_led; struct pca955x_led *pca955x_led;
struct pca955x *pca955x; struct pca955x *pca955x;
@ -201,7 +199,7 @@ static void pca955x_led_work(struct work_struct *work)
int chip_ls; /* which LSx to use (0-3 potentially) */ int chip_ls; /* which LSx to use (0-3 potentially) */
int ls_led; /* which set of bits within LSx to use (0-3) */ int ls_led; /* which set of bits within LSx to use (0-3) */
pca955x_led = container_of(work, struct pca955x_led, work); pca955x_led = container_of(led_cdev, struct pca955x_led, led_cdev);
pca955x = pca955x_led->pca955x; pca955x = pca955x_led->pca955x;
chip_ls = pca955x_led->led_num / 4; chip_ls = pca955x_led->led_num / 4;
@ -211,7 +209,7 @@ static void pca955x_led_work(struct work_struct *work)
ls = pca955x_read_ls(pca955x->client, chip_ls); ls = pca955x_read_ls(pca955x->client, chip_ls);
switch (pca955x_led->brightness) { switch (value) {
case LED_FULL: case LED_FULL:
ls = pca955x_ledsel(ls, ls_led, PCA955X_LS_LED_ON); ls = pca955x_ledsel(ls, ls_led, PCA955X_LS_LED_ON);
break; break;
@ -230,7 +228,7 @@ static void pca955x_led_work(struct work_struct *work)
* just turning off for all other values. * just turning off for all other values.
*/ */
pca955x_write_pwm(pca955x->client, 1, pca955x_write_pwm(pca955x->client, 1,
255 - pca955x_led->brightness); 255 - value);
ls = pca955x_ledsel(ls, ls_led, PCA955X_LS_BLINK1); ls = pca955x_ledsel(ls, ls_led, PCA955X_LS_BLINK1);
break; break;
} }
@ -238,21 +236,8 @@ static void pca955x_led_work(struct work_struct *work)
pca955x_write_ls(pca955x->client, chip_ls, ls); pca955x_write_ls(pca955x->client, chip_ls, ls);
mutex_unlock(&pca955x->lock); mutex_unlock(&pca955x->lock);
}
static void pca955x_led_set(struct led_classdev *led_cdev, enum led_brightness value) return 0;
{
struct pca955x_led *pca955x;
pca955x = container_of(led_cdev, struct pca955x_led, led_cdev);
pca955x->brightness = value;
/*
* Must use workqueue for the actual I/O since I2C operations
* can sleep.
*/
schedule_work(&pca955x->work);
} }
static int pca955x_probe(struct i2c_client *client, static int pca955x_probe(struct i2c_client *client,
@ -328,9 +313,7 @@ static int pca955x_probe(struct i2c_client *client,
} }
pca955x_led->led_cdev.name = pca955x_led->name; pca955x_led->led_cdev.name = pca955x_led->name;
pca955x_led->led_cdev.brightness_set = pca955x_led_set; pca955x_led->led_cdev.brightness_set_blocking = pca955x_led_set;
INIT_WORK(&pca955x_led->work, pca955x_led_work);
err = led_classdev_register(&client->dev, err = led_classdev_register(&client->dev,
&pca955x_led->led_cdev); &pca955x_led->led_cdev);
@ -355,10 +338,8 @@ static int pca955x_probe(struct i2c_client *client,
return 0; return 0;
exit: exit:
while (i--) { while (i--)
led_classdev_unregister(&pca955x->leds[i].led_cdev); led_classdev_unregister(&pca955x->leds[i].led_cdev);
cancel_work_sync(&pca955x->leds[i].work);
}
return err; return err;
} }
@ -368,10 +349,8 @@ static int pca955x_remove(struct i2c_client *client)
struct pca955x *pca955x = i2c_get_clientdata(client); struct pca955x *pca955x = i2c_get_clientdata(client);
int i; int i;
for (i = 0; i < pca955x->chipdef->bits; i++) { for (i = 0; i < pca955x->chipdef->bits; i++)
led_classdev_unregister(&pca955x->leds[i].led_cdev); led_classdev_unregister(&pca955x->leds[i].led_cdev);
cancel_work_sync(&pca955x->leds[i].work);
}
return 0; return 0;
} }

View File

@ -32,7 +32,6 @@
#include <linux/leds.h> #include <linux/leds.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/workqueue.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/platform_data/leds-pca963x.h> #include <linux/platform_data/leds-pca963x.h>
@ -96,11 +95,6 @@ static const struct i2c_device_id pca963x_id[] = {
}; };
MODULE_DEVICE_TABLE(i2c, pca963x_id); MODULE_DEVICE_TABLE(i2c, pca963x_id);
enum pca963x_cmd {
BRIGHTNESS_SET,
BLINK_SET,
};
struct pca963x_led; struct pca963x_led;
struct pca963x { struct pca963x {
@ -112,47 +106,52 @@ struct pca963x {
struct pca963x_led { struct pca963x_led {
struct pca963x *chip; struct pca963x *chip;
struct work_struct work;
enum led_brightness brightness;
struct led_classdev led_cdev; struct led_classdev led_cdev;
int led_num; /* 0 .. 15 potentially */ int led_num; /* 0 .. 15 potentially */
enum pca963x_cmd cmd;
char name[32]; char name[32];
u8 gdc; u8 gdc;
u8 gfrq; u8 gfrq;
}; };
static void pca963x_brightness_work(struct pca963x_led *pca963x) static int pca963x_brightness(struct pca963x_led *pca963x,
enum led_brightness brightness)
{ {
u8 ledout_addr = pca963x->chip->chipdef->ledout_base u8 ledout_addr = pca963x->chip->chipdef->ledout_base
+ (pca963x->led_num / 4); + (pca963x->led_num / 4);
u8 ledout; u8 ledout;
int shift = 2 * (pca963x->led_num % 4); int shift = 2 * (pca963x->led_num % 4);
u8 mask = 0x3 << shift; u8 mask = 0x3 << shift;
int ret;
mutex_lock(&pca963x->chip->mutex); mutex_lock(&pca963x->chip->mutex);
ledout = i2c_smbus_read_byte_data(pca963x->chip->client, ledout_addr); ledout = i2c_smbus_read_byte_data(pca963x->chip->client, ledout_addr);
switch (pca963x->brightness) { switch (brightness) {
case LED_FULL: case LED_FULL:
i2c_smbus_write_byte_data(pca963x->chip->client, ledout_addr, ret = i2c_smbus_write_byte_data(pca963x->chip->client,
ledout_addr,
(ledout & ~mask) | (PCA963X_LED_ON << shift)); (ledout & ~mask) | (PCA963X_LED_ON << shift));
break; break;
case LED_OFF: case LED_OFF:
i2c_smbus_write_byte_data(pca963x->chip->client, ledout_addr, ret = i2c_smbus_write_byte_data(pca963x->chip->client,
ledout & ~mask); ledout_addr, ledout & ~mask);
break; break;
default: default:
i2c_smbus_write_byte_data(pca963x->chip->client, ret = i2c_smbus_write_byte_data(pca963x->chip->client,
PCA963X_PWM_BASE + pca963x->led_num, PCA963X_PWM_BASE + pca963x->led_num,
pca963x->brightness); brightness);
i2c_smbus_write_byte_data(pca963x->chip->client, ledout_addr, if (ret < 0)
goto unlock;
ret = i2c_smbus_write_byte_data(pca963x->chip->client,
ledout_addr,
(ledout & ~mask) | (PCA963X_LED_PWM << shift)); (ledout & ~mask) | (PCA963X_LED_PWM << shift));
break; break;
} }
unlock:
mutex_unlock(&pca963x->chip->mutex); mutex_unlock(&pca963x->chip->mutex);
return ret;
} }
static void pca963x_blink_work(struct pca963x_led *pca963x) static void pca963x_blink(struct pca963x_led *pca963x)
{ {
u8 ledout_addr = pca963x->chip->chipdef->ledout_base + u8 ledout_addr = pca963x->chip->chipdef->ledout_base +
(pca963x->led_num / 4); (pca963x->led_num / 4);
@ -180,36 +179,14 @@ static void pca963x_blink_work(struct pca963x_led *pca963x)
mutex_unlock(&pca963x->chip->mutex); mutex_unlock(&pca963x->chip->mutex);
} }
static void pca963x_work(struct work_struct *work) static int pca963x_led_set(struct led_classdev *led_cdev,
{
struct pca963x_led *pca963x = container_of(work,
struct pca963x_led, work);
switch (pca963x->cmd) {
case BRIGHTNESS_SET:
pca963x_brightness_work(pca963x);
break;
case BLINK_SET:
pca963x_blink_work(pca963x);
break;
}
}
static void pca963x_led_set(struct led_classdev *led_cdev,
enum led_brightness value) enum led_brightness value)
{ {
struct pca963x_led *pca963x; struct pca963x_led *pca963x;
pca963x = container_of(led_cdev, struct pca963x_led, led_cdev); pca963x = container_of(led_cdev, struct pca963x_led, led_cdev);
pca963x->cmd = BRIGHTNESS_SET; return pca963x_brightness(pca963x, value);
pca963x->brightness = value;
/*
* Must use workqueue for the actual I/O since I2C operations
* can sleep.
*/
schedule_work(&pca963x->work);
} }
static int pca963x_blink_set(struct led_classdev *led_cdev, static int pca963x_blink_set(struct led_classdev *led_cdev,
@ -254,15 +231,10 @@ static int pca963x_blink_set(struct led_classdev *led_cdev,
*/ */
gfrq = (period * 24 / 1000) - 1; gfrq = (period * 24 / 1000) - 1;
pca963x->cmd = BLINK_SET;
pca963x->gdc = gdc; pca963x->gdc = gdc;
pca963x->gfrq = gfrq; pca963x->gfrq = gfrq;
/* pca963x_blink(pca963x);
* Must use workqueue for the actual I/O since I2C operations
* can sleep.
*/
schedule_work(&pca963x->work);
*delay_on = time_on; *delay_on = time_on;
*delay_off = time_off; *delay_off = time_off;
@ -409,13 +381,11 @@ static int pca963x_probe(struct i2c_client *client,
client->addr, i); client->addr, i);
pca963x[i].led_cdev.name = pca963x[i].name; pca963x[i].led_cdev.name = pca963x[i].name;
pca963x[i].led_cdev.brightness_set = pca963x_led_set; pca963x[i].led_cdev.brightness_set_blocking = pca963x_led_set;
if (pdata && pdata->blink_type == PCA963X_HW_BLINK) if (pdata && pdata->blink_type == PCA963X_HW_BLINK)
pca963x[i].led_cdev.blink_set = pca963x_blink_set; pca963x[i].led_cdev.blink_set = pca963x_blink_set;
INIT_WORK(&pca963x[i].work, pca963x_work);
err = led_classdev_register(&client->dev, &pca963x[i].led_cdev); err = led_classdev_register(&client->dev, &pca963x[i].led_cdev);
if (err < 0) if (err < 0)
goto exit; goto exit;
@ -435,10 +405,8 @@ static int pca963x_probe(struct i2c_client *client,
return 0; return 0;
exit: exit:
while (i--) { while (i--)
led_classdev_unregister(&pca963x[i].led_cdev); led_classdev_unregister(&pca963x[i].led_cdev);
cancel_work_sync(&pca963x[i].work);
}
return err; return err;
} }
@ -448,10 +416,8 @@ static int pca963x_remove(struct i2c_client *client)
struct pca963x *pca963x = i2c_get_clientdata(client); struct pca963x *pca963x = i2c_get_clientdata(client);
int i; int i;
for (i = 0; i < pca963x->chipdef->n_leds; i++) { for (i = 0; i < pca963x->chipdef->n_leds; i++)
led_classdev_unregister(&pca963x->leds[i].led_cdev); led_classdev_unregister(&pca963x->leds[i].led_cdev);
cancel_work_sync(&pca963x->leds[i].work);
}
return 0; return 0;
} }

View File

@ -77,7 +77,7 @@ static int powernv_get_led_type(const char *led_type_desc)
* This function is called from work queue task context when ever it gets * This function is called from work queue task context when ever it gets
* scheduled. This function can sleep at opal_async_wait_response call. * scheduled. This function can sleep at opal_async_wait_response call.
*/ */
static void powernv_led_set(struct powernv_led_data *powernv_led, static int powernv_led_set(struct powernv_led_data *powernv_led,
enum led_brightness value) enum led_brightness value)
{ {
int rc, token; int rc, token;
@ -99,7 +99,7 @@ static void powernv_led_set(struct powernv_led_data *powernv_led,
if (token != -ERESTARTSYS) if (token != -ERESTARTSYS)
dev_err(dev, "%s: Couldn't get OPAL async token\n", dev_err(dev, "%s: Couldn't get OPAL async token\n",
__func__); __func__);
return; return token;
} }
rc = opal_leds_set_ind(token, powernv_led->loc_code, rc = opal_leds_set_ind(token, powernv_led->loc_code,
@ -125,6 +125,7 @@ static void powernv_led_set(struct powernv_led_data *powernv_led,
out_token: out_token:
opal_async_release_token(token); opal_async_release_token(token);
return rc;
} }
/* /*
@ -173,20 +174,23 @@ static enum led_brightness powernv_led_get(struct powernv_led_data *powernv_led)
* LED classdev 'brightness_get' function. This schedules work * LED classdev 'brightness_get' function. This schedules work
* to update LED state. * to update LED state.
*/ */
static void powernv_brightness_set(struct led_classdev *led_cdev, static int powernv_brightness_set(struct led_classdev *led_cdev,
enum led_brightness value) enum led_brightness value)
{ {
struct powernv_led_data *powernv_led = struct powernv_led_data *powernv_led =
container_of(led_cdev, struct powernv_led_data, cdev); container_of(led_cdev, struct powernv_led_data, cdev);
struct powernv_led_common *powernv_led_common = powernv_led->common; struct powernv_led_common *powernv_led_common = powernv_led->common;
int rc;
/* Do not modify LED in unload path */ /* Do not modify LED in unload path */
if (powernv_led_common->led_disabled) if (powernv_led_common->led_disabled)
return; return 0;
mutex_lock(&powernv_led_common->lock); mutex_lock(&powernv_led_common->lock);
powernv_led_set(powernv_led, value); rc = powernv_led_set(powernv_led, value);
mutex_unlock(&powernv_led_common->lock); mutex_unlock(&powernv_led_common->lock);
return rc;
} }
/* LED classdev 'brightness_get' function */ /* LED classdev 'brightness_get' function */
@ -227,7 +231,7 @@ static int powernv_led_create(struct device *dev,
return -ENOMEM; return -ENOMEM;
} }
powernv_led->cdev.brightness_set = powernv_brightness_set; powernv_led->cdev.brightness_set_blocking = powernv_brightness_set;
powernv_led->cdev.brightness_get = powernv_brightness_get; powernv_led->cdev.brightness_get = powernv_brightness_get;
powernv_led->cdev.brightness = LED_OFF; powernv_led->cdev.brightness = LED_OFF;
powernv_led->cdev.max_brightness = LED_FULL; powernv_led->cdev.max_brightness = LED_FULL;
@ -256,8 +260,6 @@ static int powernv_led_classdev(struct platform_device *pdev,
for_each_child_of_node(led_node, np) { for_each_child_of_node(led_node, np) {
p = of_find_property(np, "led-types", NULL); p = of_find_property(np, "led-types", NULL);
if (!p)
continue;
while ((cur = of_prop_next_string(p, cur)) != NULL) { while ((cur = of_prop_next_string(p, cur)) != NULL) {
powernv_led = devm_kzalloc(dev, sizeof(*powernv_led), powernv_led = devm_kzalloc(dev, sizeof(*powernv_led),

View File

@ -22,12 +22,10 @@
#include <linux/pwm.h> #include <linux/pwm.h>
#include <linux/leds_pwm.h> #include <linux/leds_pwm.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/workqueue.h>
struct led_pwm_data { struct led_pwm_data {
struct led_classdev cdev; struct led_classdev cdev;
struct pwm_device *pwm; struct pwm_device *pwm;
struct work_struct work;
unsigned int active_low; unsigned int active_low;
unsigned int period; unsigned int period;
int duty; int duty;
@ -51,14 +49,6 @@ static void __led_pwm_set(struct led_pwm_data *led_dat)
pwm_enable(led_dat->pwm); pwm_enable(led_dat->pwm);
} }
static void led_pwm_work(struct work_struct *work)
{
struct led_pwm_data *led_dat =
container_of(work, struct led_pwm_data, work);
__led_pwm_set(led_dat);
}
static void led_pwm_set(struct led_classdev *led_cdev, static void led_pwm_set(struct led_classdev *led_cdev,
enum led_brightness brightness) enum led_brightness brightness)
{ {
@ -75,12 +65,16 @@ static void led_pwm_set(struct led_classdev *led_cdev,
led_dat->duty = duty; led_dat->duty = duty;
if (led_dat->can_sleep)
schedule_work(&led_dat->work);
else
__led_pwm_set(led_dat); __led_pwm_set(led_dat);
} }
static int led_pwm_set_blocking(struct led_classdev *led_cdev,
enum led_brightness brightness)
{
led_pwm_set(led_cdev, brightness);
return 0;
}
static inline size_t sizeof_pwm_leds_priv(int num_leds) static inline size_t sizeof_pwm_leds_priv(int num_leds)
{ {
return sizeof(struct led_pwm_priv) + return sizeof(struct led_pwm_priv) +
@ -89,11 +83,8 @@ static inline size_t sizeof_pwm_leds_priv(int num_leds)
static void led_pwm_cleanup(struct led_pwm_priv *priv) static void led_pwm_cleanup(struct led_pwm_priv *priv)
{ {
while (priv->num_leds--) { while (priv->num_leds--)
led_classdev_unregister(&priv->leds[priv->num_leds].cdev); led_classdev_unregister(&priv->leds[priv->num_leds].cdev);
if (priv->leds[priv->num_leds].can_sleep)
cancel_work_sync(&priv->leds[priv->num_leds].work);
}
} }
static int led_pwm_add(struct device *dev, struct led_pwm_priv *priv, static int led_pwm_add(struct device *dev, struct led_pwm_priv *priv,
@ -105,7 +96,6 @@ static int led_pwm_add(struct device *dev, struct led_pwm_priv *priv,
led_data->active_low = led->active_low; led_data->active_low = led->active_low;
led_data->cdev.name = led->name; led_data->cdev.name = led->name;
led_data->cdev.default_trigger = led->default_trigger; led_data->cdev.default_trigger = led->default_trigger;
led_data->cdev.brightness_set = led_pwm_set;
led_data->cdev.brightness = LED_OFF; led_data->cdev.brightness = LED_OFF;
led_data->cdev.max_brightness = led->max_brightness; led_data->cdev.max_brightness = led->max_brightness;
led_data->cdev.flags = LED_CORE_SUSPENDRESUME; led_data->cdev.flags = LED_CORE_SUSPENDRESUME;
@ -122,8 +112,10 @@ static int led_pwm_add(struct device *dev, struct led_pwm_priv *priv,
} }
led_data->can_sleep = pwm_can_sleep(led_data->pwm); led_data->can_sleep = pwm_can_sleep(led_data->pwm);
if (led_data->can_sleep) if (!led_data->can_sleep)
INIT_WORK(&led_data->work, led_pwm_work); led_data->cdev.brightness_set = led_pwm_set;
else
led_data->cdev.brightness_set_blocking = led_pwm_set_blocking;
led_data->period = pwm_get_period(led_data->pwm); led_data->period = pwm_get_period(led_data->pwm);
if (!led_data->period && (led->pwm_period_ns > 0)) if (!led_data->period && (led->pwm_period_ns > 0))
@ -132,6 +124,7 @@ static int led_pwm_add(struct device *dev, struct led_pwm_priv *priv,
ret = led_classdev_register(dev, &led_data->cdev); ret = led_classdev_register(dev, &led_data->cdev);
if (ret == 0) { if (ret == 0) {
priv->num_leds++; priv->num_leds++;
led_pwm_set(&led_data->cdev, led_data->cdev.brightness);
} else { } else {
dev_err(dev, "failed to register PWM led for %s: %d\n", dev_err(dev, "failed to register PWM led for %s: %d\n",
led->name, ret); led->name, ret);
@ -236,6 +229,6 @@ static struct platform_driver led_pwm_driver = {
module_platform_driver(led_pwm_driver); module_platform_driver(led_pwm_driver);
MODULE_AUTHOR("Luotao Fu <l.fu@pengutronix.de>"); MODULE_AUTHOR("Luotao Fu <l.fu@pengutronix.de>");
MODULE_DESCRIPTION("PWM LED driver for PXA"); MODULE_DESCRIPTION("generic PWM LED driver");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:leds-pwm"); MODULE_ALIAS("platform:leds-pwm");

View File

@ -14,7 +14,6 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/workqueue.h>
#include <linux/leds.h> #include <linux/leds.h>
#include <linux/leds-regulator.h> #include <linux/leds-regulator.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
@ -25,10 +24,8 @@
struct regulator_led { struct regulator_led {
struct led_classdev cdev; struct led_classdev cdev;
enum led_brightness value;
int enabled; int enabled;
struct mutex mutex; struct mutex mutex;
struct work_struct work;
struct regulator *vcc; struct regulator *vcc;
}; };
@ -94,22 +91,24 @@ static void regulator_led_disable(struct regulator_led *led)
led->enabled = 0; led->enabled = 0;
} }
static void regulator_led_set_value(struct regulator_led *led) static int regulator_led_brightness_set(struct led_classdev *led_cdev,
enum led_brightness value)
{ {
struct regulator_led *led = to_regulator_led(led_cdev);
int voltage; int voltage;
int ret; int ret = 0;
mutex_lock(&led->mutex); mutex_lock(&led->mutex);
if (led->value == LED_OFF) { if (value == LED_OFF) {
regulator_led_disable(led); regulator_led_disable(led);
goto out; goto out;
} }
if (led->cdev.max_brightness > 1) { if (led->cdev.max_brightness > 1) {
voltage = led_regulator_get_voltage(led->vcc, led->value); voltage = led_regulator_get_voltage(led->vcc, value);
dev_dbg(led->cdev.dev, "brightness: %d voltage: %d\n", dev_dbg(led->cdev.dev, "brightness: %d voltage: %d\n",
led->value, voltage); value, voltage);
ret = regulator_set_voltage(led->vcc, voltage, voltage); ret = regulator_set_voltage(led->vcc, voltage, voltage);
if (ret != 0) if (ret != 0)
@ -121,23 +120,7 @@ static void regulator_led_set_value(struct regulator_led *led)
out: out:
mutex_unlock(&led->mutex); mutex_unlock(&led->mutex);
} return ret;
static void led_work(struct work_struct *work)
{
struct regulator_led *led;
led = container_of(work, struct regulator_led, work);
regulator_led_set_value(led);
}
static void regulator_led_brightness_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
struct regulator_led *led = to_regulator_led(led_cdev);
led->value = value;
schedule_work(&led->work);
} }
static int regulator_led_probe(struct platform_device *pdev) static int regulator_led_probe(struct platform_device *pdev)
@ -169,9 +152,8 @@ static int regulator_led_probe(struct platform_device *pdev)
pdata->brightness); pdata->brightness);
return -EINVAL; return -EINVAL;
} }
led->value = pdata->brightness;
led->cdev.brightness_set = regulator_led_brightness_set; led->cdev.brightness_set_blocking = regulator_led_brightness_set;
led->cdev.name = pdata->name; led->cdev.name = pdata->name;
led->cdev.flags |= LED_CORE_SUSPENDRESUME; led->cdev.flags |= LED_CORE_SUSPENDRESUME;
led->vcc = vcc; led->vcc = vcc;
@ -181,21 +163,18 @@ static int regulator_led_probe(struct platform_device *pdev)
led->enabled = 1; led->enabled = 1;
mutex_init(&led->mutex); mutex_init(&led->mutex);
INIT_WORK(&led->work, led_work);
platform_set_drvdata(pdev, led); platform_set_drvdata(pdev, led);
ret = led_classdev_register(&pdev->dev, &led->cdev); ret = led_classdev_register(&pdev->dev, &led->cdev);
if (ret < 0) { if (ret < 0)
cancel_work_sync(&led->work);
return ret; return ret;
}
/* to expose the default value to userspace */ /* to expose the default value to userspace */
led->cdev.brightness = led->value; led->cdev.brightness = pdata->brightness;
/* Set the default led status */ /* Set the default led status */
regulator_led_set_value(led); regulator_led_brightness_set(&led->cdev, led->cdev.brightness);
return 0; return 0;
} }
@ -205,7 +184,6 @@ static int regulator_led_remove(struct platform_device *pdev)
struct regulator_led *led = platform_get_drvdata(pdev); struct regulator_led *led = platform_get_drvdata(pdev);
led_classdev_unregister(&led->cdev); led_classdev_unregister(&led->cdev);
cancel_work_sync(&led->work);
regulator_led_disable(led); regulator_led_disable(led);
return 0; return 0;
} }

View File

@ -234,28 +234,19 @@ static struct platform_driver sunfire_fhc_led_driver = {
}, },
}; };
static struct platform_driver * const drivers[] = {
&sunfire_clockboard_led_driver,
&sunfire_fhc_led_driver,
};
static int __init sunfire_leds_init(void) static int __init sunfire_leds_init(void)
{ {
int err = platform_driver_register(&sunfire_clockboard_led_driver); return platform_register_drivers(drivers, ARRAY_SIZE(drivers));
if (err) {
pr_err("Could not register clock board LED driver\n");
return err;
}
err = platform_driver_register(&sunfire_fhc_led_driver);
if (err) {
pr_err("Could not register FHC LED driver\n");
platform_driver_unregister(&sunfire_clockboard_led_driver);
}
return err;
} }
static void __exit sunfire_leds_exit(void) static void __exit sunfire_leds_exit(void)
{ {
platform_driver_unregister(&sunfire_clockboard_led_driver); platform_unregister_drivers(drivers, ARRAY_SIZE(drivers));
platform_driver_unregister(&sunfire_fhc_led_driver);
} }
module_init(sunfire_leds_init); module_init(sunfire_leds_init);

View File

@ -20,7 +20,7 @@
* MA 02111-1307 USA * MA 02111-1307 USA
*/ */
#include <linux/io.h> #include <linux/io.h>
#include <linux/module.h> #include <linux/init.h>
#include <linux/of_device.h> #include <linux/of_device.h>
#include <linux/of_address.h> #include <linux/of_address.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
@ -139,29 +139,17 @@ static int syscon_led_probe(struct platform_device *pdev)
return 0; return 0;
} }
static int syscon_led_remove(struct platform_device *pdev)
{
struct syscon_led *sled = platform_get_drvdata(pdev);
led_classdev_unregister(&sled->cdev);
/* Turn it off */
regmap_update_bits(sled->map, sled->offset, sled->mask, 0);
return 0;
}
static const struct of_device_id of_syscon_leds_match[] = { static const struct of_device_id of_syscon_leds_match[] = {
{ .compatible = "register-bit-led", }, { .compatible = "register-bit-led", },
{}, {},
}; };
MODULE_DEVICE_TABLE(of, of_syscon_leds_match);
static struct platform_driver syscon_led_driver = { static struct platform_driver syscon_led_driver = {
.probe = syscon_led_probe, .probe = syscon_led_probe,
.remove = syscon_led_remove,
.driver = { .driver = {
.name = "leds-syscon", .name = "leds-syscon",
.of_match_table = of_syscon_leds_match, .of_match_table = of_syscon_leds_match,
.suppress_bind_attrs = true,
}, },
}; };
module_platform_driver(syscon_led_driver); builtin_platform_driver(syscon_led_driver);

View File

@ -14,7 +14,6 @@
#include <linux/of_device.h> #include <linux/of_device.h>
#include <linux/regmap.h> #include <linux/regmap.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/workqueue.h>
#define TLC591XX_MAX_LEDS 16 #define TLC591XX_MAX_LEDS 16
@ -42,13 +41,11 @@
#define LEDOUT_MASK 0x3 #define LEDOUT_MASK 0x3
#define ldev_to_led(c) container_of(c, struct tlc591xx_led, ldev) #define ldev_to_led(c) container_of(c, struct tlc591xx_led, ldev)
#define work_to_led(work) container_of(work, struct tlc591xx_led, work)
struct tlc591xx_led { struct tlc591xx_led {
bool active; bool active;
unsigned int led_no; unsigned int led_no;
struct led_classdev ldev; struct led_classdev ldev;
struct work_struct work;
struct tlc591xx_priv *priv; struct tlc591xx_priv *priv;
}; };
@ -110,12 +107,12 @@ tlc591xx_set_pwm(struct tlc591xx_priv *priv, struct tlc591xx_led *led,
return regmap_write(priv->regmap, pwm, brightness); return regmap_write(priv->regmap, pwm, brightness);
} }
static void static int
tlc591xx_led_work(struct work_struct *work) tlc591xx_brightness_set(struct led_classdev *led_cdev,
enum led_brightness brightness)
{ {
struct tlc591xx_led *led = work_to_led(work); struct tlc591xx_led *led = ldev_to_led(led_cdev);
struct tlc591xx_priv *priv = led->priv; struct tlc591xx_priv *priv = led->priv;
enum led_brightness brightness = led->ldev.brightness;
int err; int err;
switch (brightness) { switch (brightness) {
@ -131,18 +128,7 @@ tlc591xx_led_work(struct work_struct *work)
err = tlc591xx_set_pwm(priv, led, brightness); err = tlc591xx_set_pwm(priv, led, brightness);
} }
if (err) return err;
dev_err(led->ldev.dev, "Failed setting brightness\n");
}
static void
tlc591xx_brightness_set(struct led_classdev *led_cdev,
enum led_brightness brightness)
{
struct tlc591xx_led *led = ldev_to_led(led_cdev);
led->ldev.brightness = brightness;
schedule_work(&led->work);
} }
static void static void
@ -151,10 +137,8 @@ tlc591xx_destroy_devices(struct tlc591xx_priv *priv, unsigned int j)
int i = j; int i = j;
while (--i >= 0) { while (--i >= 0) {
if (priv->leds[i].active) { if (priv->leds[i].active)
led_classdev_unregister(&priv->leds[i].ldev); led_classdev_unregister(&priv->leds[i].ldev);
cancel_work_sync(&priv->leds[i].work);
}
} }
} }
@ -175,9 +159,8 @@ tlc591xx_configure(struct device *dev,
led->priv = priv; led->priv = priv;
led->led_no = i; led->led_no = i;
led->ldev.brightness_set = tlc591xx_brightness_set; led->ldev.brightness_set_blocking = tlc591xx_brightness_set;
led->ldev.max_brightness = LED_FULL; led->ldev.max_brightness = LED_FULL;
INIT_WORK(&led->work, tlc591xx_led_work);
err = led_classdev_register(dev, &led->ldev); err = led_classdev_register(dev, &led->ldev);
if (err < 0) { if (err < 0) {
dev_err(dev, "couldn't register LED %s\n", dev_err(dev, "couldn't register LED %s\n",

View File

@ -23,7 +23,6 @@
struct wm831x_status { struct wm831x_status {
struct led_classdev cdev; struct led_classdev cdev;
struct wm831x *wm831x; struct wm831x *wm831x;
struct work_struct work;
struct mutex mutex; struct mutex mutex;
spinlock_t value_lock; spinlock_t value_lock;
@ -40,10 +39,8 @@ struct wm831x_status {
#define to_wm831x_status(led_cdev) \ #define to_wm831x_status(led_cdev) \
container_of(led_cdev, struct wm831x_status, cdev) container_of(led_cdev, struct wm831x_status, cdev)
static void wm831x_status_work(struct work_struct *work) static void wm831x_status_set(struct wm831x_status *led)
{ {
struct wm831x_status *led = container_of(work, struct wm831x_status,
work);
unsigned long flags; unsigned long flags;
mutex_lock(&led->mutex); mutex_lock(&led->mutex);
@ -70,7 +67,7 @@ static void wm831x_status_work(struct work_struct *work)
mutex_unlock(&led->mutex); mutex_unlock(&led->mutex);
} }
static void wm831x_status_set(struct led_classdev *led_cdev, static int wm831x_status_brightness_set(struct led_classdev *led_cdev,
enum led_brightness value) enum led_brightness value)
{ {
struct wm831x_status *led = to_wm831x_status(led_cdev); struct wm831x_status *led = to_wm831x_status(led_cdev);
@ -80,8 +77,10 @@ static void wm831x_status_set(struct led_classdev *led_cdev,
led->brightness = value; led->brightness = value;
if (value == LED_OFF) if (value == LED_OFF)
led->blink = 0; led->blink = 0;
schedule_work(&led->work);
spin_unlock_irqrestore(&led->value_lock, flags); spin_unlock_irqrestore(&led->value_lock, flags);
wm831x_status_set(led);
return 0;
} }
static int wm831x_status_blink_set(struct led_classdev *led_cdev, static int wm831x_status_blink_set(struct led_classdev *led_cdev,
@ -147,11 +146,8 @@ static int wm831x_status_blink_set(struct led_classdev *led_cdev,
else else
led->blink = 0; led->blink = 0;
/* Always update; if we fail turn off blinking since we expect
* a software fallback. */
schedule_work(&led->work);
spin_unlock_irqrestore(&led->value_lock, flags); spin_unlock_irqrestore(&led->value_lock, flags);
wm831x_status_set(led);
return ret; return ret;
} }
@ -206,11 +202,9 @@ static ssize_t wm831x_status_src_store(struct device *dev,
for (i = 0; i < ARRAY_SIZE(led_src_texts); i++) { for (i = 0; i < ARRAY_SIZE(led_src_texts); i++) {
if (!strcmp(name, led_src_texts[i])) { if (!strcmp(name, led_src_texts[i])) {
mutex_lock(&led->mutex); mutex_lock(&led->mutex);
led->src = i; led->src = i;
schedule_work(&led->work);
mutex_unlock(&led->mutex); mutex_unlock(&led->mutex);
wm831x_status_set(led);
} }
} }
@ -262,7 +256,6 @@ static int wm831x_status_probe(struct platform_device *pdev)
pdata.name = dev_name(&pdev->dev); pdata.name = dev_name(&pdev->dev);
mutex_init(&drvdata->mutex); mutex_init(&drvdata->mutex);
INIT_WORK(&drvdata->work, wm831x_status_work);
spin_lock_init(&drvdata->value_lock); spin_lock_init(&drvdata->value_lock);
/* We cache the configuration register and read startup values /* We cache the configuration register and read startup values
@ -287,7 +280,7 @@ static int wm831x_status_probe(struct platform_device *pdev)
drvdata->cdev.name = pdata.name; drvdata->cdev.name = pdata.name;
drvdata->cdev.default_trigger = pdata.default_trigger; drvdata->cdev.default_trigger = pdata.default_trigger;
drvdata->cdev.brightness_set = wm831x_status_set; drvdata->cdev.brightness_set_blocking = wm831x_status_brightness_set;
drvdata->cdev.blink_set = wm831x_status_blink_set; drvdata->cdev.blink_set = wm831x_status_blink_set;
drvdata->cdev.groups = wm831x_status_groups; drvdata->cdev.groups = wm831x_status_groups;

View File

@ -89,40 +89,42 @@ static const int isink_cur[] = {
#define to_wm8350_led(led_cdev) \ #define to_wm8350_led(led_cdev) \
container_of(led_cdev, struct wm8350_led, cdev) container_of(led_cdev, struct wm8350_led, cdev)
static void wm8350_led_enable(struct wm8350_led *led) static int wm8350_led_enable(struct wm8350_led *led)
{ {
int ret; int ret = 0;
if (led->enabled) if (led->enabled)
return; return ret;
ret = regulator_enable(led->isink); ret = regulator_enable(led->isink);
if (ret != 0) { if (ret != 0) {
dev_err(led->cdev.dev, "Failed to enable ISINK: %d\n", ret); dev_err(led->cdev.dev, "Failed to enable ISINK: %d\n", ret);
return; return ret;
} }
ret = regulator_enable(led->dcdc); ret = regulator_enable(led->dcdc);
if (ret != 0) { if (ret != 0) {
dev_err(led->cdev.dev, "Failed to enable DCDC: %d\n", ret); dev_err(led->cdev.dev, "Failed to enable DCDC: %d\n", ret);
regulator_disable(led->isink); regulator_disable(led->isink);
return; return ret;
} }
led->enabled = 1; led->enabled = 1;
return ret;
} }
static void wm8350_led_disable(struct wm8350_led *led) static int wm8350_led_disable(struct wm8350_led *led)
{ {
int ret; int ret = 0;
if (!led->enabled) if (!led->enabled)
return; return ret;
ret = regulator_disable(led->dcdc); ret = regulator_disable(led->dcdc);
if (ret != 0) { if (ret != 0) {
dev_err(led->cdev.dev, "Failed to disable DCDC: %d\n", ret); dev_err(led->cdev.dev, "Failed to disable DCDC: %d\n", ret);
return; return ret;
} }
ret = regulator_disable(led->isink); ret = regulator_disable(led->isink);
@ -132,27 +134,29 @@ static void wm8350_led_disable(struct wm8350_led *led)
if (ret != 0) if (ret != 0)
dev_err(led->cdev.dev, "Failed to reenable DCDC: %d\n", dev_err(led->cdev.dev, "Failed to reenable DCDC: %d\n",
ret); ret);
return; return ret;
} }
led->enabled = 0; led->enabled = 0;
return ret;
} }
static void led_work(struct work_struct *work) static int wm8350_led_set(struct led_classdev *led_cdev,
enum led_brightness value)
{ {
struct wm8350_led *led = container_of(work, struct wm8350_led, work); struct wm8350_led *led = to_wm8350_led(led_cdev);
unsigned long flags;
int ret; int ret;
int uA; int uA;
unsigned long flags;
mutex_lock(&led->mutex); led->value = value;
spin_lock_irqsave(&led->value_lock, flags); spin_lock_irqsave(&led->value_lock, flags);
if (led->value == LED_OFF) { if (led->value == LED_OFF) {
spin_unlock_irqrestore(&led->value_lock, flags); spin_unlock_irqrestore(&led->value_lock, flags);
wm8350_led_disable(led); return wm8350_led_disable(led);
goto out;
} }
/* This scales linearly into the index of valid current /* This scales linearly into the index of valid current
@ -166,36 +170,21 @@ static void led_work(struct work_struct *work)
ret = regulator_set_current_limit(led->isink, isink_cur[uA], ret = regulator_set_current_limit(led->isink, isink_cur[uA],
isink_cur[uA]); isink_cur[uA]);
if (ret != 0) if (ret != 0) {
dev_err(led->cdev.dev, "Failed to set %duA: %d\n", dev_err(led->cdev.dev, "Failed to set %duA: %d\n",
isink_cur[uA], ret); isink_cur[uA], ret);
return ret;
}
wm8350_led_enable(led); return wm8350_led_enable(led);
out:
mutex_unlock(&led->mutex);
}
static void wm8350_led_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
struct wm8350_led *led = to_wm8350_led(led_cdev);
unsigned long flags;
spin_lock_irqsave(&led->value_lock, flags);
led->value = value;
schedule_work(&led->work);
spin_unlock_irqrestore(&led->value_lock, flags);
} }
static void wm8350_led_shutdown(struct platform_device *pdev) static void wm8350_led_shutdown(struct platform_device *pdev)
{ {
struct wm8350_led *led = platform_get_drvdata(pdev); struct wm8350_led *led = platform_get_drvdata(pdev);
mutex_lock(&led->mutex);
led->value = LED_OFF; led->value = LED_OFF;
wm8350_led_disable(led); wm8350_led_disable(led);
mutex_unlock(&led->mutex);
} }
static int wm8350_led_probe(struct platform_device *pdev) static int wm8350_led_probe(struct platform_device *pdev)
@ -232,7 +221,7 @@ static int wm8350_led_probe(struct platform_device *pdev)
if (led == NULL) if (led == NULL)
return -ENOMEM; return -ENOMEM;
led->cdev.brightness_set = wm8350_led_set; led->cdev.brightness_set_blocking = wm8350_led_set;
led->cdev.default_trigger = pdata->default_trigger; led->cdev.default_trigger = pdata->default_trigger;
led->cdev.name = pdata->name; led->cdev.name = pdata->name;
led->cdev.flags |= LED_CORE_SUSPENDRESUME; led->cdev.flags |= LED_CORE_SUSPENDRESUME;
@ -251,8 +240,6 @@ static int wm8350_led_probe(struct platform_device *pdev)
pdata->max_uA); pdata->max_uA);
spin_lock_init(&led->value_lock); spin_lock_init(&led->value_lock);
mutex_init(&led->mutex);
INIT_WORK(&led->work, led_work);
led->value = LED_OFF; led->value = LED_OFF;
platform_set_drvdata(pdev, led); platform_set_drvdata(pdev, led);
@ -264,7 +251,6 @@ static int wm8350_led_remove(struct platform_device *pdev)
struct wm8350_led *led = platform_get_drvdata(pdev); struct wm8350_led *led = platform_get_drvdata(pdev);
led_classdev_unregister(&led->cdev); led_classdev_unregister(&led->cdev);
flush_work(&led->work);
wm8350_led_disable(led); wm8350_led_disable(led);
return 0; return 0;
} }

View File

@ -16,29 +16,6 @@
#include <linux/rwsem.h> #include <linux/rwsem.h>
#include <linux/leds.h> #include <linux/leds.h>
static inline void led_set_brightness_async(struct led_classdev *led_cdev,
enum led_brightness value)
{
value = min(value, led_cdev->max_brightness);
led_cdev->brightness = value;
if (!(led_cdev->flags & LED_SUSPENDED))
led_cdev->brightness_set(led_cdev, value);
}
static inline int led_set_brightness_sync(struct led_classdev *led_cdev,
enum led_brightness value)
{
int ret = 0;
led_cdev->brightness = min(value, led_cdev->max_brightness);
if (!(led_cdev->flags & LED_SUSPENDED))
ret = led_cdev->brightness_set_sync(led_cdev,
led_cdev->brightness);
return ret;
}
static inline int led_get_brightness(struct led_classdev *led_cdev) static inline int led_get_brightness(struct led_classdev *led_cdev)
{ {
return led_cdev->brightness; return led_cdev->brightness;
@ -46,6 +23,10 @@ static inline int led_get_brightness(struct led_classdev *led_cdev)
void led_init_core(struct led_classdev *led_cdev); void led_init_core(struct led_classdev *led_cdev);
void led_stop_software_blink(struct led_classdev *led_cdev); void led_stop_software_blink(struct led_classdev *led_cdev);
void led_set_brightness_nopm(struct led_classdev *led_cdev,
enum led_brightness value);
void led_set_brightness_nosleep(struct led_classdev *led_cdev,
enum led_brightness value);
extern struct rw_semaphore leds_list_lock; extern struct rw_semaphore leds_list_lock;
extern struct list_head leds_list; extern struct list_head leds_list;

View File

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

View File

@ -19,7 +19,6 @@
* *
*/ */
#include <linux/module.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/slab.h> #include <linux/slab.h>
@ -140,27 +139,4 @@ static int __init ledtrig_cpu_init(void)
return 0; return 0;
} }
module_init(ledtrig_cpu_init); device_initcall(ledtrig_cpu_init);
static void __exit ledtrig_cpu_exit(void)
{
int cpu;
unregister_cpu_notifier(&ledtrig_cpu_nb);
for_each_possible_cpu(cpu) {
struct led_trigger_cpu *trig = &per_cpu(cpu_trig, cpu);
led_trigger_unregister_simple(trig->_trig);
trig->_trig = NULL;
memset(trig->name, 0, MAX_NAME_LEN);
}
unregister_syscore_ops(&ledtrig_cpu_syscore_ops);
}
module_exit(ledtrig_cpu_exit);
MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>");
MODULE_AUTHOR("Bryan Wu <bryan.wu@canonical.com>");
MODULE_DESCRIPTION("CPU LED trigger");
MODULE_LICENSE("GPL");

View File

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

View File

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

View File

@ -38,7 +38,7 @@ static void led_heartbeat_function(unsigned long data)
unsigned long delay = 0; unsigned long delay = 0;
if (unlikely(panic_heartbeats)) { if (unlikely(panic_heartbeats)) {
led_set_brightness(led_cdev, LED_OFF); led_set_brightness_nosleep(led_cdev, LED_OFF);
return; return;
} }
@ -81,7 +81,7 @@ static void led_heartbeat_function(unsigned long data)
break; break;
} }
led_set_brightness_async(led_cdev, brightness); led_set_brightness_nosleep(led_cdev, brightness);
mod_timer(&heartbeat_data->timer, jiffies + delay); mod_timer(&heartbeat_data->timer, jiffies + delay);
} }

View File

@ -11,7 +11,6 @@
* *
*/ */
#include <linux/module.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/leds.h> #include <linux/leds.h>
@ -33,15 +32,4 @@ static int __init ledtrig_ide_init(void)
led_trigger_register_simple("ide-disk", &ledtrig_ide); led_trigger_register_simple("ide-disk", &ledtrig_ide);
return 0; return 0;
} }
device_initcall(ledtrig_ide_init);
static void __exit ledtrig_ide_exit(void)
{
led_trigger_unregister_simple(ledtrig_ide);
}
module_init(ledtrig_ide_init);
module_exit(ledtrig_ide_exit);
MODULE_AUTHOR("Richard Purdie <rpurdie@openedhand.com>");
MODULE_DESCRIPTION("LED IDE Disk Activity Trigger");
MODULE_LICENSE("GPL");

View File

@ -63,9 +63,9 @@ static ssize_t led_invert_store(struct device *dev,
oneshot_data->invert = !!state; oneshot_data->invert = !!state;
if (oneshot_data->invert) if (oneshot_data->invert)
led_set_brightness_async(led_cdev, LED_FULL); led_set_brightness_nosleep(led_cdev, LED_FULL);
else else
led_set_brightness_async(led_cdev, LED_OFF); led_set_brightness_nosleep(led_cdev, LED_OFF);
return size; return size;
} }
@ -201,4 +201,4 @@ module_exit(oneshot_trig_exit);
MODULE_AUTHOR("Fabio Baltieri <fabio.baltieri@gmail.com>"); MODULE_AUTHOR("Fabio Baltieri <fabio.baltieri@gmail.com>");
MODULE_DESCRIPTION("One-shot LED trigger"); MODULE_DESCRIPTION("One-shot LED trigger");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL v2");

View File

@ -41,7 +41,7 @@ static void transient_timer_function(unsigned long data)
struct transient_trig_data *transient_data = led_cdev->trigger_data; struct transient_trig_data *transient_data = led_cdev->trigger_data;
transient_data->activate = 0; transient_data->activate = 0;
led_set_brightness_async(led_cdev, transient_data->restore_state); led_set_brightness_nosleep(led_cdev, transient_data->restore_state);
} }
static ssize_t transient_activate_show(struct device *dev, 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) { if (state == 0 && transient_data->activate == 1) {
del_timer(&transient_data->timer); del_timer(&transient_data->timer);
transient_data->activate = state; transient_data->activate = state;
led_set_brightness_async(led_cdev, led_set_brightness_nosleep(led_cdev,
transient_data->restore_state); transient_data->restore_state);
return size; return size;
} }
@ -81,11 +81,11 @@ static ssize_t transient_activate_store(struct device *dev,
if (state == 1 && transient_data->activate == 0 && if (state == 1 && transient_data->activate == 0 &&
transient_data->duration != 0) { transient_data->duration != 0) {
transient_data->activate = state; transient_data->activate = state;
led_set_brightness_async(led_cdev, transient_data->state); led_set_brightness_nosleep(led_cdev, transient_data->state);
transient_data->restore_state = transient_data->restore_state =
(transient_data->state == LED_FULL) ? LED_OFF : LED_FULL; (transient_data->state == LED_FULL) ? LED_OFF : LED_FULL;
mod_timer(&transient_data->timer, mod_timer(&transient_data->timer,
jiffies + transient_data->duration); jiffies + msecs_to_jiffies(transient_data->duration));
} }
/* state == 0 && transient_data->activate == 0 /* state == 0 && transient_data->activate == 0
@ -204,7 +204,7 @@ static void transient_trig_deactivate(struct led_classdev *led_cdev)
if (led_cdev->activated) { if (led_cdev->activated) {
del_timer_sync(&transient_data->timer); del_timer_sync(&transient_data->timer);
led_set_brightness_async(led_cdev, led_set_brightness_nosleep(led_cdev,
transient_data->restore_state); transient_data->restore_state);
device_remove_file(led_cdev->dev, &dev_attr_activate); 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_duration);

View File

@ -107,10 +107,10 @@ static void v4l2_flash_set_led_brightness(struct v4l2_flash *v4l2_flash,
if (ctrls[LED_MODE]->val != V4L2_FLASH_LED_MODE_TORCH) if (ctrls[LED_MODE]->val != V4L2_FLASH_LED_MODE_TORCH)
return; return;
led_set_brightness(&v4l2_flash->fled_cdev->led_cdev, led_set_brightness_sync(&v4l2_flash->fled_cdev->led_cdev,
brightness); brightness);
} else { } else {
led_set_brightness(&v4l2_flash->iled_cdev->led_cdev, led_set_brightness_sync(&v4l2_flash->iled_cdev->led_cdev,
brightness); brightness);
} }
} }
@ -206,11 +206,11 @@ static int v4l2_flash_s_ctrl(struct v4l2_ctrl *c)
case V4L2_CID_FLASH_LED_MODE: case V4L2_CID_FLASH_LED_MODE:
switch (c->val) { switch (c->val) {
case V4L2_FLASH_LED_MODE_NONE: case V4L2_FLASH_LED_MODE_NONE:
led_set_brightness(led_cdev, LED_OFF); led_set_brightness_sync(led_cdev, LED_OFF);
return led_set_flash_strobe(fled_cdev, false); return led_set_flash_strobe(fled_cdev, false);
case V4L2_FLASH_LED_MODE_FLASH: case V4L2_FLASH_LED_MODE_FLASH:
/* Turn the torch LED off */ /* Turn the torch LED off */
led_set_brightness(led_cdev, LED_OFF); led_set_brightness_sync(led_cdev, LED_OFF);
if (ctrls[STROBE_SOURCE]) { if (ctrls[STROBE_SOURCE]) {
external_strobe = (ctrls[STROBE_SOURCE]->val == external_strobe = (ctrls[STROBE_SOURCE]->val ==
V4L2_FLASH_STROBE_SOURCE_EXTERNAL); V4L2_FLASH_STROBE_SOURCE_EXTERNAL);

View File

@ -44,9 +44,9 @@ struct led_classdev {
#define LED_BLINK_ONESHOT (1 << 17) #define LED_BLINK_ONESHOT (1 << 17)
#define LED_BLINK_ONESHOT_STOP (1 << 18) #define LED_BLINK_ONESHOT_STOP (1 << 18)
#define LED_BLINK_INVERT (1 << 19) #define LED_BLINK_INVERT (1 << 19)
#define LED_SYSFS_DISABLE (1 << 20) #define LED_BLINK_BRIGHTNESS_CHANGE (1 << 20)
#define SET_BRIGHTNESS_ASYNC (1 << 21) #define LED_BLINK_DISABLE (1 << 21)
#define SET_BRIGHTNESS_SYNC (1 << 22) #define LED_SYSFS_DISABLE (1 << 22)
#define LED_DEV_CAP_FLASH (1 << 23) #define LED_DEV_CAP_FLASH (1 << 23)
/* Set LED brightness level */ /* Set LED brightness level */
@ -57,7 +57,7 @@ struct led_classdev {
* Set LED brightness level immediately - it can block the caller for * Set LED brightness level immediately - it can block the caller for
* the time required for accessing a LED device register. * the time required for accessing a LED device register.
*/ */
int (*brightness_set_sync)(struct led_classdev *led_cdev, int (*brightness_set_blocking)(struct led_classdev *led_cdev,
enum led_brightness brightness); enum led_brightness brightness);
/* Get LED brightness level */ /* Get LED brightness level */
enum led_brightness (*brightness_get)(struct led_classdev *led_cdev); enum led_brightness (*brightness_get)(struct led_classdev *led_cdev);
@ -156,10 +156,25 @@ extern void led_blink_set_oneshot(struct led_classdev *led_cdev,
* *
* Set an LED's brightness, and, if necessary, cancel the * Set an LED's brightness, and, if necessary, cancel the
* software blink timer that implements blinking when the * software blink timer that implements blinking when the
* hardware doesn't. * hardware doesn't. This function is guaranteed not to sleep.
*/ */
extern void led_set_brightness(struct led_classdev *led_cdev, extern void led_set_brightness(struct led_classdev *led_cdev,
enum led_brightness brightness); enum led_brightness brightness);
/**
* led_set_brightness_sync - set LED brightness synchronously
* @led_cdev: the LED to set
* @brightness: the brightness to set it to
*
* Set an LED's brightness immediately. This function will block
* the caller for the time required for accessing device registers,
* and it can sleep.
*
* Returns: 0 on success or negative error value on failure
*/
extern int led_set_brightness_sync(struct led_classdev *led_cdev,
enum led_brightness value);
/** /**
* led_update_brightness - update LED brightness * led_update_brightness - update LED brightness
* @led_cdev: the LED to query * @led_cdev: the LED to query
@ -231,6 +246,8 @@ ssize_t led_trigger_show(struct device *dev, struct device_attribute *attr,
/* Registration functions for complex triggers */ /* Registration functions for complex triggers */
extern int led_trigger_register(struct led_trigger *trigger); extern int led_trigger_register(struct led_trigger *trigger);
extern void led_trigger_unregister(struct led_trigger *trigger); extern void led_trigger_unregister(struct led_trigger *trigger);
extern int devm_led_trigger_register(struct device *dev,
struct led_trigger *trigger);
extern void led_trigger_register_simple(const char *name, extern void led_trigger_register_simple(const char *name,
struct led_trigger **trigger); struct led_trigger **trigger);

View File

@ -715,7 +715,6 @@ struct wm8350_led_platform_data {
struct wm8350_led { struct wm8350_led {
struct platform_device *pdev; struct platform_device *pdev;
struct mutex mutex;
struct work_struct work; struct work_struct work;
spinlock_t value_lock; spinlock_t value_lock;
enum led_brightness value; enum led_brightness value;