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