1f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen/*
2f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen * MSI GT683R led driver
3f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen *
4f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen * Copyright (c) 2014 Janne Kanniainen <janne.kanniainen@gmail.com>
5f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen *
6f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen * This program is free software; you can redistribute it and/or
7f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen * modify it under the terms of the GNU General Public License as
8f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen * published by the Free Software Foundation; either version 2 of
9f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen * the License, or (at your option) any later version.
10f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen *
11f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen * This program is distributed in the hope that it will be useful,
12f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen * but WITHOUT ANY WARRANTY; without even the implied warranty of
13f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen * GNU General Public License for more details.
15f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen *
16f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen */
17f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen
18f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen#include <linux/device.h>
19f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen#include <linux/hid.h>
20f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen#include <linux/kernel.h>
21f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen#include <linux/leds.h>
22f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen#include <linux/module.h>
23f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen
24f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen#include "hid-ids.h"
25f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen
26f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen#define GT683R_BUFFER_SIZE			8
27f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen
28f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen/*
29f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen * GT683R_LED_OFF: all LEDs are off
30f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen * GT683R_LED_AUDIO: LEDs brightness depends on sound level
31f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen * GT683R_LED_BREATHING: LEDs brightness varies at human breathing rate
32f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen * GT683R_LED_NORMAL: LEDs are fully on when enabled
33f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen */
34f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainenenum gt683r_led_mode {
35f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	GT683R_LED_OFF = 0,
36f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	GT683R_LED_AUDIO = 2,
37f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	GT683R_LED_BREATHING = 3,
38f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	GT683R_LED_NORMAL = 5
39f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen};
40f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen
41f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainenenum gt683r_panels {
42f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	GT683R_LED_BACK = 0,
43f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	GT683R_LED_SIDE = 1,
44f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	GT683R_LED_FRONT = 2,
45f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	GT683R_LED_COUNT,
46f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen};
47f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen
48f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainenstatic const char * const gt683r_panel_names[] = {
49f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	"back",
50f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	"side",
51f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	"front",
52f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen};
53f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen
54f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainenstruct gt683r_led {
55f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	struct hid_device *hdev;
56f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	struct led_classdev led_devs[GT683R_LED_COUNT];
57f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	struct mutex lock;
58f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	struct work_struct work;
59f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	enum led_brightness brightnesses[GT683R_LED_COUNT];
60f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	enum gt683r_led_mode mode;
61f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen};
62f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen
63f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainenstatic const struct hid_device_id gt683r_led_id[] = {
64f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	{ HID_USB_DEVICE(USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL) },
65f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	{ }
66f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen};
67f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen
68f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainenstatic void gt683r_brightness_set(struct led_classdev *led_cdev,
69f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen				enum led_brightness brightness)
70f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen{
71f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	int i;
72f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	struct device *dev = led_cdev->dev->parent;
73f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	struct hid_device *hdev = container_of(dev, struct hid_device, dev);
74f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	struct gt683r_led *led = hid_get_drvdata(hdev);
75f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen
76f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	for (i = 0; i < GT683R_LED_COUNT; i++) {
77f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen		if (led_cdev == &led->led_devs[i])
78f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen			break;
79f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	}
80f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen
81f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	if (i < GT683R_LED_COUNT) {
82f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen		led->brightnesses[i] = brightness;
83f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen		schedule_work(&led->work);
84f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	}
85f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen}
86f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen
876522fe1c39a79d4753e37d912876315720119f82Janne Kanniainenstatic ssize_t mode_show(struct device *dev,
88f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen				struct device_attribute *attr,
89f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen				char *buf)
90f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen{
91f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	u8 sysfs_mode;
926522fe1c39a79d4753e37d912876315720119f82Janne Kanniainen	struct hid_device *hdev = container_of(dev->parent,
936522fe1c39a79d4753e37d912876315720119f82Janne Kanniainen					struct hid_device, dev);
94f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	struct gt683r_led *led = hid_get_drvdata(hdev);
95f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen
96f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	if (led->mode == GT683R_LED_NORMAL)
97f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen		sysfs_mode = 0;
98f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	else if (led->mode == GT683R_LED_AUDIO)
99f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen		sysfs_mode = 1;
100f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	else
101f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen		sysfs_mode = 2;
102f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen
103f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	return scnprintf(buf, PAGE_SIZE, "%u\n", sysfs_mode);
104f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen}
105f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen
1066522fe1c39a79d4753e37d912876315720119f82Janne Kanniainenstatic ssize_t mode_store(struct device *dev,
107f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen				struct device_attribute *attr,
108f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen				const char *buf, size_t count)
109f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen{
110f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	u8 sysfs_mode;
1116522fe1c39a79d4753e37d912876315720119f82Janne Kanniainen	struct hid_device *hdev = container_of(dev->parent,
1126522fe1c39a79d4753e37d912876315720119f82Janne Kanniainen					struct hid_device, dev);
113f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	struct gt683r_led *led = hid_get_drvdata(hdev);
114f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen
115f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen
116f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	if (kstrtou8(buf, 10, &sysfs_mode) || sysfs_mode > 2)
117f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen		return -EINVAL;
118f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen
119f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	mutex_lock(&led->lock);
120f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen
121f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	if (sysfs_mode == 0)
122f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen		led->mode = GT683R_LED_NORMAL;
123f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	else if (sysfs_mode == 1)
124f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen		led->mode = GT683R_LED_AUDIO;
125f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	else
126f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen		led->mode = GT683R_LED_BREATHING;
127f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen
128f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	mutex_unlock(&led->lock);
129f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	schedule_work(&led->work);
130f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen
131f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	return count;
132f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen}
133f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen
134f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainenstatic int gt683r_led_snd_msg(struct gt683r_led *led, u8 *msg)
135f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen{
136f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	int ret;
137f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen
138f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	ret = hid_hw_raw_request(led->hdev, msg[0], msg, GT683R_BUFFER_SIZE,
139f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen				HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
140f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	if (ret != GT683R_BUFFER_SIZE) {
141f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen		hid_err(led->hdev,
142f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen			"failed to send set report request: %i\n", ret);
143f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen		if (ret < 0)
144f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen			return ret;
145f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen		return -EIO;
146f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	}
147f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen
148f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	return 0;
149f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen}
150f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen
151f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainenstatic int gt683r_leds_set(struct gt683r_led *led, u8 leds)
152f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen{
153f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	int ret;
154f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	u8 *buffer;
155f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen
156f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	buffer = kzalloc(GT683R_BUFFER_SIZE, GFP_KERNEL);
157f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	if (!buffer)
158f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen		return -ENOMEM;
159f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen
160f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	buffer[0] = 0x01;
161f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	buffer[1] = 0x02;
162f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	buffer[2] = 0x30;
163f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	buffer[3] = leds;
164f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	ret = gt683r_led_snd_msg(led, buffer);
165f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen
166f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	kfree(buffer);
167f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	return ret;
168f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen}
169f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen
170f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainenstatic int gt683r_mode_set(struct gt683r_led *led, u8 mode)
171f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen{
172f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	int ret;
173f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	u8 *buffer;
174f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen
175f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	buffer = kzalloc(GT683R_BUFFER_SIZE, GFP_KERNEL);
176f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	if (!buffer)
177f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen		return -ENOMEM;
178f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen
179f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	buffer[0] = 0x01;
180f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	buffer[1] = 0x02;
181f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	buffer[2] = 0x20;
182f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	buffer[3] = mode;
183f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	buffer[4] = 0x01;
184f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	ret = gt683r_led_snd_msg(led, buffer);
185f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen
186f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	kfree(buffer);
187f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	return ret;
188f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen}
189f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen
190f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainenstatic void gt683r_led_work(struct work_struct *work)
191f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen{
192f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	int i;
193f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	u8 leds = 0;
194f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	u8 mode;
195f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	struct gt683r_led *led = container_of(work, struct gt683r_led, work);
196f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen
197f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	mutex_lock(&led->lock);
198f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen
199f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	for (i = 0; i < GT683R_LED_COUNT; i++) {
200f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen		if (led->brightnesses[i])
201f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen			leds |= BIT(i);
202f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	}
203f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen
204f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	if (gt683r_leds_set(led, leds))
205f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen		goto fail;
206f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen
207f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	if (leds)
208f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen		mode = led->mode;
209f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	else
210f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen		mode = GT683R_LED_OFF;
211f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen
212f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	gt683r_mode_set(led, mode);
213f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainenfail:
214f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	mutex_unlock(&led->lock);
215f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen}
216f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen
2176522fe1c39a79d4753e37d912876315720119f82Janne Kanniainenstatic DEVICE_ATTR_RW(mode);
2186522fe1c39a79d4753e37d912876315720119f82Janne Kanniainen
2196522fe1c39a79d4753e37d912876315720119f82Janne Kanniainenstatic struct attribute *gt683r_led_attrs[] = {
2206522fe1c39a79d4753e37d912876315720119f82Janne Kanniainen	&dev_attr_mode.attr,
2216522fe1c39a79d4753e37d912876315720119f82Janne Kanniainen	NULL
2226522fe1c39a79d4753e37d912876315720119f82Janne Kanniainen};
2236522fe1c39a79d4753e37d912876315720119f82Janne Kanniainen
2246522fe1c39a79d4753e37d912876315720119f82Janne Kanniainenstatic const struct attribute_group gt683r_led_group = {
2256522fe1c39a79d4753e37d912876315720119f82Janne Kanniainen	.name = "gt683r",
2266522fe1c39a79d4753e37d912876315720119f82Janne Kanniainen	.attrs = gt683r_led_attrs,
2276522fe1c39a79d4753e37d912876315720119f82Janne Kanniainen};
2286522fe1c39a79d4753e37d912876315720119f82Janne Kanniainen
2296522fe1c39a79d4753e37d912876315720119f82Janne Kanniainenstatic const struct attribute_group *gt683r_led_groups[] = {
2306522fe1c39a79d4753e37d912876315720119f82Janne Kanniainen	&gt683r_led_group,
2316522fe1c39a79d4753e37d912876315720119f82Janne Kanniainen	NULL
2326522fe1c39a79d4753e37d912876315720119f82Janne Kanniainen};
233f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen
234f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainenstatic int gt683r_led_probe(struct hid_device *hdev,
235f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen			const struct hid_device_id *id)
236f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen{
237f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	int i;
238f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	int ret;
239f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	int name_sz;
240f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	char *name;
241f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	struct gt683r_led *led;
242f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen
243f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	led = devm_kzalloc(&hdev->dev, sizeof(*led), GFP_KERNEL);
244f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	if (!led)
245f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen		return -ENOMEM;
246f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen
247c3883ae918c8754f7c251564d63211e1d5648b08Janne Kanniainen	mutex_init(&led->lock);
248c3883ae918c8754f7c251564d63211e1d5648b08Janne Kanniainen	INIT_WORK(&led->work, gt683r_led_work);
249c3883ae918c8754f7c251564d63211e1d5648b08Janne Kanniainen
250f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	led->mode = GT683R_LED_NORMAL;
251f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	led->hdev = hdev;
252f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	hid_set_drvdata(hdev, led);
253f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen
254f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	ret = hid_parse(hdev);
255f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	if (ret) {
256f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen		hid_err(hdev, "hid parsing failed\n");
257f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen		return ret;
258f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	}
259f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen
260f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
261f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	if (ret) {
262f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen		hid_err(hdev, "hw start failed\n");
263f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen		return ret;
264f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	}
265f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen
266f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	for (i = 0; i < GT683R_LED_COUNT; i++) {
267f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen		name_sz = strlen(dev_name(&hdev->dev)) +
268f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen				strlen(gt683r_panel_names[i]) + 3;
269f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen
270f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen		name = devm_kzalloc(&hdev->dev, name_sz, GFP_KERNEL);
271f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen		if (!name) {
272f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen			ret = -ENOMEM;
273f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen			goto fail;
274f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen		}
275f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen
276f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen		snprintf(name, name_sz, "%s::%s",
277f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen				dev_name(&hdev->dev), gt683r_panel_names[i]);
278f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen		led->led_devs[i].name = name;
279f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen		led->led_devs[i].max_brightness = 1;
280f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen		led->led_devs[i].brightness_set = gt683r_brightness_set;
2816522fe1c39a79d4753e37d912876315720119f82Janne Kanniainen		led->led_devs[i].groups = gt683r_led_groups;
2826522fe1c39a79d4753e37d912876315720119f82Janne Kanniainen
283f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen		ret = led_classdev_register(&hdev->dev, &led->led_devs[i]);
284f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen		if (ret) {
285f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen			hid_err(hdev, "could not register led device\n");
286f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen			goto fail;
287f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen		}
288f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	}
289f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen
290f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	return 0;
291f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen
292f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainenfail:
293f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	for (i = i - 1; i >= 0; i--)
294f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen		led_classdev_unregister(&led->led_devs[i]);
295f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	hid_hw_stop(hdev);
296f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	return ret;
297f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen}
298f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen
299f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainenstatic void gt683r_led_remove(struct hid_device *hdev)
300f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen{
301f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	int i;
302f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	struct gt683r_led *led = hid_get_drvdata(hdev);
303f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen
304f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	for (i = 0; i < GT683R_LED_COUNT; i++)
305f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen		led_classdev_unregister(&led->led_devs[i]);
306f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	flush_work(&led->work);
307f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	hid_hw_stop(hdev);
308f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen}
309f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen
310f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainenstatic struct hid_driver gt683r_led_driver = {
311f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	.probe = gt683r_led_probe,
312f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	.remove = gt683r_led_remove,
313f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	.name = "gt683r_led",
314f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen	.id_table = gt683r_led_id,
315f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen};
316f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen
317f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainenmodule_hid_driver(gt683r_led_driver);
318f471d9480275796dea2ac7ec249b050e70a2888dJanne Kanniainen
319f471d9480275796dea2ac7ec249b050e70a2888dJanne KanniainenMODULE_AUTHOR("Janne Kanniainen");
320f471d9480275796dea2ac7ec249b050e70a2888dJanne KanniainenMODULE_DESCRIPTION("MSI GT683R led driver");
321f471d9480275796dea2ac7ec249b050e70a2888dJanne KanniainenMODULE_LICENSE("GPL");
322