diff --git a/Documentation/ABI/testing/sysfs-driver-hid-blink1 b/Documentation/ABI/testing/sysfs-driver-hid-blink1
new file mode 100644
index 0000000..af3e1cd
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-driver-hid-blink1
@@ -0,0 +1,25 @@
+What:		/sys/class/leds/blink(1)::<serial>/rgb
+Date:		December 2012
+Contact:	Vivien Didelot <vivien.didelot@savoirfairelinux.com>
+Description:	The ThingM blink(1) is an USB RGB LED. The color notation is
+		3-byte hexadecimal. Read this attribute to get the last set
+		color. Write the 24-bit hexadecimal color to change the current
+		LED color. The default color is full white (0xFFFFFF).
+		For instance, set the color to green with:
+		echo 00FF00 > rgb
+
+What:		/sys/class/leds/blink(1)::<serial>/fade
+Date:		December 2012
+Contact:	Vivien Didelot <vivien.didelot@savoirfairelinux.com>
+Description:	This attribute allows to set a 16-bit fading time (which is a
+		multiple of 10ms) for the next color change. Read the attribute
+		to know the current fading time. The default value is set to 0
+		(no fading). For instance, set a fading of 2 seconds with:
+		echo 200 > fade
+
+What:		/sys/class/leds/blink(1)::<serial>/play
+Date:		December 2012
+Contact:	Vivien Didelot <vivien.didelot@savoirfairelinux.com>
+Description:	This attribute is used to play/pause the light patterns. Write 1
+		to start playing, 0 to stop. Reading this attribute returns the
+		current playing status.
diff --git a/MAINTAINERS b/MAINTAINERS
index 9386a63..78e1e08 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1592,6 +1592,11 @@ M:	Jan-Simon Moeller <jansimon.moeller@gmx.de>
 S:	Maintained
 F:	drivers/leds/leds-blinkm.c
 
+BLINK(1) USB RGB LED DRIVER
+M:	Vivien Didelot <vivien.didelot@savoirfairelinux.com>
+S:	Maintained
+F:	drivers/hid/hid-blink1.c
+
 BLOCK LAYER
 M:	Jens Axboe <axboe@kernel.dk>
 T:	git git://git.kernel.org/pub/scm/linux/kernel/git/axboe/linux-block.git
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 1630150..76a15c9 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -135,6 +135,16 @@ config HID_BELKIN
 	---help---
 	Support for Belkin Flip KVM and Wireless keyboard.
 
+config HID_BLINK1
+	tristate "ThingM blink(1) USB LED"
+	depends on USB_HID
+	depends on LEDS_CLASS
+	---help---
+	Support for the ThingM blink(1) USB RGB LED. This driver registers a
+	Linux LED class instance, plus additional sysfs attributes to control
+	RGB colors, fading and playing. The device is exposed through hidraw to
+	access other functions.
+
 config HID_CHERRY
 	tristate "Cherry Cymotion keyboard" if EXPERT
 	depends on USB_HID
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index cef68ca..f1eb590 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -41,6 +41,7 @@ obj-$(CONFIG_HID_ACRUX)		+= hid-axff.o
 obj-$(CONFIG_HID_APPLE)		+= hid-apple.o
 obj-$(CONFIG_HID_AUREAL)        += hid-aureal.o
 obj-$(CONFIG_HID_BELKIN)	+= hid-belkin.o
+obj-$(CONFIG_HID_BLINK1)	+= hid-blink1.o
 obj-$(CONFIG_HID_CHERRY)	+= hid-cherry.o
 obj-$(CONFIG_HID_CHICONY)	+= hid-chicony.o
 obj-$(CONFIG_HID_CYPRESS)	+= hid-cypress.o
diff --git a/drivers/hid/hid-blink1.c b/drivers/hid/hid-blink1.c
new file mode 100644
index 0000000..13025a8
--- /dev/null
+++ b/drivers/hid/hid-blink1.c
@@ -0,0 +1,275 @@
+/*
+ *  ThingM blink(1) USB LED Driver
+ *  Copyright (C) 2012  Vivien Didelot <vivien.didelot@savoirfairelinux.com>
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/hid.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+
+#include "hid-ids.h"
+
+#define BLINK1_MSG_LENGTH	9
+
+#define blink1_rgb_to_r(rgb)	((rgb & 0xFF0000) >> 16)
+#define blink1_rgb_to_g(rgb)	((rgb & 0x00FF00) >> 8)
+#define blink1_rgb_to_b(rgb)	((rgb & 0x0000FF))
+
+struct blink1_data {
+	struct hid_device *hdev;
+	struct led_classdev led_cdev;
+	unsigned int rgb;
+	u8 brightness;
+	u16 fade;
+	bool play;
+};
+
+static void blink1_send_command(struct blink1_data *data,
+		u8 buf[BLINK1_MSG_LENGTH])
+{
+	hid_dbg(data->hdev, "command: %d%c%.2x%.2x%.2x%.2x%.2x%.2x%.2x\n",
+			buf[0], buf[1], buf[2], buf[3], buf[4],
+			buf[5], buf[6], buf[7], buf[8]);
+
+	if (data->hdev->hid_output_raw_report(data->hdev, buf,
+				BLINK1_MSG_LENGTH, HID_FEATURE_REPORT) < 0)
+		hid_err(data->hdev, "failed to send command\n");
+}
+
+static void blink1_update_color(struct blink1_data *data)
+{
+	u8 buf[BLINK1_MSG_LENGTH] = { 1, 'n', 0, 0, 0, 0, 0, 0, 0 };
+
+	if (data->brightness) {
+		unsigned int coef = DIV_ROUND_CLOSEST(255, data->brightness);
+
+		buf[2] = DIV_ROUND_CLOSEST(blink1_rgb_to_r(data->rgb), coef);
+		buf[3] = DIV_ROUND_CLOSEST(blink1_rgb_to_g(data->rgb), coef);
+		buf[4] = DIV_ROUND_CLOSEST(blink1_rgb_to_b(data->rgb), coef);
+	}
+
+	if (data->fade) {
+		buf[1] = 'c';
+		buf[5] = (data->fade & 0xFF00) >> 8;
+		buf[6] = (data->fade & 0x00FF);
+	}
+
+	blink1_send_command(data, buf);
+}
+
+static void blink1_led_set(struct led_classdev *led_cdev,
+		enum led_brightness brightness)
+{
+	struct blink1_data *data = dev_get_drvdata(led_cdev->dev->parent);
+
+	data->brightness = brightness;
+	blink1_update_color(data);
+}
+
+static enum led_brightness blink1_led_get(struct led_classdev *led_cdev)
+{
+	struct blink1_data *data = dev_get_drvdata(led_cdev->dev->parent);
+
+	return data->brightness;
+}
+
+static ssize_t blink1_show_rgb(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct blink1_data *data = dev_get_drvdata(dev->parent);
+
+	return sprintf(buf, "%.6X\n", data->rgb);
+}
+
+static ssize_t blink1_store_rgb(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct blink1_data *data = dev_get_drvdata(dev->parent);
+	long unsigned int rgb;
+	int ret;
+
+	ret = kstrtoul(buf, 16, &rgb);
+	if (ret)
+		return ret;
+
+	/* RGB value is in 24-bit hexadecimal format */
+	if (rgb > 0xFFFFFF)
+		return -EINVAL;
+
+	data->rgb = rgb;
+	blink1_update_color(data);
+
+	return count;
+}
+
+static DEVICE_ATTR(rgb, S_IRUGO | S_IWUSR, blink1_show_rgb, blink1_store_rgb);
+
+static ssize_t blink1_show_fade(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct blink1_data *data = dev_get_drvdata(dev->parent);
+
+	return sprintf(buf, "%d\n", data->fade);
+}
+
+static ssize_t blink1_store_fade(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct blink1_data *data = dev_get_drvdata(dev->parent);
+	long unsigned int fade;
+	int ret;
+
+	ret = kstrtoul(buf, 10, &fade);
+	if (ret)
+		return ret;
+
+	/* blink(1) accepts 16-bit fade time */
+	if (fade > 65535)
+		return -EINVAL;
+
+	data->fade = fade;
+
+	return count;
+}
+
+static DEVICE_ATTR(fade, S_IRUGO | S_IWUSR,
+		blink1_show_fade, blink1_store_fade);
+
+static ssize_t blink1_show_play(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct blink1_data *data = dev_get_drvdata(dev->parent);
+
+	return sprintf(buf, "%d\n", data->play);
+}
+
+static ssize_t blink1_store_play(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct blink1_data *data = dev_get_drvdata(dev->parent);
+	u8 cmd[BLINK1_MSG_LENGTH] = { 1, 'p', 0, 0, 0, 0, 0, 0, 0 };
+	long unsigned int play;
+	int ret;
+
+	ret = kstrtoul(buf, 10, &play);
+	if (ret)
+		return ret;
+
+	data->play = !!play;
+	cmd[2] = data->play;
+	blink1_send_command(data, cmd);
+
+	return count;
+}
+
+static DEVICE_ATTR(play, S_IRUGO | S_IWUSR,
+		blink1_show_play, blink1_store_play);
+
+static const struct attribute_group blink1_sysfs_group = {
+	.attrs = (struct attribute *[]) {
+		&dev_attr_rgb.attr,
+		&dev_attr_fade.attr,
+		&dev_attr_play.attr,
+		NULL
+	},
+};
+
+static int blink1_probe(struct hid_device *hdev, const struct hid_device_id *id)
+{
+	struct blink1_data *data;
+	struct led_classdev *led;
+	char led_name[15];
+	int ret;
+
+	data = devm_kzalloc(&hdev->dev, sizeof(struct blink1_data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	hid_set_drvdata(hdev, data);
+	data->hdev = hdev;
+	data->rgb = 0xFFFFFF; /* set a default white color */
+
+	ret = hid_parse(hdev);
+	if (ret)
+		goto error;
+
+	ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
+	if (ret)
+		goto error;
+
+	/* blink(1) serial numbers range is 0x1A001000 to 0x1A002FFF */
+	led = &data->led_cdev;
+	snprintf(led_name, sizeof(led_name), "blink(1)::%s", hdev->uniq + 4);
+	led->name = led_name;
+	led->brightness_set = blink1_led_set;
+	led->brightness_get = blink1_led_get;
+	ret = led_classdev_register(&hdev->dev, led);
+	if (ret)
+		goto stop;
+
+	ret = sysfs_create_group(&led->dev->kobj, &blink1_sysfs_group);
+	if (ret)
+		goto remove_led;
+
+	return 0;
+
+remove_led:
+	led_classdev_unregister(led);
+stop:
+	hid_hw_stop(hdev);
+error:
+	return ret;
+}
+
+static void blink1_remove(struct hid_device *hdev)
+{
+	struct blink1_data *data = hid_get_drvdata(hdev);
+	struct led_classdev *led = &data->led_cdev;
+
+	sysfs_remove_group(&led->dev->kobj, &blink1_sysfs_group);
+	led_classdev_unregister(led);
+	hid_hw_stop(hdev);
+}
+
+static const struct hid_device_id blink1_table[] = {
+	{ HID_USB_DEVICE(USB_VENDOR_ID_THINGM, USB_DEVICE_ID_BLINK1) },
+	{ }
+};
+MODULE_DEVICE_TABLE(hid, blink1_table);
+
+static struct hid_driver blink1_driver = {
+	.name = "blink(1)",
+	.probe = blink1_probe,
+	.remove = blink1_remove,
+	.id_table = blink1_table,
+};
+
+static int __init blink1_init(void)
+{
+	return hid_register_driver(&blink1_driver);
+}
+module_init(blink1_init);
+
+static void __exit blink1_exit(void)
+{
+	hid_unregister_driver(&blink1_driver);
+}
+module_exit(blink1_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Vivien Didelot <vivien.didelot@savoirfairelinux.com>");
+MODULE_DESCRIPTION("ThingM blink(1) USB LED Driver");
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index f4109fd..11ba357 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -1673,6 +1673,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
 	{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_SUNPLUS, USB_DEVICE_ID_SUNPLUS_WDESKTOP) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_STANTUM_STM, USB_DEVICE_ID_SENSOR_HUB_7014) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_THINGM, USB_DEVICE_ID_BLINK1) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb300) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb304) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb323) },
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 9d7a428..ee5670d 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -740,6 +740,9 @@
 #define USB_DEVICE_ID_SYNAPTICS_WTP	0x0010
 #define USB_DEVICE_ID_SYNAPTICS_DPAD	0x0013
 
+#define USB_VENDOR_ID_THINGM		0x27b8
+#define USB_DEVICE_ID_BLINK1		0x01ed
+
 #define USB_VENDOR_ID_THRUSTMASTER	0x044f
 
 #define USB_VENDOR_ID_TIVO		0x150a
