aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/leds/leds-is31fl32xx.c
diff options
context:
space:
mode:
authorDavid Rivshin <drivshin@allworx.com>2016-03-07 19:57:15 -0500
committerJacek Anaszewski <j.anaszewski@samsung.com>2016-03-14 04:22:21 -0400
commit9d7cffaf99f5f7756746a328c2854ebe4ce9f442 (patch)
tree40503bc473d633beaca0dfa78b20ed152e05faed /drivers/leds/leds-is31fl32xx.c
parent4ef31e4993ffee122c06da04d3410c88c056286e (diff)
leds: Add driver for the ISSI IS31FL32xx family of LED controllers
The IS31FL32xx family of LED controllers are I2C devices with multiple constant-current channels, each with independent 256-level PWM control. Datasheets: http://www.issi.com/US/product-analog-fxled-driver.shtml This has been tested on the IS31FL3236 and IS31FL3216, on an ARM (TI am335x) platform. The programming paradigm of these devices is similar in the following ways: - All registers are 8 bit - All LED control registers are write-only - Each LED channel has a PWM register (0-255) - PWM register writes are shadowed until an Update register is poked - All have a concept of Software Shutdown, which disables output However, there are some differences in devices: - 3236/3235 have a separate Control register for each LED, (3218/3216 pack the enable bits into fewer registers) - 3236/3235 have a per-channel current divisor setting - 3236/3235 have a Global Control register that can turn off all LEDs - 3216 is unique in a number of ways - OUT9-OUT16 can be configured as GPIOs instead of LED controls - LEDs can be programmed with an 8-frame animation, with programmable delay between frames - LEDs can be modulated by an input audio signal - Max output current can be adjusted from 1/4 to 2x globally - Has a Configuration register instead of a Shutdown register This driver currently only supports the base PWM control function of these devices. The following features of these devices are not implemented, although it should be possible to add them in the future: - All devices are capable of going into a lower-power "software shutdown" mode. - The is31fl3236 and is31fl3235 can reduce the max output current per-channel with a divisor of 1, 2, 3, or 4. - The is31fl3216 can use some LED channels as GPIOs instead. - The is31fl3216 can animate LEDs in hardware. - The is31fl3216 can modulate LEDs according to an audio input. - The is31fl3216 can reduce/increase max output current globally. Signed-off-by: David Rivshin <drivshin@allworx.com> Signed-off-by: Jacek Anaszewski <j.anaszewski@samsung.com>
Diffstat (limited to 'drivers/leds/leds-is31fl32xx.c')
-rw-r--r--drivers/leds/leds-is31fl32xx.c504
1 files changed, 504 insertions, 0 deletions
diff --git a/drivers/leds/leds-is31fl32xx.c b/drivers/leds/leds-is31fl32xx.c
new file mode 100644
index 000000000000..9a6785694886
--- /dev/null
+++ b/drivers/leds/leds-is31fl32xx.c
@@ -0,0 +1,504 @@
1/*
2 * Driver for ISSI IS31FL32xx family of I2C LED controllers
3 *
4 * Copyright 2015 Allworx Corp.
5 *
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 *
11 * Datasheets: http://www.issi.com/US/product-analog-fxled-driver.shtml
12 */
13
14#include <linux/device.h>
15#include <linux/i2c.h>
16#include <linux/kernel.h>
17#include <linux/leds.h>
18#include <linux/module.h>
19#include <linux/of.h>
20#include <linux/of_device.h>
21
22/* Used to indicate a device has no such register */
23#define IS31FL32XX_REG_NONE 0xFF
24
25/* Software Shutdown bit in Shutdown Register */
26#define IS31FL32XX_SHUTDOWN_SSD_ENABLE 0
27#define IS31FL32XX_SHUTDOWN_SSD_DISABLE BIT(0)
28
29/* IS31FL3216 has a number of unique registers */
30#define IS31FL3216_CONFIG_REG 0x00
31#define IS31FL3216_LIGHTING_EFFECT_REG 0x03
32#define IS31FL3216_CHANNEL_CONFIG_REG 0x04
33
34/* Software Shutdown bit in 3216 Config Register */
35#define IS31FL3216_CONFIG_SSD_ENABLE BIT(7)
36#define IS31FL3216_CONFIG_SSD_DISABLE 0
37
38struct is31fl32xx_priv;
39struct is31fl32xx_led_data {
40 struct led_classdev cdev;
41 u8 channel; /* 1-based, max priv->cdef->channels */
42 struct is31fl32xx_priv *priv;
43};
44
45struct is31fl32xx_priv {
46 const struct is31fl32xx_chipdef *cdef;
47 struct i2c_client *client;
48 unsigned int num_leds;
49 struct is31fl32xx_led_data leds[0];
50};
51
52/**
53 * struct is31fl32xx_chipdef - chip-specific attributes
54 * @channels : Number of LED channels
55 * @shutdown_reg : address of Shutdown register (optional)
56 * @pwm_update_reg : address of PWM Update register
57 * @global_control_reg : address of Global Control register (optional)
58 * @reset_reg : address of Reset register (optional)
59 * @pwm_register_base : address of first PWM register
60 * @pwm_registers_reversed: : true if PWM registers count down instead of up
61 * @led_control_register_base : address of first LED control register (optional)
62 * @enable_bits_per_led_control_register: number of LEDs enable bits in each
63 * @reset_func: : pointer to reset function
64 *
65 * For all optional register addresses, the sentinel value %IS31FL32XX_REG_NONE
66 * indicates that this chip has no such register.
67 *
68 * If non-NULL, @reset_func will be called during probing to set all
69 * necessary registers to a known initialization state. This is needed
70 * for chips that do not have a @reset_reg.
71 *
72 * @enable_bits_per_led_control_register must be >=1 if
73 * @led_control_register_base != %IS31FL32XX_REG_NONE.
74 */
75struct is31fl32xx_chipdef {
76 u8 channels;
77 u8 shutdown_reg;
78 u8 pwm_update_reg;
79 u8 global_control_reg;
80 u8 reset_reg;
81 u8 pwm_register_base;
82 bool pwm_registers_reversed;
83 u8 led_control_register_base;
84 u8 enable_bits_per_led_control_register;
85 int (*reset_func)(struct is31fl32xx_priv *priv);
86 int (*sw_shutdown_func)(struct is31fl32xx_priv *priv, bool enable);
87};
88
89static const struct is31fl32xx_chipdef is31fl3236_cdef = {
90 .channels = 36,
91 .shutdown_reg = 0x00,
92 .pwm_update_reg = 0x25,
93 .global_control_reg = 0x4a,
94 .reset_reg = 0x4f,
95 .pwm_register_base = 0x01,
96 .led_control_register_base = 0x26,
97 .enable_bits_per_led_control_register = 1,
98};
99
100static const struct is31fl32xx_chipdef is31fl3235_cdef = {
101 .channels = 28,
102 .shutdown_reg = 0x00,
103 .pwm_update_reg = 0x25,
104 .global_control_reg = 0x4a,
105 .reset_reg = 0x4f,
106 .pwm_register_base = 0x05,
107 .led_control_register_base = 0x2a,
108 .enable_bits_per_led_control_register = 1,
109};
110
111static const struct is31fl32xx_chipdef is31fl3218_cdef = {
112 .channels = 18,
113 .shutdown_reg = 0x00,
114 .pwm_update_reg = 0x16,
115 .global_control_reg = IS31FL32XX_REG_NONE,
116 .reset_reg = 0x17,
117 .pwm_register_base = 0x01,
118 .led_control_register_base = 0x13,
119 .enable_bits_per_led_control_register = 6,
120};
121
122static int is31fl3216_reset(struct is31fl32xx_priv *priv);
123static int is31fl3216_software_shutdown(struct is31fl32xx_priv *priv,
124 bool enable);
125static const struct is31fl32xx_chipdef is31fl3216_cdef = {
126 .channels = 16,
127 .shutdown_reg = IS31FL32XX_REG_NONE,
128 .pwm_update_reg = 0xB0,
129 .global_control_reg = IS31FL32XX_REG_NONE,
130 .reset_reg = IS31FL32XX_REG_NONE,
131 .pwm_register_base = 0x10,
132 .pwm_registers_reversed = true,
133 .led_control_register_base = 0x01,
134 .enable_bits_per_led_control_register = 8,
135 .reset_func = is31fl3216_reset,
136 .sw_shutdown_func = is31fl3216_software_shutdown,
137};
138
139static int is31fl32xx_write(struct is31fl32xx_priv *priv, u8 reg, u8 val)
140{
141 int ret;
142
143 dev_dbg(&priv->client->dev, "writing register 0x%02X=0x%02X", reg, val);
144
145 ret = i2c_smbus_write_byte_data(priv->client, reg, val);
146 if (ret) {
147 dev_err(&priv->client->dev,
148 "register write to 0x%02X failed (error %d)",
149 reg, ret);
150 }
151 return ret;
152}
153
154/*
155 * Custom reset function for IS31FL3216 because it does not have a RESET
156 * register the way that the other IS31FL32xx chips do. We don't bother
157 * writing the GPIO and animation registers, because the registers we
158 * do write ensure those will have no effect.
159 */
160static int is31fl3216_reset(struct is31fl32xx_priv *priv)
161{
162 unsigned int i;
163 int ret;
164
165 ret = is31fl32xx_write(priv, IS31FL3216_CONFIG_REG,
166 IS31FL3216_CONFIG_SSD_ENABLE);
167 if (ret)
168 return ret;
169 for (i = 0; i < priv->cdef->channels; i++) {
170 ret = is31fl32xx_write(priv, priv->cdef->pwm_register_base+i,
171 0x00);
172 if (ret)
173 return ret;
174 }
175 ret = is31fl32xx_write(priv, priv->cdef->pwm_update_reg, 0);
176 if (ret)
177 return ret;
178 ret = is31fl32xx_write(priv, IS31FL3216_LIGHTING_EFFECT_REG, 0x00);
179 if (ret)
180 return ret;
181 ret = is31fl32xx_write(priv, IS31FL3216_CHANNEL_CONFIG_REG, 0x00);
182 if (ret)
183 return ret;
184
185 return 0;
186}
187
188/*
189 * Custom Software-Shutdown function for IS31FL3216 because it does not have
190 * a SHUTDOWN register the way that the other IS31FL32xx chips do.
191 * We don't bother doing a read/modify/write on the CONFIG register because
192 * we only ever use a value of '0' for the other fields in that register.
193 */
194static int is31fl3216_software_shutdown(struct is31fl32xx_priv *priv,
195 bool enable)
196{
197 u8 value = enable ? IS31FL3216_CONFIG_SSD_ENABLE :
198 IS31FL3216_CONFIG_SSD_DISABLE;
199
200 return is31fl32xx_write(priv, IS31FL3216_CONFIG_REG, value);
201}
202
203/*
204 * NOTE: A mutex is not needed in this function because:
205 * - All referenced data is read-only after probe()
206 * - The I2C core has a mutex on to protect the bus
207 * - There are no read/modify/write operations
208 * - Intervening operations between the write of the PWM register
209 * and the Update register are harmless.
210 *
211 * Example:
212 * PWM_REG_1 write 16
213 * UPDATE_REG write 0
214 * PWM_REG_2 write 128
215 * UPDATE_REG write 0
216 * vs:
217 * PWM_REG_1 write 16
218 * PWM_REG_2 write 128
219 * UPDATE_REG write 0
220 * UPDATE_REG write 0
221 * are equivalent. Poking the Update register merely applies all PWM
222 * register writes up to that point.
223 */
224static int is31fl32xx_brightness_set(struct led_classdev *led_cdev,
225 enum led_brightness brightness)
226{
227 const struct is31fl32xx_led_data *led_data =
228 container_of(led_cdev, struct is31fl32xx_led_data, cdev);
229 const struct is31fl32xx_chipdef *cdef = led_data->priv->cdef;
230 u8 pwm_register_offset;
231 int ret;
232
233 dev_dbg(led_cdev->dev, "%s: %d\n", __func__, brightness);
234
235 /* NOTE: led_data->channel is 1-based */
236 if (cdef->pwm_registers_reversed)
237 pwm_register_offset = cdef->channels - led_data->channel;
238 else
239 pwm_register_offset = led_data->channel - 1;
240
241 ret = is31fl32xx_write(led_data->priv,
242 cdef->pwm_register_base + pwm_register_offset,
243 brightness);
244 if (ret)
245 return ret;
246
247 return is31fl32xx_write(led_data->priv, cdef->pwm_update_reg, 0);
248}
249
250static int is31fl32xx_reset_regs(struct is31fl32xx_priv *priv)
251{
252 const struct is31fl32xx_chipdef *cdef = priv->cdef;
253 int ret;
254
255 if (cdef->reset_reg != IS31FL32XX_REG_NONE) {
256 ret = is31fl32xx_write(priv, cdef->reset_reg, 0);
257 if (ret)
258 return ret;
259 }
260
261 if (cdef->reset_func)
262 return cdef->reset_func(priv);
263
264 return 0;
265}
266
267static int is31fl32xx_software_shutdown(struct is31fl32xx_priv *priv,
268 bool enable)
269{
270 const struct is31fl32xx_chipdef *cdef = priv->cdef;
271 int ret;
272
273 if (cdef->shutdown_reg != IS31FL32XX_REG_NONE) {
274 u8 value = enable ? IS31FL32XX_SHUTDOWN_SSD_ENABLE :
275 IS31FL32XX_SHUTDOWN_SSD_DISABLE;
276 ret = is31fl32xx_write(priv, cdef->shutdown_reg, value);
277 if (ret)
278 return ret;
279 }
280
281 if (cdef->sw_shutdown_func)
282 return cdef->sw_shutdown_func(priv, enable);
283
284 return 0;
285}
286
287static int is31fl32xx_init_regs(struct is31fl32xx_priv *priv)
288{
289 const struct is31fl32xx_chipdef *cdef = priv->cdef;
290 int ret;
291
292 ret = is31fl32xx_reset_regs(priv);
293 if (ret)
294 return ret;
295
296 /*
297 * Set enable bit for all channels.
298 * We will control state with PWM registers alone.
299 */
300 if (cdef->led_control_register_base != IS31FL32XX_REG_NONE) {
301 u8 value =
302 GENMASK(cdef->enable_bits_per_led_control_register-1, 0);
303 u8 num_regs = cdef->channels /
304 cdef->enable_bits_per_led_control_register;
305 int i;
306
307 for (i = 0; i < num_regs; i++) {
308 ret = is31fl32xx_write(priv,
309 cdef->led_control_register_base+i,
310 value);
311 if (ret)
312 return ret;
313 }
314 }
315
316 ret = is31fl32xx_software_shutdown(priv, false);
317 if (ret)
318 return ret;
319
320 if (cdef->global_control_reg != IS31FL32XX_REG_NONE) {
321 ret = is31fl32xx_write(priv, cdef->global_control_reg, 0x00);
322 if (ret)
323 return ret;
324 }
325
326 return 0;
327}
328
329static inline size_t sizeof_is31fl32xx_priv(int num_leds)
330{
331 return sizeof(struct is31fl32xx_priv) +
332 (sizeof(struct is31fl32xx_led_data) * num_leds);
333}
334
335static int is31fl32xx_parse_child_dt(const struct device *dev,
336 const struct device_node *child,
337 struct is31fl32xx_led_data *led_data)
338{
339 struct led_classdev *cdev = &led_data->cdev;
340 int ret = 0;
341 u32 reg;
342
343 if (of_property_read_string(child, "label", &cdev->name))
344 cdev->name = child->name;
345
346 ret = of_property_read_u32(child, "reg", &reg);
347 if (ret || reg < 1 || reg > led_data->priv->cdef->channels) {
348 dev_err(dev,
349 "Child node %s does not have a valid reg property\n",
350 child->full_name);
351 return -EINVAL;
352 }
353 led_data->channel = reg;
354
355 of_property_read_string(child, "linux,default-trigger",
356 &cdev->default_trigger);
357
358 cdev->brightness_set_blocking = is31fl32xx_brightness_set;
359
360 return 0;
361}
362
363static struct is31fl32xx_led_data *is31fl32xx_find_led_data(
364 struct is31fl32xx_priv *priv,
365 u8 channel)
366{
367 size_t i;
368
369 for (i = 0; i < priv->num_leds; i++) {
370 if (priv->leds[i].channel == channel)
371 return &priv->leds[i];
372 }
373
374 return NULL;
375}
376
377static int is31fl32xx_parse_dt(struct device *dev,
378 struct is31fl32xx_priv *priv)
379{
380 struct device_node *child;
381 int ret = 0;
382
383 for_each_child_of_node(dev->of_node, child) {
384 struct is31fl32xx_led_data *led_data =
385 &priv->leds[priv->num_leds];
386 const struct is31fl32xx_led_data *other_led_data;
387
388 led_data->priv = priv;
389
390 ret = is31fl32xx_parse_child_dt(dev, child, led_data);
391 if (ret)
392 goto err;
393
394 /* Detect if channel is already in use by another child */
395 other_led_data = is31fl32xx_find_led_data(priv,
396 led_data->channel);
397 if (other_led_data) {
398 dev_err(dev,
399 "%s and %s both attempting to use channel %d\n",
400 led_data->cdev.name,
401 other_led_data->cdev.name,
402 led_data->channel);
403 goto err;
404 }
405
406 ret = devm_led_classdev_register(dev, &led_data->cdev);
407 if (ret) {
408 dev_err(dev, "failed to register PWM led for %s: %d\n",
409 led_data->cdev.name, ret);
410 goto err;
411 }
412
413 priv->num_leds++;
414 }
415
416 return 0;
417
418err:
419 of_node_put(child);
420 return ret;
421}
422
423static const struct of_device_id of_is31fl31xx_match[] = {
424 { .compatible = "issi,is31fl3236", .data = &is31fl3236_cdef, },
425 { .compatible = "issi,is31fl3235", .data = &is31fl3235_cdef, },
426 { .compatible = "issi,is31fl3218", .data = &is31fl3218_cdef, },
427 { .compatible = "issi,is31fl3216", .data = &is31fl3216_cdef, },
428 {},
429};
430
431MODULE_DEVICE_TABLE(of, of_is31fl31xx_match);
432
433static int is31fl32xx_probe(struct i2c_client *client,
434 const struct i2c_device_id *id)
435{
436 const struct is31fl32xx_chipdef *cdef;
437 const struct of_device_id *of_dev_id;
438 struct device *dev = &client->dev;
439 struct is31fl32xx_priv *priv;
440 int count;
441 int ret = 0;
442
443 of_dev_id = of_match_device(of_is31fl31xx_match, dev);
444 if (!of_dev_id)
445 return -EINVAL;
446
447 cdef = of_dev_id->data;
448
449 count = of_get_child_count(dev->of_node);
450 if (!count)
451 return -EINVAL;
452
453 priv = devm_kzalloc(dev, sizeof_is31fl32xx_priv(count),
454 GFP_KERNEL);
455 if (!priv)
456 return -ENOMEM;
457
458 priv->client = client;
459 priv->cdef = cdef;
460 i2c_set_clientdata(client, priv);
461
462 ret = is31fl32xx_init_regs(priv);
463 if (ret)
464 return ret;
465
466 ret = is31fl32xx_parse_dt(dev, priv);
467 if (ret)
468 return ret;
469
470 return 0;
471}
472
473static int is31fl32xx_remove(struct i2c_client *client)
474{
475 struct is31fl32xx_priv *priv = i2c_get_clientdata(client);
476
477 return is31fl32xx_reset_regs(priv);
478}
479
480/*
481 * i2c-core requires that id_table be non-NULL, even though
482 * it is not used for DeviceTree based instantiation.
483 */
484static const struct i2c_device_id is31fl31xx_id[] = {
485 {},
486};
487
488MODULE_DEVICE_TABLE(i2c, is31fl31xx_id);
489
490static struct i2c_driver is31fl32xx_driver = {
491 .driver = {
492 .name = "is31fl32xx",
493 .of_match_table = of_is31fl31xx_match,
494 },
495 .probe = is31fl32xx_probe,
496 .remove = is31fl32xx_remove,
497 .id_table = is31fl31xx_id,
498};
499
500module_i2c_driver(is31fl32xx_driver);
501
502MODULE_AUTHOR("David Rivshin <drivshin@allworx.com>");
503MODULE_DESCRIPTION("ISSI IS31FL32xx LED driver");
504MODULE_LICENSE("GPL v2");