Merge branch 'for-3.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/cooloney...
authorLinus Torvalds <torvalds@linux-foundation.org>
Fri, 27 Jul 2012 03:26:27 +0000 (20:26 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Fri, 27 Jul 2012 03:26:27 +0000 (20:26 -0700)
Pull LED subsystem update from Bryan Wu.

* 'for-3.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/cooloney/linux-leds: (50 commits)
  leds-lp8788: forgotten unlock at lp8788_led_work
  LEDS: propagate error codes in blinkm_detect()
  LEDS: memory leak in blinkm_led_common_set()
  leds: add new lp8788 led driver
  LEDS: add BlinkM RGB LED driver, documentation and update MAINTAINERS
  leds: max8997: Simplify max8997_led_set_mode implementation
  leds/leds-s3c24xx: use devm_gpio_request
  leds: convert Network Space v2 LED driver to devm_kzalloc() and cleanup error exit path
  leds: convert DAC124S085 LED driver to devm_kzalloc()
  leds: convert LM3530 LED driver to devm_kzalloc() and cleanup error exit path
  leds: convert TCA6507 LED driver to devm_kzalloc()
  leds: convert Freescale MC13783 LED driver to devm_kzalloc() and cleanup error exit path
  leds: convert ADP5520 LED driver to devm_kzalloc() and cleanup error exit path
  leds: convert PCA955x LED driver to devm_kzalloc() and cleanup error exit path
  leds: convert Sun Fire LED driver to devm_kzalloc() and cleanup error exit path
  leds: convert PCA9532 LED driver to devm_kzalloc()
  leds: convert LT3593 LED driver to devm_kzalloc()
  leds: convert Renesas TPU LED driver to devm_kzalloc() and cleanup error exit path
  leds: convert LP5523 LED driver to devm_kzalloc() and cleanup error exit path
  leds: convert PCA9633 LED driver to devm_kzalloc()
  ...

51 files changed:
Documentation/leds/00-INDEX
Documentation/leds/leds-blinkm.txt [new file with mode: 0644]
Documentation/leds/leds-lm3556.txt [new file with mode: 0644]
Documentation/leds/ledtrig-oneshot.txt [new file with mode: 0644]
MAINTAINERS
drivers/leds/Kconfig
drivers/leds/Makefile
drivers/leds/led-class.c
drivers/leds/led-core.c
drivers/leds/led-triggers.c
drivers/leds/leds-88pm860x.c
drivers/leds/leds-adp5520.c
drivers/leds/leds-asic3.c
drivers/leds/leds-atmel-pwm.c
drivers/leds/leds-bd2802.c
drivers/leds/leds-blinkm.c [new file with mode: 0644]
drivers/leds/leds-da903x.c
drivers/leds/leds-dac124s085.c
drivers/leds/leds-gpio.c
drivers/leds/leds-lm3530.c
drivers/leds/leds-lm3556.c [new file with mode: 0644]
drivers/leds/leds-lp3944.c
drivers/leds/leds-lp5521.c
drivers/leds/leds-lp5523.c
drivers/leds/leds-lp8788.c [new file with mode: 0644]
drivers/leds/leds-lt3593.c
drivers/leds/leds-max8997.c
drivers/leds/leds-mc13783.c
drivers/leds/leds-netxbig.c
drivers/leds/leds-ns2.c
drivers/leds/leds-pca9532.c
drivers/leds/leds-pca955x.c
drivers/leds/leds-pca9633.c
drivers/leds/leds-pwm.c
drivers/leds/leds-regulator.c
drivers/leds/leds-renesas-tpu.c
drivers/leds/leds-s3c24xx.c
drivers/leds/leds-sunfire.c
drivers/leds/leds-tca6507.c
drivers/leds/leds.h
drivers/leds/ledtrig-backlight.c
drivers/leds/ledtrig-default-on.c
drivers/leds/ledtrig-gpio.c
drivers/leds/ledtrig-heartbeat.c
drivers/leds/ledtrig-ide-disk.c
drivers/leds/ledtrig-oneshot.c [new file with mode: 0644]
drivers/leds/ledtrig-timer.c
drivers/leds/ledtrig-transient.c
include/linux/leds.h
include/linux/platform_data/leds-lm3556.h [new file with mode: 0644]
net/mac80211/led.c

index 29f481df32c7d26789be20d9960c4c84dec531ff..5fefe374892f9debe8024250a0ea922916878558 100644 (file)
@@ -6,3 +6,5 @@ leds-lp5521.txt
        - notes on how to use the leds-lp5521 driver.
 leds-lp5523.txt
        - notes on how to use the leds-lp5523 driver.
+leds-lm3556.txt
+       - notes on how to use the leds-lm3556 driver.
diff --git a/Documentation/leds/leds-blinkm.txt b/Documentation/leds/leds-blinkm.txt
new file mode 100644 (file)
index 0000000..9dd92f4
--- /dev/null
@@ -0,0 +1,80 @@
+The leds-blinkm driver supports the devices of the BlinkM family.
+
+They are RGB-LED modules driven by a (AT)tiny microcontroller and
+communicate through I2C. The default address of these modules is
+0x09 but this can be changed through a command. By this you could
+dasy-chain up to 127 BlinkMs on an I2C bus.
+
+The device accepts RGB and HSB color values through separate commands.
+Also you can store blinking sequences as "scripts" in
+the controller and run them. Also fading is an option.
+
+The interface this driver provides is 2-fold:
+
+a) LED class interface for use with triggers
+############################################
+
+The registration follows the scheme:
+blinkm-<i2c-bus-nr>-<i2c-device-nr>-<color>
+
+$ ls -h /sys/class/leds/blinkm-6-*
+/sys/class/leds/blinkm-6-9-blue:
+brightness  device  max_brightness  power  subsystem  trigger  uevent
+
+/sys/class/leds/blinkm-6-9-green:
+brightness  device  max_brightness  power  subsystem  trigger  uevent
+
+/sys/class/leds/blinkm-6-9-red:
+brightness  device  max_brightness  power  subsystem  trigger  uevent
+
+(same is /sys/bus/i2c/devices/6-0009/leds)
+
+We can control the colors separated into red, green and blue and
+assign triggers on each color.
+
+E.g.:
+
+$ cat blinkm-6-9-blue/brightness
+05
+
+$ echo 200 > blinkm-6-9-blue/brightness
+$
+
+$ modprobe ledtrig-heartbeat
+$ echo heartbeat > blinkm-6-9-green/trigger
+$
+
+
+b) Sysfs group to control rgb, fade, hsb, scripts ...
+#####################################################
+
+This extended interface is available as folder blinkm
+in the sysfs folder of the I2C device.
+E.g. below /sys/bus/i2c/devices/6-0009/blinkm
+
+$ ls -h /sys/bus/i2c/devices/6-0009/blinkm/
+blue  green  red  test
+
+Currently supported is just setting red, green, blue
+and a test sequence.
+
+E.g.:
+
+$ cat *
+00
+00
+00
+#Write into test to start test sequence!#
+
+$ echo 1 > test
+$
+
+$ echo 255 > red
+$
+
+
+
+as of 6/2012
+
+dl9pf <at> gmx <dot> de
+
diff --git a/Documentation/leds/leds-lm3556.txt b/Documentation/leds/leds-lm3556.txt
new file mode 100644 (file)
index 0000000..d9eb91b
--- /dev/null
@@ -0,0 +1,85 @@
+Kernel driver for lm3556
+========================
+
+*Texas Instrument:
+ 1.5 A Synchronous Boost LED Flash Driver w/ High-Side Current Source
+* Datasheet: http://www.national.com/ds/LM/LM3556.pdf
+
+Authors:
+       Daniel Jeong
+       Contact:Daniel Jeong(daniel.jeong-at-ti.com, gshark.jeong-at-gmail.com)
+
+Description
+-----------
+There are 3 functions in LM3556, Flash, Torch and Indicator.
+
+FLASH MODE
+In Flash Mode, the LED current source(LED) provides 16 target current levels
+from 93.75 mA to 1500 mA.The Flash currents are adjusted via the CURRENT
+CONTROL REGISTER(0x09).Flash mode is activated by the ENABLE REGISTER(0x0A),
+or by pulling the STROBE pin HIGH.
+LM3556 Flash can be controlled through sys/class/leds/flash/brightness file
+* if STROBE pin is enabled, below example control brightness only, and
+ON / OFF will be controlled by STROBE pin.
+
+Flash Example:
+OFF     : #echo 0 > sys/class/leds/flash/brightness
+93.75 mA: #echo 1 > sys/class/leds/flash/brightness
+... .....
+1500  mA: #echo 16 > sys/class/leds/flash/brightness
+
+TORCH MODE
+In Torch Mode, the current source(LED) is programmed via the CURRENT CONTROL
+REGISTER(0x09).Torch Mode is activated by the ENABLE REGISTER(0x0A) or by the
+hardware TORCH input.
+LM3556 torch can be controlled through sys/class/leds/torch/brightness file.
+* if TORCH pin is enabled, below example control brightness only,
+and ON / OFF will be controlled by TORCH pin.
+
+Torch Example:
+OFF     : #echo 0 > sys/class/leds/torch/brightness
+46.88 mA: #echo 1 > sys/class/leds/torch/brightness
+... .....
+375 mA  : #echo 8 > sys/class/leds/torch/brightness
+
+INDICATOR MODE
+Indicator pattern can be set through sys/class/leds/indicator/pattern file,
+and 4 patterns are pre-defined in indicator_pattern array.
+According to N-lank, Pulse time and N Period values, different pattern wiill
+be generated.If you want new patterns for your own device, change
+indicator_pattern array with your own values and INDIC_PATTERN_SIZE.
+Please refer datasheet for more detail about N-Blank, Pulse time and N Period.
+
+Indicator pattern example:
+pattern 0: #echo 0 > sys/class/leds/indicator/pattern
+....
+pattern 3: #echo 3 > sys/class/leds/indicator/pattern
+
+Indicator brightness can be controlled through
+sys/class/leds/indicator/brightness file.
+
+Example:
+OFF      : #echo 0 > sys/class/leds/indicator/brightness
+5.86 mA  : #echo 1 > sys/class/leds/indicator/brightness
+........
+46.875mA : #echo 8 > sys/class/leds/indicator/brightness
+
+Notes
+-----
+Driver expects it is registered using the i2c_board_info mechanism.
+To register the chip at address 0x63 on specific adapter, set the platform data
+according to include/linux/platform_data/leds-lm3556.h, set the i2c board info
+
+Example:
+       static struct i2c_board_info __initdata board_i2c_ch4[] = {
+               {
+                        I2C_BOARD_INFO(LM3556_NAME, 0x63),
+                        .platform_data = &lm3556_pdata,
+                },
+       };
+
+and register it in the platform init function
+
+Example:
+       board_register_i2c_bus(4, 400,
+                               board_i2c_ch4, ARRAY_SIZE(board_i2c_ch4));
diff --git a/Documentation/leds/ledtrig-oneshot.txt b/Documentation/leds/ledtrig-oneshot.txt
new file mode 100644 (file)
index 0000000..07cd1fa
--- /dev/null
@@ -0,0 +1,59 @@
+One-shot LED Trigger
+====================
+
+This is a LED trigger useful for signaling the user of an event where there are
+no clear trap points to put standard led-on and led-off settings.  Using this
+trigger, the application needs only to signal the trigger when an event has
+happened, than the trigger turns the LED on and than keeps it off for a
+specified amount of time.
+
+This trigger is meant to be usable both for sporadic and dense events.  In the
+first case, the trigger produces a clear single controlled blink for each
+event, while in the latter it keeps blinking at constant rate, as to signal
+that the events are arriving continuously.
+
+A one-shot LED only stays in a constant state when there are no events.  An
+additional "invert" property specifies if the LED has to stay off (normal) or
+on (inverted) when not rearmed.
+
+The trigger can be activated from user space on led class devices as shown
+below:
+
+  echo oneshot > trigger
+
+This adds the following sysfs attributes to the LED:
+
+  delay_on - specifies for how many milliseconds the LED has to stay at
+             LED_FULL brightness after it has been armed.
+             Default to 100 ms.
+
+  delay_off - specifies for how many milliseconds the LED has to stay at
+              LED_OFF brightness after it has been armed.
+              Default to 100 ms.
+
+  invert - reverse the blink logic.  If set to 0 (default) blink on for delay_on
+           ms, then blink off for delay_off ms, leaving the LED normally off.  If
+           set to 1, blink off for delay_off ms, then blink on for delay_on ms,
+           leaving the LED normally on.
+           Setting this value also immediately change the LED state.
+
+  shot - write any non-empty string to signal an events, this starts a blink
+         sequence if not already running.
+
+Example use-case: network devices, initialization:
+
+  echo oneshot > trigger # set trigger for this led
+  echo 33 > delay_on     # blink at 1 / (33 + 33) Hz on continuous traffic
+  echo 33 > delay_off
+
+interface goes up:
+
+  echo 1 > invert # set led as normally-on, turn the led on
+
+packet received/transmitted:
+
+  echo 1 > shot # led starts blinking, ignored if already blinking
+
+interface goes down
+
+  echo 0 > invert # set led as normally-off, turn the led off
index 8f363b005226a84701a6ef96392281afa47329db..3bc49687b653f55d1bd1c07656a43378df9630b9 100644 (file)
@@ -1529,6 +1529,11 @@ W:       http://blackfin.uclinux.org/
 S:     Supported
 F:     drivers/i2c/busses/i2c-bfin-twi.c
 
+BLINKM RGB LED DRIVER
+M:     Jan-Simon Moeller <jansimon.moeller@gmx.de>
+S:     Maintained
+F:     drivers/leds/leds-blinkm.c
+
 BLOCK LAYER
 M:     Jens Axboe <axboe@kernel.dk>
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/axboe/linux-block.git
index 12b2b55c519e7b3038677edfd9c1b4f710493f4c..c96bbaadeebd75afa2357f638295c2b2e26c811e 100644 (file)
@@ -200,6 +200,13 @@ config LEDS_LP5523
          Driver provides direct control via LED class and interface for
          programming the engines.
 
+config LEDS_LP8788
+       tristate "LED support for the TI LP8788 PMIC"
+       depends on LEDS_CLASS
+       depends on MFD_LP8788
+       help
+         This option enables support for the Keyboard LEDs on the LP8788 PMIC.
+
 config LEDS_CLEVO_MAIL
        tristate "Mail LED on Clevo notebook"
        depends on LEDS_CLASS
@@ -415,6 +422,14 @@ config LEDS_MAX8997
          This option enables support for on-chip LED drivers on
          MAXIM MAX8997 PMIC.
 
+config LEDS_LM3556
+       tristate "LED support for LM3556 Chip"
+       depends on LEDS_CLASS && I2C
+       select REGMAP_I2C
+       help
+         This option enables support for LEDs connected to LM3556.
+         LM3556 includes Torch, Flash and Indicator functions.
+
 config LEDS_OT200
        tristate "LED support for the Bachmann OT200"
        depends on LEDS_CLASS && HAS_IOMEM
@@ -422,6 +437,14 @@ config LEDS_OT200
          This option enables support for the LEDs on the Bachmann OT200.
          Say Y to enable LEDs on the Bachmann OT200.
 
+config LEDS_BLINKM
+       tristate "LED support for the BlinkM I2C RGB LED"
+       depends on LEDS_CLASS
+       depends on I2C
+       help
+         This option enables support for the BlinkM RGB LED connected
+         through I2C. Say Y to enable support for the BlinkM LED.
+
 config LEDS_TRIGGERS
        bool "LED Trigger support"
        depends on LEDS_CLASS
@@ -443,6 +466,20 @@ config LEDS_TRIGGER_TIMER
 
          If unsure, say Y.
 
+config LEDS_TRIGGER_ONESHOT
+       tristate "LED One-shot Trigger"
+       depends on LEDS_TRIGGERS
+       help
+         This allows LEDs to blink in one-shot pulses with parameters
+         controlled via sysfs.  It's useful to notify the user on
+         sporadic events, when there are no clear begin and end trap points,
+         or on dense events, where this blinks the LED at constant rate if
+         rearmed continuously.
+
+         It also shows how to use the led_blink_set_oneshot() function.
+
+         If unsure, say Y.
+
 config LEDS_TRIGGER_IDE_DISK
        bool "LED IDE Disk Trigger"
        depends on IDE_GD_ATA
@@ -497,7 +534,7 @@ config LEDS_TRIGGER_TRANSIENT
        depends on LEDS_TRIGGERS
        help
          This allows one time activation of a transient state on
-         GPIO/PWM based hadrware.
+         GPIO/PWM based hardware.
          If unsure, say Y.
 
 endif # NEW_LEDS
index f8958cd6cf6e82813483aaf52a5ff3cde1e684a5..a4429a9217bceb7303caa7a50fe49d0966f81ad5 100644 (file)
@@ -24,6 +24,7 @@ obj-$(CONFIG_LEDS_GPIO)                       += leds-gpio.o
 obj-$(CONFIG_LEDS_LP3944)              += leds-lp3944.o
 obj-$(CONFIG_LEDS_LP5521)              += leds-lp5521.o
 obj-$(CONFIG_LEDS_LP5523)              += leds-lp5523.o
+obj-$(CONFIG_LEDS_LP8788)              += leds-lp8788.o
 obj-$(CONFIG_LEDS_TCA6507)             += leds-tca6507.o
 obj-$(CONFIG_LEDS_CLEVO_MAIL)          += leds-clevo-mail.o
 obj-$(CONFIG_LEDS_HP6XX)               += leds-hp6xx.o
@@ -47,12 +48,15 @@ obj-$(CONFIG_LEDS_NETXBIG)          += leds-netxbig.o
 obj-$(CONFIG_LEDS_ASIC3)               += leds-asic3.o
 obj-$(CONFIG_LEDS_RENESAS_TPU)         += leds-renesas-tpu.o
 obj-$(CONFIG_LEDS_MAX8997)             += leds-max8997.o
+obj-$(CONFIG_LEDS_LM3556)              += leds-lm3556.o
+obj-$(CONFIG_LEDS_BLINKM)              += leds-blinkm.o
 
 # LED SPI Drivers
 obj-$(CONFIG_LEDS_DAC124S085)          += leds-dac124s085.o
 
 # LED Triggers
 obj-$(CONFIG_LEDS_TRIGGER_TIMER)       += ledtrig-timer.o
+obj-$(CONFIG_LEDS_TRIGGER_ONESHOT)     += ledtrig-oneshot.o
 obj-$(CONFIG_LEDS_TRIGGER_IDE_DISK)    += ledtrig-ide-disk.o
 obj-$(CONFIG_LEDS_TRIGGER_HEARTBEAT)   += ledtrig-heartbeat.o
 obj-$(CONFIG_LEDS_TRIGGER_BACKLIGHT)   += ledtrig-backlight.o
index e663e6f413e989d835067811a992bb62dc316f36..c599095bc00556f9c79b3444dd2933f89ca0496a 100644 (file)
@@ -53,7 +53,7 @@ static ssize_t led_brightness_store(struct device *dev,
 
        if (state == LED_OFF)
                led_trigger_remove(led_cdev);
-       led_set_brightness(led_cdev, state);
+       __led_set_brightness(led_cdev, state);
 
        return size;
 }
@@ -82,7 +82,12 @@ static void led_timer_function(unsigned long data)
        unsigned long delay;
 
        if (!led_cdev->blink_delay_on || !led_cdev->blink_delay_off) {
-               led_set_brightness(led_cdev, LED_OFF);
+               __led_set_brightness(led_cdev, LED_OFF);
+               return;
+       }
+
+       if (led_cdev->flags & LED_BLINK_ONESHOT_STOP) {
+               led_cdev->flags &= ~LED_BLINK_ONESHOT_STOP;
                return;
        }
 
@@ -100,7 +105,21 @@ static void led_timer_function(unsigned long data)
                delay = led_cdev->blink_delay_off;
        }
 
-       led_set_brightness(led_cdev, brightness);
+       __led_set_brightness(led_cdev, brightness);
+
+       /* Return in next iteration if led is in one-shot mode and we are in
+        * the final blink state so that the led is toggled each delay_on +
+        * delay_off milliseconds in worst case.
+        */
+       if (led_cdev->flags & LED_BLINK_ONESHOT) {
+               if (led_cdev->flags & LED_BLINK_INVERT) {
+                       if (brightness)
+                               led_cdev->flags |= LED_BLINK_ONESHOT_STOP;
+               } else {
+                       if (!brightness)
+                               led_cdev->flags |= LED_BLINK_ONESHOT_STOP;
+               }
+       }
 
        mod_timer(&led_cdev->blink_timer, jiffies + msecs_to_jiffies(delay));
 }
@@ -203,7 +222,7 @@ void led_classdev_unregister(struct led_classdev *led_cdev)
 #endif
 
        /* Stop blinking */
-       led_brightness_set(led_cdev, LED_OFF);
+       led_set_brightness(led_cdev, LED_OFF);
 
        device_unregister(led_cdev->dev);
 
index d65353d8d3fcb4ae012460c20bc33f20c2afa84f..2ab05af3de31ab188fb0300befb04a708702637f 100644 (file)
@@ -24,14 +24,6 @@ EXPORT_SYMBOL_GPL(leds_list_lock);
 LIST_HEAD(leds_list);
 EXPORT_SYMBOL_GPL(leds_list);
 
-static void led_stop_software_blink(struct led_classdev *led_cdev)
-{
-       /* deactivate previous settings */
-       del_timer_sync(&led_cdev->blink_timer);
-       led_cdev->blink_delay_on = 0;
-       led_cdev->blink_delay_off = 0;
-}
-
 static void led_set_software_blink(struct led_classdev *led_cdev,
                                   unsigned long delay_on,
                                   unsigned long delay_off)
@@ -53,7 +45,7 @@ static void led_set_software_blink(struct led_classdev *led_cdev,
 
        /* never off - just set to brightness */
        if (!delay_off) {
-               led_set_brightness(led_cdev, led_cdev->blink_brightness);
+               __led_set_brightness(led_cdev, led_cdev->blink_brightness);
                return;
        }
 
@@ -61,13 +53,12 @@ static void led_set_software_blink(struct led_classdev *led_cdev,
 }
 
 
-void led_blink_set(struct led_classdev *led_cdev,
-                  unsigned long *delay_on,
-                  unsigned long *delay_off)
+static void led_blink_setup(struct led_classdev *led_cdev,
+                    unsigned long *delay_on,
+                    unsigned long *delay_off)
 {
-       del_timer_sync(&led_cdev->blink_timer);
-
-       if (led_cdev->blink_set &&
+       if (!(led_cdev->flags & LED_BLINK_ONESHOT) &&
+           led_cdev->blink_set &&
            !led_cdev->blink_set(led_cdev, delay_on, delay_off))
                return;
 
@@ -77,12 +68,49 @@ void led_blink_set(struct led_classdev *led_cdev,
 
        led_set_software_blink(led_cdev, *delay_on, *delay_off);
 }
+
+void led_blink_set(struct led_classdev *led_cdev,
+                  unsigned long *delay_on,
+                  unsigned long *delay_off)
+{
+       del_timer_sync(&led_cdev->blink_timer);
+
+       led_cdev->flags &= ~LED_BLINK_ONESHOT;
+       led_cdev->flags &= ~LED_BLINK_ONESHOT_STOP;
+
+       led_blink_setup(led_cdev, delay_on, delay_off);
+}
 EXPORT_SYMBOL(led_blink_set);
 
-void led_brightness_set(struct led_classdev *led_cdev,
+void led_blink_set_oneshot(struct led_classdev *led_cdev,
+                          unsigned long *delay_on,
+                          unsigned long *delay_off,
+                          int invert)
+{
+       if ((led_cdev->flags & LED_BLINK_ONESHOT) &&
+            timer_pending(&led_cdev->blink_timer))
+               return;
+
+       led_cdev->flags |= LED_BLINK_ONESHOT;
+       led_cdev->flags &= ~LED_BLINK_ONESHOT_STOP;
+
+       if (invert)
+               led_cdev->flags |= LED_BLINK_INVERT;
+       else
+               led_cdev->flags &= ~LED_BLINK_INVERT;
+
+       led_blink_setup(led_cdev, delay_on, delay_off);
+}
+EXPORT_SYMBOL(led_blink_set_oneshot);
+
+void led_set_brightness(struct led_classdev *led_cdev,
                        enum led_brightness brightness)
 {
-       led_stop_software_blink(led_cdev);
-       led_cdev->brightness_set(led_cdev, brightness);
+       /* stop and clear soft-blink timer */
+       del_timer_sync(&led_cdev->blink_timer);
+       led_cdev->blink_delay_on = 0;
+       led_cdev->blink_delay_off = 0;
+
+       __led_set_brightness(led_cdev, brightness);
 }
-EXPORT_SYMBOL(led_brightness_set);
+EXPORT_SYMBOL(led_set_brightness);
index 46b4c766335d06bca0d1ce9539a21a32503c9212..6157cbbf41131cb1f2fdf7b4f02e96f48771a8b1 100644 (file)
@@ -99,7 +99,7 @@ ssize_t led_trigger_show(struct device *dev, struct device_attribute *attr,
 EXPORT_SYMBOL_GPL(led_trigger_show);
 
 /* Caller must ensure led_cdev->trigger_lock held */
-void led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trigger)
+void led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trig)
 {
        unsigned long flags;
 
@@ -112,15 +112,15 @@ void led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trigger)
                if (led_cdev->trigger->deactivate)
                        led_cdev->trigger->deactivate(led_cdev);
                led_cdev->trigger = NULL;
-               led_brightness_set(led_cdev, LED_OFF);
+               led_set_brightness(led_cdev, LED_OFF);
        }
-       if (trigger) {
-               write_lock_irqsave(&trigger->leddev_list_lock, flags);
-               list_add_tail(&led_cdev->trig_list, &trigger->led_cdevs);
-               write_unlock_irqrestore(&trigger->leddev_list_lock, flags);
-               led_cdev->trigger = trigger;
-               if (trigger->activate)
-                       trigger->activate(led_cdev);
+       if (trig) {
+               write_lock_irqsave(&trig->leddev_list_lock, flags);
+               list_add_tail(&led_cdev->trig_list, &trig->led_cdevs);
+               write_unlock_irqrestore(&trig->leddev_list_lock, flags);
+               led_cdev->trigger = trig;
+               if (trig->activate)
+                       trig->activate(led_cdev);
        }
 }
 EXPORT_SYMBOL_GPL(led_trigger_set);
@@ -153,24 +153,24 @@ EXPORT_SYMBOL_GPL(led_trigger_set_default);
 
 /* LED Trigger Interface */
 
-int led_trigger_register(struct led_trigger *trigger)
+int led_trigger_register(struct led_trigger *trig)
 {
        struct led_classdev *led_cdev;
-       struct led_trigger *trig;
+       struct led_trigger *_trig;
 
-       rwlock_init(&trigger->leddev_list_lock);
-       INIT_LIST_HEAD(&trigger->led_cdevs);
+       rwlock_init(&trig->leddev_list_lock);
+       INIT_LIST_HEAD(&trig->led_cdevs);
 
        down_write(&triggers_list_lock);
        /* Make sure the trigger's name isn't already in use */
-       list_for_each_entry(trig, &trigger_list, next_trig) {
-               if (!strcmp(trig->name, trigger->name)) {
+       list_for_each_entry(_trig, &trigger_list, next_trig) {
+               if (!strcmp(_trig->name, trig->name)) {
                        up_write(&triggers_list_lock);
                        return -EEXIST;
                }
        }
        /* Add to the list of led triggers */
-       list_add_tail(&trigger->next_trig, &trigger_list);
+       list_add_tail(&trig->next_trig, &trigger_list);
        up_write(&triggers_list_lock);
 
        /* Register with any LEDs that have this as a default trigger */
@@ -178,8 +178,8 @@ int led_trigger_register(struct led_trigger *trigger)
        list_for_each_entry(led_cdev, &leds_list, node) {
                down_write(&led_cdev->trigger_lock);
                if (!led_cdev->trigger && led_cdev->default_trigger &&
-                           !strcmp(led_cdev->default_trigger, trigger->name))
-                       led_trigger_set(led_cdev, trigger);
+                           !strcmp(led_cdev->default_trigger, trig->name))
+                       led_trigger_set(led_cdev, trig);
                up_write(&led_cdev->trigger_lock);
        }
        up_read(&leds_list_lock);
@@ -188,20 +188,20 @@ int led_trigger_register(struct led_trigger *trigger)
 }
 EXPORT_SYMBOL_GPL(led_trigger_register);
 
-void led_trigger_unregister(struct led_trigger *trigger)
+void led_trigger_unregister(struct led_trigger *trig)
 {
        struct led_classdev *led_cdev;
 
        /* Remove from the list of led triggers */
        down_write(&triggers_list_lock);
-       list_del(&trigger->next_trig);
+       list_del(&trig->next_trig);
        up_write(&triggers_list_lock);
 
        /* Remove anyone actively using this trigger */
        down_read(&leds_list_lock);
        list_for_each_entry(led_cdev, &leds_list, node) {
                down_write(&led_cdev->trigger_lock);
-               if (led_cdev->trigger == trigger)
+               if (led_cdev->trigger == trig)
                        led_trigger_set(led_cdev, NULL);
                up_write(&led_cdev->trigger_lock);
        }
@@ -211,58 +211,80 @@ EXPORT_SYMBOL_GPL(led_trigger_unregister);
 
 /* Simple LED Tigger Interface */
 
-void led_trigger_event(struct led_trigger *trigger,
+void led_trigger_event(struct led_trigger *trig,
                        enum led_brightness brightness)
 {
        struct list_head *entry;
 
-       if (!trigger)
+       if (!trig)
                return;
 
-       read_lock(&trigger->leddev_list_lock);
-       list_for_each(entry, &trigger->led_cdevs) {
+       read_lock(&trig->leddev_list_lock);
+       list_for_each(entry, &trig->led_cdevs) {
                struct led_classdev *led_cdev;
 
                led_cdev = list_entry(entry, struct led_classdev, trig_list);
                led_set_brightness(led_cdev, brightness);
        }
-       read_unlock(&trigger->leddev_list_lock);
+       read_unlock(&trig->leddev_list_lock);
 }
 EXPORT_SYMBOL_GPL(led_trigger_event);
 
-void led_trigger_blink(struct led_trigger *trigger,
-                      unsigned long *delay_on,
-                      unsigned long *delay_off)
+static void led_trigger_blink_setup(struct led_trigger *trig,
+                            unsigned long *delay_on,
+                            unsigned long *delay_off,
+                            int oneshot,
+                            int invert)
 {
        struct list_head *entry;
 
-       if (!trigger)
+       if (!trig)
                return;
 
-       read_lock(&trigger->leddev_list_lock);
-       list_for_each(entry, &trigger->led_cdevs) {
+       read_lock(&trig->leddev_list_lock);
+       list_for_each(entry, &trig->led_cdevs) {
                struct led_classdev *led_cdev;
 
                led_cdev = list_entry(entry, struct led_classdev, trig_list);
-               led_blink_set(led_cdev, delay_on, delay_off);
+               if (oneshot)
+                       led_blink_set_oneshot(led_cdev, delay_on, delay_off,
+                                             invert);
+               else
+                       led_blink_set(led_cdev, delay_on, delay_off);
        }
-       read_unlock(&trigger->leddev_list_lock);
+       read_unlock(&trig->leddev_list_lock);
+}
+
+void led_trigger_blink(struct led_trigger *trig,
+                      unsigned long *delay_on,
+                      unsigned long *delay_off)
+{
+       led_trigger_blink_setup(trig, delay_on, delay_off, 0, 0);
 }
 EXPORT_SYMBOL_GPL(led_trigger_blink);
 
+void led_trigger_blink_oneshot(struct led_trigger *trig,
+                              unsigned long *delay_on,
+                              unsigned long *delay_off,
+                              int invert)
+{
+       led_trigger_blink_setup(trig, delay_on, delay_off, 1, invert);
+}
+EXPORT_SYMBOL_GPL(led_trigger_blink_oneshot);
+
 void led_trigger_register_simple(const char *name, struct led_trigger **tp)
 {
-       struct led_trigger *trigger;
+       struct led_trigger *trig;
        int err;
 
-       trigger = kzalloc(sizeof(struct led_trigger), GFP_KERNEL);
+       trig = kzalloc(sizeof(struct led_trigger), GFP_KERNEL);
 
-       if (trigger) {
-               trigger->name = name;
-               err = led_trigger_register(trigger);
+       if (trig) {
+               trig->name = name;
+               err = led_trigger_register(trig);
                if (err < 0) {
-                       kfree(trigger);
-                       trigger = NULL;
+                       kfree(trig);
+                       trig = NULL;
                        printk(KERN_WARNING "LED trigger %s failed to register"
                                " (%d)\n", name, err);
                }
@@ -270,15 +292,15 @@ void led_trigger_register_simple(const char *name, struct led_trigger **tp)
                printk(KERN_WARNING "LED trigger %s failed to register"
                        " (no memory)\n", name);
 
-       *tp = trigger;
+       *tp = trig;
 }
 EXPORT_SYMBOL_GPL(led_trigger_register_simple);
 
-void led_trigger_unregister_simple(struct led_trigger *trigger)
+void led_trigger_unregister_simple(struct led_trigger *trig)
 {
-       if (trigger)
-               led_trigger_unregister(trigger);
-       kfree(trigger);
+       if (trig)
+               led_trigger_unregister(trig);
+       kfree(trig);
 }
 EXPORT_SYMBOL_GPL(led_trigger_unregister_simple);
 
index 5b61aaf7ac0f3faa565430fd1acafef8162afd20..61897cfeeda6ed76bbd012a385a7a7c5a0656f4a 100644 (file)
@@ -209,7 +209,7 @@ static int pm860x_led_probe(struct platform_device *pdev)
                return -EINVAL;
        }
 
-       data = kzalloc(sizeof(struct pm860x_led), GFP_KERNEL);
+       data = devm_kzalloc(&pdev->dev, sizeof(struct pm860x_led), GFP_KERNEL);
        if (data == NULL)
                return -ENOMEM;
        strncpy(data->name, res->name, MFD_NAME_SIZE - 1);
@@ -220,7 +220,6 @@ static int pm860x_led_probe(struct platform_device *pdev)
        data->port = pdata->flags;
        if (data->port < 0) {
                dev_err(&pdev->dev, "check device failed\n");
-               kfree(data);
                return -EINVAL;
        }
 
@@ -233,13 +232,10 @@ static int pm860x_led_probe(struct platform_device *pdev)
        ret = led_classdev_register(chip->dev, &data->cdev);
        if (ret < 0) {
                dev_err(&pdev->dev, "Failed to register LED: %d\n", ret);
-               goto out;
+               return ret;
        }
        pm860x_led_set(&data->cdev, 0);
        return 0;
-out:
-       kfree(data);
-       return ret;
 }
 
 static int pm860x_led_remove(struct platform_device *pdev)
@@ -247,7 +243,6 @@ static int pm860x_led_remove(struct platform_device *pdev)
        struct pm860x_led *data = platform_get_drvdata(pdev);
 
        led_classdev_unregister(&data->cdev);
-       kfree(data);
 
        return 0;
 }
index b1400db3f839a2730b6dc29db302306fb3f627eb..aa56a867693afebfcdfc2dd49b739dd37c9029ae 100644 (file)
@@ -119,7 +119,8 @@ static int __devinit adp5520_led_probe(struct platform_device *pdev)
                return -EFAULT;
        }
 
-       led = kzalloc(sizeof(*led) * pdata->num_leds, GFP_KERNEL);
+       led = devm_kzalloc(&pdev->dev, sizeof(*led) * pdata->num_leds,
+                               GFP_KERNEL);
        if (led == NULL) {
                dev_err(&pdev->dev, "failed to alloc memory\n");
                return -ENOMEM;
@@ -129,7 +130,7 @@ static int __devinit adp5520_led_probe(struct platform_device *pdev)
 
        if (ret) {
                dev_err(&pdev->dev, "failed to write\n");
-               goto err_free;
+               return ret;
        }
 
        for (i = 0; i < pdata->num_leds; ++i) {
@@ -179,8 +180,6 @@ err:
                }
        }
 
-err_free:
-       kfree(led);
        return ret;
 }
 
@@ -200,7 +199,6 @@ static int __devexit adp5520_led_remove(struct platform_device *pdev)
                cancel_work_sync(&led[i].work);
        }
 
-       kfree(led);
        return 0;
 }
 
index 525a92492837bb892de5b4fb29050bf939a0a4fb..5de74ff90dcf3ffddb4ba22b5ed94976f59f9ffc 100644 (file)
@@ -99,12 +99,13 @@ static int __devinit asic3_led_probe(struct platform_device *pdev)
 
        ret = mfd_cell_enable(pdev);
        if (ret < 0)
-               goto ret0;
+               return ret;
 
-       led->cdev = kzalloc(sizeof(struct led_classdev), GFP_KERNEL);
+       led->cdev = devm_kzalloc(&pdev->dev, sizeof(struct led_classdev),
+                               GFP_KERNEL);
        if (!led->cdev) {
                ret = -ENOMEM;
-               goto ret1;
+               goto out;
        }
 
        led->cdev->name = led->name;
@@ -115,15 +116,12 @@ static int __devinit asic3_led_probe(struct platform_device *pdev)
 
        ret = led_classdev_register(&pdev->dev, led->cdev);
        if (ret < 0)
-               goto ret2;
+               goto out;
 
        return 0;
 
-ret2:
-       kfree(led->cdev);
-ret1:
+out:
        (void) mfd_cell_disable(pdev);
-ret0:
        return ret;
 }
 
@@ -133,8 +131,6 @@ static int __devexit asic3_led_remove(struct platform_device *pdev)
 
        led_classdev_unregister(led->cdev);
 
-       kfree(led->cdev);
-
        return mfd_cell_disable(pdev);
 }
 
index 64ad702a2ecc64eff2505fd0f4217126fd10bdf9..45430632faab069d2638afa64b964763475f1e7b 100644 (file)
@@ -46,7 +46,8 @@ static int __devinit pwmled_probe(struct platform_device *pdev)
        if (!pdata || pdata->num_leds < 1)
                return -ENODEV;
 
-       leds = kcalloc(pdata->num_leds, sizeof(*leds), GFP_KERNEL);
+       leds = devm_kzalloc(&pdev->dev, pdata->num_leds * sizeof(*leds),
+                       GFP_KERNEL);
        if (!leds)
                return -ENOMEM;
 
@@ -108,7 +109,6 @@ err:
                        pwm_channel_free(&leds[i].pwmc);
                }
        }
-       kfree(leds);
 
        return status;
 }
@@ -129,7 +129,6 @@ static int __exit pwmled_remove(struct platform_device *pdev)
                pwm_channel_free(&led->pwmc);
        }
 
-       kfree(leds);
        platform_set_drvdata(pdev, NULL);
        return 0;
 }
index 591cbdf5a0463e99ab91892bfc50b0beeb1c579c..89ca6a2a19d13cd28c2a86fbca9d30812e2cb9da 100644 (file)
@@ -677,7 +677,7 @@ static int __devinit bd2802_probe(struct i2c_client *client,
        struct bd2802_led_platform_data *pdata;
        int ret, i;
 
-       led = kzalloc(sizeof(struct bd2802_led), GFP_KERNEL);
+       led = devm_kzalloc(&client->dev, sizeof(struct bd2802_led), GFP_KERNEL);
        if (!led) {
                dev_err(&client->dev, "failed to allocate driver data\n");
                return -ENOMEM;
@@ -697,7 +697,7 @@ static int __devinit bd2802_probe(struct i2c_client *client,
        ret = bd2802_write_byte(client, BD2802_REG_CLKSETUP, 0x00);
        if (ret < 0) {
                dev_err(&client->dev, "failed to detect device\n");
-               goto failed_free;
+               return ret;
        } else
                dev_info(&client->dev, "return 0x%02x\n", ret);
 
@@ -729,9 +729,6 @@ static int __devinit bd2802_probe(struct i2c_client *client,
 failed_unregister_dev_file:
        for (i--; i >= 0; i--)
                device_remove_file(&led->client->dev, bd2802_attributes[i]);
-failed_free:
-       kfree(led);
-
        return ret;
 }
 
@@ -746,7 +743,6 @@ static int __exit bd2802_remove(struct i2c_client *client)
                bd2802_disable_adv_conf(led);
        for (i = 0; i < ARRAY_SIZE(bd2802_attributes); i++)
                device_remove_file(&led->client->dev, bd2802_attributes[i]);
-       kfree(led);
 
        return 0;
 }
diff --git a/drivers/leds/leds-blinkm.c b/drivers/leds/leds-blinkm.c
new file mode 100644 (file)
index 0000000..f7c3d7f
--- /dev/null
@@ -0,0 +1,815 @@
+/*
+ *  leds-blinkm.c
+ *  (c) Jan-Simon Möller (dl9pf@gmx.de)
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/sysfs.h>
+#include <linux/printk.h>
+#include <linux/pm_runtime.h>
+#include <linux/leds.h>
+#include <linux/delay.h>
+
+/* Addresses to scan - BlinkM is on 0x09 by default*/
+static const unsigned short normal_i2c[] = { 0x09, I2C_CLIENT_END };
+
+static int blinkm_transfer_hw(struct i2c_client *client, int cmd);
+static int blinkm_test_run(struct i2c_client *client);
+
+struct blinkm_led {
+       struct i2c_client *i2c_client;
+       struct led_classdev led_cdev;
+       int id;
+       atomic_t active;
+};
+
+struct blinkm_work {
+       struct blinkm_led *blinkm_led;
+       struct work_struct work;
+};
+
+#define cdev_to_blmled(c)          container_of(c, struct blinkm_led, led_cdev)
+#define work_to_blmwork(c)         container_of(c, struct blinkm_work, work)
+
+struct blinkm_data {
+       struct i2c_client *i2c_client;
+       struct mutex update_lock;
+       /* used for led class interface */
+       struct blinkm_led blinkm_leds[3];
+       /* used for "blinkm" sysfs interface */
+       u8 red;                 /* color red */
+       u8 green;               /* color green */
+       u8 blue;                /* color blue */
+       /* next values to use for transfer */
+       u8 next_red;                    /* color red */
+       u8 next_green;          /* color green */
+       u8 next_blue;           /* color blue */
+       /* internal use */
+       u8 args[7];             /* set of args for transmission */
+       u8 i2c_addr;            /* i2c addr */
+       u8 fw_ver;              /* firmware version */
+       /* used, but not from userspace */
+       u8 hue;                 /* HSB  hue */
+       u8 saturation;          /* HSB  saturation */
+       u8 brightness;          /* HSB  brightness */
+       u8 next_hue;                    /* HSB  hue */
+       u8 next_saturation;             /* HSB  saturation */
+       u8 next_brightness;             /* HSB  brightness */
+       /* currently unused / todo */
+       u8 fade_speed;          /* fade speed     1 - 255 */
+       s8 time_adjust;         /* time adjust -128 - 127 */
+       u8 fade:1;              /* fade on = 1, off = 0 */
+       u8 rand:1;              /* rand fade mode on = 1 */
+       u8 script_id;           /* script ID */
+       u8 script_repeats;      /* repeats of script */
+       u8 script_startline;    /* line to start */
+};
+
+/* Colors */
+#define RED   0
+#define GREEN 1
+#define BLUE  2
+
+/* mapping command names to cmd chars - see datasheet */
+#define BLM_GO_RGB            0
+#define BLM_FADE_RGB          1
+#define BLM_FADE_HSB          2
+#define BLM_FADE_RAND_RGB     3
+#define BLM_FADE_RAND_HSB     4
+#define BLM_PLAY_SCRIPT       5
+#define BLM_STOP_SCRIPT       6
+#define BLM_SET_FADE_SPEED    7
+#define BLM_SET_TIME_ADJ      8
+#define BLM_GET_CUR_RGB       9
+#define BLM_WRITE_SCRIPT_LINE 10
+#define BLM_READ_SCRIPT_LINE  11
+#define BLM_SET_SCRIPT_LR     12       /* Length & Repeats */
+#define BLM_SET_ADDR          13
+#define BLM_GET_ADDR          14
+#define BLM_GET_FW_VER        15
+#define BLM_SET_STARTUP_PARAM 16
+
+/* BlinkM Commands
+ *  as extracted out of the datasheet:
+ *
+ *  cmdchar = command (ascii)
+ *  cmdbyte = command in hex
+ *  nr_args = number of arguments (to send)
+ *  nr_ret  = number of return values (to read)
+ *  dir = direction (0 = read, 1 = write, 2 = both)
+ *
+ */
+static const struct {
+       char cmdchar;
+       u8 cmdbyte;
+       u8 nr_args;
+       u8 nr_ret;
+       u8 dir:2;
+} blinkm_cmds[17] = {
+  /* cmdchar, cmdbyte, nr_args, nr_ret,  dir */
+       { 'n', 0x6e, 3, 0, 1},
+       { 'c', 0x63, 3, 0, 1},
+       { 'h', 0x68, 3, 0, 1},
+       { 'C', 0x43, 3, 0, 1},
+       { 'H', 0x48, 3, 0, 1},
+       { 'p', 0x70, 3, 0, 1},
+       { 'o', 0x6f, 0, 0, 1},
+       { 'f', 0x66, 1, 0, 1},
+       { 't', 0x74, 1, 0, 1},
+       { 'g', 0x67, 0, 3, 0},
+       { 'W', 0x57, 7, 0, 1},
+       { 'R', 0x52, 2, 5, 2},
+       { 'L', 0x4c, 3, 0, 1},
+       { 'A', 0x41, 4, 0, 1},
+       { 'a', 0x61, 0, 1, 0},
+       { 'Z', 0x5a, 0, 1, 0},
+       { 'B', 0x42, 5, 0, 1},
+};
+
+static ssize_t show_color_common(struct device *dev, char *buf, int color)
+{
+       struct i2c_client *client;
+       struct blinkm_data *data;
+       int ret;
+
+       client = to_i2c_client(dev);
+       data = i2c_get_clientdata(client);
+
+       ret = blinkm_transfer_hw(client, BLM_GET_CUR_RGB);
+       if (ret < 0)
+               return ret;
+       switch (color) {
+       case RED:
+               return scnprintf(buf, PAGE_SIZE, "%02X\n", data->red);
+               break;
+       case GREEN:
+               return scnprintf(buf, PAGE_SIZE, "%02X\n", data->green);
+               break;
+       case BLUE:
+               return scnprintf(buf, PAGE_SIZE, "%02X\n", data->blue);
+               break;
+       default:
+               return -EINVAL;
+       }
+       return -EINVAL;
+}
+
+static int store_color_common(struct device *dev, const char *buf, int color)
+{
+       struct i2c_client *client;
+       struct blinkm_data *data;
+       int ret;
+       u8 value;
+
+       client = to_i2c_client(dev);
+       data = i2c_get_clientdata(client);
+
+       ret = kstrtou8(buf, 10, &value);
+       if (ret < 0) {
+               dev_err(dev, "BlinkM: value too large!\n");
+               return ret;
+       }
+
+       switch (color) {
+       case RED:
+               data->next_red = value;
+               break;
+       case GREEN:
+               data->next_green = value;
+               break;
+       case BLUE:
+               data->next_blue = value;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       dev_dbg(dev, "next_red = %d, next_green = %d, next_blue = %d\n",
+                       data->next_red, data->next_green, data->next_blue);
+
+       /* if mode ... */
+       ret = blinkm_transfer_hw(client, BLM_GO_RGB);
+       if (ret < 0) {
+               dev_err(dev, "BlinkM: can't set RGB\n");
+               return ret;
+       }
+       return 0;
+}
+
+static ssize_t show_red(struct device *dev, struct device_attribute *attr,
+                       char *buf)
+{
+       return show_color_common(dev, buf, RED);
+}
+
+static ssize_t store_red(struct device *dev, struct device_attribute *attr,
+                        const char *buf, size_t count)
+{
+       int ret;
+
+       ret = store_color_common(dev, buf, RED);
+       if (ret < 0)
+               return ret;
+       return count;
+}
+
+static DEVICE_ATTR(red, S_IRUGO | S_IWUSR, show_red, store_red);
+
+static ssize_t show_green(struct device *dev, struct device_attribute *attr,
+                         char *buf)
+{
+       return show_color_common(dev, buf, GREEN);
+}
+
+static ssize_t store_green(struct device *dev, struct device_attribute *attr,
+                          const char *buf, size_t count)
+{
+
+       int ret;
+
+       ret = store_color_common(dev, buf, GREEN);
+       if (ret < 0)
+               return ret;
+       return count;
+}
+
+static DEVICE_ATTR(green, S_IRUGO | S_IWUSR, show_green, store_green);
+
+static ssize_t show_blue(struct device *dev, struct device_attribute *attr,
+                        char *buf)
+{
+       return show_color_common(dev, buf, BLUE);
+}
+
+static ssize_t store_blue(struct device *dev, struct device_attribute *attr,
+                         const char *buf, size_t count)
+{
+       int ret;
+
+       ret = store_color_common(dev, buf, BLUE);
+       if (ret < 0)
+               return ret;
+       return count;
+}
+
+static DEVICE_ATTR(blue, S_IRUGO | S_IWUSR, show_blue, store_blue);
+
+static ssize_t show_test(struct device *dev, struct device_attribute *attr,
+                        char *buf)
+{
+       return scnprintf(buf, PAGE_SIZE,
+                        "#Write into test to start test sequence!#\n");
+}
+
+static ssize_t store_test(struct device *dev, struct device_attribute *attr,
+                         const char *buf, size_t count)
+{
+
+       struct i2c_client *client;
+       int ret;
+       client = to_i2c_client(dev);
+
+       /*test */
+       ret = blinkm_test_run(client);
+       if (ret < 0)
+               return ret;
+
+       return count;
+}
+
+static DEVICE_ATTR(test, S_IRUGO | S_IWUSR, show_test, store_test);
+
+/* TODO: HSB, fade, timeadj, script ... */
+
+static struct attribute *blinkm_attrs[] = {
+       &dev_attr_red.attr,
+       &dev_attr_green.attr,
+       &dev_attr_blue.attr,
+       &dev_attr_test.attr,
+       NULL,
+};
+
+static struct attribute_group blinkm_group = {
+       .name = "blinkm",
+       .attrs = blinkm_attrs,
+};
+
+static int blinkm_write(struct i2c_client *client, int cmd, u8 *arg)
+{
+       int result;
+       int i;
+       int arglen = blinkm_cmds[cmd].nr_args;
+       /* write out cmd to blinkm - always / default step */
+       result = i2c_smbus_write_byte(client, blinkm_cmds[cmd].cmdbyte);
+       if (result < 0)
+               return result;
+       /* no args to write out */
+       if (arglen == 0)
+               return 0;
+
+       for (i = 0; i < arglen; i++) {
+               /* repeat for arglen */
+               result = i2c_smbus_write_byte(client, arg[i]);
+               if (result < 0)
+                       return result;
+       }
+       return 0;
+}
+
+static int blinkm_read(struct i2c_client *client, int cmd, u8 *arg)
+{
+       int result;
+       int i;
+       int retlen = blinkm_cmds[cmd].nr_ret;
+       for (i = 0; i < retlen; i++) {
+               /* repeat for retlen */
+               result = i2c_smbus_read_byte(client);
+               if (result < 0)
+                       return result;
+               arg[i] = result;
+       }
+
+       return 0;
+}
+
+static int blinkm_transfer_hw(struct i2c_client *client, int cmd)
+{
+       /* the protocol is simple but non-standard:
+        * e.g.  cmd 'g' (= 0x67) for "get device address"
+        * - which defaults to 0x09 - would be the sequence:
+        *   a) write 0x67 to the device (byte write)
+        *   b) read the value (0x09) back right after (byte read)
+        *
+        * Watch out for "unfinished" sequences (i.e. not enough reads
+        * or writes after a command. It will make the blinkM misbehave.
+        * Sequence is key here.
+        */
+
+       /* args / return are in private data struct */
+       struct blinkm_data *data = i2c_get_clientdata(client);
+
+       /* We start hardware transfers which are not to be
+        * mixed with other commands. Aquire a lock now. */
+       if (mutex_lock_interruptible(&data->update_lock) < 0)
+               return -EAGAIN;
+
+       /* switch cmd - usually write before reads */
+       switch (cmd) {
+       case BLM_FADE_RAND_RGB:
+       case BLM_GO_RGB:
+       case BLM_FADE_RGB:
+               data->args[0] = data->next_red;
+               data->args[1] = data->next_green;
+               data->args[2] = data->next_blue;
+               blinkm_write(client, cmd, data->args);
+               data->red = data->args[0];
+               data->green = data->args[1];
+               data->blue = data->args[2];
+               break;
+       case BLM_FADE_HSB:
+       case BLM_FADE_RAND_HSB:
+               data->args[0] = data->next_hue;
+               data->args[1] = data->next_saturation;
+               data->args[2] = data->next_brightness;
+               blinkm_write(client, cmd, data->args);
+               data->hue = data->next_hue;
+               data->saturation = data->next_saturation;
+               data->brightness = data->next_brightness;
+               break;
+       case BLM_PLAY_SCRIPT:
+               data->args[0] = data->script_id;
+               data->args[1] = data->script_repeats;
+               data->args[2] = data->script_startline;
+               blinkm_write(client, cmd, data->args);
+               break;
+       case BLM_STOP_SCRIPT:
+               blinkm_write(client, cmd, NULL);
+               break;
+       case BLM_GET_CUR_RGB:
+               data->args[0] = data->red;
+               data->args[1] = data->green;
+               data->args[2] = data->blue;
+               blinkm_write(client, cmd, NULL);
+               blinkm_read(client, cmd, data->args);
+               data->red = data->args[0];
+               data->green = data->args[1];
+               data->blue = data->args[2];
+               break;
+       case BLM_GET_ADDR:
+               data->args[0] = data->i2c_addr;
+               blinkm_write(client, cmd, NULL);
+               blinkm_read(client, cmd, data->args);
+               data->i2c_addr = data->args[0];
+               break;
+       case BLM_SET_TIME_ADJ:
+       case BLM_SET_FADE_SPEED:
+       case BLM_READ_SCRIPT_LINE:
+       case BLM_WRITE_SCRIPT_LINE:
+       case BLM_SET_SCRIPT_LR:
+       case BLM_SET_ADDR:
+       case BLM_GET_FW_VER:
+       case BLM_SET_STARTUP_PARAM:
+               dev_err(&client->dev,
+                               "BlinkM: cmd %d not implemented yet.\n", cmd);
+               break;
+       default:
+               dev_err(&client->dev, "BlinkM: unknown command %d\n", cmd);
+               mutex_unlock(&data->update_lock);
+               return -EINVAL;
+       }                       /* end switch(cmd) */
+
+       /* transfers done, unlock */
+       mutex_unlock(&data->update_lock);
+       return 0;
+}
+
+static void led_work(struct work_struct *work)
+{
+       int ret;
+       struct blinkm_led *led;
+       struct blinkm_data *data ;
+       struct blinkm_work *blm_work = work_to_blmwork(work);
+
+       led = blm_work->blinkm_led;
+       data = i2c_get_clientdata(led->i2c_client);
+       ret = blinkm_transfer_hw(led->i2c_client, BLM_GO_RGB);
+       atomic_dec(&led->active);
+       dev_dbg(&led->i2c_client->dev,
+                       "# DONE # next_red = %d, next_green = %d,"
+                       " next_blue = %d, active = %d\n",
+                       data->next_red, data->next_green,
+                       data->next_blue, atomic_read(&led->active));
+       kfree(blm_work);
+}
+
+static int blinkm_led_common_set(struct led_classdev *led_cdev,
+                                enum led_brightness value, int color)
+{
+       /* led_brightness is 0, 127 or 255 - we just use it here as-is */
+       struct blinkm_led *led = cdev_to_blmled(led_cdev);
+       struct blinkm_data *data = i2c_get_clientdata(led->i2c_client);
+       struct blinkm_work *bl_work;
+
+       switch (color) {
+       case RED:
+               /* bail out if there's no change */
+               if (data->next_red == (u8) value)
+                       return 0;
+               /* we assume a quite fast sequence here ([off]->on->off)
+                * think of network led trigger - we cannot blink that fast, so
+                * in case we already have a off->on->off transition queued up,
+                * we refuse to queue up more.
+                * Revisit: fast-changing brightness. */
+               if (atomic_read(&led->active) > 1)
+                       return 0;
+               data->next_red = (u8) value;
+               break;
+       case GREEN:
+               /* bail out if there's no change */
+               if (data->next_green == (u8) value)
+                       return 0;
+               /* we assume a quite fast sequence here ([off]->on->off)
+                * Revisit: fast-changing brightness. */
+               if (atomic_read(&led->active) > 1)
+                       return 0;
+               data->next_green = (u8) value;
+               break;
+       case BLUE:
+               /* bail out if there's no change */
+               if (data->next_blue == (u8) value)
+                       return 0;
+               /* we assume a quite fast sequence here ([off]->on->off)
+                * Revisit: fast-changing brightness. */
+               if (atomic_read(&led->active) > 1)
+                       return 0;
+               data->next_blue = (u8) value;
+               break;
+
+       default:
+               dev_err(&led->i2c_client->dev, "BlinkM: unknown color.\n");
+               return -EINVAL;
+       }
+
+       bl_work = kzalloc(sizeof(*bl_work), GFP_ATOMIC);
+       if (!bl_work)
+               return -ENOMEM;
+
+       atomic_inc(&led->active);
+       dev_dbg(&led->i2c_client->dev,
+                       "#TO_SCHED# next_red = %d, next_green = %d,"
+                       " next_blue = %d, active = %d\n",
+                       data->next_red, data->next_green,
+                       data->next_blue, atomic_read(&led->active));
+
+       /* a fresh work _item_ for each change */
+       bl_work->blinkm_led = led;
+       INIT_WORK(&bl_work->work, led_work);
+       /* queue work in own queue for easy sync on exit*/
+       schedule_work(&bl_work->work);
+
+       return 0;
+}
+
+static void blinkm_led_red_set(struct led_classdev *led_cdev,
+                              enum led_brightness value)
+{
+       blinkm_led_common_set(led_cdev, value, RED);
+}
+
+static void blinkm_led_green_set(struct led_classdev *led_cdev,
+                                enum led_brightness value)
+{
+       blinkm_led_common_set(led_cdev, value, GREEN);
+}
+
+static void blinkm_led_blue_set(struct led_classdev *led_cdev,
+                               enum led_brightness value)
+{
+       blinkm_led_common_set(led_cdev, value, BLUE);
+}
+
+static void blinkm_init_hw(struct i2c_client *client)
+{
+       int ret;
+       ret = blinkm_transfer_hw(client, BLM_STOP_SCRIPT);
+       ret = blinkm_transfer_hw(client, BLM_GO_RGB);
+}
+
+static int blinkm_test_run(struct i2c_client *client)
+{
+       int ret;
+       struct blinkm_data *data = i2c_get_clientdata(client);
+
+       data->next_red = 0x01;
+       data->next_green = 0x05;
+       data->next_blue = 0x10;
+       ret = blinkm_transfer_hw(client, BLM_GO_RGB);
+       if (ret < 0)
+               return ret;
+       msleep(2000);
+
+       data->next_red = 0x25;
+       data->next_green = 0x10;
+       data->next_blue = 0x31;
+       ret = blinkm_transfer_hw(client, BLM_FADE_RGB);
+       if (ret < 0)
+               return ret;
+       msleep(2000);
+
+       data->next_hue = 0x50;
+       data->next_saturation = 0x10;
+       data->next_brightness = 0x20;
+       ret = blinkm_transfer_hw(client, BLM_FADE_HSB);
+       if (ret < 0)
+               return ret;
+       msleep(2000);
+
+       return 0;
+}
+
+/* Return 0 if detection is successful, -ENODEV otherwise */
+static int blinkm_detect(struct i2c_client *client, struct i2c_board_info *info)
+{
+       struct i2c_adapter *adapter = client->adapter;
+       int ret;
+       int count = 99;
+       u8 tmpargs[7];
+
+       if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA
+                                    | I2C_FUNC_SMBUS_WORD_DATA
+                                    | I2C_FUNC_SMBUS_WRITE_BYTE))
+               return -ENODEV;
+
+       /* Now, we do the remaining detection. Simple for now. */
+       /* We might need more guards to protect other i2c slaves */
+
+       /* make sure the blinkM is balanced (read/writes) */
+       while (count > 0) {
+               ret = blinkm_write(client, BLM_GET_ADDR, NULL);
+               usleep_range(5000, 10000);
+               ret = blinkm_read(client, BLM_GET_ADDR, tmpargs);
+               usleep_range(5000, 10000);
+               if (tmpargs[0] == 0x09)
+                       count = 0;
+               count--;
+       }
+
+       /* Step 1: Read BlinkM address back  -  cmd_char 'a' */
+       ret = blinkm_write(client, BLM_GET_ADDR, NULL);
+       if (ret < 0)
+               return ret;
+       usleep_range(20000, 30000);     /* allow a small delay */
+       ret = blinkm_read(client, BLM_GET_ADDR, tmpargs);
+       if (ret < 0)
+               return ret;
+
+       if (tmpargs[0] != 0x09) {
+               dev_err(&client->dev, "enodev DEV ADDR = 0x%02X\n", tmpargs[0]);
+               return -ENODEV;
+       }
+
+       strlcpy(info->type, "blinkm", I2C_NAME_SIZE);
+       return 0;
+}
+
+static int __devinit blinkm_probe(struct i2c_client *client,
+                       const struct i2c_device_id *id)
+{
+       struct blinkm_data *data;
+       struct blinkm_led *led[3];
+       int err, i;
+       char blinkm_led_name[28];
+
+       data = devm_kzalloc(&client->dev,
+                       sizeof(struct blinkm_data), GFP_KERNEL);
+       if (!data) {
+               err = -ENOMEM;
+               goto exit;
+       }
+
+       data->i2c_addr = 0x09;
+       data->i2c_addr = 0x08;
+       /* i2c addr  - use fake addr of 0x08 initially (real is 0x09) */
+       data->fw_ver = 0xfe;
+       /* firmware version - use fake until we read real value
+        * (currently broken - BlinkM confused!) */
+       data->script_id = 0x01;
+       data->i2c_client = client;
+
+       i2c_set_clientdata(client, data);
+       mutex_init(&data->update_lock);
+
+       /* Register sysfs hooks */
+       err = sysfs_create_group(&client->dev.kobj, &blinkm_group);
+       if (err < 0) {
+               dev_err(&client->dev, "couldn't register sysfs group\n");
+               goto exit;
+       }
+
+       for (i = 0; i < 3; i++) {
+               /* RED = 0, GREEN = 1, BLUE = 2 */
+               led[i] = &data->blinkm_leds[i];
+               led[i]->i2c_client = client;
+               led[i]->id = i;
+               led[i]->led_cdev.max_brightness = 255;
+               led[i]->led_cdev.flags = LED_CORE_SUSPENDRESUME;
+               atomic_set(&led[i]->active, 0);
+               switch (i) {
+               case RED:
+                       snprintf(blinkm_led_name, sizeof(blinkm_led_name),
+                                        "blinkm-%d-%d-red",
+                                        client->adapter->nr,
+                                        client->addr);
+                       led[i]->led_cdev.name = blinkm_led_name;
+                       led[i]->led_cdev.brightness_set = blinkm_led_red_set;
+                       err = led_classdev_register(&client->dev,
+                                                   &led[i]->led_cdev);
+                       if (err < 0) {
+                               dev_err(&client->dev,
+                                       "couldn't register LED %s\n",
+                                       led[i]->led_cdev.name);
+                               goto failred;
+                       }
+                       break;
+               case GREEN:
+                       snprintf(blinkm_led_name, sizeof(blinkm_led_name),
+                                        "blinkm-%d-%d-green",
+                                        client->adapter->nr,
+                                        client->addr);
+                       led[i]->led_cdev.name = blinkm_led_name;
+                       led[i]->led_cdev.brightness_set = blinkm_led_green_set;
+                       err = led_classdev_register(&client->dev,
+                                                   &led[i]->led_cdev);
+                       if (err < 0) {
+                               dev_err(&client->dev,
+                                       "couldn't register LED %s\n",
+                                       led[i]->led_cdev.name);
+                               goto failgreen;
+                       }
+                       break;
+               case BLUE:
+                       snprintf(blinkm_led_name, sizeof(blinkm_led_name),
+                                        "blinkm-%d-%d-blue",
+                                        client->adapter->nr,
+                                        client->addr);
+                       led[i]->led_cdev.name = blinkm_led_name;
+                       led[i]->led_cdev.brightness_set = blinkm_led_blue_set;
+                       err = led_classdev_register(&client->dev,
+                                                   &led[i]->led_cdev);
+                       if (err < 0) {
+                               dev_err(&client->dev,
+                                       "couldn't register LED %s\n",
+                                       led[i]->led_cdev.name);
+                               goto failblue;
+                       }
+                       break;
+               }               /* end switch */
+       }                       /* end for */
+
+       /* Initialize the blinkm */
+       blinkm_init_hw(client);
+
+       return 0;
+
+failblue:
+       led_classdev_unregister(&led[GREEN]->led_cdev);
+
+failgreen:
+       led_classdev_unregister(&led[RED]->led_cdev);
+
+failred:
+       sysfs_remove_group(&client->dev.kobj, &blinkm_group);
+exit:
+       return err;
+}
+
+static int __devexit blinkm_remove(struct i2c_client *client)
+{
+       struct blinkm_data *data = i2c_get_clientdata(client);
+       int ret = 0;
+       int i;
+
+       /* make sure no workqueue entries are pending */
+       for (i = 0; i < 3; i++) {
+               flush_scheduled_work();
+               led_classdev_unregister(&data->blinkm_leds[i].led_cdev);
+       }
+
+       /* reset rgb */
+       data->next_red = 0x00;
+       data->next_green = 0x00;
+       data->next_blue = 0x00;
+       ret = blinkm_transfer_hw(client, BLM_FADE_RGB);
+       if (ret < 0)
+               dev_err(&client->dev, "Failure in blinkm_remove ignored. Continuing.\n");
+
+       /* reset hsb */
+       data->next_hue = 0x00;
+       data->next_saturation = 0x00;
+       data->next_brightness = 0x00;
+       ret = blinkm_transfer_hw(client, BLM_FADE_HSB);
+       if (ret < 0)
+               dev_err(&client->dev, "Failure in blinkm_remove ignored. Continuing.\n");
+
+       /* red fade to off */
+       data->next_red = 0xff;
+       ret = blinkm_transfer_hw(client, BLM_GO_RGB);
+       if (ret < 0)
+               dev_err(&client->dev, "Failure in blinkm_remove ignored. Continuing.\n");
+
+       /* off */
+       data->next_red = 0x00;
+       ret = blinkm_transfer_hw(client, BLM_FADE_RGB);
+       if (ret < 0)
+               dev_err(&client->dev, "Failure in blinkm_remove ignored. Continuing.\n");
+
+       sysfs_remove_group(&client->dev.kobj, &blinkm_group);
+       return 0;
+}
+
+static const struct i2c_device_id blinkm_id[] = {
+       {"blinkm", 0},
+       {}
+};
+
+MODULE_DEVICE_TABLE(i2c, blinkm_id);
+
+  /* This is the driver that will be inserted */
+static struct i2c_driver blinkm_driver = {
+       .class = I2C_CLASS_HWMON,
+       .driver = {
+                  .name = "blinkm",
+                  },
+       .probe = blinkm_probe,
+       .remove = __devexit_p(blinkm_remove),
+       .id_table = blinkm_id,
+       .detect = blinkm_detect,
+       .address_list = normal_i2c,
+};
+
+module_i2c_driver(blinkm_driver);
+
+MODULE_AUTHOR("Jan-Simon Moeller <dl9pf@gmx.de>");
+MODULE_DESCRIPTION("BlinkM RGB LED driver");
+MODULE_LICENSE("GPL");
+
index d9cd73ebd6c44c3e89ff032e2c2155eb036ad972..cc77c9d926159724a8e580e109ef488e138b7cdb 100644 (file)
@@ -108,7 +108,7 @@ static int __devinit da903x_led_probe(struct platform_device *pdev)
                return -EINVAL;
        }
 
-       led = kzalloc(sizeof(struct da903x_led), GFP_KERNEL);
+       led = devm_kzalloc(&pdev->dev, sizeof(struct da903x_led), GFP_KERNEL);
        if (led == NULL) {
                dev_err(&pdev->dev, "failed to alloc memory for LED%d\n", id);
                return -ENOMEM;
@@ -129,15 +129,11 @@ static int __devinit da903x_led_probe(struct platform_device *pdev)
        ret = led_classdev_register(led->master, &led->cdev);
        if (ret) {
                dev_err(&pdev->dev, "failed to register LED %d\n", id);
-               goto err;
+               return ret;
        }
 
        platform_set_drvdata(pdev, led);
        return 0;
-
-err:
-       kfree(led);
-       return ret;
 }
 
 static int __devexit da903x_led_remove(struct platform_device *pdev)
@@ -145,7 +141,6 @@ static int __devexit da903x_led_remove(struct platform_device *pdev)
        struct da903x_led *led = platform_get_drvdata(pdev);
 
        led_classdev_unregister(&led->cdev);
-       kfree(led);
        return 0;
 }
 
index d56c14269ff0c7e9c5e40992f689919024cef7bd..1f9d8e62d37e8d26860303b83ab206260fefd2a7 100644 (file)
@@ -69,7 +69,7 @@ static int dac124s085_probe(struct spi_device *spi)
        struct dac124s085_led   *led;
        int i, ret;
 
-       dac = kzalloc(sizeof(*dac), GFP_KERNEL);
+       dac = devm_kzalloc(&spi->dev, sizeof(*dac), GFP_KERNEL);
        if (!dac)
                return -ENOMEM;
 
@@ -102,7 +102,6 @@ eledcr:
                led_classdev_unregister(&dac->leds[i].ldev);
 
        spi_set_drvdata(spi, NULL);
-       kfree(dac);
        return ret;
 }
 
@@ -117,7 +116,6 @@ static int dac124s085_remove(struct spi_device *spi)
        }
 
        spi_set_drvdata(spi, NULL);
-       kfree(dac);
 
        return 0;
 }
index f4c470a3bc8dc3fa234114f691ad98656a3bef14..c032b2180340e123c3017e893185c34087fcdbde 100644 (file)
@@ -178,7 +178,8 @@ static struct gpio_leds_priv * __devinit gpio_leds_create_of(struct platform_dev
        if (!count)
                return NULL;
 
-       priv = kzalloc(sizeof_gpio_leds_priv(count), GFP_KERNEL);
+       priv = devm_kzalloc(&pdev->dev, sizeof_gpio_leds_priv(count),
+                       GFP_KERNEL);
        if (!priv)
                return NULL;
 
@@ -215,7 +216,6 @@ static struct gpio_leds_priv * __devinit gpio_leds_create_of(struct platform_dev
 err:
        for (count = priv->num_leds - 2; count >= 0; count--)
                delete_gpio_led(&priv->leds[count]);
-       kfree(priv);
        return NULL;
 }
 
@@ -239,8 +239,9 @@ static int __devinit gpio_led_probe(struct platform_device *pdev)
        int i, ret = 0;
 
        if (pdata && pdata->num_leds) {
-               priv = kzalloc(sizeof_gpio_leds_priv(pdata->num_leds),
-                               GFP_KERNEL);
+               priv = devm_kzalloc(&pdev->dev,
+                               sizeof_gpio_leds_priv(pdata->num_leds),
+                                       GFP_KERNEL);
                if (!priv)
                        return -ENOMEM;
 
@@ -253,7 +254,6 @@ static int __devinit gpio_led_probe(struct platform_device *pdev)
                                /* On failure: unwind the led creations */
                                for (i = i - 1; i >= 0; i--)
                                        delete_gpio_led(&priv->leds[i]);
-                               kfree(priv);
                                return ret;
                        }
                }
@@ -277,7 +277,6 @@ static int __devexit gpio_led_remove(struct platform_device *pdev)
                delete_gpio_led(&priv->leds[i]);
 
        dev_set_drvdata(&pdev->dev, NULL);
-       kfree(priv);
 
        return 0;
 }
index 84ba6de8039c8334558175d231bc6c062ff8c579..23637bdb275d0b93d0e5db08f75145295b38af91 100644 (file)
@@ -386,28 +386,24 @@ static int __devinit lm3530_probe(struct i2c_client *client,
 
        if (pdata == NULL) {
                dev_err(&client->dev, "platform data required\n");
-               err = -ENODEV;
-               goto err_out;
+               return -ENODEV;
        }
 
        /* BL mode */
        if (pdata->mode > LM3530_BL_MODE_PWM) {
                dev_err(&client->dev, "Illegal Mode request\n");
-               err = -EINVAL;
-               goto err_out;
+               return -EINVAL;
        }
 
        if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
                dev_err(&client->dev, "I2C_FUNC_I2C not supported\n");
-               err = -EIO;
-               goto err_out;
+               return -EIO;
        }
 
-       drvdata = kzalloc(sizeof(struct lm3530_data), GFP_KERNEL);
-       if (drvdata == NULL) {
-               err = -ENOMEM;
-               goto err_out;
-       }
+       drvdata = devm_kzalloc(&client->dev, sizeof(struct lm3530_data),
+                               GFP_KERNEL);
+       if (drvdata == NULL)
+               return -ENOMEM;
 
        drvdata->mode = pdata->mode;
        drvdata->client = client;
@@ -425,7 +421,7 @@ static int __devinit lm3530_probe(struct i2c_client *client,
                dev_err(&client->dev, "regulator get failed\n");
                err = PTR_ERR(drvdata->regulator);
                drvdata->regulator = NULL;
-               goto err_regulator_get;
+               return err;
        }
 
        if (drvdata->pdata->brt_val) {
@@ -458,9 +454,6 @@ err_create_file:
 err_class_register:
 err_reg_init:
        regulator_put(drvdata->regulator);
-err_regulator_get:
-       kfree(drvdata);
-err_out:
        return err;
 }
 
@@ -474,7 +467,6 @@ static int __devexit lm3530_remove(struct i2c_client *client)
                regulator_disable(drvdata->regulator);
        regulator_put(drvdata->regulator);
        led_classdev_unregister(&drvdata->led_dev);
-       kfree(drvdata);
        return 0;
 }
 
diff --git a/drivers/leds/leds-lm3556.c b/drivers/leds/leds-lm3556.c
new file mode 100644 (file)
index 0000000..3062abd
--- /dev/null
@@ -0,0 +1,512 @@
+/*
+ * Simple driver for Texas Instruments LM3556 LED Flash driver chip (Rev0x03)
+ * Copyright (C) 2012 Texas Instruments
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Please refer Documentation/leds/leds-lm3556.txt file.
+ */
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/leds.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/fs.h>
+#include <linux/regmap.h>
+#include <linux/platform_data/leds-lm3556.h>
+
+#define REG_FILT_TIME                  (0x0)
+#define REG_IVFM_MODE                  (0x1)
+#define REG_NTC                                (0x2)
+#define REG_INDIC_TIME                 (0x3)
+#define REG_INDIC_BLINK                        (0x4)
+#define REG_INDIC_PERIOD               (0x5)
+#define REG_TORCH_TIME                 (0x6)
+#define REG_CONF                       (0x7)
+#define REG_FLASH                      (0x8)
+#define REG_I_CTRL                     (0x9)
+#define REG_ENABLE                     (0xA)
+#define REG_FLAG                       (0xB)
+#define REG_MAX                                (0xB)
+
+#define IVFM_FILTER_TIME_SHIFT         (3)
+#define UVLO_EN_SHIFT                  (7)
+#define HYSTERSIS_SHIFT                        (5)
+#define IVM_D_TH_SHIFT                 (2)
+#define IVFM_ADJ_MODE_SHIFT            (0)
+#define NTC_EVENT_LVL_SHIFT            (5)
+#define NTC_TRIP_TH_SHIFT              (2)
+#define NTC_BIAS_I_LVL_SHIFT           (0)
+#define INDIC_RAMP_UP_TIME_SHIFT       (3)
+#define INDIC_RAMP_DN_TIME_SHIFT       (0)
+#define INDIC_N_BLANK_SHIFT            (4)
+#define INDIC_PULSE_TIME_SHIFT         (0)
+#define INDIC_N_PERIOD_SHIFT           (0)
+#define TORCH_RAMP_UP_TIME_SHIFT       (3)
+#define TORCH_RAMP_DN_TIME_SHIFT       (0)
+#define STROBE_USUAGE_SHIFT            (7)
+#define STROBE_PIN_POLARITY_SHIFT      (6)
+#define TORCH_PIN_POLARITY_SHIFT       (5)
+#define TX_PIN_POLARITY_SHIFT          (4)
+#define TX_EVENT_LVL_SHIFT             (3)
+#define IVFM_EN_SHIFT                  (2)
+#define NTC_MODE_SHIFT                 (1)
+#define INDIC_MODE_SHIFT               (0)
+#define INDUCTOR_I_LIMIT_SHIFT         (6)
+#define FLASH_RAMP_TIME_SHIFT          (3)
+#define FLASH_TOUT_TIME_SHIFT          (0)
+#define TORCH_I_SHIFT                  (4)
+#define FLASH_I_SHIFT                  (0)
+#define NTC_EN_SHIFT                   (7)
+#define TX_PIN_EN_SHIFT                        (6)
+#define STROBE_PIN_EN_SHIFT            (5)
+#define TORCH_PIN_EN_SHIFT             (4)
+#define PRECHG_MODE_EN_SHIFT           (3)
+#define PASS_MODE_ONLY_EN_SHIFT                (2)
+#define MODE_BITS_SHIFT                        (0)
+
+#define IVFM_FILTER_TIME_MASK          (0x3)
+#define UVLO_EN_MASK                   (0x1)
+#define HYSTERSIS_MASK                 (0x3)
+#define IVM_D_TH_MASK                  (0x7)
+#define IVFM_ADJ_MODE_MASK             (0x3)
+#define NTC_EVENT_LVL_MASK             (0x1)
+#define NTC_TRIP_TH_MASK               (0x7)
+#define NTC_BIAS_I_LVL_MASK            (0x3)
+#define INDIC_RAMP_UP_TIME_MASK                (0x7)
+#define INDIC_RAMP_DN_TIME_MASK                (0x7)
+#define INDIC_N_BLANK_MASK             (0x7)
+#define INDIC_PULSE_TIME_MASK          (0x7)
+#define INDIC_N_PERIOD_MASK            (0x7)
+#define TORCH_RAMP_UP_TIME_MASK                (0x7)
+#define TORCH_RAMP_DN_TIME_MASK                (0x7)
+#define STROBE_USUAGE_MASK             (0x1)
+#define STROBE_PIN_POLARITY_MASK       (0x1)
+#define TORCH_PIN_POLARITY_MASK                (0x1)
+#define TX_PIN_POLARITY_MASK           (0x1)
+#define TX_EVENT_LVL_MASK              (0x1)
+#define IVFM_EN_MASK                   (0x1)
+#define NTC_MODE_MASK                  (0x1)
+#define INDIC_MODE_MASK                        (0x1)
+#define INDUCTOR_I_LIMIT_MASK          (0x3)
+#define FLASH_RAMP_TIME_MASK           (0x7)
+#define FLASH_TOUT_TIME_MASK           (0x7)
+#define TORCH_I_MASK                   (0x7)
+#define FLASH_I_MASK                   (0xF)
+#define NTC_EN_MASK                    (0x1)
+#define TX_PIN_EN_MASK                 (0x1)
+#define STROBE_PIN_EN_MASK             (0x1)
+#define TORCH_PIN_EN_MASK              (0x1)
+#define PRECHG_MODE_EN_MASK            (0x1)
+#define PASS_MODE_ONLY_EN_MASK         (0x1)
+#define MODE_BITS_MASK                 (0x13)
+#define EX_PIN_CONTROL_MASK            (0xF1)
+#define EX_PIN_ENABLE_MASK             (0x70)
+
+enum lm3556_indic_pulse_time {
+       PULSE_TIME_0_MS = 0,
+       PULSE_TIME_32_MS,
+       PULSE_TIME_64_MS,
+       PULSE_TIME_92_MS,
+       PULSE_TIME_128_MS,
+       PULSE_TIME_160_MS,
+       PULSE_TIME_196_MS,
+       PULSE_TIME_224_MS,
+       PULSE_TIME_256_MS,
+       PULSE_TIME_288_MS,
+       PULSE_TIME_320_MS,
+       PULSE_TIME_352_MS,
+       PULSE_TIME_384_MS,
+       PULSE_TIME_416_MS,
+       PULSE_TIME_448_MS,
+       PULSE_TIME_480_MS,
+};
+
+enum lm3556_indic_n_blank {
+       INDIC_N_BLANK_0 = 0,
+       INDIC_N_BLANK_1,
+       INDIC_N_BLANK_2,
+       INDIC_N_BLANK_3,
+       INDIC_N_BLANK_4,
+       INDIC_N_BLANK_5,
+       INDIC_N_BLANK_6,
+       INDIC_N_BLANK_7,
+       INDIC_N_BLANK_8,
+       INDIC_N_BLANK_9,
+       INDIC_N_BLANK_10,
+       INDIC_N_BLANK_11,
+       INDIC_N_BLANK_12,
+       INDIC_N_BLANK_13,
+       INDIC_N_BLANK_14,
+       INDIC_N_BLANK_15,
+};
+
+enum lm3556_indic_period {
+       INDIC_PERIOD_0 = 0,
+       INDIC_PERIOD_1,
+       INDIC_PERIOD_2,
+       INDIC_PERIOD_3,
+       INDIC_PERIOD_4,
+       INDIC_PERIOD_5,
+       INDIC_PERIOD_6,
+       INDIC_PERIOD_7,
+};
+
+enum lm3556_mode {
+       MODES_STASNDBY = 0,
+       MODES_INDIC,
+       MODES_TORCH,
+       MODES_FLASH
+};
+
+#define INDIC_PATTERN_SIZE 4
+
+struct indicator {
+       u8 blinking;
+       u8 period_cnt;
+};
+
+struct lm3556_chip_data {
+       struct device *dev;
+
+       struct led_classdev cdev_flash;
+       struct led_classdev cdev_torch;
+       struct led_classdev cdev_indicator;
+
+       struct lm3556_platform_data *pdata;
+       struct regmap *regmap;
+       struct mutex lock;
+
+       unsigned int last_flag;
+};
+
+/* indicator pattern */
+static struct indicator indicator_pattern[INDIC_PATTERN_SIZE] = {
+       [0] = {(INDIC_N_BLANK_1 << INDIC_N_BLANK_SHIFT)
+              | PULSE_TIME_32_MS, INDIC_PERIOD_1},
+       [1] = {(INDIC_N_BLANK_15 << INDIC_N_BLANK_SHIFT)
+              | PULSE_TIME_32_MS, INDIC_PERIOD_2},
+       [2] = {(INDIC_N_BLANK_10 << INDIC_N_BLANK_SHIFT)
+              | PULSE_TIME_32_MS, INDIC_PERIOD_4},
+       [3] = {(INDIC_N_BLANK_5 << INDIC_N_BLANK_SHIFT)
+              | PULSE_TIME_32_MS, INDIC_PERIOD_7},
+};
+
+/* chip initialize */
+static int __devinit lm3556_chip_init(struct lm3556_chip_data *chip)
+{
+       unsigned int reg_val;
+       int ret;
+       struct lm3556_platform_data *pdata = chip->pdata;
+
+       /* set config register */
+       ret = regmap_read(chip->regmap, REG_CONF, &reg_val);
+       if (ret < 0) {
+               dev_err(chip->dev, "Failed to read REG_CONF Register\n");
+               goto out;
+       }
+
+       reg_val &= (~EX_PIN_CONTROL_MASK);
+       reg_val |= ((pdata->torch_pin_polarity & 0x01)
+                   << TORCH_PIN_POLARITY_SHIFT);
+       reg_val |= ((pdata->strobe_usuage & 0x01) << STROBE_USUAGE_SHIFT);
+       reg_val |= ((pdata->strobe_pin_polarity & 0x01)
+                   << STROBE_PIN_POLARITY_SHIFT);
+       reg_val |= ((pdata->tx_pin_polarity & 0x01) << TX_PIN_POLARITY_SHIFT);
+       reg_val |= ((pdata->indicator_mode & 0x01) << INDIC_MODE_SHIFT);
+
+       ret = regmap_write(chip->regmap, REG_CONF, reg_val);
+       if (ret < 0) {
+               dev_err(chip->dev, "Failed to write REG_CONF Regisgter\n");
+               goto out;
+       }
+
+       /* set enable register */
+       ret = regmap_read(chip->regmap, REG_ENABLE, &reg_val);
+       if (ret < 0) {
+               dev_err(chip->dev, "Failed to read REG_ENABLE Register\n");
+               goto out;
+       }
+
+       reg_val &= (~EX_PIN_ENABLE_MASK);
+       reg_val |= ((pdata->torch_pin_en & 0x01) << TORCH_PIN_EN_SHIFT);
+       reg_val |= ((pdata->strobe_pin_en & 0x01) << STROBE_PIN_EN_SHIFT);
+       reg_val |= ((pdata->tx_pin_en & 0x01) << TX_PIN_EN_SHIFT);
+
+       ret = regmap_write(chip->regmap, REG_ENABLE, reg_val);
+       if (ret < 0) {
+               dev_err(chip->dev, "Failed to write REG_ENABLE Regisgter\n");
+               goto out;
+       }
+
+out:
+       return ret;
+}
+
+/* chip control */
+static int lm3556_control(struct lm3556_chip_data *chip,
+                         u8 brightness, enum lm3556_mode opmode)
+{
+       int ret;
+       struct lm3556_platform_data *pdata = chip->pdata;
+
+       ret = regmap_read(chip->regmap, REG_FLAG, &chip->last_flag);
+       if (ret < 0) {
+               dev_err(chip->dev, "Failed to read REG_FLAG Register\n");
+               goto out;
+       }
+
+       if (chip->last_flag)
+               dev_info(chip->dev, "Last FLAG is 0x%x\n", chip->last_flag);
+
+       /* brightness 0 means off-state */
+       if (!brightness)
+               opmode = MODES_STASNDBY;
+
+       switch (opmode) {
+       case MODES_TORCH:
+               ret = regmap_update_bits(chip->regmap, REG_I_CTRL,
+                                        TORCH_I_MASK << TORCH_I_SHIFT,
+                                        (brightness - 1) << TORCH_I_SHIFT);
+
+               if (pdata->torch_pin_en)
+                       opmode |= (TORCH_PIN_EN_MASK << TORCH_PIN_EN_SHIFT);
+               break;
+
+       case MODES_FLASH:
+               ret = regmap_update_bits(chip->regmap, REG_I_CTRL,
+                                        FLASH_I_MASK << FLASH_I_SHIFT,
+                                        (brightness - 1) << FLASH_I_SHIFT);
+               break;
+
+       case MODES_INDIC:
+               ret = regmap_update_bits(chip->regmap, REG_I_CTRL,
+                                        TORCH_I_MASK << TORCH_I_SHIFT,
+                                        (brightness - 1) << TORCH_I_SHIFT);
+               break;
+
+       case MODES_STASNDBY:
+               if (pdata->torch_pin_en)
+                       opmode |= (TORCH_PIN_EN_MASK << TORCH_PIN_EN_SHIFT);
+               break;
+
+       default:
+               return ret;
+       }
+       if (ret < 0) {
+               dev_err(chip->dev, "Failed to write REG_I_CTRL Register\n");
+               goto out;
+       }
+       ret = regmap_update_bits(chip->regmap, REG_ENABLE,
+                                MODE_BITS_MASK << MODE_BITS_SHIFT,
+                                opmode << MODE_BITS_SHIFT);
+
+out:
+       return ret;
+}
+
+/* torch */
+static void lm3556_torch_brightness_set(struct led_classdev *cdev,
+                                       enum led_brightness brightness)
+{
+       struct lm3556_chip_data *chip =
+           container_of(cdev, struct lm3556_chip_data, cdev_torch);
+
+       mutex_lock(&chip->lock);
+       lm3556_control(chip, brightness, MODES_TORCH);
+       mutex_unlock(&chip->lock);
+}
+
+/* flash */
+static void lm3556_strobe_brightness_set(struct led_classdev *cdev,
+                                        enum led_brightness brightness)
+{
+       struct lm3556_chip_data *chip =
+           container_of(cdev, struct lm3556_chip_data, cdev_flash);
+
+       mutex_lock(&chip->lock);
+       lm3556_control(chip, brightness, MODES_FLASH);
+       mutex_unlock(&chip->lock);
+}
+
+/* indicator */
+static void lm3556_indicator_brightness_set(struct led_classdev *cdev,
+                                           enum led_brightness brightness)
+{
+       struct lm3556_chip_data *chip =
+           container_of(cdev, struct lm3556_chip_data, cdev_indicator);
+
+       mutex_lock(&chip->lock);
+       lm3556_control(chip, brightness, MODES_INDIC);
+       mutex_unlock(&chip->lock);
+}
+
+/* indicator pattern */
+static ssize_t lm3556_indicator_pattern_store(struct device *dev,
+                                             struct device_attribute *devAttr,
+                                             const char *buf, size_t size)
+{
+       ssize_t ret;
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct lm3556_chip_data *chip =
+           container_of(led_cdev, struct lm3556_chip_data, cdev_indicator);
+       unsigned int state;
+
+       ret = kstrtouint(buf, 10, &state);
+       if (ret)
+               goto out;
+       if (state > INDIC_PATTERN_SIZE - 1)
+               state = INDIC_PATTERN_SIZE - 1;
+
+       ret = regmap_write(chip->regmap, REG_INDIC_BLINK,
+                          indicator_pattern[state].blinking);
+       if (ret < 0) {
+               dev_err(chip->dev, "Failed to write REG_ENABLE Regisgter\n");
+               goto out;
+       }
+
+       ret = regmap_write(chip->regmap, REG_INDIC_PERIOD,
+                          indicator_pattern[state].period_cnt);
+       if (ret < 0) {
+               dev_err(chip->dev, "Failed to write REG_ENABLE Regisgter\n");
+               goto out;
+       }
+
+       return size;
+out:
+       dev_err(chip->dev, "Indicator pattern doesn't saved\n");
+       return size;
+}
+
+static DEVICE_ATTR(pattern, 0666, NULL, lm3556_indicator_pattern_store);
+
+static const struct regmap_config lm3556_regmap = {
+       .reg_bits = 8,
+       .val_bits = 8,
+       .max_register = REG_MAX,
+};
+
+/* module initialize */
+static int __devinit lm3556_probe(struct i2c_client *client,
+                                 const struct i2c_device_id *id)
+{
+       struct lm3556_platform_data *pdata = client->dev.platform_data;
+       struct lm3556_chip_data *chip;
+
+       int err;
+
+       if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+               dev_err(&client->dev, "i2c functionality check fail.\n");
+               return -EOPNOTSUPP;
+       }
+
+       if (pdata == NULL) {
+               dev_err(&client->dev, "Needs Platform Data.\n");
+               return -ENODATA;
+       }
+
+       chip =
+           devm_kzalloc(&client->dev, sizeof(struct lm3556_chip_data),
+                        GFP_KERNEL);
+       if (!chip)
+               return -ENOMEM;
+
+       chip->dev = &client->dev;
+       chip->pdata = pdata;
+
+       chip->regmap = devm_regmap_init_i2c(client, &lm3556_regmap);
+       if (IS_ERR(chip->regmap)) {
+               err = PTR_ERR(chip->regmap);
+               dev_err(&client->dev, "Failed to allocate register map: %d\n",
+                       err);
+               return err;
+       }
+
+       mutex_init(&chip->lock);
+       i2c_set_clientdata(client, chip);
+
+       err = lm3556_chip_init(chip);
+       if (err < 0)
+               goto err_out;
+
+       /* flash */
+       chip->cdev_flash.name = "flash";
+       chip->cdev_flash.max_brightness = 16;
+       chip->cdev_flash.brightness_set = lm3556_strobe_brightness_set;
+       err = led_classdev_register((struct device *)
+                                   &client->dev, &chip->cdev_flash);
+       if (err < 0)
+               goto err_out;
+       /* torch */
+       chip->cdev_torch.name = "torch";
+       chip->cdev_torch.max_brightness = 8;
+       chip->cdev_torch.brightness_set = lm3556_torch_brightness_set;
+       err = led_classdev_register((struct device *)
+                                   &client->dev, &chip->cdev_torch);
+       if (err < 0)
+               goto err_create_torch_file;
+       /* indicator */
+       chip->cdev_indicator.name = "indicator";
+       chip->cdev_indicator.max_brightness = 8;
+       chip->cdev_indicator.brightness_set = lm3556_indicator_brightness_set;
+       err = led_classdev_register((struct device *)
+                                   &client->dev, &chip->cdev_indicator);
+       if (err < 0)
+               goto err_create_indicator_file;
+
+       err = device_create_file(chip->cdev_indicator.dev, &dev_attr_pattern);
+       if (err < 0)
+               goto err_create_pattern_file;
+
+       dev_info(&client->dev, "LM3556 is initialized\n");
+       return 0;
+
+err_create_pattern_file:
+       led_classdev_unregister(&chip->cdev_indicator);
+err_create_indicator_file:
+       led_classdev_unregister(&chip->cdev_torch);
+err_create_torch_file:
+       led_classdev_unregister(&chip->cdev_flash);
+err_out:
+       return err;
+}
+
+static int __devexit lm3556_remove(struct i2c_client *client)
+{
+       struct lm3556_chip_data *chip = i2c_get_clientdata(client);
+
+       device_remove_file(chip->cdev_indicator.dev, &dev_attr_pattern);
+       led_classdev_unregister(&chip->cdev_indicator);
+       led_classdev_unregister(&chip->cdev_torch);
+       led_classdev_unregister(&chip->cdev_flash);
+       regmap_write(chip->regmap, REG_ENABLE, 0);
+       return 0;
+}
+
+static const struct i2c_device_id lm3556_id[] = {
+       {LM3556_NAME, 0},
+       {}
+};
+
+MODULE_DEVICE_TABLE(i2c, lm3556_id);
+
+static struct i2c_driver lm3556_i2c_driver = {
+       .driver = {
+                  .name = LM3556_NAME,
+                  .owner = THIS_MODULE,
+                  .pm = NULL,
+                  },
+       .probe = lm3556_probe,
+       .remove = __devexit_p(lm3556_remove),
+       .id_table = lm3556_id,
+};
+
+module_i2c_driver(lm3556_i2c_driver);
+
+MODULE_DESCRIPTION("Texas Instruments Flash Lighting driver for LM3556");
+MODULE_AUTHOR("Daniel Jeong <daniel.jeong@ti.com>");
+MODULE_AUTHOR("G.Shark Jeong <gshark.jeong@gmail.com>");
+MODULE_LICENSE("GPL v2");
index b8f9f0a5d4318d1291e377fe1fd4599d3bc15e13..c298f7d9f5357eb27fe302c2a96d906b2ab38b66 100644 (file)
@@ -393,7 +393,8 @@ static int __devinit lp3944_probe(struct i2c_client *client,
                return -ENODEV;
        }
 
-       data = kzalloc(sizeof(struct lp3944_data), GFP_KERNEL);
+       data = devm_kzalloc(&client->dev, sizeof(struct lp3944_data),
+                       GFP_KERNEL);
        if (!data)
                return -ENOMEM;
 
@@ -403,10 +404,8 @@ static int __devinit lp3944_probe(struct i2c_client *client,
        mutex_init(&data->lock);
 
        err = lp3944_configure(client, data, lp3944_pdata);
-       if (err < 0) {
-               kfree(data);
+       if (err < 0)
                return err;
-       }
 
        dev_info(&client->dev, "lp3944 enabled\n");
        return 0;
@@ -431,8 +430,6 @@ static int __devexit lp3944_remove(struct i2c_client *client)
                        break;
                }
 
-       kfree(data);
-
        return 0;
 }
 
index 23815624f35ef59ea11d6cfe167e173257c548c2..2064aefedc0707fd7a6d78e85ded09dd441113a0 100644 (file)
@@ -744,7 +744,7 @@ static int __devinit lp5521_probe(struct i2c_client *client,
        int ret, i, led;
        u8 buf;
 
-       chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+       chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
        if (!chip)
                return -ENOMEM;
 
@@ -755,8 +755,7 @@ static int __devinit lp5521_probe(struct i2c_client *client,
 
        if (!pdata) {
                dev_err(&client->dev, "no platform data\n");
-               ret = -EINVAL;
-               goto fail1;
+               return -EINVAL;
        }
 
        mutex_init(&chip->lock);
@@ -766,7 +765,7 @@ static int __devinit lp5521_probe(struct i2c_client *client,
        if (pdata->setup_resources) {
                ret = pdata->setup_resources();
                if (ret < 0)
-                       goto fail1;
+                       return ret;
        }
 
        if (pdata->enable) {
@@ -807,7 +806,7 @@ static int __devinit lp5521_probe(struct i2c_client *client,
        ret = lp5521_configure(client);
        if (ret < 0) {
                dev_err(&client->dev, "error configuring chip\n");
-               goto fail2;
+               goto fail1;
        }
 
        /* Initialize leds */
@@ -822,7 +821,7 @@ static int __devinit lp5521_probe(struct i2c_client *client,
                ret = lp5521_init_led(&chip->leds[led], client, i, pdata);
                if (ret) {
                        dev_err(&client->dev, "error initializing leds\n");
-                       goto fail3;
+                       goto fail2;
                }
                chip->num_leds++;
 
@@ -840,21 +839,19 @@ static int __devinit lp5521_probe(struct i2c_client *client,
        ret = lp5521_register_sysfs(client);
        if (ret) {
                dev_err(&client->dev, "registering sysfs failed\n");
-               goto fail3;
+               goto fail2;
        }
        return ret;
-fail3:
+fail2:
        for (i = 0; i < chip->num_leds; i++) {
                led_classdev_unregister(&chip->leds[i].cdev);
                cancel_work_sync(&chip->leds[i].brightness_work);
        }
-fail2:
+fail1:
        if (pdata->enable)
                pdata->enable(0);
        if (pdata->release_resources)
                pdata->release_resources();
-fail1:
-       kfree(chip);
        return ret;
 }
 
@@ -875,7 +872,6 @@ static int __devexit lp5521_remove(struct i2c_client *client)
                chip->pdata->enable(0);
        if (chip->pdata->release_resources)
                chip->pdata->release_resources();
-       kfree(chip);
        return 0;
 }
 
index 857a3e15f2dde8eb8ebc3b90bf30a0c4388b8311..fbc12acada95a6e5507bcfada4b5bb483b67251a 100644 (file)
@@ -877,7 +877,7 @@ static int __devinit lp5523_probe(struct i2c_client *client,
        struct lp5523_platform_data     *pdata;
        int ret, i, led;
 
-       chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+       chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
        if (!chip)
                return -ENOMEM;
 
@@ -888,8 +888,7 @@ static int __devinit lp5523_probe(struct i2c_client *client,
 
        if (!pdata) {
                dev_err(&client->dev, "no platform data\n");
-               ret = -EINVAL;
-               goto fail1;
+               return -EINVAL;
        }
 
        mutex_init(&chip->lock);
@@ -899,7 +898,7 @@ static int __devinit lp5523_probe(struct i2c_client *client,
        if (pdata->setup_resources) {
                ret = pdata->setup_resources();
                if (ret < 0)
-                       goto fail1;
+                       return ret;
        }
 
        if (pdata->enable) {
@@ -916,7 +915,7 @@ static int __devinit lp5523_probe(struct i2c_client *client,
                                     */
        ret = lp5523_detect(client);
        if (ret)
-               goto fail2;
+               goto fail1;
 
        dev_info(&client->dev, "LP5523 Programmable led chip found\n");
 
@@ -925,13 +924,13 @@ static int __devinit lp5523_probe(struct i2c_client *client,
                ret = lp5523_init_engine(&chip->engines[i], i + 1);
                if (ret) {
                        dev_err(&client->dev, "error initializing engine\n");
-                       goto fail2;
+                       goto fail1;
                }
        }
        ret = lp5523_configure(client);
        if (ret < 0) {
                dev_err(&client->dev, "error configuring chip\n");
-               goto fail2;
+               goto fail1;
        }
 
        /* Initialize leds */
@@ -943,10 +942,13 @@ static int __devinit lp5523_probe(struct i2c_client *client,
                if (pdata->led_config[i].led_current == 0)
                        continue;
 
+               INIT_WORK(&chip->leds[led].brightness_work,
+                       lp5523_led_brightness_work);
+
                ret = lp5523_init_led(&chip->leds[led], &client->dev, i, pdata);
                if (ret) {
                        dev_err(&client->dev, "error initializing leds\n");
-                       goto fail3;
+                       goto fail2;
                }
                chip->num_leds++;
 
@@ -956,30 +958,25 @@ static int __devinit lp5523_probe(struct i2c_client *client,
                          LP5523_REG_LED_CURRENT_BASE + chip->leds[led].chan_nr,
                          chip->leds[led].led_current);
 
-               INIT_WORK(&(chip->leds[led].brightness_work),
-                       lp5523_led_brightness_work);
-
                led++;
        }
 
        ret = lp5523_register_sysfs(client);
        if (ret) {
                dev_err(&client->dev, "registering sysfs failed\n");
-               goto fail3;
+               goto fail2;
        }
        return ret;
-fail3:
+fail2:
        for (i = 0; i < chip->num_leds; i++) {
                led_classdev_unregister(&chip->leds[i].cdev);
                cancel_work_sync(&chip->leds[i].brightness_work);
        }
-fail2:
+fail1:
        if (pdata->enable)
                pdata->enable(0);
        if (pdata->release_resources)
                pdata->release_resources();
-fail1:
-       kfree(chip);
        return ret;
 }
 
@@ -999,7 +996,6 @@ static int lp5523_remove(struct i2c_client *client)
                chip->pdata->enable(0);
        if (chip->pdata->release_resources)
                chip->pdata->release_resources();
-       kfree(chip);
        return 0;
 }
 
diff --git a/drivers/leds/leds-lp8788.c b/drivers/leds/leds-lp8788.c
new file mode 100644 (file)
index 0000000..53bd136
--- /dev/null
@@ -0,0 +1,193 @@
+/*
+ * TI LP8788 MFD - keyled driver
+ *
+ * Copyright 2012 Texas Instruments
+ *
+ * Author: Milo(Woogyom) Kim <milo.kim@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/leds.h>
+#include <linux/mutex.h>
+#include <linux/mfd/lp8788.h>
+#include <linux/mfd/lp8788-isink.h>
+
+#define MAX_BRIGHTNESS                 LP8788_ISINK_MAX_PWM
+#define DEFAULT_LED_NAME               "keyboard-backlight"
+
+struct lp8788_led {
+       struct lp8788 *lp;
+       struct mutex lock;
+       struct work_struct work;
+       struct led_classdev led_dev;
+       enum lp8788_isink_number isink_num;
+       enum led_brightness brightness;
+       int on;
+};
+
+struct lp8788_led_config {
+       enum lp8788_isink_scale scale;
+       enum lp8788_isink_number num;
+       int iout;
+};
+
+static struct lp8788_led_config default_led_config = {
+       .scale = LP8788_ISINK_SCALE_100mA,
+       .num   = LP8788_ISINK_3,
+       .iout  = 0,
+};
+
+static int lp8788_led_init_device(struct lp8788_led *led,
+                               struct lp8788_led_platform_data *pdata)
+{
+       struct lp8788_led_config *cfg = &default_led_config;
+       u8 addr, mask, val;
+       int ret;
+
+       if (pdata) {
+               cfg->scale = pdata->scale;
+               cfg->num = pdata->num;
+               cfg->iout = pdata->iout_code;
+       }
+
+       led->isink_num = cfg->num;
+
+       /* scale configuration */
+       addr = LP8788_ISINK_CTRL;
+       mask = 1 << (cfg->num + LP8788_ISINK_SCALE_OFFSET);
+       val = cfg->scale << cfg->num;
+       ret = lp8788_update_bits(led->lp, addr, mask, val);
+       if (ret)
+               return ret;
+
+       /* current configuration */
+       addr = lp8788_iout_addr[cfg->num];
+       mask = lp8788_iout_mask[cfg->num];
+       val = cfg->iout;
+
+       return lp8788_update_bits(led->lp, addr, mask, val);
+}
+
+static void lp8788_led_enable(struct lp8788_led *led,
+                       enum lp8788_isink_number num, int on)
+{
+       u8 mask = 1 << num;
+       u8 val = on << num;
+
+       if (lp8788_update_bits(led->lp, LP8788_ISINK_CTRL, mask, val))
+               return;
+
+       led->on = on;
+}
+
+static void lp8788_led_work(struct work_struct *work)
+{
+       struct lp8788_led *led = container_of(work, struct lp8788_led, work);
+       enum lp8788_isink_number num = led->isink_num;
+       int enable;
+       u8 val = led->brightness;
+
+       mutex_lock(&led->lock);
+
+       switch (num) {
+       case LP8788_ISINK_1:
+       case LP8788_ISINK_2:
+       case LP8788_ISINK_3:
+               lp8788_write_byte(led->lp, lp8788_pwm_addr[num], val);
+               break;
+       default:
+               mutex_unlock(&led->lock);
+               return;
+       }
+
+       enable = (val > 0) ? 1 : 0;
+       if (enable != led->on)
+               lp8788_led_enable(led, num, enable);
+
+       mutex_unlock(&led->lock);
+}
+
+static void lp8788_brightness_set(struct led_classdev *led_cdev,
+                               enum led_brightness brt_val)
+{
+       struct lp8788_led *led =
+                       container_of(led_cdev, struct lp8788_led, led_dev);
+
+       led->brightness = brt_val;
+       schedule_work(&led->work);
+}
+
+static __devinit int lp8788_led_probe(struct platform_device *pdev)
+{
+       struct lp8788 *lp = dev_get_drvdata(pdev->dev.parent);
+       struct lp8788_led_platform_data *led_pdata;
+       struct lp8788_led *led;
+       int ret;
+
+       led = devm_kzalloc(lp->dev, sizeof(struct lp8788_led), GFP_KERNEL);
+       if (!led)
+               return -ENOMEM;
+
+       led->lp = lp;
+       led->led_dev.max_brightness = MAX_BRIGHTNESS;
+       led->led_dev.brightness_set = lp8788_brightness_set;
+
+       led_pdata = lp->pdata ? lp->pdata->led_pdata : NULL;
+
+       if (!led_pdata || !led_pdata->name)
+               led->led_dev.name = DEFAULT_LED_NAME;
+       else
+               led->led_dev.name = led_pdata->name;
+
+       mutex_init(&led->lock);
+       INIT_WORK(&led->work, lp8788_led_work);
+
+       platform_set_drvdata(pdev, led);
+
+       ret = lp8788_led_init_device(led, led_pdata);
+       if (ret) {
+               dev_err(lp->dev, "led init device err: %d\n", ret);
+               return ret;
+       }
+
+       ret = led_classdev_register(lp->dev, &led->led_dev);
+       if (ret) {
+               dev_err(lp->dev, "led register err: %d\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int __devexit lp8788_led_remove(struct platform_device *pdev)
+{
+       struct lp8788_led *led = platform_get_drvdata(pdev);
+
+       led_classdev_unregister(&led->led_dev);
+       flush_work_sync(&led->work);
+
+       return 0;
+}
+
+static struct platform_driver lp8788_led_driver = {
+       .probe = lp8788_led_probe,
+       .remove = __devexit_p(lp8788_led_remove),
+       .driver = {
+               .name = LP8788_DEV_KEYLED,
+               .owner = THIS_MODULE,
+       },
+};
+module_platform_driver(lp8788_led_driver);
+
+MODULE_DESCRIPTION("Texas Instruments LP8788 Keyboard LED Driver");
+MODULE_AUTHOR("Milo Kim");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:lp8788-keyled");
index e311a96c4469759ae72eadabfc3f86c8c7795760..09a732217f6d96fd3b554d85b6127201da8db6b5 100644 (file)
@@ -149,8 +149,9 @@ static int __devinit lt3593_led_probe(struct platform_device *pdev)
        if (!pdata)
                return -EBUSY;
 
-       leds_data = kzalloc(sizeof(struct lt3593_led_data) * pdata->num_leds,
-                               GFP_KERNEL);
+       leds_data = devm_kzalloc(&pdev->dev,
+                       sizeof(struct lt3593_led_data) * pdata->num_leds,
+                       GFP_KERNEL);
        if (!leds_data)
                return -ENOMEM;
 
@@ -169,8 +170,6 @@ err:
        for (i = i - 1; i >= 0; i--)
                delete_lt3593_led(&leds_data[i]);
 
-       kfree(leds_data);
-
        return ret;
 }
 
@@ -185,8 +184,6 @@ static int __devexit lt3593_led_remove(struct platform_device *pdev)
        for (i = 0; i < pdata->num_leds; i++)
                delete_lt3593_led(&leds_data[i]);
 
-       kfree(leds_data);
-
        return 0;
 }
 
index f4c0e37fad1e06e612e99936d761985b026f581a..569e36de37dfe4963ff36993c0bab05b99402270 100644 (file)
@@ -49,71 +49,37 @@ struct max8997_led {
        struct mutex mutex;
 };
 
-static void max8997_led_clear_mode(struct max8997_led *led,
-                       enum max8997_led_mode mode)
-{
-       struct i2c_client *client = led->iodev->i2c;
-       u8 val = 0, mask = 0;
-       int ret;
-
-       switch (mode) {
-       case MAX8997_FLASH_MODE:
-               mask = led->id ?
-                     MAX8997_LED1_FLASH_MASK : MAX8997_LED0_FLASH_MASK;
-               break;
-       case MAX8997_MOVIE_MODE:
-               mask = led->id ?
-                     MAX8997_LED1_MOVIE_MASK : MAX8997_LED0_MOVIE_MASK;
-               break;
-       case MAX8997_FLASH_PIN_CONTROL_MODE:
-               mask = led->id ?
-                     MAX8997_LED1_FLASH_PIN_MASK : MAX8997_LED0_FLASH_PIN_MASK;
-               break;
-       case MAX8997_MOVIE_PIN_CONTROL_MODE:
-               mask = led->id ?
-                     MAX8997_LED1_MOVIE_PIN_MASK : MAX8997_LED0_MOVIE_PIN_MASK;
-               break;
-       default:
-               break;
-       }
-
-       if (mask) {
-               ret = max8997_update_reg(client,
-                               MAX8997_REG_LEN_CNTL, val, mask);
-               if (ret)
-                       dev_err(led->iodev->dev,
-                               "failed to update register(%d)\n", ret);
-       }
-}
-
 static void max8997_led_set_mode(struct max8997_led *led,
                        enum max8997_led_mode mode)
 {
        int ret;
        struct i2c_client *client = led->iodev->i2c;
-       u8 mask = 0;
-
-       /* First, clear the previous mode */
-       max8997_led_clear_mode(led, led->led_mode);
+       u8 mask = 0, val;
 
        switch (mode) {
        case MAX8997_FLASH_MODE:
-               mask = led->id ?
+               mask = MAX8997_LED1_FLASH_MASK | MAX8997_LED0_FLASH_MASK;
+               val = led->id ?
                      MAX8997_LED1_FLASH_MASK : MAX8997_LED0_FLASH_MASK;
                led->cdev.max_brightness = MAX8997_LED_FLASH_MAX_BRIGHTNESS;
                break;
        case MAX8997_MOVIE_MODE:
-               mask = led->id ?
+               mask = MAX8997_LED1_MOVIE_MASK | MAX8997_LED0_MOVIE_MASK;
+               val = led->id ?
                      MAX8997_LED1_MOVIE_MASK : MAX8997_LED0_MOVIE_MASK;
                led->cdev.max_brightness = MAX8997_LED_MOVIE_MAX_BRIGHTNESS;
                break;
        case MAX8997_FLASH_PIN_CONTROL_MODE:
-               mask = led->id ?
+               mask = MAX8997_LED1_FLASH_PIN_MASK |
+                      MAX8997_LED0_FLASH_PIN_MASK;
+               val = led->id ?
                      MAX8997_LED1_FLASH_PIN_MASK : MAX8997_LED0_FLASH_PIN_MASK;
                led->cdev.max_brightness = MAX8997_LED_FLASH_MAX_BRIGHTNESS;
                break;
        case MAX8997_MOVIE_PIN_CONTROL_MODE:
-               mask = led->id ?
+               mask = MAX8997_LED1_MOVIE_PIN_MASK |
+                      MAX8997_LED0_MOVIE_PIN_MASK;
+               val = led->id ?
                      MAX8997_LED1_MOVIE_PIN_MASK : MAX8997_LED0_MOVIE_PIN_MASK;
                led->cdev.max_brightness = MAX8997_LED_MOVIE_MAX_BRIGHTNESS;
                break;
@@ -123,8 +89,8 @@ static void max8997_led_set_mode(struct max8997_led *led,
        }
 
        if (mask) {
-               ret = max8997_update_reg(client,
-                               MAX8997_REG_LEN_CNTL, mask, mask);
+               ret = max8997_update_reg(client, MAX8997_REG_LEN_CNTL, val,
+                                        mask);
                if (ret)
                        dev_err(led->iodev->dev,
                                "failed to update register(%d)\n", ret);
@@ -276,11 +242,9 @@ static int __devinit max8997_led_probe(struct platform_device *pdev)
                return -ENODEV;
        }
 
-       led = kzalloc(sizeof(*led), GFP_KERNEL);
-       if (led == NULL) {
-               ret = -ENOMEM;
-               goto err_mem;
-       }
+       led = devm_kzalloc(&pdev->dev, sizeof(*led), GFP_KERNEL);
+       if (led == NULL)
+               return -ENOMEM;
 
        led->id = pdev->id;
        snprintf(name, sizeof(name), "max8997-led%d", pdev->id);
@@ -315,23 +279,17 @@ static int __devinit max8997_led_probe(struct platform_device *pdev)
 
        ret = led_classdev_register(&pdev->dev, &led->cdev);
        if (ret < 0)
-               goto err_led;
+               return ret;
 
        ret = device_create_file(led->cdev.dev, &dev_attr_mode);
        if (ret != 0) {
                dev_err(&pdev->dev,
                        "failed to create file: %d\n", ret);
-               goto err_file;
+               led_classdev_unregister(&led->cdev);
+               return ret;
        }
 
        return 0;
-
-err_file:
-       led_classdev_unregister(&led->cdev);
-err_led:
-       kfree(led);
-err_mem:
-       return ret;
 }
 
 static int __devexit max8997_led_remove(struct platform_device *pdev)
@@ -340,7 +298,6 @@ static int __devexit max8997_led_remove(struct platform_device *pdev)
 
        device_remove_file(led->cdev.dev, &dev_attr_mode);
        led_classdev_unregister(&led->cdev);
-       kfree(led);
 
        return 0;
 }
@@ -354,17 +311,7 @@ static struct platform_driver max8997_led_driver = {
        .remove = __devexit_p(max8997_led_remove),
 };
 
-static int __init max8997_led_init(void)
-{
-       return platform_driver_register(&max8997_led_driver);
-}
-module_init(max8997_led_init);
-
-static void __exit max8997_led_exit(void)
-{
-       platform_driver_unregister(&max8997_led_driver);
-}
-module_exit(max8997_led_exit);
+module_platform_driver(max8997_led_driver);
 
 MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>");
 MODULE_DESCRIPTION("MAX8997 LED driver");
index 4cc6a2e3df3487e1ce971b407bd56095d84b9b54..2a5d43400677c4947cfebd054516c226693f9d32 100644 (file)
@@ -280,7 +280,8 @@ static int __devinit mc13783_led_probe(struct platform_device *pdev)
                return -EINVAL;
        }
 
-       led = kcalloc(pdata->num_leds, sizeof(*led), GFP_KERNEL);
+       led = devm_kzalloc(&pdev->dev, pdata->num_leds * sizeof(*led),
+                               GFP_KERNEL);
        if (led == NULL) {
                dev_err(&pdev->dev, "failed to alloc memory\n");
                return -ENOMEM;
@@ -289,7 +290,7 @@ static int __devinit mc13783_led_probe(struct platform_device *pdev)
        ret = mc13783_leds_prepare(pdev);
        if (ret) {
                dev_err(&pdev->dev, "unable to init led driver\n");
-               goto err_free;
+               return ret;
        }
 
        for (i = 0; i < pdata->num_leds; i++) {
@@ -344,8 +345,6 @@ err_register:
                cancel_work_sync(&led[i].work);
        }
 
-err_free:
-       kfree(led);
        return ret;
 }
 
@@ -372,7 +371,7 @@ static int __devexit mc13783_led_remove(struct platform_device *pdev)
 
        mc13xxx_unlock(dev);
 
-       kfree(led);
+       platform_set_drvdata(pdev, NULL);
        return 0;
 }
 
index 73973fdbd8be738ac57c23c6fb728b309a0a13d3..e37618e363cf7ce5a1686b79f9152360dd5dc275 100644 (file)
@@ -362,14 +362,14 @@ static int __devinit netxbig_led_probe(struct platform_device *pdev)
        if (!pdata)
                return -EINVAL;
 
-       leds_data = kzalloc(sizeof(struct netxbig_led_data) * pdata->num_leds,
-                           GFP_KERNEL);
+       leds_data = devm_kzalloc(&pdev->dev,
+               sizeof(struct netxbig_led_data) * pdata->num_leds, GFP_KERNEL);
        if (!leds_data)
                return -ENOMEM;
 
        ret = gpio_ext_init(pdata->gpio_ext);
        if (ret < 0)
-               goto err_free_data;
+               return ret;
 
        for (i = 0; i < pdata->num_leds; i++) {
                ret = create_netxbig_led(pdev, &leds_data[i], &pdata->leds[i]);
@@ -386,9 +386,6 @@ err_free_leds:
                delete_netxbig_led(&leds_data[i]);
 
        gpio_ext_free(pdata->gpio_ext);
-err_free_data:
-       kfree(leds_data);
-
        return ret;
 }
 
@@ -404,7 +401,6 @@ static int __devexit netxbig_led_remove(struct platform_device *pdev)
                delete_netxbig_led(&leds_data[i]);
 
        gpio_ext_free(pdata->gpio_ext);
-       kfree(leds_data);
 
        return 0;
 }
index 01cf89ec6944e8bb31be8ac4d524eea91957de34..10528dafb043d62f1de2462d88f75c980942909a 100644 (file)
@@ -273,29 +273,23 @@ static int __devinit ns2_led_probe(struct platform_device *pdev)
        if (!pdata)
                return -EINVAL;
 
-       leds_data = kzalloc(sizeof(struct ns2_led_data) *
+       leds_data = devm_kzalloc(&pdev->dev, sizeof(struct ns2_led_data) *
                            pdata->num_leds, GFP_KERNEL);
        if (!leds_data)
                return -ENOMEM;
 
        for (i = 0; i < pdata->num_leds; i++) {
                ret = create_ns2_led(pdev, &leds_data[i], &pdata->leds[i]);
-               if (ret < 0)
-                       goto err;
-
+               if (ret < 0) {
+                       for (i = i - 1; i >= 0; i--)
+                               delete_ns2_led(&leds_data[i]);
+                       return ret;
+               }
        }
 
        platform_set_drvdata(pdev, leds_data);
 
        return 0;
-
-err:
-       for (i = i - 1; i >= 0; i--)
-               delete_ns2_led(&leds_data[i]);
-
-       kfree(leds_data);
-
-       return ret;
 }
 
 static int __devexit ns2_led_remove(struct platform_device *pdev)
@@ -309,7 +303,6 @@ static int __devexit ns2_led_remove(struct platform_device *pdev)
        for (i = 0; i < pdata->num_leds; i++)
                delete_ns2_led(&leds_data[i]);
 
-       kfree(leds_data);
        platform_set_drvdata(pdev, NULL);
 
        return 0;
index ceccab44b5b818404da63b76f74cda552e7eb367..cee8a5b483ac50a59d16731c154639ede3bad03f 100644 (file)
@@ -449,7 +449,6 @@ static int pca9532_probe(struct i2c_client *client,
 {
        struct pca9532_data *data = i2c_get_clientdata(client);
        struct pca9532_platform_data *pca9532_pdata = client->dev.platform_data;
-       int err;
 
        if (!pca9532_pdata)
                return -EIO;
@@ -458,7 +457,7 @@ static int pca9532_probe(struct i2c_client *client,
                I2C_FUNC_SMBUS_BYTE_DATA))
                return -EIO;
 
-       data = kzalloc(sizeof(*data), GFP_KERNEL);
+       data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
        if (!data)
                return -ENOMEM;
 
@@ -469,11 +468,7 @@ static int pca9532_probe(struct i2c_client *client,
        data->client = client;
        mutex_init(&data->update_lock);
 
-       err = pca9532_configure(client, data, pca9532_pdata);
-       if (err)
-               kfree(data);
-
-       return err;
+       return pca9532_configure(client, data, pca9532_pdata);
 }
 
 static int pca9532_remove(struct i2c_client *client)
@@ -485,7 +480,6 @@ static int pca9532_remove(struct i2c_client *client)
        if (err)
                return err;
 
-       kfree(data);
        return 0;
 }
 
index 5f462dbf0dbbf4920bfc281c6ad69cfc8486ed5d..aef3cf0432fe7ba1374e9b4111fdd0c7ae1f9af1 100644 (file)
@@ -293,15 +293,14 @@ static int __devinit pca955x_probe(struct i2c_client *client,
                }
        }
 
-       pca955x = kzalloc(sizeof(*pca955x), GFP_KERNEL);
+       pca955x = devm_kzalloc(&client->dev, sizeof(*pca955x), GFP_KERNEL);
        if (!pca955x)
                return -ENOMEM;
 
-       pca955x->leds = kzalloc(sizeof(*pca955x_led) * chip->bits, GFP_KERNEL);
-       if (!pca955x->leds) {
-               err = -ENOMEM;
-               goto exit_nomem;
-       }
+       pca955x->leds = devm_kzalloc(&client->dev,
+                       sizeof(*pca955x_led) * chip->bits, GFP_KERNEL);
+       if (!pca955x->leds)
+               return -ENOMEM;
 
        i2c_set_clientdata(client, pca955x);
 
@@ -361,10 +360,6 @@ exit:
                cancel_work_sync(&pca955x->leds[i].work);
        }
 
-       kfree(pca955x->leds);
-exit_nomem:
-       kfree(pca955x);
-
        return err;
 }
 
@@ -378,9 +373,6 @@ static int __devexit pca955x_remove(struct i2c_client *client)
                cancel_work_sync(&pca955x->leds[i].work);
        }
 
-       kfree(pca955x->leds);
-       kfree(pca955x);
-
        return 0;
 }
 
index d8926fd031aa938f6e6e95c8713ebe63b6c596db..edcd706c5631f1024b305821a95857996260f593 100644 (file)
@@ -108,7 +108,7 @@ static int __devinit pca9633_probe(struct i2c_client *client,
                }
        }
 
-       pca9633 = kcalloc(4, sizeof(*pca9633), GFP_KERNEL);
+       pca9633 = devm_kzalloc(&client->dev, 4 * sizeof(*pca9633), GFP_KERNEL);
        if (!pca9633)
                return -ENOMEM;
 
@@ -156,8 +156,6 @@ exit:
                cancel_work_sync(&pca9633[i].work);
        }
 
-       kfree(pca9633);
-
        return err;
 }
 
@@ -171,8 +169,6 @@ static int __devexit pca9633_remove(struct i2c_client *client)
                cancel_work_sync(&pca9633[i].work);
        }
 
-       kfree(pca9633);
-
        return 0;
 }
 
index 3ed92f34bd4477108549499e7bbf4959af1a56cd..f2e44c7194374f135906fc3e9aa4ac87ca33a6bb 100644 (file)
@@ -57,7 +57,8 @@ static int led_pwm_probe(struct platform_device *pdev)
        if (!pdata)
                return -EBUSY;
 
-       leds_data = kzalloc(sizeof(struct led_pwm_data) * pdata->num_leds,
+       leds_data = devm_kzalloc(&pdev->dev,
+                       sizeof(struct led_pwm_data) * pdata->num_leds,
                                GFP_KERNEL);
        if (!leds_data)
                return -ENOMEM;
@@ -103,8 +104,6 @@ err:
                }
        }
 
-       kfree(leds_data);
-
        return ret;
 }
 
@@ -121,8 +120,6 @@ static int __devexit led_pwm_remove(struct platform_device *pdev)
                pwm_free(leds_data[i].pwm);
        }
 
-       kfree(leds_data);
-
        return 0;
 }
 
index df7e963bddd304f37bf507fe61ca317066111253..25d382d60fa92cfe0f91ddfbc99920b5a2740a11 100644 (file)
@@ -158,7 +158,7 @@ static int __devinit regulator_led_probe(struct platform_device *pdev)
                return PTR_ERR(vcc);
        }
 
-       led = kzalloc(sizeof(*led), GFP_KERNEL);
+       led = devm_kzalloc(&pdev->dev, sizeof(*led), GFP_KERNEL);
        if (led == NULL) {
                ret = -ENOMEM;
                goto err_vcc;
@@ -169,7 +169,7 @@ static int __devinit regulator_led_probe(struct platform_device *pdev)
                dev_err(&pdev->dev, "Invalid default brightness %d\n",
                                pdata->brightness);
                ret = -EINVAL;
-               goto err_led;
+               goto err_vcc;
        }
        led->value = pdata->brightness;
 
@@ -190,7 +190,7 @@ static int __devinit regulator_led_probe(struct platform_device *pdev)
        ret = led_classdev_register(&pdev->dev, &led->cdev);
        if (ret < 0) {
                cancel_work_sync(&led->work);
-               goto err_led;
+               goto err_vcc;
        }
 
        /* to expose the default value to userspace */
@@ -201,8 +201,6 @@ static int __devinit regulator_led_probe(struct platform_device *pdev)
 
        return 0;
 
-err_led:
-       kfree(led);
 err_vcc:
        regulator_put(vcc);
        return ret;
@@ -216,7 +214,6 @@ static int __devexit regulator_led_remove(struct platform_device *pdev)
        cancel_work_sync(&led->work);
        regulator_led_disable(led);
        regulator_put(led->vcc);
-       kfree(led);
        return 0;
 }
 
index 32fe337d5c687fbb09c06bd778e70cdc3cc7a223..9ee12c28059a0a8e60bc879dd083db2b90ae2fdc 100644 (file)
@@ -243,31 +243,30 @@ static int __devinit r_tpu_probe(struct platform_device *pdev)
        struct led_renesas_tpu_config *cfg = pdev->dev.platform_data;
        struct r_tpu_priv *p;
        struct resource *res;
-       int ret = -ENXIO;
+       int ret;
 
        if (!cfg) {
                dev_err(&pdev->dev, "missing platform data\n");
                goto err0;
        }
 
-       p = kzalloc(sizeof(*p), GFP_KERNEL);
+       p = devm_kzalloc(&pdev->dev, sizeof(*p), GFP_KERNEL);
        if (p == NULL) {
                dev_err(&pdev->dev, "failed to allocate driver data\n");
-               ret = -ENOMEM;
-               goto err0;
+               return -ENOMEM;
        }
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        if (!res) {
                dev_err(&pdev->dev, "failed to get I/O memory\n");
-               goto err1;
+               return -ENXIO;
        }
 
        /* map memory, let mapbase point to our channel */
        p->mapbase = ioremap_nocache(res->start, resource_size(res));
        if (p->mapbase == NULL) {
                dev_err(&pdev->dev, "failed to remap I/O memory\n");
-               goto err1;
+               return -ENXIO;
        }
 
        /* get hold of clock */
@@ -275,7 +274,7 @@ static int __devinit r_tpu_probe(struct platform_device *pdev)
        if (IS_ERR(p->clk)) {
                dev_err(&pdev->dev, "cannot get clock\n");
                ret = PTR_ERR(p->clk);
-               goto err2;
+               goto err0;
        }
 
        p->pdev = pdev;
@@ -294,7 +293,7 @@ static int __devinit r_tpu_probe(struct platform_device *pdev)
        p->ldev.flags |= LED_CORE_SUSPENDRESUME;
        ret = led_classdev_register(&pdev->dev, &p->ldev);
        if (ret < 0)
-               goto err3;
+               goto err1;
 
        /* max_brightness may be updated by the LED core code */
        p->min_rate = p->ldev.max_brightness * p->refresh_rate;
@@ -302,14 +301,11 @@ static int __devinit r_tpu_probe(struct platform_device *pdev)
        pm_runtime_enable(&pdev->dev);
        return 0;
 
- err3:
+ err1:
        r_tpu_set_pin(p, R_TPU_PIN_UNUSED, LED_OFF);
        clk_put(p->clk);
- err2:
-       iounmap(p->mapbase);
- err1:
-       kfree(p);
  err0:
+       iounmap(p->mapbase);
        return ret;
 }
 
@@ -327,7 +323,6 @@ static int __devexit r_tpu_remove(struct platform_device *pdev)
        clk_put(p->clk);
 
        iounmap(p->mapbase);
-       kfree(p);
        return 0;
 }
 
index bd0a5ed49c42d9473bf1fa2e735500ee8c2f67ad..942f0ea1817832f3d82389b8369bf6b2c545a457 100644 (file)
@@ -45,17 +45,19 @@ static void s3c24xx_led_set(struct led_classdev *led_cdev,
 {
        struct s3c24xx_gpio_led *led = to_gpio(led_cdev);
        struct s3c24xx_led_platdata *pd = led->pdata;
+       int state = (value ? 1 : 0) ^ (pd->flags & S3C24XX_LEDF_ACTLOW);
 
        /* there will be a short delay between setting the output and
         * going from output to input when using tristate. */
 
-       s3c2410_gpio_setpin(pd->gpio, (value ? 1 : 0) ^
-                           (pd->flags & S3C24XX_LEDF_ACTLOW));
-
-       if (pd->flags & S3C24XX_LEDF_TRISTATE)
-               s3c2410_gpio_cfgpin(pd->gpio,
-                       value ? S3C2410_GPIO_OUTPUT : S3C2410_GPIO_INPUT);
+       gpio_set_value(pd->gpio, state);
 
+       if (pd->flags & S3C24XX_LEDF_TRISTATE) {
+               if (value)
+                       gpio_direction_output(pd->gpio, state);
+               else
+                       gpio_direction_input(pd->gpio);
+       }
 }
 
 static int s3c24xx_led_remove(struct platform_device *dev)
@@ -63,7 +65,6 @@ static int s3c24xx_led_remove(struct platform_device *dev)
        struct s3c24xx_gpio_led *led = pdev_to_gpio(dev);
 
        led_classdev_unregister(&led->cdev);
-       kfree(led);
 
        return 0;
 }
@@ -74,7 +75,8 @@ static int s3c24xx_led_probe(struct platform_device *dev)
        struct s3c24xx_gpio_led *led;
        int ret;
 
-       led = kzalloc(sizeof(struct s3c24xx_gpio_led), GFP_KERNEL);
+       led = devm_kzalloc(&dev->dev, sizeof(struct s3c24xx_gpio_led),
+                          GFP_KERNEL);
        if (led == NULL) {
                dev_err(&dev->dev, "No memory for device\n");
                return -ENOMEM;
@@ -89,27 +91,27 @@ static int s3c24xx_led_probe(struct platform_device *dev)
 
        led->pdata = pdata;
 
+       ret = devm_gpio_request(&dev->dev, pdata->gpio, "S3C24XX_LED");
+       if (ret < 0)
+               return ret;
+
        /* no point in having a pull-up if we are always driving */
 
-       if (pdata->flags & S3C24XX_LEDF_TRISTATE) {
-               s3c2410_gpio_setpin(pdata->gpio, 0);
-               s3c2410_gpio_cfgpin(pdata->gpio, S3C2410_GPIO_INPUT);
-       } else {
-               s3c2410_gpio_pullup(pdata->gpio, 0);
-               s3c2410_gpio_setpin(pdata->gpio, 0);
-               s3c2410_gpio_cfgpin(pdata->gpio, S3C2410_GPIO_OUTPUT);
-       }
+       s3c_gpio_setpull(pdata->gpio, S3C_GPIO_PULL_NONE);
+
+       if (pdata->flags & S3C24XX_LEDF_TRISTATE)
+               gpio_direction_input(pdata->gpio);
+       else
+               gpio_direction_output(pdata->gpio,
+                       pdata->flags & S3C24XX_LEDF_ACTLOW ? 1 : 0);
 
        /* register our new led device */
 
        ret = led_classdev_register(&dev->dev, &led->cdev);
-       if (ret < 0) {
+       if (ret < 0)
                dev_err(&dev->dev, "led_classdev_register failed\n");
-               kfree(led);
-               return ret;
-       }
 
-       return 0;
+       return ret;
 }
 
 static struct platform_driver s3c24xx_led_driver = {
index 1757396b20b34fcb4b80edadbf8129066bb94e5c..134d9a4b34f18d66c4269927f21f088470c2de0f 100644 (file)
@@ -132,15 +132,13 @@ static int __devinit sunfire_led_generic_probe(struct platform_device *pdev,
        if (pdev->num_resources != 1) {
                printk(KERN_ERR PFX "Wrong number of resources %d, should be 1\n",
                       pdev->num_resources);
-               err = -EINVAL;
-               goto out;
+               return -EINVAL;
        }
 
-       p = kzalloc(sizeof(*p), GFP_KERNEL);
+       p = devm_kzalloc(&pdev->dev, sizeof(*p), GFP_KERNEL);
        if (!p) {
                printk(KERN_ERR PFX "Could not allocate struct sunfire_drvdata\n");
-               err = -ENOMEM;
-               goto out;
+               return -ENOMEM;
        }
 
        for (i = 0; i < NUM_LEDS_PER_BOARD; i++) {
@@ -156,20 +154,15 @@ static int __devinit sunfire_led_generic_probe(struct platform_device *pdev,
                if (err) {
                        printk(KERN_ERR PFX "Could not register %s LED\n",
                               lp->name);
-                       goto out_unregister_led_cdevs;
+                       for (i--; i >= 0; i--)
+                               led_classdev_unregister(&p->leds[i].led_cdev);
+                       return err;
                }
        }
 
        dev_set_drvdata(&pdev->dev, p);
 
        return 0;
-
-out_unregister_led_cdevs:
-       for (i--; i >= 0; i--)
-               led_classdev_unregister(&p->leds[i].led_cdev);
-       kfree(p);
-out:
-       return err;
 }
 
 static int __devexit sunfire_led_generic_remove(struct platform_device *pdev)
@@ -180,8 +173,6 @@ static int __devexit sunfire_led_generic_remove(struct platform_device *pdev)
        for (i = 0; i < NUM_LEDS_PER_BOARD; i++)
                led_classdev_unregister(&p->leds[i].led_cdev);
 
-       kfree(p);
-
        return 0;
 }
 
index 6c1c14f3163505530cf253d1884e2eb7d0bc8099..dabcf7ae8d0fd6369f6a4fa6142abc3de1d37be3 100644 (file)
@@ -687,7 +687,7 @@ static int __devinit tca6507_probe(struct i2c_client *client,
                        NUM_LEDS);
                return -ENODEV;
        }
-       tca = kzalloc(sizeof(*tca), GFP_KERNEL);
+       tca = devm_kzalloc(&client->dev, sizeof(*tca), GFP_KERNEL);
        if (!tca)
                return -ENOMEM;
 
@@ -727,7 +727,6 @@ exit:
                if (tca->leds[i].led_cdev.name)
                        led_classdev_unregister(&tca->leds[i].led_cdev);
        }
-       kfree(tca);
        return err;
 }
 
@@ -743,7 +742,6 @@ static int __devexit tca6507_remove(struct i2c_client *client)
        }
        tca6507_remove_gpio(tca);
        cancel_work_sync(&tca->work);
-       kfree(tca);
 
        return 0;
 }
@@ -758,18 +756,7 @@ static struct i2c_driver tca6507_driver = {
        .id_table = tca6507_id,
 };
 
-static int __init tca6507_leds_init(void)
-{
-       return i2c_add_driver(&tca6507_driver);
-}
-
-static void __exit tca6507_leds_exit(void)
-{
-       i2c_del_driver(&tca6507_driver);
-}
-
-module_init(tca6507_leds_init);
-module_exit(tca6507_leds_exit);
+module_i2c_driver(tca6507_driver);
 
 MODULE_AUTHOR("NeilBrown <neilb@suse.de>");
 MODULE_DESCRIPTION("TCA6507 LED/GPO driver");
index e77c7f8dcdd469bfa8b84472dabb81e2df1b10e7..d02acd4961269ab672ab11f1bc17960691496b05 100644 (file)
@@ -17,7 +17,7 @@
 #include <linux/rwsem.h>
 #include <linux/leds.h>
 
-static inline void led_set_brightness(struct led_classdev *led_cdev,
+static inline void __led_set_brightness(struct led_classdev *led_cdev,
                                        enum led_brightness value)
 {
        if (value > led_cdev->max_brightness)
index e2726867c5d42c7a5f2cc6c37b786d922eb0b883..b941685f222740970911b5fbbf93161304e76a37 100644 (file)
@@ -46,9 +46,9 @@ static int fb_notifier_callback(struct notifier_block *p,
 
                if ((n->old_status == UNBLANK) ^ n->invert) {
                        n->brightness = led->brightness;
-                       led_set_brightness(led, LED_OFF);
+                       __led_set_brightness(led, LED_OFF);
                } else {
-                       led_set_brightness(led, n->brightness);
+                       __led_set_brightness(led, n->brightness);
                }
 
                n->old_status = new_status;
@@ -87,9 +87,9 @@ static ssize_t bl_trig_invert_store(struct device *dev,
 
        /* After inverting, we need to update the LED. */
        if ((n->old_status == BLANK) ^ n->invert)
-               led_set_brightness(led, LED_OFF);
+               __led_set_brightness(led, LED_OFF);
        else
-               led_set_brightness(led, n->brightness);
+               __led_set_brightness(led, n->brightness);
 
        return num;
 }
index a4ef54b9d508efffe60b0cd4db2213479680547e..eac1f1b1adac97fb96e514be5d8d0e18d447ca2a 100644 (file)
@@ -19,7 +19,7 @@
 
 static void defon_trig_activate(struct led_classdev *led_cdev)
 {
-       led_set_brightness(led_cdev, led_cdev->max_brightness);
+       __led_set_brightness(led_cdev, led_cdev->max_brightness);
 }
 
 static struct led_trigger defon_led_trigger = {
index f057c101b896e56561a41f54809e1a10e52d0aaa..ba215dc42f9861ad41db278a970e37a6aedeceae 100644 (file)
@@ -54,12 +54,12 @@ static void gpio_trig_work(struct work_struct *work)
 
        if (tmp) {
                if (gpio_data->desired_brightness)
-                       led_set_brightness(gpio_data->led,
+                       __led_set_brightness(gpio_data->led,
                                           gpio_data->desired_brightness);
                else
-                       led_set_brightness(gpio_data->led, LED_FULL);
+                       __led_set_brightness(gpio_data->led, LED_FULL);
        } else {
-               led_set_brightness(gpio_data->led, LED_OFF);
+               __led_set_brightness(gpio_data->led, LED_OFF);
        }
 }
 
index a019fbb70880bd402aea095b8b90a8ca26a0e66a..1edc7463ce8381141d1d23abc779e4c5af9aa19d 100644 (file)
@@ -74,7 +74,7 @@ static void led_heartbeat_function(unsigned long data)
                break;
        }
 
-       led_set_brightness(led_cdev, brightness);
+       __led_set_brightness(led_cdev, brightness);
        mod_timer(&heartbeat_data->timer, jiffies + delay);
 }
 
index ec099fcbcb00ecf01c4f8ef4313710230428b15b..2cd7c0cf5924fe17807446d12287058566fadd36 100644 (file)
  */
 
 #include <linux/module.h>
-#include <linux/jiffies.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
-#include <linux/timer.h>
 #include <linux/leds.h>
 
-static void ledtrig_ide_timerfunc(unsigned long data);
+#define BLINK_DELAY 30
 
 DEFINE_LED_TRIGGER(ledtrig_ide);
-static DEFINE_TIMER(ledtrig_ide_timer, ledtrig_ide_timerfunc, 0, 0);
-static int ide_activity;
-static int ide_lastactivity;
+static unsigned long ide_blink_delay = BLINK_DELAY;
 
 void ledtrig_ide_activity(void)
 {
-       ide_activity++;
-       if (!timer_pending(&ledtrig_ide_timer))
-               mod_timer(&ledtrig_ide_timer, jiffies + msecs_to_jiffies(10));
+       led_trigger_blink_oneshot(ledtrig_ide,
+                                 &ide_blink_delay, &ide_blink_delay, 0);
 }
 EXPORT_SYMBOL(ledtrig_ide_activity);
 
-static void ledtrig_ide_timerfunc(unsigned long data)
-{
-       if (ide_lastactivity != ide_activity) {
-               ide_lastactivity = ide_activity;
-               /* INT_MAX will set each LED to its maximum brightness */
-               led_trigger_event(ledtrig_ide, INT_MAX);
-               mod_timer(&ledtrig_ide_timer, jiffies + msecs_to_jiffies(10));
-       } else {
-               led_trigger_event(ledtrig_ide, LED_OFF);
-       }
-}
-
 static int __init ledtrig_ide_init(void)
 {
        led_trigger_register_simple("ide-disk", &ledtrig_ide);
diff --git a/drivers/leds/ledtrig-oneshot.c b/drivers/leds/ledtrig-oneshot.c
new file mode 100644 (file)
index 0000000..2c029aa
--- /dev/null
@@ -0,0 +1,204 @@
+/*
+ * One-shot LED Trigger
+ *
+ * Copyright 2012, Fabio Baltieri <fabio.baltieri@gmail.com>
+ *
+ * Based on ledtrig-timer.c by Richard Purdie <rpurdie@openedhand.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/ctype.h>
+#include <linux/slab.h>
+#include <linux/leds.h>
+#include "leds.h"
+
+#define DEFAULT_DELAY 100
+
+struct oneshot_trig_data {
+       unsigned int invert;
+};
+
+static ssize_t led_shot(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t size)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct oneshot_trig_data *oneshot_data = led_cdev->trigger_data;
+
+       led_blink_set_oneshot(led_cdev,
+                       &led_cdev->blink_delay_on, &led_cdev->blink_delay_off,
+                       oneshot_data->invert);
+
+       /* content is ignored */
+       return size;
+}
+static ssize_t led_invert_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct oneshot_trig_data *oneshot_data = led_cdev->trigger_data;
+
+       return sprintf(buf, "%u\n", oneshot_data->invert);
+}
+
+static ssize_t led_invert_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t size)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct oneshot_trig_data *oneshot_data = led_cdev->trigger_data;
+       unsigned long state;
+       int ret;
+
+       ret = kstrtoul(buf, 0, &state);
+       if (ret)
+               return ret;
+
+       oneshot_data->invert = !!state;
+
+       if (oneshot_data->invert)
+               __led_set_brightness(led_cdev, LED_FULL);
+       else
+               __led_set_brightness(led_cdev, LED_OFF);
+
+       return size;
+}
+
+static ssize_t led_delay_on_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+
+       return sprintf(buf, "%lu\n", led_cdev->blink_delay_on);
+}
+
+static ssize_t led_delay_on_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t size)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       unsigned long state;
+       int ret;
+
+       ret = kstrtoul(buf, 0, &state);
+       if (ret)
+               return ret;
+
+       led_cdev->blink_delay_on = state;
+
+       return size;
+}
+static ssize_t led_delay_off_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+
+       return sprintf(buf, "%lu\n", led_cdev->blink_delay_off);
+}
+
+static ssize_t led_delay_off_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t size)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       unsigned long state;
+       int ret;
+
+       ret = kstrtoul(buf, 0, &state);
+       if (ret)
+               return ret;
+
+       led_cdev->blink_delay_off = state;
+
+       return size;
+}
+
+static DEVICE_ATTR(delay_on, 0644, led_delay_on_show, led_delay_on_store);
+static DEVICE_ATTR(delay_off, 0644, led_delay_off_show, led_delay_off_store);
+static DEVICE_ATTR(invert, 0644, led_invert_show, led_invert_store);
+static DEVICE_ATTR(shot, 0200, NULL, led_shot);
+
+static void oneshot_trig_activate(struct led_classdev *led_cdev)
+{
+       struct oneshot_trig_data *oneshot_data;
+       int rc;
+
+       oneshot_data = kzalloc(sizeof(*oneshot_data), GFP_KERNEL);
+       if (!oneshot_data)
+               return;
+
+       led_cdev->trigger_data = oneshot_data;
+
+       rc = device_create_file(led_cdev->dev, &dev_attr_delay_on);
+       if (rc)
+               goto err_out_trig_data;
+       rc = device_create_file(led_cdev->dev, &dev_attr_delay_off);
+       if (rc)
+               goto err_out_delayon;
+       rc = device_create_file(led_cdev->dev, &dev_attr_invert);
+       if (rc)
+               goto err_out_delayoff;
+       rc = device_create_file(led_cdev->dev, &dev_attr_shot);
+       if (rc)
+               goto err_out_invert;
+
+       led_cdev->blink_delay_on = DEFAULT_DELAY;
+       led_cdev->blink_delay_off = DEFAULT_DELAY;
+
+       led_cdev->activated = true;
+
+       return;
+
+err_out_invert:
+       device_remove_file(led_cdev->dev, &dev_attr_invert);
+err_out_delayoff:
+       device_remove_file(led_cdev->dev, &dev_attr_delay_off);
+err_out_delayon:
+       device_remove_file(led_cdev->dev, &dev_attr_delay_on);
+err_out_trig_data:
+       kfree(led_cdev->trigger_data);
+}
+
+static void oneshot_trig_deactivate(struct led_classdev *led_cdev)
+{
+       struct oneshot_trig_data *oneshot_data = led_cdev->trigger_data;
+
+       if (led_cdev->activated) {
+               device_remove_file(led_cdev->dev, &dev_attr_delay_on);
+               device_remove_file(led_cdev->dev, &dev_attr_delay_off);
+               device_remove_file(led_cdev->dev, &dev_attr_invert);
+               device_remove_file(led_cdev->dev, &dev_attr_shot);
+               kfree(oneshot_data);
+               led_cdev->activated = false;
+       }
+
+       /* Stop blinking */
+       led_set_brightness(led_cdev, LED_OFF);
+}
+
+static struct led_trigger oneshot_led_trigger = {
+       .name     = "oneshot",
+       .activate = oneshot_trig_activate,
+       .deactivate = oneshot_trig_deactivate,
+};
+
+static int __init oneshot_trig_init(void)
+{
+       return led_trigger_register(&oneshot_led_trigger);
+}
+
+static void __exit oneshot_trig_exit(void)
+{
+       led_trigger_unregister(&oneshot_led_trigger);
+}
+
+module_init(oneshot_trig_init);
+module_exit(oneshot_trig_exit);
+
+MODULE_AUTHOR("Fabio Baltieri <fabio.baltieri@gmail.com>");
+MODULE_DESCRIPTION("One-shot LED trigger");
+MODULE_LICENSE("GPL");
index 9010f7abaf2cac05b5d65c118af6fcbb70a8f34e..f774d05922042a69fd470b9ede566423a736c846 100644 (file)
@@ -104,7 +104,7 @@ static void timer_trig_deactivate(struct led_classdev *led_cdev)
        }
 
        /* Stop blinking */
-       led_brightness_set(led_cdev, LED_OFF);
+       led_set_brightness(led_cdev, LED_OFF);
 }
 
 static struct led_trigger timer_led_trigger = {
index 83179f435e1e141884840941ce73d8d9cb4ce8f4..398f1042c43ef7193beb48c1e37ed4297906789b 100644 (file)
@@ -41,7 +41,7 @@ static void transient_timer_function(unsigned long data)
        struct transient_trig_data *transient_data = led_cdev->trigger_data;
 
        transient_data->activate = 0;
-       led_set_brightness(led_cdev, transient_data->restore_state);
+       __led_set_brightness(led_cdev, transient_data->restore_state);
 }
 
 static ssize_t transient_activate_show(struct device *dev,
@@ -72,7 +72,7 @@ static ssize_t transient_activate_store(struct device *dev,
        if (state == 0 && transient_data->activate == 1) {
                del_timer(&transient_data->timer);
                transient_data->activate = state;
-               led_set_brightness(led_cdev, transient_data->restore_state);
+               __led_set_brightness(led_cdev, transient_data->restore_state);
                return size;
        }
 
@@ -80,7 +80,7 @@ static ssize_t transient_activate_store(struct device *dev,
        if (state == 1 && transient_data->activate == 0 &&
            transient_data->duration != 0) {
                transient_data->activate = state;
-               led_set_brightness(led_cdev, transient_data->state);
+               __led_set_brightness(led_cdev, transient_data->state);
                transient_data->restore_state =
                    (transient_data->state == LED_FULL) ? LED_OFF : LED_FULL;
                mod_timer(&transient_data->timer,
@@ -203,7 +203,7 @@ static void transient_trig_deactivate(struct led_classdev *led_cdev)
 
        if (led_cdev->activated) {
                del_timer_sync(&transient_data->timer);
-               led_set_brightness(led_cdev, transient_data->restore_state);
+               __led_set_brightness(led_cdev, transient_data->restore_state);
                device_remove_file(led_cdev->dev, &dev_attr_activate);
                device_remove_file(led_cdev->dev, &dev_attr_duration);
                device_remove_file(led_cdev->dev, &dev_attr_state);
index 39eee41d8c6f4deee39d6904087a567657a2349e..3aade1d8f410a287064167232e649be2dfb9522c 100644 (file)
@@ -38,6 +38,9 @@ struct led_classdev {
 #define LED_SUSPENDED          (1 << 0)
        /* Upper 16 bits reflect control information */
 #define LED_CORE_SUSPENDRESUME (1 << 16)
+#define LED_BLINK_ONESHOT      (1 << 17)
+#define LED_BLINK_ONESHOT_STOP (1 << 18)
+#define LED_BLINK_INVERT       (1 << 19)
 
        /* Set LED brightness level */
        /* Must not sleep, use a workqueue if needed */
@@ -103,7 +106,25 @@ extern void led_blink_set(struct led_classdev *led_cdev,
                          unsigned long *delay_on,
                          unsigned long *delay_off);
 /**
- * led_brightness_set - set LED brightness
+ * led_blink_set_oneshot - do a oneshot software blink
+ * @led_cdev: the LED to start blinking
+ * @delay_on: the time it should be on (in ms)
+ * @delay_off: the time it should ble off (in ms)
+ * @invert: blink off, then on, leaving the led on
+ *
+ * This function makes the LED blink one time for delay_on +
+ * delay_off time, ignoring the request if another one-shot
+ * blink is already in progress.
+ *
+ * If invert is set, led blinks for delay_off first, then for
+ * delay_on and leave the led on after the on-off cycle.
+ */
+extern void led_blink_set_oneshot(struct led_classdev *led_cdev,
+                                 unsigned long *delay_on,
+                                 unsigned long *delay_off,
+                                 int invert);
+/**
+ * led_set_brightness - set LED brightness
  * @led_cdev: the LED to set
  * @brightness: the brightness to set it to
  *
@@ -111,7 +132,7 @@ extern void led_blink_set(struct led_classdev *led_cdev,
  * software blink timer that implements blinking when the
  * hardware doesn't.
  */
-extern void led_brightness_set(struct led_classdev *led_cdev,
+extern void led_set_brightness(struct led_classdev *led_cdev,
                               enum led_brightness brightness);
 
 /*
@@ -150,6 +171,10 @@ extern void led_trigger_event(struct led_trigger *trigger,
 extern void led_trigger_blink(struct led_trigger *trigger,
                              unsigned long *delay_on,
                              unsigned long *delay_off);
+extern void led_trigger_blink_oneshot(struct led_trigger *trigger,
+                                     unsigned long *delay_on,
+                                     unsigned long *delay_off,
+                                     int invert);
 
 #else
 
diff --git a/include/linux/platform_data/leds-lm3556.h b/include/linux/platform_data/leds-lm3556.h
new file mode 100644 (file)
index 0000000..4b4e7d6
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Simple driver for Texas Instruments LM3556 LED Flash driver chip (Rev0x03)
+ * Copyright (C) 2012 Texas Instruments
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#ifndef __LINUX_LM3556_H
+#define __LINUX_LM3556_H
+
+#define LM3556_NAME "leds-lm3556"
+
+enum lm3556_pin_polarity {
+       PIN_LOW_ACTIVE = 0,
+       PIN_HIGH_ACTIVE,
+};
+
+enum lm3556_pin_enable {
+       PIN_DISABLED = 0,
+       PIN_ENABLED,
+};
+
+enum lm3556_strobe_usuage {
+       STROBE_EDGE_DETECT = 0,
+       STROBE_LEVEL_DETECT,
+};
+
+enum lm3556_indic_mode {
+       INDIC_MODE_INTERNAL = 0,
+       INDIC_MODE_EXTERNAL,
+};
+
+struct lm3556_platform_data {
+       enum lm3556_pin_enable torch_pin_en;
+       enum lm3556_pin_polarity torch_pin_polarity;
+
+       enum lm3556_strobe_usuage strobe_usuage;
+       enum lm3556_pin_enable strobe_pin_en;
+       enum lm3556_pin_polarity strobe_pin_polarity;
+
+       enum lm3556_pin_enable tx_pin_en;
+       enum lm3556_pin_polarity tx_pin_polarity;
+
+       enum lm3556_indic_mode indicator_mode;
+};
+
+#endif /* __LINUX_LM3556_H */
index 1bf7903496f8f4ec67ce4e861146f329bd5ad600..bcffa69031298f97ee99f50ade02a6e862c5a8a8 100644 (file)
@@ -276,7 +276,7 @@ static void ieee80211_stop_tpt_led_trig(struct ieee80211_local *local)
 
        read_lock(&tpt_trig->trig.leddev_list_lock);
        list_for_each_entry(led_cdev, &tpt_trig->trig.led_cdevs, trig_list)
-               led_brightness_set(led_cdev, LED_OFF);
+               led_set_brightness(led_cdev, LED_OFF);
        read_unlock(&tpt_trig->trig.leddev_list_lock);
 }