xref: /OK3568_Linux_fs/kernel/drivers/hid/hid-picolcd_leds.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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