162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  leds-blinkm.c
462306a36Sopenharmony_ci *  (c) Jan-Simon Möller (dl9pf@gmx.de)
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/module.h>
862306a36Sopenharmony_ci#include <linux/slab.h>
962306a36Sopenharmony_ci#include <linux/jiffies.h>
1062306a36Sopenharmony_ci#include <linux/i2c.h>
1162306a36Sopenharmony_ci#include <linux/err.h>
1262306a36Sopenharmony_ci#include <linux/mutex.h>
1362306a36Sopenharmony_ci#include <linux/sysfs.h>
1462306a36Sopenharmony_ci#include <linux/printk.h>
1562306a36Sopenharmony_ci#include <linux/pm_runtime.h>
1662306a36Sopenharmony_ci#include <linux/leds.h>
1762306a36Sopenharmony_ci#include <linux/delay.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci/* Addresses to scan - BlinkM is on 0x09 by default*/
2062306a36Sopenharmony_cistatic const unsigned short normal_i2c[] = { 0x09, I2C_CLIENT_END };
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_cistatic int blinkm_transfer_hw(struct i2c_client *client, int cmd);
2362306a36Sopenharmony_cistatic int blinkm_test_run(struct i2c_client *client);
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistruct blinkm_led {
2662306a36Sopenharmony_ci	struct i2c_client *i2c_client;
2762306a36Sopenharmony_ci	struct led_classdev led_cdev;
2862306a36Sopenharmony_ci	int id;
2962306a36Sopenharmony_ci};
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#define cdev_to_blmled(c)          container_of(c, struct blinkm_led, led_cdev)
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistruct blinkm_data {
3462306a36Sopenharmony_ci	struct i2c_client *i2c_client;
3562306a36Sopenharmony_ci	struct mutex update_lock;
3662306a36Sopenharmony_ci	/* used for led class interface */
3762306a36Sopenharmony_ci	struct blinkm_led blinkm_leds[3];
3862306a36Sopenharmony_ci	/* used for "blinkm" sysfs interface */
3962306a36Sopenharmony_ci	u8 red;			/* color red */
4062306a36Sopenharmony_ci	u8 green;		/* color green */
4162306a36Sopenharmony_ci	u8 blue;		/* color blue */
4262306a36Sopenharmony_ci	/* next values to use for transfer */
4362306a36Sopenharmony_ci	u8 next_red;			/* color red */
4462306a36Sopenharmony_ci	u8 next_green;		/* color green */
4562306a36Sopenharmony_ci	u8 next_blue;		/* color blue */
4662306a36Sopenharmony_ci	/* internal use */
4762306a36Sopenharmony_ci	u8 args[7];		/* set of args for transmission */
4862306a36Sopenharmony_ci	u8 i2c_addr;		/* i2c addr */
4962306a36Sopenharmony_ci	u8 fw_ver;		/* firmware version */
5062306a36Sopenharmony_ci	/* used, but not from userspace */
5162306a36Sopenharmony_ci	u8 hue;			/* HSB  hue */
5262306a36Sopenharmony_ci	u8 saturation;		/* HSB  saturation */
5362306a36Sopenharmony_ci	u8 brightness;		/* HSB  brightness */
5462306a36Sopenharmony_ci	u8 next_hue;			/* HSB  hue */
5562306a36Sopenharmony_ci	u8 next_saturation;		/* HSB  saturation */
5662306a36Sopenharmony_ci	u8 next_brightness;		/* HSB  brightness */
5762306a36Sopenharmony_ci	/* currently unused / todo */
5862306a36Sopenharmony_ci	u8 fade_speed;		/* fade speed     1 - 255 */
5962306a36Sopenharmony_ci	s8 time_adjust;		/* time adjust -128 - 127 */
6062306a36Sopenharmony_ci	u8 fade:1;		/* fade on = 1, off = 0 */
6162306a36Sopenharmony_ci	u8 rand:1;		/* rand fade mode on = 1 */
6262306a36Sopenharmony_ci	u8 script_id;		/* script ID */
6362306a36Sopenharmony_ci	u8 script_repeats;	/* repeats of script */
6462306a36Sopenharmony_ci	u8 script_startline;	/* line to start */
6562306a36Sopenharmony_ci};
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci/* Colors */
6862306a36Sopenharmony_ci#define RED   0
6962306a36Sopenharmony_ci#define GREEN 1
7062306a36Sopenharmony_ci#define BLUE  2
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci/* mapping command names to cmd chars - see datasheet */
7362306a36Sopenharmony_ci#define BLM_GO_RGB            0
7462306a36Sopenharmony_ci#define BLM_FADE_RGB          1
7562306a36Sopenharmony_ci#define BLM_FADE_HSB          2
7662306a36Sopenharmony_ci#define BLM_FADE_RAND_RGB     3
7762306a36Sopenharmony_ci#define BLM_FADE_RAND_HSB     4
7862306a36Sopenharmony_ci#define BLM_PLAY_SCRIPT       5
7962306a36Sopenharmony_ci#define BLM_STOP_SCRIPT       6
8062306a36Sopenharmony_ci#define BLM_SET_FADE_SPEED    7
8162306a36Sopenharmony_ci#define BLM_SET_TIME_ADJ      8
8262306a36Sopenharmony_ci#define BLM_GET_CUR_RGB       9
8362306a36Sopenharmony_ci#define BLM_WRITE_SCRIPT_LINE 10
8462306a36Sopenharmony_ci#define BLM_READ_SCRIPT_LINE  11
8562306a36Sopenharmony_ci#define BLM_SET_SCRIPT_LR     12	/* Length & Repeats */
8662306a36Sopenharmony_ci#define BLM_SET_ADDR          13
8762306a36Sopenharmony_ci#define BLM_GET_ADDR          14
8862306a36Sopenharmony_ci#define BLM_GET_FW_VER        15
8962306a36Sopenharmony_ci#define BLM_SET_STARTUP_PARAM 16
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci/* BlinkM Commands
9262306a36Sopenharmony_ci *  as extracted out of the datasheet:
9362306a36Sopenharmony_ci *
9462306a36Sopenharmony_ci *  cmdchar = command (ascii)
9562306a36Sopenharmony_ci *  cmdbyte = command in hex
9662306a36Sopenharmony_ci *  nr_args = number of arguments (to send)
9762306a36Sopenharmony_ci *  nr_ret  = number of return values (to read)
9862306a36Sopenharmony_ci *  dir = direction (0 = read, 1 = write, 2 = both)
9962306a36Sopenharmony_ci *
10062306a36Sopenharmony_ci */
10162306a36Sopenharmony_cistatic const struct {
10262306a36Sopenharmony_ci	char cmdchar;
10362306a36Sopenharmony_ci	u8 cmdbyte;
10462306a36Sopenharmony_ci	u8 nr_args;
10562306a36Sopenharmony_ci	u8 nr_ret;
10662306a36Sopenharmony_ci	u8 dir:2;
10762306a36Sopenharmony_ci} blinkm_cmds[17] = {
10862306a36Sopenharmony_ci  /* cmdchar, cmdbyte, nr_args, nr_ret,  dir */
10962306a36Sopenharmony_ci	{ 'n', 0x6e, 3, 0, 1},
11062306a36Sopenharmony_ci	{ 'c', 0x63, 3, 0, 1},
11162306a36Sopenharmony_ci	{ 'h', 0x68, 3, 0, 1},
11262306a36Sopenharmony_ci	{ 'C', 0x43, 3, 0, 1},
11362306a36Sopenharmony_ci	{ 'H', 0x48, 3, 0, 1},
11462306a36Sopenharmony_ci	{ 'p', 0x70, 3, 0, 1},
11562306a36Sopenharmony_ci	{ 'o', 0x6f, 0, 0, 1},
11662306a36Sopenharmony_ci	{ 'f', 0x66, 1, 0, 1},
11762306a36Sopenharmony_ci	{ 't', 0x74, 1, 0, 1},
11862306a36Sopenharmony_ci	{ 'g', 0x67, 0, 3, 0},
11962306a36Sopenharmony_ci	{ 'W', 0x57, 7, 0, 1},
12062306a36Sopenharmony_ci	{ 'R', 0x52, 2, 5, 2},
12162306a36Sopenharmony_ci	{ 'L', 0x4c, 3, 0, 1},
12262306a36Sopenharmony_ci	{ 'A', 0x41, 4, 0, 1},
12362306a36Sopenharmony_ci	{ 'a', 0x61, 0, 1, 0},
12462306a36Sopenharmony_ci	{ 'Z', 0x5a, 0, 1, 0},
12562306a36Sopenharmony_ci	{ 'B', 0x42, 5, 0, 1},
12662306a36Sopenharmony_ci};
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_cistatic ssize_t show_color_common(struct device *dev, char *buf, int color)
12962306a36Sopenharmony_ci{
13062306a36Sopenharmony_ci	struct i2c_client *client;
13162306a36Sopenharmony_ci	struct blinkm_data *data;
13262306a36Sopenharmony_ci	int ret;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	client = to_i2c_client(dev);
13562306a36Sopenharmony_ci	data = i2c_get_clientdata(client);
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	ret = blinkm_transfer_hw(client, BLM_GET_CUR_RGB);
13862306a36Sopenharmony_ci	if (ret < 0)
13962306a36Sopenharmony_ci		return ret;
14062306a36Sopenharmony_ci	switch (color) {
14162306a36Sopenharmony_ci	case RED:
14262306a36Sopenharmony_ci		return sysfs_emit(buf, "%02X\n", data->red);
14362306a36Sopenharmony_ci	case GREEN:
14462306a36Sopenharmony_ci		return sysfs_emit(buf, "%02X\n", data->green);
14562306a36Sopenharmony_ci	case BLUE:
14662306a36Sopenharmony_ci		return sysfs_emit(buf, "%02X\n", data->blue);
14762306a36Sopenharmony_ci	default:
14862306a36Sopenharmony_ci		return -EINVAL;
14962306a36Sopenharmony_ci	}
15062306a36Sopenharmony_ci	return -EINVAL;
15162306a36Sopenharmony_ci}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_cistatic int store_color_common(struct device *dev, const char *buf, int color)
15462306a36Sopenharmony_ci{
15562306a36Sopenharmony_ci	struct i2c_client *client;
15662306a36Sopenharmony_ci	struct blinkm_data *data;
15762306a36Sopenharmony_ci	int ret;
15862306a36Sopenharmony_ci	u8 value;
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	client = to_i2c_client(dev);
16162306a36Sopenharmony_ci	data = i2c_get_clientdata(client);
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	ret = kstrtou8(buf, 10, &value);
16462306a36Sopenharmony_ci	if (ret < 0) {
16562306a36Sopenharmony_ci		dev_err(dev, "BlinkM: value too large!\n");
16662306a36Sopenharmony_ci		return ret;
16762306a36Sopenharmony_ci	}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	switch (color) {
17062306a36Sopenharmony_ci	case RED:
17162306a36Sopenharmony_ci		data->next_red = value;
17262306a36Sopenharmony_ci		break;
17362306a36Sopenharmony_ci	case GREEN:
17462306a36Sopenharmony_ci		data->next_green = value;
17562306a36Sopenharmony_ci		break;
17662306a36Sopenharmony_ci	case BLUE:
17762306a36Sopenharmony_ci		data->next_blue = value;
17862306a36Sopenharmony_ci		break;
17962306a36Sopenharmony_ci	default:
18062306a36Sopenharmony_ci		return -EINVAL;
18162306a36Sopenharmony_ci	}
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	dev_dbg(dev, "next_red = %d, next_green = %d, next_blue = %d\n",
18462306a36Sopenharmony_ci			data->next_red, data->next_green, data->next_blue);
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	/* if mode ... */
18762306a36Sopenharmony_ci	ret = blinkm_transfer_hw(client, BLM_GO_RGB);
18862306a36Sopenharmony_ci	if (ret < 0) {
18962306a36Sopenharmony_ci		dev_err(dev, "BlinkM: can't set RGB\n");
19062306a36Sopenharmony_ci		return ret;
19162306a36Sopenharmony_ci	}
19262306a36Sopenharmony_ci	return 0;
19362306a36Sopenharmony_ci}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_cistatic ssize_t red_show(struct device *dev, struct device_attribute *attr,
19662306a36Sopenharmony_ci			char *buf)
19762306a36Sopenharmony_ci{
19862306a36Sopenharmony_ci	return show_color_common(dev, buf, RED);
19962306a36Sopenharmony_ci}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_cistatic ssize_t red_store(struct device *dev, struct device_attribute *attr,
20262306a36Sopenharmony_ci			 const char *buf, size_t count)
20362306a36Sopenharmony_ci{
20462306a36Sopenharmony_ci	int ret;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	ret = store_color_common(dev, buf, RED);
20762306a36Sopenharmony_ci	if (ret < 0)
20862306a36Sopenharmony_ci		return ret;
20962306a36Sopenharmony_ci	return count;
21062306a36Sopenharmony_ci}
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_cistatic DEVICE_ATTR_RW(red);
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_cistatic ssize_t green_show(struct device *dev, struct device_attribute *attr,
21562306a36Sopenharmony_ci			  char *buf)
21662306a36Sopenharmony_ci{
21762306a36Sopenharmony_ci	return show_color_common(dev, buf, GREEN);
21862306a36Sopenharmony_ci}
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_cistatic ssize_t green_store(struct device *dev, struct device_attribute *attr,
22162306a36Sopenharmony_ci			   const char *buf, size_t count)
22262306a36Sopenharmony_ci{
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	int ret;
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	ret = store_color_common(dev, buf, GREEN);
22762306a36Sopenharmony_ci	if (ret < 0)
22862306a36Sopenharmony_ci		return ret;
22962306a36Sopenharmony_ci	return count;
23062306a36Sopenharmony_ci}
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_cistatic DEVICE_ATTR_RW(green);
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_cistatic ssize_t blue_show(struct device *dev, struct device_attribute *attr,
23562306a36Sopenharmony_ci			 char *buf)
23662306a36Sopenharmony_ci{
23762306a36Sopenharmony_ci	return show_color_common(dev, buf, BLUE);
23862306a36Sopenharmony_ci}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_cistatic ssize_t blue_store(struct device *dev, struct device_attribute *attr,
24162306a36Sopenharmony_ci			  const char *buf, size_t count)
24262306a36Sopenharmony_ci{
24362306a36Sopenharmony_ci	int ret;
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	ret = store_color_common(dev, buf, BLUE);
24662306a36Sopenharmony_ci	if (ret < 0)
24762306a36Sopenharmony_ci		return ret;
24862306a36Sopenharmony_ci	return count;
24962306a36Sopenharmony_ci}
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_cistatic DEVICE_ATTR_RW(blue);
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_cistatic ssize_t test_show(struct device *dev, struct device_attribute *attr,
25462306a36Sopenharmony_ci			 char *buf)
25562306a36Sopenharmony_ci{
25662306a36Sopenharmony_ci	return sysfs_emit(buf,
25762306a36Sopenharmony_ci			 "#Write into test to start test sequence!#\n");
25862306a36Sopenharmony_ci}
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_cistatic ssize_t test_store(struct device *dev, struct device_attribute *attr,
26162306a36Sopenharmony_ci			  const char *buf, size_t count)
26262306a36Sopenharmony_ci{
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	struct i2c_client *client;
26562306a36Sopenharmony_ci	int ret;
26662306a36Sopenharmony_ci	client = to_i2c_client(dev);
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	/*test */
26962306a36Sopenharmony_ci	ret = blinkm_test_run(client);
27062306a36Sopenharmony_ci	if (ret < 0)
27162306a36Sopenharmony_ci		return ret;
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	return count;
27462306a36Sopenharmony_ci}
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_cistatic DEVICE_ATTR_RW(test);
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci/* TODO: HSB, fade, timeadj, script ... */
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_cistatic struct attribute *blinkm_attrs[] = {
28162306a36Sopenharmony_ci	&dev_attr_red.attr,
28262306a36Sopenharmony_ci	&dev_attr_green.attr,
28362306a36Sopenharmony_ci	&dev_attr_blue.attr,
28462306a36Sopenharmony_ci	&dev_attr_test.attr,
28562306a36Sopenharmony_ci	NULL,
28662306a36Sopenharmony_ci};
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_cistatic const struct attribute_group blinkm_group = {
28962306a36Sopenharmony_ci	.name = "blinkm",
29062306a36Sopenharmony_ci	.attrs = blinkm_attrs,
29162306a36Sopenharmony_ci};
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_cistatic int blinkm_write(struct i2c_client *client, int cmd, u8 *arg)
29462306a36Sopenharmony_ci{
29562306a36Sopenharmony_ci	int result;
29662306a36Sopenharmony_ci	int i;
29762306a36Sopenharmony_ci	int arglen = blinkm_cmds[cmd].nr_args;
29862306a36Sopenharmony_ci	/* write out cmd to blinkm - always / default step */
29962306a36Sopenharmony_ci	result = i2c_smbus_write_byte(client, blinkm_cmds[cmd].cmdbyte);
30062306a36Sopenharmony_ci	if (result < 0)
30162306a36Sopenharmony_ci		return result;
30262306a36Sopenharmony_ci	/* no args to write out */
30362306a36Sopenharmony_ci	if (arglen == 0)
30462306a36Sopenharmony_ci		return 0;
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	for (i = 0; i < arglen; i++) {
30762306a36Sopenharmony_ci		/* repeat for arglen */
30862306a36Sopenharmony_ci		result = i2c_smbus_write_byte(client, arg[i]);
30962306a36Sopenharmony_ci		if (result < 0)
31062306a36Sopenharmony_ci			return result;
31162306a36Sopenharmony_ci	}
31262306a36Sopenharmony_ci	return 0;
31362306a36Sopenharmony_ci}
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_cistatic int blinkm_read(struct i2c_client *client, int cmd, u8 *arg)
31662306a36Sopenharmony_ci{
31762306a36Sopenharmony_ci	int result;
31862306a36Sopenharmony_ci	int i;
31962306a36Sopenharmony_ci	int retlen = blinkm_cmds[cmd].nr_ret;
32062306a36Sopenharmony_ci	for (i = 0; i < retlen; i++) {
32162306a36Sopenharmony_ci		/* repeat for retlen */
32262306a36Sopenharmony_ci		result = i2c_smbus_read_byte(client);
32362306a36Sopenharmony_ci		if (result < 0)
32462306a36Sopenharmony_ci			return result;
32562306a36Sopenharmony_ci		arg[i] = result;
32662306a36Sopenharmony_ci	}
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	return 0;
32962306a36Sopenharmony_ci}
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_cistatic int blinkm_transfer_hw(struct i2c_client *client, int cmd)
33262306a36Sopenharmony_ci{
33362306a36Sopenharmony_ci	/* the protocol is simple but non-standard:
33462306a36Sopenharmony_ci	 * e.g.  cmd 'g' (= 0x67) for "get device address"
33562306a36Sopenharmony_ci	 * - which defaults to 0x09 - would be the sequence:
33662306a36Sopenharmony_ci	 *   a) write 0x67 to the device (byte write)
33762306a36Sopenharmony_ci	 *   b) read the value (0x09) back right after (byte read)
33862306a36Sopenharmony_ci	 *
33962306a36Sopenharmony_ci	 * Watch out for "unfinished" sequences (i.e. not enough reads
34062306a36Sopenharmony_ci	 * or writes after a command. It will make the blinkM misbehave.
34162306a36Sopenharmony_ci	 * Sequence is key here.
34262306a36Sopenharmony_ci	 */
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	/* args / return are in private data struct */
34562306a36Sopenharmony_ci	struct blinkm_data *data = i2c_get_clientdata(client);
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	/* We start hardware transfers which are not to be
34862306a36Sopenharmony_ci	 * mixed with other commands. Aquire a lock now. */
34962306a36Sopenharmony_ci	if (mutex_lock_interruptible(&data->update_lock) < 0)
35062306a36Sopenharmony_ci		return -EAGAIN;
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	/* switch cmd - usually write before reads */
35362306a36Sopenharmony_ci	switch (cmd) {
35462306a36Sopenharmony_ci	case BLM_FADE_RAND_RGB:
35562306a36Sopenharmony_ci	case BLM_GO_RGB:
35662306a36Sopenharmony_ci	case BLM_FADE_RGB:
35762306a36Sopenharmony_ci		data->args[0] = data->next_red;
35862306a36Sopenharmony_ci		data->args[1] = data->next_green;
35962306a36Sopenharmony_ci		data->args[2] = data->next_blue;
36062306a36Sopenharmony_ci		blinkm_write(client, cmd, data->args);
36162306a36Sopenharmony_ci		data->red = data->args[0];
36262306a36Sopenharmony_ci		data->green = data->args[1];
36362306a36Sopenharmony_ci		data->blue = data->args[2];
36462306a36Sopenharmony_ci		break;
36562306a36Sopenharmony_ci	case BLM_FADE_HSB:
36662306a36Sopenharmony_ci	case BLM_FADE_RAND_HSB:
36762306a36Sopenharmony_ci		data->args[0] = data->next_hue;
36862306a36Sopenharmony_ci		data->args[1] = data->next_saturation;
36962306a36Sopenharmony_ci		data->args[2] = data->next_brightness;
37062306a36Sopenharmony_ci		blinkm_write(client, cmd, data->args);
37162306a36Sopenharmony_ci		data->hue = data->next_hue;
37262306a36Sopenharmony_ci		data->saturation = data->next_saturation;
37362306a36Sopenharmony_ci		data->brightness = data->next_brightness;
37462306a36Sopenharmony_ci		break;
37562306a36Sopenharmony_ci	case BLM_PLAY_SCRIPT:
37662306a36Sopenharmony_ci		data->args[0] = data->script_id;
37762306a36Sopenharmony_ci		data->args[1] = data->script_repeats;
37862306a36Sopenharmony_ci		data->args[2] = data->script_startline;
37962306a36Sopenharmony_ci		blinkm_write(client, cmd, data->args);
38062306a36Sopenharmony_ci		break;
38162306a36Sopenharmony_ci	case BLM_STOP_SCRIPT:
38262306a36Sopenharmony_ci		blinkm_write(client, cmd, NULL);
38362306a36Sopenharmony_ci		break;
38462306a36Sopenharmony_ci	case BLM_GET_CUR_RGB:
38562306a36Sopenharmony_ci		data->args[0] = data->red;
38662306a36Sopenharmony_ci		data->args[1] = data->green;
38762306a36Sopenharmony_ci		data->args[2] = data->blue;
38862306a36Sopenharmony_ci		blinkm_write(client, cmd, NULL);
38962306a36Sopenharmony_ci		blinkm_read(client, cmd, data->args);
39062306a36Sopenharmony_ci		data->red = data->args[0];
39162306a36Sopenharmony_ci		data->green = data->args[1];
39262306a36Sopenharmony_ci		data->blue = data->args[2];
39362306a36Sopenharmony_ci		break;
39462306a36Sopenharmony_ci	case BLM_GET_ADDR:
39562306a36Sopenharmony_ci		data->args[0] = data->i2c_addr;
39662306a36Sopenharmony_ci		blinkm_write(client, cmd, NULL);
39762306a36Sopenharmony_ci		blinkm_read(client, cmd, data->args);
39862306a36Sopenharmony_ci		data->i2c_addr = data->args[0];
39962306a36Sopenharmony_ci		break;
40062306a36Sopenharmony_ci	case BLM_SET_TIME_ADJ:
40162306a36Sopenharmony_ci	case BLM_SET_FADE_SPEED:
40262306a36Sopenharmony_ci	case BLM_READ_SCRIPT_LINE:
40362306a36Sopenharmony_ci	case BLM_WRITE_SCRIPT_LINE:
40462306a36Sopenharmony_ci	case BLM_SET_SCRIPT_LR:
40562306a36Sopenharmony_ci	case BLM_SET_ADDR:
40662306a36Sopenharmony_ci	case BLM_GET_FW_VER:
40762306a36Sopenharmony_ci	case BLM_SET_STARTUP_PARAM:
40862306a36Sopenharmony_ci		dev_err(&client->dev,
40962306a36Sopenharmony_ci				"BlinkM: cmd %d not implemented yet.\n", cmd);
41062306a36Sopenharmony_ci		break;
41162306a36Sopenharmony_ci	default:
41262306a36Sopenharmony_ci		dev_err(&client->dev, "BlinkM: unknown command %d\n", cmd);
41362306a36Sopenharmony_ci		mutex_unlock(&data->update_lock);
41462306a36Sopenharmony_ci		return -EINVAL;
41562306a36Sopenharmony_ci	}			/* end switch(cmd) */
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	/* transfers done, unlock */
41862306a36Sopenharmony_ci	mutex_unlock(&data->update_lock);
41962306a36Sopenharmony_ci	return 0;
42062306a36Sopenharmony_ci}
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_cistatic int blinkm_led_common_set(struct led_classdev *led_cdev,
42362306a36Sopenharmony_ci				 enum led_brightness value, int color)
42462306a36Sopenharmony_ci{
42562306a36Sopenharmony_ci	/* led_brightness is 0, 127 or 255 - we just use it here as-is */
42662306a36Sopenharmony_ci	struct blinkm_led *led = cdev_to_blmled(led_cdev);
42762306a36Sopenharmony_ci	struct blinkm_data *data = i2c_get_clientdata(led->i2c_client);
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	switch (color) {
43062306a36Sopenharmony_ci	case RED:
43162306a36Sopenharmony_ci		/* bail out if there's no change */
43262306a36Sopenharmony_ci		if (data->next_red == (u8) value)
43362306a36Sopenharmony_ci			return 0;
43462306a36Sopenharmony_ci		data->next_red = (u8) value;
43562306a36Sopenharmony_ci		break;
43662306a36Sopenharmony_ci	case GREEN:
43762306a36Sopenharmony_ci		/* bail out if there's no change */
43862306a36Sopenharmony_ci		if (data->next_green == (u8) value)
43962306a36Sopenharmony_ci			return 0;
44062306a36Sopenharmony_ci		data->next_green = (u8) value;
44162306a36Sopenharmony_ci		break;
44262306a36Sopenharmony_ci	case BLUE:
44362306a36Sopenharmony_ci		/* bail out if there's no change */
44462306a36Sopenharmony_ci		if (data->next_blue == (u8) value)
44562306a36Sopenharmony_ci			return 0;
44662306a36Sopenharmony_ci		data->next_blue = (u8) value;
44762306a36Sopenharmony_ci		break;
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	default:
45062306a36Sopenharmony_ci		dev_err(&led->i2c_client->dev, "BlinkM: unknown color.\n");
45162306a36Sopenharmony_ci		return -EINVAL;
45262306a36Sopenharmony_ci	}
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	blinkm_transfer_hw(led->i2c_client, BLM_GO_RGB);
45562306a36Sopenharmony_ci	dev_dbg(&led->i2c_client->dev,
45662306a36Sopenharmony_ci			"# DONE # next_red = %d, next_green = %d,"
45762306a36Sopenharmony_ci			" next_blue = %d\n",
45862306a36Sopenharmony_ci			data->next_red, data->next_green,
45962306a36Sopenharmony_ci			data->next_blue);
46062306a36Sopenharmony_ci	return 0;
46162306a36Sopenharmony_ci}
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_cistatic int blinkm_led_red_set(struct led_classdev *led_cdev,
46462306a36Sopenharmony_ci			       enum led_brightness value)
46562306a36Sopenharmony_ci{
46662306a36Sopenharmony_ci	return blinkm_led_common_set(led_cdev, value, RED);
46762306a36Sopenharmony_ci}
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_cistatic int blinkm_led_green_set(struct led_classdev *led_cdev,
47062306a36Sopenharmony_ci				 enum led_brightness value)
47162306a36Sopenharmony_ci{
47262306a36Sopenharmony_ci	return blinkm_led_common_set(led_cdev, value, GREEN);
47362306a36Sopenharmony_ci}
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_cistatic int blinkm_led_blue_set(struct led_classdev *led_cdev,
47662306a36Sopenharmony_ci				enum led_brightness value)
47762306a36Sopenharmony_ci{
47862306a36Sopenharmony_ci	return blinkm_led_common_set(led_cdev, value, BLUE);
47962306a36Sopenharmony_ci}
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_cistatic void blinkm_init_hw(struct i2c_client *client)
48262306a36Sopenharmony_ci{
48362306a36Sopenharmony_ci	blinkm_transfer_hw(client, BLM_STOP_SCRIPT);
48462306a36Sopenharmony_ci	blinkm_transfer_hw(client, BLM_GO_RGB);
48562306a36Sopenharmony_ci}
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_cistatic int blinkm_test_run(struct i2c_client *client)
48862306a36Sopenharmony_ci{
48962306a36Sopenharmony_ci	int ret;
49062306a36Sopenharmony_ci	struct blinkm_data *data = i2c_get_clientdata(client);
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	data->next_red = 0x01;
49362306a36Sopenharmony_ci	data->next_green = 0x05;
49462306a36Sopenharmony_ci	data->next_blue = 0x10;
49562306a36Sopenharmony_ci	ret = blinkm_transfer_hw(client, BLM_GO_RGB);
49662306a36Sopenharmony_ci	if (ret < 0)
49762306a36Sopenharmony_ci		return ret;
49862306a36Sopenharmony_ci	msleep(2000);
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	data->next_red = 0x25;
50162306a36Sopenharmony_ci	data->next_green = 0x10;
50262306a36Sopenharmony_ci	data->next_blue = 0x31;
50362306a36Sopenharmony_ci	ret = blinkm_transfer_hw(client, BLM_FADE_RGB);
50462306a36Sopenharmony_ci	if (ret < 0)
50562306a36Sopenharmony_ci		return ret;
50662306a36Sopenharmony_ci	msleep(2000);
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci	data->next_hue = 0x50;
50962306a36Sopenharmony_ci	data->next_saturation = 0x10;
51062306a36Sopenharmony_ci	data->next_brightness = 0x20;
51162306a36Sopenharmony_ci	ret = blinkm_transfer_hw(client, BLM_FADE_HSB);
51262306a36Sopenharmony_ci	if (ret < 0)
51362306a36Sopenharmony_ci		return ret;
51462306a36Sopenharmony_ci	msleep(2000);
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	return 0;
51762306a36Sopenharmony_ci}
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci/* Return 0 if detection is successful, -ENODEV otherwise */
52062306a36Sopenharmony_cistatic int blinkm_detect(struct i2c_client *client, struct i2c_board_info *info)
52162306a36Sopenharmony_ci{
52262306a36Sopenharmony_ci	struct i2c_adapter *adapter = client->adapter;
52362306a36Sopenharmony_ci	int ret;
52462306a36Sopenharmony_ci	int count = 99;
52562306a36Sopenharmony_ci	u8 tmpargs[7];
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA
52862306a36Sopenharmony_ci				     | I2C_FUNC_SMBUS_WORD_DATA
52962306a36Sopenharmony_ci				     | I2C_FUNC_SMBUS_WRITE_BYTE))
53062306a36Sopenharmony_ci		return -ENODEV;
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	/* Now, we do the remaining detection. Simple for now. */
53362306a36Sopenharmony_ci	/* We might need more guards to protect other i2c slaves */
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	/* make sure the blinkM is balanced (read/writes) */
53662306a36Sopenharmony_ci	while (count > 0) {
53762306a36Sopenharmony_ci		ret = blinkm_write(client, BLM_GET_ADDR, NULL);
53862306a36Sopenharmony_ci		if (ret)
53962306a36Sopenharmony_ci			return ret;
54062306a36Sopenharmony_ci		usleep_range(5000, 10000);
54162306a36Sopenharmony_ci		ret = blinkm_read(client, BLM_GET_ADDR, tmpargs);
54262306a36Sopenharmony_ci		if (ret)
54362306a36Sopenharmony_ci			return ret;
54462306a36Sopenharmony_ci		usleep_range(5000, 10000);
54562306a36Sopenharmony_ci		if (tmpargs[0] == 0x09)
54662306a36Sopenharmony_ci			count = 0;
54762306a36Sopenharmony_ci		count--;
54862306a36Sopenharmony_ci	}
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	/* Step 1: Read BlinkM address back  -  cmd_char 'a' */
55162306a36Sopenharmony_ci	ret = blinkm_write(client, BLM_GET_ADDR, NULL);
55262306a36Sopenharmony_ci	if (ret < 0)
55362306a36Sopenharmony_ci		return ret;
55462306a36Sopenharmony_ci	usleep_range(20000, 30000);	/* allow a small delay */
55562306a36Sopenharmony_ci	ret = blinkm_read(client, BLM_GET_ADDR, tmpargs);
55662306a36Sopenharmony_ci	if (ret < 0)
55762306a36Sopenharmony_ci		return ret;
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	if (tmpargs[0] != 0x09) {
56062306a36Sopenharmony_ci		dev_err(&client->dev, "enodev DEV ADDR = 0x%02X\n", tmpargs[0]);
56162306a36Sopenharmony_ci		return -ENODEV;
56262306a36Sopenharmony_ci	}
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	strscpy(info->type, "blinkm", I2C_NAME_SIZE);
56562306a36Sopenharmony_ci	return 0;
56662306a36Sopenharmony_ci}
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_cistatic int blinkm_probe(struct i2c_client *client)
56962306a36Sopenharmony_ci{
57062306a36Sopenharmony_ci	struct blinkm_data *data;
57162306a36Sopenharmony_ci	struct blinkm_led *led[3];
57262306a36Sopenharmony_ci	int err, i;
57362306a36Sopenharmony_ci	char blinkm_led_name[28];
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci	data = devm_kzalloc(&client->dev,
57662306a36Sopenharmony_ci			sizeof(struct blinkm_data), GFP_KERNEL);
57762306a36Sopenharmony_ci	if (!data) {
57862306a36Sopenharmony_ci		err = -ENOMEM;
57962306a36Sopenharmony_ci		goto exit;
58062306a36Sopenharmony_ci	}
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci	data->i2c_addr = 0x08;
58362306a36Sopenharmony_ci	/* i2c addr  - use fake addr of 0x08 initially (real is 0x09) */
58462306a36Sopenharmony_ci	data->fw_ver = 0xfe;
58562306a36Sopenharmony_ci	/* firmware version - use fake until we read real value
58662306a36Sopenharmony_ci	 * (currently broken - BlinkM confused!) */
58762306a36Sopenharmony_ci	data->script_id = 0x01;
58862306a36Sopenharmony_ci	data->i2c_client = client;
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci	i2c_set_clientdata(client, data);
59162306a36Sopenharmony_ci	mutex_init(&data->update_lock);
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci	/* Register sysfs hooks */
59462306a36Sopenharmony_ci	err = sysfs_create_group(&client->dev.kobj, &blinkm_group);
59562306a36Sopenharmony_ci	if (err < 0) {
59662306a36Sopenharmony_ci		dev_err(&client->dev, "couldn't register sysfs group\n");
59762306a36Sopenharmony_ci		goto exit;
59862306a36Sopenharmony_ci	}
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	for (i = 0; i < 3; i++) {
60162306a36Sopenharmony_ci		/* RED = 0, GREEN = 1, BLUE = 2 */
60262306a36Sopenharmony_ci		led[i] = &data->blinkm_leds[i];
60362306a36Sopenharmony_ci		led[i]->i2c_client = client;
60462306a36Sopenharmony_ci		led[i]->id = i;
60562306a36Sopenharmony_ci		led[i]->led_cdev.max_brightness = 255;
60662306a36Sopenharmony_ci		led[i]->led_cdev.flags = LED_CORE_SUSPENDRESUME;
60762306a36Sopenharmony_ci		switch (i) {
60862306a36Sopenharmony_ci		case RED:
60962306a36Sopenharmony_ci			snprintf(blinkm_led_name, sizeof(blinkm_led_name),
61062306a36Sopenharmony_ci					 "blinkm-%d-%d-red",
61162306a36Sopenharmony_ci					 client->adapter->nr,
61262306a36Sopenharmony_ci					 client->addr);
61362306a36Sopenharmony_ci			led[i]->led_cdev.name = blinkm_led_name;
61462306a36Sopenharmony_ci			led[i]->led_cdev.brightness_set_blocking =
61562306a36Sopenharmony_ci							blinkm_led_red_set;
61662306a36Sopenharmony_ci			err = led_classdev_register(&client->dev,
61762306a36Sopenharmony_ci						    &led[i]->led_cdev);
61862306a36Sopenharmony_ci			if (err < 0) {
61962306a36Sopenharmony_ci				dev_err(&client->dev,
62062306a36Sopenharmony_ci					"couldn't register LED %s\n",
62162306a36Sopenharmony_ci					led[i]->led_cdev.name);
62262306a36Sopenharmony_ci				goto failred;
62362306a36Sopenharmony_ci			}
62462306a36Sopenharmony_ci			break;
62562306a36Sopenharmony_ci		case GREEN:
62662306a36Sopenharmony_ci			snprintf(blinkm_led_name, sizeof(blinkm_led_name),
62762306a36Sopenharmony_ci					 "blinkm-%d-%d-green",
62862306a36Sopenharmony_ci					 client->adapter->nr,
62962306a36Sopenharmony_ci					 client->addr);
63062306a36Sopenharmony_ci			led[i]->led_cdev.name = blinkm_led_name;
63162306a36Sopenharmony_ci			led[i]->led_cdev.brightness_set_blocking =
63262306a36Sopenharmony_ci							blinkm_led_green_set;
63362306a36Sopenharmony_ci			err = led_classdev_register(&client->dev,
63462306a36Sopenharmony_ci						    &led[i]->led_cdev);
63562306a36Sopenharmony_ci			if (err < 0) {
63662306a36Sopenharmony_ci				dev_err(&client->dev,
63762306a36Sopenharmony_ci					"couldn't register LED %s\n",
63862306a36Sopenharmony_ci					led[i]->led_cdev.name);
63962306a36Sopenharmony_ci				goto failgreen;
64062306a36Sopenharmony_ci			}
64162306a36Sopenharmony_ci			break;
64262306a36Sopenharmony_ci		case BLUE:
64362306a36Sopenharmony_ci			snprintf(blinkm_led_name, sizeof(blinkm_led_name),
64462306a36Sopenharmony_ci					 "blinkm-%d-%d-blue",
64562306a36Sopenharmony_ci					 client->adapter->nr,
64662306a36Sopenharmony_ci					 client->addr);
64762306a36Sopenharmony_ci			led[i]->led_cdev.name = blinkm_led_name;
64862306a36Sopenharmony_ci			led[i]->led_cdev.brightness_set_blocking =
64962306a36Sopenharmony_ci							blinkm_led_blue_set;
65062306a36Sopenharmony_ci			err = led_classdev_register(&client->dev,
65162306a36Sopenharmony_ci						    &led[i]->led_cdev);
65262306a36Sopenharmony_ci			if (err < 0) {
65362306a36Sopenharmony_ci				dev_err(&client->dev,
65462306a36Sopenharmony_ci					"couldn't register LED %s\n",
65562306a36Sopenharmony_ci					led[i]->led_cdev.name);
65662306a36Sopenharmony_ci				goto failblue;
65762306a36Sopenharmony_ci			}
65862306a36Sopenharmony_ci			break;
65962306a36Sopenharmony_ci		}		/* end switch */
66062306a36Sopenharmony_ci	}			/* end for */
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	/* Initialize the blinkm */
66362306a36Sopenharmony_ci	blinkm_init_hw(client);
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci	return 0;
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_cifailblue:
66862306a36Sopenharmony_ci	led_classdev_unregister(&led[GREEN]->led_cdev);
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_cifailgreen:
67162306a36Sopenharmony_ci	led_classdev_unregister(&led[RED]->led_cdev);
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_cifailred:
67462306a36Sopenharmony_ci	sysfs_remove_group(&client->dev.kobj, &blinkm_group);
67562306a36Sopenharmony_ciexit:
67662306a36Sopenharmony_ci	return err;
67762306a36Sopenharmony_ci}
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_cistatic void blinkm_remove(struct i2c_client *client)
68062306a36Sopenharmony_ci{
68162306a36Sopenharmony_ci	struct blinkm_data *data = i2c_get_clientdata(client);
68262306a36Sopenharmony_ci	int ret = 0;
68362306a36Sopenharmony_ci	int i;
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci	/* make sure no workqueue entries are pending */
68662306a36Sopenharmony_ci	for (i = 0; i < 3; i++)
68762306a36Sopenharmony_ci		led_classdev_unregister(&data->blinkm_leds[i].led_cdev);
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	/* reset rgb */
69062306a36Sopenharmony_ci	data->next_red = 0x00;
69162306a36Sopenharmony_ci	data->next_green = 0x00;
69262306a36Sopenharmony_ci	data->next_blue = 0x00;
69362306a36Sopenharmony_ci	ret = blinkm_transfer_hw(client, BLM_FADE_RGB);
69462306a36Sopenharmony_ci	if (ret < 0)
69562306a36Sopenharmony_ci		dev_err(&client->dev, "Failure in blinkm_remove ignored. Continuing.\n");
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci	/* reset hsb */
69862306a36Sopenharmony_ci	data->next_hue = 0x00;
69962306a36Sopenharmony_ci	data->next_saturation = 0x00;
70062306a36Sopenharmony_ci	data->next_brightness = 0x00;
70162306a36Sopenharmony_ci	ret = blinkm_transfer_hw(client, BLM_FADE_HSB);
70262306a36Sopenharmony_ci	if (ret < 0)
70362306a36Sopenharmony_ci		dev_err(&client->dev, "Failure in blinkm_remove ignored. Continuing.\n");
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci	/* red fade to off */
70662306a36Sopenharmony_ci	data->next_red = 0xff;
70762306a36Sopenharmony_ci	ret = blinkm_transfer_hw(client, BLM_GO_RGB);
70862306a36Sopenharmony_ci	if (ret < 0)
70962306a36Sopenharmony_ci		dev_err(&client->dev, "Failure in blinkm_remove ignored. Continuing.\n");
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci	/* off */
71262306a36Sopenharmony_ci	data->next_red = 0x00;
71362306a36Sopenharmony_ci	ret = blinkm_transfer_hw(client, BLM_FADE_RGB);
71462306a36Sopenharmony_ci	if (ret < 0)
71562306a36Sopenharmony_ci		dev_err(&client->dev, "Failure in blinkm_remove ignored. Continuing.\n");
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci	sysfs_remove_group(&client->dev.kobj, &blinkm_group);
71862306a36Sopenharmony_ci}
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_cistatic const struct i2c_device_id blinkm_id[] = {
72162306a36Sopenharmony_ci	{"blinkm", 0},
72262306a36Sopenharmony_ci	{}
72362306a36Sopenharmony_ci};
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, blinkm_id);
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci  /* This is the driver that will be inserted */
72862306a36Sopenharmony_cistatic struct i2c_driver blinkm_driver = {
72962306a36Sopenharmony_ci	.class = I2C_CLASS_HWMON,
73062306a36Sopenharmony_ci	.driver = {
73162306a36Sopenharmony_ci		   .name = "blinkm",
73262306a36Sopenharmony_ci		   },
73362306a36Sopenharmony_ci	.probe = blinkm_probe,
73462306a36Sopenharmony_ci	.remove = blinkm_remove,
73562306a36Sopenharmony_ci	.id_table = blinkm_id,
73662306a36Sopenharmony_ci	.detect = blinkm_detect,
73762306a36Sopenharmony_ci	.address_list = normal_i2c,
73862306a36Sopenharmony_ci};
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_cimodule_i2c_driver(blinkm_driver);
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ciMODULE_AUTHOR("Jan-Simon Moeller <dl9pf@gmx.de>");
74362306a36Sopenharmony_ciMODULE_DESCRIPTION("BlinkM RGB LED driver");
74462306a36Sopenharmony_ciMODULE_LICENSE("GPL");
74562306a36Sopenharmony_ci
746