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