aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/hid/hid-picolcd.c
diff options
context:
space:
mode:
authorBruno Prémont <bonbons@linux-vserver.org>2010-03-30 16:36:49 -0400
committerJiri Kosina <jkosina@suse.cz>2010-03-31 05:32:29 -0400
commit467d6523065187d4c081b078755da4103d7ffacb (patch)
tree58050a9aa061974ffebefc8952e4793a2eab0711 /drivers/hid/hid-picolcd.c
parente8d931bb5977a5b36cc8e9b0fd2f26cb80a6c207 (diff)
HID: add GPO (leds) support to PicoLCD device
Add leds support to PicoLCD device to drive the GPO pins. GPO support depends on leds class and is only being compiled if leds class has been selected. Signed-off-by: Bruno Prémont <bonbons@linux-vserver.org> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
Diffstat (limited to 'drivers/hid/hid-picolcd.c')
-rw-r--r--drivers/hid/hid-picolcd.c163
1 files changed, 163 insertions, 0 deletions
diff --git a/drivers/hid/hid-picolcd.c b/drivers/hid/hid-picolcd.c
index 99a488363a4e..517677305ef9 100644
--- a/drivers/hid/hid-picolcd.c
+++ b/drivers/hid/hid-picolcd.c
@@ -29,6 +29,8 @@
29#include <linux/backlight.h> 29#include <linux/backlight.h>
30#include <linux/lcd.h> 30#include <linux/lcd.h>
31 31
32#include <linux/leds.h>
33
32#include <linux/seq_file.h> 34#include <linux/seq_file.h>
33#include <linux/debugfs.h> 35#include <linux/debugfs.h>
34 36
@@ -194,6 +196,11 @@ struct picolcd_data {
194 u8 lcd_brightness; 196 u8 lcd_brightness;
195 u8 lcd_power; 197 u8 lcd_power;
196#endif /* CONFIG_BACKLIGHT_CLASS_DEVICE */ 198#endif /* CONFIG_BACKLIGHT_CLASS_DEVICE */
199#if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE)
200 /* LED stuff */
201 u8 led_state;
202 struct led_classdev *led[8];
203#endif /* CONFIG_LEDS_CLASS */
197 204
198 /* Housekeeping stuff */ 205 /* Housekeeping stuff */
199 spinlock_t lock; 206 spinlock_t lock;
@@ -947,6 +954,153 @@ static inline int picolcd_resume_lcd(struct picolcd_data *data)
947} 954}
948#endif /* CONFIG_LCD_CLASS_DEVICE */ 955#endif /* CONFIG_LCD_CLASS_DEVICE */
949 956
957#if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE)
958/**
959 * LED class device
960 */
961static void picolcd_leds_set(struct picolcd_data *data)
962{
963 struct hid_report *report;
964 unsigned long flags;
965
966 if (!data->led[0])
967 return;
968 report = picolcd_out_report(REPORT_LED_STATE, data->hdev);
969 if (!report || report->maxfield != 1 || report->field[0]->report_count != 1)
970 return;
971
972 spin_lock_irqsave(&data->lock, flags);
973 hid_set_field(report->field[0], 0, data->led_state);
974 usbhid_submit_report(data->hdev, report, USB_DIR_OUT);
975 spin_unlock_irqrestore(&data->lock, flags);
976}
977
978static void picolcd_led_set_brightness(struct led_classdev *led_cdev,
979 enum led_brightness value)
980{
981 struct device *dev;
982 struct hid_device *hdev;
983 struct picolcd_data *data;
984 int i, state = 0;
985
986 dev = led_cdev->dev->parent;
987 hdev = container_of(dev, struct hid_device, dev);
988 data = hid_get_drvdata(hdev);
989 for (i = 0; i < 8; i++) {
990 if (led_cdev != data->led[i])
991 continue;
992 state = (data->led_state >> i) & 1;
993 if (value == LED_OFF && state) {
994 data->led_state &= ~(1 << i);
995 picolcd_leds_set(data);
996 } else if (value != LED_OFF && !state) {
997 data->led_state |= 1 << i;
998 picolcd_leds_set(data);
999 }
1000 break;
1001 }
1002}
1003
1004static enum led_brightness picolcd_led_get_brightness(struct led_classdev *led_cdev)
1005{
1006 struct device *dev;
1007 struct hid_device *hdev;
1008 struct picolcd_data *data;
1009 int i, value = 0;
1010
1011 dev = led_cdev->dev->parent;
1012 hdev = container_of(dev, struct hid_device, dev);
1013 data = hid_get_drvdata(hdev);
1014 for (i = 0; i < 8; i++)
1015 if (led_cdev == data->led[i]) {
1016 value = (data->led_state >> i) & 1;
1017 break;
1018 }
1019 return value ? LED_FULL : LED_OFF;
1020}
1021
1022static int picolcd_init_leds(struct picolcd_data *data, struct hid_report *report)
1023{
1024 struct device *dev = &data->hdev->dev;
1025 struct led_classdev *led;
1026 size_t name_sz = strlen(dev_name(dev)) + 8;
1027 char *name;
1028 int i, ret = 0;
1029
1030 if (!report)
1031 return -ENODEV;
1032 if (report->maxfield != 1 || report->field[0]->report_count != 1 ||
1033 report->field[0]->report_size != 8) {
1034 dev_err(dev, "unsupported LED_STATE report");
1035 return -EINVAL;
1036 }
1037
1038 for (i = 0; i < 8; i++) {
1039 led = kzalloc(sizeof(struct led_classdev)+name_sz, GFP_KERNEL);
1040 if (!led) {
1041 dev_err(dev, "can't allocate memory for LED %d\n", i);
1042 ret = -ENOMEM;
1043 goto err;
1044 }
1045 name = (void *)(&led[1]);
1046 snprintf(name, name_sz, "%s::GPO%d", dev_name(dev), i);
1047 led->name = name;
1048 led->brightness = 0;
1049 led->max_brightness = 1;
1050 led->brightness_get = picolcd_led_get_brightness;
1051 led->brightness_set = picolcd_led_set_brightness;
1052
1053 data->led[i] = led;
1054 ret = led_classdev_register(dev, data->led[i]);
1055 if (ret) {
1056 data->led[i] = NULL;
1057 kfree(led);
1058 dev_err(dev, "can't register LED %d\n", i);
1059 goto err;
1060 }
1061 }
1062 return 0;
1063err:
1064 for (i = 0; i < 8; i++)
1065 if (data->led[i]) {
1066 led = data->led[i];
1067 data->led[i] = NULL;
1068 led_classdev_unregister(led);
1069 kfree(led);
1070 }
1071 return ret;
1072}
1073
1074static void picolcd_exit_leds(struct picolcd_data *data)
1075{
1076 struct led_classdev *led;
1077 int i;
1078
1079 for (i = 0; i < 8; i++) {
1080 led = data->led[i];
1081 data->led[i] = NULL;
1082 if (!led)
1083 continue;
1084 led_classdev_unregister(led);
1085 kfree(led);
1086 }
1087}
1088
1089#else
1090static inline int picolcd_init_leds(struct picolcd_data *data,
1091 struct hid_report *report)
1092{
1093 return 0;
1094}
1095static void picolcd_exit_leds(struct picolcd_data *data)
1096{
1097}
1098static inline int picolcd_leds_set(struct picolcd_data *data)
1099{
1100 return 0;
1101}
1102#endif /* CONFIG_LEDS_CLASS */
1103
950/* 1104/*
951 * input class device 1105 * input class device
952 */ 1106 */
@@ -1089,6 +1243,7 @@ static int picolcd_reset(struct hid_device *hdev)
1089 schedule_delayed_work(&data->fb_info->deferred_work, 0); 1243 schedule_delayed_work(&data->fb_info->deferred_work, 0);
1090#endif /* CONFIG_FB */ 1244#endif /* CONFIG_FB */
1091 1245
1246 picolcd_leds_set(data);
1092 return 0; 1247 return 0;
1093} 1248}
1094 1249
@@ -1782,6 +1937,11 @@ static int picolcd_probe_lcd(struct hid_device *hdev, struct picolcd_data *data)
1782 if (error) 1937 if (error)
1783 goto err; 1938 goto err;
1784 1939
1940 /* Setup the LED class devices */
1941 error = picolcd_init_leds(data, picolcd_out_report(REPORT_LED_STATE, hdev));
1942 if (error)
1943 goto err;
1944
1785#ifdef CONFIG_DEBUG_FS 1945#ifdef CONFIG_DEBUG_FS
1786 report = picolcd_out_report(REPORT_READ_MEMORY, hdev); 1946 report = picolcd_out_report(REPORT_READ_MEMORY, hdev);
1787 if (report && report->maxfield == 1 && report->field[0]->report_size == 8) 1947 if (report && report->maxfield == 1 && report->field[0]->report_size == 8)
@@ -1791,6 +1951,7 @@ static int picolcd_probe_lcd(struct hid_device *hdev, struct picolcd_data *data)
1791#endif 1951#endif
1792 return 0; 1952 return 0;
1793err: 1953err:
1954 picolcd_exit_leds(data);
1794 picolcd_exit_backlight(data); 1955 picolcd_exit_backlight(data);
1795 picolcd_exit_lcd(data); 1956 picolcd_exit_lcd(data);
1796 picolcd_exit_framebuffer(data); 1957 picolcd_exit_framebuffer(data);
@@ -1923,6 +2084,8 @@ static void picolcd_remove(struct hid_device *hdev)
1923 complete(&data->pending->ready); 2084 complete(&data->pending->ready);
1924 spin_unlock_irqrestore(&data->lock, flags); 2085 spin_unlock_irqrestore(&data->lock, flags);
1925 2086
2087 /* Cleanup LED */
2088 picolcd_exit_leds(data);
1926 /* Clean up the framebuffer */ 2089 /* Clean up the framebuffer */
1927 picolcd_exit_backlight(data); 2090 picolcd_exit_backlight(data);
1928 picolcd_exit_lcd(data); 2091 picolcd_exit_lcd(data);