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:
Linus Torvalds 2013-09-12 11:35:33 -07:00
commit 5223161dc0
44 changed files with 1645 additions and 315 deletions

View File

@ -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>;
};
};

View 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";
};
};

View File

@ -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

View File

@ -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.

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

@ -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++) {

View File

@ -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) */

View File

@ -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",

View File

@ -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;

View File

@ -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++) {

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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;

View File

@ -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
View 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");

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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)) !=

View File

@ -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
View 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", &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");

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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) {

View File

@ -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;
}

View File

@ -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 */

View File

@ -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*/