mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-11 12:28:41 +08:00
Merge branch 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/cooloney/linux-leds
Pull led updates from Bryan Wu: "Sorry for the late pull request, since I'm just back from vacation. LED subsystem updates for 3.12: - pca9633 driver DT supporting and pca9634 chip supporting - restore legacy device attributes for lp5521 - other fixing and updates" * 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/cooloney/linux-leds: (28 commits) leds: wm831x-status: Request a REG resource leds: trigger: ledtrig-backlight: Fix invalid memory access in fb_event notification callback leds-pca963x: Fix device tree parsing leds-pca9633: Rename to leds-pca963x leds-pca9633: Add mutex to the ledout register leds-pca9633: Unique naming of the LEDs leds-pca9633: Add support for PCA9634 leds: lp5562: use LP55xx common macros for device attributes Documentation: leds-lp5521,lp5523: update device attribute information leds: lp5523: remove unnecessary writing commands leds: lp5523: restore legacy device attributes leds: lp5523: LED MUX configuration on initializing leds: lp5523: make separate API for loading engine leds: lp5521: remove unnecessary writing commands leds: lp5521: restore legacy device attributes leds: lp55xx: add common macros for device attributes leds: lp55xx: add common data structure for program Documentation: leds: Fix a typo leds: ss4200: Fix incorrect placement of __initdata leds: clevo-mail: Fix incorrect placement of __initdata ...
This commit is contained in:
commit
5223161dc0
@ -1,7 +1,7 @@
|
||||
Binding for TI/National Semiconductor LP55xx Led Drivers
|
||||
|
||||
Required properties:
|
||||
- compatible: "national,lp5521" or "national,lp5523" or "ti,lp5562"
|
||||
- compatible: "national,lp5521" or "national,lp5523" or "ti,lp5562" or "ti,lp8501"
|
||||
- reg: I2C slave address
|
||||
- clock-mode: Input clock mode, (0: automode, 1: internal, 2: external)
|
||||
|
||||
@ -11,6 +11,11 @@ Each child has own specific current settings
|
||||
|
||||
Optional properties:
|
||||
- label: Used for naming LEDs
|
||||
- pwr-sel: LP8501 specific property. Power selection for output channels.
|
||||
0: D1~9 are connected to VDD
|
||||
1: D1~6 with VDD, D7~9 with VOUT
|
||||
2: D1~6 with VOUT, D7~9 with VDD
|
||||
3: D1~9 are connected to VOUT
|
||||
|
||||
Alternatively, each child can have specific channel name
|
||||
- chan-name: Name of each channel name
|
||||
@ -145,3 +150,68 @@ lp5562@30 {
|
||||
max-cur = /bits/ 8 <0x60>;
|
||||
};
|
||||
};
|
||||
|
||||
example 4) LP8501
|
||||
9 channels are defined. The 'pwr-sel' is LP8501 specific property.
|
||||
Others are same as LP5523.
|
||||
|
||||
lp8501@32 {
|
||||
compatible = "ti,lp8501";
|
||||
reg = <0x32>;
|
||||
clock-mode = /bits/ 8 <2>;
|
||||
pwr-sel = /bits/ 8 <3>; /* D1~9 connected to VOUT */
|
||||
|
||||
chan0 {
|
||||
chan-name = "d1";
|
||||
led-cur = /bits/ 8 <0x14>;
|
||||
max-cur = /bits/ 8 <0x20>;
|
||||
};
|
||||
|
||||
chan1 {
|
||||
chan-name = "d2";
|
||||
led-cur = /bits/ 8 <0x14>;
|
||||
max-cur = /bits/ 8 <0x20>;
|
||||
};
|
||||
|
||||
chan2 {
|
||||
chan-name = "d3";
|
||||
led-cur = /bits/ 8 <0x14>;
|
||||
max-cur = /bits/ 8 <0x20>;
|
||||
};
|
||||
|
||||
chan3 {
|
||||
chan-name = "d4";
|
||||
led-cur = /bits/ 8 <0x14>;
|
||||
max-cur = /bits/ 8 <0x20>;
|
||||
};
|
||||
|
||||
chan4 {
|
||||
chan-name = "d5";
|
||||
led-cur = /bits/ 8 <0x14>;
|
||||
max-cur = /bits/ 8 <0x20>;
|
||||
};
|
||||
|
||||
chan5 {
|
||||
chan-name = "d6";
|
||||
led-cur = /bits/ 8 <0x14>;
|
||||
max-cur = /bits/ 8 <0x20>;
|
||||
};
|
||||
|
||||
chan6 {
|
||||
chan-name = "d7";
|
||||
led-cur = /bits/ 8 <0x14>;
|
||||
max-cur = /bits/ 8 <0x20>;
|
||||
};
|
||||
|
||||
chan7 {
|
||||
chan-name = "d8";
|
||||
led-cur = /bits/ 8 <0x14>;
|
||||
max-cur = /bits/ 8 <0x20>;
|
||||
};
|
||||
|
||||
chan8 {
|
||||
chan-name = "d9";
|
||||
led-cur = /bits/ 8 <0x14>;
|
||||
max-cur = /bits/ 8 <0x20>;
|
||||
};
|
||||
};
|
||||
|
47
Documentation/devicetree/bindings/leds/pca963x.txt
Normal file
47
Documentation/devicetree/bindings/leds/pca963x.txt
Normal file
@ -0,0 +1,47 @@
|
||||
LEDs connected to pca9632, pca9633 or pca9634
|
||||
|
||||
Required properties:
|
||||
- compatible : should be : "nxp,pca9632", "nxp,pca9633" or "nxp,pca9634"
|
||||
|
||||
Optional properties:
|
||||
- nxp,totem-pole : use totem pole (push-pull) instead of default open-drain
|
||||
- nxp,hw-blink : use hardware blinking instead of software blinking
|
||||
|
||||
Each led is represented as a sub-node of the nxp,pca963x device.
|
||||
|
||||
LED sub-node properties:
|
||||
- label : (optional) see Documentation/devicetree/bindings/leds/common.txt
|
||||
- reg : number of LED line (could be from 0 to 3 in pca9632 or pca9633
|
||||
or 0 to 7 in pca9634)
|
||||
- linux,default-trigger : (optional)
|
||||
see Documentation/devicetree/bindings/leds/common.txt
|
||||
|
||||
Examples:
|
||||
|
||||
pca9632: pca9632 {
|
||||
compatible = "nxp,pca9632";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
reg = <0x62>;
|
||||
|
||||
red@0 {
|
||||
label = "red";
|
||||
reg = <0>;
|
||||
linux,default-trigger = "none";
|
||||
};
|
||||
green@1 {
|
||||
label = "green";
|
||||
reg = <1>;
|
||||
linux,default-trigger = "none";
|
||||
};
|
||||
blue@2 {
|
||||
label = "blue";
|
||||
reg = <2>;
|
||||
linux,default-trigger = "none";
|
||||
};
|
||||
unused@3 {
|
||||
label = "unused";
|
||||
reg = <3>;
|
||||
linux,default-trigger = "none";
|
||||
};
|
||||
};
|
@ -18,7 +18,25 @@ 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.
|
||||
|
||||
LP5521 has the internal program memory for running various LED patterns.
|
||||
For the details, please refer to 'firmware' section in leds-lp55xx.txt
|
||||
There are two ways to run LED patterns.
|
||||
|
||||
1) Legacy interface - enginex_mode and enginex_load
|
||||
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
|
||||
|
||||
To stop the engine:
|
||||
echo "disabled" > engine3_mode
|
||||
|
||||
2) Firmware interface - LP55xx common interface
|
||||
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
|
||||
|
@ -28,7 +28,26 @@ If both fields are NULL, 'lp5523' is used by default.
|
||||
/sys/class/leds/lp5523:channelN (N: 0 ~ 8)
|
||||
|
||||
LP5523 has the internal program memory for running various LED patterns.
|
||||
For the details, please refer to 'firmware' section in leds-lp55xx.txt
|
||||
There are two ways to run LED patterns.
|
||||
|
||||
1) Legacy interface - enginex_mode, enginex_load and enginex_leds
|
||||
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
|
||||
|
||||
To stop the engine:
|
||||
echo "disabled" > engine3_mode
|
||||
|
||||
2) Firmware interface - LP55xx common interface
|
||||
For the details, please refer to 'firmware' section in leds-lp55xx.txt
|
||||
|
||||
Selftest uses always the current from the platform data.
|
||||
|
||||
|
@ -1,11 +1,11 @@
|
||||
LP5521/LP5523/LP55231 Common Driver
|
||||
===================================
|
||||
LP5521/LP5523/LP55231/LP5562/LP8501 Common Driver
|
||||
=================================================
|
||||
|
||||
Authors: Milo(Woogyom) Kim <milo.kim@ti.com>
|
||||
|
||||
Description
|
||||
-----------
|
||||
LP5521, LP5523/55231 and LP5562 have common features as below.
|
||||
LP5521, LP5523/55231, LP5562 and LP8501 have common features as below.
|
||||
|
||||
Register access via the I2C
|
||||
Device initialization/deinitialization
|
||||
@ -109,6 +109,30 @@ 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.
|
||||
|
||||
The pattern sqeuence of LP8501 is same as LP5523.
|
||||
However pattern data is specific.
|
||||
Ex 1) Engine 1 is used
|
||||
echo 1 > /sys/bus/i2c/devices/xxxx/select_engine
|
||||
echo 1 > /sys/class/firmware/lp8501/loading
|
||||
echo "9d0140ff7e0040007e00a001c000" > /sys/class/firmware/lp8501/data
|
||||
echo 0 > /sys/class/firmware/lp8501/loading
|
||||
echo 1 > /sys/bus/i2c/devices/xxxx/run_engine
|
||||
|
||||
Ex 2) Engine 2 and 3 are used at the same time
|
||||
echo 2 > /sys/bus/i2c/devices/xxxx/select_engine
|
||||
sleep 1
|
||||
echo 1 > /sys/class/firmware/lp8501/loading
|
||||
echo "9d0140ff7e0040007e00a001c000" > /sys/class/firmware/lp8501/data
|
||||
echo 0 > /sys/class/firmware/lp8501/loading
|
||||
sleep 1
|
||||
echo 3 > /sys/bus/i2c/devices/xxxx/select_engine
|
||||
sleep 1
|
||||
echo 1 > /sys/class/firmware/lp8501/loading
|
||||
echo "9d0340ff7e0040007e00a001c000" > /sys/class/firmware/lp8501/data
|
||||
echo 0 > /sys/class/firmware/lp8501/loading
|
||||
sleep 1
|
||||
echo 1 > /sys/class/leds/d1/device/run_engine
|
||||
|
||||
( 'run_engine' and 'firmware_cb' )
|
||||
The sequence of running the program data is common.
|
||||
But each device has own specific register addresses for commands.
|
||||
|
@ -194,11 +194,11 @@ config LEDS_LP3944
|
||||
module will be called leds-lp3944.
|
||||
|
||||
config LEDS_LP55XX_COMMON
|
||||
tristate "Common Driver for TI/National LP5521, LP5523/55231 and LP5562"
|
||||
depends on LEDS_LP5521 || LEDS_LP5523 || LEDS_LP5562
|
||||
tristate "Common Driver for TI/National LP5521/5523/55231/5562/8501"
|
||||
depends on LEDS_LP5521 || LEDS_LP5523 || LEDS_LP5562 || LEDS_LP8501
|
||||
select FW_LOADER
|
||||
help
|
||||
This option supports common operations for LP5521 and LP5523/55231
|
||||
This option supports common operations for LP5521/5523/55231/5562/8501
|
||||
devices.
|
||||
|
||||
config LEDS_LP5521
|
||||
@ -232,6 +232,18 @@ config LEDS_LP5562
|
||||
Driver provides direct control via LED class and interface for
|
||||
programming the engines.
|
||||
|
||||
config LEDS_LP8501
|
||||
tristate "LED Support for TI LP8501 LED driver chip"
|
||||
depends on LEDS_CLASS && I2C
|
||||
select LEDS_LP55XX_COMMON
|
||||
help
|
||||
If you say yes here you get support for TI LP8501 LED driver.
|
||||
It is 9 channel chip with programmable engines.
|
||||
Driver provides direct control via LED class and interface for
|
||||
programming the engines.
|
||||
It is similar as LP5523, but output power selection is available.
|
||||
And register layout and engine program schemes are different.
|
||||
|
||||
config LEDS_LP8788
|
||||
tristate "LED support for the TI LP8788 PMIC"
|
||||
depends on LEDS_CLASS
|
||||
@ -279,13 +291,14 @@ config LEDS_PCA955X
|
||||
LED driver chips accessed via the I2C bus. Supported
|
||||
devices include PCA9550, PCA9551, PCA9552, and PCA9553.
|
||||
|
||||
config LEDS_PCA9633
|
||||
tristate "LED support for PCA9633 I2C chip"
|
||||
config LEDS_PCA963X
|
||||
tristate "LED support for PCA963x I2C chip"
|
||||
depends on LEDS_CLASS
|
||||
depends on I2C
|
||||
help
|
||||
This option enables support for LEDs connected to the PCA9633
|
||||
LED driver chip accessed via the I2C bus.
|
||||
This option enables support for LEDs connected to the PCA963x
|
||||
LED driver chip accessed via the I2C bus. Supported
|
||||
devices include PCA9633 and PCA9634
|
||||
|
||||
config LEDS_WM831X_STATUS
|
||||
tristate "LED support for status LEDs on WM831x PMICs"
|
||||
@ -398,10 +411,7 @@ config LEDS_MC13783
|
||||
config LEDS_NS2
|
||||
tristate "LED support for Network Space v2 GPIO LEDs"
|
||||
depends on LEDS_CLASS
|
||||
depends on MACH_NETSPACE_V2 || MACH_INETSPACE_V2 || \
|
||||
MACH_NETSPACE_MAX_V2 || MACH_D2NET_V2 || \
|
||||
MACH_NETSPACE_V2_DT || MACH_INETSPACE_V2_DT || \
|
||||
MACH_NETSPACE_MAX_V2_DT || MACH_NETSPACE_MINI_V2_DT
|
||||
depends on ARCH_KIRKWOOD
|
||||
default y
|
||||
help
|
||||
This option enable support for the dual-GPIO LED found on the
|
||||
@ -410,8 +420,8 @@ config LEDS_NS2
|
||||
|
||||
config LEDS_NETXBIG
|
||||
tristate "LED support for Big Network series LEDs"
|
||||
depends on MACH_NET2BIG_V2 || MACH_NET5BIG_V2
|
||||
depends on LEDS_CLASS
|
||||
depends on ARCH_KIRKWOOD
|
||||
default y
|
||||
help
|
||||
This option enable support for LEDs found on the LaCie 2Big
|
||||
|
@ -27,6 +27,7 @@ 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_LP5562) += leds-lp5562.o
|
||||
obj-$(CONFIG_LEDS_LP8501) += leds-lp8501.o
|
||||
obj-$(CONFIG_LEDS_LP8788) += leds-lp8788.o
|
||||
obj-$(CONFIG_LEDS_TCA6507) += leds-tca6507.o
|
||||
obj-$(CONFIG_LEDS_CLEVO_MAIL) += leds-clevo-mail.o
|
||||
@ -34,7 +35,7 @@ obj-$(CONFIG_LEDS_HP6XX) += leds-hp6xx.o
|
||||
obj-$(CONFIG_LEDS_OT200) += leds-ot200.o
|
||||
obj-$(CONFIG_LEDS_FSG) += leds-fsg.o
|
||||
obj-$(CONFIG_LEDS_PCA955X) += leds-pca955x.o
|
||||
obj-$(CONFIG_LEDS_PCA9633) += leds-pca9633.o
|
||||
obj-$(CONFIG_LEDS_PCA963X) += leds-pca963x.o
|
||||
obj-$(CONFIG_LEDS_DA903X) += leds-da903x.o
|
||||
obj-$(CONFIG_LEDS_DA9052) += leds-da9052.o
|
||||
obj-$(CONFIG_LEDS_WM831X_STATUS) += leds-wm831x-status.o
|
||||
|
@ -157,7 +157,7 @@ static int pm860x_led_dt_init(struct platform_device *pdev,
|
||||
static int pm860x_led_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent);
|
||||
struct pm860x_led_pdata *pdata = pdev->dev.platform_data;
|
||||
struct pm860x_led_pdata *pdata = dev_get_platdata(&pdev->dev);
|
||||
struct pm860x_led *data;
|
||||
struct resource *res;
|
||||
int ret = 0;
|
||||
|
@ -87,7 +87,7 @@ static int adp5520_led_setup(struct adp5520_led *led)
|
||||
|
||||
static int adp5520_led_prepare(struct platform_device *pdev)
|
||||
{
|
||||
struct adp5520_leds_platform_data *pdata = pdev->dev.platform_data;
|
||||
struct adp5520_leds_platform_data *pdata = dev_get_platdata(&pdev->dev);
|
||||
struct device *dev = pdev->dev.parent;
|
||||
int ret = 0;
|
||||
|
||||
@ -103,7 +103,7 @@ static int adp5520_led_prepare(struct platform_device *pdev)
|
||||
|
||||
static int adp5520_led_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct adp5520_leds_platform_data *pdata = pdev->dev.platform_data;
|
||||
struct adp5520_leds_platform_data *pdata = dev_get_platdata(&pdev->dev);
|
||||
struct adp5520_led *led, *led_dat;
|
||||
struct led_info *cur_led;
|
||||
int ret, i;
|
||||
@ -185,7 +185,7 @@ err:
|
||||
|
||||
static int adp5520_led_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct adp5520_leds_platform_data *pdata = pdev->dev.platform_data;
|
||||
struct adp5520_leds_platform_data *pdata = dev_get_platdata(&pdev->dev);
|
||||
struct adp5520_led *led;
|
||||
int i;
|
||||
|
||||
|
@ -94,7 +94,7 @@ static int blink_set(struct led_classdev *cdev,
|
||||
|
||||
static int asic3_led_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct asic3_led *led = pdev->dev.platform_data;
|
||||
struct asic3_led *led = dev_get_platdata(&pdev->dev);
|
||||
int ret;
|
||||
|
||||
ret = mfd_cell_enable(pdev);
|
||||
@ -127,7 +127,7 @@ out:
|
||||
|
||||
static int asic3_led_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct asic3_led *led = pdev->dev.platform_data;
|
||||
struct asic3_led *led = dev_get_platdata(&pdev->dev);
|
||||
|
||||
led_classdev_unregister(led->cdev);
|
||||
|
||||
|
@ -42,7 +42,7 @@ static int pwmled_probe(struct platform_device *pdev)
|
||||
int i;
|
||||
int status;
|
||||
|
||||
pdata = pdev->dev.platform_data;
|
||||
pdata = dev_get_platdata(&pdev->dev);
|
||||
if (!pdata || pdata->num_leds < 1)
|
||||
return -ENODEV;
|
||||
|
||||
@ -119,7 +119,7 @@ static int pwmled_remove(struct platform_device *pdev)
|
||||
struct pwmled *leds;
|
||||
unsigned i;
|
||||
|
||||
pdata = pdev->dev.platform_data;
|
||||
pdata = dev_get_platdata(&pdev->dev);
|
||||
leds = platform_get_drvdata(pdev);
|
||||
|
||||
for (i = 0; i < pdata->num_leds; i++) {
|
||||
|
@ -684,7 +684,7 @@ static int bd2802_probe(struct i2c_client *client,
|
||||
}
|
||||
|
||||
led->client = client;
|
||||
pdata = led->pdata = client->dev.platform_data;
|
||||
pdata = led->pdata = dev_get_platdata(&client->dev);
|
||||
i2c_set_clientdata(client, led);
|
||||
|
||||
/* Configure RESET GPIO (L: RESET, H: RESET cancel) */
|
||||
|
@ -40,7 +40,7 @@ static int __init clevo_mail_led_dmi_callback(const struct dmi_system_id *id)
|
||||
* detected as working, but in reality it is not) as low as
|
||||
* possible.
|
||||
*/
|
||||
static struct dmi_system_id __initdata clevo_mail_led_dmi_table[] = {
|
||||
static struct dmi_system_id clevo_mail_led_dmi_table[] __initdata = {
|
||||
{
|
||||
.callback = clevo_mail_led_dmi_callback,
|
||||
.ident = "Clevo D410J",
|
||||
|
@ -93,7 +93,7 @@ static void da903x_led_set(struct led_classdev *led_cdev,
|
||||
|
||||
static int da903x_led_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct led_info *pdata = pdev->dev.platform_data;
|
||||
struct led_info *pdata = dev_get_platdata(&pdev->dev);
|
||||
struct da903x_led *led;
|
||||
int id, ret;
|
||||
|
||||
|
@ -112,7 +112,7 @@ static int da9052_led_probe(struct platform_device *pdev)
|
||||
int i;
|
||||
|
||||
da9052 = dev_get_drvdata(pdev->dev.parent);
|
||||
pdata = da9052->dev->platform_data;
|
||||
pdata = dev_get_platdata(da9052->dev);
|
||||
if (pdata == NULL) {
|
||||
dev_err(&pdev->dev, "No platform data\n");
|
||||
goto err;
|
||||
@ -185,7 +185,7 @@ static int da9052_led_remove(struct platform_device *pdev)
|
||||
int i;
|
||||
|
||||
da9052 = dev_get_drvdata(pdev->dev.parent);
|
||||
pdata = da9052->dev->platform_data;
|
||||
pdata = dev_get_platdata(da9052->dev);
|
||||
pled = pdata->pled;
|
||||
|
||||
for (i = 0; i < pled->num_leds; i++) {
|
||||
|
@ -233,7 +233,7 @@ static struct gpio_leds_priv *gpio_leds_create_of(struct platform_device *pdev)
|
||||
|
||||
static int gpio_led_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct gpio_led_platform_data *pdata = pdev->dev.platform_data;
|
||||
struct gpio_led_platform_data *pdata = dev_get_platdata(&pdev->dev);
|
||||
struct gpio_leds_priv *priv;
|
||||
int i, ret = 0;
|
||||
|
||||
|
@ -403,7 +403,7 @@ static DEVICE_ATTR(mode, 0644, lm3530_mode_get, lm3530_mode_set);
|
||||
static int lm3530_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct lm3530_platform_data *pdata = client->dev.platform_data;
|
||||
struct lm3530_platform_data *pdata = dev_get_platdata(&client->dev);
|
||||
struct lm3530_data *drvdata;
|
||||
int err = 0;
|
||||
|
||||
|
@ -671,7 +671,7 @@ static int lm3533_led_probe(struct platform_device *pdev)
|
||||
if (!lm3533)
|
||||
return -EINVAL;
|
||||
|
||||
pdata = pdev->dev.platform_data;
|
||||
pdata = dev_get_platdata(&pdev->dev);
|
||||
if (!pdata) {
|
||||
dev_err(&pdev->dev, "no platform data\n");
|
||||
return -EINVAL;
|
||||
|
@ -423,7 +423,7 @@ static const struct regmap_config lm355x_regmap = {
|
||||
static int lm355x_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct lm355x_platform_data *pdata = client->dev.platform_data;
|
||||
struct lm355x_platform_data *pdata = dev_get_platdata(&client->dev);
|
||||
struct lm355x_chip_data *chip;
|
||||
|
||||
int err;
|
||||
|
@ -316,7 +316,7 @@ static const struct regmap_config lm3642_regmap = {
|
||||
static int lm3642_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct lm3642_platform_data *pdata = client->dev.platform_data;
|
||||
struct lm3642_platform_data *pdata = dev_get_platdata(&client->dev);
|
||||
struct lm3642_chip_data *chip;
|
||||
|
||||
int err;
|
||||
|
@ -289,7 +289,7 @@ static void lp3944_led_set_brightness(struct led_classdev *led_cdev,
|
||||
dev_dbg(&led->client->dev, "%s: %s, %d\n",
|
||||
__func__, led_cdev->name, brightness);
|
||||
|
||||
led->status = brightness;
|
||||
led->status = !!brightness;
|
||||
schedule_work(&led->work);
|
||||
}
|
||||
|
||||
@ -377,7 +377,8 @@ exit:
|
||||
static int lp3944_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct lp3944_platform_data *lp3944_pdata = client->dev.platform_data;
|
||||
struct lp3944_platform_data *lp3944_pdata =
|
||||
dev_get_platdata(&client->dev);
|
||||
struct lp3944_data *data;
|
||||
int err;
|
||||
|
||||
@ -413,7 +414,7 @@ static int lp3944_probe(struct i2c_client *client,
|
||||
|
||||
static int lp3944_remove(struct i2c_client *client)
|
||||
{
|
||||
struct lp3944_platform_data *pdata = client->dev.platform_data;
|
||||
struct lp3944_platform_data *pdata = dev_get_platdata(&client->dev);
|
||||
struct lp3944_data *data = i2c_get_clientdata(client);
|
||||
int i;
|
||||
|
||||
|
@ -220,17 +220,11 @@ static int lp5521_update_program_memory(struct lp55xx_chip *chip,
|
||||
};
|
||||
unsigned cmd;
|
||||
char c[3];
|
||||
int program_size;
|
||||
int nrchars;
|
||||
int offset = 0;
|
||||
int ret;
|
||||
int i;
|
||||
int offset = 0;
|
||||
int i = 0;
|
||||
|
||||
/* clear program memory before updating */
|
||||
for (i = 0; i < LP5521_PROGRAM_LENGTH; i++)
|
||||
lp55xx_write(chip, addr[idx] + i, 0);
|
||||
|
||||
i = 0;
|
||||
while ((offset < size - 1) && (i < LP5521_PROGRAM_LENGTH)) {
|
||||
/* separate sscanfs because length is working only for %s */
|
||||
ret = sscanf(data + offset, "%2s%n ", c, &nrchars);
|
||||
@ -250,11 +244,19 @@ static int lp5521_update_program_memory(struct lp55xx_chip *chip,
|
||||
if (i % 2)
|
||||
goto err;
|
||||
|
||||
program_size = i;
|
||||
for (i = 0; i < program_size; i++)
|
||||
lp55xx_write(chip, addr[idx] + i, pattern[i]);
|
||||
mutex_lock(&chip->lock);
|
||||
|
||||
return 0;
|
||||
for (i = 0; i < LP5521_PROGRAM_LENGTH; i++) {
|
||||
ret = lp55xx_write(chip, addr[idx] + i, pattern[i]);
|
||||
if (ret) {
|
||||
mutex_unlock(&chip->lock);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&chip->lock);
|
||||
|
||||
return size;
|
||||
|
||||
err:
|
||||
dev_err(&chip->cl->dev, "wrong pattern format\n");
|
||||
@ -365,6 +367,80 @@ static void lp5521_led_brightness_work(struct work_struct *work)
|
||||
mutex_unlock(&chip->lock);
|
||||
}
|
||||
|
||||
static ssize_t show_engine_mode(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf, int nr)
|
||||
{
|
||||
struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev));
|
||||
struct lp55xx_chip *chip = led->chip;
|
||||
enum lp55xx_engine_mode mode = chip->engines[nr - 1].mode;
|
||||
|
||||
switch (mode) {
|
||||
case LP55XX_ENGINE_RUN:
|
||||
return sprintf(buf, "run\n");
|
||||
case LP55XX_ENGINE_LOAD:
|
||||
return sprintf(buf, "load\n");
|
||||
case LP55XX_ENGINE_DISABLED:
|
||||
default:
|
||||
return sprintf(buf, "disabled\n");
|
||||
}
|
||||
}
|
||||
show_mode(1)
|
||||
show_mode(2)
|
||||
show_mode(3)
|
||||
|
||||
static ssize_t store_engine_mode(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t len, int nr)
|
||||
{
|
||||
struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev));
|
||||
struct lp55xx_chip *chip = led->chip;
|
||||
struct lp55xx_engine *engine = &chip->engines[nr - 1];
|
||||
|
||||
mutex_lock(&chip->lock);
|
||||
|
||||
chip->engine_idx = nr;
|
||||
|
||||
if (!strncmp(buf, "run", 3)) {
|
||||
lp5521_run_engine(chip, true);
|
||||
engine->mode = LP55XX_ENGINE_RUN;
|
||||
} else if (!strncmp(buf, "load", 4)) {
|
||||
lp5521_stop_engine(chip);
|
||||
lp5521_load_engine(chip);
|
||||
engine->mode = LP55XX_ENGINE_LOAD;
|
||||
} else if (!strncmp(buf, "disabled", 8)) {
|
||||
lp5521_stop_engine(chip);
|
||||
engine->mode = LP55XX_ENGINE_DISABLED;
|
||||
}
|
||||
|
||||
mutex_unlock(&chip->lock);
|
||||
|
||||
return len;
|
||||
}
|
||||
store_mode(1)
|
||||
store_mode(2)
|
||||
store_mode(3)
|
||||
|
||||
static ssize_t store_engine_load(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t len, int nr)
|
||||
{
|
||||
struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev));
|
||||
struct lp55xx_chip *chip = led->chip;
|
||||
|
||||
mutex_lock(&chip->lock);
|
||||
|
||||
chip->engine_idx = nr;
|
||||
lp5521_load_engine(chip);
|
||||
|
||||
mutex_unlock(&chip->lock);
|
||||
|
||||
return lp5521_update_program_memory(chip, buf, len);
|
||||
}
|
||||
store_load(1)
|
||||
store_load(2)
|
||||
store_load(3)
|
||||
|
||||
static ssize_t lp5521_selftest(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
@ -381,9 +457,21 @@ static ssize_t lp5521_selftest(struct device *dev,
|
||||
}
|
||||
|
||||
/* device attributes */
|
||||
static DEVICE_ATTR(selftest, S_IRUGO, lp5521_selftest, NULL);
|
||||
static LP55XX_DEV_ATTR_RW(engine1_mode, show_engine1_mode, store_engine1_mode);
|
||||
static LP55XX_DEV_ATTR_RW(engine2_mode, show_engine2_mode, store_engine2_mode);
|
||||
static LP55XX_DEV_ATTR_RW(engine3_mode, show_engine3_mode, store_engine3_mode);
|
||||
static LP55XX_DEV_ATTR_WO(engine1_load, store_engine1_load);
|
||||
static LP55XX_DEV_ATTR_WO(engine2_load, store_engine2_load);
|
||||
static LP55XX_DEV_ATTR_WO(engine3_load, store_engine3_load);
|
||||
static LP55XX_DEV_ATTR_RO(selftest, lp5521_selftest);
|
||||
|
||||
static struct attribute *lp5521_attributes[] = {
|
||||
&dev_attr_engine1_mode.attr,
|
||||
&dev_attr_engine2_mode.attr,
|
||||
&dev_attr_engine3_mode.attr,
|
||||
&dev_attr_engine1_load.attr,
|
||||
&dev_attr_engine2_load.attr,
|
||||
&dev_attr_engine3_load.attr,
|
||||
&dev_attr_selftest.attr,
|
||||
NULL
|
||||
};
|
||||
@ -420,7 +508,7 @@ static int lp5521_probe(struct i2c_client *client,
|
||||
struct lp55xx_platform_data *pdata;
|
||||
struct device_node *np = client->dev.of_node;
|
||||
|
||||
if (!client->dev.platform_data) {
|
||||
if (!dev_get_platdata(&client->dev)) {
|
||||
if (np) {
|
||||
ret = lp55xx_of_populate_pdata(&client->dev, np);
|
||||
if (ret < 0)
|
||||
@ -430,7 +518,7 @@ static int lp5521_probe(struct i2c_client *client,
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
pdata = client->dev.platform_data;
|
||||
pdata = dev_get_platdata(&client->dev);
|
||||
|
||||
chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
|
||||
if (!chip)
|
||||
|
@ -49,6 +49,9 @@
|
||||
#define LP5523_REG_RESET 0x3D
|
||||
#define LP5523_REG_LED_TEST_CTRL 0x41
|
||||
#define LP5523_REG_LED_TEST_ADC 0x42
|
||||
#define LP5523_REG_CH1_PROG_START 0x4C
|
||||
#define LP5523_REG_CH2_PROG_START 0x4D
|
||||
#define LP5523_REG_CH3_PROG_START 0x4E
|
||||
#define LP5523_REG_PROG_PAGE_SEL 0x4F
|
||||
#define LP5523_REG_PROG_MEM 0x50
|
||||
|
||||
@ -65,11 +68,15 @@
|
||||
#define LP5523_RESET 0xFF
|
||||
#define LP5523_ADC_SHORTCIRC_LIM 80
|
||||
#define LP5523_EXT_CLK_USED 0x08
|
||||
#define LP5523_ENG_STATUS_MASK 0x07
|
||||
|
||||
/* Memory Page Selection */
|
||||
#define LP5523_PAGE_ENG1 0
|
||||
#define LP5523_PAGE_ENG2 1
|
||||
#define LP5523_PAGE_ENG3 2
|
||||
#define LP5523_PAGE_MUX1 3
|
||||
#define LP5523_PAGE_MUX2 4
|
||||
#define LP5523_PAGE_MUX3 5
|
||||
|
||||
/* Program Memory Operations */
|
||||
#define LP5523_MODE_ENG1_M 0x30 /* Operation Mode Register */
|
||||
@ -94,11 +101,15 @@
|
||||
#define LP5523_RUN_ENG2 0x08
|
||||
#define LP5523_RUN_ENG3 0x02
|
||||
|
||||
#define LED_ACTIVE(mux, led) (!!(mux & (0x0001 << led)))
|
||||
|
||||
enum lp5523_chip_id {
|
||||
LP5523,
|
||||
LP55231,
|
||||
};
|
||||
|
||||
static int lp5523_init_program_engine(struct lp55xx_chip *chip);
|
||||
|
||||
static inline void lp5523_wait_opmode_done(void)
|
||||
{
|
||||
usleep_range(1000, 2000);
|
||||
@ -134,7 +145,11 @@ static int lp5523_post_init_device(struct lp55xx_chip *chip)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return lp55xx_write(chip, LP5523_REG_ENABLE_LEDS_LSB, 0xff);
|
||||
ret = lp55xx_write(chip, LP5523_REG_ENABLE_LEDS_LSB, 0xff);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return lp5523_init_program_engine(chip);
|
||||
}
|
||||
|
||||
static void lp5523_load_engine(struct lp55xx_chip *chip)
|
||||
@ -152,15 +167,21 @@ static void lp5523_load_engine(struct lp55xx_chip *chip)
|
||||
[LP55XX_ENGINE_3] = LP5523_LOAD_ENG3,
|
||||
};
|
||||
|
||||
lp55xx_update_bits(chip, LP5523_REG_OP_MODE, mask[idx], val[idx]);
|
||||
|
||||
lp5523_wait_opmode_done();
|
||||
}
|
||||
|
||||
static void lp5523_load_engine_and_select_page(struct lp55xx_chip *chip)
|
||||
{
|
||||
enum lp55xx_engine_index idx = chip->engine_idx;
|
||||
u8 page_sel[] = {
|
||||
[LP55XX_ENGINE_1] = LP5523_PAGE_ENG1,
|
||||
[LP55XX_ENGINE_2] = LP5523_PAGE_ENG2,
|
||||
[LP55XX_ENGINE_3] = LP5523_PAGE_ENG3,
|
||||
};
|
||||
|
||||
lp55xx_update_bits(chip, LP5523_REG_OP_MODE, mask[idx], val[idx]);
|
||||
|
||||
lp5523_wait_opmode_done();
|
||||
lp5523_load_engine(chip);
|
||||
|
||||
lp55xx_write(chip, LP5523_REG_PROG_PAGE_SEL, page_sel[idx]);
|
||||
}
|
||||
@ -227,23 +248,75 @@ static void lp5523_run_engine(struct lp55xx_chip *chip, bool start)
|
||||
lp55xx_update_bits(chip, LP5523_REG_ENABLE, LP5523_EXEC_M, exec);
|
||||
}
|
||||
|
||||
static int lp5523_init_program_engine(struct lp55xx_chip *chip)
|
||||
{
|
||||
int i;
|
||||
int j;
|
||||
int ret;
|
||||
u8 status;
|
||||
/* one pattern per engine setting LED MUX start and stop addresses */
|
||||
static const u8 pattern[][LP5523_PROGRAM_LENGTH] = {
|
||||
{ 0x9c, 0x30, 0x9c, 0xb0, 0x9d, 0x80, 0xd8, 0x00, 0},
|
||||
{ 0x9c, 0x40, 0x9c, 0xc0, 0x9d, 0x80, 0xd8, 0x00, 0},
|
||||
{ 0x9c, 0x50, 0x9c, 0xd0, 0x9d, 0x80, 0xd8, 0x00, 0},
|
||||
};
|
||||
|
||||
/* hardcode 32 bytes of memory for each engine from program memory */
|
||||
ret = lp55xx_write(chip, LP5523_REG_CH1_PROG_START, 0x00);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = lp55xx_write(chip, LP5523_REG_CH2_PROG_START, 0x10);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = lp55xx_write(chip, LP5523_REG_CH3_PROG_START, 0x20);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* write LED MUX address space for each engine */
|
||||
for (i = LP55XX_ENGINE_1; i <= LP55XX_ENGINE_3; i++) {
|
||||
chip->engine_idx = i;
|
||||
lp5523_load_engine_and_select_page(chip);
|
||||
|
||||
for (j = 0; j < LP5523_PROGRAM_LENGTH; j++) {
|
||||
ret = lp55xx_write(chip, LP5523_REG_PROG_MEM + j,
|
||||
pattern[i - 1][j]);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
lp5523_run_engine(chip, true);
|
||||
|
||||
/* Let the programs run for couple of ms and check the engine status */
|
||||
usleep_range(3000, 6000);
|
||||
lp55xx_read(chip, LP5523_REG_STATUS, &status);
|
||||
status &= LP5523_ENG_STATUS_MASK;
|
||||
|
||||
if (status != LP5523_ENG_STATUS_MASK) {
|
||||
dev_err(&chip->cl->dev,
|
||||
"cound not configure LED engine, status = 0x%.2x\n",
|
||||
status);
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
out:
|
||||
lp5523_stop_engine(chip);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int lp5523_update_program_memory(struct lp55xx_chip *chip,
|
||||
const u8 *data, size_t size)
|
||||
{
|
||||
u8 pattern[LP5523_PROGRAM_LENGTH] = {0};
|
||||
unsigned cmd;
|
||||
char c[3];
|
||||
int update_size;
|
||||
int nrchars;
|
||||
int offset = 0;
|
||||
int ret;
|
||||
int i;
|
||||
int offset = 0;
|
||||
int i = 0;
|
||||
|
||||
/* clear program memory before updating */
|
||||
for (i = 0; i < LP5523_PROGRAM_LENGTH; i++)
|
||||
lp55xx_write(chip, LP5523_REG_PROG_MEM + i, 0);
|
||||
|
||||
i = 0;
|
||||
while ((offset < size - 1) && (i < LP5523_PROGRAM_LENGTH)) {
|
||||
/* separate sscanfs because length is working only for %s */
|
||||
ret = sscanf(data + offset, "%2s%n ", c, &nrchars);
|
||||
@ -263,11 +336,19 @@ static int lp5523_update_program_memory(struct lp55xx_chip *chip,
|
||||
if (i % 2)
|
||||
goto err;
|
||||
|
||||
update_size = i;
|
||||
for (i = 0; i < update_size; i++)
|
||||
lp55xx_write(chip, LP5523_REG_PROG_MEM + i, pattern[i]);
|
||||
mutex_lock(&chip->lock);
|
||||
|
||||
return 0;
|
||||
for (i = 0; i < LP5523_PROGRAM_LENGTH; i++) {
|
||||
ret = lp55xx_write(chip, LP5523_REG_PROG_MEM + i, pattern[i]);
|
||||
if (ret) {
|
||||
mutex_unlock(&chip->lock);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&chip->lock);
|
||||
|
||||
return size;
|
||||
|
||||
err:
|
||||
dev_err(&chip->cl->dev, "wrong pattern format\n");
|
||||
@ -290,10 +371,196 @@ static void lp5523_firmware_loaded(struct lp55xx_chip *chip)
|
||||
* 2) write firmware data into program memory
|
||||
*/
|
||||
|
||||
lp5523_load_engine(chip);
|
||||
lp5523_load_engine_and_select_page(chip);
|
||||
lp5523_update_program_memory(chip, fw->data, fw->size);
|
||||
}
|
||||
|
||||
static ssize_t show_engine_mode(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf, int nr)
|
||||
{
|
||||
struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev));
|
||||
struct lp55xx_chip *chip = led->chip;
|
||||
enum lp55xx_engine_mode mode = chip->engines[nr - 1].mode;
|
||||
|
||||
switch (mode) {
|
||||
case LP55XX_ENGINE_RUN:
|
||||
return sprintf(buf, "run\n");
|
||||
case LP55XX_ENGINE_LOAD:
|
||||
return sprintf(buf, "load\n");
|
||||
case LP55XX_ENGINE_DISABLED:
|
||||
default:
|
||||
return sprintf(buf, "disabled\n");
|
||||
}
|
||||
}
|
||||
show_mode(1)
|
||||
show_mode(2)
|
||||
show_mode(3)
|
||||
|
||||
static ssize_t store_engine_mode(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t len, int nr)
|
||||
{
|
||||
struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev));
|
||||
struct lp55xx_chip *chip = led->chip;
|
||||
struct lp55xx_engine *engine = &chip->engines[nr - 1];
|
||||
|
||||
mutex_lock(&chip->lock);
|
||||
|
||||
chip->engine_idx = nr;
|
||||
|
||||
if (!strncmp(buf, "run", 3)) {
|
||||
lp5523_run_engine(chip, true);
|
||||
engine->mode = LP55XX_ENGINE_RUN;
|
||||
} else if (!strncmp(buf, "load", 4)) {
|
||||
lp5523_stop_engine(chip);
|
||||
lp5523_load_engine(chip);
|
||||
engine->mode = LP55XX_ENGINE_LOAD;
|
||||
} else if (!strncmp(buf, "disabled", 8)) {
|
||||
lp5523_stop_engine(chip);
|
||||
engine->mode = LP55XX_ENGINE_DISABLED;
|
||||
}
|
||||
|
||||
mutex_unlock(&chip->lock);
|
||||
|
||||
return len;
|
||||
}
|
||||
store_mode(1)
|
||||
store_mode(2)
|
||||
store_mode(3)
|
||||
|
||||
static int lp5523_mux_parse(const char *buf, u16 *mux, size_t len)
|
||||
{
|
||||
u16 tmp_mux = 0;
|
||||
int i;
|
||||
|
||||
len = min_t(int, len, LP5523_MAX_LEDS);
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
switch (buf[i]) {
|
||||
case '1':
|
||||
tmp_mux |= (1 << i);
|
||||
break;
|
||||
case '0':
|
||||
break;
|
||||
case '\n':
|
||||
i = len;
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
*mux = tmp_mux;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void lp5523_mux_to_array(u16 led_mux, char *array)
|
||||
{
|
||||
int i, pos = 0;
|
||||
for (i = 0; i < LP5523_MAX_LEDS; i++)
|
||||
pos += sprintf(array + pos, "%x", LED_ACTIVE(led_mux, i));
|
||||
|
||||
array[pos] = '\0';
|
||||
}
|
||||
|
||||
static ssize_t show_engine_leds(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf, int nr)
|
||||
{
|
||||
struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev));
|
||||
struct lp55xx_chip *chip = led->chip;
|
||||
char mux[LP5523_MAX_LEDS + 1];
|
||||
|
||||
lp5523_mux_to_array(chip->engines[nr - 1].led_mux, mux);
|
||||
|
||||
return sprintf(buf, "%s\n", mux);
|
||||
}
|
||||
show_leds(1)
|
||||
show_leds(2)
|
||||
show_leds(3)
|
||||
|
||||
static int lp5523_load_mux(struct lp55xx_chip *chip, u16 mux, int nr)
|
||||
{
|
||||
struct lp55xx_engine *engine = &chip->engines[nr - 1];
|
||||
int ret;
|
||||
u8 mux_page[] = {
|
||||
[LP55XX_ENGINE_1] = LP5523_PAGE_MUX1,
|
||||
[LP55XX_ENGINE_2] = LP5523_PAGE_MUX2,
|
||||
[LP55XX_ENGINE_3] = LP5523_PAGE_MUX3,
|
||||
};
|
||||
|
||||
lp5523_load_engine(chip);
|
||||
|
||||
ret = lp55xx_write(chip, LP5523_REG_PROG_PAGE_SEL, mux_page[nr]);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = lp55xx_write(chip, LP5523_REG_PROG_MEM , (u8)(mux >> 8));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = lp55xx_write(chip, LP5523_REG_PROG_MEM + 1, (u8)(mux));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
engine->led_mux = mux;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t store_engine_leds(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t len, int nr)
|
||||
{
|
||||
struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev));
|
||||
struct lp55xx_chip *chip = led->chip;
|
||||
struct lp55xx_engine *engine = &chip->engines[nr - 1];
|
||||
u16 mux = 0;
|
||||
ssize_t ret;
|
||||
|
||||
if (lp5523_mux_parse(buf, &mux, len))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&chip->lock);
|
||||
|
||||
chip->engine_idx = nr;
|
||||
ret = -EINVAL;
|
||||
|
||||
if (engine->mode != LP55XX_ENGINE_LOAD)
|
||||
goto leave;
|
||||
|
||||
if (lp5523_load_mux(chip, mux, nr))
|
||||
goto leave;
|
||||
|
||||
ret = len;
|
||||
leave:
|
||||
mutex_unlock(&chip->lock);
|
||||
return ret;
|
||||
}
|
||||
store_leds(1)
|
||||
store_leds(2)
|
||||
store_leds(3)
|
||||
|
||||
static ssize_t store_engine_load(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t len, int nr)
|
||||
{
|
||||
struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev));
|
||||
struct lp55xx_chip *chip = led->chip;
|
||||
|
||||
mutex_lock(&chip->lock);
|
||||
|
||||
chip->engine_idx = nr;
|
||||
lp5523_load_engine_and_select_page(chip);
|
||||
|
||||
mutex_unlock(&chip->lock);
|
||||
|
||||
return lp5523_update_program_memory(chip, buf, len);
|
||||
}
|
||||
store_load(1)
|
||||
store_load(2)
|
||||
store_load(3)
|
||||
|
||||
static ssize_t lp5523_selftest(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
@ -393,9 +660,27 @@ static void lp5523_led_brightness_work(struct work_struct *work)
|
||||
mutex_unlock(&chip->lock);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(selftest, S_IRUGO, lp5523_selftest, NULL);
|
||||
static LP55XX_DEV_ATTR_RW(engine1_mode, show_engine1_mode, store_engine1_mode);
|
||||
static LP55XX_DEV_ATTR_RW(engine2_mode, show_engine2_mode, store_engine2_mode);
|
||||
static LP55XX_DEV_ATTR_RW(engine3_mode, show_engine3_mode, store_engine3_mode);
|
||||
static LP55XX_DEV_ATTR_RW(engine1_leds, show_engine1_leds, store_engine1_leds);
|
||||
static LP55XX_DEV_ATTR_RW(engine2_leds, show_engine2_leds, store_engine2_leds);
|
||||
static LP55XX_DEV_ATTR_RW(engine3_leds, show_engine3_leds, store_engine3_leds);
|
||||
static LP55XX_DEV_ATTR_WO(engine1_load, store_engine1_load);
|
||||
static LP55XX_DEV_ATTR_WO(engine2_load, store_engine2_load);
|
||||
static LP55XX_DEV_ATTR_WO(engine3_load, store_engine3_load);
|
||||
static LP55XX_DEV_ATTR_RO(selftest, lp5523_selftest);
|
||||
|
||||
static struct attribute *lp5523_attributes[] = {
|
||||
&dev_attr_engine1_mode.attr,
|
||||
&dev_attr_engine2_mode.attr,
|
||||
&dev_attr_engine3_mode.attr,
|
||||
&dev_attr_engine1_load.attr,
|
||||
&dev_attr_engine2_load.attr,
|
||||
&dev_attr_engine3_load.attr,
|
||||
&dev_attr_engine1_leds.attr,
|
||||
&dev_attr_engine2_leds.attr,
|
||||
&dev_attr_engine3_leds.attr,
|
||||
&dev_attr_selftest.attr,
|
||||
NULL,
|
||||
};
|
||||
@ -432,7 +717,7 @@ static int lp5523_probe(struct i2c_client *client,
|
||||
struct lp55xx_platform_data *pdata;
|
||||
struct device_node *np = client->dev.of_node;
|
||||
|
||||
if (!client->dev.platform_data) {
|
||||
if (!dev_get_platdata(&client->dev)) {
|
||||
if (np) {
|
||||
ret = lp55xx_of_populate_pdata(&client->dev, np);
|
||||
if (ret < 0)
|
||||
@ -442,7 +727,7 @@ static int lp5523_probe(struct i2c_client *client,
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
pdata = client->dev.platform_data;
|
||||
pdata = dev_get_platdata(&client->dev);
|
||||
|
||||
chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
|
||||
if (!chip)
|
||||
|
@ -477,8 +477,8 @@ static ssize_t lp5562_store_engine_mux(struct device *dev,
|
||||
return len;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(led_pattern, S_IWUSR, NULL, lp5562_store_pattern);
|
||||
static DEVICE_ATTR(engine_mux, S_IWUSR, NULL, lp5562_store_engine_mux);
|
||||
static LP55XX_DEV_ATTR_WO(led_pattern, lp5562_store_pattern);
|
||||
static LP55XX_DEV_ATTR_WO(engine_mux, lp5562_store_engine_mux);
|
||||
|
||||
static struct attribute *lp5562_attributes[] = {
|
||||
&dev_attr_led_pattern.attr,
|
||||
@ -518,7 +518,7 @@ static int lp5562_probe(struct i2c_client *client,
|
||||
struct lp55xx_platform_data *pdata;
|
||||
struct device_node *np = client->dev.of_node;
|
||||
|
||||
if (!client->dev.platform_data) {
|
||||
if (!dev_get_platdata(&client->dev)) {
|
||||
if (np) {
|
||||
ret = lp55xx_of_populate_pdata(&client->dev, np);
|
||||
if (ret < 0)
|
||||
@ -528,7 +528,7 @@ static int lp5562_probe(struct i2c_client *client,
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
pdata = client->dev.platform_data;
|
||||
pdata = dev_get_platdata(&client->dev);
|
||||
|
||||
chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
|
||||
if (!chip)
|
||||
|
@ -593,6 +593,9 @@ int lp55xx_of_populate_pdata(struct device *dev, struct device_node *np)
|
||||
of_property_read_string(np, "label", &pdata->label);
|
||||
of_property_read_u8(np, "clock-mode", &pdata->clock_mode);
|
||||
|
||||
/* LP8501 specific */
|
||||
of_property_read_u8(np, "pwr-sel", (u8 *)&pdata->pwr_sel);
|
||||
|
||||
dev->platform_data = pdata;
|
||||
|
||||
return 0;
|
||||
|
@ -20,8 +20,62 @@ enum lp55xx_engine_index {
|
||||
LP55XX_ENGINE_1,
|
||||
LP55XX_ENGINE_2,
|
||||
LP55XX_ENGINE_3,
|
||||
LP55XX_ENGINE_MAX = LP55XX_ENGINE_3,
|
||||
};
|
||||
|
||||
enum lp55xx_engine_mode {
|
||||
LP55XX_ENGINE_DISABLED,
|
||||
LP55XX_ENGINE_LOAD,
|
||||
LP55XX_ENGINE_RUN,
|
||||
};
|
||||
|
||||
#define LP55XX_DEV_ATTR_RW(name, show, store) \
|
||||
DEVICE_ATTR(name, S_IRUGO | S_IWUSR, show, store)
|
||||
#define LP55XX_DEV_ATTR_RO(name, show) \
|
||||
DEVICE_ATTR(name, S_IRUGO, show, NULL)
|
||||
#define LP55XX_DEV_ATTR_WO(name, store) \
|
||||
DEVICE_ATTR(name, S_IWUSR, NULL, store)
|
||||
|
||||
#define show_mode(nr) \
|
||||
static ssize_t show_engine##nr##_mode(struct device *dev, \
|
||||
struct device_attribute *attr, \
|
||||
char *buf) \
|
||||
{ \
|
||||
return show_engine_mode(dev, attr, buf, nr); \
|
||||
}
|
||||
|
||||
#define store_mode(nr) \
|
||||
static ssize_t store_engine##nr##_mode(struct device *dev, \
|
||||
struct device_attribute *attr, \
|
||||
const char *buf, size_t len) \
|
||||
{ \
|
||||
return store_engine_mode(dev, attr, buf, len, nr); \
|
||||
}
|
||||
|
||||
#define show_leds(nr) \
|
||||
static ssize_t show_engine##nr##_leds(struct device *dev, \
|
||||
struct device_attribute *attr, \
|
||||
char *buf) \
|
||||
{ \
|
||||
return show_engine_leds(dev, attr, buf, nr); \
|
||||
}
|
||||
|
||||
#define store_leds(nr) \
|
||||
static ssize_t store_engine##nr##_leds(struct device *dev, \
|
||||
struct device_attribute *attr, \
|
||||
const char *buf, size_t len) \
|
||||
{ \
|
||||
return store_engine_leds(dev, attr, buf, len, nr); \
|
||||
}
|
||||
|
||||
#define store_load(nr) \
|
||||
static ssize_t store_engine##nr##_load(struct device *dev, \
|
||||
struct device_attribute *attr, \
|
||||
const char *buf, size_t len) \
|
||||
{ \
|
||||
return store_engine_load(dev, attr, buf, len, nr); \
|
||||
}
|
||||
|
||||
struct lp55xx_led;
|
||||
struct lp55xx_chip;
|
||||
|
||||
@ -71,6 +125,16 @@ struct lp55xx_device_config {
|
||||
const struct attribute_group *dev_attr_group;
|
||||
};
|
||||
|
||||
/*
|
||||
* struct lp55xx_engine
|
||||
* @mode : Engine mode
|
||||
* @led_mux : Mux bits for LED selection. Only used in LP5523
|
||||
*/
|
||||
struct lp55xx_engine {
|
||||
enum lp55xx_engine_mode mode;
|
||||
u16 led_mux;
|
||||
};
|
||||
|
||||
/*
|
||||
* struct lp55xx_chip
|
||||
* @cl : I2C communication for access registers
|
||||
@ -79,6 +143,7 @@ struct lp55xx_device_config {
|
||||
* @num_leds : Number of registered LEDs
|
||||
* @cfg : Device specific configuration data
|
||||
* @engine_idx : Selected engine number
|
||||
* @engines : Engine structure for the device attribute R/W interface
|
||||
* @fw : Firmware data for running a LED pattern
|
||||
*/
|
||||
struct lp55xx_chip {
|
||||
@ -89,6 +154,7 @@ struct lp55xx_chip {
|
||||
int num_leds;
|
||||
struct lp55xx_device_config *cfg;
|
||||
enum lp55xx_engine_index engine_idx;
|
||||
struct lp55xx_engine engines[LP55XX_ENGINE_MAX];
|
||||
const struct firmware *fw;
|
||||
};
|
||||
|
||||
|
410
drivers/leds/leds-lp8501.c
Normal file
410
drivers/leds/leds-lp8501.c
Normal file
@ -0,0 +1,410 @@
|
||||
/*
|
||||
* TI LP8501 9 channel LED Driver
|
||||
*
|
||||
* Copyright (C) 2013 Texas Instruments
|
||||
*
|
||||
* Author: Milo(Woogyom) Kim <milo.kim@ti.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/platform_data/leds-lp55xx.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "leds-lp55xx-common.h"
|
||||
|
||||
#define LP8501_PROGRAM_LENGTH 32
|
||||
#define LP8501_MAX_LEDS 9
|
||||
|
||||
/* Registers */
|
||||
#define LP8501_REG_ENABLE 0x00
|
||||
#define LP8501_ENABLE BIT(6)
|
||||
#define LP8501_EXEC_M 0x3F
|
||||
#define LP8501_EXEC_ENG1_M 0x30
|
||||
#define LP8501_EXEC_ENG2_M 0x0C
|
||||
#define LP8501_EXEC_ENG3_M 0x03
|
||||
#define LP8501_RUN_ENG1 0x20
|
||||
#define LP8501_RUN_ENG2 0x08
|
||||
#define LP8501_RUN_ENG3 0x02
|
||||
|
||||
#define LP8501_REG_OP_MODE 0x01
|
||||
#define LP8501_MODE_ENG1_M 0x30
|
||||
#define LP8501_MODE_ENG2_M 0x0C
|
||||
#define LP8501_MODE_ENG3_M 0x03
|
||||
#define LP8501_LOAD_ENG1 0x10
|
||||
#define LP8501_LOAD_ENG2 0x04
|
||||
#define LP8501_LOAD_ENG3 0x01
|
||||
|
||||
#define LP8501_REG_PWR_CONFIG 0x05
|
||||
#define LP8501_PWR_CONFIG_M 0x03
|
||||
|
||||
#define LP8501_REG_LED_PWM_BASE 0x16
|
||||
|
||||
#define LP8501_REG_LED_CURRENT_BASE 0x26
|
||||
|
||||
#define LP8501_REG_CONFIG 0x36
|
||||
#define LP8501_PWM_PSAVE BIT(7)
|
||||
#define LP8501_AUTO_INC BIT(6)
|
||||
#define LP8501_PWR_SAVE BIT(5)
|
||||
#define LP8501_CP_AUTO 0x18
|
||||
#define LP8501_INT_CLK BIT(0)
|
||||
#define LP8501_DEFAULT_CFG \
|
||||
(LP8501_PWM_PSAVE | LP8501_AUTO_INC | LP8501_PWR_SAVE | LP8501_CP_AUTO)
|
||||
|
||||
#define LP8501_REG_RESET 0x3D
|
||||
#define LP8501_RESET 0xFF
|
||||
|
||||
#define LP8501_REG_PROG_PAGE_SEL 0x4F
|
||||
#define LP8501_PAGE_ENG1 0
|
||||
#define LP8501_PAGE_ENG2 1
|
||||
#define LP8501_PAGE_ENG3 2
|
||||
|
||||
#define LP8501_REG_PROG_MEM 0x50
|
||||
|
||||
#define LP8501_ENG1_IS_LOADING(mode) \
|
||||
((mode & LP8501_MODE_ENG1_M) == LP8501_LOAD_ENG1)
|
||||
#define LP8501_ENG2_IS_LOADING(mode) \
|
||||
((mode & LP8501_MODE_ENG2_M) == LP8501_LOAD_ENG2)
|
||||
#define LP8501_ENG3_IS_LOADING(mode) \
|
||||
((mode & LP8501_MODE_ENG3_M) == LP8501_LOAD_ENG3)
|
||||
|
||||
static inline void lp8501_wait_opmode_done(void)
|
||||
{
|
||||
usleep_range(1000, 2000);
|
||||
}
|
||||
|
||||
static void lp8501_set_led_current(struct lp55xx_led *led, u8 led_current)
|
||||
{
|
||||
led->led_current = led_current;
|
||||
lp55xx_write(led->chip, LP8501_REG_LED_CURRENT_BASE + led->chan_nr,
|
||||
led_current);
|
||||
}
|
||||
|
||||
static int lp8501_post_init_device(struct lp55xx_chip *chip)
|
||||
{
|
||||
int ret;
|
||||
u8 val = LP8501_DEFAULT_CFG;
|
||||
|
||||
ret = lp55xx_write(chip, LP8501_REG_ENABLE, LP8501_ENABLE);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Chip startup time is 500 us, 1 - 2 ms gives some margin */
|
||||
usleep_range(1000, 2000);
|
||||
|
||||
if (chip->pdata->clock_mode != LP55XX_CLOCK_EXT)
|
||||
val |= LP8501_INT_CLK;
|
||||
|
||||
ret = lp55xx_write(chip, LP8501_REG_CONFIG, val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Power selection for each output */
|
||||
return lp55xx_update_bits(chip, LP8501_REG_PWR_CONFIG,
|
||||
LP8501_PWR_CONFIG_M, chip->pdata->pwr_sel);
|
||||
}
|
||||
|
||||
static void lp8501_load_engine(struct lp55xx_chip *chip)
|
||||
{
|
||||
enum lp55xx_engine_index idx = chip->engine_idx;
|
||||
u8 mask[] = {
|
||||
[LP55XX_ENGINE_1] = LP8501_MODE_ENG1_M,
|
||||
[LP55XX_ENGINE_2] = LP8501_MODE_ENG2_M,
|
||||
[LP55XX_ENGINE_3] = LP8501_MODE_ENG3_M,
|
||||
};
|
||||
|
||||
u8 val[] = {
|
||||
[LP55XX_ENGINE_1] = LP8501_LOAD_ENG1,
|
||||
[LP55XX_ENGINE_2] = LP8501_LOAD_ENG2,
|
||||
[LP55XX_ENGINE_3] = LP8501_LOAD_ENG3,
|
||||
};
|
||||
|
||||
u8 page_sel[] = {
|
||||
[LP55XX_ENGINE_1] = LP8501_PAGE_ENG1,
|
||||
[LP55XX_ENGINE_2] = LP8501_PAGE_ENG2,
|
||||
[LP55XX_ENGINE_3] = LP8501_PAGE_ENG3,
|
||||
};
|
||||
|
||||
lp55xx_update_bits(chip, LP8501_REG_OP_MODE, mask[idx], val[idx]);
|
||||
|
||||
lp8501_wait_opmode_done();
|
||||
|
||||
lp55xx_write(chip, LP8501_REG_PROG_PAGE_SEL, page_sel[idx]);
|
||||
}
|
||||
|
||||
static void lp8501_stop_engine(struct lp55xx_chip *chip)
|
||||
{
|
||||
lp55xx_write(chip, LP8501_REG_OP_MODE, 0);
|
||||
lp8501_wait_opmode_done();
|
||||
}
|
||||
|
||||
static void lp8501_turn_off_channels(struct lp55xx_chip *chip)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < LP8501_MAX_LEDS; i++)
|
||||
lp55xx_write(chip, LP8501_REG_LED_PWM_BASE + i, 0);
|
||||
}
|
||||
|
||||
static void lp8501_run_engine(struct lp55xx_chip *chip, bool start)
|
||||
{
|
||||
int ret;
|
||||
u8 mode;
|
||||
u8 exec;
|
||||
|
||||
/* stop engine */
|
||||
if (!start) {
|
||||
lp8501_stop_engine(chip);
|
||||
lp8501_turn_off_channels(chip);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* To run the engine,
|
||||
* operation mode and enable register should updated at the same time
|
||||
*/
|
||||
|
||||
ret = lp55xx_read(chip, LP8501_REG_OP_MODE, &mode);
|
||||
if (ret)
|
||||
return;
|
||||
|
||||
ret = lp55xx_read(chip, LP8501_REG_ENABLE, &exec);
|
||||
if (ret)
|
||||
return;
|
||||
|
||||
/* change operation mode to RUN only when each engine is loading */
|
||||
if (LP8501_ENG1_IS_LOADING(mode)) {
|
||||
mode = (mode & ~LP8501_MODE_ENG1_M) | LP8501_RUN_ENG1;
|
||||
exec = (exec & ~LP8501_EXEC_ENG1_M) | LP8501_RUN_ENG1;
|
||||
}
|
||||
|
||||
if (LP8501_ENG2_IS_LOADING(mode)) {
|
||||
mode = (mode & ~LP8501_MODE_ENG2_M) | LP8501_RUN_ENG2;
|
||||
exec = (exec & ~LP8501_EXEC_ENG2_M) | LP8501_RUN_ENG2;
|
||||
}
|
||||
|
||||
if (LP8501_ENG3_IS_LOADING(mode)) {
|
||||
mode = (mode & ~LP8501_MODE_ENG3_M) | LP8501_RUN_ENG3;
|
||||
exec = (exec & ~LP8501_EXEC_ENG3_M) | LP8501_RUN_ENG3;
|
||||
}
|
||||
|
||||
lp55xx_write(chip, LP8501_REG_OP_MODE, mode);
|
||||
lp8501_wait_opmode_done();
|
||||
|
||||
lp55xx_update_bits(chip, LP8501_REG_ENABLE, LP8501_EXEC_M, exec);
|
||||
}
|
||||
|
||||
static int lp8501_update_program_memory(struct lp55xx_chip *chip,
|
||||
const u8 *data, size_t size)
|
||||
{
|
||||
u8 pattern[LP8501_PROGRAM_LENGTH] = {0};
|
||||
unsigned cmd;
|
||||
char c[3];
|
||||
int update_size;
|
||||
int nrchars;
|
||||
int offset = 0;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
/* clear program memory before updating */
|
||||
for (i = 0; i < LP8501_PROGRAM_LENGTH; i++)
|
||||
lp55xx_write(chip, LP8501_REG_PROG_MEM + i, 0);
|
||||
|
||||
i = 0;
|
||||
while ((offset < size - 1) && (i < LP8501_PROGRAM_LENGTH)) {
|
||||
/* separate sscanfs because length is working only for %s */
|
||||
ret = sscanf(data + offset, "%2s%n ", c, &nrchars);
|
||||
if (ret != 1)
|
||||
goto err;
|
||||
|
||||
ret = sscanf(c, "%2x", &cmd);
|
||||
if (ret != 1)
|
||||
goto err;
|
||||
|
||||
pattern[i] = (u8)cmd;
|
||||
offset += nrchars;
|
||||
i++;
|
||||
}
|
||||
|
||||
/* Each instruction is 16bit long. Check that length is even */
|
||||
if (i % 2)
|
||||
goto err;
|
||||
|
||||
update_size = i;
|
||||
for (i = 0; i < update_size; i++)
|
||||
lp55xx_write(chip, LP8501_REG_PROG_MEM + i, pattern[i]);
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
dev_err(&chip->cl->dev, "wrong pattern format\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static void lp8501_firmware_loaded(struct lp55xx_chip *chip)
|
||||
{
|
||||
const struct firmware *fw = chip->fw;
|
||||
|
||||
if (fw->size > LP8501_PROGRAM_LENGTH) {
|
||||
dev_err(&chip->cl->dev, "firmware data size overflow: %zu\n",
|
||||
fw->size);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Program momery sequence
|
||||
* 1) set engine mode to "LOAD"
|
||||
* 2) write firmware data into program memory
|
||||
*/
|
||||
|
||||
lp8501_load_engine(chip);
|
||||
lp8501_update_program_memory(chip, fw->data, fw->size);
|
||||
}
|
||||
|
||||
static void lp8501_led_brightness_work(struct work_struct *work)
|
||||
{
|
||||
struct lp55xx_led *led = container_of(work, struct lp55xx_led,
|
||||
brightness_work);
|
||||
struct lp55xx_chip *chip = led->chip;
|
||||
|
||||
mutex_lock(&chip->lock);
|
||||
lp55xx_write(chip, LP8501_REG_LED_PWM_BASE + led->chan_nr,
|
||||
led->brightness);
|
||||
mutex_unlock(&chip->lock);
|
||||
}
|
||||
|
||||
/* Chip specific configurations */
|
||||
static struct lp55xx_device_config lp8501_cfg = {
|
||||
.reset = {
|
||||
.addr = LP8501_REG_RESET,
|
||||
.val = LP8501_RESET,
|
||||
},
|
||||
.enable = {
|
||||
.addr = LP8501_REG_ENABLE,
|
||||
.val = LP8501_ENABLE,
|
||||
},
|
||||
.max_channel = LP8501_MAX_LEDS,
|
||||
.post_init_device = lp8501_post_init_device,
|
||||
.brightness_work_fn = lp8501_led_brightness_work,
|
||||
.set_led_current = lp8501_set_led_current,
|
||||
.firmware_cb = lp8501_firmware_loaded,
|
||||
.run_engine = lp8501_run_engine,
|
||||
};
|
||||
|
||||
static int lp8501_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
int ret;
|
||||
struct lp55xx_chip *chip;
|
||||
struct lp55xx_led *led;
|
||||
struct lp55xx_platform_data *pdata;
|
||||
struct device_node *np = client->dev.of_node;
|
||||
|
||||
if (!dev_get_platdata(&client->dev)) {
|
||||
if (np) {
|
||||
ret = lp55xx_of_populate_pdata(&client->dev, np);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
} else {
|
||||
dev_err(&client->dev, "no platform data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
pdata = dev_get_platdata(&client->dev);
|
||||
|
||||
chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
|
||||
if (!chip)
|
||||
return -ENOMEM;
|
||||
|
||||
led = devm_kzalloc(&client->dev,
|
||||
sizeof(*led) * pdata->num_channels, GFP_KERNEL);
|
||||
if (!led)
|
||||
return -ENOMEM;
|
||||
|
||||
chip->cl = client;
|
||||
chip->pdata = pdata;
|
||||
chip->cfg = &lp8501_cfg;
|
||||
|
||||
mutex_init(&chip->lock);
|
||||
|
||||
i2c_set_clientdata(client, led);
|
||||
|
||||
ret = lp55xx_init_device(chip);
|
||||
if (ret)
|
||||
goto err_init;
|
||||
|
||||
dev_info(&client->dev, "%s Programmable led chip found\n", id->name);
|
||||
|
||||
ret = lp55xx_register_leds(led, chip);
|
||||
if (ret)
|
||||
goto err_register_leds;
|
||||
|
||||
ret = lp55xx_register_sysfs(chip);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "registering sysfs failed\n");
|
||||
goto err_register_sysfs;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_register_sysfs:
|
||||
lp55xx_unregister_leds(led, chip);
|
||||
err_register_leds:
|
||||
lp55xx_deinit_device(chip);
|
||||
err_init:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int lp8501_remove(struct i2c_client *client)
|
||||
{
|
||||
struct lp55xx_led *led = i2c_get_clientdata(client);
|
||||
struct lp55xx_chip *chip = led->chip;
|
||||
|
||||
lp8501_stop_engine(chip);
|
||||
lp55xx_unregister_sysfs(chip);
|
||||
lp55xx_unregister_leds(led, chip);
|
||||
lp55xx_deinit_device(chip);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id lp8501_id[] = {
|
||||
{ "lp8501", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, lp8501_id);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id of_lp8501_leds_match[] = {
|
||||
{ .compatible = "ti,lp8501", },
|
||||
{},
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, of_lp8501_leds_match);
|
||||
#endif
|
||||
|
||||
static struct i2c_driver lp8501_driver = {
|
||||
.driver = {
|
||||
.name = "lp8501",
|
||||
.of_match_table = of_match_ptr(of_lp8501_leds_match),
|
||||
},
|
||||
.probe = lp8501_probe,
|
||||
.remove = lp8501_remove,
|
||||
.id_table = lp8501_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(lp8501_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Texas Instruments LP8501 LED drvier");
|
||||
MODULE_AUTHOR("Milo Kim");
|
||||
MODULE_LICENSE("GPL");
|
@ -135,7 +135,7 @@ static void delete_lt3593_led(struct lt3593_led_data *led)
|
||||
|
||||
static int lt3593_led_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct gpio_led_platform_data *pdata = pdev->dev.platform_data;
|
||||
struct gpio_led_platform_data *pdata = dev_get_platdata(&pdev->dev);
|
||||
struct lt3593_led_data *leds_data;
|
||||
int i, ret = 0;
|
||||
|
||||
@ -169,7 +169,7 @@ err:
|
||||
static int lt3593_led_remove(struct platform_device *pdev)
|
||||
{
|
||||
int i;
|
||||
struct gpio_led_platform_data *pdata = pdev->dev.platform_data;
|
||||
struct gpio_led_platform_data *pdata = dev_get_platdata(&pdev->dev);
|
||||
struct lt3593_led_data *leds_data;
|
||||
|
||||
leds_data = platform_get_drvdata(pdev);
|
||||
|
@ -306,7 +306,7 @@ create_netxbig_led(struct platform_device *pdev,
|
||||
struct netxbig_led_data *led_dat,
|
||||
const struct netxbig_led *template)
|
||||
{
|
||||
struct netxbig_led_platform_data *pdata = pdev->dev.platform_data;
|
||||
struct netxbig_led_platform_data *pdata = dev_get_platdata(&pdev->dev);
|
||||
int ret;
|
||||
|
||||
spin_lock_init(&led_dat->lock);
|
||||
@ -354,7 +354,7 @@ create_netxbig_led(struct platform_device *pdev,
|
||||
|
||||
static int netxbig_led_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct netxbig_led_platform_data *pdata = pdev->dev.platform_data;
|
||||
struct netxbig_led_platform_data *pdata = dev_get_platdata(&pdev->dev);
|
||||
struct netxbig_led_data *leds_data;
|
||||
int i;
|
||||
int ret;
|
||||
@ -391,7 +391,7 @@ err_free_leds:
|
||||
|
||||
static int netxbig_led_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct netxbig_led_platform_data *pdata = pdev->dev.platform_data;
|
||||
struct netxbig_led_platform_data *pdata = dev_get_platdata(&pdev->dev);
|
||||
struct netxbig_led_data *leds_data;
|
||||
int i;
|
||||
|
||||
|
@ -321,7 +321,7 @@ static inline int sizeof_ns2_led_priv(int num_leds)
|
||||
|
||||
static int ns2_led_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct ns2_led_platform_data *pdata = pdev->dev.platform_data;
|
||||
struct ns2_led_platform_data *pdata = dev_get_platdata(&pdev->dev);
|
||||
struct ns2_led_priv *priv;
|
||||
int i;
|
||||
int ret;
|
||||
|
@ -446,7 +446,8 @@ static int pca9532_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct pca9532_data *data = i2c_get_clientdata(client);
|
||||
struct pca9532_platform_data *pca9532_pdata = client->dev.platform_data;
|
||||
struct pca9532_platform_data *pca9532_pdata =
|
||||
dev_get_platdata(&client->dev);
|
||||
|
||||
if (!pca9532_pdata)
|
||||
return -EIO;
|
||||
|
@ -267,7 +267,7 @@ static int pca955x_probe(struct i2c_client *client,
|
||||
|
||||
chip = &pca955x_chipdefs[id->driver_data];
|
||||
adapter = to_i2c_adapter(client->dev.parent);
|
||||
pdata = client->dev.platform_data;
|
||||
pdata = dev_get_platdata(&client->dev);
|
||||
|
||||
/* Make sure the slave address / chip type combo given is possible */
|
||||
if ((client->addr & ~((1 << chip->slv_addr_shift) - 1)) !=
|
||||
|
@ -1,194 +0,0 @@
|
||||
/*
|
||||
* Copyright 2011 bct electronic GmbH
|
||||
*
|
||||
* Author: Peter Meerwald <p.meerwald@bct-electronic.com>
|
||||
*
|
||||
* Based on leds-pca955x.c
|
||||
*
|
||||
* 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 PCA9633 I2C LED driver (7-bit slave address 0x62)
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/platform_data/leds-pca9633.h>
|
||||
|
||||
/* LED select registers determine the source that drives LED outputs */
|
||||
#define PCA9633_LED_OFF 0x0 /* LED driver off */
|
||||
#define PCA9633_LED_ON 0x1 /* LED driver on */
|
||||
#define PCA9633_LED_PWM 0x2 /* Controlled through PWM */
|
||||
#define PCA9633_LED_GRP_PWM 0x3 /* Controlled through PWM/GRPPWM */
|
||||
|
||||
#define PCA9633_MODE1 0x00
|
||||
#define PCA9633_MODE2 0x01
|
||||
#define PCA9633_PWM_BASE 0x02
|
||||
#define PCA9633_LEDOUT 0x08
|
||||
|
||||
static const struct i2c_device_id pca9633_id[] = {
|
||||
{ "pca9633", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, pca9633_id);
|
||||
|
||||
struct pca9633_led {
|
||||
struct i2c_client *client;
|
||||
struct work_struct work;
|
||||
enum led_brightness brightness;
|
||||
struct led_classdev led_cdev;
|
||||
int led_num; /* 0 .. 3 potentially */
|
||||
char name[32];
|
||||
};
|
||||
|
||||
static void pca9633_led_work(struct work_struct *work)
|
||||
{
|
||||
struct pca9633_led *pca9633 = container_of(work,
|
||||
struct pca9633_led, work);
|
||||
u8 ledout = i2c_smbus_read_byte_data(pca9633->client, PCA9633_LEDOUT);
|
||||
int shift = 2 * pca9633->led_num;
|
||||
u8 mask = 0x3 << shift;
|
||||
|
||||
switch (pca9633->brightness) {
|
||||
case LED_FULL:
|
||||
i2c_smbus_write_byte_data(pca9633->client, PCA9633_LEDOUT,
|
||||
(ledout & ~mask) | (PCA9633_LED_ON << shift));
|
||||
break;
|
||||
case LED_OFF:
|
||||
i2c_smbus_write_byte_data(pca9633->client, PCA9633_LEDOUT,
|
||||
ledout & ~mask);
|
||||
break;
|
||||
default:
|
||||
i2c_smbus_write_byte_data(pca9633->client,
|
||||
PCA9633_PWM_BASE + pca9633->led_num,
|
||||
pca9633->brightness);
|
||||
i2c_smbus_write_byte_data(pca9633->client, PCA9633_LEDOUT,
|
||||
(ledout & ~mask) | (PCA9633_LED_PWM << shift));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void pca9633_led_set(struct led_classdev *led_cdev,
|
||||
enum led_brightness value)
|
||||
{
|
||||
struct pca9633_led *pca9633;
|
||||
|
||||
pca9633 = container_of(led_cdev, struct pca9633_led, led_cdev);
|
||||
|
||||
pca9633->brightness = value;
|
||||
|
||||
/*
|
||||
* Must use workqueue for the actual I/O since I2C operations
|
||||
* can sleep.
|
||||
*/
|
||||
schedule_work(&pca9633->work);
|
||||
}
|
||||
|
||||
static int pca9633_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct pca9633_led *pca9633;
|
||||
struct pca9633_platform_data *pdata;
|
||||
int i, err;
|
||||
|
||||
pdata = client->dev.platform_data;
|
||||
|
||||
if (pdata) {
|
||||
if (pdata->leds.num_leds <= 0 || pdata->leds.num_leds > 4) {
|
||||
dev_err(&client->dev, "board info must claim at most 4 LEDs");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
pca9633 = devm_kzalloc(&client->dev, 4 * sizeof(*pca9633), GFP_KERNEL);
|
||||
if (!pca9633)
|
||||
return -ENOMEM;
|
||||
|
||||
i2c_set_clientdata(client, pca9633);
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
pca9633[i].client = client;
|
||||
pca9633[i].led_num = i;
|
||||
|
||||
/* Platform data can specify LED names and default triggers */
|
||||
if (pdata && i < pdata->leds.num_leds) {
|
||||
if (pdata->leds.leds[i].name)
|
||||
snprintf(pca9633[i].name,
|
||||
sizeof(pca9633[i].name), "pca9633:%s",
|
||||
pdata->leds.leds[i].name);
|
||||
if (pdata->leds.leds[i].default_trigger)
|
||||
pca9633[i].led_cdev.default_trigger =
|
||||
pdata->leds.leds[i].default_trigger;
|
||||
} else {
|
||||
snprintf(pca9633[i].name, sizeof(pca9633[i].name),
|
||||
"pca9633:%d", i);
|
||||
}
|
||||
|
||||
pca9633[i].led_cdev.name = pca9633[i].name;
|
||||
pca9633[i].led_cdev.brightness_set = pca9633_led_set;
|
||||
|
||||
INIT_WORK(&pca9633[i].work, pca9633_led_work);
|
||||
|
||||
err = led_classdev_register(&client->dev, &pca9633[i].led_cdev);
|
||||
if (err < 0)
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* Disable LED all-call address and set normal mode */
|
||||
i2c_smbus_write_byte_data(client, PCA9633_MODE1, 0x00);
|
||||
|
||||
/* Configure output: open-drain or totem pole (push-pull) */
|
||||
if (pdata && pdata->outdrv == PCA9633_OPEN_DRAIN)
|
||||
i2c_smbus_write_byte_data(client, PCA9633_MODE2, 0x01);
|
||||
|
||||
/* Turn off LEDs */
|
||||
i2c_smbus_write_byte_data(client, PCA9633_LEDOUT, 0x00);
|
||||
|
||||
return 0;
|
||||
|
||||
exit:
|
||||
while (i--) {
|
||||
led_classdev_unregister(&pca9633[i].led_cdev);
|
||||
cancel_work_sync(&pca9633[i].work);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int pca9633_remove(struct i2c_client *client)
|
||||
{
|
||||
struct pca9633_led *pca9633 = i2c_get_clientdata(client);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
led_classdev_unregister(&pca9633[i].led_cdev);
|
||||
cancel_work_sync(&pca9633[i].work);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct i2c_driver pca9633_driver = {
|
||||
.driver = {
|
||||
.name = "leds-pca9633",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = pca9633_probe,
|
||||
.remove = pca9633_remove,
|
||||
.id_table = pca9633_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(pca9633_driver);
|
||||
|
||||
MODULE_AUTHOR("Peter Meerwald <p.meerwald@bct-electronic.com>");
|
||||
MODULE_DESCRIPTION("PCA9633 LED driver");
|
||||
MODULE_LICENSE("GPL v2");
|
461
drivers/leds/leds-pca963x.c
Normal file
461
drivers/leds/leds-pca963x.c
Normal file
@ -0,0 +1,461 @@
|
||||
/*
|
||||
* Copyright 2011 bct electronic GmbH
|
||||
* Copyright 2013 Qtechnology/AS
|
||||
*
|
||||
* Author: Peter Meerwald <p.meerwald@bct-electronic.com>
|
||||
* Author: Ricardo Ribalda <ricardo.ribalda@gmail.com>
|
||||
*
|
||||
* Based on leds-pca955x.c
|
||||
*
|
||||
* 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 PCA9633 I2C LED driver (7-bit slave address 0x62)
|
||||
* LED driver for the PCA9634 I2C LED driver (7-bit slave address set by hw.)
|
||||
*
|
||||
* Note that hardware blinking violates the leds infrastructure driver
|
||||
* interface since the hardware only supports blinking all LEDs with the
|
||||
* same delay_on/delay_off rates. That is, only the LEDs that are set to
|
||||
* blink will actually blink but all LEDs that are set to blink will blink
|
||||
* in identical fashion. The delay_on/delay_off values of the last LED
|
||||
* that is set to blink will be used for all of the blinking LEDs.
|
||||
* Hardware blinking is disabled by default but can be enabled by setting
|
||||
* the 'blink_type' member in the platform_data struct to 'PCA963X_HW_BLINK'
|
||||
* or by adding the 'nxp,hw-blink' property to the DTS.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_data/leds-pca963x.h>
|
||||
|
||||
/* LED select registers determine the source that drives LED outputs */
|
||||
#define PCA963X_LED_OFF 0x0 /* LED driver off */
|
||||
#define PCA963X_LED_ON 0x1 /* LED driver on */
|
||||
#define PCA963X_LED_PWM 0x2 /* Controlled through PWM */
|
||||
#define PCA963X_LED_GRP_PWM 0x3 /* Controlled through PWM/GRPPWM */
|
||||
|
||||
#define PCA963X_MODE2_DMBLNK 0x20 /* Enable blinking */
|
||||
|
||||
#define PCA963X_MODE1 0x00
|
||||
#define PCA963X_MODE2 0x01
|
||||
#define PCA963X_PWM_BASE 0x02
|
||||
|
||||
enum pca963x_type {
|
||||
pca9633,
|
||||
pca9634,
|
||||
};
|
||||
|
||||
struct pca963x_chipdef {
|
||||
u8 grppwm;
|
||||
u8 grpfreq;
|
||||
u8 ledout_base;
|
||||
int n_leds;
|
||||
};
|
||||
|
||||
static struct pca963x_chipdef pca963x_chipdefs[] = {
|
||||
[pca9633] = {
|
||||
.grppwm = 0x6,
|
||||
.grpfreq = 0x7,
|
||||
.ledout_base = 0x8,
|
||||
.n_leds = 4,
|
||||
},
|
||||
[pca9634] = {
|
||||
.grppwm = 0xa,
|
||||
.grpfreq = 0xb,
|
||||
.ledout_base = 0xc,
|
||||
.n_leds = 8,
|
||||
},
|
||||
};
|
||||
|
||||
/* Total blink period in milliseconds */
|
||||
#define PCA963X_BLINK_PERIOD_MIN 42
|
||||
#define PCA963X_BLINK_PERIOD_MAX 10667
|
||||
|
||||
static const struct i2c_device_id pca963x_id[] = {
|
||||
{ "pca9632", pca9633 },
|
||||
{ "pca9633", pca9633 },
|
||||
{ "pca9634", pca9634 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, pca963x_id);
|
||||
|
||||
enum pca963x_cmd {
|
||||
BRIGHTNESS_SET,
|
||||
BLINK_SET,
|
||||
};
|
||||
|
||||
struct pca963x_led;
|
||||
|
||||
struct pca963x {
|
||||
struct pca963x_chipdef *chipdef;
|
||||
struct mutex mutex;
|
||||
struct i2c_client *client;
|
||||
struct pca963x_led *leds;
|
||||
};
|
||||
|
||||
struct pca963x_led {
|
||||
struct pca963x *chip;
|
||||
struct work_struct work;
|
||||
enum led_brightness brightness;
|
||||
struct led_classdev led_cdev;
|
||||
int led_num; /* 0 .. 7 potentially */
|
||||
enum pca963x_cmd cmd;
|
||||
char name[32];
|
||||
u8 gdc;
|
||||
u8 gfrq;
|
||||
};
|
||||
|
||||
static void pca963x_brightness_work(struct pca963x_led *pca963x)
|
||||
{
|
||||
u8 ledout_addr = pca963x->chip->chipdef->ledout_base
|
||||
+ (pca963x->led_num / 4);
|
||||
u8 ledout;
|
||||
int shift = 2 * (pca963x->led_num % 4);
|
||||
u8 mask = 0x3 << shift;
|
||||
|
||||
mutex_lock(&pca963x->chip->mutex);
|
||||
ledout = i2c_smbus_read_byte_data(pca963x->chip->client, ledout_addr);
|
||||
switch (pca963x->brightness) {
|
||||
case LED_FULL:
|
||||
i2c_smbus_write_byte_data(pca963x->chip->client, ledout_addr,
|
||||
(ledout & ~mask) | (PCA963X_LED_ON << shift));
|
||||
break;
|
||||
case LED_OFF:
|
||||
i2c_smbus_write_byte_data(pca963x->chip->client, ledout_addr,
|
||||
ledout & ~mask);
|
||||
break;
|
||||
default:
|
||||
i2c_smbus_write_byte_data(pca963x->chip->client,
|
||||
PCA963X_PWM_BASE + pca963x->led_num,
|
||||
pca963x->brightness);
|
||||
i2c_smbus_write_byte_data(pca963x->chip->client, ledout_addr,
|
||||
(ledout & ~mask) | (PCA963X_LED_PWM << shift));
|
||||
break;
|
||||
}
|
||||
mutex_unlock(&pca963x->chip->mutex);
|
||||
}
|
||||
|
||||
static void pca963x_blink_work(struct pca963x_led *pca963x)
|
||||
{
|
||||
u8 ledout_addr = pca963x->chip->chipdef->ledout_base +
|
||||
(pca963x->led_num / 4);
|
||||
u8 ledout;
|
||||
u8 mode2 = i2c_smbus_read_byte_data(pca963x->chip->client,
|
||||
PCA963X_MODE2);
|
||||
int shift = 2 * (pca963x->led_num % 4);
|
||||
u8 mask = 0x3 << shift;
|
||||
|
||||
i2c_smbus_write_byte_data(pca963x->chip->client,
|
||||
pca963x->chip->chipdef->grppwm, pca963x->gdc);
|
||||
|
||||
i2c_smbus_write_byte_data(pca963x->chip->client,
|
||||
pca963x->chip->chipdef->grpfreq, pca963x->gfrq);
|
||||
|
||||
if (!(mode2 & PCA963X_MODE2_DMBLNK))
|
||||
i2c_smbus_write_byte_data(pca963x->chip->client, PCA963X_MODE2,
|
||||
mode2 | PCA963X_MODE2_DMBLNK);
|
||||
|
||||
mutex_lock(&pca963x->chip->mutex);
|
||||
ledout = i2c_smbus_read_byte_data(pca963x->chip->client, ledout_addr);
|
||||
if ((ledout & mask) != (PCA963X_LED_GRP_PWM << shift))
|
||||
i2c_smbus_write_byte_data(pca963x->chip->client, ledout_addr,
|
||||
(ledout & ~mask) | (PCA963X_LED_GRP_PWM << shift));
|
||||
mutex_unlock(&pca963x->chip->mutex);
|
||||
}
|
||||
|
||||
static void pca963x_work(struct work_struct *work)
|
||||
{
|
||||
struct pca963x_led *pca963x = container_of(work,
|
||||
struct pca963x_led, work);
|
||||
|
||||
switch (pca963x->cmd) {
|
||||
case BRIGHTNESS_SET:
|
||||
pca963x_brightness_work(pca963x);
|
||||
break;
|
||||
case BLINK_SET:
|
||||
pca963x_blink_work(pca963x);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void pca963x_led_set(struct led_classdev *led_cdev,
|
||||
enum led_brightness value)
|
||||
{
|
||||
struct pca963x_led *pca963x;
|
||||
|
||||
pca963x = container_of(led_cdev, struct pca963x_led, led_cdev);
|
||||
|
||||
pca963x->cmd = BRIGHTNESS_SET;
|
||||
pca963x->brightness = value;
|
||||
|
||||
/*
|
||||
* Must use workqueue for the actual I/O since I2C operations
|
||||
* can sleep.
|
||||
*/
|
||||
schedule_work(&pca963x->work);
|
||||
}
|
||||
|
||||
static int pca963x_blink_set(struct led_classdev *led_cdev,
|
||||
unsigned long *delay_on, unsigned long *delay_off)
|
||||
{
|
||||
struct pca963x_led *pca963x;
|
||||
unsigned long time_on, time_off, period;
|
||||
u8 gdc, gfrq;
|
||||
|
||||
pca963x = container_of(led_cdev, struct pca963x_led, led_cdev);
|
||||
|
||||
time_on = *delay_on;
|
||||
time_off = *delay_off;
|
||||
|
||||
/* If both zero, pick reasonable defaults of 500ms each */
|
||||
if (!time_on && !time_off) {
|
||||
time_on = 500;
|
||||
time_off = 500;
|
||||
}
|
||||
|
||||
period = time_on + time_off;
|
||||
|
||||
/* If period not supported by hardware, default to someting sane. */
|
||||
if ((period < PCA963X_BLINK_PERIOD_MIN) ||
|
||||
(period > PCA963X_BLINK_PERIOD_MAX)) {
|
||||
time_on = 500;
|
||||
time_off = 500;
|
||||
period = time_on + time_off;
|
||||
}
|
||||
|
||||
/*
|
||||
* From manual: duty cycle = (GDC / 256) ->
|
||||
* (time_on / period) = (GDC / 256) ->
|
||||
* GDC = ((time_on * 256) / period)
|
||||
*/
|
||||
gdc = (time_on * 256) / period;
|
||||
|
||||
/*
|
||||
* From manual: period = ((GFRQ + 1) / 24) in seconds.
|
||||
* So, period (in ms) = (((GFRQ + 1) / 24) * 1000) ->
|
||||
* GFRQ = ((period * 24 / 1000) - 1)
|
||||
*/
|
||||
gfrq = (period * 24 / 1000) - 1;
|
||||
|
||||
pca963x->cmd = BLINK_SET;
|
||||
pca963x->gdc = gdc;
|
||||
pca963x->gfrq = gfrq;
|
||||
|
||||
/*
|
||||
* Must use workqueue for the actual I/O since I2C operations
|
||||
* can sleep.
|
||||
*/
|
||||
schedule_work(&pca963x->work);
|
||||
|
||||
*delay_on = time_on;
|
||||
*delay_off = time_off;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_OF)
|
||||
static struct pca963x_platform_data *
|
||||
pca963x_dt_init(struct i2c_client *client, struct pca963x_chipdef *chip)
|
||||
{
|
||||
struct device_node *np = client->dev.of_node, *child;
|
||||
struct pca963x_platform_data *pdata;
|
||||
struct led_info *pca963x_leds;
|
||||
int count;
|
||||
|
||||
count = of_get_child_count(np);
|
||||
if (!count || count > chip->n_leds)
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
pca963x_leds = devm_kzalloc(&client->dev,
|
||||
sizeof(struct led_info) * chip->n_leds, GFP_KERNEL);
|
||||
if (!pca963x_leds)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
for_each_child_of_node(np, child) {
|
||||
struct led_info led;
|
||||
u32 reg;
|
||||
int res;
|
||||
|
||||
res = of_property_read_u32(child, "reg", ®);
|
||||
if ((res != 0) || (reg >= chip->n_leds))
|
||||
continue;
|
||||
led.name =
|
||||
of_get_property(child, "label", NULL) ? : child->name;
|
||||
led.default_trigger =
|
||||
of_get_property(child, "linux,default-trigger", NULL);
|
||||
pca963x_leds[reg] = led;
|
||||
}
|
||||
pdata = devm_kzalloc(&client->dev,
|
||||
sizeof(struct pca963x_platform_data), GFP_KERNEL);
|
||||
if (!pdata)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
pdata->leds.leds = pca963x_leds;
|
||||
pdata->leds.num_leds = chip->n_leds;
|
||||
|
||||
/* default to open-drain unless totem pole (push-pull) is specified */
|
||||
if (of_property_read_bool(np, "nxp,totem-pole"))
|
||||
pdata->outdrv = PCA963X_TOTEM_POLE;
|
||||
else
|
||||
pdata->outdrv = PCA963X_OPEN_DRAIN;
|
||||
|
||||
/* default to software blinking unless hardware blinking is specified */
|
||||
if (of_property_read_bool(np, "nxp,hw-blink"))
|
||||
pdata->blink_type = PCA963X_HW_BLINK;
|
||||
else
|
||||
pdata->blink_type = PCA963X_SW_BLINK;
|
||||
|
||||
return pdata;
|
||||
}
|
||||
|
||||
static const struct of_device_id of_pca963x_match[] = {
|
||||
{ .compatible = "nxp,pca9632", },
|
||||
{ .compatible = "nxp,pca9633", },
|
||||
{ .compatible = "nxp,pca9634", },
|
||||
{},
|
||||
};
|
||||
#else
|
||||
static struct pca963x_platform_data *
|
||||
pca963x_dt_init(struct i2c_client *client, struct pca963x_chipdef *chip)
|
||||
{
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int pca963x_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct pca963x *pca963x_chip;
|
||||
struct pca963x_led *pca963x;
|
||||
struct pca963x_platform_data *pdata;
|
||||
struct pca963x_chipdef *chip;
|
||||
int i, err;
|
||||
|
||||
chip = &pca963x_chipdefs[id->driver_data];
|
||||
pdata = dev_get_platdata(&client->dev);
|
||||
|
||||
if (!pdata) {
|
||||
pdata = pca963x_dt_init(client, chip);
|
||||
if (IS_ERR(pdata)) {
|
||||
dev_warn(&client->dev, "could not parse configuration\n");
|
||||
pdata = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (pdata && (pdata->leds.num_leds < 1 ||
|
||||
pdata->leds.num_leds > chip->n_leds)) {
|
||||
dev_err(&client->dev, "board info must claim 1-%d LEDs",
|
||||
chip->n_leds);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pca963x_chip = devm_kzalloc(&client->dev, sizeof(*pca963x_chip),
|
||||
GFP_KERNEL);
|
||||
if (!pca963x_chip)
|
||||
return -ENOMEM;
|
||||
pca963x = devm_kzalloc(&client->dev, chip->n_leds * sizeof(*pca963x),
|
||||
GFP_KERNEL);
|
||||
if (!pca963x)
|
||||
return -ENOMEM;
|
||||
|
||||
i2c_set_clientdata(client, pca963x_chip);
|
||||
|
||||
mutex_init(&pca963x_chip->mutex);
|
||||
pca963x_chip->chipdef = chip;
|
||||
pca963x_chip->client = client;
|
||||
pca963x_chip->leds = pca963x;
|
||||
|
||||
/* Turn off LEDs by default*/
|
||||
i2c_smbus_write_byte_data(client, chip->ledout_base, 0x00);
|
||||
if (chip->n_leds > 4)
|
||||
i2c_smbus_write_byte_data(client, chip->ledout_base + 1, 0x00);
|
||||
|
||||
for (i = 0; i < chip->n_leds; i++) {
|
||||
pca963x[i].led_num = i;
|
||||
pca963x[i].chip = pca963x_chip;
|
||||
|
||||
/* Platform data can specify LED names and default triggers */
|
||||
if (pdata && i < pdata->leds.num_leds) {
|
||||
if (pdata->leds.leds[i].name)
|
||||
snprintf(pca963x[i].name,
|
||||
sizeof(pca963x[i].name), "pca963x:%s",
|
||||
pdata->leds.leds[i].name);
|
||||
if (pdata->leds.leds[i].default_trigger)
|
||||
pca963x[i].led_cdev.default_trigger =
|
||||
pdata->leds.leds[i].default_trigger;
|
||||
}
|
||||
if (!pdata || i >= pdata->leds.num_leds ||
|
||||
!pdata->leds.leds[i].name)
|
||||
snprintf(pca963x[i].name, sizeof(pca963x[i].name),
|
||||
"pca963x:%d:%.2x:%d", client->adapter->nr,
|
||||
client->addr, i);
|
||||
|
||||
pca963x[i].led_cdev.name = pca963x[i].name;
|
||||
pca963x[i].led_cdev.brightness_set = pca963x_led_set;
|
||||
|
||||
if (pdata && pdata->blink_type == PCA963X_HW_BLINK)
|
||||
pca963x[i].led_cdev.blink_set = pca963x_blink_set;
|
||||
|
||||
INIT_WORK(&pca963x[i].work, pca963x_work);
|
||||
|
||||
err = led_classdev_register(&client->dev, &pca963x[i].led_cdev);
|
||||
if (err < 0)
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* Disable LED all-call address and set normal mode */
|
||||
i2c_smbus_write_byte_data(client, PCA963X_MODE1, 0x00);
|
||||
|
||||
/* Configure output: open-drain or totem pole (push-pull) */
|
||||
if (pdata && pdata->outdrv == PCA963X_OPEN_DRAIN)
|
||||
i2c_smbus_write_byte_data(client, PCA963X_MODE2, 0x01);
|
||||
|
||||
return 0;
|
||||
|
||||
exit:
|
||||
while (i--) {
|
||||
led_classdev_unregister(&pca963x[i].led_cdev);
|
||||
cancel_work_sync(&pca963x[i].work);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int pca963x_remove(struct i2c_client *client)
|
||||
{
|
||||
struct pca963x *pca963x = i2c_get_clientdata(client);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < pca963x->chipdef->n_leds; i++) {
|
||||
led_classdev_unregister(&pca963x->leds[i].led_cdev);
|
||||
cancel_work_sync(&pca963x->leds[i].work);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct i2c_driver pca963x_driver = {
|
||||
.driver = {
|
||||
.name = "leds-pca963x",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_match_ptr(of_pca963x_match),
|
||||
},
|
||||
.probe = pca963x_probe,
|
||||
.remove = pca963x_remove,
|
||||
.id_table = pca963x_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(pca963x_driver);
|
||||
|
||||
MODULE_AUTHOR("Peter Meerwald <p.meerwald@bct-electronic.com>");
|
||||
MODULE_DESCRIPTION("PCA963X LED driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -147,7 +147,7 @@ err:
|
||||
|
||||
static int led_pwm_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct led_pwm_platform_data *pdata = pdev->dev.platform_data;
|
||||
struct led_pwm_platform_data *pdata = dev_get_platdata(&pdev->dev);
|
||||
struct led_pwm_priv *priv;
|
||||
int i, ret = 0;
|
||||
|
||||
|
@ -142,7 +142,8 @@ static void regulator_led_brightness_set(struct led_classdev *led_cdev,
|
||||
|
||||
static int regulator_led_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct led_regulator_platform_data *pdata = pdev->dev.platform_data;
|
||||
struct led_regulator_platform_data *pdata =
|
||||
dev_get_platdata(&pdev->dev);
|
||||
struct regulator_led *led;
|
||||
struct regulator *vcc;
|
||||
int ret = 0;
|
||||
|
@ -71,7 +71,7 @@ static int s3c24xx_led_remove(struct platform_device *dev)
|
||||
|
||||
static int s3c24xx_led_probe(struct platform_device *dev)
|
||||
{
|
||||
struct s3c24xx_led_platdata *pdata = dev->dev.platform_data;
|
||||
struct s3c24xx_led_platdata *pdata = dev_get_platdata(&dev->dev);
|
||||
struct s3c24xx_gpio_led *led;
|
||||
int ret;
|
||||
|
||||
|
@ -91,7 +91,7 @@ MODULE_PARM_DESC(nodetect, "Skip DMI-based hardware detection");
|
||||
* detected as working, but in reality it is not) as low as
|
||||
* possible.
|
||||
*/
|
||||
static struct dmi_system_id __initdata nas_led_whitelist[] = {
|
||||
static struct dmi_system_id nas_led_whitelist[] __initdata = {
|
||||
{
|
||||
.callback = ss4200_led_dmi_callback,
|
||||
.ident = "Intel SS4200-E",
|
||||
@ -197,7 +197,7 @@ static void nasgpio_led_set_attr(struct led_classdev *led_cdev,
|
||||
spin_unlock(&nasgpio_gpio_lock);
|
||||
}
|
||||
|
||||
u32 nasgpio_led_get_attr(struct led_classdev *led_cdev, u32 port)
|
||||
static u32 nasgpio_led_get_attr(struct led_classdev *led_cdev, u32 port)
|
||||
{
|
||||
struct nasgpio_led *led = led_classdev_to_nasgpio_led(led_cdev);
|
||||
u32 gpio_in;
|
||||
|
@ -737,7 +737,7 @@ static int tca6507_probe(struct i2c_client *client,
|
||||
int i = 0;
|
||||
|
||||
adapter = to_i2c_adapter(client->dev.parent);
|
||||
pdata = client->dev.platform_data;
|
||||
pdata = dev_get_platdata(&client->dev);
|
||||
|
||||
if (!i2c_check_functionality(adapter, I2C_FUNC_I2C))
|
||||
return -EIO;
|
||||
|
@ -230,9 +230,9 @@ static int wm831x_status_probe(struct platform_device *pdev)
|
||||
int id = pdev->id % ARRAY_SIZE(chip_pdata->status);
|
||||
int ret;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_IO, 0);
|
||||
res = platform_get_resource(pdev, IORESOURCE_REG, 0);
|
||||
if (res == NULL) {
|
||||
dev_err(&pdev->dev, "No I/O resource\n");
|
||||
dev_err(&pdev->dev, "No register resource\n");
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
@ -246,8 +246,8 @@ static int wm831x_status_probe(struct platform_device *pdev)
|
||||
drvdata->wm831x = wm831x;
|
||||
drvdata->reg = res->start;
|
||||
|
||||
if (wm831x->dev->platform_data)
|
||||
chip_pdata = wm831x->dev->platform_data;
|
||||
if (dev_get_platdata(wm831x->dev))
|
||||
chip_pdata = dev_get_platdata(wm831x->dev);
|
||||
else
|
||||
chip_pdata = NULL;
|
||||
|
||||
|
@ -203,7 +203,7 @@ static int wm8350_led_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct regulator *isink, *dcdc;
|
||||
struct wm8350_led *led;
|
||||
struct wm8350_led_platform_data *pdata = pdev->dev.platform_data;
|
||||
struct wm8350_led_platform_data *pdata = dev_get_platdata(&pdev->dev);
|
||||
int i;
|
||||
|
||||
if (pdata == NULL) {
|
||||
|
@ -36,26 +36,28 @@ static int fb_notifier_callback(struct notifier_block *p,
|
||||
struct bl_trig_notifier, notifier);
|
||||
struct led_classdev *led = n->led;
|
||||
struct fb_event *fb_event = data;
|
||||
int *blank = fb_event->data;
|
||||
int new_status = *blank ? BLANK : UNBLANK;
|
||||
int *blank;
|
||||
int new_status;
|
||||
|
||||
switch (event) {
|
||||
case FB_EVENT_BLANK:
|
||||
if (new_status == n->old_status)
|
||||
break;
|
||||
/* If we aren't interested in this event, skip it immediately ... */
|
||||
if (event != FB_EVENT_BLANK)
|
||||
return 0;
|
||||
|
||||
if ((n->old_status == UNBLANK) ^ n->invert) {
|
||||
n->brightness = led->brightness;
|
||||
__led_set_brightness(led, LED_OFF);
|
||||
} else {
|
||||
__led_set_brightness(led, n->brightness);
|
||||
}
|
||||
blank = fb_event->data;
|
||||
new_status = *blank ? BLANK : UNBLANK;
|
||||
|
||||
n->old_status = new_status;
|
||||
if (new_status == n->old_status)
|
||||
return 0;
|
||||
|
||||
break;
|
||||
if ((n->old_status == UNBLANK) ^ n->invert) {
|
||||
n->brightness = led->brightness;
|
||||
__led_set_brightness(led, LED_OFF);
|
||||
} else {
|
||||
__led_set_brightness(led, n->brightness);
|
||||
}
|
||||
|
||||
n->old_status = new_status;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -36,6 +36,13 @@ struct lp55xx_predef_pattern {
|
||||
u8 size_b;
|
||||
};
|
||||
|
||||
enum lp8501_pwr_sel {
|
||||
LP8501_ALL_VDD, /* D1~9 are connected to VDD */
|
||||
LP8501_6VDD_3VOUT, /* D1~6 with VDD, D7~9 with VOUT */
|
||||
LP8501_3VDD_6VOUT, /* D1~6 with VOUT, D7~9 with VDD */
|
||||
LP8501_ALL_VOUT, /* D1~9 are connected to VOUT */
|
||||
};
|
||||
|
||||
/*
|
||||
* struct lp55xx_platform_data
|
||||
* @led_config : Configurable led class device
|
||||
@ -67,6 +74,9 @@ struct lp55xx_platform_data {
|
||||
/* Predefined pattern data */
|
||||
struct lp55xx_predef_pattern *patterns;
|
||||
unsigned int num_patterns;
|
||||
|
||||
/* LP8501 specific */
|
||||
enum lp8501_pwr_sel pwr_sel;
|
||||
};
|
||||
|
||||
#endif /* _LEDS_LP55XX_H */
|
||||
|
@ -1,7 +1,8 @@
|
||||
/*
|
||||
* PCA9633 LED chip driver.
|
||||
* PCA963X LED chip driver.
|
||||
*
|
||||
* Copyright 2012 bct electronic GmbH
|
||||
* Copyright 2013 Qtechnology A/S
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@ -18,18 +19,24 @@
|
||||
* 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_PCA9633_H
|
||||
#define __LINUX_PCA9633_H
|
||||
#ifndef __LINUX_PCA963X_H
|
||||
#define __LINUX_PCA963X_H
|
||||
#include <linux/leds.h>
|
||||
|
||||
enum pca9633_outdrv {
|
||||
PCA9633_OPEN_DRAIN,
|
||||
PCA9633_TOTEM_POLE, /* aka push-pull */
|
||||
enum pca963x_outdrv {
|
||||
PCA963X_OPEN_DRAIN,
|
||||
PCA963X_TOTEM_POLE, /* aka push-pull */
|
||||
};
|
||||
|
||||
struct pca9633_platform_data {
|
||||
enum pca963x_blink_type {
|
||||
PCA963X_SW_BLINK,
|
||||
PCA963X_HW_BLINK,
|
||||
};
|
||||
|
||||
struct pca963x_platform_data {
|
||||
struct led_platform_data leds;
|
||||
enum pca9633_outdrv outdrv;
|
||||
enum pca963x_outdrv outdrv;
|
||||
enum pca963x_blink_type blink_type;
|
||||
};
|
||||
|
||||
#endif /* __LINUX_PCA9633_H*/
|
||||
#endif /* __LINUX_PCA963X_H*/
|
Loading…
Reference in New Issue
Block a user