1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /***************************************************************************
3*4882a593Smuzhiyun * Copyright (C) 2010-2012 by Bruno Prémont <bonbons@linux-vserver.org> *
4*4882a593Smuzhiyun * *
5*4882a593Smuzhiyun * Based on Logitech G13 driver (v0.4) *
6*4882a593Smuzhiyun * Copyright (C) 2009 by Rick L. Vinyard, Jr. <rvinyard@cs.nmsu.edu> *
7*4882a593Smuzhiyun * *
8*4882a593Smuzhiyun ***************************************************************************/
9*4882a593Smuzhiyun
10*4882a593Smuzhiyun #include <linux/hid.h>
11*4882a593Smuzhiyun #include <linux/hid-debug.h>
12*4882a593Smuzhiyun #include <linux/input.h>
13*4882a593Smuzhiyun #include "hid-ids.h"
14*4882a593Smuzhiyun
15*4882a593Smuzhiyun #include <linux/fb.h>
16*4882a593Smuzhiyun #include <linux/vmalloc.h>
17*4882a593Smuzhiyun #include <linux/backlight.h>
18*4882a593Smuzhiyun #include <linux/lcd.h>
19*4882a593Smuzhiyun
20*4882a593Smuzhiyun #include <linux/leds.h>
21*4882a593Smuzhiyun
22*4882a593Smuzhiyun #include <linux/seq_file.h>
23*4882a593Smuzhiyun #include <linux/debugfs.h>
24*4882a593Smuzhiyun
25*4882a593Smuzhiyun #include <linux/completion.h>
26*4882a593Smuzhiyun #include <linux/uaccess.h>
27*4882a593Smuzhiyun #include <linux/module.h>
28*4882a593Smuzhiyun
29*4882a593Smuzhiyun #include "hid-picolcd.h"
30*4882a593Smuzhiyun
31*4882a593Smuzhiyun
picolcd_leds_set(struct picolcd_data * data)32*4882a593Smuzhiyun void picolcd_leds_set(struct picolcd_data *data)
33*4882a593Smuzhiyun {
34*4882a593Smuzhiyun struct hid_report *report;
35*4882a593Smuzhiyun unsigned long flags;
36*4882a593Smuzhiyun
37*4882a593Smuzhiyun if (!data->led[0])
38*4882a593Smuzhiyun return;
39*4882a593Smuzhiyun report = picolcd_out_report(REPORT_LED_STATE, data->hdev);
40*4882a593Smuzhiyun if (!report || report->maxfield != 1 || report->field[0]->report_count != 1)
41*4882a593Smuzhiyun return;
42*4882a593Smuzhiyun
43*4882a593Smuzhiyun spin_lock_irqsave(&data->lock, flags);
44*4882a593Smuzhiyun hid_set_field(report->field[0], 0, data->led_state);
45*4882a593Smuzhiyun if (!(data->status & PICOLCD_FAILED))
46*4882a593Smuzhiyun hid_hw_request(data->hdev, report, HID_REQ_SET_REPORT);
47*4882a593Smuzhiyun spin_unlock_irqrestore(&data->lock, flags);
48*4882a593Smuzhiyun }
49*4882a593Smuzhiyun
picolcd_led_set_brightness(struct led_classdev * led_cdev,enum led_brightness value)50*4882a593Smuzhiyun static void picolcd_led_set_brightness(struct led_classdev *led_cdev,
51*4882a593Smuzhiyun enum led_brightness value)
52*4882a593Smuzhiyun {
53*4882a593Smuzhiyun struct device *dev;
54*4882a593Smuzhiyun struct hid_device *hdev;
55*4882a593Smuzhiyun struct picolcd_data *data;
56*4882a593Smuzhiyun int i, state = 0;
57*4882a593Smuzhiyun
58*4882a593Smuzhiyun dev = led_cdev->dev->parent;
59*4882a593Smuzhiyun hdev = to_hid_device(dev);
60*4882a593Smuzhiyun data = hid_get_drvdata(hdev);
61*4882a593Smuzhiyun if (!data)
62*4882a593Smuzhiyun return;
63*4882a593Smuzhiyun for (i = 0; i < 8; i++) {
64*4882a593Smuzhiyun if (led_cdev != data->led[i])
65*4882a593Smuzhiyun continue;
66*4882a593Smuzhiyun state = (data->led_state >> i) & 1;
67*4882a593Smuzhiyun if (value == LED_OFF && state) {
68*4882a593Smuzhiyun data->led_state &= ~(1 << i);
69*4882a593Smuzhiyun picolcd_leds_set(data);
70*4882a593Smuzhiyun } else if (value != LED_OFF && !state) {
71*4882a593Smuzhiyun data->led_state |= 1 << i;
72*4882a593Smuzhiyun picolcd_leds_set(data);
73*4882a593Smuzhiyun }
74*4882a593Smuzhiyun break;
75*4882a593Smuzhiyun }
76*4882a593Smuzhiyun }
77*4882a593Smuzhiyun
picolcd_led_get_brightness(struct led_classdev * led_cdev)78*4882a593Smuzhiyun static enum led_brightness picolcd_led_get_brightness(struct led_classdev *led_cdev)
79*4882a593Smuzhiyun {
80*4882a593Smuzhiyun struct device *dev;
81*4882a593Smuzhiyun struct hid_device *hdev;
82*4882a593Smuzhiyun struct picolcd_data *data;
83*4882a593Smuzhiyun int i, value = 0;
84*4882a593Smuzhiyun
85*4882a593Smuzhiyun dev = led_cdev->dev->parent;
86*4882a593Smuzhiyun hdev = to_hid_device(dev);
87*4882a593Smuzhiyun data = hid_get_drvdata(hdev);
88*4882a593Smuzhiyun for (i = 0; i < 8; i++)
89*4882a593Smuzhiyun if (led_cdev == data->led[i]) {
90*4882a593Smuzhiyun value = (data->led_state >> i) & 1;
91*4882a593Smuzhiyun break;
92*4882a593Smuzhiyun }
93*4882a593Smuzhiyun return value ? LED_FULL : LED_OFF;
94*4882a593Smuzhiyun }
95*4882a593Smuzhiyun
picolcd_init_leds(struct picolcd_data * data,struct hid_report * report)96*4882a593Smuzhiyun int picolcd_init_leds(struct picolcd_data *data, struct hid_report *report)
97*4882a593Smuzhiyun {
98*4882a593Smuzhiyun struct device *dev = &data->hdev->dev;
99*4882a593Smuzhiyun struct led_classdev *led;
100*4882a593Smuzhiyun size_t name_sz = strlen(dev_name(dev)) + 8;
101*4882a593Smuzhiyun char *name;
102*4882a593Smuzhiyun int i, ret = 0;
103*4882a593Smuzhiyun
104*4882a593Smuzhiyun if (!report)
105*4882a593Smuzhiyun return -ENODEV;
106*4882a593Smuzhiyun if (report->maxfield != 1 || report->field[0]->report_count != 1 ||
107*4882a593Smuzhiyun report->field[0]->report_size != 8) {
108*4882a593Smuzhiyun dev_err(dev, "unsupported LED_STATE report");
109*4882a593Smuzhiyun return -EINVAL;
110*4882a593Smuzhiyun }
111*4882a593Smuzhiyun
112*4882a593Smuzhiyun for (i = 0; i < 8; i++) {
113*4882a593Smuzhiyun led = kzalloc(sizeof(struct led_classdev)+name_sz, GFP_KERNEL);
114*4882a593Smuzhiyun if (!led) {
115*4882a593Smuzhiyun dev_err(dev, "can't allocate memory for LED %d\n", i);
116*4882a593Smuzhiyun ret = -ENOMEM;
117*4882a593Smuzhiyun goto err;
118*4882a593Smuzhiyun }
119*4882a593Smuzhiyun name = (void *)(&led[1]);
120*4882a593Smuzhiyun snprintf(name, name_sz, "%s::GPO%d", dev_name(dev), i);
121*4882a593Smuzhiyun led->name = name;
122*4882a593Smuzhiyun led->brightness = 0;
123*4882a593Smuzhiyun led->max_brightness = 1;
124*4882a593Smuzhiyun led->brightness_get = picolcd_led_get_brightness;
125*4882a593Smuzhiyun led->brightness_set = picolcd_led_set_brightness;
126*4882a593Smuzhiyun
127*4882a593Smuzhiyun data->led[i] = led;
128*4882a593Smuzhiyun ret = led_classdev_register(dev, data->led[i]);
129*4882a593Smuzhiyun if (ret) {
130*4882a593Smuzhiyun data->led[i] = NULL;
131*4882a593Smuzhiyun kfree(led);
132*4882a593Smuzhiyun dev_err(dev, "can't register LED %d\n", i);
133*4882a593Smuzhiyun goto err;
134*4882a593Smuzhiyun }
135*4882a593Smuzhiyun }
136*4882a593Smuzhiyun return 0;
137*4882a593Smuzhiyun err:
138*4882a593Smuzhiyun for (i = 0; i < 8; i++)
139*4882a593Smuzhiyun if (data->led[i]) {
140*4882a593Smuzhiyun led = data->led[i];
141*4882a593Smuzhiyun data->led[i] = NULL;
142*4882a593Smuzhiyun led_classdev_unregister(led);
143*4882a593Smuzhiyun kfree(led);
144*4882a593Smuzhiyun }
145*4882a593Smuzhiyun return ret;
146*4882a593Smuzhiyun }
147*4882a593Smuzhiyun
picolcd_exit_leds(struct picolcd_data * data)148*4882a593Smuzhiyun void picolcd_exit_leds(struct picolcd_data *data)
149*4882a593Smuzhiyun {
150*4882a593Smuzhiyun struct led_classdev *led;
151*4882a593Smuzhiyun int i;
152*4882a593Smuzhiyun
153*4882a593Smuzhiyun for (i = 0; i < 8; i++) {
154*4882a593Smuzhiyun led = data->led[i];
155*4882a593Smuzhiyun data->led[i] = NULL;
156*4882a593Smuzhiyun if (!led)
157*4882a593Smuzhiyun continue;
158*4882a593Smuzhiyun led_classdev_unregister(led);
159*4882a593Smuzhiyun kfree(led);
160*4882a593Smuzhiyun }
161*4882a593Smuzhiyun }
162*4882a593Smuzhiyun
163*4882a593Smuzhiyun
164