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