]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/commitdiff
leds: Add support for hardware accelerated LED flashing
authorMárton Németh <nm127@freemail.hu>
Wed, 31 Oct 2007 14:07:12 +0000 (15:07 +0100)
committerRichard Purdie <rpurdie@rpsys.net>
Thu, 7 Feb 2008 09:49:38 +0000 (09:49 +0000)
Extends the leds subsystem with a blink_set() callback function which can
be optionally implemented by a LED driver. If implemented, the driver can use
the hardware acceleration for blinking a LED.

Signed-off-by: Márton Németh <nm127@freemail.hu>
Signed-off-by: Richard Purdie <rpurdie@rpsys.net>
Documentation/leds-class.txt
drivers/leds/Kconfig
drivers/leds/ledtrig-timer.c
include/linux/leds.h

index 877af6efc18b51285eb598824ecc32630ab2e184..56757c751d6fc5611b181f6d79302a195b3fe8c7 100644 (file)
@@ -48,6 +48,26 @@ above leaves scope for further attributes should they be needed. If sections
 of the name don't apply, just leave that section blank.
 
 
 of the name don't apply, just leave that section blank.
 
 
+Hardware accelerated blink of LEDs
+==================================
+
+Some LEDs can be programmed to blink without any CPU interaction. To
+support this feature, a LED driver can optionally implement the
+blink_set() function (see <linux/leds.h>). If implemeted, triggers can
+attempt to use it before falling back to software timers. The blink_set()
+function should return 0 if the blink setting is supported, or -EINVAL
+otherwise, which means that LED blinking will be handled by software.
+
+The blink_set() function should choose a user friendly blinking
+value if it is called with *delay_on==0 && *delay_off==0 parameters. In
+this case the driver should give back the chosen value through delay_on
+and delay_off parameters to the leds subsystem.
+
+Any call to the brightness_set() callback function should cancel the
+previously programmed hardware blinking function so setting the brightness
+to 0 can also cancel the blinking of the LED.
+
+
 Known Issues
 ============
 
 Known Issues
 ============
 
@@ -56,10 +76,6 @@ would cause nightmare dependency issues. I see this as a minor issue
 compared to the benefits the simple trigger functionality brings. The
 rest of the LED subsystem can be modular.
 
 compared to the benefits the simple trigger functionality brings. The
 rest of the LED subsystem can be modular.
 
-Some leds can be programmed to flash in hardware. As this isn't a generic
-LED device property, this should be exported as a device specific sysfs
-attribute rather than part of the class if this functionality is required.
-
 
 Future Development
 ==================
 
 Future Development
 ==================
index cf3a6d4d947567297d5d999684b2faa27a311419..659448ead6858b718b711759ffebb9a10839a41d 100644 (file)
@@ -152,7 +152,11 @@ config LEDS_TRIGGER_TIMER
        depends on LEDS_TRIGGERS
        help
          This allows LEDs to be controlled by a programmable timer
        depends on LEDS_TRIGGERS
        help
          This allows LEDs to be controlled by a programmable timer
-         via sysfs. If unsure, say Y.
+         via sysfs. Some LED hardware can be programmed to start
+         blinking the LED without any further software interaction.
+         For more details read Documentation/leds-class.txt.
+
+         If unsure, say Y.
 
 config LEDS_TRIGGER_IDE_DISK
        bool "LED IDE Disk Trigger"
 
 config LEDS_TRIGGER_IDE_DISK
        bool "LED IDE Disk Trigger"
index ed9ff02c77ea742c23c616a0f340c400770c33ec..82c55d6e49023ff9fe2866a819a1c5ec7bad7949 100644 (file)
@@ -77,8 +77,21 @@ static ssize_t led_delay_on_store(struct device *dev,
                count++;
 
        if (count == size) {
                count++;
 
        if (count == size) {
-               timer_data->delay_on = state;
-               mod_timer(&timer_data->timer, jiffies + 1);
+               if (timer_data->delay_on != state) {
+                       /* the new value differs from the previous */
+                       timer_data->delay_on = state;
+
+                       /* deactivate previous settings */
+                       del_timer_sync(&timer_data->timer);
+
+                       /* try to activate hardware acceleration, if any */
+                       if (!led_cdev->blink_set ||
+                           led_cdev->blink_set(led_cdev,
+                               &timer_data->delay_on, &timer_data->delay_off)) {
+                               /* no hardware acceleration, blink via timer */
+                               mod_timer(&timer_data->timer, jiffies + 1);
+                       }
+               }
                ret = count;
        }
 
                ret = count;
        }
 
@@ -110,8 +123,21 @@ static ssize_t led_delay_off_store(struct device *dev,
                count++;
 
        if (count == size) {
                count++;
 
        if (count == size) {
-               timer_data->delay_off = state;
-               mod_timer(&timer_data->timer, jiffies + 1);
+               if (timer_data->delay_off != state) {
+                       /* the new value differs from the previous */
+                       timer_data->delay_off = state;
+
+                       /* deactivate previous settings */
+                       del_timer_sync(&timer_data->timer);
+
+                       /* try to activate hardware acceleration, if any */
+                       if (!led_cdev->blink_set ||
+                           led_cdev->blink_set(led_cdev,
+                               &timer_data->delay_on, &timer_data->delay_off)) {
+                               /* no hardware acceleration, blink via timer */
+                               mod_timer(&timer_data->timer, jiffies + 1);
+                       }
+               }
                ret = count;
        }
 
                ret = count;
        }
 
@@ -143,6 +169,13 @@ static void timer_trig_activate(struct led_classdev *led_cdev)
        if (rc)
                goto err_out_delayon;
 
        if (rc)
                goto err_out_delayon;
 
+       /* If there is hardware support for blinking, start one
+        * user friendly blink rate chosen by the driver.
+        */
+       if (led_cdev->blink_set)
+               led_cdev->blink_set(led_cdev,
+                       &timer_data->delay_on, &timer_data->delay_off);
+
        return;
 
 err_out_delayon:
        return;
 
 err_out_delayon:
index 00f89fd6c52a21f83d26a7b94e351e3c782854bf..0201f6f51ceab72892ce6b2c167d8dceaa2a23d5 100644 (file)
@@ -38,6 +38,11 @@ struct led_classdev {
        void            (*brightness_set)(struct led_classdev *led_cdev,
                                          enum led_brightness brightness);
 
        void            (*brightness_set)(struct led_classdev *led_cdev,
                                          enum led_brightness brightness);
 
+       /* Activate hardware accelerated blink */
+       int             (*blink_set)(struct led_classdev *led_cdev,
+                                    unsigned long *delay_on,
+                                    unsigned long *delay_off);
+
        struct device           *dev;
        struct list_head         node;                  /* LED Device list */
        char                    *default_trigger;       /* Trigger to use */
        struct device           *dev;
        struct list_head         node;                  /* LED Device list */
        char                    *default_trigger;       /* Trigger to use */