brintos

brintos / linux-shallow public Read only

0
0
Text · 21.0 KiB · 577497b Raw
834 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 *  leds-blinkm.c4 *  (c) Jan-Simon Möller (dl9pf@gmx.de)5 *  (c) Joseph Strauss (jstrauss@mailbox.org)6 */7 8#include <linux/module.h>9#include <linux/slab.h>10#include <linux/jiffies.h>11#include <linux/i2c.h>12#include <linux/err.h>13#include <linux/mutex.h>14#include <linux/sysfs.h>15#include <linux/printk.h>16#include <linux/pm_runtime.h>17#include <linux/leds.h>18#include <linux/delay.h>19#include <linux/led-class-multicolor.h>20#include <linux/kconfig.h>21 22#define NUM_LEDS 323 24/* Addresses to scan - BlinkM is on 0x09 by default*/25static const unsigned short normal_i2c[] = { 0x09, I2C_CLIENT_END };26 27static int blinkm_transfer_hw(struct i2c_client *client, int cmd);28static int blinkm_test_run(struct i2c_client *client);29 30/* Contains structs for both the color-separated sysfs classes, and the new multicolor class */31struct blinkm_led {32	struct i2c_client *i2c_client;33	union {34		/* used when multicolor support is disabled */35		struct led_classdev led_cdev;36		struct led_classdev_mc mcled_cdev;37	} cdev;38	int id;39};40 41#define led_cdev_to_blmled(c)			container_of(c, struct blinkm_led, cdev.led_cdev)42#define mcled_cdev_to_led(c)			container_of(c, struct blinkm_led, cdev.mcled_cdev)43 44struct blinkm_data {45	struct i2c_client *i2c_client;46	struct mutex update_lock;47	/* used for led class interface */48	struct blinkm_led blinkm_leds[NUM_LEDS];49	/* used for "blinkm" sysfs interface */50	u8 red;			/* color red */51	u8 green;		/* color green */52	u8 blue;		/* color blue */53	/* next values to use for transfer */54	u8 next_red;			/* color red */55	u8 next_green;		/* color green */56	u8 next_blue;		/* color blue */57	/* internal use */58	u8 args[7];		/* set of args for transmission */59	u8 i2c_addr;		/* i2c addr */60	u8 fw_ver;		/* firmware version */61	/* used, but not from userspace */62	u8 hue;			/* HSB  hue */63	u8 saturation;		/* HSB  saturation */64	u8 brightness;		/* HSB  brightness */65	u8 next_hue;			/* HSB  hue */66	u8 next_saturation;		/* HSB  saturation */67	u8 next_brightness;		/* HSB  brightness */68	/* currently unused / todo */69	u8 fade_speed;		/* fade speed     1 - 255 */70	s8 time_adjust;		/* time adjust -128 - 127 */71	u8 fade:1;		/* fade on = 1, off = 0 */72	u8 rand:1;		/* rand fade mode on = 1 */73	u8 script_id;		/* script ID */74	u8 script_repeats;	/* repeats of script */75	u8 script_startline;	/* line to start */76};77 78/* Colors */79#define RED   080#define GREEN 181#define BLUE  282 83/* mapping command names to cmd chars - see datasheet */84#define BLM_GO_RGB            085#define BLM_FADE_RGB          186#define BLM_FADE_HSB          287#define BLM_FADE_RAND_RGB     388#define BLM_FADE_RAND_HSB     489#define BLM_PLAY_SCRIPT       590#define BLM_STOP_SCRIPT       691#define BLM_SET_FADE_SPEED    792#define BLM_SET_TIME_ADJ      893#define BLM_GET_CUR_RGB       994#define BLM_WRITE_SCRIPT_LINE 1095#define BLM_READ_SCRIPT_LINE  1196#define BLM_SET_SCRIPT_LR     12	/* Length & Repeats */97#define BLM_SET_ADDR          1398#define BLM_GET_ADDR          1499#define BLM_GET_FW_VER        15100#define BLM_SET_STARTUP_PARAM 16101 102/* BlinkM Commands103 *  as extracted out of the datasheet:104 *105 *  cmdchar = command (ascii)106 *  cmdbyte = command in hex107 *  nr_args = number of arguments (to send)108 *  nr_ret  = number of return values (to read)109 *  dir = direction (0 = read, 1 = write, 2 = both)110 *111 */112static const struct {113	char cmdchar;114	u8 cmdbyte;115	u8 nr_args;116	u8 nr_ret;117	u8 dir:2;118} blinkm_cmds[17] = {119  /* cmdchar, cmdbyte, nr_args, nr_ret,  dir */120	{ 'n', 0x6e, 3, 0, 1},121	{ 'c', 0x63, 3, 0, 1},122	{ 'h', 0x68, 3, 0, 1},123	{ 'C', 0x43, 3, 0, 1},124	{ 'H', 0x48, 3, 0, 1},125	{ 'p', 0x70, 3, 0, 1},126	{ 'o', 0x6f, 0, 0, 1},127	{ 'f', 0x66, 1, 0, 1},128	{ 't', 0x74, 1, 0, 1},129	{ 'g', 0x67, 0, 3, 0},130	{ 'W', 0x57, 7, 0, 1},131	{ 'R', 0x52, 2, 5, 2},132	{ 'L', 0x4c, 3, 0, 1},133	{ 'A', 0x41, 4, 0, 1},134	{ 'a', 0x61, 0, 1, 0},135	{ 'Z', 0x5a, 0, 1, 0},136	{ 'B', 0x42, 5, 0, 1},137};138 139static ssize_t show_color_common(struct device *dev, char *buf, int color)140{141	struct i2c_client *client;142	struct blinkm_data *data;143	int ret;144 145	client = to_i2c_client(dev);146	data = i2c_get_clientdata(client);147 148	ret = blinkm_transfer_hw(client, BLM_GET_CUR_RGB);149	if (ret < 0)150		return ret;151	switch (color) {152	case RED:153		return sysfs_emit(buf, "%02X\n", data->red);154	case GREEN:155		return sysfs_emit(buf, "%02X\n", data->green);156	case BLUE:157		return sysfs_emit(buf, "%02X\n", data->blue);158	default:159		return -EINVAL;160	}161	return -EINVAL;162}163 164static int store_color_common(struct device *dev, const char *buf, int color)165{166	struct i2c_client *client;167	struct blinkm_data *data;168	int ret;169	u8 value;170 171	client = to_i2c_client(dev);172	data = i2c_get_clientdata(client);173 174	ret = kstrtou8(buf, 10, &value);175	if (ret < 0) {176		dev_err(dev, "BlinkM: value too large!\n");177		return ret;178	}179 180	switch (color) {181	case RED:182		data->next_red = value;183		break;184	case GREEN:185		data->next_green = value;186		break;187	case BLUE:188		data->next_blue = value;189		break;190	default:191		return -EINVAL;192	}193 194	dev_dbg(dev, "next_red = %d, next_green = %d, next_blue = %d\n",195			data->next_red, data->next_green, data->next_blue);196 197	/* if mode ... */198	ret = blinkm_transfer_hw(client, BLM_GO_RGB);199	if (ret < 0) {200		dev_err(dev, "BlinkM: can't set RGB\n");201		return ret;202	}203	return 0;204}205 206static ssize_t red_show(struct device *dev, struct device_attribute *attr,207			char *buf)208{209	return show_color_common(dev, buf, RED);210}211 212static ssize_t red_store(struct device *dev, struct device_attribute *attr,213			 const char *buf, size_t count)214{215	int ret;216 217	ret = store_color_common(dev, buf, RED);218	if (ret < 0)219		return ret;220	return count;221}222 223static DEVICE_ATTR_RW(red);224 225static ssize_t green_show(struct device *dev, struct device_attribute *attr,226			  char *buf)227{228	return show_color_common(dev, buf, GREEN);229}230 231static ssize_t green_store(struct device *dev, struct device_attribute *attr,232			   const char *buf, size_t count)233{234 235	int ret;236 237	ret = store_color_common(dev, buf, GREEN);238	if (ret < 0)239		return ret;240	return count;241}242 243static DEVICE_ATTR_RW(green);244 245static ssize_t blue_show(struct device *dev, struct device_attribute *attr,246			 char *buf)247{248	return show_color_common(dev, buf, BLUE);249}250 251static ssize_t blue_store(struct device *dev, struct device_attribute *attr,252			  const char *buf, size_t count)253{254	int ret;255 256	ret = store_color_common(dev, buf, BLUE);257	if (ret < 0)258		return ret;259	return count;260}261 262static DEVICE_ATTR_RW(blue);263 264static ssize_t test_show(struct device *dev, struct device_attribute *attr,265			 char *buf)266{267	return sysfs_emit(buf,268			 "#Write into test to start test sequence!#\n");269}270 271static ssize_t test_store(struct device *dev, struct device_attribute *attr,272			  const char *buf, size_t count)273{274 275	struct i2c_client *client;276	int ret;277	client = to_i2c_client(dev);278 279	/*test */280	ret = blinkm_test_run(client);281	if (ret < 0)282		return ret;283 284	return count;285}286 287static DEVICE_ATTR_RW(test);288 289/* TODO: HSB, fade, timeadj, script ... */290 291static struct attribute *blinkm_attrs[] = {292	&dev_attr_red.attr,293	&dev_attr_green.attr,294	&dev_attr_blue.attr,295	&dev_attr_test.attr,296	NULL,297};298 299static const struct attribute_group blinkm_group = {300	.name = "blinkm",301	.attrs = blinkm_attrs,302};303 304static int blinkm_write(struct i2c_client *client, int cmd, u8 *arg)305{306	int result;307	int i;308	int arglen = blinkm_cmds[cmd].nr_args;309	/* write out cmd to blinkm - always / default step */310	result = i2c_smbus_write_byte(client, blinkm_cmds[cmd].cmdbyte);311	if (result < 0)312		return result;313	/* no args to write out */314	if (arglen == 0)315		return 0;316 317	for (i = 0; i < arglen; i++) {318		/* repeat for arglen */319		result = i2c_smbus_write_byte(client, arg[i]);320		if (result < 0)321			return result;322	}323	return 0;324}325 326static int blinkm_read(struct i2c_client *client, int cmd, u8 *arg)327{328	int result;329	int i;330	int retlen = blinkm_cmds[cmd].nr_ret;331	for (i = 0; i < retlen; i++) {332		/* repeat for retlen */333		result = i2c_smbus_read_byte(client);334		if (result < 0)335			return result;336		arg[i] = result;337	}338 339	return 0;340}341 342static int blinkm_transfer_hw(struct i2c_client *client, int cmd)343{344	/* the protocol is simple but non-standard:345	 * e.g.  cmd 'g' (= 0x67) for "get device address"346	 * - which defaults to 0x09 - would be the sequence:347	 *   a) write 0x67 to the device (byte write)348	 *   b) read the value (0x09) back right after (byte read)349	 *350	 * Watch out for "unfinished" sequences (i.e. not enough reads351	 * or writes after a command. It will make the blinkM misbehave.352	 * Sequence is key here.353	 */354 355	/* args / return are in private data struct */356	struct blinkm_data *data = i2c_get_clientdata(client);357 358	/* We start hardware transfers which are not to be359	 * mixed with other commands. Aquire a lock now. */360	if (mutex_lock_interruptible(&data->update_lock) < 0)361		return -EAGAIN;362 363	/* switch cmd - usually write before reads */364	switch (cmd) {365	case BLM_FADE_RAND_RGB:366	case BLM_GO_RGB:367	case BLM_FADE_RGB:368		data->args[0] = data->next_red;369		data->args[1] = data->next_green;370		data->args[2] = data->next_blue;371		blinkm_write(client, cmd, data->args);372		data->red = data->args[0];373		data->green = data->args[1];374		data->blue = data->args[2];375		break;376	case BLM_FADE_HSB:377	case BLM_FADE_RAND_HSB:378		data->args[0] = data->next_hue;379		data->args[1] = data->next_saturation;380		data->args[2] = data->next_brightness;381		blinkm_write(client, cmd, data->args);382		data->hue = data->next_hue;383		data->saturation = data->next_saturation;384		data->brightness = data->next_brightness;385		break;386	case BLM_PLAY_SCRIPT:387		data->args[0] = data->script_id;388		data->args[1] = data->script_repeats;389		data->args[2] = data->script_startline;390		blinkm_write(client, cmd, data->args);391		break;392	case BLM_STOP_SCRIPT:393		blinkm_write(client, cmd, NULL);394		break;395	case BLM_GET_CUR_RGB:396		data->args[0] = data->red;397		data->args[1] = data->green;398		data->args[2] = data->blue;399		blinkm_write(client, cmd, NULL);400		blinkm_read(client, cmd, data->args);401		data->red = data->args[0];402		data->green = data->args[1];403		data->blue = data->args[2];404		break;405	case BLM_GET_ADDR:406		data->args[0] = data->i2c_addr;407		blinkm_write(client, cmd, NULL);408		blinkm_read(client, cmd, data->args);409		data->i2c_addr = data->args[0];410		break;411	case BLM_SET_TIME_ADJ:412	case BLM_SET_FADE_SPEED:413	case BLM_READ_SCRIPT_LINE:414	case BLM_WRITE_SCRIPT_LINE:415	case BLM_SET_SCRIPT_LR:416	case BLM_SET_ADDR:417	case BLM_GET_FW_VER:418	case BLM_SET_STARTUP_PARAM:419		dev_err(&client->dev,420				"BlinkM: cmd %d not implemented yet.\n", cmd);421		break;422	default:423		dev_err(&client->dev, "BlinkM: unknown command %d\n", cmd);424		mutex_unlock(&data->update_lock);425		return -EINVAL;426	}			/* end switch(cmd) */427 428	/* transfers done, unlock */429	mutex_unlock(&data->update_lock);430	return 0;431}432 433static int blinkm_set_mc_brightness(struct led_classdev *led_cdev,434				 enum led_brightness value)435{436	struct led_classdev_mc *mcled_cdev = lcdev_to_mccdev(led_cdev);437	struct blinkm_led *led = mcled_cdev_to_led(mcled_cdev);438	struct blinkm_data *data = i2c_get_clientdata(led->i2c_client);439 440	led_mc_calc_color_components(mcled_cdev, value);441 442	data->next_red = (u8) mcled_cdev->subled_info[RED].brightness;443	data->next_green = (u8) mcled_cdev->subled_info[GREEN].brightness;444	data->next_blue = (u8) mcled_cdev->subled_info[BLUE].brightness;445 446	blinkm_transfer_hw(led->i2c_client, BLM_GO_RGB);447 448	return 0;449}450 451static int blinkm_led_common_set(struct led_classdev *led_cdev,452				 enum led_brightness value, int color)453{454	/* led_brightness is 0, 127 or 255 - we just use it here as-is */455	struct blinkm_led *led = led_cdev_to_blmled(led_cdev);456	struct blinkm_data *data = i2c_get_clientdata(led->i2c_client);457 458	switch (color) {459	case RED:460		/* bail out if there's no change */461		if (data->next_red == (u8) value)462			return 0;463		data->next_red = (u8) value;464		break;465	case GREEN:466		/* bail out if there's no change */467		if (data->next_green == (u8) value)468			return 0;469		data->next_green = (u8) value;470		break;471	case BLUE:472		/* bail out if there's no change */473		if (data->next_blue == (u8) value)474			return 0;475		data->next_blue = (u8) value;476		break;477 478	default:479		dev_err(&led->i2c_client->dev, "BlinkM: unknown color.\n");480		return -EINVAL;481	}482 483	blinkm_transfer_hw(led->i2c_client, BLM_GO_RGB);484	dev_dbg(&led->i2c_client->dev,485			"# DONE # next_red = %d, next_green = %d,"486			" next_blue = %d\n",487			data->next_red, data->next_green,488			data->next_blue);489	return 0;490}491 492static int blinkm_led_red_set(struct led_classdev *led_cdev,493			       enum led_brightness value)494{495	return blinkm_led_common_set(led_cdev, value, RED);496}497 498static int blinkm_led_green_set(struct led_classdev *led_cdev,499				 enum led_brightness value)500{501	return blinkm_led_common_set(led_cdev, value, GREEN);502}503 504static int blinkm_led_blue_set(struct led_classdev *led_cdev,505				enum led_brightness value)506{507	return blinkm_led_common_set(led_cdev, value, BLUE);508}509 510static void blinkm_init_hw(struct i2c_client *client)511{512	blinkm_transfer_hw(client, BLM_STOP_SCRIPT);513	blinkm_transfer_hw(client, BLM_GO_RGB);514}515 516static int blinkm_test_run(struct i2c_client *client)517{518	int ret;519	struct blinkm_data *data = i2c_get_clientdata(client);520 521	data->next_red = 0x01;522	data->next_green = 0x05;523	data->next_blue = 0x10;524	ret = blinkm_transfer_hw(client, BLM_GO_RGB);525	if (ret < 0)526		return ret;527	msleep(2000);528 529	data->next_red = 0x25;530	data->next_green = 0x10;531	data->next_blue = 0x31;532	ret = blinkm_transfer_hw(client, BLM_FADE_RGB);533	if (ret < 0)534		return ret;535	msleep(2000);536 537	data->next_hue = 0x50;538	data->next_saturation = 0x10;539	data->next_brightness = 0x20;540	ret = blinkm_transfer_hw(client, BLM_FADE_HSB);541	if (ret < 0)542		return ret;543	msleep(2000);544 545	return 0;546}547 548/* Return 0 if detection is successful, -ENODEV otherwise */549static int blinkm_detect(struct i2c_client *client, struct i2c_board_info *info)550{551	struct i2c_adapter *adapter = client->adapter;552	int ret;553	int count = 99;554	u8 tmpargs[7];555 556	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA557				     | I2C_FUNC_SMBUS_WORD_DATA558				     | I2C_FUNC_SMBUS_WRITE_BYTE))559		return -ENODEV;560 561	/* Now, we do the remaining detection. Simple for now. */562	/* We might need more guards to protect other i2c slaves */563 564	/* make sure the blinkM is balanced (read/writes) */565	while (count > 0) {566		ret = blinkm_write(client, BLM_GET_ADDR, NULL);567		if (ret)568			return ret;569		usleep_range(5000, 10000);570		ret = blinkm_read(client, BLM_GET_ADDR, tmpargs);571		if (ret)572			return ret;573		usleep_range(5000, 10000);574		if (tmpargs[0] == 0x09)575			count = 0;576		count--;577	}578 579	/* Step 1: Read BlinkM address back  -  cmd_char 'a' */580	ret = blinkm_write(client, BLM_GET_ADDR, NULL);581	if (ret < 0)582		return ret;583	usleep_range(20000, 30000);	/* allow a small delay */584	ret = blinkm_read(client, BLM_GET_ADDR, tmpargs);585	if (ret < 0)586		return ret;587 588	if (tmpargs[0] != 0x09) {589		dev_err(&client->dev, "enodev DEV ADDR = 0x%02X\n", tmpargs[0]);590		return -ENODEV;591	}592 593	strscpy(info->type, "blinkm", I2C_NAME_SIZE);594	return 0;595}596 597static int register_separate_colors(struct i2c_client *client, struct blinkm_data *data)598{599	/* 3 separate classes for red, green, and blue respectively */600	struct blinkm_led *leds[NUM_LEDS];601	int err;602	char blinkm_led_name[28];603	/* Register red, green, and blue sysfs classes */604	for (int i = 0; i < NUM_LEDS; i++) {605		/* RED = 0, GREEN = 1, BLUE = 2 */606		leds[i] = &data->blinkm_leds[i];607		leds[i]->i2c_client = client;608		leds[i]->id = i;609		leds[i]->cdev.led_cdev.max_brightness = 255;610		leds[i]->cdev.led_cdev.flags = LED_CORE_SUSPENDRESUME;611		switch (i) {612		case RED:613			scnprintf(blinkm_led_name, sizeof(blinkm_led_name),614					 "blinkm-%d-%d-red",615					 client->adapter->nr,616					 client->addr);617			leds[i]->cdev.led_cdev.name = blinkm_led_name;618			leds[i]->cdev.led_cdev.brightness_set_blocking =619							blinkm_led_red_set;620			err = led_classdev_register(&client->dev,621							&leds[i]->cdev.led_cdev);622			if (err < 0) {623				dev_err(&client->dev,624					"couldn't register LED %s\n",625					leds[i]->cdev.led_cdev.name);626				goto failred;627			}628			break;629		case GREEN:630			scnprintf(blinkm_led_name, sizeof(blinkm_led_name),631					 "blinkm-%d-%d-green",632					 client->adapter->nr,633					 client->addr);634			leds[i]->cdev.led_cdev.name = blinkm_led_name;635			leds[i]->cdev.led_cdev.brightness_set_blocking =636							blinkm_led_green_set;637			err = led_classdev_register(&client->dev,638						&leds[i]->cdev.led_cdev);639			if (err < 0) {640				dev_err(&client->dev,641					"couldn't register LED %s\n",642					leds[i]->cdev.led_cdev.name);643				goto failgreen;644			}645			break;646		case BLUE:647			scnprintf(blinkm_led_name, sizeof(blinkm_led_name),648					 "blinkm-%d-%d-blue",649					 client->adapter->nr,650					 client->addr);651			leds[i]->cdev.led_cdev.name = blinkm_led_name;652			leds[i]->cdev.led_cdev.brightness_set_blocking =653							blinkm_led_blue_set;654			err = led_classdev_register(&client->dev,655							&leds[i]->cdev.led_cdev);656			if (err < 0) {657				dev_err(&client->dev,658					"couldn't register LED %s\n",659					leds[i]->cdev.led_cdev.name);660				goto failblue;661			}662			break;663		default:664			break;665		}		/* end switch */666	}			/* end for */667	return 0;668 669failblue:670	led_classdev_unregister(&leds[GREEN]->cdev.led_cdev);671failgreen:672	led_classdev_unregister(&leds[RED]->cdev.led_cdev);673failred:674	sysfs_remove_group(&client->dev.kobj, &blinkm_group);675 676	return err;677}678 679static int register_multicolor(struct i2c_client *client, struct blinkm_data *data)680{681	struct blinkm_led *mc_led;682	struct mc_subled *mc_led_info;683	char blinkm_led_name[28];684	int err;685 686	/* Register multicolor sysfs class */687	/* The first element of leds is used for multicolor facilities */688	mc_led = &data->blinkm_leds[RED];689	mc_led->i2c_client = client;690 691	mc_led_info = devm_kcalloc(&client->dev, NUM_LEDS, sizeof(*mc_led_info),692					GFP_KERNEL);693	if (!mc_led_info)694		return -ENOMEM;695 696	mc_led_info[RED].color_index = LED_COLOR_ID_RED;697	mc_led_info[GREEN].color_index = LED_COLOR_ID_GREEN;698	mc_led_info[BLUE].color_index = LED_COLOR_ID_BLUE;699 700	mc_led->cdev.mcled_cdev.subled_info = mc_led_info;701	mc_led->cdev.mcled_cdev.num_colors = NUM_LEDS;702	mc_led->cdev.mcled_cdev.led_cdev.brightness = 255;703	mc_led->cdev.mcled_cdev.led_cdev.max_brightness = 255;704	mc_led->cdev.mcled_cdev.led_cdev.flags = LED_CORE_SUSPENDRESUME;705 706	scnprintf(blinkm_led_name, sizeof(blinkm_led_name),707		 "blinkm-%d-%d:rgb:indicator",708		 client->adapter->nr,709		 client->addr);710	mc_led->cdev.mcled_cdev.led_cdev.name = blinkm_led_name;711	mc_led->cdev.mcled_cdev.led_cdev.brightness_set_blocking = blinkm_set_mc_brightness;712 713	err = led_classdev_multicolor_register(&client->dev, &mc_led->cdev.mcled_cdev);714	if (err < 0) {715		dev_err(&client->dev, "couldn't register LED %s\n",716				mc_led->cdev.led_cdev.name);717		sysfs_remove_group(&client->dev.kobj, &blinkm_group);718	}719	return 0;720}721 722static int blinkm_probe(struct i2c_client *client)723{724	struct blinkm_data *data;725	int err;726 727	data = devm_kzalloc(&client->dev,728			sizeof(struct blinkm_data), GFP_KERNEL);729	if (!data)730		return -ENOMEM;731 732	data->i2c_addr = 0x08;733	/* i2c addr  - use fake addr of 0x08 initially (real is 0x09) */734	data->fw_ver = 0xfe;735	/* firmware version - use fake until we read real value736	 * (currently broken - BlinkM confused!)737	 */738	data->script_id = 0x01;739	data->i2c_client = client;740 741	i2c_set_clientdata(client, data);742	mutex_init(&data->update_lock);743 744	/* Register sysfs hooks */745	err = sysfs_create_group(&client->dev.kobj, &blinkm_group);746	if (err < 0) {747		dev_err(&client->dev, "couldn't register sysfs group\n");748		return err;749	}750 751	if (!IS_ENABLED(CONFIG_LEDS_BLINKM_MULTICOLOR)) {752		err = register_separate_colors(client, data);753		if (err < 0)754			return err;755	} else {756		err = register_multicolor(client, data);757		if (err < 0)758			return err;759	}760 761	blinkm_init_hw(client);762 763	return 0;764}765 766static void blinkm_remove(struct i2c_client *client)767{768	struct blinkm_data *data = i2c_get_clientdata(client);769	int ret = 0;770	int i;771 772	/* make sure no workqueue entries are pending */773	for (i = 0; i < NUM_LEDS; i++)774		led_classdev_unregister(&data->blinkm_leds[i].cdev.led_cdev);775 776	/* reset rgb */777	data->next_red = 0x00;778	data->next_green = 0x00;779	data->next_blue = 0x00;780	ret = blinkm_transfer_hw(client, BLM_FADE_RGB);781	if (ret < 0)782		dev_err(&client->dev, "Failure in blinkm_remove ignored. Continuing.\n");783 784	/* reset hsb */785	data->next_hue = 0x00;786	data->next_saturation = 0x00;787	data->next_brightness = 0x00;788	ret = blinkm_transfer_hw(client, BLM_FADE_HSB);789	if (ret < 0)790		dev_err(&client->dev, "Failure in blinkm_remove ignored. Continuing.\n");791 792	/* red fade to off */793	data->next_red = 0xff;794	ret = blinkm_transfer_hw(client, BLM_GO_RGB);795	if (ret < 0)796		dev_err(&client->dev, "Failure in blinkm_remove ignored. Continuing.\n");797 798	/* off */799	data->next_red = 0x00;800	ret = blinkm_transfer_hw(client, BLM_FADE_RGB);801	if (ret < 0)802		dev_err(&client->dev, "Failure in blinkm_remove ignored. Continuing.\n");803 804	sysfs_remove_group(&client->dev.kobj, &blinkm_group);805}806 807static const struct i2c_device_id blinkm_id[] = {808	{ "blinkm" },809	{}810};811 812MODULE_DEVICE_TABLE(i2c, blinkm_id);813 814  /* This is the driver that will be inserted */815static struct i2c_driver blinkm_driver = {816	.class = I2C_CLASS_HWMON,817	.driver = {818		   .name = "blinkm",819		   },820	.probe = blinkm_probe,821	.remove = blinkm_remove,822	.id_table = blinkm_id,823	.detect = blinkm_detect,824	.address_list = normal_i2c,825};826 827module_i2c_driver(blinkm_driver);828 829MODULE_AUTHOR("Jan-Simon Moeller <dl9pf@gmx.de>");830MODULE_AUTHOR("Joseph Strauss <jstrauss@mailbox.org>");831MODULE_DESCRIPTION("BlinkM RGB LED driver");832MODULE_LICENSE("GPL");833 834