aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorH. Nikolaus Schaller <hns@goldelico.com>2016-07-19 07:47:30 -0400
committerJacek Anaszewski <j.anaszewski@samsung.com>2016-08-15 08:02:31 -0400
commit8c40b7d09ec17c7c01a6db2e16a52dca57b68e36 (patch)
tree5c52b1fe778c9672ba7747a6d3ac31b454f127ed
parent694d0d0bb2030d2e36df73e2d23d5770511dbc8d (diff)
leds: is31fl319x: 1/3/6/9-channel light effect led driver
This is a driver for the Integrated Silicon Solution Inc. LED driver chips series IS31FL319x. They can drive 1, 3, 6 or up to 9 LEDs. Each LED is individually controllable in brightness (through pwm) in 256 steps so that RGB LEDs can show any of ca. 16 Mio colors. The maximum current of the LEDs can be programmed and limited to 5 .. 40mA through a device tree property. The chip is connected through I2C and can have one of 4 addresses in the range 0x64 .. 0x67 depending on how the AD pin is connected. The address is defined by the reg property as usual. The chip also has a shutdown input which could be connected to a GPIO, but this driver uses software shutdown if all LEDs are inactivated. The chip also has breathing and audio features which are not fully supported by this driver. Tested-on: OMAP5 based Pyra handheld prototype. Signed-off-by: H. Nikolaus Schaller <hns@goldelico.com> Signed-off-by: Andrey Utkin <andrey_utkin@fastmail.com> Signed-off-by: Jacek Anaszewski <j.anaszewski@samsung.com>
-rw-r--r--drivers/leds/Kconfig12
-rw-r--r--drivers/leds/Makefile1
-rw-r--r--drivers/leds/leds-is31fl319x.c450
3 files changed, 463 insertions, 0 deletions
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 9dcc9b13d495..6df9a2f098f5 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -584,6 +584,18 @@ config LEDS_SEAD3
584 This driver can also be built as a module. If so the module 584 This driver can also be built as a module. If so the module
585 will be called leds-sead3. 585 will be called leds-sead3.
586 586
587config LEDS_IS31FL319X
588 tristate "LED Support for ISSI IS31FL319x I2C LED controller family"
589 depends on LEDS_CLASS && I2C && OF
590 select REGMAP_I2C
591 help
592 This option enables support for LEDs connected to ISSI IS31FL319x
593 fancy LED driver chips accessed via the I2C bus.
594 Driver supports individual PWM brightness control for each channel.
595
596 This driver can also be built as a module. If so the module will be
597 called leds-is31fl319x.
598
587config LEDS_IS31FL32XX 599config LEDS_IS31FL32XX
588 tristate "LED support for ISSI IS31FL32XX I2C LED controller family" 600 tristate "LED support for ISSI IS31FL32XX I2C LED controller family"
589 depends on LEDS_CLASS && I2C && OF 601 depends on LEDS_CLASS && I2C && OF
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index 0684c865a1c0..e7a0e4edadd4 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -67,6 +67,7 @@ obj-$(CONFIG_LEDS_MENF21BMC) += leds-menf21bmc.o
67obj-$(CONFIG_LEDS_KTD2692) += leds-ktd2692.o 67obj-$(CONFIG_LEDS_KTD2692) += leds-ktd2692.o
68obj-$(CONFIG_LEDS_POWERNV) += leds-powernv.o 68obj-$(CONFIG_LEDS_POWERNV) += leds-powernv.o
69obj-$(CONFIG_LEDS_SEAD3) += leds-sead3.o 69obj-$(CONFIG_LEDS_SEAD3) += leds-sead3.o
70obj-$(CONFIG_LEDS_IS31FL319X) += leds-is31fl319x.o
70obj-$(CONFIG_LEDS_IS31FL32XX) += leds-is31fl32xx.o 71obj-$(CONFIG_LEDS_IS31FL32XX) += leds-is31fl32xx.o
71 72
72# LED SPI Drivers 73# LED SPI Drivers
diff --git a/drivers/leds/leds-is31fl319x.c b/drivers/leds/leds-is31fl319x.c
new file mode 100644
index 000000000000..f123309597e4
--- /dev/null
+++ b/drivers/leds/leds-is31fl319x.c
@@ -0,0 +1,450 @@
1/*
2 * Copyright 2015-16 Golden Delicious Computers
3 *
4 * Author: Nikolaus Schaller <hns@goldelico.com>
5 *
6 * This file is subject to the terms and conditions of version 2 of
7 * the GNU General Public License. See the file COPYING in the main
8 * directory of this archive for more details.
9 *
10 * LED driver for the IS31FL319{0,1,3,6,9} to drive 1, 3, 6 or 9 light
11 * effect LEDs.
12 *
13 */
14
15#include <linux/err.h>
16#include <linux/i2c.h>
17#include <linux/leds.h>
18#include <linux/module.h>
19#include <linux/of.h>
20#include <linux/of_device.h>
21#include <linux/regmap.h>
22#include <linux/slab.h>
23
24/* register numbers */
25#define IS31FL319X_SHUTDOWN 0x00
26#define IS31FL319X_CTRL1 0x01
27#define IS31FL319X_CTRL2 0x02
28#define IS31FL319X_CONFIG1 0x03
29#define IS31FL319X_CONFIG2 0x04
30#define IS31FL319X_RAMP_MODE 0x05
31#define IS31FL319X_BREATH_MASK 0x06
32#define IS31FL319X_PWM(channel) (0x07 + channel)
33#define IS31FL319X_DATA_UPDATE 0x10
34#define IS31FL319X_T0(channel) (0x11 + channel)
35#define IS31FL319X_T123_1 0x1a
36#define IS31FL319X_T123_2 0x1b
37#define IS31FL319X_T123_3 0x1c
38#define IS31FL319X_T4(channel) (0x1d + channel)
39#define IS31FL319X_TIME_UPDATE 0x26
40#define IS31FL319X_RESET 0xff
41
42#define IS31FL319X_REG_CNT (IS31FL319X_RESET + 1)
43
44#define IS31FL319X_MAX_LEDS 9
45
46/* CS (Current Setting) in CONFIG2 register */
47#define IS31FL319X_CONFIG2_CS_SHIFT 4
48#define IS31FL319X_CONFIG2_CS_MASK 0x7
49#define IS31FL319X_CONFIG2_CS_STEP_REF 12
50
51#define IS31FL319X_CURRENT_MIN ((u32)5000)
52#define IS31FL319X_CURRENT_MAX ((u32)40000)
53#define IS31FL319X_CURRENT_STEP ((u32)5000)
54#define IS31FL319X_CURRENT_DEFAULT ((u32)20000)
55
56/* Audio gain in CONFIG2 register */
57#define IS31FL319X_AUDIO_GAIN_DB_MAX ((u32)21)
58#define IS31FL319X_AUDIO_GAIN_DB_STEP ((u32)3)
59
60/*
61 * regmap is used as a cache of chip's register space,
62 * to avoid reading back brightness values from chip,
63 * which is known to hang.
64 */
65struct is31fl319x_chip {
66 const struct is31fl319x_chipdef *cdef;
67 struct i2c_client *client;
68 struct regmap *regmap;
69 struct mutex lock;
70 u32 audio_gain_db;
71
72 struct is31fl319x_led {
73 struct is31fl319x_chip *chip;
74 struct led_classdev cdev;
75 u32 max_microamp;
76 bool configured;
77 } leds[IS31FL319X_MAX_LEDS];
78};
79
80struct is31fl319x_chipdef {
81 int num_leds;
82};
83
84static const struct is31fl319x_chipdef is31fl3190_cdef = {
85 .num_leds = 1,
86};
87
88static const struct is31fl319x_chipdef is31fl3193_cdef = {
89 .num_leds = 3,
90};
91
92static const struct is31fl319x_chipdef is31fl3196_cdef = {
93 .num_leds = 6,
94};
95
96static const struct is31fl319x_chipdef is31fl3199_cdef = {
97 .num_leds = 9,
98};
99
100static const struct of_device_id of_is31fl319x_match[] = {
101 { .compatible = "issi,is31fl3190", .data = &is31fl3190_cdef, },
102 { .compatible = "issi,is31fl3191", .data = &is31fl3190_cdef, },
103 { .compatible = "issi,is31fl3193", .data = &is31fl3193_cdef, },
104 { .compatible = "issi,is31fl3196", .data = &is31fl3196_cdef, },
105 { .compatible = "issi,is31fl3199", .data = &is31fl3199_cdef, },
106 { .compatible = "si-en,sn3199", .data = &is31fl3199_cdef, },
107 { }
108};
109MODULE_DEVICE_TABLE(of, of_is31fl319x_match);
110
111static int is31fl319x_brightness_set(struct led_classdev *cdev,
112 enum led_brightness brightness)
113{
114 struct is31fl319x_led *led = container_of(cdev, struct is31fl319x_led,
115 cdev);
116 struct is31fl319x_chip *is31 = led->chip;
117 int chan = led - is31->leds;
118 int ret;
119 int i;
120 u8 ctrl1 = 0, ctrl2 = 0;
121
122 dev_dbg(&is31->client->dev, "%s %d: %d\n", __func__, chan, brightness);
123
124 mutex_lock(&is31->lock);
125
126 /* update PWM register */
127 ret = regmap_write(is31->regmap, IS31FL319X_PWM(chan), brightness);
128 if (ret < 0)
129 goto out;
130
131 /* read current brightness of all PWM channels */
132 for (i = 0; i < is31->cdef->num_leds; i++) {
133 unsigned int pwm_value;
134 bool on;
135
136 /*
137 * since neither cdev nor the chip can provide
138 * the current setting, we read from the regmap cache
139 */
140
141 ret = regmap_read(is31->regmap, IS31FL319X_PWM(i), &pwm_value);
142 dev_dbg(&is31->client->dev, "%s read %d: ret=%d: %d\n",
143 __func__, i, ret, pwm_value);
144 on = ret >= 0 && pwm_value > LED_OFF;
145
146 if (i < 3)
147 ctrl1 |= on << i; /* 0..2 => bit 0..2 */
148 else if (i < 6)
149 ctrl1 |= on << (i + 1); /* 3..5 => bit 4..6 */
150 else
151 ctrl2 |= on << (i - 6); /* 6..8 => bit 0..2 */
152 }
153
154 if (ctrl1 > 0 || ctrl2 > 0) {
155 dev_dbg(&is31->client->dev, "power up %02x %02x\n",
156 ctrl1, ctrl2);
157 regmap_write(is31->regmap, IS31FL319X_CTRL1, ctrl1);
158 regmap_write(is31->regmap, IS31FL319X_CTRL2, ctrl2);
159 /* update PWMs */
160 regmap_write(is31->regmap, IS31FL319X_DATA_UPDATE, 0x00);
161 /* enable chip from shut down */
162 ret = regmap_write(is31->regmap, IS31FL319X_SHUTDOWN, 0x01);
163 } else {
164 dev_dbg(&is31->client->dev, "power down\n");
165 /* shut down (no need to clear CTRL1/2) */
166 ret = regmap_write(is31->regmap, IS31FL319X_SHUTDOWN, 0x00);
167 }
168
169out:
170 mutex_unlock(&is31->lock);
171
172 return ret;
173}
174
175static int is31fl319x_parse_child_dt(const struct device *dev,
176 const struct device_node *child,
177 struct is31fl319x_led *led)
178{
179 struct led_classdev *cdev = &led->cdev;
180 int ret;
181
182 if (of_property_read_string(child, "label", &cdev->name))
183 cdev->name = child->name;
184
185 ret = of_property_read_string(child, "linux,default-trigger",
186 &cdev->default_trigger);
187 if (ret < 0 && ret != -EINVAL) /* is optional */
188 return ret;
189
190 led->max_microamp = IS31FL319X_CURRENT_DEFAULT;
191 ret = of_property_read_u32(child, "led-max-microamp",
192 &led->max_microamp);
193 if (!ret) {
194 if (led->max_microamp < IS31FL319X_CURRENT_MIN)
195 return -EINVAL; /* not supported */
196 led->max_microamp = min(led->max_microamp,
197 IS31FL319X_CURRENT_MAX);
198 }
199
200 return 0;
201}
202
203static int is31fl319x_parse_dt(struct device *dev,
204 struct is31fl319x_chip *is31)
205{
206 struct device_node *np = dev->of_node, *child;
207 const struct of_device_id *of_dev_id;
208 int count;
209 int ret;
210
211 if (!np)
212 return -ENODEV;
213
214 of_dev_id = of_match_device(of_is31fl319x_match, dev);
215 if (!of_dev_id) {
216 dev_err(dev, "Failed to match device with supported chips\n");
217 return -EINVAL;
218 }
219
220 is31->cdef = of_dev_id->data;
221
222 count = of_get_child_count(np);
223
224 dev_dbg(dev, "probe %s with %d leds defined in DT\n",
225 of_dev_id->compatible, count);
226
227 if (!count || count > is31->cdef->num_leds) {
228 dev_err(dev, "Number of leds defined must be between 1 and %u\n",
229 is31->cdef->num_leds);
230 return -ENODEV;
231 }
232
233 for_each_child_of_node(np, child) {
234 struct is31fl319x_led *led;
235 u32 reg;
236
237 ret = of_property_read_u32(child, "reg", &reg);
238 if (ret) {
239 dev_err(dev, "Failed to read led 'reg' property\n");
240 goto put_child_node;
241 }
242
243 if (reg < 1 || reg > is31->cdef->num_leds) {
244 dev_err(dev, "invalid led reg %u\n", reg);
245 ret = -EINVAL;
246 goto put_child_node;
247 }
248
249 led = &is31->leds[reg - 1];
250
251 if (led->configured) {
252 dev_err(dev, "led %u is already configured\n", reg);
253 ret = -EINVAL;
254 goto put_child_node;
255 }
256
257 ret = is31fl319x_parse_child_dt(dev, child, led);
258 if (ret) {
259 dev_err(dev, "led %u DT parsing failed\n", reg);
260 goto put_child_node;
261 }
262
263 led->configured = true;
264 }
265
266 is31->audio_gain_db = 0;
267 ret = of_property_read_u32(np, "audio-gain-db", &is31->audio_gain_db);
268 if (!ret)
269 is31->audio_gain_db = min(is31->audio_gain_db,
270 IS31FL319X_AUDIO_GAIN_DB_MAX);
271
272 return 0;
273
274put_child_node:
275 of_node_put(child);
276 return ret;
277}
278
279static bool is31fl319x_readable_reg(struct device *dev, unsigned int reg)
280{ /* we have no readable registers */
281 return false;
282}
283
284static bool is31fl319x_volatile_reg(struct device *dev, unsigned int reg)
285{ /* volatile registers are not cached */
286 switch (reg) {
287 case IS31FL319X_DATA_UPDATE:
288 case IS31FL319X_TIME_UPDATE:
289 case IS31FL319X_RESET:
290 return true; /* always write-through */
291 default:
292 return false;
293 }
294}
295
296static const struct reg_default is31fl319x_reg_defaults[] = {
297 { IS31FL319X_CONFIG1, 0x00},
298 { IS31FL319X_CONFIG2, 0x00},
299 { IS31FL319X_PWM(0), 0x00},
300 { IS31FL319X_PWM(1), 0x00},
301 { IS31FL319X_PWM(2), 0x00},
302 { IS31FL319X_PWM(3), 0x00},
303 { IS31FL319X_PWM(4), 0x00},
304 { IS31FL319X_PWM(5), 0x00},
305 { IS31FL319X_PWM(6), 0x00},
306 { IS31FL319X_PWM(7), 0x00},
307 { IS31FL319X_PWM(8), 0x00},
308};
309
310static struct regmap_config regmap_config = {
311 .reg_bits = 8,
312 .val_bits = 8,
313 .max_register = IS31FL319X_REG_CNT,
314 .cache_type = REGCACHE_FLAT,
315 .readable_reg = is31fl319x_readable_reg,
316 .volatile_reg = is31fl319x_volatile_reg,
317 .reg_defaults = is31fl319x_reg_defaults,
318 .num_reg_defaults = ARRAY_SIZE(is31fl319x_reg_defaults),
319};
320
321static inline int is31fl319x_microamp_to_cs(struct device *dev, u32 microamp)
322{ /* round down to nearest supported value (range check done by caller) */
323 u32 step = microamp / IS31FL319X_CURRENT_STEP;
324
325 return ((IS31FL319X_CONFIG2_CS_STEP_REF - step) &
326 IS31FL319X_CONFIG2_CS_MASK) <<
327 IS31FL319X_CONFIG2_CS_SHIFT; /* CS encoding */
328}
329
330static inline int is31fl319x_db_to_gain(u32 dezibel)
331{ /* round down to nearest supported value (range check done by caller) */
332 return dezibel / IS31FL319X_AUDIO_GAIN_DB_STEP;
333}
334
335static int is31fl319x_probe(struct i2c_client *client,
336 const struct i2c_device_id *id)
337{
338 struct is31fl319x_chip *is31;
339 struct device *dev = &client->dev;
340 struct i2c_adapter *adapter = to_i2c_adapter(dev->parent);
341 int err;
342 int i = 0;
343 u32 aggregated_led_microamp = IS31FL319X_CURRENT_MAX;
344
345 if (!i2c_check_functionality(adapter, I2C_FUNC_I2C))
346 return -EIO;
347
348 is31 = devm_kzalloc(&client->dev, sizeof(*is31), GFP_KERNEL);
349 if (!is31)
350 return -ENOMEM;
351
352 mutex_init(&is31->lock);
353
354 err = is31fl319x_parse_dt(&client->dev, is31);
355 if (err)
356 goto free_mutex;
357
358 is31->client = client;
359 is31->regmap = devm_regmap_init_i2c(client, &regmap_config);
360 if (IS_ERR(is31->regmap)) {
361 dev_err(&client->dev, "failed to allocate register map\n");
362 err = PTR_ERR(is31->regmap);
363 goto free_mutex;
364 }
365
366 i2c_set_clientdata(client, is31);
367
368 /* check for write-reply from chip (we can't read any registers) */
369 err = regmap_write(is31->regmap, IS31FL319X_RESET, 0x00);
370 if (err < 0) {
371 dev_err(&client->dev, "no response from chip write: err = %d\n",
372 err);
373 err = -EIO; /* does not answer */
374 goto free_mutex;
375 }
376
377 /*
378 * Kernel conventions require per-LED led-max-microamp property.
379 * But the chip does not allow to limit individual LEDs.
380 * So we take minimum from all subnodes for safety of hardware.
381 */
382 for (i = 0; i < is31->cdef->num_leds; i++)
383 if (is31->leds[i].configured &&
384 is31->leds[i].max_microamp < aggregated_led_microamp)
385 aggregated_led_microamp = is31->leds[i].max_microamp;
386
387 regmap_write(is31->regmap, IS31FL319X_CONFIG2,
388 is31fl319x_microamp_to_cs(dev, aggregated_led_microamp) |
389 is31fl319x_db_to_gain(is31->audio_gain_db));
390
391 for (i = 0; i < is31->cdef->num_leds; i++) {
392 struct is31fl319x_led *led = &is31->leds[i];
393
394 if (!led->configured)
395 continue;
396
397 led->chip = is31;
398 led->cdev.brightness_set_blocking = is31fl319x_brightness_set;
399
400 err = devm_led_classdev_register(&client->dev, &led->cdev);
401 if (err < 0)
402 goto free_mutex;
403 }
404
405 return 0;
406
407free_mutex:
408 mutex_destroy(&is31->lock);
409 return err;
410}
411
412static int is31fl319x_remove(struct i2c_client *client)
413{
414 struct is31fl319x_chip *is31 = i2c_get_clientdata(client);
415
416 mutex_destroy(&is31->lock);
417 return 0;
418}
419
420/*
421 * i2c-core (and modalias) requires that id_table be properly filled,
422 * even though it is not used for DeviceTree based instantiation.
423 */
424static const struct i2c_device_id is31fl319x_id[] = {
425 { "is31fl3190" },
426 { "is31fl3191" },
427 { "is31fl3193" },
428 { "is31fl3196" },
429 { "is31fl3199" },
430 { "sn3199" },
431 {},
432};
433MODULE_DEVICE_TABLE(i2c, is31fl319x_id);
434
435static struct i2c_driver is31fl319x_driver = {
436 .driver = {
437 .name = "leds-is31fl319x",
438 .of_match_table = of_match_ptr(of_is31fl319x_match),
439 },
440 .probe = is31fl319x_probe,
441 .remove = is31fl319x_remove,
442 .id_table = is31fl319x_id,
443};
444
445module_i2c_driver(is31fl319x_driver);
446
447MODULE_AUTHOR("H. Nikolaus Schaller <hns@goldelico.com>");
448MODULE_AUTHOR("Andrey Utkin <andrey_utkin@fastmail.com>");
449MODULE_DESCRIPTION("IS31FL319X LED driver");
450MODULE_LICENSE("GPL v2");