aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKim, Milo <Milo.Kim@ti.com>2013-02-19 00:10:14 -0500
committerBryan Wu <cooloney@gmail.com>2013-04-01 14:04:48 -0400
commitff45262a85dbf1bc74463c5dcea1d71a406d4d8e (patch)
tree4b78b4d3efa02a63346a395fa02e610d4b62bf55
parent07961ac7c0ee8b546658717034fe692fd12eefa9 (diff)
leds: add new LP5562 LED driver
LP5562 can drive up to 4 channels, RGB and White. LEDs can be controlled directly via the led class control interface. LP55xx common driver LP5562 is one of LP55xx family device, so LP55xx common code are used. On the other hand, chip specific configuration is defined in the structure 'lp55xx_device_config' LED pattern data LP5562 has also internal program memory which is used for running various LED patterns. LP5562 driver supports the firmware interface and the predefined pattern data as well. LP5562 device attributes: 'led_pattern' and 'engine_mux' A 'led_pattern' is an index code which runs the predefined pattern data. And 'engine_mux' is updated with the firmware interface is activated. Detailed description has been updated in the documentation files, 'leds-lp55xx.txt' and 'leds-lp5562.txt'. Changes on the header file LP5562 configurable definitions are added. Pattern RGB data is fixed as constant value. (No side effect on other devices, LP5521 or LP5523.) (cooloney@gmail.com: remove redundant mutex_unlock(). Reported by Dan Carpenter <dan.carpenter@oracle.com>) Signed-off-by: Milo(Woogyom) Kim <milo.kim@ti.com> Signed-off-by: Bryan Wu <cooloney@gmail.com>
-rw-r--r--Documentation/leds/00-INDEX2
-rw-r--r--Documentation/leds/leds-lp5562.txt135
-rw-r--r--Documentation/leds/leds-lp55xx.txt46
-rw-r--r--drivers/leds/Kconfig14
-rw-r--r--drivers/leds/Makefile1
-rw-r--r--drivers/leds/leds-lp5562.c593
-rw-r--r--drivers/leds/leds-lp55xx-common.c2
-rw-r--r--include/linux/platform_data/leds-lp55xx.h13
8 files changed, 799 insertions, 7 deletions
diff --git a/Documentation/leds/00-INDEX b/Documentation/leds/00-INDEX
index 5246090ef15c..1ecd1596633e 100644
--- a/Documentation/leds/00-INDEX
+++ b/Documentation/leds/00-INDEX
@@ -6,6 +6,8 @@ leds-lp5521.txt
6 - notes on how to use the leds-lp5521 driver. 6 - notes on how to use the leds-lp5521 driver.
7leds-lp5523.txt 7leds-lp5523.txt
8 - notes on how to use the leds-lp5523 driver. 8 - notes on how to use the leds-lp5523 driver.
9leds-lp5562.txt
10 - notes on how to use the leds-lp5562 driver.
9leds-lp55xx.txt 11leds-lp55xx.txt
10 - description about lp55xx common driver. 12 - description about lp55xx common driver.
11leds-lm3556.txt 13leds-lm3556.txt
diff --git a/Documentation/leds/leds-lp5562.txt b/Documentation/leds/leds-lp5562.txt
new file mode 100644
index 000000000000..96061000dd93
--- /dev/null
+++ b/Documentation/leds/leds-lp5562.txt
@@ -0,0 +1,135 @@
1Kernel driver for LP5562
2========================
3
4* TI LP5562 LED Driver
5
6Author: Milo(Woogyom) Kim <milo.kim@ti.com>
7
8Description
9
10 LP5562 can drive up to 4 channels. R/G/B and White.
11 LEDs can be controlled directly via the led class control interface.
12
13 All four channels can be also controlled using the engine micro programs.
14 LP5562 has the internal program memory for running various LED patterns.
15 For the details, please refer to 'firmware' section in leds-lp55xx.txt
16
17Device attribute: engine_mux
18
19 3 Engines are allocated in LP5562, but the number of channel is 4.
20 Therefore each channel should be mapped to the engine number.
21 Value : RGB or W
22
23 This attribute is used for programming LED data with the firmware interface.
24 Unlike the LP5521/LP5523/55231, LP5562 has unique feature for the engine mux,
25 so additional sysfs is required.
26
27 LED Map
28 Red ... Engine 1 (fixed)
29 Green ... Engine 2 (fixed)
30 Blue ... Engine 3 (fixed)
31 White ... Engine 1 or 2 or 3 (selective)
32
33How to load the program data using engine_mux
34
35 Before loading the LP5562 program data, engine_mux should be written between
36 the engine selection and loading the firmware.
37 Engine mux has two different mode, RGB and W.
38 RGB is used for loading RGB program data, W is used for W program data.
39
40 For example, run blinking green channel pattern,
41 echo 2 > /sys/bus/i2c/devices/xxxx/select_engine # 2 is for green channel
42 echo "RGB" > /sys/bus/i2c/devices/xxxx/engine_mux # engine mux for RGB
43 echo 1 > /sys/class/firmware/lp5562/loading
44 echo "4000600040FF6000" > /sys/class/firmware/lp5562/data
45 echo 0 > /sys/class/firmware/lp5562/loading
46 echo 1 > /sys/bus/i2c/devices/xxxx/run_engine
47
48 To run a blinking white pattern,
49 echo 1 or 2 or 3 > /sys/bus/i2c/devices/xxxx/select_engine
50 echo "W" > /sys/bus/i2c/devices/xxxx/engine_mux
51 echo 1 > /sys/class/firmware/lp5562/loading
52 echo "4000600040FF6000" > /sys/class/firmware/lp5562/data
53 echo 0 > /sys/class/firmware/lp5562/loading
54 echo 1 > /sys/bus/i2c/devices/xxxx/run_engine
55
56How to load the predefined patterns
57
58 Please refer to 'leds-lp55xx.txt"
59
60Setting Current of Each Channel
61
62 Like LP5521 and LP5523/55231, LP5562 provides LED current settings.
63 The 'led_current' and 'max_current' are used.
64
65(Example of Platform data)
66
67To configure the platform specific data, lp55xx_platform_data structure is used.
68
69static struct lp55xx_led_config lp5562_led_config[] = {
70 {
71 .name = "R",
72 .chan_nr = 0,
73 .led_current = 20,
74 .max_current = 40,
75 },
76 {
77 .name = "G",
78 .chan_nr = 1,
79 .led_current = 20,
80 .max_current = 40,
81 },
82 {
83 .name = "B",
84 .chan_nr = 2,
85 .led_current = 20,
86 .max_current = 40,
87 },
88 {
89 .name = "W",
90 .chan_nr = 3,
91 .led_current = 20,
92 .max_current = 40,
93 },
94};
95
96static int lp5562_setup(void)
97{
98 /* setup HW resources */
99}
100
101static void lp5562_release(void)
102{
103 /* Release HW resources */
104}
105
106static void lp5562_enable(bool state)
107{
108 /* Control of chip enable signal */
109}
110
111static struct lp55xx_platform_data lp5562_platform_data = {
112 .led_config = lp5562_led_config,
113 .num_channels = ARRAY_SIZE(lp5562_led_config),
114 .setup_resources = lp5562_setup,
115 .release_resources = lp5562_release,
116 .enable = lp5562_enable,
117};
118
119If the current is set to 0 in the platform data, that channel is
120disabled and it is not visible in the sysfs.
121
122The 'update_config' : CONFIG register (ADDR 08h)
123This value is platform-specific data.
124If update_config is not defined, the CONFIG register is set with
125'LP5562_PWRSAVE_EN | LP5562_CLK_AUTO'.
126(Enable auto-powersave, set automatic clock source selection)
127
128#define LP5562_CONFIGS (LP5562_PWM_HF | LP5562_PWRSAVE_EN | \
129 LP5562_CLK_SRC_EXT)
130
131static struct lp55xx_platform_data lp5562_pdata = {
132 .led_config = lp5562_led_config,
133 .num_channels = ARRAY_SIZE(lp5562_led_config),
134 .update_config = LP5562_CONFIGS,
135};
diff --git a/Documentation/leds/leds-lp55xx.txt b/Documentation/leds/leds-lp55xx.txt
index ced41868d2d1..eec8fa2ffe4e 100644
--- a/Documentation/leds/leds-lp55xx.txt
+++ b/Documentation/leds/leds-lp55xx.txt
@@ -5,7 +5,7 @@ Authors: Milo(Woogyom) Kim <milo.kim@ti.com>
5 5
6Description 6Description
7----------- 7-----------
8LP5521, LP5523/55231 have common features as below. 8LP5521, LP5523/55231 and LP5562 have common features as below.
9 9
10 Register access via the I2C 10 Register access via the I2C
11 Device initialization/deinitialization 11 Device initialization/deinitialization
@@ -116,3 +116,47 @@ To support this, 'run_engine' and 'firmware_cb' are configurable in each driver.
116run_engine : Control the selected engine 116run_engine : Control the selected engine
117firmware_cb : The callback function after loading the firmware is done. 117firmware_cb : The callback function after loading the firmware is done.
118 Chip specific commands for loading and updating program memory. 118 Chip specific commands for loading and updating program memory.
119
120( Predefined pattern data )
121
122Without the firmware interface, LP55xx driver provides another method for
123loading a LED pattern. That is 'predefined' pattern.
124A predefined pattern is defined in the platform data and load it(or them)
125via the sysfs if needed.
126To use the predefined pattern concept, 'patterns' and 'num_patterns' should be
127configured.
128
129 Example of predefined pattern data:
130
131 /* mode_1: blinking data */
132 static const u8 mode_1[] = {
133 0x40, 0x00, 0x60, 0x00, 0x40, 0xFF, 0x60, 0x00,
134 };
135
136 /* mode_2: always on */
137 static const u8 mode_2[] = { 0x40, 0xFF, };
138
139 struct lp55xx_predef_pattern board_led_patterns[] = {
140 {
141 .r = mode_1,
142 .size_r = ARRAY_SIZE(mode_1),
143 },
144 {
145 .b = mode_2,
146 .size_b = ARRAY_SIZE(mode_2),
147 },
148 }
149
150 struct lp55xx_platform_data lp5562_pdata = {
151 ...
152 .patterns = board_led_patterns,
153 .num_patterns = ARRAY_SIZE(board_led_patterns),
154 };
155
156Then, mode_1 and mode_2 can be run via through the sysfs.
157
158 echo 1 > /sys/bus/i2c/devices/xxxx/led_pattern # red blinking LED pattern
159 echo 2 > /sys/bus/i2c/devices/xxxx/led_pattern # blue LED always on
160
161To stop running pattern,
162 echo 0 > /sys/bus/i2c/devices/xxxx/led_pattern
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index ec50824c02ec..c7f755034375 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -194,8 +194,8 @@ config LEDS_LP3944
194 module will be called leds-lp3944. 194 module will be called leds-lp3944.
195 195
196config LEDS_LP55XX_COMMON 196config LEDS_LP55XX_COMMON
197 tristate "Common Driver for TI/National LP5521 and LP5523/55231" 197 tristate "Common Driver for TI/National LP5521, LP5523/55231 and LP5562"
198 depends on LEDS_LP5521 || LEDS_LP5523 198 depends on LEDS_LP5521 || LEDS_LP5523 || LEDS_LP5562
199 select FW_LOADER 199 select FW_LOADER
200 help 200 help
201 This option supports common operations for LP5521 and LP5523/55231 201 This option supports common operations for LP5521 and LP5523/55231
@@ -222,6 +222,16 @@ config LEDS_LP5523
222 Driver provides direct control via LED class and interface for 222 Driver provides direct control via LED class and interface for
223 programming the engines. 223 programming the engines.
224 224
225config LEDS_LP5562
226 tristate "LED Support for TI LP5562 LED driver chip"
227 depends on LEDS_CLASS && I2C
228 select LEDS_LP55XX_COMMON
229 help
230 If you say yes here you get support for TI LP5562 LED driver.
231 It is 4 channels chip with programmable engines.
232 Driver provides direct control via LED class and interface for
233 programming the engines.
234
225config LEDS_LP8788 235config LEDS_LP8788
226 tristate "LED support for the TI LP8788 PMIC" 236 tristate "LED support for the TI LP8788 PMIC"
227 depends on LEDS_CLASS 237 depends on LEDS_CLASS
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index 215e7e3b6173..ab8f5c549ad3 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -26,6 +26,7 @@ obj-$(CONFIG_LEDS_LP3944) += leds-lp3944.o
26obj-$(CONFIG_LEDS_LP55XX_COMMON) += leds-lp55xx-common.o 26obj-$(CONFIG_LEDS_LP55XX_COMMON) += leds-lp55xx-common.o
27obj-$(CONFIG_LEDS_LP5521) += leds-lp5521.o 27obj-$(CONFIG_LEDS_LP5521) += leds-lp5521.o
28obj-$(CONFIG_LEDS_LP5523) += leds-lp5523.o 28obj-$(CONFIG_LEDS_LP5523) += leds-lp5523.o
29obj-$(CONFIG_LEDS_LP5562) += leds-lp5562.o
29obj-$(CONFIG_LEDS_LP8788) += leds-lp8788.o 30obj-$(CONFIG_LEDS_LP8788) += leds-lp8788.o
30obj-$(CONFIG_LEDS_TCA6507) += leds-tca6507.o 31obj-$(CONFIG_LEDS_TCA6507) += leds-tca6507.o
31obj-$(CONFIG_LEDS_CLEVO_MAIL) += leds-clevo-mail.o 32obj-$(CONFIG_LEDS_CLEVO_MAIL) += leds-clevo-mail.o
diff --git a/drivers/leds/leds-lp5562.c b/drivers/leds/leds-lp5562.c
new file mode 100644
index 000000000000..f8b927788c3a
--- /dev/null
+++ b/drivers/leds/leds-lp5562.c
@@ -0,0 +1,593 @@
1/*
2 * LP5562 LED driver
3 *
4 * Copyright (C) 2013 Texas Instruments
5 *
6 * Author: Milo(Woogyom) Kim <milo.kim@ti.com>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 */
12
13#include <linux/delay.h>
14#include <linux/firmware.h>
15#include <linux/i2c.h>
16#include <linux/init.h>
17#include <linux/leds.h>
18#include <linux/module.h>
19#include <linux/mutex.h>
20#include <linux/platform_data/leds-lp55xx.h>
21#include <linux/slab.h>
22
23#include "leds-lp55xx-common.h"
24
25#define LP5562_PROGRAM_LENGTH 32
26#define LP5562_MAX_LEDS 4
27
28/* ENABLE Register 00h */
29#define LP5562_REG_ENABLE 0x00
30#define LP5562_EXEC_ENG1_M 0x30
31#define LP5562_EXEC_ENG2_M 0x0C
32#define LP5562_EXEC_ENG3_M 0x03
33#define LP5562_EXEC_M 0x3F
34#define LP5562_MASTER_ENABLE 0x40 /* Chip master enable */
35#define LP5562_LOGARITHMIC_PWM 0x80 /* Logarithmic PWM adjustment */
36#define LP5562_EXEC_RUN 0x2A
37#define LP5562_ENABLE_DEFAULT \
38 (LP5562_MASTER_ENABLE | LP5562_LOGARITHMIC_PWM)
39#define LP5562_ENABLE_RUN_PROGRAM \
40 (LP5562_ENABLE_DEFAULT | LP5562_EXEC_RUN)
41
42/* OPMODE Register 01h */
43#define LP5562_REG_OP_MODE 0x01
44#define LP5562_MODE_ENG1_M 0x30
45#define LP5562_MODE_ENG2_M 0x0C
46#define LP5562_MODE_ENG3_M 0x03
47#define LP5562_LOAD_ENG1 0x10
48#define LP5562_LOAD_ENG2 0x04
49#define LP5562_LOAD_ENG3 0x01
50#define LP5562_RUN_ENG1 0x20
51#define LP5562_RUN_ENG2 0x08
52#define LP5562_RUN_ENG3 0x02
53#define LP5562_ENG1_IS_LOADING(mode) \
54 ((mode & LP5562_MODE_ENG1_M) == LP5562_LOAD_ENG1)
55#define LP5562_ENG2_IS_LOADING(mode) \
56 ((mode & LP5562_MODE_ENG2_M) == LP5562_LOAD_ENG2)
57#define LP5562_ENG3_IS_LOADING(mode) \
58 ((mode & LP5562_MODE_ENG3_M) == LP5562_LOAD_ENG3)
59
60/* BRIGHTNESS Registers */
61#define LP5562_REG_R_PWM 0x04
62#define LP5562_REG_G_PWM 0x03
63#define LP5562_REG_B_PWM 0x02
64#define LP5562_REG_W_PWM 0x0E
65
66/* CURRENT Registers */
67#define LP5562_REG_R_CURRENT 0x07
68#define LP5562_REG_G_CURRENT 0x06
69#define LP5562_REG_B_CURRENT 0x05
70#define LP5562_REG_W_CURRENT 0x0F
71
72/* CONFIG Register 08h */
73#define LP5562_REG_CONFIG 0x08
74#define LP5562_DEFAULT_CFG \
75 (LP5562_PWM_HF | LP5562_PWRSAVE_EN | LP5562_CLK_INT)
76
77/* RESET Register 0Dh */
78#define LP5562_REG_RESET 0x0D
79#define LP5562_RESET 0xFF
80
81/* PROGRAM ENGINE Registers */
82#define LP5562_REG_PROG_MEM_ENG1 0x10
83#define LP5562_REG_PROG_MEM_ENG2 0x30
84#define LP5562_REG_PROG_MEM_ENG3 0x50
85
86/* LEDMAP Register 70h */
87#define LP5562_REG_ENG_SEL 0x70
88#define LP5562_ENG_SEL_PWM 0
89#define LP5562_ENG_FOR_RGB_M 0x3F
90#define LP5562_ENG_SEL_RGB 0x1B /* R:ENG1, G:ENG2, B:ENG3 */
91#define LP5562_ENG_FOR_W_M 0xC0
92#define LP5562_ENG1_FOR_W 0x40 /* W:ENG1 */
93#define LP5562_ENG2_FOR_W 0x80 /* W:ENG2 */
94#define LP5562_ENG3_FOR_W 0xC0 /* W:ENG3 */
95
96/* Program Commands */
97#define LP5562_CMD_DISABLE 0x00
98#define LP5562_CMD_LOAD 0x15
99#define LP5562_CMD_RUN 0x2A
100#define LP5562_CMD_DIRECT 0x3F
101#define LP5562_PATTERN_OFF 0
102
103static inline void lp5562_wait_opmode_done(void)
104{
105 /* operation mode change needs to be longer than 153 us */
106 usleep_range(200, 300);
107}
108
109static inline void lp5562_wait_enable_done(void)
110{
111 /* it takes more 488 us to update ENABLE register */
112 usleep_range(500, 600);
113}
114
115static void lp5562_set_led_current(struct lp55xx_led *led, u8 led_current)
116{
117 u8 addr[] = {
118 LP5562_REG_R_CURRENT,
119 LP5562_REG_G_CURRENT,
120 LP5562_REG_B_CURRENT,
121 LP5562_REG_W_CURRENT,
122 };
123
124 led->led_current = led_current;
125 lp55xx_write(led->chip, addr[led->chan_nr], led_current);
126}
127
128static void lp5562_load_engine(struct lp55xx_chip *chip)
129{
130 enum lp55xx_engine_index idx = chip->engine_idx;
131 u8 mask[] = {
132 [LP55XX_ENGINE_1] = LP5562_MODE_ENG1_M,
133 [LP55XX_ENGINE_2] = LP5562_MODE_ENG2_M,
134 [LP55XX_ENGINE_3] = LP5562_MODE_ENG3_M,
135 };
136
137 u8 val[] = {
138 [LP55XX_ENGINE_1] = LP5562_LOAD_ENG1,
139 [LP55XX_ENGINE_2] = LP5562_LOAD_ENG2,
140 [LP55XX_ENGINE_3] = LP5562_LOAD_ENG3,
141 };
142
143 lp55xx_update_bits(chip, LP5562_REG_OP_MODE, mask[idx], val[idx]);
144
145 lp5562_wait_opmode_done();
146}
147
148static void lp5562_stop_engine(struct lp55xx_chip *chip)
149{
150 lp55xx_write(chip, LP5562_REG_OP_MODE, LP5562_CMD_DISABLE);
151 lp5562_wait_opmode_done();
152}
153
154static void lp5562_run_engine(struct lp55xx_chip *chip, bool start)
155{
156 int ret;
157 u8 mode;
158 u8 exec;
159
160 /* stop engine */
161 if (!start) {
162 lp55xx_write(chip, LP5562_REG_ENABLE, LP5562_ENABLE_DEFAULT);
163 lp5562_wait_enable_done();
164 lp5562_stop_engine(chip);
165 lp55xx_write(chip, LP5562_REG_ENG_SEL, LP5562_ENG_SEL_PWM);
166 lp55xx_write(chip, LP5562_REG_OP_MODE, LP5562_CMD_DIRECT);
167 lp5562_wait_opmode_done();
168 return;
169 }
170
171 /*
172 * To run the engine,
173 * operation mode and enable register should updated at the same time
174 */
175
176 ret = lp55xx_read(chip, LP5562_REG_OP_MODE, &mode);
177 if (ret)
178 return;
179
180 ret = lp55xx_read(chip, LP5562_REG_ENABLE, &exec);
181 if (ret)
182 return;
183
184 /* change operation mode to RUN only when each engine is loading */
185 if (LP5562_ENG1_IS_LOADING(mode)) {
186 mode = (mode & ~LP5562_MODE_ENG1_M) | LP5562_RUN_ENG1;
187 exec = (exec & ~LP5562_EXEC_ENG1_M) | LP5562_RUN_ENG1;
188 }
189
190 if (LP5562_ENG2_IS_LOADING(mode)) {
191 mode = (mode & ~LP5562_MODE_ENG2_M) | LP5562_RUN_ENG2;
192 exec = (exec & ~LP5562_EXEC_ENG2_M) | LP5562_RUN_ENG2;
193 }
194
195 if (LP5562_ENG3_IS_LOADING(mode)) {
196 mode = (mode & ~LP5562_MODE_ENG3_M) | LP5562_RUN_ENG3;
197 exec = (exec & ~LP5562_EXEC_ENG3_M) | LP5562_RUN_ENG3;
198 }
199
200 lp55xx_write(chip, LP5562_REG_OP_MODE, mode);
201 lp5562_wait_opmode_done();
202
203 lp55xx_update_bits(chip, LP5562_REG_ENABLE, LP5562_EXEC_M, exec);
204 lp5562_wait_enable_done();
205}
206
207static int lp5562_update_firmware(struct lp55xx_chip *chip,
208 const u8 *data, size_t size)
209{
210 enum lp55xx_engine_index idx = chip->engine_idx;
211 u8 pattern[LP5562_PROGRAM_LENGTH] = {0};
212 u8 addr[] = {
213 [LP55XX_ENGINE_1] = LP5562_REG_PROG_MEM_ENG1,
214 [LP55XX_ENGINE_2] = LP5562_REG_PROG_MEM_ENG2,
215 [LP55XX_ENGINE_3] = LP5562_REG_PROG_MEM_ENG3,
216 };
217 unsigned cmd;
218 char c[3];
219 int program_size;
220 int nrchars;
221 int offset = 0;
222 int ret;
223 int i;
224
225 /* clear program memory before updating */
226 for (i = 0; i < LP5562_PROGRAM_LENGTH; i++)
227 lp55xx_write(chip, addr[idx] + i, 0);
228
229 i = 0;
230 while ((offset < size - 1) && (i < LP5562_PROGRAM_LENGTH)) {
231 /* separate sscanfs because length is working only for %s */
232 ret = sscanf(data + offset, "%2s%n ", c, &nrchars);
233 if (ret != 1)
234 goto err;
235
236 ret = sscanf(c, "%2x", &cmd);
237 if (ret != 1)
238 goto err;
239
240 pattern[i] = (u8)cmd;
241 offset += nrchars;
242 i++;
243 }
244
245 /* Each instruction is 16bit long. Check that length is even */
246 if (i % 2)
247 goto err;
248
249 program_size = i;
250 for (i = 0; i < program_size; i++)
251 lp55xx_write(chip, addr[idx] + i, pattern[i]);
252
253 return 0;
254
255err:
256 dev_err(&chip->cl->dev, "wrong pattern format\n");
257 return -EINVAL;
258}
259
260static void lp5562_firmware_loaded(struct lp55xx_chip *chip)
261{
262 const struct firmware *fw = chip->fw;
263
264 if (fw->size > LP5562_PROGRAM_LENGTH) {
265 dev_err(&chip->cl->dev, "firmware data size overflow: %zu\n",
266 fw->size);
267 return;
268 }
269
270 /*
271 * Program momery sequence
272 * 1) set engine mode to "LOAD"
273 * 2) write firmware data into program memory
274 */
275
276 lp5562_load_engine(chip);
277 lp5562_update_firmware(chip, fw->data, fw->size);
278}
279
280static int lp5562_post_init_device(struct lp55xx_chip *chip)
281{
282 int ret;
283 u8 update_cfg = chip->pdata->update_config ? : LP5562_DEFAULT_CFG;
284
285 /* Set all PWMs to direct control mode */
286 ret = lp55xx_write(chip, LP5562_REG_OP_MODE, LP5562_CMD_DIRECT);
287 if (ret)
288 return ret;
289
290 lp5562_wait_opmode_done();
291
292 ret = lp55xx_write(chip, LP5562_REG_CONFIG, update_cfg);
293 if (ret)
294 return ret;
295
296 /* Initialize all channels PWM to zero -> leds off */
297 lp55xx_write(chip, LP5562_REG_R_PWM, 0);
298 lp55xx_write(chip, LP5562_REG_G_PWM, 0);
299 lp55xx_write(chip, LP5562_REG_B_PWM, 0);
300 lp55xx_write(chip, LP5562_REG_W_PWM, 0);
301
302 /* Set LED map as register PWM by default */
303 lp55xx_write(chip, LP5562_REG_ENG_SEL, LP5562_ENG_SEL_PWM);
304
305 return 0;
306}
307
308static void lp5562_led_brightness_work(struct work_struct *work)
309{
310 struct lp55xx_led *led = container_of(work, struct lp55xx_led,
311 brightness_work);
312 struct lp55xx_chip *chip = led->chip;
313 u8 addr[] = {
314 LP5562_REG_R_PWM,
315 LP5562_REG_G_PWM,
316 LP5562_REG_B_PWM,
317 LP5562_REG_W_PWM,
318 };
319
320 mutex_lock(&chip->lock);
321 lp55xx_write(chip, addr[led->chan_nr], led->brightness);
322 mutex_unlock(&chip->lock);
323}
324
325static void lp5562_write_program_memory(struct lp55xx_chip *chip,
326 u8 base, const u8 *rgb, int size)
327{
328 int i;
329
330 if (!rgb || size <= 0)
331 return;
332
333 for (i = 0; i < size; i++)
334 lp55xx_write(chip, base + i, *(rgb + i));
335
336 lp55xx_write(chip, base + i, 0);
337 lp55xx_write(chip, base + i + 1, 0);
338}
339
340/* check the size of program count */
341static inline bool _is_pc_overflow(struct lp55xx_predef_pattern *ptn)
342{
343 return (ptn->size_r >= LP5562_PROGRAM_LENGTH ||
344 ptn->size_g >= LP5562_PROGRAM_LENGTH ||
345 ptn->size_b >= LP5562_PROGRAM_LENGTH);
346}
347
348static int lp5562_run_predef_led_pattern(struct lp55xx_chip *chip, int mode)
349{
350 struct lp55xx_predef_pattern *ptn;
351 int i;
352
353 if (mode == LP5562_PATTERN_OFF) {
354 lp5562_run_engine(chip, false);
355 return 0;
356 }
357
358 ptn = chip->pdata->patterns + (mode - 1);
359 if (!ptn || _is_pc_overflow(ptn)) {
360 dev_err(&chip->cl->dev, "invalid pattern data\n");
361 return -EINVAL;
362 }
363
364 lp5562_stop_engine(chip);
365
366 /* Set LED map as RGB */
367 lp55xx_write(chip, LP5562_REG_ENG_SEL, LP5562_ENG_SEL_RGB);
368
369 /* Load engines */
370 for (i = LP55XX_ENGINE_1; i <= LP55XX_ENGINE_3; i++) {
371 chip->engine_idx = i;
372 lp5562_load_engine(chip);
373 }
374
375 /* Clear program registers */
376 lp55xx_write(chip, LP5562_REG_PROG_MEM_ENG1, 0);
377 lp55xx_write(chip, LP5562_REG_PROG_MEM_ENG1 + 1, 0);
378 lp55xx_write(chip, LP5562_REG_PROG_MEM_ENG2, 0);
379 lp55xx_write(chip, LP5562_REG_PROG_MEM_ENG2 + 1, 0);
380 lp55xx_write(chip, LP5562_REG_PROG_MEM_ENG3, 0);
381 lp55xx_write(chip, LP5562_REG_PROG_MEM_ENG3 + 1, 0);
382
383 /* Program engines */
384 lp5562_write_program_memory(chip, LP5562_REG_PROG_MEM_ENG1,
385 ptn->r, ptn->size_r);
386 lp5562_write_program_memory(chip, LP5562_REG_PROG_MEM_ENG2,
387 ptn->g, ptn->size_g);
388 lp5562_write_program_memory(chip, LP5562_REG_PROG_MEM_ENG3,
389 ptn->b, ptn->size_b);
390
391 /* Run engines */
392 lp5562_run_engine(chip, true);
393
394 return 0;
395}
396
397static ssize_t lp5562_store_pattern(struct device *dev,
398 struct device_attribute *attr,
399 const char *buf, size_t len)
400{
401 struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev));
402 struct lp55xx_chip *chip = led->chip;
403 struct lp55xx_predef_pattern *ptn = chip->pdata->patterns;
404 int num_patterns = chip->pdata->num_patterns;
405 unsigned long mode;
406 int ret;
407
408 ret = kstrtoul(buf, 0, &mode);
409 if (ret)
410 return ret;
411
412 if (mode > num_patterns || !ptn)
413 return -EINVAL;
414
415 mutex_lock(&chip->lock);
416 ret = lp5562_run_predef_led_pattern(chip, mode);
417 mutex_unlock(&chip->lock);
418
419 if (ret)
420 return ret;
421
422 return len;
423}
424
425static ssize_t lp5562_store_engine_mux(struct device *dev,
426 struct device_attribute *attr,
427 const char *buf, size_t len)
428{
429 struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev));
430 struct lp55xx_chip *chip = led->chip;
431 u8 mask;
432 u8 val;
433
434 /* LED map
435 * R ... Engine 1 (fixed)
436 * G ... Engine 2 (fixed)
437 * B ... Engine 3 (fixed)
438 * W ... Engine 1 or 2 or 3
439 */
440
441 if (sysfs_streq(buf, "RGB")) {
442 mask = LP5562_ENG_FOR_RGB_M;
443 val = LP5562_ENG_SEL_RGB;
444 } else if (sysfs_streq(buf, "W")) {
445 enum lp55xx_engine_index idx = chip->engine_idx;
446
447 mask = LP5562_ENG_FOR_W_M;
448 switch (idx) {
449 case LP55XX_ENGINE_1:
450 val = LP5562_ENG1_FOR_W;
451 break;
452 case LP55XX_ENGINE_2:
453 val = LP5562_ENG2_FOR_W;
454 break;
455 case LP55XX_ENGINE_3:
456 val = LP5562_ENG3_FOR_W;
457 break;
458 default:
459 return -EINVAL;
460 }
461
462 } else {
463 dev_err(dev, "choose RGB or W\n");
464 return -EINVAL;
465 }
466
467 mutex_lock(&chip->lock);
468 lp55xx_update_bits(chip, LP5562_REG_ENG_SEL, mask, val);
469 mutex_unlock(&chip->lock);
470
471 return len;
472}
473
474static DEVICE_ATTR(led_pattern, S_IWUSR, NULL, lp5562_store_pattern);
475static DEVICE_ATTR(engine_mux, S_IWUSR, NULL, lp5562_store_engine_mux);
476
477static struct attribute *lp5562_attributes[] = {
478 &dev_attr_led_pattern.attr,
479 &dev_attr_engine_mux.attr,
480 NULL,
481};
482
483static const struct attribute_group lp5562_group = {
484 .attrs = lp5562_attributes,
485};
486
487/* Chip specific configurations */
488static struct lp55xx_device_config lp5562_cfg = {
489 .max_channel = LP5562_MAX_LEDS,
490 .reset = {
491 .addr = LP5562_REG_RESET,
492 .val = LP5562_RESET,
493 },
494 .enable = {
495 .addr = LP5562_REG_ENABLE,
496 .val = LP5562_ENABLE_DEFAULT,
497 },
498 .post_init_device = lp5562_post_init_device,
499 .set_led_current = lp5562_set_led_current,
500 .brightness_work_fn = lp5562_led_brightness_work,
501 .run_engine = lp5562_run_engine,
502 .firmware_cb = lp5562_firmware_loaded,
503 .dev_attr_group = &lp5562_group,
504};
505
506static int lp5562_probe(struct i2c_client *client,
507 const struct i2c_device_id *id)
508{
509 int ret;
510 struct lp55xx_chip *chip;
511 struct lp55xx_led *led;
512 struct lp55xx_platform_data *pdata = client->dev.platform_data;
513
514 if (!pdata) {
515 dev_err(&client->dev, "no platform data\n");
516 return -EINVAL;
517 }
518
519 chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
520 if (!chip)
521 return -ENOMEM;
522
523 led = devm_kzalloc(&client->dev,
524 sizeof(*led) * pdata->num_channels, GFP_KERNEL);
525 if (!led)
526 return -ENOMEM;
527
528 chip->cl = client;
529 chip->pdata = pdata;
530 chip->cfg = &lp5562_cfg;
531
532 mutex_init(&chip->lock);
533
534 i2c_set_clientdata(client, led);
535
536 ret = lp55xx_init_device(chip);
537 if (ret)
538 goto err_init;
539
540 ret = lp55xx_register_leds(led, chip);
541 if (ret)
542 goto err_register_leds;
543
544 ret = lp55xx_register_sysfs(chip);
545 if (ret) {
546 dev_err(&client->dev, "registering sysfs failed\n");
547 goto err_register_sysfs;
548 }
549
550 return 0;
551
552err_register_sysfs:
553 lp55xx_unregister_leds(led, chip);
554err_register_leds:
555 lp55xx_deinit_device(chip);
556err_init:
557 return ret;
558}
559
560static int lp5562_remove(struct i2c_client *client)
561{
562 struct lp55xx_led *led = i2c_get_clientdata(client);
563 struct lp55xx_chip *chip = led->chip;
564
565 lp5562_stop_engine(chip);
566
567 lp55xx_unregister_sysfs(chip);
568 lp55xx_unregister_leds(led, chip);
569 lp55xx_deinit_device(chip);
570
571 return 0;
572}
573
574static const struct i2c_device_id lp5562_id[] = {
575 { "lp5562", 0 },
576 { }
577};
578MODULE_DEVICE_TABLE(i2c, lp5562_id);
579
580static struct i2c_driver lp5562_driver = {
581 .driver = {
582 .name = "lp5562",
583 },
584 .probe = lp5562_probe,
585 .remove = lp5562_remove,
586 .id_table = lp5562_id,
587};
588
589module_i2c_driver(lp5562_driver);
590
591MODULE_DESCRIPTION("Texas Instruments LP5562 LED Driver");
592MODULE_AUTHOR("Milo Kim");
593MODULE_LICENSE("GPL");
diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c
index d9eb84157423..8a388a4afed7 100644
--- a/drivers/leds/leds-lp55xx-common.c
+++ b/drivers/leds/leds-lp55xx-common.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * LP5521/LP5523/LP55231 Common Driver 2 * LP5521/LP5523/LP55231/LP5562 Common Driver
3 * 3 *
4 * Copyright 2012 Texas Instruments 4 * Copyright 2012 Texas Instruments
5 * 5 *
diff --git a/include/linux/platform_data/leds-lp55xx.h b/include/linux/platform_data/leds-lp55xx.h
index 1509570d5a3f..1f1041e8b4fc 100644
--- a/include/linux/platform_data/leds-lp55xx.h
+++ b/include/linux/platform_data/leds-lp55xx.h
@@ -32,6 +32,13 @@
32#define LP5521_CLK_INT 1 /* Internal clock */ 32#define LP5521_CLK_INT 1 /* Internal clock */
33#define LP5521_CLK_AUTO 2 /* Automatic clock selection */ 33#define LP5521_CLK_AUTO 2 /* Automatic clock selection */
34 34
35/* Bits in LP5562 CONFIG register */
36#define LP5562_PWM_HF LP5521_PWM_HF
37#define LP5562_PWRSAVE_EN LP5521_PWRSAVE_EN
38#define LP5562_CLK_SRC_EXT LP5521_CLK_SRC_EXT
39#define LP5562_CLK_INT LP5521_CLK_INT
40#define LP5562_CLK_AUTO LP5521_CLK_AUTO
41
35struct lp55xx_led_config { 42struct lp55xx_led_config {
36 const char *name; 43 const char *name;
37 u8 chan_nr; 44 u8 chan_nr;
@@ -40,9 +47,9 @@ struct lp55xx_led_config {
40}; 47};
41 48
42struct lp55xx_predef_pattern { 49struct lp55xx_predef_pattern {
43 u8 *r; 50 const u8 *r;
44 u8 *g; 51 const u8 *g;
45 u8 *b; 52 const u8 *b;
46 u8 size_r; 53 u8 size_r;
47 u8 size_g; 54 u8 size_g;
48 u8 size_b; 55 u8 size_b;