1 // SPDX-License-Identifier: GPL-2.0-only 2 /*************************************************************************** 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 * * 8 ***************************************************************************/ 9 10 #include <linux/hid.h> 11 #include <linux/hid-debug.h> 12 #include <linux/input.h> 13 #include "hid-ids.h" 14 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 32 void 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); 45 if (!(data->status & PICOLCD_FAILED)) 46 hid_hw_request(data->hdev, report, HID_REQ_SET_REPORT); 47 spin_unlock_irqrestore(&data->lock, flags); 48 } 49 50 static 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; 59 hdev = to_hid_device(dev); 60 data = hid_get_drvdata(hdev); 61 if (!data) 62 return; 63 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 78 static 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; 86 hdev = to_hid_device(dev); 87 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 96 int 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; 137 err: 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 148 void 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 164