Merge branch 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/cooloney/linux-leds
Pull LED subsystem update from Bryan Wu. * 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/cooloney/linux-leds: (61 commits) leds: leds-sunfire: use dev_err()/pr_err() instead of printk() leds: 88pm860x: Add missing of_node_put() leds: tca6507: Use of_get_child_count() leds: leds-pwm: make it depend on PWM and not HAVE_PWM Documentation: leds: update LP55xx family devices leds-lp55xx: fix problem on removing LED attributes leds-lp5521/5523: add author and copyright description leds-lp5521/5523: use new lp55xx common header leds-lp55xx: clean up headers leds-lp55xx: clean up definitions leds-lp55xx: clean up unused data and functions leds-lp55xx: clean up _remove() leds-lp55xx: add new function for removing device attribtues leds-lp55xx: code refactoring on selftest function leds-lp55xx: use common device attribute driver function leds-lp55xx: support device specific attributes leds-lp5523: use generic firmware interface leds-lp5521: use generic firmware interface leds-lp55xx: support firmware interface leds-lp55xx: add new lp55xx_register_sysfs() for the firmware interface ...
This commit is contained in:
commit
0512c04a2b
|
@ -0,0 +1,48 @@
|
|||
LED connected to PWM
|
||||
|
||||
Required properties:
|
||||
- compatible : should be "pwm-leds".
|
||||
|
||||
Each LED is represented as a sub-node of the pwm-leds device. Each
|
||||
node's name represents the name of the corresponding LED.
|
||||
|
||||
LED sub-node properties:
|
||||
- pwms : PWM property to point to the PWM device (phandle)/port (id) and to
|
||||
specify the period time to be used: <&phandle id period_ns>;
|
||||
- pwm-names : (optional) Name to be used by the PWM subsystem for the PWM device
|
||||
For the pwms and pwm-names property please refer to:
|
||||
Documentation/devicetree/bindings/pwm/pwm.txt
|
||||
- max-brightness : Maximum brightness possible for the LED
|
||||
- label : (optional)
|
||||
see Documentation/devicetree/bindings/leds/common.txt
|
||||
- linux,default-trigger : (optional)
|
||||
see Documentation/devicetree/bindings/leds/common.txt
|
||||
|
||||
Example:
|
||||
|
||||
twl_pwm: pwm {
|
||||
/* provides two PWMs (id 0, 1 for PWM1 and PWM2) */
|
||||
compatible = "ti,twl6030-pwm";
|
||||
#pwm-cells = <2>;
|
||||
};
|
||||
|
||||
twl_pwmled: pwmled {
|
||||
/* provides one PWM (id 0 for Charing indicator LED) */
|
||||
compatible = "ti,twl6030-pwmled";
|
||||
#pwm-cells = <2>;
|
||||
};
|
||||
|
||||
pwmleds {
|
||||
compatible = "pwm-leds";
|
||||
kpad {
|
||||
label = "omap4::keypad";
|
||||
pwms = <&twl_pwm 0 7812500>;
|
||||
max-brightness = <127>;
|
||||
};
|
||||
|
||||
charging {
|
||||
label = "omap4:green:chrg";
|
||||
pwms = <&twl_pwmled 0 7812500>;
|
||||
max-brightness = <255>;
|
||||
};
|
||||
};
|
|
@ -0,0 +1,33 @@
|
|||
LEDs conected to tca6507
|
||||
|
||||
Required properties:
|
||||
- compatible : should be : "ti,tca6507".
|
||||
|
||||
Each led is represented as a sub-node of the ti,tca6507 device.
|
||||
|
||||
LED sub-node properties:
|
||||
- label : (optional) see Documentation/devicetree/bindings/leds/common.txt
|
||||
- reg : number of LED line (could be from 0 to 6)
|
||||
- linux,default-trigger : (optional)
|
||||
see Documentation/devicetree/bindings/leds/common.txt
|
||||
|
||||
Examples:
|
||||
|
||||
tca6507@45 {
|
||||
compatible = "ti,tca6507";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
reg = <0x45>;
|
||||
|
||||
led0: red-aux@0 {
|
||||
label = "red:aux";
|
||||
reg = <0x0>;
|
||||
};
|
||||
|
||||
led1: green-aux@1 {
|
||||
label = "green:aux";
|
||||
reg = <0x5>;
|
||||
linux,default-trigger = "default-on";
|
||||
};
|
||||
};
|
||||
|
|
@ -6,5 +6,7 @@ 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-lp55xx.txt
|
||||
- description about lp55xx common driver.
|
||||
leds-lm3556.txt
|
||||
- notes on how to use the leds-lm3556 driver.
|
||||
|
|
|
@ -17,19 +17,8 @@ lp5521:channelx, where x is 0 .. 2
|
|||
All three channels can be also controlled using the engine micro programs.
|
||||
More details of the instructions can be found from the public data sheet.
|
||||
|
||||
Control interface for the engines:
|
||||
x is 1 .. 3
|
||||
enginex_mode : disabled, load, run
|
||||
enginex_load : store program (visible only in engine load mode)
|
||||
|
||||
Example (start to blink the channel 2 led):
|
||||
cd /sys/class/leds/lp5521:channel2/device
|
||||
echo "load" > engine3_mode
|
||||
echo "037f4d0003ff6000" > engine3_load
|
||||
echo "run" > engine3_mode
|
||||
|
||||
stop the engine:
|
||||
echo "disabled" > engine3_mode
|
||||
LP5521 has the internal program memory for running various LED patterns.
|
||||
For the details, please refer to 'firmware' section in leds-lp55xx.txt
|
||||
|
||||
sysfs contains a selftest entry.
|
||||
The test communicates with the chip and checks that
|
||||
|
@ -47,7 +36,7 @@ The name of each channel can be configurable.
|
|||
If the name field is not defined, the default name will be set to 'xxxx:channelN'
|
||||
(XXXX : pdata->label or i2c client name, N : channel number)
|
||||
|
||||
static struct lp5521_led_config lp5521_led_config[] = {
|
||||
static struct lp55xx_led_config lp5521_led_config[] = {
|
||||
{
|
||||
.name = "red",
|
||||
.chan_nr = 0,
|
||||
|
@ -81,10 +70,10 @@ static void lp5521_enable(bool state)
|
|||
/* Control of chip enable signal */
|
||||
}
|
||||
|
||||
static struct lp5521_platform_data lp5521_platform_data = {
|
||||
static struct lp55xx_platform_data lp5521_platform_data = {
|
||||
.led_config = lp5521_led_config,
|
||||
.num_channels = ARRAY_SIZE(lp5521_led_config),
|
||||
.clock_mode = LP5521_CLOCK_EXT,
|
||||
.clock_mode = LP55XX_CLOCK_EXT,
|
||||
.setup_resources = lp5521_setup,
|
||||
.release_resources = lp5521_release,
|
||||
.enable = lp5521_enable,
|
||||
|
@ -105,47 +94,9 @@ example of update_config :
|
|||
LP5521_CP_MODE_AUTO | LP5521_R_TO_BATT | \
|
||||
LP5521_CLK_INT)
|
||||
|
||||
static struct lp5521_platform_data lp5521_pdata = {
|
||||
static struct lp55xx_platform_data lp5521_pdata = {
|
||||
.led_config = lp5521_led_config,
|
||||
.num_channels = ARRAY_SIZE(lp5521_led_config),
|
||||
.clock_mode = LP5521_CLOCK_INT,
|
||||
.clock_mode = LP55XX_CLOCK_INT,
|
||||
.update_config = LP5521_CONFIGS,
|
||||
};
|
||||
|
||||
LED patterns : LP5521 has autonomous operation without external control.
|
||||
Pattern data can be defined in the platform data.
|
||||
|
||||
example of led pattern data :
|
||||
|
||||
/* RGB(50,5,0) 500ms on, 500ms off, infinite loop */
|
||||
static u8 pattern_red[] = {
|
||||
0x40, 0x32, 0x60, 0x00, 0x40, 0x00, 0x60, 0x00,
|
||||
};
|
||||
|
||||
static u8 pattern_green[] = {
|
||||
0x40, 0x05, 0x60, 0x00, 0x40, 0x00, 0x60, 0x00,
|
||||
};
|
||||
|
||||
static struct lp5521_led_pattern board_led_patterns[] = {
|
||||
{
|
||||
.r = pattern_red,
|
||||
.g = pattern_green,
|
||||
.size_r = ARRAY_SIZE(pattern_red),
|
||||
.size_g = ARRAY_SIZE(pattern_green),
|
||||
},
|
||||
};
|
||||
|
||||
static struct lp5521_platform_data lp5521_platform_data = {
|
||||
.led_config = lp5521_led_config,
|
||||
.num_channels = ARRAY_SIZE(lp5521_led_config),
|
||||
.clock_mode = LP5521_CLOCK_EXT,
|
||||
.patterns = board_led_patterns,
|
||||
.num_patterns = ARRAY_SIZE(board_led_patterns),
|
||||
};
|
||||
|
||||
Then predefined led pattern(s) can be executed via the sysfs.
|
||||
To start the pattern #1,
|
||||
# echo 1 > /sys/bus/i2c/devices/xxxx/led_pattern
|
||||
(xxxx : i2c bus & slave address)
|
||||
To end the pattern,
|
||||
# echo 0 > /sys/bus/i2c/devices/xxxx/led_pattern
|
||||
|
|
|
@ -27,25 +27,8 @@ c) Default
|
|||
If both fields are NULL, 'lp5523' is used by default.
|
||||
/sys/class/leds/lp5523:channelN (N: 0 ~ 8)
|
||||
|
||||
The chip provides 3 engines. Each engine can control channels without
|
||||
interaction from the main CPU. Details of the micro engine code can be found
|
||||
from the public data sheet. Leds can be muxed to different channels.
|
||||
|
||||
Control interface for the engines:
|
||||
x is 1 .. 3
|
||||
enginex_mode : disabled, load, run
|
||||
enginex_load : microcode load (visible only in load mode)
|
||||
enginex_leds : led mux control (visible only in load mode)
|
||||
|
||||
cd /sys/class/leds/lp5523:channel2/device
|
||||
echo "load" > engine3_mode
|
||||
echo "9d80400004ff05ff437f0000" > engine3_load
|
||||
echo "111111111" > engine3_leds
|
||||
echo "run" > engine3_mode
|
||||
|
||||
sysfs contains a selftest entry. It measures each channel
|
||||
voltage level and checks if it looks reasonable. If the level is too high,
|
||||
the led is missing; if the level is too low, there is a short circuit.
|
||||
LP5523 has the internal program memory for running various LED patterns.
|
||||
For the details, please refer to 'firmware' section in leds-lp55xx.txt
|
||||
|
||||
Selftest uses always the current from the platform data.
|
||||
|
||||
|
@ -58,7 +41,7 @@ Example platform data:
|
|||
|
||||
Note - chan_nr can have values between 0 and 8.
|
||||
|
||||
static struct lp5523_led_config lp5523_led_config[] = {
|
||||
static struct lp55xx_led_config lp5523_led_config[] = {
|
||||
{
|
||||
.name = "D1",
|
||||
.chan_nr = 0,
|
||||
|
@ -88,10 +71,10 @@ static void lp5523_enable(bool state)
|
|||
/* Control chip enable signal */
|
||||
}
|
||||
|
||||
static struct lp5523_platform_data lp5523_platform_data = {
|
||||
static struct lp55xx_platform_data lp5523_platform_data = {
|
||||
.led_config = lp5523_led_config,
|
||||
.num_channels = ARRAY_SIZE(lp5523_led_config),
|
||||
.clock_mode = LP5523_CLOCK_EXT,
|
||||
.clock_mode = LP55XX_CLOCK_EXT,
|
||||
.setup_resources = lp5523_setup,
|
||||
.release_resources = lp5523_release,
|
||||
.enable = lp5523_enable,
|
||||
|
|
|
@ -0,0 +1,118 @@
|
|||
LP5521/LP5523/LP55231 Common Driver
|
||||
===================================
|
||||
|
||||
Authors: Milo(Woogyom) Kim <milo.kim@ti.com>
|
||||
|
||||
Description
|
||||
-----------
|
||||
LP5521, LP5523/55231 have common features as below.
|
||||
|
||||
Register access via the I2C
|
||||
Device initialization/deinitialization
|
||||
Create LED class devices for multiple output channels
|
||||
Device attributes for user-space interface
|
||||
Program memory for running LED patterns
|
||||
|
||||
The LP55xx common driver provides these features using exported functions.
|
||||
lp55xx_init_device() / lp55xx_deinit_device()
|
||||
lp55xx_register_leds() / lp55xx_unregister_leds()
|
||||
lp55xx_regsister_sysfs() / lp55xx_unregister_sysfs()
|
||||
|
||||
( Driver Structure Data )
|
||||
|
||||
In lp55xx common driver, two different data structure is used.
|
||||
|
||||
o lp55xx_led
|
||||
control multi output LED channels such as led current, channel index.
|
||||
o lp55xx_chip
|
||||
general chip control such like the I2C and platform data.
|
||||
|
||||
For example, LP5521 has maximum 3 LED channels.
|
||||
LP5523/55231 has 9 output channels.
|
||||
|
||||
lp55xx_chip for LP5521 ... lp55xx_led #1
|
||||
lp55xx_led #2
|
||||
lp55xx_led #3
|
||||
|
||||
lp55xx_chip for LP5523 ... lp55xx_led #1
|
||||
lp55xx_led #2
|
||||
.
|
||||
.
|
||||
lp55xx_led #9
|
||||
|
||||
( Chip Dependent Code )
|
||||
|
||||
To support device specific configurations, special structure
|
||||
'lpxx_device_config' is used.
|
||||
|
||||
Maximum number of channels
|
||||
Reset command, chip enable command
|
||||
Chip specific initialization
|
||||
Brightness control register access
|
||||
Setting LED output current
|
||||
Program memory address access for running patterns
|
||||
Additional device specific attributes
|
||||
|
||||
( Firmware Interface )
|
||||
|
||||
LP55xx family devices have the internal program memory for running
|
||||
various LED patterns.
|
||||
This pattern data is saved as a file in the user-land or
|
||||
hex byte string is written into the memory through the I2C.
|
||||
LP55xx common driver supports the firmware interface.
|
||||
|
||||
LP55xx chips have three program engines.
|
||||
To load and run the pattern, the programming sequence is following.
|
||||
(1) Select an engine number (1/2/3)
|
||||
(2) Mode change to load
|
||||
(3) Write pattern data into selected area
|
||||
(4) Mode change to run
|
||||
|
||||
The LP55xx common driver provides simple interfaces as below.
|
||||
select_engine : Select which engine is used for running program
|
||||
run_engine : Start program which is loaded via the firmware interface
|
||||
firmware : Load program data
|
||||
|
||||
For example, run blinking pattern in engine #1 of LP5521
|
||||
echo 1 > /sys/bus/i2c/devices/xxxx/select_engine
|
||||
echo 1 > /sys/class/firmware/lp5521/loading
|
||||
echo "4000600040FF6000" > /sys/class/firmware/lp5521/data
|
||||
echo 0 > /sys/class/firmware/lp5521/loading
|
||||
echo 1 > /sys/bus/i2c/devices/xxxx/run_engine
|
||||
|
||||
For example, run blinking pattern in engine #3 of LP55231
|
||||
echo 3 > /sys/bus/i2c/devices/xxxx/select_engine
|
||||
echo 1 > /sys/class/firmware/lp55231/loading
|
||||
echo "9d0740ff7e0040007e00a0010000" > /sys/class/firmware/lp55231/data
|
||||
echo 0 > /sys/class/firmware/lp55231/loading
|
||||
echo 1 > /sys/bus/i2c/devices/xxxx/run_engine
|
||||
|
||||
To start blinking patterns in engine #2 and #3 simultaneously,
|
||||
for idx in 2 3
|
||||
do
|
||||
echo $idx > /sys/class/leds/red/device/select_engine
|
||||
sleep 0.1
|
||||
echo 1 > /sys/class/firmware/lp5521/loading
|
||||
echo "4000600040FF6000" > /sys/class/firmware/lp5521/data
|
||||
echo 0 > /sys/class/firmware/lp5521/loading
|
||||
done
|
||||
echo 1 > /sys/class/leds/red/device/run_engine
|
||||
|
||||
Here is another example for LP5523.
|
||||
echo 2 > /sys/bus/i2c/devices/xxxx/select_engine
|
||||
echo 1 > /sys/class/firmware/lp5523/loading
|
||||
echo "9d80400004ff05ff437f0000" > /sys/class/firmware/lp5523/data
|
||||
echo 0 > /sys/class/firmware/lp5523/loading
|
||||
echo 1 > /sys/bus/i2c/devices/xxxx/run_engine
|
||||
|
||||
As soon as 'loading' is set to 0, registered callback is called.
|
||||
Inside the callback, the selected engine is loaded and memory is updated.
|
||||
To run programmed pattern, 'run_engine' attribute should be enabled.
|
||||
|
||||
( 'run_engine' and 'firmware_cb' )
|
||||
The sequence of running the program data is common.
|
||||
But each device has own specific register addresses for commands.
|
||||
To support this, 'run_engine' and 'firmware_cb' are configurable in each driver.
|
||||
run_engine : Control the selected engine
|
||||
firmware_cb : The callback function after loading the firmware is done.
|
||||
Chip specific commands for loading and updating program memory.
|
|
@ -40,7 +40,7 @@
|
|||
#include <sound/tpa6130a2-plat.h>
|
||||
#include <media/radio-si4713.h>
|
||||
#include <media/si4713.h>
|
||||
#include <linux/leds-lp5523.h>
|
||||
#include <linux/platform_data/leds-lp55xx.h>
|
||||
|
||||
#include <linux/platform_data/tsl2563.h>
|
||||
#include <linux/lis3lv02d.h>
|
||||
|
@ -160,7 +160,7 @@ static struct tsl2563_platform_data rx51_tsl2563_platform_data = {
|
|||
#endif
|
||||
|
||||
#if defined(CONFIG_LEDS_LP5523) || defined(CONFIG_LEDS_LP5523_MODULE)
|
||||
static struct lp5523_led_config rx51_lp5523_led_config[] = {
|
||||
static struct lp55xx_led_config rx51_lp5523_led_config[] = {
|
||||
{
|
||||
.name = "lp5523:kb1",
|
||||
.chan_nr = 0,
|
||||
|
@ -216,10 +216,10 @@ static void rx51_lp5523_enable(bool state)
|
|||
gpio_set_value(RX51_LP5523_CHIP_EN_GPIO, !!state);
|
||||
}
|
||||
|
||||
static struct lp5523_platform_data rx51_lp5523_platform_data = {
|
||||
static struct lp55xx_platform_data rx51_lp5523_platform_data = {
|
||||
.led_config = rx51_lp5523_led_config,
|
||||
.num_channels = ARRAY_SIZE(rx51_lp5523_led_config),
|
||||
.clock_mode = LP5523_CLOCK_AUTO,
|
||||
.clock_mode = LP55XX_CLOCK_AUTO,
|
||||
.setup_resources = rx51_lp5523_setup,
|
||||
.release_resources = rx51_lp5523_release,
|
||||
.enable = rx51_lp5523_enable,
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
#include <linux/mfd/tps6105x.h>
|
||||
#include <linux/mfd/abx500/ab8500-gpio.h>
|
||||
#include <linux/mfd/abx500/ab8500-codec.h>
|
||||
#include <linux/leds-lp5521.h>
|
||||
#include <linux/platform_data/leds-lp55xx.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/smsc911x.h>
|
||||
#include <linux/gpio_keys.h>
|
||||
|
@ -301,7 +301,7 @@ static struct tc3589x_platform_data mop500_tc35892_data = {
|
|||
.irq_base = MOP500_EGPIO_IRQ_BASE,
|
||||
};
|
||||
|
||||
static struct lp5521_led_config lp5521_pri_led[] = {
|
||||
static struct lp55xx_led_config lp5521_pri_led[] = {
|
||||
[0] = {
|
||||
.chan_nr = 0,
|
||||
.led_current = 0x2f,
|
||||
|
@ -319,14 +319,14 @@ static struct lp5521_led_config lp5521_pri_led[] = {
|
|||
},
|
||||
};
|
||||
|
||||
static struct lp5521_platform_data __initdata lp5521_pri_data = {
|
||||
static struct lp55xx_platform_data __initdata lp5521_pri_data = {
|
||||
.label = "lp5521_pri",
|
||||
.led_config = &lp5521_pri_led[0],
|
||||
.num_channels = 3,
|
||||
.clock_mode = LP5521_CLOCK_EXT,
|
||||
.clock_mode = LP55XX_CLOCK_EXT,
|
||||
};
|
||||
|
||||
static struct lp5521_led_config lp5521_sec_led[] = {
|
||||
static struct lp55xx_led_config lp5521_sec_led[] = {
|
||||
[0] = {
|
||||
.chan_nr = 0,
|
||||
.led_current = 0x2f,
|
||||
|
@ -344,11 +344,11 @@ static struct lp5521_led_config lp5521_sec_led[] = {
|
|||
},
|
||||
};
|
||||
|
||||
static struct lp5521_platform_data __initdata lp5521_sec_data = {
|
||||
static struct lp55xx_platform_data __initdata lp5521_sec_data = {
|
||||
.label = "lp5521_sec",
|
||||
.led_config = &lp5521_sec_led[0],
|
||||
.num_channels = 3,
|
||||
.clock_mode = LP5521_CLOCK_EXT,
|
||||
.clock_mode = LP55XX_CLOCK_EXT,
|
||||
};
|
||||
|
||||
static struct i2c_board_info __initdata mop500_i2c0_devices[] = {
|
||||
|
|
|
@ -193,9 +193,18 @@ config LEDS_LP3944
|
|||
To compile this driver as a module, choose M here: the
|
||||
module will be called leds-lp3944.
|
||||
|
||||
config LEDS_LP55XX_COMMON
|
||||
tristate "Common Driver for TI/National LP5521 and LP5523/55231"
|
||||
depends on LEDS_LP5521 || LEDS_LP5523
|
||||
select FW_LOADER
|
||||
help
|
||||
This option supports common operations for LP5521 and LP5523/55231
|
||||
devices.
|
||||
|
||||
config LEDS_LP5521
|
||||
tristate "LED Support for N.S. LP5521 LED driver chip"
|
||||
depends on LEDS_CLASS && I2C
|
||||
select LEDS_LP55XX_COMMON
|
||||
help
|
||||
If you say yes here you get support for the National Semiconductor
|
||||
LP5521 LED driver. It is 3 channel chip with programmable engines.
|
||||
|
@ -205,6 +214,7 @@ config LEDS_LP5521
|
|||
config LEDS_LP5523
|
||||
tristate "LED Support for TI/National LP5523/55231 LED driver chip"
|
||||
depends on LEDS_CLASS && I2C
|
||||
select LEDS_LP55XX_COMMON
|
||||
help
|
||||
If you say yes here you get support for TI/National Semiconductor
|
||||
LP5523/55231 LED driver.
|
||||
|
@ -310,7 +320,7 @@ config LEDS_DAC124S085
|
|||
config LEDS_PWM
|
||||
tristate "PWM driven LED Support"
|
||||
depends on LEDS_CLASS
|
||||
depends on HAVE_PWM
|
||||
depends on PWM
|
||||
help
|
||||
This option enables support for pwm driven LEDs
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ obj-$(CONFIG_LEDS_PCA9532) += leds-pca9532.o
|
|||
obj-$(CONFIG_LEDS_GPIO_REGISTER) += leds-gpio-register.o
|
||||
obj-$(CONFIG_LEDS_GPIO) += leds-gpio.o
|
||||
obj-$(CONFIG_LEDS_LP3944) += leds-lp3944.o
|
||||
obj-$(CONFIG_LEDS_LP55XX_COMMON) += leds-lp55xx-common.o
|
||||
obj-$(CONFIG_LEDS_LP5521) += leds-lp5521.o
|
||||
obj-$(CONFIG_LEDS_LP5523) += leds-lp5523.o
|
||||
obj-$(CONFIG_LEDS_LP8788) += leds-lp8788.o
|
||||
|
|
|
@ -128,8 +128,10 @@ static void pm860x_led_set(struct led_classdev *cdev,
|
|||
static int pm860x_led_dt_init(struct platform_device *pdev,
|
||||
struct pm860x_led *data)
|
||||
{
|
||||
struct device_node *nproot = pdev->dev.parent->of_node, *np;
|
||||
struct device_node *nproot, *np;
|
||||
int iset = 0;
|
||||
|
||||
nproot = of_node_get(pdev->dev.parent->of_node);
|
||||
if (!nproot)
|
||||
return -ENODEV;
|
||||
nproot = of_find_node_by_name(nproot, "leds");
|
||||
|
@ -145,6 +147,7 @@ static int pm860x_led_dt_init(struct platform_device *pdev,
|
|||
break;
|
||||
}
|
||||
}
|
||||
of_node_put(nproot);
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
|
|
|
@ -187,6 +187,40 @@ static void lm3530_als_configure(struct lm3530_platform_data *pdata,
|
|||
(pdata->als2_resistor_sel << LM3530_ALS2_IMP_SHIFT);
|
||||
}
|
||||
|
||||
static int lm3530_led_enable(struct lm3530_data *drvdata)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (drvdata->enable)
|
||||
return 0;
|
||||
|
||||
ret = regulator_enable(drvdata->regulator);
|
||||
if (ret) {
|
||||
dev_err(drvdata->led_dev.dev, "Failed to enable vin:%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
drvdata->enable = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void lm3530_led_disable(struct lm3530_data *drvdata)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!drvdata->enable)
|
||||
return;
|
||||
|
||||
ret = regulator_disable(drvdata->regulator);
|
||||
if (ret) {
|
||||
dev_err(drvdata->led_dev.dev, "Failed to disable vin:%d\n",
|
||||
ret);
|
||||
return;
|
||||
}
|
||||
|
||||
drvdata->enable = false;
|
||||
}
|
||||
|
||||
static int lm3530_init_registers(struct lm3530_data *drvdata)
|
||||
{
|
||||
int ret = 0;
|
||||
|
@ -245,15 +279,9 @@ static int lm3530_init_registers(struct lm3530_data *drvdata)
|
|||
reg_val[12] = LM3530_DEF_ZT_3; /* LM3530_ALS_Z3T_REG */
|
||||
reg_val[13] = LM3530_DEF_ZT_4; /* LM3530_ALS_Z4T_REG */
|
||||
|
||||
if (!drvdata->enable) {
|
||||
ret = regulator_enable(drvdata->regulator);
|
||||
if (ret) {
|
||||
dev_err(&drvdata->client->dev,
|
||||
"Enable regulator failed\n");
|
||||
return ret;
|
||||
}
|
||||
drvdata->enable = true;
|
||||
}
|
||||
ret = lm3530_led_enable(drvdata);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < LM3530_REG_MAX; i++) {
|
||||
/* do not update brightness register when pwm mode */
|
||||
|
@ -305,13 +333,8 @@ static void lm3530_brightness_set(struct led_classdev *led_cdev,
|
|||
else
|
||||
drvdata->brightness = brt_val;
|
||||
|
||||
if (brt_val == 0) {
|
||||
err = regulator_disable(drvdata->regulator);
|
||||
if (err)
|
||||
dev_err(&drvdata->client->dev,
|
||||
"Disable regulator failed\n");
|
||||
drvdata->enable = false;
|
||||
}
|
||||
if (brt_val == 0)
|
||||
lm3530_led_disable(drvdata);
|
||||
break;
|
||||
case LM3530_BL_MODE_ALS:
|
||||
break;
|
||||
|
@ -458,8 +481,7 @@ static int lm3530_remove(struct i2c_client *client)
|
|||
|
||||
device_remove_file(drvdata->led_dev.dev, &dev_attr_mode);
|
||||
|
||||
if (drvdata->enable)
|
||||
regulator_disable(drvdata->regulator);
|
||||
lm3530_led_disable(drvdata);
|
||||
led_classdev_unregister(&drvdata->led_dev);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -380,7 +380,7 @@ static void lm355x_indicator_brightness_set(struct led_classdev *cdev,
|
|||
|
||||
/* indicator pattern only for lm3556*/
|
||||
static ssize_t lm3556_indicator_pattern_store(struct device *dev,
|
||||
struct device_attribute *devAttr,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t size)
|
||||
{
|
||||
ssize_t ret;
|
||||
|
|
|
@ -176,7 +176,7 @@ out:
|
|||
|
||||
/* torch pin config for lm3642*/
|
||||
static ssize_t lm3642_torch_pin_store(struct device *dev,
|
||||
struct device_attribute *devAttr,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t size)
|
||||
{
|
||||
ssize_t ret;
|
||||
|
@ -233,7 +233,7 @@ static void lm3642_torch_brightness_set(struct led_classdev *cdev,
|
|||
|
||||
/* strobe pin config for lm3642*/
|
||||
static ssize_t lm3642_strobe_pin_store(struct device *dev,
|
||||
struct device_attribute *devAttr,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t size)
|
||||
{
|
||||
ssize_t ret;
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,523 @@
|
|||
/*
|
||||
* LP5521/LP5523/LP55231 Common 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.
|
||||
*
|
||||
* Derived from leds-lp5521.c, leds-lp5523.c
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_data/leds-lp55xx.h>
|
||||
|
||||
#include "leds-lp55xx-common.h"
|
||||
|
||||
static struct lp55xx_led *cdev_to_lp55xx_led(struct led_classdev *cdev)
|
||||
{
|
||||
return container_of(cdev, struct lp55xx_led, cdev);
|
||||
}
|
||||
|
||||
static struct lp55xx_led *dev_to_lp55xx_led(struct device *dev)
|
||||
{
|
||||
return cdev_to_lp55xx_led(dev_get_drvdata(dev));
|
||||
}
|
||||
|
||||
static void lp55xx_reset_device(struct lp55xx_chip *chip)
|
||||
{
|
||||
struct lp55xx_device_config *cfg = chip->cfg;
|
||||
u8 addr = cfg->reset.addr;
|
||||
u8 val = cfg->reset.val;
|
||||
|
||||
/* no error checking here because no ACK from the device after reset */
|
||||
lp55xx_write(chip, addr, val);
|
||||
}
|
||||
|
||||
static int lp55xx_detect_device(struct lp55xx_chip *chip)
|
||||
{
|
||||
struct lp55xx_device_config *cfg = chip->cfg;
|
||||
u8 addr = cfg->enable.addr;
|
||||
u8 val = cfg->enable.val;
|
||||
int ret;
|
||||
|
||||
ret = lp55xx_write(chip, addr, val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
usleep_range(1000, 2000);
|
||||
|
||||
ret = lp55xx_read(chip, addr, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (val != cfg->enable.val)
|
||||
return -ENODEV;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lp55xx_post_init_device(struct lp55xx_chip *chip)
|
||||
{
|
||||
struct lp55xx_device_config *cfg = chip->cfg;
|
||||
|
||||
if (!cfg->post_init_device)
|
||||
return 0;
|
||||
|
||||
return cfg->post_init_device(chip);
|
||||
}
|
||||
|
||||
static ssize_t lp55xx_show_current(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct lp55xx_led *led = dev_to_lp55xx_led(dev);
|
||||
|
||||
return sprintf(buf, "%d\n", led->led_current);
|
||||
}
|
||||
|
||||
static ssize_t lp55xx_store_current(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t len)
|
||||
{
|
||||
struct lp55xx_led *led = dev_to_lp55xx_led(dev);
|
||||
struct lp55xx_chip *chip = led->chip;
|
||||
unsigned long curr;
|
||||
|
||||
if (kstrtoul(buf, 0, &curr))
|
||||
return -EINVAL;
|
||||
|
||||
if (curr > led->max_current)
|
||||
return -EINVAL;
|
||||
|
||||
if (!chip->cfg->set_led_current)
|
||||
return len;
|
||||
|
||||
mutex_lock(&chip->lock);
|
||||
chip->cfg->set_led_current(led, (u8)curr);
|
||||
mutex_unlock(&chip->lock);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static ssize_t lp55xx_show_max_current(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct lp55xx_led *led = dev_to_lp55xx_led(dev);
|
||||
|
||||
return sprintf(buf, "%d\n", led->max_current);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(led_current, S_IRUGO | S_IWUSR, lp55xx_show_current,
|
||||
lp55xx_store_current);
|
||||
static DEVICE_ATTR(max_current, S_IRUGO , lp55xx_show_max_current, NULL);
|
||||
|
||||
static struct attribute *lp55xx_led_attributes[] = {
|
||||
&dev_attr_led_current.attr,
|
||||
&dev_attr_max_current.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute_group lp55xx_led_attr_group = {
|
||||
.attrs = lp55xx_led_attributes
|
||||
};
|
||||
|
||||
static void lp55xx_set_brightness(struct led_classdev *cdev,
|
||||
enum led_brightness brightness)
|
||||
{
|
||||
struct lp55xx_led *led = cdev_to_lp55xx_led(cdev);
|
||||
|
||||
led->brightness = (u8)brightness;
|
||||
schedule_work(&led->brightness_work);
|
||||
}
|
||||
|
||||
static int lp55xx_init_led(struct lp55xx_led *led,
|
||||
struct lp55xx_chip *chip, int chan)
|
||||
{
|
||||
struct lp55xx_platform_data *pdata = chip->pdata;
|
||||
struct lp55xx_device_config *cfg = chip->cfg;
|
||||
struct device *dev = &chip->cl->dev;
|
||||
char name[32];
|
||||
int ret;
|
||||
int max_channel = cfg->max_channel;
|
||||
|
||||
if (chan >= max_channel) {
|
||||
dev_err(dev, "invalid channel: %d / %d\n", chan, max_channel);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (pdata->led_config[chan].led_current == 0)
|
||||
return 0;
|
||||
|
||||
led->led_current = pdata->led_config[chan].led_current;
|
||||
led->max_current = pdata->led_config[chan].max_current;
|
||||
led->chan_nr = pdata->led_config[chan].chan_nr;
|
||||
|
||||
if (led->chan_nr >= max_channel) {
|
||||
dev_err(dev, "Use channel numbers between 0 and %d\n",
|
||||
max_channel - 1);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
led->cdev.brightness_set = lp55xx_set_brightness;
|
||||
|
||||
if (pdata->led_config[chan].name) {
|
||||
led->cdev.name = pdata->led_config[chan].name;
|
||||
} else {
|
||||
snprintf(name, sizeof(name), "%s:channel%d",
|
||||
pdata->label ? : chip->cl->name, chan);
|
||||
led->cdev.name = name;
|
||||
}
|
||||
|
||||
/*
|
||||
* register led class device for each channel and
|
||||
* add device attributes
|
||||
*/
|
||||
|
||||
ret = led_classdev_register(dev, &led->cdev);
|
||||
if (ret) {
|
||||
dev_err(dev, "led register err: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = sysfs_create_group(&led->cdev.dev->kobj, &lp55xx_led_attr_group);
|
||||
if (ret) {
|
||||
dev_err(dev, "led sysfs err: %d\n", ret);
|
||||
led_classdev_unregister(&led->cdev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void lp55xx_firmware_loaded(const struct firmware *fw, void *context)
|
||||
{
|
||||
struct lp55xx_chip *chip = context;
|
||||
struct device *dev = &chip->cl->dev;
|
||||
|
||||
if (!fw) {
|
||||
dev_err(dev, "firmware request failed\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* handling firmware data is chip dependent */
|
||||
mutex_lock(&chip->lock);
|
||||
|
||||
chip->fw = fw;
|
||||
if (chip->cfg->firmware_cb)
|
||||
chip->cfg->firmware_cb(chip);
|
||||
|
||||
mutex_unlock(&chip->lock);
|
||||
|
||||
out:
|
||||
/* firmware should be released for other channel use */
|
||||
release_firmware(chip->fw);
|
||||
}
|
||||
|
||||
static int lp55xx_request_firmware(struct lp55xx_chip *chip)
|
||||
{
|
||||
const char *name = chip->cl->name;
|
||||
struct device *dev = &chip->cl->dev;
|
||||
|
||||
return request_firmware_nowait(THIS_MODULE, true, name, dev,
|
||||
GFP_KERNEL, chip, lp55xx_firmware_loaded);
|
||||
}
|
||||
|
||||
static ssize_t lp55xx_show_engine_select(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev));
|
||||
struct lp55xx_chip *chip = led->chip;
|
||||
|
||||
return sprintf(buf, "%d\n", chip->engine_idx);
|
||||
}
|
||||
|
||||
static ssize_t lp55xx_store_engine_select(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t len)
|
||||
{
|
||||
struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev));
|
||||
struct lp55xx_chip *chip = led->chip;
|
||||
unsigned long val;
|
||||
int ret;
|
||||
|
||||
if (kstrtoul(buf, 0, &val))
|
||||
return -EINVAL;
|
||||
|
||||
/* select the engine to be run */
|
||||
|
||||
switch (val) {
|
||||
case LP55XX_ENGINE_1:
|
||||
case LP55XX_ENGINE_2:
|
||||
case LP55XX_ENGINE_3:
|
||||
mutex_lock(&chip->lock);
|
||||
chip->engine_idx = val;
|
||||
ret = lp55xx_request_firmware(chip);
|
||||
mutex_unlock(&chip->lock);
|
||||
break;
|
||||
default:
|
||||
dev_err(dev, "%lu: invalid engine index. (1, 2, 3)\n", val);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
dev_err(dev, "request firmware err: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static inline void lp55xx_run_engine(struct lp55xx_chip *chip, bool start)
|
||||
{
|
||||
if (chip->cfg->run_engine)
|
||||
chip->cfg->run_engine(chip, start);
|
||||
}
|
||||
|
||||
static ssize_t lp55xx_store_engine_run(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t len)
|
||||
{
|
||||
struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev));
|
||||
struct lp55xx_chip *chip = led->chip;
|
||||
unsigned long val;
|
||||
|
||||
if (kstrtoul(buf, 0, &val))
|
||||
return -EINVAL;
|
||||
|
||||
/* run or stop the selected engine */
|
||||
|
||||
if (val <= 0) {
|
||||
lp55xx_run_engine(chip, false);
|
||||
return len;
|
||||
}
|
||||
|
||||
mutex_lock(&chip->lock);
|
||||
lp55xx_run_engine(chip, true);
|
||||
mutex_unlock(&chip->lock);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(select_engine, S_IRUGO | S_IWUSR,
|
||||
lp55xx_show_engine_select, lp55xx_store_engine_select);
|
||||
static DEVICE_ATTR(run_engine, S_IWUSR, NULL, lp55xx_store_engine_run);
|
||||
|
||||
static struct attribute *lp55xx_engine_attributes[] = {
|
||||
&dev_attr_select_engine.attr,
|
||||
&dev_attr_run_engine.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group lp55xx_engine_attr_group = {
|
||||
.attrs = lp55xx_engine_attributes,
|
||||
};
|
||||
|
||||
int lp55xx_write(struct lp55xx_chip *chip, u8 reg, u8 val)
|
||||
{
|
||||
return i2c_smbus_write_byte_data(chip->cl, reg, val);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(lp55xx_write);
|
||||
|
||||
int lp55xx_read(struct lp55xx_chip *chip, u8 reg, u8 *val)
|
||||
{
|
||||
s32 ret;
|
||||
|
||||
ret = i2c_smbus_read_byte_data(chip->cl, reg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = ret;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(lp55xx_read);
|
||||
|
||||
int lp55xx_update_bits(struct lp55xx_chip *chip, u8 reg, u8 mask, u8 val)
|
||||
{
|
||||
int ret;
|
||||
u8 tmp;
|
||||
|
||||
ret = lp55xx_read(chip, reg, &tmp);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
tmp &= ~mask;
|
||||
tmp |= val & mask;
|
||||
|
||||
return lp55xx_write(chip, reg, tmp);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(lp55xx_update_bits);
|
||||
|
||||
int lp55xx_init_device(struct lp55xx_chip *chip)
|
||||
{
|
||||
struct lp55xx_platform_data *pdata;
|
||||
struct lp55xx_device_config *cfg;
|
||||
struct device *dev = &chip->cl->dev;
|
||||
int ret = 0;
|
||||
|
||||
WARN_ON(!chip);
|
||||
|
||||
pdata = chip->pdata;
|
||||
cfg = chip->cfg;
|
||||
|
||||
if (!pdata || !cfg)
|
||||
return -EINVAL;
|
||||
|
||||
if (pdata->setup_resources) {
|
||||
ret = pdata->setup_resources();
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "setup resoure err: %d\n", ret);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
if (pdata->enable) {
|
||||
pdata->enable(0);
|
||||
usleep_range(1000, 2000); /* Keep enable down at least 1ms */
|
||||
pdata->enable(1);
|
||||
usleep_range(1000, 2000); /* 500us abs min. */
|
||||
}
|
||||
|
||||
lp55xx_reset_device(chip);
|
||||
|
||||
/*
|
||||
* Exact value is not available. 10 - 20ms
|
||||
* appears to be enough for reset.
|
||||
*/
|
||||
usleep_range(10000, 20000);
|
||||
|
||||
ret = lp55xx_detect_device(chip);
|
||||
if (ret) {
|
||||
dev_err(dev, "device detection err: %d\n", ret);
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* chip specific initialization */
|
||||
ret = lp55xx_post_init_device(chip);
|
||||
if (ret) {
|
||||
dev_err(dev, "post init device err: %d\n", ret);
|
||||
goto err_post_init;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_post_init:
|
||||
lp55xx_deinit_device(chip);
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(lp55xx_init_device);
|
||||
|
||||
void lp55xx_deinit_device(struct lp55xx_chip *chip)
|
||||
{
|
||||
struct lp55xx_platform_data *pdata = chip->pdata;
|
||||
|
||||
if (pdata->enable)
|
||||
pdata->enable(0);
|
||||
|
||||
if (pdata->release_resources)
|
||||
pdata->release_resources();
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(lp55xx_deinit_device);
|
||||
|
||||
int lp55xx_register_leds(struct lp55xx_led *led, struct lp55xx_chip *chip)
|
||||
{
|
||||
struct lp55xx_platform_data *pdata = chip->pdata;
|
||||
struct lp55xx_device_config *cfg = chip->cfg;
|
||||
int num_channels = pdata->num_channels;
|
||||
struct lp55xx_led *each;
|
||||
u8 led_current;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
if (!cfg->brightness_work_fn) {
|
||||
dev_err(&chip->cl->dev, "empty brightness configuration\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_channels; i++) {
|
||||
|
||||
/* do not initialize channels that are not connected */
|
||||
if (pdata->led_config[i].led_current == 0)
|
||||
continue;
|
||||
|
||||
led_current = pdata->led_config[i].led_current;
|
||||
each = led + i;
|
||||
ret = lp55xx_init_led(each, chip, i);
|
||||
if (ret)
|
||||
goto err_init_led;
|
||||
|
||||
INIT_WORK(&each->brightness_work, cfg->brightness_work_fn);
|
||||
|
||||
chip->num_leds++;
|
||||
each->chip = chip;
|
||||
|
||||
/* setting led current at each channel */
|
||||
if (cfg->set_led_current)
|
||||
cfg->set_led_current(each, led_current);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_init_led:
|
||||
lp55xx_unregister_leds(led, chip);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(lp55xx_register_leds);
|
||||
|
||||
void lp55xx_unregister_leds(struct lp55xx_led *led, struct lp55xx_chip *chip)
|
||||
{
|
||||
int i;
|
||||
struct lp55xx_led *each;
|
||||
|
||||
for (i = 0; i < chip->num_leds; i++) {
|
||||
each = led + i;
|
||||
led_classdev_unregister(&each->cdev);
|
||||
flush_work(&each->brightness_work);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(lp55xx_unregister_leds);
|
||||
|
||||
int lp55xx_register_sysfs(struct lp55xx_chip *chip)
|
||||
{
|
||||
struct device *dev = &chip->cl->dev;
|
||||
struct lp55xx_device_config *cfg = chip->cfg;
|
||||
int ret;
|
||||
|
||||
if (!cfg->run_engine || !cfg->firmware_cb)
|
||||
goto dev_specific_attrs;
|
||||
|
||||
ret = sysfs_create_group(&dev->kobj, &lp55xx_engine_attr_group);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dev_specific_attrs:
|
||||
return cfg->dev_attr_group ?
|
||||
sysfs_create_group(&dev->kobj, cfg->dev_attr_group) : 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(lp55xx_register_sysfs);
|
||||
|
||||
void lp55xx_unregister_sysfs(struct lp55xx_chip *chip)
|
||||
{
|
||||
struct device *dev = &chip->cl->dev;
|
||||
struct lp55xx_device_config *cfg = chip->cfg;
|
||||
|
||||
if (cfg->dev_attr_group)
|
||||
sysfs_remove_group(&dev->kobj, cfg->dev_attr_group);
|
||||
|
||||
sysfs_remove_group(&dev->kobj, &lp55xx_engine_attr_group);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(lp55xx_unregister_sysfs);
|
||||
|
||||
MODULE_AUTHOR("Milo Kim <milo.kim@ti.com>");
|
||||
MODULE_DESCRIPTION("LP55xx Common Driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
* LP55XX Common Driver Header
|
||||
*
|
||||
* Copyright (C) 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.
|
||||
*
|
||||
* Derived from leds-lp5521.c, leds-lp5523.c
|
||||
*/
|
||||
|
||||
#ifndef _LEDS_LP55XX_COMMON_H
|
||||
#define _LEDS_LP55XX_COMMON_H
|
||||
|
||||
enum lp55xx_engine_index {
|
||||
LP55XX_ENGINE_INVALID,
|
||||
LP55XX_ENGINE_1,
|
||||
LP55XX_ENGINE_2,
|
||||
LP55XX_ENGINE_3,
|
||||
};
|
||||
|
||||
struct lp55xx_led;
|
||||
struct lp55xx_chip;
|
||||
|
||||
/*
|
||||
* struct lp55xx_reg
|
||||
* @addr : Register address
|
||||
* @val : Register value
|
||||
*/
|
||||
struct lp55xx_reg {
|
||||
u8 addr;
|
||||
u8 val;
|
||||
};
|
||||
|
||||
/*
|
||||
* struct lp55xx_device_config
|
||||
* @reset : Chip specific reset command
|
||||
* @enable : Chip specific enable command
|
||||
* @max_channel : Maximum number of channels
|
||||
* @post_init_device : Chip specific initialization code
|
||||
* @brightness_work_fn : Brightness work function
|
||||
* @set_led_current : LED current set function
|
||||
* @firmware_cb : Call function when the firmware is loaded
|
||||
* @run_engine : Run internal engine for pattern
|
||||
* @dev_attr_group : Device specific attributes
|
||||
*/
|
||||
struct lp55xx_device_config {
|
||||
const struct lp55xx_reg reset;
|
||||
const struct lp55xx_reg enable;
|
||||
const int max_channel;
|
||||
|
||||
/* define if the device has specific initialization process */
|
||||
int (*post_init_device) (struct lp55xx_chip *chip);
|
||||
|
||||
/* access brightness register */
|
||||
void (*brightness_work_fn)(struct work_struct *work);
|
||||
|
||||
/* current setting function */
|
||||
void (*set_led_current) (struct lp55xx_led *led, u8 led_current);
|
||||
|
||||
/* access program memory when the firmware is loaded */
|
||||
void (*firmware_cb)(struct lp55xx_chip *chip);
|
||||
|
||||
/* used for running firmware LED patterns */
|
||||
void (*run_engine) (struct lp55xx_chip *chip, bool start);
|
||||
|
||||
/* additional device specific attributes */
|
||||
const struct attribute_group *dev_attr_group;
|
||||
};
|
||||
|
||||
/*
|
||||
* struct lp55xx_chip
|
||||
* @cl : I2C communication for access registers
|
||||
* @pdata : Platform specific data
|
||||
* @lock : Lock for user-space interface
|
||||
* @num_leds : Number of registered LEDs
|
||||
* @cfg : Device specific configuration data
|
||||
* @engine_idx : Selected engine number
|
||||
* @fw : Firmware data for running a LED pattern
|
||||
*/
|
||||
struct lp55xx_chip {
|
||||
struct i2c_client *cl;
|
||||
struct lp55xx_platform_data *pdata;
|
||||
struct mutex lock; /* lock for user-space interface */
|
||||
int num_leds;
|
||||
struct lp55xx_device_config *cfg;
|
||||
enum lp55xx_engine_index engine_idx;
|
||||
const struct firmware *fw;
|
||||
};
|
||||
|
||||
/*
|
||||
* struct lp55xx_led
|
||||
* @chan_nr : Channel number
|
||||
* @cdev : LED class device
|
||||
* @led_current : Current setting at each led channel
|
||||
* @max_current : Maximun current at each led channel
|
||||
* @brightness_work : Workqueue for brightness control
|
||||
* @brightness : Brightness value
|
||||
* @chip : The lp55xx chip data
|
||||
*/
|
||||
struct lp55xx_led {
|
||||
int chan_nr;
|
||||
struct led_classdev cdev;
|
||||
u8 led_current;
|
||||
u8 max_current;
|
||||
struct work_struct brightness_work;
|
||||
u8 brightness;
|
||||
struct lp55xx_chip *chip;
|
||||
};
|
||||
|
||||
/* register access */
|
||||
extern int lp55xx_write(struct lp55xx_chip *chip, u8 reg, u8 val);
|
||||
extern int lp55xx_read(struct lp55xx_chip *chip, u8 reg, u8 *val);
|
||||
extern int lp55xx_update_bits(struct lp55xx_chip *chip, u8 reg,
|
||||
u8 mask, u8 val);
|
||||
|
||||
/* common device init/deinit functions */
|
||||
extern int lp55xx_init_device(struct lp55xx_chip *chip);
|
||||
extern void lp55xx_deinit_device(struct lp55xx_chip *chip);
|
||||
|
||||
/* common LED class device functions */
|
||||
extern int lp55xx_register_leds(struct lp55xx_led *led,
|
||||
struct lp55xx_chip *chip);
|
||||
extern void lp55xx_unregister_leds(struct lp55xx_led *led,
|
||||
struct lp55xx_chip *chip);
|
||||
|
||||
/* common device attributes functions */
|
||||
extern int lp55xx_register_sysfs(struct lp55xx_chip *chip);
|
||||
extern void lp55xx_unregister_sysfs(struct lp55xx_chip *chip);
|
||||
|
||||
#endif /* _LEDS_LP55XX_COMMON_H */
|
|
@ -130,9 +130,10 @@ static 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;
|
||||
struct device *dev = &pdev->dev;
|
||||
int ret;
|
||||
|
||||
led = devm_kzalloc(lp->dev, sizeof(struct lp8788_led), GFP_KERNEL);
|
||||
led = devm_kzalloc(dev, sizeof(struct lp8788_led), GFP_KERNEL);
|
||||
if (!led)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -154,13 +155,13 @@ static int lp8788_led_probe(struct platform_device *pdev)
|
|||
|
||||
ret = lp8788_led_init_device(led, led_pdata);
|
||||
if (ret) {
|
||||
dev_err(lp->dev, "led init device err: %d\n", ret);
|
||||
dev_err(dev, "led init device err: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = led_classdev_register(lp->dev, &led->led_dev);
|
||||
ret = led_classdev_register(dev, &led->led_dev);
|
||||
if (ret) {
|
||||
dev_err(lp->dev, "led register err: %d\n", ret);
|
||||
dev_err(dev, "led register err: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
@ -186,7 +186,7 @@ static int pca9532_set_blink(struct led_classdev *led_cdev,
|
|||
int err = 0;
|
||||
|
||||
if (*delay_on == 0 && *delay_off == 0) {
|
||||
/* led subsystem ask us for a blink rate */
|
||||
/* led subsystem ask us for a blink rate */
|
||||
*delay_on = 1000;
|
||||
*delay_off = 1000;
|
||||
}
|
||||
|
@ -311,7 +311,6 @@ static int pca9532_destroy_devices(struct pca9532_data *data, int n_devs)
|
|||
break;
|
||||
case PCA9532_TYPE_N2100_BEEP:
|
||||
if (data->idev != NULL) {
|
||||
input_unregister_device(data->idev);
|
||||
cancel_work_sync(&data->work);
|
||||
data->idev = NULL;
|
||||
}
|
||||
|
@ -382,7 +381,7 @@ static int pca9532_configure(struct i2c_client *client,
|
|||
BUG_ON(data->idev);
|
||||
led->state = PCA9532_PWM1;
|
||||
pca9532_setled(led);
|
||||
data->idev = input_allocate_device();
|
||||
data->idev = devm_input_allocate_device(&client->dev);
|
||||
if (data->idev == NULL) {
|
||||
err = -ENOMEM;
|
||||
goto exit;
|
||||
|
@ -401,7 +400,6 @@ static int pca9532_configure(struct i2c_client *client,
|
|||
INIT_WORK(&data->work, pca9532_input_work);
|
||||
err = input_register_device(data->idev);
|
||||
if (err) {
|
||||
input_free_device(data->idev);
|
||||
cancel_work_sync(&data->work);
|
||||
data->idev = NULL;
|
||||
goto exit;
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/fb.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/err.h>
|
||||
|
@ -30,6 +31,11 @@ struct led_pwm_data {
|
|||
unsigned int period;
|
||||
};
|
||||
|
||||
struct led_pwm_priv {
|
||||
int num_leds;
|
||||
struct led_pwm_data leds[0];
|
||||
};
|
||||
|
||||
static void led_pwm_set(struct led_classdev *led_cdev,
|
||||
enum led_brightness brightness)
|
||||
{
|
||||
|
@ -47,88 +53,152 @@ static void led_pwm_set(struct led_classdev *led_cdev,
|
|||
}
|
||||
}
|
||||
|
||||
static int led_pwm_probe(struct platform_device *pdev)
|
||||
static inline size_t sizeof_pwm_leds_priv(int num_leds)
|
||||
{
|
||||
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;
|
||||
return sizeof(struct led_pwm_priv) +
|
||||
(sizeof(struct led_pwm_data) * num_leds);
|
||||
}
|
||||
|
||||
if (!pdata)
|
||||
return -EBUSY;
|
||||
static struct led_pwm_priv *led_pwm_create_of(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
struct device_node *child;
|
||||
struct led_pwm_priv *priv;
|
||||
int count, ret;
|
||||
|
||||
leds_data = devm_kzalloc(&pdev->dev,
|
||||
sizeof(struct led_pwm_data) * pdata->num_leds,
|
||||
GFP_KERNEL);
|
||||
if (!leds_data)
|
||||
return -ENOMEM;
|
||||
/* count LEDs in this device, so we know how much to allocate */
|
||||
count = of_get_child_count(node);
|
||||
if (!count)
|
||||
return NULL;
|
||||
|
||||
for (i = 0; i < pdata->num_leds; i++) {
|
||||
cur_led = &pdata->leds[i];
|
||||
led_dat = &leds_data[i];
|
||||
priv = devm_kzalloc(&pdev->dev, sizeof_pwm_leds_priv(count),
|
||||
GFP_KERNEL);
|
||||
if (!priv)
|
||||
return NULL;
|
||||
|
||||
led_dat->pwm = pwm_request(cur_led->pwm_id,
|
||||
cur_led->name);
|
||||
for_each_child_of_node(node, child) {
|
||||
struct led_pwm_data *led_dat = &priv->leds[priv->num_leds];
|
||||
|
||||
led_dat->cdev.name = of_get_property(child, "label",
|
||||
NULL) ? : child->name;
|
||||
|
||||
led_dat->pwm = devm_of_pwm_get(&pdev->dev, child, NULL);
|
||||
if (IS_ERR(led_dat->pwm)) {
|
||||
ret = PTR_ERR(led_dat->pwm);
|
||||
dev_err(&pdev->dev, "unable to request PWM %d\n",
|
||||
cur_led->pwm_id);
|
||||
dev_err(&pdev->dev, "unable to request PWM for %s\n",
|
||||
led_dat->cdev.name);
|
||||
goto err;
|
||||
}
|
||||
/* Get the period from PWM core when n*/
|
||||
led_dat->period = pwm_get_period(led_dat->pwm);
|
||||
|
||||
led_dat->cdev.default_trigger = of_get_property(child,
|
||||
"linux,default-trigger", NULL);
|
||||
of_property_read_u32(child, "max-brightness",
|
||||
&led_dat->cdev.max_brightness);
|
||||
|
||||
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->period = cur_led->pwm_period_ns;
|
||||
led_dat->cdev.brightness_set = led_pwm_set;
|
||||
led_dat->cdev.brightness = LED_OFF;
|
||||
led_dat->cdev.max_brightness = cur_led->max_brightness;
|
||||
led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME;
|
||||
|
||||
ret = led_classdev_register(&pdev->dev, &led_dat->cdev);
|
||||
if (ret < 0) {
|
||||
pwm_free(led_dat->pwm);
|
||||
dev_err(&pdev->dev, "failed to register for %s\n",
|
||||
led_dat->cdev.name);
|
||||
of_node_put(child);
|
||||
goto err;
|
||||
}
|
||||
priv->num_leds++;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, leds_data);
|
||||
return priv;
|
||||
err:
|
||||
while (priv->num_leds--)
|
||||
led_classdev_unregister(&priv->leds[priv->num_leds].cdev);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int led_pwm_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct led_pwm_platform_data *pdata = pdev->dev.platform_data;
|
||||
struct led_pwm_priv *priv;
|
||||
int i, ret = 0;
|
||||
|
||||
if (pdata && pdata->num_leds) {
|
||||
priv = devm_kzalloc(&pdev->dev,
|
||||
sizeof_pwm_leds_priv(pdata->num_leds),
|
||||
GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < pdata->num_leds; i++) {
|
||||
struct led_pwm *cur_led = &pdata->leds[i];
|
||||
struct led_pwm_data *led_dat = &priv->leds[i];
|
||||
|
||||
led_dat->pwm = devm_pwm_get(&pdev->dev, cur_led->name);
|
||||
if (IS_ERR(led_dat->pwm)) {
|
||||
ret = PTR_ERR(led_dat->pwm);
|
||||
dev_err(&pdev->dev,
|
||||
"unable to request PWM for %s\n",
|
||||
cur_led->name);
|
||||
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->period = cur_led->pwm_period_ns;
|
||||
led_dat->cdev.brightness_set = led_pwm_set;
|
||||
led_dat->cdev.brightness = LED_OFF;
|
||||
led_dat->cdev.max_brightness = cur_led->max_brightness;
|
||||
led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME;
|
||||
|
||||
ret = led_classdev_register(&pdev->dev, &led_dat->cdev);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
}
|
||||
priv->num_leds = pdata->num_leds;
|
||||
} else {
|
||||
priv = led_pwm_create_of(pdev);
|
||||
if (!priv)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, priv);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
while (i--)
|
||||
led_classdev_unregister(&priv->leds[i].cdev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int led_pwm_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct led_pwm_priv *priv = platform_get_drvdata(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);
|
||||
}
|
||||
for (i = 0; i < priv->num_leds; i++)
|
||||
led_classdev_unregister(&priv->leds[i].cdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id of_pwm_leds_match[] = {
|
||||
{ .compatible = "pwm-leds", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, of_pwm_leds_match);
|
||||
|
||||
static struct platform_driver led_pwm_driver = {
|
||||
.probe = led_pwm_probe,
|
||||
.remove = led_pwm_remove,
|
||||
.driver = {
|
||||
.name = "leds_pwm",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_match_ptr(of_pwm_leds_match),
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -133,24 +133,24 @@ static int r_tpu_enable(struct r_tpu_priv *p, enum led_brightness brightness)
|
|||
rate = clk_get_rate(p->clk);
|
||||
|
||||
/* pick the lowest acceptable rate */
|
||||
for (k = 0; k < ARRAY_SIZE(prescaler); k++)
|
||||
if ((rate / prescaler[k]) < p->min_rate)
|
||||
for (k = ARRAY_SIZE(prescaler) - 1; k >= 0; k--)
|
||||
if ((rate / prescaler[k]) >= p->min_rate)
|
||||
break;
|
||||
|
||||
if (!k) {
|
||||
if (k < 0) {
|
||||
dev_err(&p->pdev->dev, "clock rate mismatch\n");
|
||||
goto err0;
|
||||
}
|
||||
dev_dbg(&p->pdev->dev, "rate = %lu, prescaler %u\n",
|
||||
rate, prescaler[k - 1]);
|
||||
rate, prescaler[k]);
|
||||
|
||||
/* clear TCNT on TGRB match, count on rising edge, set prescaler */
|
||||
r_tpu_write(p, TCR, 0x0040 | (k - 1));
|
||||
r_tpu_write(p, TCR, 0x0040 | k);
|
||||
|
||||
/* output 0 until TGRA, output 1 until TGRB */
|
||||
r_tpu_write(p, TIOR, 0x0002);
|
||||
|
||||
rate /= prescaler[k - 1] * p->refresh_rate;
|
||||
rate /= prescaler[k] * p->refresh_rate;
|
||||
r_tpu_write(p, TGRB, rate);
|
||||
dev_dbg(&p->pdev->dev, "TRGB = 0x%04lx\n", rate);
|
||||
|
||||
|
|
|
@ -63,8 +63,7 @@ MODULE_LICENSE("GPL");
|
|||
/*
|
||||
* PCI ID of the Intel ICH7 LPC Device within which the GPIO block lives.
|
||||
*/
|
||||
static const struct pci_device_id ich7_lpc_pci_id[] =
|
||||
{
|
||||
static DEFINE_PCI_DEVICE_TABLE(ich7_lpc_pci_id) = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_0) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_1) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_30) },
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
* Copyright (C) 2008 David S. Miller <davem@davemloft.net>
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
|
@ -14,9 +16,6 @@
|
|||
#include <asm/fhc.h>
|
||||
#include <asm/upa.h>
|
||||
|
||||
#define DRIVER_NAME "leds-sunfire"
|
||||
#define PFX DRIVER_NAME ": "
|
||||
|
||||
MODULE_AUTHOR("David S. Miller (davem@davemloft.net)");
|
||||
MODULE_DESCRIPTION("Sun Fire LED driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@ -130,14 +129,14 @@ static int sunfire_led_generic_probe(struct platform_device *pdev,
|
|||
int i, err;
|
||||
|
||||
if (pdev->num_resources != 1) {
|
||||
printk(KERN_ERR PFX "Wrong number of resources %d, should be 1\n",
|
||||
dev_err(&pdev->dev, "Wrong number of resources %d, should be 1\n",
|
||||
pdev->num_resources);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
p = devm_kzalloc(&pdev->dev, sizeof(*p), GFP_KERNEL);
|
||||
if (!p) {
|
||||
printk(KERN_ERR PFX "Could not allocate struct sunfire_drvdata\n");
|
||||
dev_err(&pdev->dev, "Could not allocate struct sunfire_drvdata\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
|
@ -152,7 +151,7 @@ static int sunfire_led_generic_probe(struct platform_device *pdev,
|
|||
|
||||
err = led_classdev_register(&pdev->dev, lp);
|
||||
if (err) {
|
||||
printk(KERN_ERR PFX "Could not register %s LED\n",
|
||||
dev_err(&pdev->dev, "Could not register %s LED\n",
|
||||
lp->name);
|
||||
for (i--; i >= 0; i--)
|
||||
led_classdev_unregister(&p->leds[i].led_cdev);
|
||||
|
@ -188,7 +187,7 @@ static struct led_type clockboard_led_types[NUM_LEDS_PER_BOARD] = {
|
|||
{
|
||||
.name = "clockboard-right",
|
||||
.handler = clockboard_right_set,
|
||||
.default_trigger= "heartbeat",
|
||||
.default_trigger = "heartbeat",
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -209,7 +208,7 @@ static struct led_type fhc_led_types[NUM_LEDS_PER_BOARD] = {
|
|||
{
|
||||
.name = "fhc-right",
|
||||
.handler = fhc_right_set,
|
||||
.default_trigger= "heartbeat",
|
||||
.default_trigger = "heartbeat",
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -244,13 +243,13 @@ static int __init sunfire_leds_init(void)
|
|||
int err = platform_driver_register(&sunfire_clockboard_led_driver);
|
||||
|
||||
if (err) {
|
||||
printk(KERN_ERR PFX "Could not register clock board LED driver\n");
|
||||
pr_err("Could not register clock board LED driver\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
err = platform_driver_register(&sunfire_fhc_led_driver);
|
||||
if (err) {
|
||||
printk(KERN_ERR PFX "Could not register FHC LED driver\n");
|
||||
pr_err("Could not register FHC LED driver\n");
|
||||
platform_driver_unregister(&sunfire_clockboard_led_driver);
|
||||
}
|
||||
|
||||
|
|
|
@ -667,8 +667,68 @@ static void tca6507_remove_gpio(struct tca6507_chip *tca)
|
|||
}
|
||||
#endif /* CONFIG_GPIOLIB */
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static struct tca6507_platform_data *
|
||||
tca6507_led_dt_init(struct i2c_client *client)
|
||||
{
|
||||
struct device_node *np = client->dev.of_node, *child;
|
||||
struct tca6507_platform_data *pdata;
|
||||
struct led_info *tca_leds;
|
||||
int count;
|
||||
|
||||
count = of_get_child_count(np);
|
||||
if (!count || count > NUM_LEDS)
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
tca_leds = devm_kzalloc(&client->dev,
|
||||
sizeof(struct led_info) * count, GFP_KERNEL);
|
||||
if (!tca_leds)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
for_each_child_of_node(np, child) {
|
||||
struct led_info led;
|
||||
u32 reg;
|
||||
int ret;
|
||||
|
||||
led.name =
|
||||
of_get_property(child, "label", NULL) ? : child->name;
|
||||
led.default_trigger =
|
||||
of_get_property(child, "linux,default-trigger", NULL);
|
||||
|
||||
ret = of_property_read_u32(child, "reg", ®);
|
||||
if (ret != 0)
|
||||
continue;
|
||||
|
||||
tca_leds[reg] = led;
|
||||
}
|
||||
pdata = devm_kzalloc(&client->dev,
|
||||
sizeof(struct tca6507_platform_data), GFP_KERNEL);
|
||||
if (!pdata)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
pdata->leds.leds = tca_leds;
|
||||
pdata->leds.num_leds = count;
|
||||
|
||||
return pdata;
|
||||
}
|
||||
|
||||
static const struct of_device_id of_tca6507_leds_match[] = {
|
||||
{ .compatible = "ti,tca6507", },
|
||||
{},
|
||||
};
|
||||
|
||||
#else
|
||||
static struct tca6507_platform_data *
|
||||
tca6507_led_dt_init(struct i2c_client *client)
|
||||
{
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
#define of_tca6507_leds_match NULL
|
||||
#endif
|
||||
|
||||
static int tca6507_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct tca6507_chip *tca;
|
||||
struct i2c_adapter *adapter;
|
||||
|
@ -683,9 +743,12 @@ static int tca6507_probe(struct i2c_client *client,
|
|||
return -EIO;
|
||||
|
||||
if (!pdata || pdata->leds.num_leds != NUM_LEDS) {
|
||||
dev_err(&client->dev, "Need %d entries in platform-data list\n",
|
||||
NUM_LEDS);
|
||||
return -ENODEV;
|
||||
pdata = tca6507_led_dt_init(client);
|
||||
if (IS_ERR(pdata)) {
|
||||
dev_err(&client->dev, "Need %d entries in platform-data list\n",
|
||||
NUM_LEDS);
|
||||
return PTR_ERR(pdata);
|
||||
}
|
||||
}
|
||||
tca = devm_kzalloc(&client->dev, sizeof(*tca), GFP_KERNEL);
|
||||
if (!tca)
|
||||
|
@ -750,6 +813,7 @@ static struct i2c_driver tca6507_driver = {
|
|||
.driver = {
|
||||
.name = "leds-tca6507",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_tca6507_leds_match,
|
||||
},
|
||||
.probe = tca6507_probe,
|
||||
.remove = tca6507_remove,
|
||||
|
|
|
@ -157,7 +157,7 @@ static int wm831x_status_blink_set(struct led_classdev *led_cdev,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static const char *led_src_texts[] = {
|
||||
static const char * const led_src_texts[] = {
|
||||
"otp",
|
||||
"power",
|
||||
"charger",
|
||||
|
|
|
@ -471,7 +471,7 @@ static struct pwm_chip *of_node_to_pwmchip(struct device_node *np)
|
|||
}
|
||||
|
||||
/**
|
||||
* of_pwm_request() - request a PWM via the PWM framework
|
||||
* of_pwm_get() - request a PWM via the PWM framework
|
||||
* @np: device node to get the PWM from
|
||||
* @con_id: consumer name
|
||||
*
|
||||
|
@ -486,8 +486,7 @@ static struct pwm_chip *of_node_to_pwmchip(struct device_node *np)
|
|||
* becomes mandatory for devices that look up the PWM device via the con_id
|
||||
* parameter.
|
||||
*/
|
||||
static struct pwm_device *of_pwm_request(struct device_node *np,
|
||||
const char *con_id)
|
||||
struct pwm_device *of_pwm_get(struct device_node *np, const char *con_id)
|
||||
{
|
||||
struct pwm_device *pwm = NULL;
|
||||
struct of_phandle_args args;
|
||||
|
@ -545,6 +544,7 @@ put:
|
|||
|
||||
return pwm;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(of_pwm_get);
|
||||
|
||||
/**
|
||||
* pwm_add_table() - register PWM device consumers
|
||||
|
@ -587,7 +587,7 @@ struct pwm_device *pwm_get(struct device *dev, const char *con_id)
|
|||
|
||||
/* look up via DT first */
|
||||
if (IS_ENABLED(CONFIG_OF) && dev && dev->of_node)
|
||||
return of_pwm_request(dev->of_node, con_id);
|
||||
return of_pwm_get(dev->of_node, con_id);
|
||||
|
||||
/*
|
||||
* We look up the provider in the static table typically provided by
|
||||
|
@ -708,6 +708,36 @@ struct pwm_device *devm_pwm_get(struct device *dev, const char *con_id)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(devm_pwm_get);
|
||||
|
||||
/**
|
||||
* devm_of_pwm_get() - resource managed of_pwm_get()
|
||||
* @dev: device for PWM consumer
|
||||
* @np: device node to get the PWM from
|
||||
* @con_id: consumer name
|
||||
*
|
||||
* This function performs like of_pwm_get() but the acquired PWM device will
|
||||
* automatically be released on driver detach.
|
||||
*/
|
||||
struct pwm_device *devm_of_pwm_get(struct device *dev, struct device_node *np,
|
||||
const char *con_id)
|
||||
{
|
||||
struct pwm_device **ptr, *pwm;
|
||||
|
||||
ptr = devres_alloc(devm_pwm_release, sizeof(**ptr), GFP_KERNEL);
|
||||
if (!ptr)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
pwm = of_pwm_get(np, con_id);
|
||||
if (!IS_ERR(pwm)) {
|
||||
*ptr = pwm;
|
||||
devres_add(dev, ptr);
|
||||
} else {
|
||||
devres_free(ptr);
|
||||
}
|
||||
|
||||
return pwm;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_of_pwm_get);
|
||||
|
||||
static int devm_pwm_match(struct device *dev, void *res, void *data)
|
||||
{
|
||||
struct pwm_device **p = res;
|
||||
|
|
|
@ -1,73 +0,0 @@
|
|||
/*
|
||||
* LP5521 LED chip driver.
|
||||
*
|
||||
* Copyright (C) 2010 Nokia Corporation
|
||||
*
|
||||
* Contact: Samu Onkalo <samu.p.onkalo@nokia.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.
|
||||
*
|
||||
* 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., 51 Franklin St, Fifth Floor, Boston, MA
|
||||
* 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_LP5521_H
|
||||
#define __LINUX_LP5521_H
|
||||
|
||||
/* See Documentation/leds/leds-lp5521.txt */
|
||||
|
||||
struct lp5521_led_config {
|
||||
char *name;
|
||||
u8 chan_nr;
|
||||
u8 led_current; /* mA x10, 0 if led is not connected */
|
||||
u8 max_current;
|
||||
};
|
||||
|
||||
struct lp5521_led_pattern {
|
||||
u8 *r;
|
||||
u8 *g;
|
||||
u8 *b;
|
||||
u8 size_r;
|
||||
u8 size_g;
|
||||
u8 size_b;
|
||||
};
|
||||
|
||||
#define LP5521_CLOCK_AUTO 0
|
||||
#define LP5521_CLOCK_INT 1
|
||||
#define LP5521_CLOCK_EXT 2
|
||||
|
||||
/* Bits in CONFIG register */
|
||||
#define LP5521_PWM_HF 0x40 /* PWM: 0 = 256Hz, 1 = 558Hz */
|
||||
#define LP5521_PWRSAVE_EN 0x20 /* 1 = Power save mode */
|
||||
#define LP5521_CP_MODE_OFF 0 /* Charge pump (CP) off */
|
||||
#define LP5521_CP_MODE_BYPASS 8 /* CP forced to bypass mode */
|
||||
#define LP5521_CP_MODE_1X5 0x10 /* CP forced to 1.5x mode */
|
||||
#define LP5521_CP_MODE_AUTO 0x18 /* Automatic mode selection */
|
||||
#define LP5521_R_TO_BATT 4 /* R out: 0 = CP, 1 = Vbat */
|
||||
#define LP5521_CLK_SRC_EXT 0 /* Ext-clk source (CLK_32K) */
|
||||
#define LP5521_CLK_INT 1 /* Internal clock */
|
||||
#define LP5521_CLK_AUTO 2 /* Automatic clock selection */
|
||||
|
||||
struct lp5521_platform_data {
|
||||
struct lp5521_led_config *led_config;
|
||||
u8 num_channels;
|
||||
u8 clock_mode;
|
||||
int (*setup_resources)(void);
|
||||
void (*release_resources)(void);
|
||||
void (*enable)(bool state);
|
||||
const char *label;
|
||||
u8 update_config;
|
||||
struct lp5521_led_pattern *patterns;
|
||||
int num_patterns;
|
||||
};
|
||||
|
||||
#endif /* __LINUX_LP5521_H */
|
|
@ -1,49 +0,0 @@
|
|||
/*
|
||||
* LP5523 LED Driver
|
||||
*
|
||||
* Copyright (C) 2010 Nokia Corporation
|
||||
*
|
||||
* Contact: Samu Onkalo <samu.p.onkalo@nokia.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.
|
||||
*
|
||||
* 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., 51 Franklin St, Fifth Floor, Boston, MA
|
||||
* 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_LP5523_H
|
||||
#define __LINUX_LP5523_H
|
||||
|
||||
/* See Documentation/leds/leds-lp5523.txt */
|
||||
|
||||
struct lp5523_led_config {
|
||||
const char *name;
|
||||
u8 chan_nr;
|
||||
u8 led_current; /* mA x10, 0 if led is not connected */
|
||||
u8 max_current;
|
||||
};
|
||||
|
||||
#define LP5523_CLOCK_AUTO 0
|
||||
#define LP5523_CLOCK_INT 1
|
||||
#define LP5523_CLOCK_EXT 2
|
||||
|
||||
struct lp5523_platform_data {
|
||||
struct lp5523_led_config *led_config;
|
||||
u8 num_channels;
|
||||
u8 clock_mode;
|
||||
int (*setup_resources)(void);
|
||||
void (*release_resources)(void);
|
||||
void (*enable)(bool state);
|
||||
const char *label;
|
||||
};
|
||||
|
||||
#endif /* __LINUX_LP5523_H */
|
|
@ -7,7 +7,7 @@
|
|||
struct led_pwm {
|
||||
const char *name;
|
||||
const char *default_trigger;
|
||||
unsigned pwm_id;
|
||||
unsigned pwm_id __deprecated;
|
||||
u8 active_low;
|
||||
unsigned max_brightness;
|
||||
unsigned pwm_period_ns;
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
* LP55XX Platform Data Header
|
||||
*
|
||||
* Copyright (C) 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.
|
||||
*
|
||||
* Derived from leds-lp5521.h, leds-lp5523.h
|
||||
*/
|
||||
|
||||
#ifndef _LEDS_LP55XX_H
|
||||
#define _LEDS_LP55XX_H
|
||||
|
||||
/* Clock configuration */
|
||||
#define LP55XX_CLOCK_AUTO 0
|
||||
#define LP55XX_CLOCK_INT 1
|
||||
#define LP55XX_CLOCK_EXT 2
|
||||
|
||||
/* Bits in LP5521 CONFIG register. 'update_config' in lp55xx_platform_data */
|
||||
#define LP5521_PWM_HF 0x40 /* PWM: 0 = 256Hz, 1 = 558Hz */
|
||||
#define LP5521_PWRSAVE_EN 0x20 /* 1 = Power save mode */
|
||||
#define LP5521_CP_MODE_OFF 0 /* Charge pump (CP) off */
|
||||
#define LP5521_CP_MODE_BYPASS 8 /* CP forced to bypass mode */
|
||||
#define LP5521_CP_MODE_1X5 0x10 /* CP forced to 1.5x mode */
|
||||
#define LP5521_CP_MODE_AUTO 0x18 /* Automatic mode selection */
|
||||
#define LP5521_R_TO_BATT 4 /* R out: 0 = CP, 1 = Vbat */
|
||||
#define LP5521_CLK_SRC_EXT 0 /* Ext-clk source (CLK_32K) */
|
||||
#define LP5521_CLK_INT 1 /* Internal clock */
|
||||
#define LP5521_CLK_AUTO 2 /* Automatic clock selection */
|
||||
|
||||
struct lp55xx_led_config {
|
||||
const char *name;
|
||||
u8 chan_nr;
|
||||
u8 led_current; /* mA x10, 0 if led is not connected */
|
||||
u8 max_current;
|
||||
};
|
||||
|
||||
struct lp55xx_predef_pattern {
|
||||
u8 *r;
|
||||
u8 *g;
|
||||
u8 *b;
|
||||
u8 size_r;
|
||||
u8 size_g;
|
||||
u8 size_b;
|
||||
};
|
||||
|
||||
/*
|
||||
* struct lp55xx_platform_data
|
||||
* @led_config : Configurable led class device
|
||||
* @num_channels : Number of LED channels
|
||||
* @label : Used for naming LEDs
|
||||
* @clock_mode : Input clock mode. LP55XX_CLOCK_AUTO or _INT or _EXT
|
||||
* @setup_resources : Platform specific function before enabling the chip
|
||||
* @release_resources : Platform specific function after disabling the chip
|
||||
* @enable : EN pin control by platform side
|
||||
* @patterns : Predefined pattern data for RGB channels
|
||||
* @num_patterns : Number of patterns
|
||||
* @update_config : Value of CONFIG register
|
||||
*/
|
||||
struct lp55xx_platform_data {
|
||||
|
||||
/* LED channel configuration */
|
||||
struct lp55xx_led_config *led_config;
|
||||
u8 num_channels;
|
||||
const char *label;
|
||||
|
||||
/* Clock configuration */
|
||||
u8 clock_mode;
|
||||
|
||||
/* Platform specific functions */
|
||||
int (*setup_resources)(void);
|
||||
void (*release_resources)(void);
|
||||
void (*enable)(bool state);
|
||||
|
||||
/* Predefined pattern data */
|
||||
struct lp55xx_predef_pattern *patterns;
|
||||
unsigned int num_patterns;
|
||||
|
||||
/* _CONFIG register */
|
||||
u8 update_config;
|
||||
};
|
||||
|
||||
#endif /* _LEDS_LP55XX_H */
|
|
@ -174,10 +174,13 @@ struct pwm_device *pwm_request_from_chip(struct pwm_chip *chip,
|
|||
struct pwm_device *of_pwm_xlate_with_flags(struct pwm_chip *pc,
|
||||
const struct of_phandle_args *args);
|
||||
|
||||
struct pwm_device *pwm_get(struct device *dev, const char *consumer);
|
||||
struct pwm_device *pwm_get(struct device *dev, const char *con_id);
|
||||
struct pwm_device *of_pwm_get(struct device_node *np, const char *con_id);
|
||||
void pwm_put(struct pwm_device *pwm);
|
||||
|
||||
struct pwm_device *devm_pwm_get(struct device *dev, const char *consumer);
|
||||
struct pwm_device *devm_pwm_get(struct device *dev, const char *con_id);
|
||||
struct pwm_device *devm_of_pwm_get(struct device *dev, struct device_node *np,
|
||||
const char *con_id);
|
||||
void devm_pwm_put(struct device *dev, struct pwm_device *pwm);
|
||||
#else
|
||||
static inline int pwm_set_chip_data(struct pwm_device *pwm, void *data)
|
||||
|
@ -213,6 +216,12 @@ static inline struct pwm_device *pwm_get(struct device *dev,
|
|||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
static inline struct pwm_device *of_pwm_get(struct device_node *np,
|
||||
const char *con_id)
|
||||
{
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
static inline void pwm_put(struct pwm_device *pwm)
|
||||
{
|
||||
}
|
||||
|
@ -223,6 +232,13 @@ static inline struct pwm_device *devm_pwm_get(struct device *dev,
|
|||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
static inline struct pwm_device *devm_of_pwm_get(struct device *dev,
|
||||
struct device_node *np,
|
||||
const char *con_id)
|
||||
{
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
static inline void devm_pwm_put(struct device *dev, struct pwm_device *pwm)
|
||||
{
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue