diff options
| -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); |
