]> Git Repo - linux.git/blame - drivers/leds/leds-lp8501.c
leds: leds-lp55xx: Generalize load_engine_and_select_page function
[linux.git] / drivers / leds / leds-lp8501.c
CommitLineData
d2912cb1 1// SPDX-License-Identifier: GPL-2.0-only
33b3a561
KM
2/*
3 * TI LP8501 9 channel LED Driver
4 *
5 * Copyright (C) 2013 Texas Instruments
6 *
7 * Author: Milo(Woogyom) Kim <[email protected]>
33b3a561
KM
8 */
9
10#include <linux/delay.h>
11#include <linux/firmware.h>
12#include <linux/i2c.h>
13#include <linux/init.h>
14#include <linux/leds.h>
15#include <linux/module.h>
16#include <linux/mutex.h>
c68f46dd 17#include <linux/of.h>
33b3a561
KM
18#include <linux/platform_data/leds-lp55xx.h>
19#include <linux/slab.h>
20
21#include "leds-lp55xx-common.h"
22
23#define LP8501_PROGRAM_LENGTH 32
409a9dc5 24#define LP8501_PAGES_PER_ENGINE 1
33b3a561
KM
25#define LP8501_MAX_LEDS 9
26
27/* Registers */
28#define LP8501_REG_ENABLE 0x00
29#define LP8501_ENABLE BIT(6)
30#define LP8501_EXEC_M 0x3F
31#define LP8501_EXEC_ENG1_M 0x30
32#define LP8501_EXEC_ENG2_M 0x0C
33#define LP8501_EXEC_ENG3_M 0x03
34#define LP8501_RUN_ENG1 0x20
35#define LP8501_RUN_ENG2 0x08
36#define LP8501_RUN_ENG3 0x02
37
38#define LP8501_REG_OP_MODE 0x01
39#define LP8501_MODE_ENG1_M 0x30
40#define LP8501_MODE_ENG2_M 0x0C
41#define LP8501_MODE_ENG3_M 0x03
42#define LP8501_LOAD_ENG1 0x10
43#define LP8501_LOAD_ENG2 0x04
44#define LP8501_LOAD_ENG3 0x01
45
46#define LP8501_REG_PWR_CONFIG 0x05
47#define LP8501_PWR_CONFIG_M 0x03
48
49#define LP8501_REG_LED_PWM_BASE 0x16
50
51#define LP8501_REG_LED_CURRENT_BASE 0x26
52
53#define LP8501_REG_CONFIG 0x36
54#define LP8501_PWM_PSAVE BIT(7)
55#define LP8501_AUTO_INC BIT(6)
56#define LP8501_PWR_SAVE BIT(5)
54a7bef5
MZ
57#define LP8501_CP_MODE_MASK 0x18
58#define LP8501_CP_MODE_SHIFT 3
33b3a561 59#define LP8501_INT_CLK BIT(0)
54a7bef5 60#define LP8501_DEFAULT_CFG (LP8501_PWM_PSAVE | LP8501_AUTO_INC | LP8501_PWR_SAVE)
33b3a561 61
a9b202b9
CM
62#define LP8501_REG_STATUS 0x3A
63#define LP8501_ENGINE_BUSY BIT(4)
64
33b3a561
KM
65#define LP8501_REG_RESET 0x3D
66#define LP8501_RESET 0xFF
67
68#define LP8501_REG_PROG_PAGE_SEL 0x4F
69#define LP8501_PAGE_ENG1 0
70#define LP8501_PAGE_ENG2 1
71#define LP8501_PAGE_ENG3 2
72
73#define LP8501_REG_PROG_MEM 0x50
74
75#define LP8501_ENG1_IS_LOADING(mode) \
76 ((mode & LP8501_MODE_ENG1_M) == LP8501_LOAD_ENG1)
77#define LP8501_ENG2_IS_LOADING(mode) \
78 ((mode & LP8501_MODE_ENG2_M) == LP8501_LOAD_ENG2)
79#define LP8501_ENG3_IS_LOADING(mode) \
80 ((mode & LP8501_MODE_ENG3_M) == LP8501_LOAD_ENG3)
81
82static inline void lp8501_wait_opmode_done(void)
83{
84 usleep_range(1000, 2000);
85}
86
87static void lp8501_set_led_current(struct lp55xx_led *led, u8 led_current)
88{
89 led->led_current = led_current;
90 lp55xx_write(led->chip, LP8501_REG_LED_CURRENT_BASE + led->chan_nr,
91 led_current);
92}
93
94static int lp8501_post_init_device(struct lp55xx_chip *chip)
95{
96 int ret;
97 u8 val = LP8501_DEFAULT_CFG;
98
99 ret = lp55xx_write(chip, LP8501_REG_ENABLE, LP8501_ENABLE);
100 if (ret)
101 return ret;
102
103 /* Chip startup time is 500 us, 1 - 2 ms gives some margin */
104 usleep_range(1000, 2000);
105
106 if (chip->pdata->clock_mode != LP55XX_CLOCK_EXT)
107 val |= LP8501_INT_CLK;
108
54a7bef5
MZ
109 val |= (chip->pdata->charge_pump_mode << LP8501_CP_MODE_SHIFT) & LP8501_CP_MODE_MASK;
110
33b3a561
KM
111 ret = lp55xx_write(chip, LP8501_REG_CONFIG, val);
112 if (ret)
113 return ret;
114
115 /* Power selection for each output */
116 return lp55xx_update_bits(chip, LP8501_REG_PWR_CONFIG,
117 LP8501_PWR_CONFIG_M, chip->pdata->pwr_sel);
118}
119
33b3a561
KM
120static void lp8501_turn_off_channels(struct lp55xx_chip *chip)
121{
122 int i;
123
124 for (i = 0; i < LP8501_MAX_LEDS; i++)
125 lp55xx_write(chip, LP8501_REG_LED_PWM_BASE + i, 0);
126}
127
128static void lp8501_run_engine(struct lp55xx_chip *chip, bool start)
129{
130 int ret;
131 u8 mode;
132 u8 exec;
133
134 /* stop engine */
135 if (!start) {
a9b202b9 136 lp55xx_stop_all_engine(chip);
33b3a561
KM
137 lp8501_turn_off_channels(chip);
138 return;
139 }
140
141 /*
142 * To run the engine,
143 * operation mode and enable register should updated at the same time
144 */
145
146 ret = lp55xx_read(chip, LP8501_REG_OP_MODE, &mode);
147 if (ret)
148 return;
149
150 ret = lp55xx_read(chip, LP8501_REG_ENABLE, &exec);
151 if (ret)
152 return;
153
154 /* change operation mode to RUN only when each engine is loading */
155 if (LP8501_ENG1_IS_LOADING(mode)) {
156 mode = (mode & ~LP8501_MODE_ENG1_M) | LP8501_RUN_ENG1;
157 exec = (exec & ~LP8501_EXEC_ENG1_M) | LP8501_RUN_ENG1;
158 }
159
160 if (LP8501_ENG2_IS_LOADING(mode)) {
161 mode = (mode & ~LP8501_MODE_ENG2_M) | LP8501_RUN_ENG2;
162 exec = (exec & ~LP8501_EXEC_ENG2_M) | LP8501_RUN_ENG2;
163 }
164
165 if (LP8501_ENG3_IS_LOADING(mode)) {
166 mode = (mode & ~LP8501_MODE_ENG3_M) | LP8501_RUN_ENG3;
167 exec = (exec & ~LP8501_EXEC_ENG3_M) | LP8501_RUN_ENG3;
168 }
169
170 lp55xx_write(chip, LP8501_REG_OP_MODE, mode);
171 lp8501_wait_opmode_done();
172
173 lp55xx_update_bits(chip, LP8501_REG_ENABLE, LP8501_EXEC_M, exec);
174}
175
176static int lp8501_update_program_memory(struct lp55xx_chip *chip,
177 const u8 *data, size_t size)
178{
179 u8 pattern[LP8501_PROGRAM_LENGTH] = {0};
180 unsigned cmd;
181 char c[3];
182 int update_size;
183 int nrchars;
184 int offset = 0;
185 int ret;
186 int i;
187
188 /* clear program memory before updating */
189 for (i = 0; i < LP8501_PROGRAM_LENGTH; i++)
190 lp55xx_write(chip, LP8501_REG_PROG_MEM + i, 0);
191
192 i = 0;
193 while ((offset < size - 1) && (i < LP8501_PROGRAM_LENGTH)) {
194 /* separate sscanfs because length is working only for %s */
195 ret = sscanf(data + offset, "%2s%n ", c, &nrchars);
196 if (ret != 1)
197 goto err;
198
199 ret = sscanf(c, "%2x", &cmd);
200 if (ret != 1)
201 goto err;
202
203 pattern[i] = (u8)cmd;
204 offset += nrchars;
205 i++;
206 }
207
208 /* Each instruction is 16bit long. Check that length is even */
209 if (i % 2)
210 goto err;
211
212 update_size = i;
213 for (i = 0; i < update_size; i++)
214 lp55xx_write(chip, LP8501_REG_PROG_MEM + i, pattern[i]);
215
216 return 0;
217
218err:
219 dev_err(&chip->cl->dev, "wrong pattern format\n");
220 return -EINVAL;
221}
222
223static void lp8501_firmware_loaded(struct lp55xx_chip *chip)
224{
225 const struct firmware *fw = chip->fw;
226
227 if (fw->size > LP8501_PROGRAM_LENGTH) {
228 dev_err(&chip->cl->dev, "firmware data size overflow: %zu\n",
229 fw->size);
230 return;
231 }
232
233 /*
bfb18d82 234 * Program memory sequence
33b3a561
KM
235 * 1) set engine mode to "LOAD"
236 * 2) write firmware data into program memory
237 */
238
409a9dc5 239 lp55xx_load_engine(chip);
33b3a561
KM
240 lp8501_update_program_memory(chip, fw->data, fw->size);
241}
242
95b2af63 243static int lp8501_led_brightness(struct lp55xx_led *led)
33b3a561 244{
33b3a561 245 struct lp55xx_chip *chip = led->chip;
95b2af63 246 int ret;
33b3a561
KM
247
248 mutex_lock(&chip->lock);
95b2af63 249 ret = lp55xx_write(chip, LP8501_REG_LED_PWM_BASE + led->chan_nr,
33b3a561
KM
250 led->brightness);
251 mutex_unlock(&chip->lock);
95b2af63
AL
252
253 return ret;
33b3a561
KM
254}
255
256/* Chip specific configurations */
257static struct lp55xx_device_config lp8501_cfg = {
a9b202b9
CM
258 .reg_op_mode = {
259 .addr = LP8501_REG_OP_MODE,
260 },
261 .engine_busy = {
262 .addr = LP8501_REG_STATUS,
4d310b96 263 .mask = LP8501_ENGINE_BUSY,
a9b202b9 264 },
33b3a561
KM
265 .reset = {
266 .addr = LP8501_REG_RESET,
267 .val = LP8501_RESET,
268 },
269 .enable = {
270 .addr = LP8501_REG_ENABLE,
271 .val = LP8501_ENABLE,
272 },
409a9dc5 273 .pages_per_engine = LP8501_PAGES_PER_ENGINE,
33b3a561
KM
274 .max_channel = LP8501_MAX_LEDS,
275 .post_init_device = lp8501_post_init_device,
95b2af63 276 .brightness_fn = lp8501_led_brightness,
33b3a561
KM
277 .set_led_current = lp8501_set_led_current,
278 .firmware_cb = lp8501_firmware_loaded,
279 .run_engine = lp8501_run_engine,
280};
281
33b3a561 282static const struct i2c_device_id lp8501_id[] = {
db30c289 283 { "lp8501", .driver_data = (kernel_ulong_t)&lp8501_cfg, },
33b3a561
KM
284 { }
285};
286MODULE_DEVICE_TABLE(i2c, lp8501_id);
287
33b3a561 288static const struct of_device_id of_lp8501_leds_match[] = {
db30c289 289 { .compatible = "ti,lp8501", .data = &lp8501_cfg, },
33b3a561
KM
290 {},
291};
292
293MODULE_DEVICE_TABLE(of, of_lp8501_leds_match);
33b3a561
KM
294
295static struct i2c_driver lp8501_driver = {
296 .driver = {
297 .name = "lp8501",
3d590af8 298 .of_match_table = of_lp8501_leds_match,
33b3a561 299 },
db30c289
CM
300 .probe = lp55xx_probe,
301 .remove = lp55xx_remove,
33b3a561
KM
302 .id_table = lp8501_id,
303};
304
305module_i2c_driver(lp8501_driver);
306
336af37e 307MODULE_DESCRIPTION("Texas Instruments LP8501 LED driver");
33b3a561
KM
308MODULE_AUTHOR("Milo Kim");
309MODULE_LICENSE("GPL");
This page took 0.800583 seconds and 4 git commands to generate.