diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 8f58c3c1bec3..728dede997d3 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -857,6 +857,7 @@
 #define USB_DEVICE_ID_MX5500_RECEIVER_MOUSE_DEV		0xc71c
 #define USB_DEVICE_ID_DINOVO_MINI_RECEIVER_KBD_DEV	0xc71e
 #define USB_DEVICE_ID_DINOVO_MINI_RECEIVER_MOUSE_DEV	0xc71f
+#define USB_DEVICE_ID_LOGITECH_LITRA_GLOW   0xc900
 #define USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2	0xca03
 #define USB_DEVICE_ID_LOGITECH_VIBRATION_WHEEL	0xca04

diff --git a/drivers/hid/hid-logitech-hidpp.c
b/drivers/hid/hid-logitech-hidpp.c
index 6d8d933efe18..c495484e9765 100644
--- a/drivers/hid/hid-logitech-hidpp.c
+++ b/drivers/hid/hid-logitech-hidpp.c
@@ -11,6 +11,7 @@
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

 #include <linux/device.h>
+#include <linux/dmi.h>
 #include <linux/input.h>
 #include <linux/usb.h>
 #include <linux/hid.h>
@@ -77,6 +78,7 @@ MODULE_PARM_DESC(disable_tap_to_click,
 #define HIDPP_QUIRK_HIDPP_WHEELS		BIT(26)
 #define HIDPP_QUIRK_HIDPP_EXTRA_MOUSE_BTNS	BIT(27)
 #define HIDPP_QUIRK_HIDPP_CONSUMER_VENDOR_KEYS	BIT(28)
+#define HIDPP_QUIRK_CLASS_SIMPLE_START	BIT(29)

 /* These are just aliases for now */
 #define HIDPP_QUIRK_KBD_SCROLL_WHEEL HIDPP_QUIRK_HIDPP_WHEELS
@@ -99,6 +101,7 @@ MODULE_PARM_DESC(disable_tap_to_click,
 #define HIDPP_CAPABILITY_HIDPP20_HI_RES_WHEEL	BIT(7)
 #define HIDPP_CAPABILITY_HIDPP20_HI_RES_SCROLL	BIT(8)
 #define HIDPP_CAPABILITY_HIDPP10_FAST_SCROLL	BIT(9)
+#define HIDPP_CAPABILITY_ILLUMINATION_LIGHT		BIT(10)

 #define lg_map_key_clear(c)  hid_map_usage_clear(hi, usage, bit, max,
EV_KEY, (c))

@@ -207,6 +210,7 @@ struct hidpp_device {
 	struct hidpp_scroll_counter vertical_wheel_counter;

 	u8 wireless_feature_index;
+	u8 illumination_feature_index;
 };

 /* HID++ 1.0 error codes */
@@ -228,6 +232,7 @@ struct hidpp_device {
 #define HIDPP20_ERROR				0xff

 static void hidpp_connect_event(struct hidpp_device *hidpp_dev);
+static void hidpp_illumination_defered_event(struct hidpp_device *hidpp);

 static int __hidpp_send_report(struct hid_device *hdev,
 				struct hidpp_report *hidpp_report)
@@ -402,6 +407,9 @@ static void delayed_work_cb(struct work_struct *work)
 	struct hidpp_device *hidpp = container_of(work, struct
hidpp_device,
 							work);
 	hidpp_connect_event(hidpp);
+	if (hidpp->capabilities & HIDPP_CAPABILITY_ILLUMINATION_LIGHT) {
+		hidpp_illumination_defered_event(hidpp);
+	}
 }

 static inline bool hidpp_match_answer(struct hidpp_report *question,
@@ -857,6 +865,8 @@ static int hidpp_unifying_init(struct hidpp_device
*hidpp)
 #define CMD_ROOT_GET_FEATURE				0x00
 #define CMD_ROOT_GET_PROTOCOL_VERSION			0x10

+#define HIDPP_FEATURE_TYPE_HIDDEN 0x70
+
 static int hidpp_root_get_feature(struct hidpp_device *hidpp, u16
feature,
 	u8 *feature_index, u8 *feature_type)
 {
@@ -1723,6 +1733,392 @@ static int hidpp_set_wireless_feature_index(struct
hidpp_device *hidpp)
 	return ret;
 }

+/*
--------------------------------------------------------------------------
*/
+/* 0x1990: Illumination Light
*/
+/*
--------------------------------------------------------------------------
*/
+
+#define HIDPP_PAGE_ILLUMINATION_LIGHT 0x1990
+
+#define HIDPP_ILLUMINATION_FUNC_GET 0x00
+#define HIDPP_ILLUMINATION_FUNC_SET 0x10
+#define HIDPP_ILLUMINATION_FUNC_GET_BRIGHTNESS_INFO 0x20
+#define HIDPP_ILLUMINATION_FUNC_GET_BRIGHTNESS 0x30
+#define HIDPP_ILLUMINATION_FUNC_SET_BRIGHTNESS 0x40
+
+/* Not yet supported
+ * #define HIDPP_ILLUMINATION_FUNC_GET_BRIGHTNESS_LEVELS 0x50
+ * #define HIDPP_ILLUMINATION_FUNC_SET_BRIGHTNESS_LEVELS 0x60
+ */
+
+#define HIDPP_ILLUMINATION_FUNC_GET_COLOR_TEMPERATURE_INFO 0x70
+#define HIDPP_ILLUMINATION_FUNC_GET_COLOR_TEMPERATURE 0x80
+#define HIDPP_ILLUMINATION_FUNC_SET_COLOR_TEMPERATURE 0x90
+
+/* Not yet supported
+ * #define HIDPP_ILLUMINATION_FUNC_GET_COLOR_TEMPERATURE_LEVELS 0xA0
+ * #define HIDPP_ILLUMINATION_FUNC_SET_COLOR_TEMPERATURE_LEVELS 0xB0
+ */
+
+#define HIDPP_ILLUMINATION_EVENT_CHANGE 0x00
+#define HIDPP_ILLUMINATION_EVENT_BRIGHTNESS_CHANGE 0x10
+#define HIDPP_ILLUMINATION_EVENT_COLOR_TEMPERATURE_CHANGE 0x20
+
+#define HIDPP_ILLUMINATION_CAP_EVENTS BIT(0)
+#define HIDPP_ILLUMINATION_CAP_LINEAR_LEVELS BIT(1)
+#define HIDPP_ILLUMINATION_CAP_NON_LINEAR_LEVELS BIT(2)
+
+struct control_info {
+	u16 min;
+	u16 max;
+	u16 res;
+	u8 capabilities;
+	u8 max_levels;
+};
+
+struct led_data {
+	struct led_classdev cdev;
+	struct hidpp_device *drv_data;
+	struct hid_device *hdev;
+	u16 feature_index;
+	struct control_info brightness_info;
+	struct control_info color_temperature_info;
+	struct {
+		struct mutex mutex;
+		int on;
+		int brightness;
+	} hw_change;
+	char dirname[256];
+};
+
+/* kernel led interface designates 0 as off. To not lose the ability to
chose
+ * minimal brightness, we thus need to increase the reported range by 1
+ */
+static unsigned int device_to_led_brightness(struct led_data *led, u16
device_brightness)
+{
+	u16 relative = device_brightness - led->brightness_info.min;
+	u16 step = relative / led->brightness_info.res;
+
+	return step + 1;
+}
+
+static u16 led_to_device_brightness(struct led_data *led, unsigned int
led_brightness)
+{
+	unsigned int step = led_brightness - 1;
+	unsigned int relative = step * led->brightness_info.res;
+
+	return led->brightness_info.min + relative;
+}
+
+static int request_led_on(struct hidpp_device *hidpp, struct led_data
*led, bool *on)
+{
+
+	struct hidpp_report report;
+
+	int ret = hidpp_send_fap_command_sync(hidpp,
+		hidpp->illumination_feature_index,
+		HIDPP_ILLUMINATION_FUNC_GET, NULL,
+		0, &report);
+	if (ret) {
+		hid_err(hidpp->hid_dev, "Getting Illumination failed\n");
+		return ret;
+	}
+
+	*on = report.fap.params[0] & BIT(0);
+	return 0;
+}
+
+static int request_led_brightness(struct hidpp_device *hidpp, struct
led_data *led,
+	unsigned int *led_brightness)
+{
+	u16 device_brightness;
+	struct hidpp_report report;
+
+	int ret = hidpp_send_fap_command_sync(
+		hidpp, hidpp->illumination_feature_index,
+		HIDPP_ILLUMINATION_FUNC_GET_BRIGHTNESS, NULL, 0, &report);
+	if (ret) {
+		hid_err(hidpp->hid_dev,
+			"Getting Illumination Brightness failed\n");
+		return ret;
+	}
+
+	device_brightness = get_unaligned_be16(&report.fap.params[0]);
+	*led_brightness = device_to_led_brightness(led,
device_brightness);
+	return 0;
+}
+
+static enum led_brightness led_brightness_get(struct led_classdev
*led_cdev)
+{
+	bool on;
+	unsigned int brightness;
+	struct led_data *led = container_of(led_cdev, struct led_data,
cdev);
+	struct hidpp_device *hidpp = led->drv_data;
+
+	int ret = request_led_on(hidpp, led, &on);
+
+	if (ret || !on)
+		return LED_OFF;
+
+	ret = request_led_brightness(hidpp, led, &brightness);
+	if (ret)
+		return LED_OFF;
+
+	return brightness;
+}
+
+
+static void led_brightness_set_dummy(struct led_classdev *led_cdev,
+			enum led_brightness led_brightness)
+{
+}
+
+static int led_brightness_set_sync(struct led_classdev *led_cdev,
+			       enum led_brightness led_brightness)
+{
+	struct hidpp_report report;
+	struct led_data *led = container_of(led_cdev, struct led_data,
cdev);
+	struct hidpp_device *hidpp = led->drv_data;
+	u16 device_brightness = led_to_device_brightness(led,
led_brightness);
+
+	bool on = led_brightness != 0;
+	u8 params[2] = { on ? BIT(0) : 0, 0 };
+
+	int ret = hidpp_send_fap_command_sync(hidpp,
+
hidpp->illumination_feature_index,
+					  HIDPP_ILLUMINATION_FUNC_SET,
params,
+					  1, &report);
+	if (ret) {
+		hid_err(hidpp->hid_dev, "Setting Illumination failed\n");
+		return ret;
+	}
+
+	if (!on)
+		return 0;
+
+	put_unaligned_be16(device_brightness, params);
+	ret = hidpp_send_fap_command_sync(
+		hidpp, hidpp->illumination_feature_index,
+		HIDPP_ILLUMINATION_FUNC_SET_BRIGHTNESS, params, 2,
+		&report);
+	if (ret) {
+		hid_err(hidpp->hid_dev,
+			"Setting Illumination Brightness failed\n");
+	}
+	return ret;
+}
+
+static int get_brightness_info_sync(struct hidpp_device *hidpp,
+				    struct control_info *info)
+{
+	struct hidpp_report resp;
+	int ret = hidpp_send_fap_command_sync(
+		hidpp, hidpp->illumination_feature_index,
+		HIDPP_ILLUMINATION_FUNC_GET_BRIGHTNESS_INFO, NULL, 0,
&resp);
+	if (ret) {
+		hid_err(hidpp->hid_dev,
+			"%s: failed with %d\n", __func__, ret);
+		return ret;
+	}
+
+	info->capabilities = resp.fap.params[0];
+	info->min = get_unaligned_be16(&resp.fap.params[1]);
+	info->max = get_unaligned_be16(&resp.fap.params[3]);
+	info->res = get_unaligned_be16(&resp.fap.params[5]);
+	info->max_levels = resp.fap.params[7] & 0x0F;
+	return 0;
+}
+
+static int get_color_temperature_info_sync(struct hidpp_device *hidpp,
+					   struct control_info *info)
+{
+	struct hidpp_report resp;
+	int ret = hidpp_send_fap_command_sync(
+		hidpp, hidpp->illumination_feature_index,
+		HIDPP_ILLUMINATION_FUNC_GET_COLOR_TEMPERATURE_INFO, NULL,
0,
+		&resp);
+	if (ret) {
+		hid_err(hidpp->hid_dev,
+			"%s: failed with %d\n", __func__, ret);
+		return ret;
+	}
+
+	info->capabilities = resp.fap.params[0];
+	info->min = get_unaligned_be16(&resp.fap.params[1]);
+	info->max = get_unaligned_be16(&resp.fap.params[3]);
+	info->res = get_unaligned_be16(&resp.fap.params[5]);
+	info->max_levels = resp.fap.params[7];
+	return 0;
+}
+
+static int register_led(struct hidpp_device *hidpp)
+{
+	int ret;
+	unsigned int brightness_range;
+	struct led_data *led = devm_kzalloc(&hidpp->hid_dev->dev,
sizeof(struct led_data),
+			  GFP_KERNEL);
+
+	if (!led)
+		return -ENOMEM;
+
+	ret = get_brightness_info_sync(hidpp, &led->brightness_info);
+	if (ret)
+		goto cleanup;
+
+	ret = get_color_temperature_info_sync(hidpp,
+
&led->color_temperature_info);
+	if (ret)
+		goto cleanup;
+
+	led->drv_data = hidpp;
+	mutex_init(&led->hw_change.mutex);
+	led->hw_change.on = -1;
+	led->hw_change.brightness = -1;
+	led->cdev.name = "logitech::illumination";
+	led->cdev.flags = LED_BRIGHT_HW_CHANGED | LED_HW_PLUGGABLE;
+	led->cdev.max_brightness = device_to_led_brightness(led,
led->brightness_info.max);
+	brightness_range = led->brightness_info.max -
led->brightness_info.min;
+	if (brightness_range == 0) {
+		/* According to docs set value is not supported under
these
+		 * conditions.
+		 * LED interface enforces a set function.
+		 */
+		led->cdev.brightness_set = led_brightness_set_dummy;
+	} else {
+		led->cdev.brightness_set_blocking =
led_brightness_set_sync;
+	}
+	led->cdev.brightness_get = led_brightness_get;
+
+	ret = devm_led_classdev_register(&hidpp->hid_dev->dev,
&led->cdev);
+	if (ret < 0)
+		goto register_fail;
+
+	hidpp->private_data = led;
+	return 0;
+register_fail:
+	mutex_destroy(&led->hw_change.mutex);
+cleanup:
+	devm_kfree(&hidpp->hid_dev->dev, led);
+	return ret;
+}
+
+static int hidpp_initialize_illumination(struct hidpp_device *hidpp)
+{
+	int ret;
+	unsigned long capabilities = hidpp->capabilities;
+
+	if (hidpp->protocol_major >= 2) {
+		u8 feature_index;
+		u8 feature_type;
+
+		ret = hidpp_root_get_feature(hidpp,
+
HIDPP_PAGE_ILLUMINATION_LIGHT,
+					     &feature_index,
&feature_type);
+		if (!ret && !(feature_type & HIDPP_FEATURE_TYPE_HIDDEN)) {
+			hidpp->capabilities |=
+				HIDPP_CAPABILITY_ILLUMINATION_LIGHT;
+			hidpp->illumination_feature_index = feature_index;
+			hid_dbg(hidpp->hid_dev,
+				"Detected HID++ 2.0 Illumination
Light\n");
+			return 0;
+		}
+	}
+
+	if (hidpp->capabilities == capabilities)
+		hid_dbg(hidpp->hid_dev,
+			"Did not detect HID++ Illumination Light hardware
support\n");
+	return 0;
+}
+
+static void hidpp_remove_illumination(struct hidpp_device *hidpp)
+{
+	struct led_data *led = (struct led_data *)hidpp->private_data;
+
+	mutex_destroy(&led->hw_change.mutex);
+}
+
+static void hidpp_illumination_defered_event(struct hidpp_device *hidpp)
+{
+	bool has_hw_change, on;
+	int ret;
+	unsigned int led_brightness;
+	struct led_data *led = (struct led_data *)hidpp->private_data;
+
+	mutex_lock(&led->hw_change.mutex);
+
+	has_hw_change = led->hw_change.on == -1 &&
led->hw_change.brightness == -1;
+
+	if (!has_hw_change)
+		goto unlock;
+
+	if (led->hw_change.on == -1) {
+		ret = request_led_on(hidpp, led, &on);
+		if (ret)
+			goto reset_hw_change;
+	} else {
+		on = led->hw_change.on;
+	}
+
+	if (!on) {
+		led_brightness = LED_OFF;
+		goto notify_changed;
+	}
+
+	if (led->hw_change.brightness == -1) {
+		ret = request_led_brightness(hidpp, led, &led_brightness);
+		if (ret)
+			goto reset_hw_change;
+	} else {
+		led_brightness = device_to_led_brightness(led,
led->hw_change.brightness);
+	}
+
+notify_changed:
+	led_classdev_notify_brightness_hw_changed(
+		&led->cdev, led_brightness);
+reset_hw_change:
+	led->hw_change.on = -1;
+	led->hw_change.brightness = -1;
+unlock:
+	mutex_unlock(&led->hw_change.mutex);
+}
+
+static int hidpp20_illumination_raw_event(struct hidpp_device *hidpp, u8
*data,
+					  int size)
+{
+	struct led_data *led = (struct led_data *)hidpp->private_data;
+	struct hidpp_report *report = (struct hidpp_report *)data;
+
+	switch (report->report_id) {
+	case REPORT_ID_HIDPP_LONG:
+		/* size is already checked in hidpp_raw_event.
+		 * only leave long through
+		 */
+		break;
+	default:
+		hid_err(hidpp->hid_dev, "%s:Unhandled report_id %u\n",
__func__, report->report_id);
+		return 0;
+	}
+
+	if (report->fap.feature_index !=
hidpp->illumination_feature_index)
+		return 0;
+
+	if (report->fap.funcindex_clientid ==
HIDPP_ILLUMINATION_EVENT_CHANGE) {
+		mutex_lock(&led->hw_change.mutex);
+		led->hw_change.on = report->fap.params[0] & BIT(0);
+		mutex_unlock(&led->hw_change.mutex);
+		return 0;
+	}
+
+	if (report->fap.funcindex_clientid ==
+	    HIDPP_ILLUMINATION_EVENT_BRIGHTNESS_CHANGE) {
+		mutex_lock(&led->hw_change.mutex);
+		led->hw_change.brightness =
get_unaligned_be16(&report->fap.params[0]);
+		mutex_unlock(&led->hw_change.mutex);
+		return 0;
+	}
+
+	return 0;
+}
+
 /*
--------------------------------------------------------------------------
*/
 /* 0x2120: Hi-resolution scrolling
*/
 /*
--------------------------------------------------------------------------
*/
@@ -3640,6 +4036,12 @@ static int hidpp_raw_hidpp_event(struct
hidpp_device *hidpp, u8 *data,
 			return ret;
 	}

+	if (hidpp->capabilities & HIDPP_CAPABILITY_ILLUMINATION_LIGHT) {
+		ret = hidpp20_illumination_raw_event(hidpp, data, size);
+		if (ret != 0)
+			return ret;
+	}
+
 	if (hidpp->quirks & HIDPP_QUIRK_HIDPP_WHEELS) {
 		ret = hidpp10_wheel_raw_event(hidpp, data, size);
 		if (ret != 0)
@@ -3964,6 +4366,7 @@ static void hidpp_connect_event(struct hidpp_device
*hidpp)

 	hidpp_initialize_battery(hidpp);
 	hidpp_initialize_hires_scroll(hidpp);
+	hidpp_initialize_illumination(hidpp);

 	/* forward current battery state */
 	if (hidpp->capabilities & HIDPP_CAPABILITY_HIDPP10_BATTERY) {
@@ -3986,6 +4389,14 @@ static void hidpp_connect_event(struct hidpp_device
*hidpp)
 	if (hidpp->capabilities & HIDPP_CAPABILITY_HI_RES_SCROLL)
 		hi_res_scroll_enable(hidpp);

+	if (hidpp->capabilities & HIDPP_CAPABILITY_ILLUMINATION_LIGHT) {
+		ret = register_led(hidpp);
+		if (ret) {
+			hid_err(hdev, "Registering leds failed.\n");
+			return;
+		}
+	}
+
 	if (!(hidpp->quirks & HIDPP_QUIRK_NO_HIDINPUT) ||
hidpp->delayed_input)
 		/* if the input nodes are already created, we can stop now
*/
 		return;
@@ -4156,11 +4567,15 @@ static int hidpp_probe(struct hid_device *hdev,
const struct hid_device_id *id)
 		hid_warn(hdev, "Cannot allocate sysfs group for %s\n",
 			 hdev->name);

-	/*
-	 * Plain USB connections need to actually call start and open
-	 * on the transport driver to allow incoming data.
-	 */
-	ret = hid_hw_start(hdev, 0);
+	if (hidpp->quirks & HIDPP_QUIRK_CLASS_SIMPLE_START) {
+		ret = hid_hw_start(hdev, HID_CLAIMED_DRIVER);
+	} else {
+		/*
+		 * Plain USB connections need to actually call start and
open
+		 * on the transport driver to allow incoming data.
+		 */
+		ret = hid_hw_start(hdev, 0);
+	}
 	if (ret) {
 		hid_err(hdev, "hw start failed\n");
 		goto hid_hw_start_fail;
@@ -4211,6 +4626,10 @@ static int hidpp_probe(struct hid_device *hdev,
const struct hid_device_id *id)

 	hidpp_connect_event(hidpp);

+	/* Resetting HID node made communication with Glow break down*/
+	if (hidpp->quirks & HIDPP_QUIRK_CLASS_SIMPLE_START)
+		return 0;
+
 	/* Reset the HID node state */
 	hid_device_io_stop(hdev);
 	hid_hw_close(hdev);
@@ -4254,6 +4673,9 @@ static void hidpp_remove(struct hid_device *hdev)
 	if (!hidpp)
 		return hid_hw_stop(hdev);

+	if (hidpp->capabilities & HIDPP_CAPABILITY_ILLUMINATION_LIGHT)
+		hidpp_remove_illumination(hidpp);
+
 	sysfs_remove_group(&hdev->dev.kobj, &ps_attribute_group);

 	hid_hw_stop(hdev);
@@ -4334,6 +4756,9 @@ static const struct hid_device_id hidpp_devices[] =
{
 		.driver_data = HIDPP_QUIRK_CLASS_G920 |
HIDPP_QUIRK_FORCE_OUTPUT_REPORTS},
 	{ /* Logitech G Pro Gaming Mouse over USB */
 	  HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC088) },
+	{ /* Logitech Litra Glow over USB*/
+	  HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
USB_DEVICE_ID_LOGITECH_LITRA_GLOW),
+		.driver_data = HIDPP_QUIRK_CLASS_SIMPLE_START |
HIDPP_QUIRK_FORCE_OUTPUT_REPORTS },

 	{ /* MX5000 keyboard over Bluetooth */
 	  HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb305),
diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c
index 0e9702c7f7d6..64c176b0ba88 100644
--- a/drivers/hid/hid-quirks.c
+++ b/drivers/hid/hid-quirks.c
@@ -494,6 +494,7 @@ static const struct hid_device_id
hid_have_special_driver[] = {
 #endif
 #if IS_ENABLED(CONFIG_HID_LOGITECH_HIDPP)
 	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
USB_DEVICE_ID_LOGITECH_G920_WHEEL) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
USB_DEVICE_ID_LOGITECH_LITRA_GLOW) },
