aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/leds/leds-blinkm.txt80
-rw-r--r--MAINTAINERS5
-rw-r--r--drivers/leds/Kconfig8
-rw-r--r--drivers/leds/Makefile1
-rw-r--r--drivers/leds/leds-blinkm.c812
5 files changed, 906 insertions, 0 deletions
diff --git a/Documentation/leds/leds-blinkm.txt b/Documentation/leds/leds-blinkm.txt
new file mode 100644
index 000000000000..9dd92f4cf4e1
--- /dev/null
+++ b/Documentation/leds/leds-blinkm.txt
@@ -0,0 +1,80 @@
1The leds-blinkm driver supports the devices of the BlinkM family.
2
3They are RGB-LED modules driven by a (AT)tiny microcontroller and
4communicate through I2C. The default address of these modules is
50x09 but this can be changed through a command. By this you could
6dasy-chain up to 127 BlinkMs on an I2C bus.
7
8The device accepts RGB and HSB color values through separate commands.
9Also you can store blinking sequences as "scripts" in
10the controller and run them. Also fading is an option.
11
12The interface this driver provides is 2-fold:
13
14a) LED class interface for use with triggers
15############################################
16
17The registration follows the scheme:
18blinkm-<i2c-bus-nr>-<i2c-device-nr>-<color>
19
20$ ls -h /sys/class/leds/blinkm-6-*
21/sys/class/leds/blinkm-6-9-blue:
22brightness device max_brightness power subsystem trigger uevent
23
24/sys/class/leds/blinkm-6-9-green:
25brightness device max_brightness power subsystem trigger uevent
26
27/sys/class/leds/blinkm-6-9-red:
28brightness device max_brightness power subsystem trigger uevent
29
30(same is /sys/bus/i2c/devices/6-0009/leds)
31
32We can control the colors separated into red, green and blue and
33assign triggers on each color.
34
35E.g.:
36
37$ cat blinkm-6-9-blue/brightness
3805
39
40$ echo 200 > blinkm-6-9-blue/brightness
41$
42
43$ modprobe ledtrig-heartbeat
44$ echo heartbeat > blinkm-6-9-green/trigger
45$
46
47
48b) Sysfs group to control rgb, fade, hsb, scripts ...
49#####################################################
50
51This extended interface is available as folder blinkm
52in the sysfs folder of the I2C device.
53E.g. below /sys/bus/i2c/devices/6-0009/blinkm
54
55$ ls -h /sys/bus/i2c/devices/6-0009/blinkm/
56blue green red test
57
58Currently supported is just setting red, green, blue
59and a test sequence.
60
61E.g.:
62
63$ cat *
6400
6500
6600
67#Write into test to start test sequence!#
68
69$ echo 1 > test
70$
71
72$ echo 255 > red
73$
74
75
76
77as of 6/2012
78
79dl9pf <at> gmx <dot> de
80
diff --git a/MAINTAINERS b/MAINTAINERS
index fe643e7b9df6..4d8811af9edd 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1518,6 +1518,11 @@ W: http://blackfin.uclinux.org/
1518S: Supported 1518S: Supported
1519F: drivers/i2c/busses/i2c-bfin-twi.c 1519F: drivers/i2c/busses/i2c-bfin-twi.c
1520 1520
1521BLINKM RGB LED DRIVER
1522M: Jan-Simon Moeller <jansimon.moeller@gmx.de>
1523S: Maintained
1524F: drivers/leds/leds-blinkm.c
1525
1521BLOCK LAYER 1526BLOCK LAYER
1522M: Jens Axboe <axboe@kernel.dk> 1527M: Jens Axboe <axboe@kernel.dk>
1523T: git git://git.kernel.org/pub/scm/linux/kernel/git/axboe/linux-block.git 1528T: git git://git.kernel.org/pub/scm/linux/kernel/git/axboe/linux-block.git
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index f028f0348e83..f0aaf415316d 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -430,6 +430,14 @@ config LEDS_OT200
430 This option enables support for the LEDs on the Bachmann OT200. 430 This option enables support for the LEDs on the Bachmann OT200.
431 Say Y to enable LEDs on the Bachmann OT200. 431 Say Y to enable LEDs on the Bachmann OT200.
432 432
433config LEDS_BLINKM
434 tristate "LED support for the BlinkM I2C RGB LED"
435 depends on LEDS_CLASS
436 depends on I2C
437 help
438 This option enables support for the BlinkM RGB LED connected
439 through I2C. Say Y to enable support for the BlinkM LED.
440
433config LEDS_TRIGGERS 441config LEDS_TRIGGERS
434 bool "LED Trigger support" 442 bool "LED Trigger support"
435 depends on LEDS_CLASS 443 depends on LEDS_CLASS
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index 5eebd7bce4be..d94d18f8a4be 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -48,6 +48,7 @@ obj-$(CONFIG_LEDS_ASIC3) += leds-asic3.o
48obj-$(CONFIG_LEDS_RENESAS_TPU) += leds-renesas-tpu.o 48obj-$(CONFIG_LEDS_RENESAS_TPU) += leds-renesas-tpu.o
49obj-$(CONFIG_LEDS_MAX8997) += leds-max8997.o 49obj-$(CONFIG_LEDS_MAX8997) += leds-max8997.o
50obj-$(CONFIG_LEDS_LM3556) += leds-lm3556.o 50obj-$(CONFIG_LEDS_LM3556) += leds-lm3556.o
51obj-$(CONFIG_LEDS_BLINKM) += leds-blinkm.o
51 52
52# LED SPI Drivers 53# LED SPI Drivers
53obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o 54obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o
diff --git a/drivers/leds/leds-blinkm.c b/drivers/leds/leds-blinkm.c
new file mode 100644
index 000000000000..5a9df43e5302
--- /dev/null
+++ b/drivers/leds/leds-blinkm.c
@@ -0,0 +1,812 @@
1/*
2 * leds-blinkm.c
3 * (c) Jan-Simon Möller (dl9pf@gmx.de)
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19
20#include <linux/module.h>
21#include <linux/init.h>
22#include <linux/slab.h>
23#include <linux/jiffies.h>
24#include <linux/i2c.h>
25#include <linux/err.h>
26#include <linux/mutex.h>
27#include <linux/sysfs.h>
28#include <linux/printk.h>
29#include <linux/pm_runtime.h>
30#include <linux/leds.h>
31#include <linux/delay.h>
32
33/* Addresses to scan - BlinkM is on 0x09 by default*/
34static const unsigned short normal_i2c[] = { 0x09, I2C_CLIENT_END };
35
36static int blinkm_transfer_hw(struct i2c_client *client, int cmd);
37static int blinkm_test_run(struct i2c_client *client);
38
39struct blinkm_led {
40 struct i2c_client *i2c_client;
41 struct led_classdev led_cdev;
42 int id;
43 atomic_t active;
44};
45
46struct blinkm_work {
47 struct blinkm_led *blinkm_led;
48 struct work_struct work;
49};
50
51#define cdev_to_blmled(c) container_of(c, struct blinkm_led, led_cdev)
52#define work_to_blmwork(c) container_of(c, struct blinkm_work, work)
53
54struct blinkm_data {
55 struct i2c_client *i2c_client;
56 struct mutex update_lock;
57 /* used for led class interface */
58 struct blinkm_led blinkm_leds[3];
59 /* used for "blinkm" sysfs interface */
60 u8 red; /* color red */
61 u8 green; /* color green */
62 u8 blue; /* color blue */
63 /* next values to use for transfer */
64 u8 next_red; /* color red */
65 u8 next_green; /* color green */
66 u8 next_blue; /* color blue */
67 /* internal use */
68 u8 args[7]; /* set of args for transmission */
69 u8 i2c_addr; /* i2c addr */
70 u8 fw_ver; /* firmware version */
71 /* used, but not from userspace */
72 u8 hue; /* HSB hue */
73 u8 saturation; /* HSB saturation */
74 u8 brightness; /* HSB brightness */
75 u8 next_hue; /* HSB hue */
76 u8 next_saturation; /* HSB saturation */
77 u8 next_brightness; /* HSB brightness */
78 /* currently unused / todo */
79 u8 fade_speed; /* fade speed 1 - 255 */
80 s8 time_adjust; /* time adjust -128 - 127 */
81 u8 fade:1; /* fade on = 1, off = 0 */
82 u8 rand:1; /* rand fade mode on = 1 */
83 u8 script_id; /* script ID */
84 u8 script_repeats; /* repeats of script */
85 u8 script_startline; /* line to start */
86};
87
88/* Colors */
89#define RED 0
90#define GREEN 1
91#define BLUE 2
92
93/* mapping command names to cmd chars - see datasheet */
94#define BLM_GO_RGB 0
95#define BLM_FADE_RGB 1
96#define BLM_FADE_HSB 2
97#define BLM_FADE_RAND_RGB 3
98#define BLM_FADE_RAND_HSB 4
99#define BLM_PLAY_SCRIPT 5
100#define BLM_STOP_SCRIPT 6
101#define BLM_SET_FADE_SPEED 7
102#define BLM_SET_TIME_ADJ 8
103#define BLM_GET_CUR_RGB 9
104#define BLM_WRITE_SCRIPT_LINE 10
105#define BLM_READ_SCRIPT_LINE 11
106#define BLM_SET_SCRIPT_LR 12 /* Length & Repeats */
107#define BLM_SET_ADDR 13
108#define BLM_GET_ADDR 14
109#define BLM_GET_FW_VER 15
110#define BLM_SET_STARTUP_PARAM 16
111
112/* BlinkM Commands
113 * as extracted out of the datasheet:
114 *
115 * cmdchar = command (ascii)
116 * cmdbyte = command in hex
117 * nr_args = number of arguments (to send)
118 * nr_ret = number of return values (to read)
119 * dir = direction (0 = read, 1 = write, 2 = both)
120 *
121 */
122static const struct {
123 char cmdchar;
124 u8 cmdbyte;
125 u8 nr_args;
126 u8 nr_ret;
127 u8 dir:2;
128} blinkm_cmds[17] = {
129 /* cmdchar, cmdbyte, nr_args, nr_ret, dir */
130 { 'n', 0x6e, 3, 0, 1},
131 { 'c', 0x63, 3, 0, 1},
132 { 'h', 0x68, 3, 0, 1},
133 { 'C', 0x43, 3, 0, 1},
134 { 'H', 0x48, 3, 0, 1},
135 { 'p', 0x70, 3, 0, 1},
136 { 'o', 0x6f, 0, 0, 1},
137 { 'f', 0x66, 1, 0, 1},
138 { 't', 0x74, 1, 0, 1},
139 { 'g', 0x67, 0, 3, 0},
140 { 'W', 0x57, 7, 0, 1},
141 { 'R', 0x52, 2, 5, 2},
142 { 'L', 0x4c, 3, 0, 1},
143 { 'A', 0x41, 4, 0, 1},
144 { 'a', 0x61, 0, 1, 0},
145 { 'Z', 0x5a, 0, 1, 0},
146 { 'B', 0x42, 5, 0, 1},
147};
148
149static ssize_t show_color_common(struct device *dev, char *buf, int color)
150{
151 struct i2c_client *client;
152 struct blinkm_data *data;
153 int ret;
154
155 client = to_i2c_client(dev);
156 data = i2c_get_clientdata(client);
157
158 ret = blinkm_transfer_hw(client, BLM_GET_CUR_RGB);
159 if (ret < 0)
160 return ret;
161 switch (color) {
162 case RED:
163 return scnprintf(buf, PAGE_SIZE, "%02X\n", data->red);
164 break;
165 case GREEN:
166 return scnprintf(buf, PAGE_SIZE, "%02X\n", data->green);
167 break;
168 case BLUE:
169 return scnprintf(buf, PAGE_SIZE, "%02X\n", data->blue);
170 break;
171 default:
172 return -EINVAL;
173 }
174 return -EINVAL;
175}
176
177static int store_color_common(struct device *dev, const char *buf, int color)
178{
179 struct i2c_client *client;
180 struct blinkm_data *data;
181 int ret;
182 u8 value;
183
184 client = to_i2c_client(dev);
185 data = i2c_get_clientdata(client);
186
187 ret = kstrtou8(buf, 10, &value);
188 if (ret < 0) {
189 dev_err(dev, "BlinkM: value too large!\n");
190 return ret;
191 }
192
193 switch (color) {
194 case RED:
195 data->next_red = value;
196 break;
197 case GREEN:
198 data->next_green = value;
199 break;
200 case BLUE:
201 data->next_blue = value;
202 break;
203 default:
204 return -EINVAL;
205 }
206
207 dev_dbg(dev, "next_red = %d, next_green = %d, next_blue = %d\n",
208 data->next_red, data->next_green, data->next_blue);
209
210 /* if mode ... */
211 ret = blinkm_transfer_hw(client, BLM_GO_RGB);
212 if (ret < 0) {
213 dev_err(dev, "BlinkM: can't set RGB\n");
214 return ret;
215 }
216 return 0;
217}
218
219static ssize_t show_red(struct device *dev, struct device_attribute *attr,
220 char *buf)
221{
222 return show_color_common(dev, buf, RED);
223}
224
225static ssize_t store_red(struct device *dev, struct device_attribute *attr,
226 const char *buf, size_t count)
227{
228 int ret;
229
230 ret = store_color_common(dev, buf, RED);
231 if (ret < 0)
232 return ret;
233 return count;
234}
235
236static DEVICE_ATTR(red, S_IRUGO | S_IWUSR, show_red, store_red);
237
238static ssize_t show_green(struct device *dev, struct device_attribute *attr,
239 char *buf)
240{
241 return show_color_common(dev, buf, GREEN);
242}
243
244static ssize_t store_green(struct device *dev, struct device_attribute *attr,
245 const char *buf, size_t count)
246{
247
248 int ret;
249
250 ret = store_color_common(dev, buf, GREEN);
251 if (ret < 0)
252 return ret;
253 return count;
254}
255
256static DEVICE_ATTR(green, S_IRUGO | S_IWUSR, show_green, store_green);
257
258static ssize_t show_blue(struct device *dev, struct device_attribute *attr,
259 char *buf)
260{
261 return show_color_common(dev, buf, BLUE);
262}
263
264static ssize_t store_blue(struct device *dev, struct device_attribute *attr,
265 const char *buf, size_t count)
266{
267 int ret;
268
269 ret = store_color_common(dev, buf, BLUE);
270 if (ret < 0)
271 return ret;
272 return count;
273}
274
275static DEVICE_ATTR(blue, S_IRUGO | S_IWUSR, show_blue, store_blue);
276
277static ssize_t show_test(struct device *dev, struct device_attribute *attr,
278 char *buf)
279{
280 return scnprintf(buf, PAGE_SIZE,
281 "#Write into test to start test sequence!#\n");
282}
283
284static ssize_t store_test(struct device *dev, struct device_attribute *attr,
285 const char *buf, size_t count)
286{
287
288 struct i2c_client *client;
289 int ret;
290 client = to_i2c_client(dev);
291
292 /*test */
293 ret = blinkm_test_run(client);
294 if (ret < 0)
295 return ret;
296
297 return count;
298}
299
300static DEVICE_ATTR(test, S_IRUGO | S_IWUSR, show_test, store_test);
301
302/* TODO: HSB, fade, timeadj, script ... */
303
304static struct attribute *blinkm_attrs[] = {
305 &dev_attr_red.attr,
306 &dev_attr_green.attr,
307 &dev_attr_blue.attr,
308 &dev_attr_test.attr,
309 NULL,
310};
311
312static struct attribute_group blinkm_group = {
313 .name = "blinkm",
314 .attrs = blinkm_attrs,
315};
316
317static int blinkm_write(struct i2c_client *client, int cmd, u8 *arg)
318{
319 int result;
320 int i;
321 int arglen = blinkm_cmds[cmd].nr_args;
322 /* write out cmd to blinkm - always / default step */
323 result = i2c_smbus_write_byte(client, blinkm_cmds[cmd].cmdbyte);
324 if (result < 0)
325 return result;
326 /* no args to write out */
327 if (arglen == 0)
328 return 0;
329
330 for (i = 0; i < arglen; i++) {
331 /* repeat for arglen */
332 result = i2c_smbus_write_byte(client, arg[i]);
333 if (result < 0)
334 return result;
335 }
336 return 0;
337}
338
339static int blinkm_read(struct i2c_client *client, int cmd, u8 *arg)
340{
341 int result;
342 int i;
343 int retlen = blinkm_cmds[cmd].nr_ret;
344 for (i = 0; i < retlen; i++) {
345 /* repeat for retlen */
346 result = i2c_smbus_read_byte(client);
347 if (result < 0)
348 return result;
349 arg[i] = result;
350 }
351
352 return 0;
353}
354
355static int blinkm_transfer_hw(struct i2c_client *client, int cmd)
356{
357 /* the protocol is simple but non-standard:
358 * e.g. cmd 'g' (= 0x67) for "get device address"
359 * - which defaults to 0x09 - would be the sequence:
360 * a) write 0x67 to the device (byte write)
361 * b) read the value (0x09) back right after (byte read)
362 *
363 * Watch out for "unfinished" sequences (i.e. not enough reads
364 * or writes after a command. It will make the blinkM misbehave.
365 * Sequence is key here.
366 */
367
368 /* args / return are in private data struct */
369 struct blinkm_data *data = i2c_get_clientdata(client);
370
371 /* We start hardware transfers which are not to be
372 * mixed with other commands. Aquire a lock now. */
373 if (mutex_lock_interruptible(&data->update_lock) < 0)
374 return -EAGAIN;
375
376 /* switch cmd - usually write before reads */
377 switch (cmd) {
378 case BLM_FADE_RAND_RGB:
379 case BLM_GO_RGB:
380 case BLM_FADE_RGB:
381 data->args[0] = data->next_red;
382 data->args[1] = data->next_green;
383 data->args[2] = data->next_blue;
384 blinkm_write(client, cmd, data->args);
385 data->red = data->args[0];
386 data->green = data->args[1];
387 data->blue = data->args[2];
388 break;
389 case BLM_FADE_HSB:
390 case BLM_FADE_RAND_HSB:
391 data->args[0] = data->next_hue;
392 data->args[1] = data->next_saturation;
393 data->args[2] = data->next_brightness;
394 blinkm_write(client, cmd, data->args);
395 data->hue = data->next_hue;
396 data->saturation = data->next_saturation;
397 data->brightness = data->next_brightness;
398 break;
399 case BLM_PLAY_SCRIPT:
400 data->args[0] = data->script_id;
401 data->args[1] = data->script_repeats;
402 data->args[2] = data->script_startline;
403 blinkm_write(client, cmd, data->args);
404 break;
405 case BLM_STOP_SCRIPT:
406 blinkm_write(client, cmd, NULL);
407 break;
408 case BLM_GET_CUR_RGB:
409 data->args[0] = data->red;
410 data->args[1] = data->green;
411 data->args[2] = data->blue;
412 blinkm_write(client, cmd, NULL);
413 blinkm_read(client, cmd, data->args);
414 data->red = data->args[0];
415 data->green = data->args[1];
416 data->blue = data->args[2];
417 break;
418 case BLM_GET_ADDR:
419 data->args[0] = data->i2c_addr;
420 blinkm_write(client, cmd, NULL);
421 blinkm_read(client, cmd, data->args);
422 data->i2c_addr = data->args[0];
423 break;
424 case BLM_SET_TIME_ADJ:
425 case BLM_SET_FADE_SPEED:
426 case BLM_READ_SCRIPT_LINE:
427 case BLM_WRITE_SCRIPT_LINE:
428 case BLM_SET_SCRIPT_LR:
429 case BLM_SET_ADDR:
430 case BLM_GET_FW_VER:
431 case BLM_SET_STARTUP_PARAM:
432 dev_err(&client->dev,
433 "BlinkM: cmd %d not implemented yet.\n", cmd);
434 break;
435 default:
436 dev_err(&client->dev, "BlinkM: unknown command %d\n", cmd);
437 mutex_unlock(&data->update_lock);
438 return -EINVAL;
439 } /* end switch(cmd) */
440
441 /* transfers done, unlock */
442 mutex_unlock(&data->update_lock);
443 return 0;
444}
445
446static void led_work(struct work_struct *work)
447{
448 int ret;
449 struct blinkm_led *led;
450 struct blinkm_data *data ;
451 struct blinkm_work *blm_work = work_to_blmwork(work);
452
453 led = blm_work->blinkm_led;
454 data = i2c_get_clientdata(led->i2c_client);
455 ret = blinkm_transfer_hw(led->i2c_client, BLM_GO_RGB);
456 atomic_dec(&led->active);
457 dev_dbg(&led->i2c_client->dev,
458 "# DONE # next_red = %d, next_green = %d,"
459 " next_blue = %d, active = %d\n",
460 data->next_red, data->next_green,
461 data->next_blue, atomic_read(&led->active));
462 kfree(blm_work);
463}
464
465static int blinkm_led_common_set(struct led_classdev *led_cdev,
466 enum led_brightness value, int color)
467{
468 /* led_brightness is 0, 127 or 255 - we just use it here as-is */
469 struct blinkm_led *led = cdev_to_blmled(led_cdev);
470 struct blinkm_data *data = i2c_get_clientdata(led->i2c_client);
471 struct blinkm_work *bl_work = kzalloc(sizeof(struct blinkm_work),
472 GFP_ATOMIC);
473
474 switch (color) {
475 case RED:
476 /* bail out if there's no change */
477 if (data->next_red == (u8) value)
478 return 0;
479 /* we assume a quite fast sequence here ([off]->on->off)
480 * think of network led trigger - we cannot blink that fast, so
481 * in case we already have a off->on->off transition queued up,
482 * we refuse to queue up more.
483 * Revisit: fast-changing brightness. */
484 if (atomic_read(&led->active) > 1)
485 return 0;
486 data->next_red = (u8) value;
487 break;
488 case GREEN:
489 /* bail out if there's no change */
490 if (data->next_green == (u8) value)
491 return 0;
492 /* we assume a quite fast sequence here ([off]->on->off)
493 * Revisit: fast-changing brightness. */
494 if (atomic_read(&led->active) > 1)
495 return 0;
496 data->next_green = (u8) value;
497 break;
498 case BLUE:
499 /* bail out if there's no change */
500 if (data->next_blue == (u8) value)
501 return 0;
502 /* we assume a quite fast sequence here ([off]->on->off)
503 * Revisit: fast-changing brightness. */
504 if (atomic_read(&led->active) > 1)
505 return 0;
506 data->next_blue = (u8) value;
507 break;
508
509 default:
510 dev_err(&led->i2c_client->dev, "BlinkM: unknown color.\n");
511 return -EINVAL;
512 }
513
514 atomic_inc(&led->active);
515 dev_dbg(&led->i2c_client->dev,
516 "#TO_SCHED# next_red = %d, next_green = %d,"
517 " next_blue = %d, active = %d\n",
518 data->next_red, data->next_green,
519 data->next_blue, atomic_read(&led->active));
520
521 /* a fresh work _item_ for each change */
522 bl_work->blinkm_led = led;
523 INIT_WORK(&bl_work->work, led_work);
524 /* queue work in own queue for easy sync on exit*/
525 schedule_work(&bl_work->work);
526
527 return 0;
528}
529
530static void blinkm_led_red_set(struct led_classdev *led_cdev,
531 enum led_brightness value)
532{
533 blinkm_led_common_set(led_cdev, value, RED);
534}
535
536static void blinkm_led_green_set(struct led_classdev *led_cdev,
537 enum led_brightness value)
538{
539 blinkm_led_common_set(led_cdev, value, GREEN);
540}
541
542static void blinkm_led_blue_set(struct led_classdev *led_cdev,
543 enum led_brightness value)
544{
545 blinkm_led_common_set(led_cdev, value, BLUE);
546}
547
548static void blinkm_init_hw(struct i2c_client *client)
549{
550 int ret;
551 ret = blinkm_transfer_hw(client, BLM_STOP_SCRIPT);
552 ret = blinkm_transfer_hw(client, BLM_GO_RGB);
553}
554
555static int blinkm_test_run(struct i2c_client *client)
556{
557 int ret;
558 struct blinkm_data *data = i2c_get_clientdata(client);
559
560 data->next_red = 0x01;
561 data->next_green = 0x05;
562 data->next_blue = 0x10;
563 ret = blinkm_transfer_hw(client, BLM_GO_RGB);
564 if (ret < 0)
565 return ret;
566 msleep(2000);
567
568 data->next_red = 0x25;
569 data->next_green = 0x10;
570 data->next_blue = 0x31;
571 ret = blinkm_transfer_hw(client, BLM_FADE_RGB);
572 if (ret < 0)
573 return ret;
574 msleep(2000);
575
576 data->next_hue = 0x50;
577 data->next_saturation = 0x10;
578 data->next_brightness = 0x20;
579 ret = blinkm_transfer_hw(client, BLM_FADE_HSB);
580 if (ret < 0)
581 return ret;
582 msleep(2000);
583
584 return 0;
585}
586
587/* Return 0 if detection is successful, -ENODEV otherwise */
588static int blinkm_detect(struct i2c_client *client, struct i2c_board_info *info)
589{
590 struct i2c_adapter *adapter = client->adapter;
591 int ret;
592 int count = 99;
593 u8 tmpargs[7];
594
595 if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA
596 | I2C_FUNC_SMBUS_WORD_DATA
597 | I2C_FUNC_SMBUS_WRITE_BYTE))
598 return -ENODEV;
599
600 /* Now, we do the remaining detection. Simple for now. */
601 /* We might need more guards to protect other i2c slaves */
602
603 /* make sure the blinkM is balanced (read/writes) */
604 while (count > 0) {
605 ret = blinkm_write(client, BLM_GET_ADDR, NULL);
606 usleep_range(5000, 10000);
607 ret = blinkm_read(client, BLM_GET_ADDR, tmpargs);
608 usleep_range(5000, 10000);
609 if (tmpargs[0] == 0x09)
610 count = 0;
611 count--;
612 }
613
614 /* Step 1: Read BlinkM address back - cmd_char 'a' */
615 ret = blinkm_write(client, BLM_GET_ADDR, NULL);
616 if (ret < 0)
617 return -ENODEV;
618 usleep_range(20000, 30000); /* allow a small delay */
619 ret = blinkm_read(client, BLM_GET_ADDR, tmpargs);
620 if (ret < 0)
621 return -ENODEV;
622
623 if (tmpargs[0] != 0x09) {
624 dev_err(&client->dev, "enodev DEV ADDR = 0x%02X\n", tmpargs[0]);
625 return -ENODEV;
626 }
627
628 strlcpy(info->type, "blinkm", I2C_NAME_SIZE);
629 return 0;
630}
631
632static int __devinit blinkm_probe(struct i2c_client *client,
633 const struct i2c_device_id *id)
634{
635 struct blinkm_data *data;
636 struct blinkm_led *led[3];
637 int err, i;
638 char blinkm_led_name[28];
639
640 data = devm_kzalloc(&client->dev,
641 sizeof(struct blinkm_data), GFP_KERNEL);
642 if (!data) {
643 err = -ENOMEM;
644 goto exit;
645 }
646
647 data->i2c_addr = 0x09;
648 data->i2c_addr = 0x08;
649 /* i2c addr - use fake addr of 0x08 initially (real is 0x09) */
650 data->fw_ver = 0xfe;
651 /* firmware version - use fake until we read real value
652 * (currently broken - BlinkM confused!) */
653 data->script_id = 0x01;
654 data->i2c_client = client;
655
656 i2c_set_clientdata(client, data);
657 mutex_init(&data->update_lock);
658
659 /* Register sysfs hooks */
660 err = sysfs_create_group(&client->dev.kobj, &blinkm_group);
661 if (err < 0) {
662 dev_err(&client->dev, "couldn't register sysfs group\n");
663 goto exit;
664 }
665
666 for (i = 0; i < 3; i++) {
667 /* RED = 0, GREEN = 1, BLUE = 2 */
668 led[i] = &data->blinkm_leds[i];
669 led[i]->i2c_client = client;
670 led[i]->id = i;
671 led[i]->led_cdev.max_brightness = 255;
672 led[i]->led_cdev.flags = LED_CORE_SUSPENDRESUME;
673 atomic_set(&led[i]->active, 0);
674 switch (i) {
675 case RED:
676 snprintf(blinkm_led_name, sizeof(blinkm_led_name),
677 "blinkm-%d-%d-red",
678 client->adapter->nr,
679 client->addr);
680 led[i]->led_cdev.name = blinkm_led_name;
681 led[i]->led_cdev.brightness_set = blinkm_led_red_set;
682 err = led_classdev_register(&client->dev,
683 &led[i]->led_cdev);
684 if (err < 0) {
685 dev_err(&client->dev,
686 "couldn't register LED %s\n",
687 led[i]->led_cdev.name);
688 goto failred;
689 }
690 break;
691 case GREEN:
692 snprintf(blinkm_led_name, sizeof(blinkm_led_name),
693 "blinkm-%d-%d-green",
694 client->adapter->nr,
695 client->addr);
696 led[i]->led_cdev.name = blinkm_led_name;
697 led[i]->led_cdev.brightness_set = blinkm_led_green_set;
698 err = led_classdev_register(&client->dev,
699 &led[i]->led_cdev);
700 if (err < 0) {
701 dev_err(&client->dev,
702 "couldn't register LED %s\n",
703 led[i]->led_cdev.name);
704 goto failgreen;
705 }
706 break;
707 case BLUE:
708 snprintf(blinkm_led_name, sizeof(blinkm_led_name),
709 "blinkm-%d-%d-blue",
710 client->adapter->nr,
711 client->addr);
712 led[i]->led_cdev.name = blinkm_led_name;
713 led[i]->led_cdev.brightness_set = blinkm_led_blue_set;
714 err = led_classdev_register(&client->dev,
715 &led[i]->led_cdev);
716 if (err < 0) {
717 dev_err(&client->dev,
718 "couldn't register LED %s\n",
719 led[i]->led_cdev.name);
720 goto failblue;
721 }
722 break;
723 } /* end switch */
724 } /* end for */
725
726 /* Initialize the blinkm */
727 blinkm_init_hw(client);
728
729 return 0;
730
731failblue:
732 led_classdev_unregister(&led[GREEN]->led_cdev);
733
734failgreen:
735 led_classdev_unregister(&led[RED]->led_cdev);
736
737failred:
738 sysfs_remove_group(&client->dev.kobj, &blinkm_group);
739exit:
740 return err;
741}
742
743static int __devexit blinkm_remove(struct i2c_client *client)
744{
745 struct blinkm_data *data = i2c_get_clientdata(client);
746 int ret = 0;
747 int i;
748
749 /* make sure no workqueue entries are pending */
750 for (i = 0; i < 3; i++) {
751 flush_scheduled_work();
752 led_classdev_unregister(&data->blinkm_leds[i].led_cdev);
753 }
754
755 /* reset rgb */
756 data->next_red = 0x00;
757 data->next_green = 0x00;
758 data->next_blue = 0x00;
759 ret = blinkm_transfer_hw(client, BLM_FADE_RGB);
760 if (ret < 0)
761 dev_err(&client->dev, "Failure in blinkm_remove ignored. Continuing.\n");
762
763 /* reset hsb */
764 data->next_hue = 0x00;
765 data->next_saturation = 0x00;
766 data->next_brightness = 0x00;
767 ret = blinkm_transfer_hw(client, BLM_FADE_HSB);
768 if (ret < 0)
769 dev_err(&client->dev, "Failure in blinkm_remove ignored. Continuing.\n");
770
771 /* red fade to off */
772 data->next_red = 0xff;
773 ret = blinkm_transfer_hw(client, BLM_GO_RGB);
774 if (ret < 0)
775 dev_err(&client->dev, "Failure in blinkm_remove ignored. Continuing.\n");
776
777 /* off */
778 data->next_red = 0x00;
779 ret = blinkm_transfer_hw(client, BLM_FADE_RGB);
780 if (ret < 0)
781 dev_err(&client->dev, "Failure in blinkm_remove ignored. Continuing.\n");
782
783 sysfs_remove_group(&client->dev.kobj, &blinkm_group);
784 return 0;
785}
786
787static const struct i2c_device_id blinkm_id[] = {
788 {"blinkm", 0},
789 {}
790};
791
792MODULE_DEVICE_TABLE(i2c, blinkm_id);
793
794 /* This is the driver that will be inserted */
795static struct i2c_driver blinkm_driver = {
796 .class = I2C_CLASS_HWMON,
797 .driver = {
798 .name = "blinkm",
799 },
800 .probe = blinkm_probe,
801 .remove = __devexit_p(blinkm_remove),
802 .id_table = blinkm_id,
803 .detect = blinkm_detect,
804 .address_list = normal_i2c,
805};
806
807module_i2c_driver(blinkm_driver);
808
809MODULE_AUTHOR("Jan-Simon Moeller <dl9pf@gmx.de>");
810MODULE_DESCRIPTION("BlinkM RGB LED driver");
811MODULE_LICENSE("GPL");
812