xref: /openbmc/linux/drivers/hid/hid-gt683r.c (revision a4b49409)
1c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2f471d948SJanne Kanniainen /*
3f471d948SJanne Kanniainen  * MSI GT683R led driver
4f471d948SJanne Kanniainen  *
5f471d948SJanne Kanniainen  * Copyright (c) 2014 Janne Kanniainen <janne.kanniainen@gmail.com>
6f471d948SJanne Kanniainen  */
7f471d948SJanne Kanniainen 
8f471d948SJanne Kanniainen #include <linux/device.h>
9f471d948SJanne Kanniainen #include <linux/hid.h>
10f471d948SJanne Kanniainen #include <linux/kernel.h>
11f471d948SJanne Kanniainen #include <linux/leds.h>
12f471d948SJanne Kanniainen #include <linux/module.h>
13f471d948SJanne Kanniainen 
14f471d948SJanne Kanniainen #include "hid-ids.h"
15f471d948SJanne Kanniainen 
16f471d948SJanne Kanniainen #define GT683R_BUFFER_SIZE			8
17f471d948SJanne Kanniainen 
18f471d948SJanne Kanniainen /*
19f471d948SJanne Kanniainen  * GT683R_LED_OFF: all LEDs are off
20f471d948SJanne Kanniainen  * GT683R_LED_AUDIO: LEDs brightness depends on sound level
21f471d948SJanne Kanniainen  * GT683R_LED_BREATHING: LEDs brightness varies at human breathing rate
22f471d948SJanne Kanniainen  * GT683R_LED_NORMAL: LEDs are fully on when enabled
23f471d948SJanne Kanniainen  */
24f471d948SJanne Kanniainen enum gt683r_led_mode {
25f471d948SJanne Kanniainen 	GT683R_LED_OFF = 0,
26f471d948SJanne Kanniainen 	GT683R_LED_AUDIO = 2,
27f471d948SJanne Kanniainen 	GT683R_LED_BREATHING = 3,
28f471d948SJanne Kanniainen 	GT683R_LED_NORMAL = 5
29f471d948SJanne Kanniainen };
30f471d948SJanne Kanniainen 
31f471d948SJanne Kanniainen enum gt683r_panels {
32f471d948SJanne Kanniainen 	GT683R_LED_BACK = 0,
33f471d948SJanne Kanniainen 	GT683R_LED_SIDE = 1,
34f471d948SJanne Kanniainen 	GT683R_LED_FRONT = 2,
35f471d948SJanne Kanniainen 	GT683R_LED_COUNT,
36f471d948SJanne Kanniainen };
37f471d948SJanne Kanniainen 
38f471d948SJanne Kanniainen static const char * const gt683r_panel_names[] = {
39f471d948SJanne Kanniainen 	"back",
40f471d948SJanne Kanniainen 	"side",
41f471d948SJanne Kanniainen 	"front",
42f471d948SJanne Kanniainen };
43f471d948SJanne Kanniainen 
44f471d948SJanne Kanniainen struct gt683r_led {
45f471d948SJanne Kanniainen 	struct hid_device *hdev;
46f471d948SJanne Kanniainen 	struct led_classdev led_devs[GT683R_LED_COUNT];
47f471d948SJanne Kanniainen 	struct mutex lock;
48f471d948SJanne Kanniainen 	struct work_struct work;
49f471d948SJanne Kanniainen 	enum led_brightness brightnesses[GT683R_LED_COUNT];
50f471d948SJanne Kanniainen 	enum gt683r_led_mode mode;
51f471d948SJanne Kanniainen };
52f471d948SJanne Kanniainen 
53f471d948SJanne Kanniainen static const struct hid_device_id gt683r_led_id[] = {
54f471d948SJanne Kanniainen 	{ HID_USB_DEVICE(USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL) },
55f471d948SJanne Kanniainen 	{ }
56f471d948SJanne Kanniainen };
57*a4b49409SBixuan Cui MODULE_DEVICE_TABLE(hid, gt683r_led_id);
58f471d948SJanne Kanniainen 
gt683r_brightness_set(struct led_classdev * led_cdev,enum led_brightness brightness)59f471d948SJanne Kanniainen static void gt683r_brightness_set(struct led_classdev *led_cdev,
60f471d948SJanne Kanniainen 				enum led_brightness brightness)
61f471d948SJanne Kanniainen {
62f471d948SJanne Kanniainen 	int i;
63f471d948SJanne Kanniainen 	struct device *dev = led_cdev->dev->parent;
64ee79a8f8SGeliang Tang 	struct hid_device *hdev = to_hid_device(dev);
65f471d948SJanne Kanniainen 	struct gt683r_led *led = hid_get_drvdata(hdev);
66f471d948SJanne Kanniainen 
67f471d948SJanne Kanniainen 	for (i = 0; i < GT683R_LED_COUNT; i++) {
68f471d948SJanne Kanniainen 		if (led_cdev == &led->led_devs[i])
69f471d948SJanne Kanniainen 			break;
70f471d948SJanne Kanniainen 	}
71f471d948SJanne Kanniainen 
72f471d948SJanne Kanniainen 	if (i < GT683R_LED_COUNT) {
73f471d948SJanne Kanniainen 		led->brightnesses[i] = brightness;
74f471d948SJanne Kanniainen 		schedule_work(&led->work);
75f471d948SJanne Kanniainen 	}
76f471d948SJanne Kanniainen }
77f471d948SJanne Kanniainen 
mode_show(struct device * dev,struct device_attribute * attr,char * buf)786522fe1cSJanne Kanniainen static ssize_t mode_show(struct device *dev,
79f471d948SJanne Kanniainen 				struct device_attribute *attr,
80f471d948SJanne Kanniainen 				char *buf)
81f471d948SJanne Kanniainen {
82f471d948SJanne Kanniainen 	u8 sysfs_mode;
83ee79a8f8SGeliang Tang 	struct hid_device *hdev = to_hid_device(dev->parent);
84f471d948SJanne Kanniainen 	struct gt683r_led *led = hid_get_drvdata(hdev);
85f471d948SJanne Kanniainen 
86f471d948SJanne Kanniainen 	if (led->mode == GT683R_LED_NORMAL)
87f471d948SJanne Kanniainen 		sysfs_mode = 0;
88f471d948SJanne Kanniainen 	else if (led->mode == GT683R_LED_AUDIO)
89f471d948SJanne Kanniainen 		sysfs_mode = 1;
90f471d948SJanne Kanniainen 	else
91f471d948SJanne Kanniainen 		sysfs_mode = 2;
92f471d948SJanne Kanniainen 
93f471d948SJanne Kanniainen 	return scnprintf(buf, PAGE_SIZE, "%u\n", sysfs_mode);
94f471d948SJanne Kanniainen }
95f471d948SJanne Kanniainen 
mode_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)966522fe1cSJanne Kanniainen static ssize_t mode_store(struct device *dev,
97f471d948SJanne Kanniainen 				struct device_attribute *attr,
98f471d948SJanne Kanniainen 				const char *buf, size_t count)
99f471d948SJanne Kanniainen {
100f471d948SJanne Kanniainen 	u8 sysfs_mode;
101ee79a8f8SGeliang Tang 	struct hid_device *hdev = to_hid_device(dev->parent);
102f471d948SJanne Kanniainen 	struct gt683r_led *led = hid_get_drvdata(hdev);
103f471d948SJanne Kanniainen 
104f471d948SJanne Kanniainen 
105f471d948SJanne Kanniainen 	if (kstrtou8(buf, 10, &sysfs_mode) || sysfs_mode > 2)
106f471d948SJanne Kanniainen 		return -EINVAL;
107f471d948SJanne Kanniainen 
108f471d948SJanne Kanniainen 	mutex_lock(&led->lock);
109f471d948SJanne Kanniainen 
110f471d948SJanne Kanniainen 	if (sysfs_mode == 0)
111f471d948SJanne Kanniainen 		led->mode = GT683R_LED_NORMAL;
112f471d948SJanne Kanniainen 	else if (sysfs_mode == 1)
113f471d948SJanne Kanniainen 		led->mode = GT683R_LED_AUDIO;
114f471d948SJanne Kanniainen 	else
115f471d948SJanne Kanniainen 		led->mode = GT683R_LED_BREATHING;
116f471d948SJanne Kanniainen 
117f471d948SJanne Kanniainen 	mutex_unlock(&led->lock);
118f471d948SJanne Kanniainen 	schedule_work(&led->work);
119f471d948SJanne Kanniainen 
120f471d948SJanne Kanniainen 	return count;
121f471d948SJanne Kanniainen }
122f471d948SJanne Kanniainen 
gt683r_led_snd_msg(struct gt683r_led * led,u8 * msg)123f471d948SJanne Kanniainen static int gt683r_led_snd_msg(struct gt683r_led *led, u8 *msg)
124f471d948SJanne Kanniainen {
125f471d948SJanne Kanniainen 	int ret;
126f471d948SJanne Kanniainen 
127f471d948SJanne Kanniainen 	ret = hid_hw_raw_request(led->hdev, msg[0], msg, GT683R_BUFFER_SIZE,
128f471d948SJanne Kanniainen 				HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
129f471d948SJanne Kanniainen 	if (ret != GT683R_BUFFER_SIZE) {
130f471d948SJanne Kanniainen 		hid_err(led->hdev,
131f471d948SJanne Kanniainen 			"failed to send set report request: %i\n", ret);
132f471d948SJanne Kanniainen 		if (ret < 0)
133f471d948SJanne Kanniainen 			return ret;
134f471d948SJanne Kanniainen 		return -EIO;
135f471d948SJanne Kanniainen 	}
136f471d948SJanne Kanniainen 
137f471d948SJanne Kanniainen 	return 0;
138f471d948SJanne Kanniainen }
139f471d948SJanne Kanniainen 
gt683r_leds_set(struct gt683r_led * led,u8 leds)140f471d948SJanne Kanniainen static int gt683r_leds_set(struct gt683r_led *led, u8 leds)
141f471d948SJanne Kanniainen {
142f471d948SJanne Kanniainen 	int ret;
143f471d948SJanne Kanniainen 	u8 *buffer;
144f471d948SJanne Kanniainen 
145f471d948SJanne Kanniainen 	buffer = kzalloc(GT683R_BUFFER_SIZE, GFP_KERNEL);
146f471d948SJanne Kanniainen 	if (!buffer)
147f471d948SJanne Kanniainen 		return -ENOMEM;
148f471d948SJanne Kanniainen 
149f471d948SJanne Kanniainen 	buffer[0] = 0x01;
150f471d948SJanne Kanniainen 	buffer[1] = 0x02;
151f471d948SJanne Kanniainen 	buffer[2] = 0x30;
152f471d948SJanne Kanniainen 	buffer[3] = leds;
153f471d948SJanne Kanniainen 	ret = gt683r_led_snd_msg(led, buffer);
154f471d948SJanne Kanniainen 
155f471d948SJanne Kanniainen 	kfree(buffer);
156f471d948SJanne Kanniainen 	return ret;
157f471d948SJanne Kanniainen }
158f471d948SJanne Kanniainen 
gt683r_mode_set(struct gt683r_led * led,u8 mode)159f471d948SJanne Kanniainen static int gt683r_mode_set(struct gt683r_led *led, u8 mode)
160f471d948SJanne Kanniainen {
161f471d948SJanne Kanniainen 	int ret;
162f471d948SJanne Kanniainen 	u8 *buffer;
163f471d948SJanne Kanniainen 
164f471d948SJanne Kanniainen 	buffer = kzalloc(GT683R_BUFFER_SIZE, GFP_KERNEL);
165f471d948SJanne Kanniainen 	if (!buffer)
166f471d948SJanne Kanniainen 		return -ENOMEM;
167f471d948SJanne Kanniainen 
168f471d948SJanne Kanniainen 	buffer[0] = 0x01;
169f471d948SJanne Kanniainen 	buffer[1] = 0x02;
170f471d948SJanne Kanniainen 	buffer[2] = 0x20;
171f471d948SJanne Kanniainen 	buffer[3] = mode;
172f471d948SJanne Kanniainen 	buffer[4] = 0x01;
173f471d948SJanne Kanniainen 	ret = gt683r_led_snd_msg(led, buffer);
174f471d948SJanne Kanniainen 
175f471d948SJanne Kanniainen 	kfree(buffer);
176f471d948SJanne Kanniainen 	return ret;
177f471d948SJanne Kanniainen }
178f471d948SJanne Kanniainen 
gt683r_led_work(struct work_struct * work)179f471d948SJanne Kanniainen static void gt683r_led_work(struct work_struct *work)
180f471d948SJanne Kanniainen {
181f471d948SJanne Kanniainen 	int i;
182f471d948SJanne Kanniainen 	u8 leds = 0;
183f471d948SJanne Kanniainen 	u8 mode;
184f471d948SJanne Kanniainen 	struct gt683r_led *led = container_of(work, struct gt683r_led, work);
185f471d948SJanne Kanniainen 
186f471d948SJanne Kanniainen 	mutex_lock(&led->lock);
187f471d948SJanne Kanniainen 
188f471d948SJanne Kanniainen 	for (i = 0; i < GT683R_LED_COUNT; i++) {
189f471d948SJanne Kanniainen 		if (led->brightnesses[i])
190f471d948SJanne Kanniainen 			leds |= BIT(i);
191f471d948SJanne Kanniainen 	}
192f471d948SJanne Kanniainen 
193f471d948SJanne Kanniainen 	if (gt683r_leds_set(led, leds))
194f471d948SJanne Kanniainen 		goto fail;
195f471d948SJanne Kanniainen 
196f471d948SJanne Kanniainen 	if (leds)
197f471d948SJanne Kanniainen 		mode = led->mode;
198f471d948SJanne Kanniainen 	else
199f471d948SJanne Kanniainen 		mode = GT683R_LED_OFF;
200f471d948SJanne Kanniainen 
201f471d948SJanne Kanniainen 	gt683r_mode_set(led, mode);
202f471d948SJanne Kanniainen fail:
203f471d948SJanne Kanniainen 	mutex_unlock(&led->lock);
204f471d948SJanne Kanniainen }
205f471d948SJanne Kanniainen 
2066522fe1cSJanne Kanniainen static DEVICE_ATTR_RW(mode);
2076522fe1cSJanne Kanniainen 
2086522fe1cSJanne Kanniainen static struct attribute *gt683r_led_attrs[] = {
2096522fe1cSJanne Kanniainen 	&dev_attr_mode.attr,
2106522fe1cSJanne Kanniainen 	NULL
2116522fe1cSJanne Kanniainen };
2126522fe1cSJanne Kanniainen 
2136522fe1cSJanne Kanniainen static const struct attribute_group gt683r_led_group = {
2146522fe1cSJanne Kanniainen 	.name = "gt683r",
2156522fe1cSJanne Kanniainen 	.attrs = gt683r_led_attrs,
2166522fe1cSJanne Kanniainen };
2176522fe1cSJanne Kanniainen 
2186522fe1cSJanne Kanniainen static const struct attribute_group *gt683r_led_groups[] = {
2196522fe1cSJanne Kanniainen 	&gt683r_led_group,
2206522fe1cSJanne Kanniainen 	NULL
2216522fe1cSJanne Kanniainen };
222f471d948SJanne Kanniainen 
gt683r_led_probe(struct hid_device * hdev,const struct hid_device_id * id)223f471d948SJanne Kanniainen static int gt683r_led_probe(struct hid_device *hdev,
224f471d948SJanne Kanniainen 			const struct hid_device_id *id)
225f471d948SJanne Kanniainen {
226f471d948SJanne Kanniainen 	int i;
227f471d948SJanne Kanniainen 	int ret;
228f471d948SJanne Kanniainen 	int name_sz;
229f471d948SJanne Kanniainen 	char *name;
230f471d948SJanne Kanniainen 	struct gt683r_led *led;
231f471d948SJanne Kanniainen 
232f471d948SJanne Kanniainen 	led = devm_kzalloc(&hdev->dev, sizeof(*led), GFP_KERNEL);
233f471d948SJanne Kanniainen 	if (!led)
234f471d948SJanne Kanniainen 		return -ENOMEM;
235f471d948SJanne Kanniainen 
236c3883ae9SJanne Kanniainen 	mutex_init(&led->lock);
237c3883ae9SJanne Kanniainen 	INIT_WORK(&led->work, gt683r_led_work);
238c3883ae9SJanne Kanniainen 
239f471d948SJanne Kanniainen 	led->mode = GT683R_LED_NORMAL;
240f471d948SJanne Kanniainen 	led->hdev = hdev;
241f471d948SJanne Kanniainen 	hid_set_drvdata(hdev, led);
242f471d948SJanne Kanniainen 
243f471d948SJanne Kanniainen 	ret = hid_parse(hdev);
244f471d948SJanne Kanniainen 	if (ret) {
245f471d948SJanne Kanniainen 		hid_err(hdev, "hid parsing failed\n");
246f471d948SJanne Kanniainen 		return ret;
247f471d948SJanne Kanniainen 	}
248f471d948SJanne Kanniainen 
249f471d948SJanne Kanniainen 	ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
250f471d948SJanne Kanniainen 	if (ret) {
251f471d948SJanne Kanniainen 		hid_err(hdev, "hw start failed\n");
252f471d948SJanne Kanniainen 		return ret;
253f471d948SJanne Kanniainen 	}
254f471d948SJanne Kanniainen 
255f471d948SJanne Kanniainen 	for (i = 0; i < GT683R_LED_COUNT; i++) {
256f471d948SJanne Kanniainen 		name_sz = strlen(dev_name(&hdev->dev)) +
257f471d948SJanne Kanniainen 				strlen(gt683r_panel_names[i]) + 3;
258f471d948SJanne Kanniainen 
259f471d948SJanne Kanniainen 		name = devm_kzalloc(&hdev->dev, name_sz, GFP_KERNEL);
260f471d948SJanne Kanniainen 		if (!name) {
261f471d948SJanne Kanniainen 			ret = -ENOMEM;
262f471d948SJanne Kanniainen 			goto fail;
263f471d948SJanne Kanniainen 		}
264f471d948SJanne Kanniainen 
265f471d948SJanne Kanniainen 		snprintf(name, name_sz, "%s::%s",
266f471d948SJanne Kanniainen 				dev_name(&hdev->dev), gt683r_panel_names[i]);
267f471d948SJanne Kanniainen 		led->led_devs[i].name = name;
268f471d948SJanne Kanniainen 		led->led_devs[i].max_brightness = 1;
269f471d948SJanne Kanniainen 		led->led_devs[i].brightness_set = gt683r_brightness_set;
2706522fe1cSJanne Kanniainen 		led->led_devs[i].groups = gt683r_led_groups;
2716522fe1cSJanne Kanniainen 
272f471d948SJanne Kanniainen 		ret = led_classdev_register(&hdev->dev, &led->led_devs[i]);
273f471d948SJanne Kanniainen 		if (ret) {
274f471d948SJanne Kanniainen 			hid_err(hdev, "could not register led device\n");
275f471d948SJanne Kanniainen 			goto fail;
276f471d948SJanne Kanniainen 		}
277f471d948SJanne Kanniainen 	}
278f471d948SJanne Kanniainen 
279f471d948SJanne Kanniainen 	return 0;
280f471d948SJanne Kanniainen 
281f471d948SJanne Kanniainen fail:
282f471d948SJanne Kanniainen 	for (i = i - 1; i >= 0; i--)
283f471d948SJanne Kanniainen 		led_classdev_unregister(&led->led_devs[i]);
284f471d948SJanne Kanniainen 	hid_hw_stop(hdev);
285f471d948SJanne Kanniainen 	return ret;
286f471d948SJanne Kanniainen }
287f471d948SJanne Kanniainen 
gt683r_led_remove(struct hid_device * hdev)288f471d948SJanne Kanniainen static void gt683r_led_remove(struct hid_device *hdev)
289f471d948SJanne Kanniainen {
290f471d948SJanne Kanniainen 	int i;
291f471d948SJanne Kanniainen 	struct gt683r_led *led = hid_get_drvdata(hdev);
292f471d948SJanne Kanniainen 
293f471d948SJanne Kanniainen 	for (i = 0; i < GT683R_LED_COUNT; i++)
294f471d948SJanne Kanniainen 		led_classdev_unregister(&led->led_devs[i]);
295f471d948SJanne Kanniainen 	flush_work(&led->work);
296f471d948SJanne Kanniainen 	hid_hw_stop(hdev);
297f471d948SJanne Kanniainen }
298f471d948SJanne Kanniainen 
299f471d948SJanne Kanniainen static struct hid_driver gt683r_led_driver = {
300f471d948SJanne Kanniainen 	.probe = gt683r_led_probe,
301f471d948SJanne Kanniainen 	.remove = gt683r_led_remove,
302f471d948SJanne Kanniainen 	.name = "gt683r_led",
303f471d948SJanne Kanniainen 	.id_table = gt683r_led_id,
304f471d948SJanne Kanniainen };
305f471d948SJanne Kanniainen 
306f471d948SJanne Kanniainen module_hid_driver(gt683r_led_driver);
307f471d948SJanne Kanniainen 
308f471d948SJanne Kanniainen MODULE_AUTHOR("Janne Kanniainen");
309f471d948SJanne Kanniainen MODULE_DESCRIPTION("MSI GT683R led driver");
310f471d948SJanne Kanniainen MODULE_LICENSE("GPL");
311