diff options
Diffstat (limited to 'drivers/hid')
-rw-r--r-- | drivers/hid/Kconfig | 6 | ||||
-rw-r--r-- | drivers/hid/hid-picolcd.c | 163 |
2 files changed, 166 insertions, 3 deletions
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 399edc53963e..34f6593ea3b6 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig | |||
@@ -278,11 +278,11 @@ config HID_PICOLCD | |||
278 | - Keypad | 278 | - Keypad |
279 | - Switching between Firmware and Flash mode | 279 | - Switching between Firmware and Flash mode |
280 | - Framebuffer for monochrome 256x64 display | 280 | - Framebuffer for monochrome 256x64 display |
281 | - Backlight control (needs CONFIG_BACKLIGHT_CLASS_DEVICE) | 281 | - Backlight control (needs CONFIG_BACKLIGHT_CLASS_DEVICE) |
282 | - Contrast control (needs CONFIG_LCD_CLASS_DEVICE) | 282 | - Contrast control (needs CONFIG_LCD_CLASS_DEVICE) |
283 | - General purpose outputs (needs CONFIG_LEDS_CLASS) | ||
283 | Features that are not (yet) supported: | 284 | Features that are not (yet) supported: |
284 | - IR | 285 | - IR |
285 | - General purpose outputs | ||
286 | - EEProm / Flash access | 286 | - EEProm / Flash access |
287 | 287 | ||
288 | config HID_QUANTA | 288 | config HID_QUANTA |
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 | */ | ||
961 | static 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 | |||
978 | static 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 | |||
1004 | static 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 | |||
1022 | static 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; | ||
1063 | err: | ||
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 | |||
1074 | static 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 | ||
1090 | static inline int picolcd_init_leds(struct picolcd_data *data, | ||
1091 | struct hid_report *report) | ||
1092 | { | ||
1093 | return 0; | ||
1094 | } | ||
1095 | static void picolcd_exit_leds(struct picolcd_data *data) | ||
1096 | { | ||
1097 | } | ||
1098 | static 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; |
1793 | err: | 1953 | err: |
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); |