Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

LEDS: add BlinkM RGB LED driver, documentation and update MAINTAINERS

Add driver for BlinkM device to drivers/leds/.
Add entry to MAINTAINERS file.
Add documentation in Documentation/leds/.

A BlinkM is a RGB LED controlled through I2C.

This driver implements an interface to the LED framework
and another sysfs group to access the internal options
of the BlinkM.

rev6: Use module_i2c_driver().
rev5: Removed own workqueue in favor of events wq.
rev4: Fixed comments by Bryan Wu.
rev3: Fixed issues found by Jonathan Neuschäfer.

(bryan.wu@canonical.com: remove 2 trailing whitespace)

Signed-off-by: Jan-Simon Möller <jansimon.moeller@gmx.de>
Signed-off-by: Bryan Wu <bryan.wu@canonical.com>

authored by

Jan-Simon Möller and committed by
Bryan Wu
b54cf35a eb18618b

+906
+80
Documentation/leds/leds-blinkm.txt
··· 1 + The leds-blinkm driver supports the devices of the BlinkM family. 2 + 3 + They are RGB-LED modules driven by a (AT)tiny microcontroller and 4 + communicate through I2C. The default address of these modules is 5 + 0x09 but this can be changed through a command. By this you could 6 + dasy-chain up to 127 BlinkMs on an I2C bus. 7 + 8 + The device accepts RGB and HSB color values through separate commands. 9 + Also you can store blinking sequences as "scripts" in 10 + the controller and run them. Also fading is an option. 11 + 12 + The interface this driver provides is 2-fold: 13 + 14 + a) LED class interface for use with triggers 15 + ############################################ 16 + 17 + The registration follows the scheme: 18 + blinkm-<i2c-bus-nr>-<i2c-device-nr>-<color> 19 + 20 + $ ls -h /sys/class/leds/blinkm-6-* 21 + /sys/class/leds/blinkm-6-9-blue: 22 + brightness device max_brightness power subsystem trigger uevent 23 + 24 + /sys/class/leds/blinkm-6-9-green: 25 + brightness device max_brightness power subsystem trigger uevent 26 + 27 + /sys/class/leds/blinkm-6-9-red: 28 + brightness device max_brightness power subsystem trigger uevent 29 + 30 + (same is /sys/bus/i2c/devices/6-0009/leds) 31 + 32 + We can control the colors separated into red, green and blue and 33 + assign triggers on each color. 34 + 35 + E.g.: 36 + 37 + $ cat blinkm-6-9-blue/brightness 38 + 05 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 + 48 + b) Sysfs group to control rgb, fade, hsb, scripts ... 49 + ##################################################### 50 + 51 + This extended interface is available as folder blinkm 52 + in the sysfs folder of the I2C device. 53 + E.g. below /sys/bus/i2c/devices/6-0009/blinkm 54 + 55 + $ ls -h /sys/bus/i2c/devices/6-0009/blinkm/ 56 + blue green red test 57 + 58 + Currently supported is just setting red, green, blue 59 + and a test sequence. 60 + 61 + E.g.: 62 + 63 + $ cat * 64 + 00 65 + 00 66 + 00 67 + #Write into test to start test sequence!# 68 + 69 + $ echo 1 > test 70 + $ 71 + 72 + $ echo 255 > red 73 + $ 74 + 75 + 76 + 77 + as of 6/2012 78 + 79 + dl9pf <at> gmx <dot> de 80 +
+5
MAINTAINERS
··· 1518 1518 S: Supported 1519 1519 F: drivers/i2c/busses/i2c-bfin-twi.c 1520 1520 1521 + BLINKM RGB LED DRIVER 1522 + M: Jan-Simon Moeller <jansimon.moeller@gmx.de> 1523 + S: Maintained 1524 + F: drivers/leds/leds-blinkm.c 1525 + 1521 1526 BLOCK LAYER 1522 1527 M: Jens Axboe <axboe@kernel.dk> 1523 1528 T: git git://git.kernel.org/pub/scm/linux/kernel/git/axboe/linux-block.git
+8
drivers/leds/Kconfig
··· 430 430 This option enables support for the LEDs on the Bachmann OT200. 431 431 Say Y to enable LEDs on the Bachmann OT200. 432 432 433 + config 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 + 433 441 config LEDS_TRIGGERS 434 442 bool "LED Trigger support" 435 443 depends on LEDS_CLASS
+1
drivers/leds/Makefile
··· 48 48 obj-$(CONFIG_LEDS_RENESAS_TPU) += leds-renesas-tpu.o 49 49 obj-$(CONFIG_LEDS_MAX8997) += leds-max8997.o 50 50 obj-$(CONFIG_LEDS_LM3556) += leds-lm3556.o 51 + obj-$(CONFIG_LEDS_BLINKM) += leds-blinkm.o 51 52 52 53 # LED SPI Drivers 53 54 obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o
+812
drivers/leds/leds-blinkm.c
··· 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*/ 34 + static const unsigned short normal_i2c[] = { 0x09, I2C_CLIENT_END }; 35 + 36 + static int blinkm_transfer_hw(struct i2c_client *client, int cmd); 37 + static int blinkm_test_run(struct i2c_client *client); 38 + 39 + struct blinkm_led { 40 + struct i2c_client *i2c_client; 41 + struct led_classdev led_cdev; 42 + int id; 43 + atomic_t active; 44 + }; 45 + 46 + struct 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 + 54 + struct 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 + */ 122 + static 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 + 149 + static 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 + 177 + static 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 + 219 + static 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 + 225 + static 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 + 236 + static DEVICE_ATTR(red, S_IRUGO | S_IWUSR, show_red, store_red); 237 + 238 + static 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 + 244 + static 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 + 256 + static DEVICE_ATTR(green, S_IRUGO | S_IWUSR, show_green, store_green); 257 + 258 + static 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 + 264 + static 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 + 275 + static DEVICE_ATTR(blue, S_IRUGO | S_IWUSR, show_blue, store_blue); 276 + 277 + static 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 + 284 + static 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 + 300 + static DEVICE_ATTR(test, S_IRUGO | S_IWUSR, show_test, store_test); 301 + 302 + /* TODO: HSB, fade, timeadj, script ... */ 303 + 304 + static 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 + 312 + static struct attribute_group blinkm_group = { 313 + .name = "blinkm", 314 + .attrs = blinkm_attrs, 315 + }; 316 + 317 + static 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 + 339 + static 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 + 355 + static 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 + 446 + static 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 + 465 + static 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 + 530 + static 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 + 536 + static 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 + 542 + static 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 + 548 + static 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 + 555 + static 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 */ 588 + static 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 + 632 + static 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 + 731 + failblue: 732 + led_classdev_unregister(&led[GREEN]->led_cdev); 733 + 734 + failgreen: 735 + led_classdev_unregister(&led[RED]->led_cdev); 736 + 737 + failred: 738 + sysfs_remove_group(&client->dev.kobj, &blinkm_group); 739 + exit: 740 + return err; 741 + } 742 + 743 + static 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 + 787 + static const struct i2c_device_id blinkm_id[] = { 788 + {"blinkm", 0}, 789 + {} 790 + }; 791 + 792 + MODULE_DEVICE_TABLE(i2c, blinkm_id); 793 + 794 + /* This is the driver that will be inserted */ 795 + static 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 + 807 + module_i2c_driver(blinkm_driver); 808 + 809 + MODULE_AUTHOR("Jan-Simon Moeller <dl9pf@gmx.de>"); 810 + MODULE_DESCRIPTION("BlinkM RGB LED driver"); 811 + MODULE_LICENSE("GPL"); 812 +