Merge branch 'for-linus' of git://git.o-hand.com/linux-rpurdie-leds
* 'for-linus' of git://git.o-hand.com/linux-rpurdie-leds: leds: introduce lp5521 led driver leds: just ignore invalid GPIOs in leds-gpio leds: Fix &&/|| confusion in leds-pca9532.c leds: move h1940-leds's probe function to .devinit.text leds: remove an unnecessary "goto" on drivers/leds/leds-s3c24.c leds: add BD2802GU LED driver leds: remove experimental flag from leds-clevo-mail leds: Prevent multiple LED triggers with the same name leds: Add gpio-led trigger leds: Add rb532 LED driver for the User LED leds: Add suspend/resume state flags to leds-gpio leds: simple driver for pwm driven LEDs leds: Fix leds-gpio driver multiple module_init/exit usage leds: Add dac124s085 driver leds: allow led-drivers to use a variable range of brightness values leds: Add openfirmware platform device support
This commit is contained in:
Коммит
3cd69271f8
|
@ -1,15 +1,43 @@
|
|||
LED connected to GPIO
|
||||
LEDs connected to GPIO lines
|
||||
|
||||
Required properties:
|
||||
- compatible : should be "gpio-led".
|
||||
- label : (optional) the label for this LED. If omitted, the label is
|
||||
- compatible : should be "gpio-leds".
|
||||
|
||||
Each LED is represented as a sub-node of the gpio-leds device. Each
|
||||
node's name represents the name of the corresponding LED.
|
||||
|
||||
LED sub-node properties:
|
||||
- gpios : Should specify the LED's GPIO, see "Specifying GPIO information
|
||||
for devices" in Documentation/powerpc/booting-without-of.txt. Active
|
||||
low LEDs should be indicated using flags in the GPIO specifier.
|
||||
- label : (optional) The label for this LED. If omitted, the label is
|
||||
taken from the node name (excluding the unit address).
|
||||
- gpios : should specify LED GPIO.
|
||||
- linux,default-trigger : (optional) This parameter, if present, is a
|
||||
string defining the trigger assigned to the LED. Current triggers are:
|
||||
"backlight" - LED will act as a back-light, controlled by the framebuffer
|
||||
system
|
||||
"default-on" - LED will turn on
|
||||
"heartbeat" - LED "double" flashes at a load average based rate
|
||||
"ide-disk" - LED indicates disk activity
|
||||
"timer" - LED flashes at a fixed, configurable rate
|
||||
|
||||
Example:
|
||||
Examples:
|
||||
|
||||
led@0 {
|
||||
compatible = "gpio-led";
|
||||
label = "hdd";
|
||||
gpios = <&mcu_pio 0 1>;
|
||||
leds {
|
||||
compatible = "gpio-leds";
|
||||
hdd {
|
||||
label = "IDE Activity";
|
||||
gpios = <&mcu_pio 0 1>; /* Active low */
|
||||
linux,default-trigger = "ide-disk";
|
||||
};
|
||||
};
|
||||
|
||||
run-control {
|
||||
compatible = "gpio-leds";
|
||||
red {
|
||||
gpios = <&mpc8572 6 0>;
|
||||
};
|
||||
green {
|
||||
gpios = <&mpc8572 7 0>;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -31,6 +31,13 @@ config LEDS_LOCOMO
|
|||
This option enables support for the LEDs on Sharp Locomo.
|
||||
Zaurus models SL-5500 and SL-5600.
|
||||
|
||||
config LEDS_MIKROTIK_RB532
|
||||
tristate "LED Support for Mikrotik Routerboard 532"
|
||||
depends on LEDS_CLASS && MIKROTIK_RB532
|
||||
help
|
||||
This option enables support for the so called "User LED" of
|
||||
Mikrotik's Routerboard 532.
|
||||
|
||||
config LEDS_S3C24XX
|
||||
tristate "LED Support for Samsung S3C24XX GPIO LEDs"
|
||||
depends on LEDS_CLASS && ARCH_S3C2410
|
||||
|
@ -117,11 +124,40 @@ config LEDS_GPIO
|
|||
help
|
||||
This option enables support for the LEDs connected to GPIO
|
||||
outputs. To be useful the particular board must have LEDs
|
||||
and they must be connected to the GPIO lines.
|
||||
and they must be connected to the GPIO lines. The LEDs must be
|
||||
defined as platform devices and/or OpenFirmware platform devices.
|
||||
The code to use these bindings can be selected below.
|
||||
|
||||
config LEDS_GPIO_PLATFORM
|
||||
bool "Platform device bindings for GPIO LEDs"
|
||||
depends on LEDS_GPIO
|
||||
default y
|
||||
help
|
||||
Let the leds-gpio driver drive LEDs which have been defined as
|
||||
platform devices. If you don't know what this means, say yes.
|
||||
|
||||
config LEDS_GPIO_OF
|
||||
bool "OpenFirmware platform device bindings for GPIO LEDs"
|
||||
depends on LEDS_GPIO && OF_DEVICE
|
||||
default y
|
||||
help
|
||||
Let the leds-gpio driver drive LEDs which have been defined as
|
||||
of_platform devices. For instance, LEDs which are listed in a "dts"
|
||||
file.
|
||||
|
||||
config LEDS_LP5521
|
||||
tristate "LED Support for the LP5521 LEDs"
|
||||
depends on LEDS_CLASS && I2C
|
||||
help
|
||||
If you say 'Y' here you get support for the National Semiconductor
|
||||
LP5521 LED driver used in n8x0 boards.
|
||||
|
||||
This driver can be built as a module by choosing 'M'. The module
|
||||
will be called leds-lp5521.
|
||||
|
||||
config LEDS_CLEVO_MAIL
|
||||
tristate "Mail LED on Clevo notebook (EXPERIMENTAL)"
|
||||
depends on LEDS_CLASS && X86 && SERIO_I8042 && DMI && EXPERIMENTAL
|
||||
tristate "Mail LED on Clevo notebook"
|
||||
depends on LEDS_CLASS && X86 && SERIO_I8042 && DMI
|
||||
help
|
||||
This driver makes the mail LED accessible from userspace
|
||||
programs through the leds subsystem. This LED have three
|
||||
|
@ -171,6 +207,26 @@ config LEDS_DA903X
|
|||
This option enables support for on-chip LED drivers found
|
||||
on Dialog Semiconductor DA9030/DA9034 PMICs.
|
||||
|
||||
config LEDS_DAC124S085
|
||||
tristate "LED Support for DAC124S085 SPI DAC"
|
||||
depends on LEDS_CLASS && SPI
|
||||
help
|
||||
This option enables support for DAC124S085 SPI DAC from NatSemi,
|
||||
which can be used to control up to four LEDs.
|
||||
|
||||
config LEDS_PWM
|
||||
tristate "PWM driven LED Support"
|
||||
depends on LEDS_CLASS && HAVE_PWM
|
||||
help
|
||||
This option enables support for pwm driven LEDs
|
||||
|
||||
config LEDS_BD2802
|
||||
tristate "LED driver for BD2802 RGB LED"
|
||||
depends on LEDS_CLASS && I2C
|
||||
help
|
||||
This option enables support for BD2802GU RGB LED driver chips
|
||||
accessed via the I2C bus.
|
||||
|
||||
comment "LED Triggers"
|
||||
|
||||
config LEDS_TRIGGERS
|
||||
|
@ -216,6 +272,19 @@ config LEDS_TRIGGER_BACKLIGHT
|
|||
|
||||
If unsure, say N.
|
||||
|
||||
config LEDS_TRIGGER_GPIO
|
||||
tristate "LED GPIO Trigger"
|
||||
depends on LEDS_TRIGGERS
|
||||
depends on GPIOLIB
|
||||
help
|
||||
This allows LEDs to be controlled by gpio events. It's good
|
||||
when using gpios as switches and triggering the needed LEDs
|
||||
from there. One use case is n810's keypad LEDs that could
|
||||
be triggered by this trigger when user slides up to show
|
||||
keypad.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config LEDS_TRIGGER_DEFAULT_ON
|
||||
tristate "LED Default ON Trigger"
|
||||
depends on LEDS_TRIGGERS
|
||||
|
|
|
@ -6,7 +6,9 @@ obj-$(CONFIG_LEDS_TRIGGERS) += led-triggers.o
|
|||
|
||||
# LED Platform Drivers
|
||||
obj-$(CONFIG_LEDS_ATMEL_PWM) += leds-atmel-pwm.o
|
||||
obj-$(CONFIG_LEDS_BD2802) += leds-bd2802.o
|
||||
obj-$(CONFIG_LEDS_LOCOMO) += leds-locomo.o
|
||||
obj-$(CONFIG_LEDS_MIKROTIK_RB532) += leds-rb532.o
|
||||
obj-$(CONFIG_LEDS_S3C24XX) += leds-s3c24xx.o
|
||||
obj-$(CONFIG_LEDS_AMS_DELTA) += leds-ams-delta.o
|
||||
obj-$(CONFIG_LEDS_NET48XX) += leds-net48xx.o
|
||||
|
@ -24,10 +26,15 @@ obj-$(CONFIG_LEDS_FSG) += leds-fsg.o
|
|||
obj-$(CONFIG_LEDS_PCA955X) += leds-pca955x.o
|
||||
obj-$(CONFIG_LEDS_DA903X) += leds-da903x.o
|
||||
obj-$(CONFIG_LEDS_WM8350) += leds-wm8350.o
|
||||
obj-$(CONFIG_LEDS_PWM) += leds-pwm.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_IDE_DISK) += ledtrig-ide-disk.o
|
||||
obj-$(CONFIG_LEDS_TRIGGER_HEARTBEAT) += ledtrig-heartbeat.o
|
||||
obj-$(CONFIG_LEDS_TRIGGER_BACKLIGHT) += ledtrig-backlight.o
|
||||
obj-$(CONFIG_LEDS_TRIGGER_GPIO) += ledtrig-gpio.o
|
||||
obj-$(CONFIG_LEDS_TRIGGER_DEFAULT_ON) += ledtrig-default-on.o
|
||||
|
|
|
@ -64,7 +64,16 @@ static ssize_t led_brightness_store(struct device *dev,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t led_max_brightness_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct led_classdev *led_cdev = dev_get_drvdata(dev);
|
||||
|
||||
return sprintf(buf, "%u\n", led_cdev->max_brightness);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(brightness, 0644, led_brightness_show, led_brightness_store);
|
||||
static DEVICE_ATTR(max_brightness, 0444, led_max_brightness_show, NULL);
|
||||
#ifdef CONFIG_LEDS_TRIGGERS
|
||||
static DEVICE_ATTR(trigger, 0644, led_trigger_show, led_trigger_store);
|
||||
#endif
|
||||
|
@ -138,6 +147,13 @@ int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
|
|||
list_add_tail(&led_cdev->node, &leds_list);
|
||||
up_write(&leds_list_lock);
|
||||
|
||||
if (!led_cdev->max_brightness)
|
||||
led_cdev->max_brightness = LED_FULL;
|
||||
|
||||
rc = device_create_file(led_cdev->dev, &dev_attr_max_brightness);
|
||||
if (rc)
|
||||
goto err_out_attr_max;
|
||||
|
||||
led_update_brightness(led_cdev);
|
||||
|
||||
#ifdef CONFIG_LEDS_TRIGGERS
|
||||
|
@ -155,9 +171,11 @@ int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
|
|||
|
||||
#ifdef CONFIG_LEDS_TRIGGERS
|
||||
err_out_led_list:
|
||||
device_remove_file(led_cdev->dev, &dev_attr_max_brightness);
|
||||
#endif
|
||||
err_out_attr_max:
|
||||
device_remove_file(led_cdev->dev, &dev_attr_brightness);
|
||||
list_del(&led_cdev->node);
|
||||
#endif
|
||||
err_out:
|
||||
device_unregister(led_cdev->dev);
|
||||
return rc;
|
||||
|
@ -172,6 +190,7 @@ EXPORT_SYMBOL_GPL(led_classdev_register);
|
|||
*/
|
||||
void led_classdev_unregister(struct led_classdev *led_cdev)
|
||||
{
|
||||
device_remove_file(led_cdev->dev, &dev_attr_max_brightness);
|
||||
device_remove_file(led_cdev->dev, &dev_attr_brightness);
|
||||
#ifdef CONFIG_LEDS_TRIGGERS
|
||||
device_remove_file(led_cdev->dev, &dev_attr_trigger);
|
||||
|
|
|
@ -156,12 +156,20 @@ EXPORT_SYMBOL_GPL(led_trigger_set_default);
|
|||
int led_trigger_register(struct led_trigger *trigger)
|
||||
{
|
||||
struct led_classdev *led_cdev;
|
||||
struct led_trigger *trig;
|
||||
|
||||
rwlock_init(&trigger->leddev_list_lock);
|
||||
INIT_LIST_HEAD(&trigger->led_cdevs);
|
||||
|
||||
/* Add to the list of led triggers */
|
||||
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)) {
|
||||
up_write(&triggers_list_lock);
|
||||
return -EEXIST;
|
||||
}
|
||||
}
|
||||
/* Add to the list of led triggers */
|
||||
list_add_tail(&trigger->next_trig, &trigger_list);
|
||||
up_write(&triggers_list_lock);
|
||||
|
||||
|
|
|
@ -0,0 +1,765 @@
|
|||
/*
|
||||
* leds-bd2802.c - RGB LED Driver
|
||||
*
|
||||
* Copyright (C) 2009 Samsung Electronics
|
||||
* Kim Kyuwon <q1.kim@samsung.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.
|
||||
*
|
||||
* Datasheet: http://www.rohm.com/products/databook/driver/pdf/bd2802gu-e.pdf
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/leds-bd2802.h>
|
||||
|
||||
|
||||
#define LED_CTL(rgb2en, rgb1en) ((rgb2en) << 4 | ((rgb1en) << 0))
|
||||
|
||||
#define BD2802_LED_OFFSET 0xa
|
||||
#define BD2802_COLOR_OFFSET 0x3
|
||||
|
||||
#define BD2802_REG_CLKSETUP 0x00
|
||||
#define BD2802_REG_CONTROL 0x01
|
||||
#define BD2802_REG_HOURSETUP 0x02
|
||||
#define BD2802_REG_CURRENT1SETUP 0x03
|
||||
#define BD2802_REG_CURRENT2SETUP 0x04
|
||||
#define BD2802_REG_WAVEPATTERN 0x05
|
||||
|
||||
#define BD2802_CURRENT_032 0x10 /* 3.2mA */
|
||||
#define BD2802_CURRENT_000 0x00 /* 0.0mA */
|
||||
|
||||
#define BD2802_PATTERN_FULL 0x07
|
||||
#define BD2802_PATTERN_HALF 0x03
|
||||
|
||||
enum led_ids {
|
||||
LED1,
|
||||
LED2,
|
||||
LED_NUM,
|
||||
};
|
||||
|
||||
enum led_colors {
|
||||
RED,
|
||||
GREEN,
|
||||
BLUE,
|
||||
};
|
||||
|
||||
enum led_bits {
|
||||
BD2802_OFF,
|
||||
BD2802_BLINK,
|
||||
BD2802_ON,
|
||||
};
|
||||
|
||||
/*
|
||||
* State '0' : 'off'
|
||||
* State '1' : 'blink'
|
||||
* State '2' : 'on'.
|
||||
*/
|
||||
struct led_state {
|
||||
unsigned r:2;
|
||||
unsigned g:2;
|
||||
unsigned b:2;
|
||||
};
|
||||
|
||||
struct bd2802_led {
|
||||
struct bd2802_led_platform_data *pdata;
|
||||
struct i2c_client *client;
|
||||
struct rw_semaphore rwsem;
|
||||
struct work_struct work;
|
||||
|
||||
struct led_state led[2];
|
||||
|
||||
/*
|
||||
* Making led_classdev as array is not recommended, because array
|
||||
* members prevent using 'container_of' macro. So repetitive works
|
||||
* are needed.
|
||||
*/
|
||||
struct led_classdev cdev_led1r;
|
||||
struct led_classdev cdev_led1g;
|
||||
struct led_classdev cdev_led1b;
|
||||
struct led_classdev cdev_led2r;
|
||||
struct led_classdev cdev_led2g;
|
||||
struct led_classdev cdev_led2b;
|
||||
|
||||
/*
|
||||
* Advanced Configuration Function(ADF) mode:
|
||||
* In ADF mode, user can set registers of BD2802GU directly,
|
||||
* therefore BD2802GU doesn't enter reset state.
|
||||
*/
|
||||
int adf_on;
|
||||
|
||||
enum led_ids led_id;
|
||||
enum led_colors color;
|
||||
enum led_bits state;
|
||||
};
|
||||
|
||||
|
||||
/*--------------------------------------------------------------*/
|
||||
/* BD2802GU helper functions */
|
||||
/*--------------------------------------------------------------*/
|
||||
|
||||
static inline int bd2802_is_rgb_off(struct bd2802_led *led, enum led_ids id,
|
||||
enum led_colors color)
|
||||
{
|
||||
switch (color) {
|
||||
case RED:
|
||||
return !led->led[id].r;
|
||||
case GREEN:
|
||||
return !led->led[id].g;
|
||||
case BLUE:
|
||||
return !led->led[id].b;
|
||||
default:
|
||||
dev_err(&led->client->dev, "%s: Invalid color\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static inline int bd2802_is_led_off(struct bd2802_led *led, enum led_ids id)
|
||||
{
|
||||
if (led->led[id].r || led->led[id].g || led->led[id].b)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline int bd2802_is_all_off(struct bd2802_led *led)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < LED_NUM; i++)
|
||||
if (!bd2802_is_led_off(led, i))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline u8 bd2802_get_base_offset(enum led_ids id, enum led_colors color)
|
||||
{
|
||||
return id * BD2802_LED_OFFSET + color * BD2802_COLOR_OFFSET;
|
||||
}
|
||||
|
||||
static inline u8 bd2802_get_reg_addr(enum led_ids id, enum led_colors color,
|
||||
u8 reg_offset)
|
||||
{
|
||||
return reg_offset + bd2802_get_base_offset(id, color);
|
||||
}
|
||||
|
||||
|
||||
/*--------------------------------------------------------------*/
|
||||
/* BD2802GU core functions */
|
||||
/*--------------------------------------------------------------*/
|
||||
|
||||
static int bd2802_write_byte(struct i2c_client *client, u8 reg, u8 val)
|
||||
{
|
||||
int ret = i2c_smbus_write_byte_data(client, reg, val);
|
||||
if (ret >= 0)
|
||||
return 0;
|
||||
|
||||
dev_err(&client->dev, "%s: reg 0x%x, val 0x%x, err %d\n",
|
||||
__func__, reg, val, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void bd2802_update_state(struct bd2802_led *led, enum led_ids id,
|
||||
enum led_colors color, enum led_bits led_bit)
|
||||
{
|
||||
int i;
|
||||
u8 value;
|
||||
|
||||
for (i = 0; i < LED_NUM; i++) {
|
||||
if (i == id) {
|
||||
switch (color) {
|
||||
case RED:
|
||||
led->led[i].r = led_bit;
|
||||
break;
|
||||
case GREEN:
|
||||
led->led[i].g = led_bit;
|
||||
break;
|
||||
case BLUE:
|
||||
led->led[i].b = led_bit;
|
||||
break;
|
||||
default:
|
||||
dev_err(&led->client->dev,
|
||||
"%s: Invalid color\n", __func__);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (led_bit == BD2802_BLINK || led_bit == BD2802_ON)
|
||||
return;
|
||||
|
||||
if (!bd2802_is_led_off(led, id))
|
||||
return;
|
||||
|
||||
if (bd2802_is_all_off(led) && !led->adf_on) {
|
||||
gpio_set_value(led->pdata->reset_gpio, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* In this case, other led is turned on, and current led is turned
|
||||
* off. So set RGB LED Control register to stop the current RGB LED
|
||||
*/
|
||||
value = (id == LED1) ? LED_CTL(1, 0) : LED_CTL(0, 1);
|
||||
bd2802_write_byte(led->client, BD2802_REG_CONTROL, value);
|
||||
}
|
||||
|
||||
static void bd2802_configure(struct bd2802_led *led)
|
||||
{
|
||||
struct bd2802_led_platform_data *pdata = led->pdata;
|
||||
u8 reg;
|
||||
|
||||
reg = bd2802_get_reg_addr(LED1, RED, BD2802_REG_HOURSETUP);
|
||||
bd2802_write_byte(led->client, reg, pdata->rgb_time);
|
||||
|
||||
reg = bd2802_get_reg_addr(LED2, RED, BD2802_REG_HOURSETUP);
|
||||
bd2802_write_byte(led->client, reg, pdata->rgb_time);
|
||||
}
|
||||
|
||||
static void bd2802_reset_cancel(struct bd2802_led *led)
|
||||
{
|
||||
gpio_set_value(led->pdata->reset_gpio, 1);
|
||||
udelay(100);
|
||||
bd2802_configure(led);
|
||||
}
|
||||
|
||||
static void bd2802_enable(struct bd2802_led *led, enum led_ids id)
|
||||
{
|
||||
enum led_ids other_led = (id == LED1) ? LED2 : LED1;
|
||||
u8 value, other_led_on;
|
||||
|
||||
other_led_on = !bd2802_is_led_off(led, other_led);
|
||||
if (id == LED1)
|
||||
value = LED_CTL(other_led_on, 1);
|
||||
else
|
||||
value = LED_CTL(1 , other_led_on);
|
||||
|
||||
bd2802_write_byte(led->client, BD2802_REG_CONTROL, value);
|
||||
}
|
||||
|
||||
static void bd2802_set_on(struct bd2802_led *led, enum led_ids id,
|
||||
enum led_colors color)
|
||||
{
|
||||
u8 reg;
|
||||
|
||||
if (bd2802_is_all_off(led) && !led->adf_on)
|
||||
bd2802_reset_cancel(led);
|
||||
|
||||
reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT1SETUP);
|
||||
bd2802_write_byte(led->client, reg, BD2802_CURRENT_032);
|
||||
reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT2SETUP);
|
||||
bd2802_write_byte(led->client, reg, BD2802_CURRENT_000);
|
||||
reg = bd2802_get_reg_addr(id, color, BD2802_REG_WAVEPATTERN);
|
||||
bd2802_write_byte(led->client, reg, BD2802_PATTERN_FULL);
|
||||
|
||||
bd2802_enable(led, id);
|
||||
bd2802_update_state(led, id, color, BD2802_ON);
|
||||
}
|
||||
|
||||
static void bd2802_set_blink(struct bd2802_led *led, enum led_ids id,
|
||||
enum led_colors color)
|
||||
{
|
||||
u8 reg;
|
||||
|
||||
if (bd2802_is_all_off(led) && !led->adf_on)
|
||||
bd2802_reset_cancel(led);
|
||||
|
||||
reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT1SETUP);
|
||||
bd2802_write_byte(led->client, reg, BD2802_CURRENT_000);
|
||||
reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT2SETUP);
|
||||
bd2802_write_byte(led->client, reg, BD2802_CURRENT_032);
|
||||
reg = bd2802_get_reg_addr(id, color, BD2802_REG_WAVEPATTERN);
|
||||
bd2802_write_byte(led->client, reg, BD2802_PATTERN_HALF);
|
||||
|
||||
bd2802_enable(led, id);
|
||||
bd2802_update_state(led, id, color, BD2802_BLINK);
|
||||
}
|
||||
|
||||
static void bd2802_turn_on(struct bd2802_led *led, enum led_ids id,
|
||||
enum led_colors color, enum led_bits led_bit)
|
||||
{
|
||||
if (led_bit == BD2802_OFF) {
|
||||
dev_err(&led->client->dev,
|
||||
"Only 'blink' and 'on' are allowed\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (led_bit == BD2802_BLINK)
|
||||
bd2802_set_blink(led, id, color);
|
||||
else
|
||||
bd2802_set_on(led, id, color);
|
||||
}
|
||||
|
||||
static void bd2802_turn_off(struct bd2802_led *led, enum led_ids id,
|
||||
enum led_colors color)
|
||||
{
|
||||
u8 reg;
|
||||
|
||||
if (bd2802_is_rgb_off(led, id, color))
|
||||
return;
|
||||
|
||||
reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT1SETUP);
|
||||
bd2802_write_byte(led->client, reg, BD2802_CURRENT_000);
|
||||
reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT2SETUP);
|
||||
bd2802_write_byte(led->client, reg, BD2802_CURRENT_000);
|
||||
|
||||
bd2802_update_state(led, id, color, BD2802_OFF);
|
||||
}
|
||||
|
||||
static void bd2802_restore_state(struct bd2802_led *led)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < LED_NUM; i++) {
|
||||
if (led->led[i].r)
|
||||
bd2802_turn_on(led, i, RED, led->led[i].r);
|
||||
if (led->led[i].g)
|
||||
bd2802_turn_on(led, i, GREEN, led->led[i].g);
|
||||
if (led->led[i].b)
|
||||
bd2802_turn_on(led, i, BLUE, led->led[i].b);
|
||||
}
|
||||
}
|
||||
|
||||
#define BD2802_SET_REGISTER(reg_addr, reg_name) \
|
||||
static ssize_t bd2802_store_reg##reg_addr(struct device *dev, \
|
||||
struct device_attribute *attr, const char *buf, size_t count) \
|
||||
{ \
|
||||
struct bd2802_led *led = i2c_get_clientdata(to_i2c_client(dev));\
|
||||
unsigned long val; \
|
||||
int ret; \
|
||||
if (!count) \
|
||||
return -EINVAL; \
|
||||
ret = strict_strtoul(buf, 16, &val); \
|
||||
if (ret) \
|
||||
return ret; \
|
||||
down_write(&led->rwsem); \
|
||||
bd2802_write_byte(led->client, reg_addr, (u8) val); \
|
||||
up_write(&led->rwsem); \
|
||||
return count; \
|
||||
} \
|
||||
static struct device_attribute bd2802_reg##reg_addr##_attr = { \
|
||||
.attr = {.name = reg_name, .mode = 0644, .owner = THIS_MODULE}, \
|
||||
.store = bd2802_store_reg##reg_addr, \
|
||||
};
|
||||
|
||||
BD2802_SET_REGISTER(0x00, "0x00");
|
||||
BD2802_SET_REGISTER(0x01, "0x01");
|
||||
BD2802_SET_REGISTER(0x02, "0x02");
|
||||
BD2802_SET_REGISTER(0x03, "0x03");
|
||||
BD2802_SET_REGISTER(0x04, "0x04");
|
||||
BD2802_SET_REGISTER(0x05, "0x05");
|
||||
BD2802_SET_REGISTER(0x06, "0x06");
|
||||
BD2802_SET_REGISTER(0x07, "0x07");
|
||||
BD2802_SET_REGISTER(0x08, "0x08");
|
||||
BD2802_SET_REGISTER(0x09, "0x09");
|
||||
BD2802_SET_REGISTER(0x0a, "0x0a");
|
||||
BD2802_SET_REGISTER(0x0b, "0x0b");
|
||||
BD2802_SET_REGISTER(0x0c, "0x0c");
|
||||
BD2802_SET_REGISTER(0x0d, "0x0d");
|
||||
BD2802_SET_REGISTER(0x0e, "0x0e");
|
||||
BD2802_SET_REGISTER(0x0f, "0x0f");
|
||||
BD2802_SET_REGISTER(0x10, "0x10");
|
||||
BD2802_SET_REGISTER(0x11, "0x11");
|
||||
BD2802_SET_REGISTER(0x12, "0x12");
|
||||
BD2802_SET_REGISTER(0x13, "0x13");
|
||||
BD2802_SET_REGISTER(0x14, "0x14");
|
||||
BD2802_SET_REGISTER(0x15, "0x15");
|
||||
|
||||
static struct device_attribute *bd2802_addr_attributes[] = {
|
||||
&bd2802_reg0x00_attr,
|
||||
&bd2802_reg0x01_attr,
|
||||
&bd2802_reg0x02_attr,
|
||||
&bd2802_reg0x03_attr,
|
||||
&bd2802_reg0x04_attr,
|
||||
&bd2802_reg0x05_attr,
|
||||
&bd2802_reg0x06_attr,
|
||||
&bd2802_reg0x07_attr,
|
||||
&bd2802_reg0x08_attr,
|
||||
&bd2802_reg0x09_attr,
|
||||
&bd2802_reg0x0a_attr,
|
||||
&bd2802_reg0x0b_attr,
|
||||
&bd2802_reg0x0c_attr,
|
||||
&bd2802_reg0x0d_attr,
|
||||
&bd2802_reg0x0e_attr,
|
||||
&bd2802_reg0x0f_attr,
|
||||
&bd2802_reg0x10_attr,
|
||||
&bd2802_reg0x11_attr,
|
||||
&bd2802_reg0x12_attr,
|
||||
&bd2802_reg0x13_attr,
|
||||
&bd2802_reg0x14_attr,
|
||||
&bd2802_reg0x15_attr,
|
||||
};
|
||||
|
||||
static void bd2802_enable_adv_conf(struct bd2802_led *led)
|
||||
{
|
||||
int i, ret;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(bd2802_addr_attributes); i++) {
|
||||
ret = device_create_file(&led->client->dev,
|
||||
bd2802_addr_attributes[i]);
|
||||
if (ret) {
|
||||
dev_err(&led->client->dev, "failed to sysfs file %s\n",
|
||||
bd2802_addr_attributes[i]->attr.name);
|
||||
goto failed_remove_files;
|
||||
}
|
||||
}
|
||||
|
||||
if (bd2802_is_all_off(led))
|
||||
bd2802_reset_cancel(led);
|
||||
|
||||
led->adf_on = 1;
|
||||
|
||||
return;
|
||||
|
||||
failed_remove_files:
|
||||
for (i--; i >= 0; i--)
|
||||
device_remove_file(&led->client->dev,
|
||||
bd2802_addr_attributes[i]);
|
||||
}
|
||||
|
||||
static void bd2802_disable_adv_conf(struct bd2802_led *led)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(bd2802_addr_attributes); i++)
|
||||
device_remove_file(&led->client->dev,
|
||||
bd2802_addr_attributes[i]);
|
||||
|
||||
if (bd2802_is_all_off(led))
|
||||
gpio_set_value(led->pdata->reset_gpio, 0);
|
||||
|
||||
led->adf_on = 0;
|
||||
}
|
||||
|
||||
static ssize_t bd2802_show_adv_conf(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct bd2802_led *led = i2c_get_clientdata(to_i2c_client(dev));
|
||||
ssize_t ret;
|
||||
|
||||
down_read(&led->rwsem);
|
||||
if (led->adf_on)
|
||||
ret = sprintf(buf, "on\n");
|
||||
else
|
||||
ret = sprintf(buf, "off\n");
|
||||
up_read(&led->rwsem);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t bd2802_store_adv_conf(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
struct bd2802_led *led = i2c_get_clientdata(to_i2c_client(dev));
|
||||
|
||||
if (!count)
|
||||
return -EINVAL;
|
||||
|
||||
down_write(&led->rwsem);
|
||||
if (!led->adf_on && !strncmp(buf, "on", 2))
|
||||
bd2802_enable_adv_conf(led);
|
||||
else if (led->adf_on && !strncmp(buf, "off", 3))
|
||||
bd2802_disable_adv_conf(led);
|
||||
up_write(&led->rwsem);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static struct device_attribute bd2802_adv_conf_attr = {
|
||||
.attr = {
|
||||
.name = "advanced_configuration",
|
||||
.mode = 0644,
|
||||
.owner = THIS_MODULE
|
||||
},
|
||||
.show = bd2802_show_adv_conf,
|
||||
.store = bd2802_store_adv_conf,
|
||||
};
|
||||
|
||||
static void bd2802_led_work(struct work_struct *work)
|
||||
{
|
||||
struct bd2802_led *led = container_of(work, struct bd2802_led, work);
|
||||
|
||||
if (led->state)
|
||||
bd2802_turn_on(led, led->led_id, led->color, led->state);
|
||||
else
|
||||
bd2802_turn_off(led, led->led_id, led->color);
|
||||
}
|
||||
|
||||
#define BD2802_CONTROL_RGBS(name, id, clr) \
|
||||
static void bd2802_set_##name##_brightness(struct led_classdev *led_cdev,\
|
||||
enum led_brightness value) \
|
||||
{ \
|
||||
struct bd2802_led *led = \
|
||||
container_of(led_cdev, struct bd2802_led, cdev_##name); \
|
||||
led->led_id = id; \
|
||||
led->color = clr; \
|
||||
if (value == LED_OFF) \
|
||||
led->state = BD2802_OFF; \
|
||||
else \
|
||||
led->state = BD2802_ON; \
|
||||
schedule_work(&led->work); \
|
||||
} \
|
||||
static int bd2802_set_##name##_blink(struct led_classdev *led_cdev, \
|
||||
unsigned long *delay_on, unsigned long *delay_off) \
|
||||
{ \
|
||||
struct bd2802_led *led = \
|
||||
container_of(led_cdev, struct bd2802_led, cdev_##name); \
|
||||
if (*delay_on == 0 || *delay_off == 0) \
|
||||
return -EINVAL; \
|
||||
led->led_id = id; \
|
||||
led->color = clr; \
|
||||
led->state = BD2802_BLINK; \
|
||||
schedule_work(&led->work); \
|
||||
return 0; \
|
||||
}
|
||||
|
||||
BD2802_CONTROL_RGBS(led1r, LED1, RED);
|
||||
BD2802_CONTROL_RGBS(led1g, LED1, GREEN);
|
||||
BD2802_CONTROL_RGBS(led1b, LED1, BLUE);
|
||||
BD2802_CONTROL_RGBS(led2r, LED2, RED);
|
||||
BD2802_CONTROL_RGBS(led2g, LED2, GREEN);
|
||||
BD2802_CONTROL_RGBS(led2b, LED2, BLUE);
|
||||
|
||||
static int bd2802_register_led_classdev(struct bd2802_led *led)
|
||||
{
|
||||
int ret;
|
||||
|
||||
INIT_WORK(&led->work, bd2802_led_work);
|
||||
|
||||
led->cdev_led1r.name = "led1_R";
|
||||
led->cdev_led1r.brightness = LED_OFF;
|
||||
led->cdev_led1r.brightness_set = bd2802_set_led1r_brightness;
|
||||
led->cdev_led1r.blink_set = bd2802_set_led1r_blink;
|
||||
led->cdev_led1r.flags |= LED_CORE_SUSPENDRESUME;
|
||||
|
||||
ret = led_classdev_register(&led->client->dev, &led->cdev_led1r);
|
||||
if (ret < 0) {
|
||||
dev_err(&led->client->dev, "couldn't register LED %s\n",
|
||||
led->cdev_led1r.name);
|
||||
goto failed_unregister_led1_R;
|
||||
}
|
||||
|
||||
led->cdev_led1g.name = "led1_G";
|
||||
led->cdev_led1g.brightness = LED_OFF;
|
||||
led->cdev_led1g.brightness_set = bd2802_set_led1g_brightness;
|
||||
led->cdev_led1g.blink_set = bd2802_set_led1g_blink;
|
||||
led->cdev_led1g.flags |= LED_CORE_SUSPENDRESUME;
|
||||
|
||||
ret = led_classdev_register(&led->client->dev, &led->cdev_led1g);
|
||||
if (ret < 0) {
|
||||
dev_err(&led->client->dev, "couldn't register LED %s\n",
|
||||
led->cdev_led1g.name);
|
||||
goto failed_unregister_led1_G;
|
||||
}
|
||||
|
||||
led->cdev_led1b.name = "led1_B";
|
||||
led->cdev_led1b.brightness = LED_OFF;
|
||||
led->cdev_led1b.brightness_set = bd2802_set_led1b_brightness;
|
||||
led->cdev_led1b.blink_set = bd2802_set_led1b_blink;
|
||||
led->cdev_led1b.flags |= LED_CORE_SUSPENDRESUME;
|
||||
|
||||
ret = led_classdev_register(&led->client->dev, &led->cdev_led1b);
|
||||
if (ret < 0) {
|
||||
dev_err(&led->client->dev, "couldn't register LED %s\n",
|
||||
led->cdev_led1b.name);
|
||||
goto failed_unregister_led1_B;
|
||||
}
|
||||
|
||||
led->cdev_led2r.name = "led2_R";
|
||||
led->cdev_led2r.brightness = LED_OFF;
|
||||
led->cdev_led2r.brightness_set = bd2802_set_led2r_brightness;
|
||||
led->cdev_led2r.blink_set = bd2802_set_led2r_blink;
|
||||
led->cdev_led2r.flags |= LED_CORE_SUSPENDRESUME;
|
||||
|
||||
ret = led_classdev_register(&led->client->dev, &led->cdev_led2r);
|
||||
if (ret < 0) {
|
||||
dev_err(&led->client->dev, "couldn't register LED %s\n",
|
||||
led->cdev_led2r.name);
|
||||
goto failed_unregister_led2_R;
|
||||
}
|
||||
|
||||
led->cdev_led2g.name = "led2_G";
|
||||
led->cdev_led2g.brightness = LED_OFF;
|
||||
led->cdev_led2g.brightness_set = bd2802_set_led2g_brightness;
|
||||
led->cdev_led2g.blink_set = bd2802_set_led2g_blink;
|
||||
led->cdev_led2g.flags |= LED_CORE_SUSPENDRESUME;
|
||||
|
||||
ret = led_classdev_register(&led->client->dev, &led->cdev_led2g);
|
||||
if (ret < 0) {
|
||||
dev_err(&led->client->dev, "couldn't register LED %s\n",
|
||||
led->cdev_led2g.name);
|
||||
goto failed_unregister_led2_G;
|
||||
}
|
||||
|
||||
led->cdev_led2b.name = "led2_B";
|
||||
led->cdev_led2b.brightness = LED_OFF;
|
||||
led->cdev_led2b.brightness_set = bd2802_set_led2b_brightness;
|
||||
led->cdev_led2b.blink_set = bd2802_set_led2b_blink;
|
||||
led->cdev_led2b.flags |= LED_CORE_SUSPENDRESUME;
|
||||
|
||||
ret = led_classdev_register(&led->client->dev, &led->cdev_led2b);
|
||||
if (ret < 0) {
|
||||
dev_err(&led->client->dev, "couldn't register LED %s\n",
|
||||
led->cdev_led2b.name);
|
||||
goto failed_unregister_led2_B;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
failed_unregister_led2_B:
|
||||
led_classdev_unregister(&led->cdev_led2g);
|
||||
failed_unregister_led2_G:
|
||||
led_classdev_unregister(&led->cdev_led2r);
|
||||
failed_unregister_led2_R:
|
||||
led_classdev_unregister(&led->cdev_led1b);
|
||||
failed_unregister_led1_B:
|
||||
led_classdev_unregister(&led->cdev_led1g);
|
||||
failed_unregister_led1_G:
|
||||
led_classdev_unregister(&led->cdev_led1r);
|
||||
failed_unregister_led1_R:
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void bd2802_unregister_led_classdev(struct bd2802_led *led)
|
||||
{
|
||||
cancel_work_sync(&led->work);
|
||||
led_classdev_unregister(&led->cdev_led1r);
|
||||
}
|
||||
|
||||
static int __devinit bd2802_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct bd2802_led *led;
|
||||
struct bd2802_led_platform_data *pdata;
|
||||
int ret;
|
||||
|
||||
led = kzalloc(sizeof(struct bd2802_led), GFP_KERNEL);
|
||||
if (!led) {
|
||||
dev_err(&client->dev, "failed to allocate driver data\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
led->client = client;
|
||||
pdata = led->pdata = client->dev.platform_data;
|
||||
i2c_set_clientdata(client, led);
|
||||
|
||||
/* Configure RESET GPIO (L: RESET, H: RESET cancel) */
|
||||
gpio_request(pdata->reset_gpio, "RGB_RESETB");
|
||||
gpio_direction_output(pdata->reset_gpio, 1);
|
||||
|
||||
/* Tacss = min 0.1ms */
|
||||
udelay(100);
|
||||
|
||||
/* Detect BD2802GU */
|
||||
ret = bd2802_write_byte(client, BD2802_REG_CLKSETUP, 0x00);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "failed to detect device\n");
|
||||
goto failed_free;
|
||||
} else
|
||||
dev_info(&client->dev, "return 0x%02x\n", ret);
|
||||
|
||||
/* To save the power, reset BD2802 after detecting */
|
||||
gpio_set_value(led->pdata->reset_gpio, 0);
|
||||
|
||||
init_rwsem(&led->rwsem);
|
||||
|
||||
ret = device_create_file(&client->dev, &bd2802_adv_conf_attr);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "failed to create sysfs file %s\n",
|
||||
bd2802_adv_conf_attr.attr.name);
|
||||
goto failed_free;
|
||||
}
|
||||
|
||||
ret = bd2802_register_led_classdev(led);
|
||||
if (ret < 0)
|
||||
goto failed_unregister_dev_file;
|
||||
|
||||
return 0;
|
||||
|
||||
failed_unregister_dev_file:
|
||||
device_remove_file(&client->dev, &bd2802_adv_conf_attr);
|
||||
failed_free:
|
||||
i2c_set_clientdata(client, NULL);
|
||||
kfree(led);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __exit bd2802_remove(struct i2c_client *client)
|
||||
{
|
||||
struct bd2802_led *led = i2c_get_clientdata(client);
|
||||
|
||||
bd2802_unregister_led_classdev(led);
|
||||
gpio_set_value(led->pdata->reset_gpio, 0);
|
||||
if (led->adf_on)
|
||||
bd2802_disable_adv_conf(led);
|
||||
device_remove_file(&client->dev, &bd2802_adv_conf_attr);
|
||||
i2c_set_clientdata(client, NULL);
|
||||
kfree(led);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bd2802_suspend(struct i2c_client *client, pm_message_t mesg)
|
||||
{
|
||||
struct bd2802_led *led = i2c_get_clientdata(client);
|
||||
|
||||
gpio_set_value(led->pdata->reset_gpio, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bd2802_resume(struct i2c_client *client)
|
||||
{
|
||||
struct bd2802_led *led = i2c_get_clientdata(client);
|
||||
|
||||
if (!bd2802_is_all_off(led) || led->adf_on) {
|
||||
gpio_set_value(led->pdata->reset_gpio, 1);
|
||||
udelay(100);
|
||||
bd2802_restore_state(led);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id bd2802_id[] = {
|
||||
{ "BD2802", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, bd2802_id);
|
||||
|
||||
static struct i2c_driver bd2802_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "BD2802",
|
||||
},
|
||||
.probe = bd2802_probe,
|
||||
.remove = __exit_p(bd2802_remove),
|
||||
.suspend = bd2802_suspend,
|
||||
.resume = bd2802_resume,
|
||||
.id_table = bd2802_id,
|
||||
};
|
||||
|
||||
static int __init bd2802_init(void)
|
||||
{
|
||||
return i2c_add_driver(&bd2802_i2c_driver);
|
||||
}
|
||||
module_init(bd2802_init);
|
||||
|
||||
static void __exit bd2802_exit(void)
|
||||
{
|
||||
i2c_del_driver(&bd2802_i2c_driver);
|
||||
}
|
||||
module_exit(bd2802_exit);
|
||||
|
||||
MODULE_AUTHOR("Kim Kyuwon <q1.kim@samsung.com>");
|
||||
MODULE_DESCRIPTION("BD2802 LED driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -0,0 +1,150 @@
|
|||
/*
|
||||
* Copyright 2008
|
||||
* Guennadi Liakhovetski, DENX Software Engineering, <lg@denx.de>
|
||||
*
|
||||
* This file is subject to the terms and conditions of version 2 of
|
||||
* the GNU General Public License. See the file COPYING in the main
|
||||
* directory of this archive for more details.
|
||||
*
|
||||
* LED driver for the DAC124S085 SPI DAC
|
||||
*/
|
||||
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
struct dac124s085_led {
|
||||
struct led_classdev ldev;
|
||||
struct spi_device *spi;
|
||||
int id;
|
||||
int brightness;
|
||||
char name[sizeof("dac124s085-3")];
|
||||
|
||||
struct mutex mutex;
|
||||
struct work_struct work;
|
||||
spinlock_t lock;
|
||||
};
|
||||
|
||||
struct dac124s085 {
|
||||
struct dac124s085_led leds[4];
|
||||
};
|
||||
|
||||
#define REG_WRITE (0 << 12)
|
||||
#define REG_WRITE_UPDATE (1 << 12)
|
||||
#define ALL_WRITE_UPDATE (2 << 12)
|
||||
#define POWER_DOWN_OUTPUT (3 << 12)
|
||||
|
||||
static void dac124s085_led_work(struct work_struct *work)
|
||||
{
|
||||
struct dac124s085_led *led = container_of(work, struct dac124s085_led,
|
||||
work);
|
||||
u16 word;
|
||||
|
||||
mutex_lock(&led->mutex);
|
||||
word = cpu_to_le16(((led->id) << 14) | REG_WRITE_UPDATE |
|
||||
(led->brightness & 0xfff));
|
||||
spi_write(led->spi, (const u8 *)&word, sizeof(word));
|
||||
mutex_unlock(&led->mutex);
|
||||
}
|
||||
|
||||
static void dac124s085_set_brightness(struct led_classdev *ldev,
|
||||
enum led_brightness brightness)
|
||||
{
|
||||
struct dac124s085_led *led = container_of(ldev, struct dac124s085_led,
|
||||
ldev);
|
||||
|
||||
spin_lock(&led->lock);
|
||||
led->brightness = brightness;
|
||||
schedule_work(&led->work);
|
||||
spin_unlock(&led->lock);
|
||||
}
|
||||
|
||||
static int dac124s085_probe(struct spi_device *spi)
|
||||
{
|
||||
struct dac124s085 *dac;
|
||||
struct dac124s085_led *led;
|
||||
int i, ret;
|
||||
|
||||
dac = kzalloc(sizeof(*dac), GFP_KERNEL);
|
||||
if (!dac)
|
||||
return -ENOMEM;
|
||||
|
||||
spi->bits_per_word = 16;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(dac->leds); i++) {
|
||||
led = dac->leds + i;
|
||||
led->id = i;
|
||||
led->brightness = LED_OFF;
|
||||
led->spi = spi;
|
||||
snprintf(led->name, sizeof(led->name), "dac124s085-%d", i);
|
||||
spin_lock_init(&led->lock);
|
||||
INIT_WORK(&led->work, dac124s085_led_work);
|
||||
mutex_init(&led->mutex);
|
||||
led->ldev.name = led->name;
|
||||
led->ldev.brightness = LED_OFF;
|
||||
led->ldev.max_brightness = 0xfff;
|
||||
led->ldev.brightness_set = dac124s085_set_brightness;
|
||||
ret = led_classdev_register(&spi->dev, &led->ldev);
|
||||
if (ret < 0)
|
||||
goto eledcr;
|
||||
}
|
||||
|
||||
spi_set_drvdata(spi, dac);
|
||||
|
||||
return 0;
|
||||
|
||||
eledcr:
|
||||
while (i--)
|
||||
led_classdev_unregister(&dac->leds[i].ldev);
|
||||
|
||||
spi_set_drvdata(spi, NULL);
|
||||
kfree(dac);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dac124s085_remove(struct spi_device *spi)
|
||||
{
|
||||
struct dac124s085 *dac = spi_get_drvdata(spi);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(dac->leds); i++) {
|
||||
led_classdev_unregister(&dac->leds[i].ldev);
|
||||
cancel_work_sync(&dac->leds[i].work);
|
||||
}
|
||||
|
||||
spi_set_drvdata(spi, NULL);
|
||||
kfree(dac);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct spi_driver dac124s085_driver = {
|
||||
.probe = dac124s085_probe,
|
||||
.remove = dac124s085_remove,
|
||||
.driver = {
|
||||
.name = "dac124s085",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init dac124s085_leds_init(void)
|
||||
{
|
||||
return spi_register_driver(&dac124s085_driver);
|
||||
}
|
||||
|
||||
static void __exit dac124s085_leds_exit(void)
|
||||
{
|
||||
spi_unregister_driver(&dac124s085_driver);
|
||||
}
|
||||
|
||||
module_init(dac124s085_leds_init);
|
||||
module_exit(dac124s085_leds_exit);
|
||||
|
||||
MODULE_AUTHOR("Guennadi Liakhovetski <lg@denx.de>");
|
||||
MODULE_DESCRIPTION("DAC124S085 LED driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -3,6 +3,7 @@
|
|||
*
|
||||
* Copyright (C) 2007 8D Technologies inc.
|
||||
* Raphael Assenat <raph@8d.com>
|
||||
* Copyright (C) 2008 Freescale Semiconductor, Inc.
|
||||
*
|
||||
* 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
|
||||
|
@ -71,11 +72,67 @@ static int gpio_blink_set(struct led_classdev *led_cdev,
|
|||
return led_dat->platform_gpio_blink_set(led_dat->gpio, delay_on, delay_off);
|
||||
}
|
||||
|
||||
static int __devinit create_gpio_led(const struct gpio_led *template,
|
||||
struct gpio_led_data *led_dat, struct device *parent,
|
||||
int (*blink_set)(unsigned, unsigned long *, unsigned long *))
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* skip leds that aren't available */
|
||||
if (!gpio_is_valid(template->gpio)) {
|
||||
printk(KERN_INFO "Skipping unavilable LED gpio %d (%s)\n",
|
||||
template->gpio, template->name);
|
||||
return;
|
||||
}
|
||||
|
||||
ret = gpio_request(template->gpio, template->name);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
led_dat->cdev.name = template->name;
|
||||
led_dat->cdev.default_trigger = template->default_trigger;
|
||||
led_dat->gpio = template->gpio;
|
||||
led_dat->can_sleep = gpio_cansleep(template->gpio);
|
||||
led_dat->active_low = template->active_low;
|
||||
if (blink_set) {
|
||||
led_dat->platform_gpio_blink_set = blink_set;
|
||||
led_dat->cdev.blink_set = gpio_blink_set;
|
||||
}
|
||||
led_dat->cdev.brightness_set = gpio_led_set;
|
||||
led_dat->cdev.brightness = LED_OFF;
|
||||
if (!template->retain_state_suspended)
|
||||
led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME;
|
||||
|
||||
ret = gpio_direction_output(led_dat->gpio, led_dat->active_low);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
INIT_WORK(&led_dat->work, gpio_led_work);
|
||||
|
||||
ret = led_classdev_register(parent, &led_dat->cdev);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
return 0;
|
||||
err:
|
||||
gpio_free(led_dat->gpio);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void delete_gpio_led(struct gpio_led_data *led)
|
||||
{
|
||||
if (!gpio_is_valid(led->gpio))
|
||||
return;
|
||||
led_classdev_unregister(&led->cdev);
|
||||
cancel_work_sync(&led->work);
|
||||
gpio_free(led->gpio);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_LEDS_GPIO_PLATFORM
|
||||
static int gpio_led_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct gpio_led_platform_data *pdata = pdev->dev.platform_data;
|
||||
struct gpio_led *cur_led;
|
||||
struct gpio_led_data *leds_data, *led_dat;
|
||||
struct gpio_led_data *leds_data;
|
||||
int i, ret = 0;
|
||||
|
||||
if (!pdata)
|
||||
|
@ -87,35 +144,10 @@ static int gpio_led_probe(struct platform_device *pdev)
|
|||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < pdata->num_leds; i++) {
|
||||
cur_led = &pdata->leds[i];
|
||||
led_dat = &leds_data[i];
|
||||
|
||||
ret = gpio_request(cur_led->gpio, cur_led->name);
|
||||
ret = create_gpio_led(&pdata->leds[i], &leds_data[i],
|
||||
&pdev->dev, pdata->gpio_blink_set);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
led_dat->cdev.name = cur_led->name;
|
||||
led_dat->cdev.default_trigger = cur_led->default_trigger;
|
||||
led_dat->gpio = cur_led->gpio;
|
||||
led_dat->can_sleep = gpio_cansleep(cur_led->gpio);
|
||||
led_dat->active_low = cur_led->active_low;
|
||||
if (pdata->gpio_blink_set) {
|
||||
led_dat->platform_gpio_blink_set = pdata->gpio_blink_set;
|
||||
led_dat->cdev.blink_set = gpio_blink_set;
|
||||
}
|
||||
led_dat->cdev.brightness_set = gpio_led_set;
|
||||
led_dat->cdev.brightness = LED_OFF;
|
||||
led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME;
|
||||
|
||||
gpio_direction_output(led_dat->gpio, led_dat->active_low);
|
||||
|
||||
INIT_WORK(&led_dat->work, gpio_led_work);
|
||||
|
||||
ret = led_classdev_register(&pdev->dev, &led_dat->cdev);
|
||||
if (ret < 0) {
|
||||
gpio_free(led_dat->gpio);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, leds_data);
|
||||
|
@ -123,13 +155,8 @@ static int gpio_led_probe(struct platform_device *pdev)
|
|||
return 0;
|
||||
|
||||
err:
|
||||
if (i > 0) {
|
||||
for (i = i - 1; i >= 0; i--) {
|
||||
led_classdev_unregister(&leds_data[i].cdev);
|
||||
cancel_work_sync(&leds_data[i].work);
|
||||
gpio_free(leds_data[i].gpio);
|
||||
}
|
||||
}
|
||||
for (i = i - 1; i >= 0; i--)
|
||||
delete_gpio_led(&leds_data[i]);
|
||||
|
||||
kfree(leds_data);
|
||||
|
||||
|
@ -144,11 +171,8 @@ static int __devexit gpio_led_remove(struct platform_device *pdev)
|
|||
|
||||
leds_data = platform_get_drvdata(pdev);
|
||||
|
||||
for (i = 0; i < pdata->num_leds; i++) {
|
||||
led_classdev_unregister(&leds_data[i].cdev);
|
||||
cancel_work_sync(&leds_data[i].work);
|
||||
gpio_free(leds_data[i].gpio);
|
||||
}
|
||||
for (i = 0; i < pdata->num_leds; i++)
|
||||
delete_gpio_led(&leds_data[i]);
|
||||
|
||||
kfree(leds_data);
|
||||
|
||||
|
@ -164,20 +188,133 @@ static struct platform_driver gpio_led_driver = {
|
|||
},
|
||||
};
|
||||
|
||||
MODULE_ALIAS("platform:leds-gpio");
|
||||
#endif /* CONFIG_LEDS_GPIO_PLATFORM */
|
||||
|
||||
/* Code to create from OpenFirmware platform devices */
|
||||
#ifdef CONFIG_LEDS_GPIO_OF
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/of_gpio.h>
|
||||
|
||||
struct gpio_led_of_platform_data {
|
||||
int num_leds;
|
||||
struct gpio_led_data led_data[];
|
||||
};
|
||||
|
||||
static int __devinit of_gpio_leds_probe(struct of_device *ofdev,
|
||||
const struct of_device_id *match)
|
||||
{
|
||||
struct device_node *np = ofdev->node, *child;
|
||||
struct gpio_led led;
|
||||
struct gpio_led_of_platform_data *pdata;
|
||||
int count = 0, ret;
|
||||
|
||||
/* count LEDs defined by this device, so we know how much to allocate */
|
||||
for_each_child_of_node(np, child)
|
||||
count++;
|
||||
if (!count)
|
||||
return 0; /* or ENODEV? */
|
||||
|
||||
pdata = kzalloc(sizeof(*pdata) + sizeof(struct gpio_led_data) * count,
|
||||
GFP_KERNEL);
|
||||
if (!pdata)
|
||||
return -ENOMEM;
|
||||
|
||||
memset(&led, 0, sizeof(led));
|
||||
for_each_child_of_node(np, child) {
|
||||
enum of_gpio_flags flags;
|
||||
|
||||
led.gpio = of_get_gpio_flags(child, 0, &flags);
|
||||
led.active_low = flags & OF_GPIO_ACTIVE_LOW;
|
||||
led.name = of_get_property(child, "label", NULL) ? : child->name;
|
||||
led.default_trigger =
|
||||
of_get_property(child, "linux,default-trigger", NULL);
|
||||
|
||||
ret = create_gpio_led(&led, &pdata->led_data[pdata->num_leds++],
|
||||
&ofdev->dev, NULL);
|
||||
if (ret < 0) {
|
||||
of_node_put(child);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
dev_set_drvdata(&ofdev->dev, pdata);
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
for (count = pdata->num_leds - 2; count >= 0; count--)
|
||||
delete_gpio_led(&pdata->led_data[count]);
|
||||
|
||||
kfree(pdata);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devexit of_gpio_leds_remove(struct of_device *ofdev)
|
||||
{
|
||||
struct gpio_led_of_platform_data *pdata = dev_get_drvdata(&ofdev->dev);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < pdata->num_leds; i++)
|
||||
delete_gpio_led(&pdata->led_data[i]);
|
||||
|
||||
kfree(pdata);
|
||||
|
||||
dev_set_drvdata(&ofdev->dev, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id of_gpio_leds_match[] = {
|
||||
{ .compatible = "gpio-leds", },
|
||||
{},
|
||||
};
|
||||
|
||||
static struct of_platform_driver of_gpio_leds_driver = {
|
||||
.driver = {
|
||||
.name = "of_gpio_leds",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.match_table = of_gpio_leds_match,
|
||||
.probe = of_gpio_leds_probe,
|
||||
.remove = __devexit_p(of_gpio_leds_remove),
|
||||
};
|
||||
#endif
|
||||
|
||||
static int __init gpio_led_init(void)
|
||||
{
|
||||
return platform_driver_register(&gpio_led_driver);
|
||||
int ret;
|
||||
|
||||
#ifdef CONFIG_LEDS_GPIO_PLATFORM
|
||||
ret = platform_driver_register(&gpio_led_driver);
|
||||
if (ret)
|
||||
return ret;
|
||||
#endif
|
||||
#ifdef CONFIG_LEDS_GPIO_OF
|
||||
ret = of_register_platform_driver(&of_gpio_leds_driver);
|
||||
#endif
|
||||
#ifdef CONFIG_LEDS_GPIO_PLATFORM
|
||||
if (ret)
|
||||
platform_driver_unregister(&gpio_led_driver);
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit gpio_led_exit(void)
|
||||
{
|
||||
#ifdef CONFIG_LEDS_GPIO_PLATFORM
|
||||
platform_driver_unregister(&gpio_led_driver);
|
||||
#endif
|
||||
#ifdef CONFIG_LEDS_GPIO_OF
|
||||
of_unregister_platform_driver(&of_gpio_leds_driver);
|
||||
#endif
|
||||
}
|
||||
|
||||
module_init(gpio_led_init);
|
||||
module_exit(gpio_led_exit);
|
||||
|
||||
MODULE_AUTHOR("Raphael Assenat <raph@8d.com>");
|
||||
MODULE_AUTHOR("Raphael Assenat <raph@8d.com>, Trent Piepho <tpiepho@freescale.com>");
|
||||
MODULE_DESCRIPTION("GPIO LED driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:leds-gpio");
|
||||
|
|
|
@ -104,7 +104,7 @@ static struct led_classdev h1940_blueled = {
|
|||
.default_trigger = "h1940-bluetooth",
|
||||
};
|
||||
|
||||
static int __init h1940leds_probe(struct platform_device *pdev)
|
||||
static int __devinit h1940leds_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
|
|
|
@ -169,7 +169,7 @@ static int pca9532_event(struct input_dev *dev, unsigned int type,
|
|||
{
|
||||
struct pca9532_data *data = input_get_drvdata(dev);
|
||||
|
||||
if (type != EV_SND && (code != SND_BELL || code != SND_TONE))
|
||||
if (!(type == EV_SND && (code == SND_BELL || code == SND_TONE)))
|
||||
return -1;
|
||||
|
||||
/* XXX: allow different kind of beeps with psc/pwm modifications */
|
||||
|
|
|
@ -0,0 +1,153 @@
|
|||
/*
|
||||
* linux/drivers/leds-pwm.c
|
||||
*
|
||||
* simple PWM based LED control
|
||||
*
|
||||
* Copyright 2009 Luotao Fu @ Pengutronix (l.fu@pengutronix.de)
|
||||
*
|
||||
* based on leds-gpio.c by Raphael Assenat <raph@8d.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/platform_device.h>
|
||||
#include <linux/fb.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/pwm.h>
|
||||
#include <linux/leds_pwm.h>
|
||||
|
||||
struct led_pwm_data {
|
||||
struct led_classdev cdev;
|
||||
struct pwm_device *pwm;
|
||||
unsigned int active_low;
|
||||
unsigned int period;
|
||||
unsigned int max_brightness;
|
||||
};
|
||||
|
||||
static void led_pwm_set(struct led_classdev *led_cdev,
|
||||
enum led_brightness brightness)
|
||||
{
|
||||
struct led_pwm_data *led_dat =
|
||||
container_of(led_cdev, struct led_pwm_data, cdev);
|
||||
unsigned int max = led_dat->max_brightness;
|
||||
unsigned int period = led_dat->period;
|
||||
|
||||
if (brightness == 0) {
|
||||
pwm_config(led_dat->pwm, 0, period);
|
||||
pwm_disable(led_dat->pwm);
|
||||
} else {
|
||||
pwm_config(led_dat->pwm, brightness * period / max, period);
|
||||
pwm_enable(led_dat->pwm);
|
||||
}
|
||||
}
|
||||
|
||||
static int led_pwm_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct led_pwm_platform_data *pdata = pdev->dev.platform_data;
|
||||
struct led_pwm *cur_led;
|
||||
struct led_pwm_data *leds_data, *led_dat;
|
||||
int i, ret = 0;
|
||||
|
||||
if (!pdata)
|
||||
return -EBUSY;
|
||||
|
||||
leds_data = kzalloc(sizeof(struct led_pwm_data) * pdata->num_leds,
|
||||
GFP_KERNEL);
|
||||
if (!leds_data)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < pdata->num_leds; i++) {
|
||||
cur_led = &pdata->leds[i];
|
||||
led_dat = &leds_data[i];
|
||||
|
||||
led_dat->pwm = pwm_request(cur_led->pwm_id,
|
||||
cur_led->name);
|
||||
if (IS_ERR(led_dat->pwm)) {
|
||||
dev_err(&pdev->dev, "unable to request PWM %d\n",
|
||||
cur_led->pwm_id);
|
||||
goto err;
|
||||
}
|
||||
|
||||
led_dat->cdev.name = cur_led->name;
|
||||
led_dat->cdev.default_trigger = cur_led->default_trigger;
|
||||
led_dat->active_low = cur_led->active_low;
|
||||
led_dat->max_brightness = cur_led->max_brightness;
|
||||
led_dat->period = cur_led->pwm_period_ns;
|
||||
led_dat->cdev.brightness_set = led_pwm_set;
|
||||
led_dat->cdev.brightness = LED_OFF;
|
||||
led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME;
|
||||
|
||||
ret = led_classdev_register(&pdev->dev, &led_dat->cdev);
|
||||
if (ret < 0) {
|
||||
pwm_free(led_dat->pwm);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, leds_data);
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
if (i > 0) {
|
||||
for (i = i - 1; i >= 0; i--) {
|
||||
led_classdev_unregister(&leds_data[i].cdev);
|
||||
pwm_free(leds_data[i].pwm);
|
||||
}
|
||||
}
|
||||
|
||||
kfree(leds_data);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devexit led_pwm_remove(struct platform_device *pdev)
|
||||
{
|
||||
int i;
|
||||
struct led_pwm_platform_data *pdata = pdev->dev.platform_data;
|
||||
struct led_pwm_data *leds_data;
|
||||
|
||||
leds_data = platform_get_drvdata(pdev);
|
||||
|
||||
for (i = 0; i < pdata->num_leds; i++) {
|
||||
led_classdev_unregister(&leds_data[i].cdev);
|
||||
pwm_free(leds_data[i].pwm);
|
||||
}
|
||||
|
||||
kfree(leds_data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver led_pwm_driver = {
|
||||
.probe = led_pwm_probe,
|
||||
.remove = __devexit_p(led_pwm_remove),
|
||||
.driver = {
|
||||
.name = "leds_pwm",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init led_pwm_init(void)
|
||||
{
|
||||
return platform_driver_register(&led_pwm_driver);
|
||||
}
|
||||
|
||||
static void __exit led_pwm_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&led_pwm_driver);
|
||||
}
|
||||
|
||||
module_init(led_pwm_init);
|
||||
module_exit(led_pwm_exit);
|
||||
|
||||
MODULE_AUTHOR("Luotao Fu <l.fu@pengutronix.de>");
|
||||
MODULE_DESCRIPTION("PWM LED driver for PXA");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:leds-pwm");
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* LEDs driver for the "User LED" on Routerboard532
|
||||
*
|
||||
* Copyright (C) 2009 Phil Sutter <n0-1@freewrt.org>
|
||||
*
|
||||
* Based on leds-cobalt-qube.c by Florian Fainelly and
|
||||
* rb-diag.c (my own standalone driver for both LED and
|
||||
* button of Routerboard532).
|
||||
*/
|
||||
|
||||
#include <linux/leds.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include <asm/mach-rc32434/gpio.h>
|
||||
#include <asm/mach-rc32434/rb.h>
|
||||
|
||||
static void rb532_led_set(struct led_classdev *cdev,
|
||||
enum led_brightness brightness)
|
||||
{
|
||||
if (brightness)
|
||||
set_latch_u5(LO_ULED, 0);
|
||||
|
||||
else
|
||||
set_latch_u5(0, LO_ULED);
|
||||
}
|
||||
|
||||
static enum led_brightness rb532_led_get(struct led_classdev *cdev)
|
||||
{
|
||||
return (get_latch_u5() & LO_ULED) ? LED_FULL : LED_OFF;
|
||||
}
|
||||
|
||||
static struct led_classdev rb532_uled = {
|
||||
.name = "uled",
|
||||
.brightness_set = rb532_led_set,
|
||||
.brightness_get = rb532_led_get,
|
||||
.default_trigger = "nand-disk",
|
||||
};
|
||||
|
||||
static int __devinit rb532_led_probe(struct platform_device *pdev)
|
||||
{
|
||||
return led_classdev_register(&pdev->dev, &rb532_uled);
|
||||
}
|
||||
|
||||
static int __devexit rb532_led_remove(struct platform_device *pdev)
|
||||
{
|
||||
led_classdev_unregister(&rb532_uled);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver rb532_led_driver = {
|
||||
.probe = rb532_led_probe,
|
||||
.remove = __devexit_p(rb532_led_remove),
|
||||
.driver = {
|
||||
.name = "rb532-led",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init rb532_led_init(void)
|
||||
{
|
||||
return platform_driver_register(&rb532_led_driver);
|
||||
}
|
||||
|
||||
static void __exit rb532_led_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&rb532_led_driver);
|
||||
}
|
||||
|
||||
module_init(rb532_led_init);
|
||||
module_exit(rb532_led_exit);
|
||||
|
||||
MODULE_ALIAS("platform:rb532-led");
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("User LED support for Routerboard532");
|
||||
MODULE_AUTHOR("Phil Sutter <n0-1@freewrt.org>");
|
|
@ -102,14 +102,11 @@ static int s3c24xx_led_probe(struct platform_device *dev)
|
|||
ret = led_classdev_register(&dev->dev, &led->cdev);
|
||||
if (ret < 0) {
|
||||
dev_err(&dev->dev, "led_classdev_register failed\n");
|
||||
goto exit_err1;
|
||||
kfree(led);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
exit_err1:
|
||||
kfree(led);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct platform_driver s3c24xx_led_driver = {
|
||||
|
|
|
@ -20,8 +20,8 @@
|
|||
static inline void led_set_brightness(struct led_classdev *led_cdev,
|
||||
enum led_brightness value)
|
||||
{
|
||||
if (value > LED_FULL)
|
||||
value = LED_FULL;
|
||||
if (value > led_cdev->max_brightness)
|
||||
value = led_cdev->max_brightness;
|
||||
led_cdev->brightness = value;
|
||||
if (!(led_cdev->flags & LED_SUSPENDED))
|
||||
led_cdev->brightness_set(led_cdev, value);
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
static void defon_trig_activate(struct led_classdev *led_cdev)
|
||||
{
|
||||
led_set_brightness(led_cdev, LED_FULL);
|
||||
led_set_brightness(led_cdev, led_cdev->max_brightness);
|
||||
}
|
||||
|
||||
static struct led_trigger defon_led_trigger = {
|
||||
|
|
|
@ -0,0 +1,239 @@
|
|||
/*
|
||||
* ledtrig-gio.c - LED Trigger Based on GPIO events
|
||||
*
|
||||
* Copyright 2009 Felipe Balbi <me@felipebalbi.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/gpio.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/leds.h>
|
||||
#include "leds.h"
|
||||
|
||||
struct gpio_trig_data {
|
||||
struct led_classdev *led;
|
||||
struct work_struct work;
|
||||
|
||||
unsigned desired_brightness; /* desired brightness when led is on */
|
||||
unsigned inverted; /* true when gpio is inverted */
|
||||
unsigned gpio; /* gpio that triggers the leds */
|
||||
};
|
||||
|
||||
static irqreturn_t gpio_trig_irq(int irq, void *_led)
|
||||
{
|
||||
struct led_classdev *led = _led;
|
||||
struct gpio_trig_data *gpio_data = led->trigger_data;
|
||||
|
||||
/* just schedule_work since gpio_get_value can sleep */
|
||||
schedule_work(&gpio_data->work);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
};
|
||||
|
||||
static void gpio_trig_work(struct work_struct *work)
|
||||
{
|
||||
struct gpio_trig_data *gpio_data = container_of(work,
|
||||
struct gpio_trig_data, work);
|
||||
int tmp;
|
||||
|
||||
if (!gpio_data->gpio)
|
||||
return;
|
||||
|
||||
tmp = gpio_get_value(gpio_data->gpio);
|
||||
if (gpio_data->inverted)
|
||||
tmp = !tmp;
|
||||
|
||||
if (tmp) {
|
||||
if (gpio_data->desired_brightness)
|
||||
led_set_brightness(gpio_data->led,
|
||||
gpio_data->desired_brightness);
|
||||
else
|
||||
led_set_brightness(gpio_data->led, LED_FULL);
|
||||
} else {
|
||||
led_set_brightness(gpio_data->led, LED_OFF);
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t gpio_trig_brightness_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct led_classdev *led = dev_get_drvdata(dev);
|
||||
struct gpio_trig_data *gpio_data = led->trigger_data;
|
||||
|
||||
return sprintf(buf, "%u\n", gpio_data->desired_brightness);
|
||||
}
|
||||
|
||||
static ssize_t gpio_trig_brightness_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t n)
|
||||
{
|
||||
struct led_classdev *led = dev_get_drvdata(dev);
|
||||
struct gpio_trig_data *gpio_data = led->trigger_data;
|
||||
unsigned desired_brightness;
|
||||
int ret;
|
||||
|
||||
ret = sscanf(buf, "%u", &desired_brightness);
|
||||
if (ret < 1 || desired_brightness > 255) {
|
||||
dev_err(dev, "invalid value\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
gpio_data->desired_brightness = desired_brightness;
|
||||
|
||||
return n;
|
||||
}
|
||||
static DEVICE_ATTR(desired_brightness, 0644, gpio_trig_brightness_show,
|
||||
gpio_trig_brightness_store);
|
||||
|
||||
static ssize_t gpio_trig_inverted_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct led_classdev *led = dev_get_drvdata(dev);
|
||||
struct gpio_trig_data *gpio_data = led->trigger_data;
|
||||
|
||||
return sprintf(buf, "%s\n", gpio_data->inverted ? "yes" : "no");
|
||||
}
|
||||
|
||||
static ssize_t gpio_trig_inverted_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t n)
|
||||
{
|
||||
struct led_classdev *led = dev_get_drvdata(dev);
|
||||
struct gpio_trig_data *gpio_data = led->trigger_data;
|
||||
unsigned inverted;
|
||||
int ret;
|
||||
|
||||
ret = sscanf(buf, "%u", &inverted);
|
||||
if (ret < 1) {
|
||||
dev_err(dev, "invalid value\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
gpio_data->inverted = !!inverted;
|
||||
|
||||
return n;
|
||||
}
|
||||
static DEVICE_ATTR(inverted, 0644, gpio_trig_inverted_show,
|
||||
gpio_trig_inverted_store);
|
||||
|
||||
static ssize_t gpio_trig_gpio_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct led_classdev *led = dev_get_drvdata(dev);
|
||||
struct gpio_trig_data *gpio_data = led->trigger_data;
|
||||
|
||||
return sprintf(buf, "%u\n", gpio_data->gpio);
|
||||
}
|
||||
|
||||
static ssize_t gpio_trig_gpio_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t n)
|
||||
{
|
||||
struct led_classdev *led = dev_get_drvdata(dev);
|
||||
struct gpio_trig_data *gpio_data = led->trigger_data;
|
||||
unsigned gpio;
|
||||
int ret;
|
||||
|
||||
ret = sscanf(buf, "%u", &gpio);
|
||||
if (ret < 1) {
|
||||
dev_err(dev, "couldn't read gpio number\n");
|
||||
flush_work(&gpio_data->work);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!gpio) {
|
||||
free_irq(gpio_to_irq(gpio_data->gpio), led);
|
||||
return n;
|
||||
}
|
||||
|
||||
if (gpio_data->gpio > 0 && gpio_data->gpio != gpio)
|
||||
free_irq(gpio_to_irq(gpio_data->gpio), led);
|
||||
|
||||
gpio_data->gpio = gpio;
|
||||
ret = request_irq(gpio_to_irq(gpio), gpio_trig_irq,
|
||||
IRQF_SHARED | IRQF_TRIGGER_RISING
|
||||
| IRQF_TRIGGER_FALLING, "ledtrig-gpio", led);
|
||||
if (ret)
|
||||
dev_err(dev, "request_irq failed with error %d\n", ret);
|
||||
|
||||
return ret ? ret : n;
|
||||
}
|
||||
static DEVICE_ATTR(gpio, 0644, gpio_trig_gpio_show, gpio_trig_gpio_store);
|
||||
|
||||
static void gpio_trig_activate(struct led_classdev *led)
|
||||
{
|
||||
struct gpio_trig_data *gpio_data;
|
||||
int ret;
|
||||
|
||||
gpio_data = kzalloc(sizeof(*gpio_data), GFP_KERNEL);
|
||||
if (!gpio_data)
|
||||
return;
|
||||
|
||||
ret = device_create_file(led->dev, &dev_attr_gpio);
|
||||
if (ret)
|
||||
goto err_gpio;
|
||||
|
||||
ret = device_create_file(led->dev, &dev_attr_inverted);
|
||||
if (ret)
|
||||
goto err_inverted;
|
||||
|
||||
ret = device_create_file(led->dev, &dev_attr_desired_brightness);
|
||||
if (ret)
|
||||
goto err_brightness;
|
||||
|
||||
gpio_data->led = led;
|
||||
led->trigger_data = gpio_data;
|
||||
INIT_WORK(&gpio_data->work, gpio_trig_work);
|
||||
|
||||
return;
|
||||
|
||||
err_brightness:
|
||||
device_remove_file(led->dev, &dev_attr_inverted);
|
||||
|
||||
err_inverted:
|
||||
device_remove_file(led->dev, &dev_attr_gpio);
|
||||
|
||||
err_gpio:
|
||||
kfree(gpio_data);
|
||||
}
|
||||
|
||||
static void gpio_trig_deactivate(struct led_classdev *led)
|
||||
{
|
||||
struct gpio_trig_data *gpio_data = led->trigger_data;
|
||||
|
||||
if (gpio_data) {
|
||||
device_remove_file(led->dev, &dev_attr_gpio);
|
||||
device_remove_file(led->dev, &dev_attr_inverted);
|
||||
device_remove_file(led->dev, &dev_attr_desired_brightness);
|
||||
flush_work(&gpio_data->work);
|
||||
free_irq(gpio_to_irq(gpio_data->gpio),led);
|
||||
kfree(gpio_data);
|
||||
}
|
||||
}
|
||||
|
||||
static struct led_trigger gpio_led_trigger = {
|
||||
.name = "gpio",
|
||||
.activate = gpio_trig_activate,
|
||||
.deactivate = gpio_trig_deactivate,
|
||||
};
|
||||
|
||||
static int __init gpio_trig_init(void)
|
||||
{
|
||||
return led_trigger_register(&gpio_led_trigger);
|
||||
}
|
||||
module_init(gpio_trig_init);
|
||||
|
||||
static void __exit gpio_trig_exit(void)
|
||||
{
|
||||
led_trigger_unregister(&gpio_led_trigger);
|
||||
}
|
||||
module_exit(gpio_trig_exit);
|
||||
|
||||
MODULE_AUTHOR("Felipe Balbi <me@felipebalbi.com>");
|
||||
MODULE_DESCRIPTION("GPIO LED trigger");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -47,7 +47,7 @@ static void led_heartbeat_function(unsigned long data)
|
|||
msecs_to_jiffies(heartbeat_data->period);
|
||||
delay = msecs_to_jiffies(70);
|
||||
heartbeat_data->phase++;
|
||||
brightness = LED_FULL;
|
||||
brightness = led_cdev->max_brightness;
|
||||
break;
|
||||
case 1:
|
||||
delay = heartbeat_data->period / 4 - msecs_to_jiffies(70);
|
||||
|
@ -56,7 +56,7 @@ static void led_heartbeat_function(unsigned long data)
|
|||
case 2:
|
||||
delay = msecs_to_jiffies(70);
|
||||
heartbeat_data->phase++;
|
||||
brightness = LED_FULL;
|
||||
brightness = led_cdev->max_brightness;
|
||||
break;
|
||||
default:
|
||||
delay = heartbeat_data->period - heartbeat_data->period / 4 -
|
||||
|
|
|
@ -37,7 +37,8 @@ static void ledtrig_ide_timerfunc(unsigned long data)
|
|||
{
|
||||
if (ide_lastactivity != ide_activity) {
|
||||
ide_lastactivity = ide_activity;
|
||||
led_trigger_event(ledtrig_ide, LED_FULL);
|
||||
/* 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);
|
||||
|
|
|
@ -166,7 +166,7 @@ static void timer_trig_activate(struct led_classdev *led_cdev)
|
|||
|
||||
timer_data->brightness_on = led_get_brightness(led_cdev);
|
||||
if (timer_data->brightness_on == LED_OFF)
|
||||
timer_data->brightness_on = LED_FULL;
|
||||
timer_data->brightness_on = led_cdev->max_brightness;
|
||||
led_cdev->trigger_data = timer_data;
|
||||
|
||||
init_timer(&timer_data->timer);
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* leds-bd2802.h - RGB LED Driver
|
||||
*
|
||||
* Copyright (C) 2009 Samsung Electronics
|
||||
* Kim Kyuwon <q1.kim@samsung.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.
|
||||
*
|
||||
* Datasheet: http://www.rohm.com/products/databook/driver/pdf/bd2802gu-e.pdf
|
||||
*
|
||||
*/
|
||||
#ifndef _LEDS_BD2802_H_
|
||||
#define _LEDS_BD2802_H_
|
||||
|
||||
struct bd2802_led_platform_data{
|
||||
int reset_gpio;
|
||||
u8 rgb_time;
|
||||
};
|
||||
|
||||
#define RGB_TIME(slopedown, slopeup, waveform) \
|
||||
((slopedown) << 6 | (slopeup) << 4 | (waveform))
|
||||
|
||||
#endif /* _LEDS_BD2802_H_ */
|
||||
|
|
@ -30,6 +30,7 @@ enum led_brightness {
|
|||
struct led_classdev {
|
||||
const char *name;
|
||||
int brightness;
|
||||
int max_brightness;
|
||||
int flags;
|
||||
|
||||
/* Lower 16 bits reflect status */
|
||||
|
@ -140,7 +141,8 @@ struct gpio_led {
|
|||
const char *name;
|
||||
const char *default_trigger;
|
||||
unsigned gpio;
|
||||
u8 active_low;
|
||||
u8 active_low : 1;
|
||||
u8 retain_state_suspended : 1;
|
||||
};
|
||||
|
||||
struct gpio_led_platform_data {
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* PWM LED driver data - see drivers/leds/leds-pwm.c
|
||||
*/
|
||||
#ifndef __LINUX_LEDS_PWM_H
|
||||
#define __LINUX_LEDS_PWM_H
|
||||
|
||||
struct led_pwm {
|
||||
const char *name;
|
||||
const char *default_trigger;
|
||||
unsigned pwm_id;
|
||||
u8 active_low;
|
||||
unsigned max_brightness;
|
||||
unsigned pwm_period_ns;
|
||||
};
|
||||
|
||||
struct led_pwm_platform_data {
|
||||
int num_leds;
|
||||
struct led_pwm *leds;
|
||||
};
|
||||
|
||||
#endif
|
Загрузка…
Ссылка в новой задаче