diff options
| -rw-r--r-- | drivers/hid/Kconfig | 2 | ||||
| -rw-r--r-- | drivers/hid/hid-picolcd.c | 115 |
2 files changed, 115 insertions, 2 deletions
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index a813ea9c792d..588b9acecb7a 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig | |||
| @@ -278,8 +278,8 @@ 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 | Features that are not (yet) supported: | 282 | Features that are not (yet) supported: |
| 282 | - Backlight control | ||
| 283 | - Contrast control | 283 | - Contrast control |
| 284 | - IR | 284 | - IR |
| 285 | - General purpose outputs | 285 | - General purpose outputs |
diff --git a/drivers/hid/hid-picolcd.c b/drivers/hid/hid-picolcd.c index e14464789e10..0ea7a3f44bb5 100644 --- a/drivers/hid/hid-picolcd.c +++ b/drivers/hid/hid-picolcd.c | |||
| @@ -26,6 +26,7 @@ | |||
| 26 | 26 | ||
| 27 | #include <linux/fb.h> | 27 | #include <linux/fb.h> |
| 28 | #include <linux/vmalloc.h> | 28 | #include <linux/vmalloc.h> |
| 29 | #include <linux/backlight.h> | ||
| 29 | 30 | ||
| 30 | #include <linux/seq_file.h> | 31 | #include <linux/seq_file.h> |
| 31 | #include <linux/debugfs.h> | 32 | #include <linux/debugfs.h> |
| @@ -183,6 +184,11 @@ struct picolcd_data { | |||
| 183 | struct fb_info *fb_info; | 184 | struct fb_info *fb_info; |
| 184 | struct fb_deferred_io fb_defio; | 185 | struct fb_deferred_io fb_defio; |
| 185 | #endif /* CONFIG_FB */ | 186 | #endif /* CONFIG_FB */ |
| 187 | #if defined(CONFIG_BACKLIGHT_CLASS_DEVICE) || defined(CONFIG_BACKLIGHT_CLASS_DEVICE_MODULE) | ||
| 188 | struct backlight_device *backlight; | ||
| 189 | u8 lcd_brightness; | ||
| 190 | u8 lcd_power; | ||
| 191 | #endif /* CONFIG_BACKLIGHT_CLASS_DEVICE */ | ||
| 186 | 192 | ||
| 187 | /* Housekeeping stuff */ | 193 | /* Housekeeping stuff */ |
| 188 | spinlock_t lock; | 194 | spinlock_t lock; |
| @@ -729,7 +735,7 @@ static void picolcd_exit_framebuffer(struct picolcd_data *data) | |||
| 729 | kfree(fb_vbitmap); | 735 | kfree(fb_vbitmap); |
| 730 | } | 736 | } |
| 731 | 737 | ||
| 732 | 738 | #define picolcd_fbinfo(d) ((d)->fb_info) | |
| 733 | #else | 739 | #else |
| 734 | static inline int picolcd_fb_reset(struct picolcd_data *data, int clear) | 740 | static inline int picolcd_fb_reset(struct picolcd_data *data, int clear) |
| 735 | { | 741 | { |
| @@ -742,8 +748,107 @@ static inline int picolcd_init_framebuffer(struct picolcd_data *data) | |||
| 742 | static void picolcd_exit_framebuffer(struct picolcd_data *data) | 748 | static void picolcd_exit_framebuffer(struct picolcd_data *data) |
| 743 | { | 749 | { |
| 744 | } | 750 | } |
| 751 | #define picolcd_fbinfo(d) NULL | ||
| 745 | #endif /* CONFIG_FB */ | 752 | #endif /* CONFIG_FB */ |
| 746 | 753 | ||
| 754 | #if defined(CONFIG_BACKLIGHT_CLASS_DEVICE) || defined(CONFIG_BACKLIGHT_CLASS_DEVICE_MODULE) | ||
| 755 | /* | ||
| 756 | * backlight class device | ||
| 757 | */ | ||
| 758 | static int picolcd_get_brightness(struct backlight_device *bdev) | ||
| 759 | { | ||
| 760 | struct picolcd_data *data = bl_get_data(bdev); | ||
| 761 | return data->lcd_brightness; | ||
| 762 | } | ||
| 763 | |||
| 764 | static int picolcd_set_brightness(struct backlight_device *bdev) | ||
| 765 | { | ||
| 766 | struct picolcd_data *data = bl_get_data(bdev); | ||
| 767 | struct hid_report *report = picolcd_out_report(REPORT_BRIGHTNESS, data->hdev); | ||
| 768 | unsigned long flags; | ||
| 769 | |||
| 770 | if (!report || report->maxfield != 1 || report->field[0]->report_count != 1) | ||
| 771 | return -ENODEV; | ||
| 772 | |||
| 773 | data->lcd_brightness = bdev->props.brightness & 0x0ff; | ||
| 774 | data->lcd_power = bdev->props.power; | ||
| 775 | spin_lock_irqsave(&data->lock, flags); | ||
| 776 | hid_set_field(report->field[0], 0, data->lcd_power == FB_BLANK_UNBLANK ? data->lcd_brightness : 0); | ||
| 777 | usbhid_submit_report(data->hdev, report, USB_DIR_OUT); | ||
| 778 | spin_unlock_irqrestore(&data->lock, flags); | ||
| 779 | return 0; | ||
| 780 | } | ||
| 781 | |||
| 782 | static int picolcd_check_bl_fb(struct backlight_device *bdev, struct fb_info *fb) | ||
| 783 | { | ||
| 784 | return fb && fb == picolcd_fbinfo((struct picolcd_data *)bl_get_data(bdev)); | ||
| 785 | } | ||
| 786 | |||
| 787 | static const struct backlight_ops picolcd_blops = { | ||
| 788 | .update_status = picolcd_set_brightness, | ||
| 789 | .get_brightness = picolcd_get_brightness, | ||
| 790 | .check_fb = picolcd_check_bl_fb, | ||
| 791 | }; | ||
| 792 | |||
| 793 | static int picolcd_init_backlight(struct picolcd_data *data, struct hid_report *report) | ||
| 794 | { | ||
| 795 | struct device *dev = &data->hdev->dev; | ||
| 796 | struct backlight_device *bdev; | ||
| 797 | struct backlight_properties props; | ||
| 798 | if (!report) | ||
| 799 | return -ENODEV; | ||
| 800 | if (report->maxfield != 1 || report->field[0]->report_count != 1 || | ||
| 801 | report->field[0]->report_size != 8) { | ||
| 802 | dev_err(dev, "unsupported BRIGHTNESS report"); | ||
| 803 | return -EINVAL; | ||
| 804 | } | ||
| 805 | |||
| 806 | memset(&props, 0, sizeof(props)); | ||
| 807 | props.max_brightness = 0xff; | ||
| 808 | bdev = backlight_device_register(dev_name(dev), dev, data, | ||
| 809 | &picolcd_blops, &props); | ||
| 810 | if (IS_ERR(bdev)) { | ||
| 811 | dev_err(dev, "failed to register backlight\n"); | ||
| 812 | return PTR_ERR(bdev); | ||
| 813 | } | ||
| 814 | bdev->props.brightness = 0xff; | ||
| 815 | data->lcd_brightness = 0xff; | ||
| 816 | data->backlight = bdev; | ||
| 817 | picolcd_set_brightness(bdev); | ||
| 818 | return 0; | ||
| 819 | } | ||
| 820 | |||
| 821 | static void picolcd_exit_backlight(struct picolcd_data *data) | ||
| 822 | { | ||
| 823 | struct backlight_device *bdev = data->backlight; | ||
| 824 | |||
| 825 | data->backlight = NULL; | ||
| 826 | if (bdev) | ||
| 827 | backlight_device_unregister(bdev); | ||
| 828 | } | ||
| 829 | |||
| 830 | static inline int picolcd_resume_backlight(struct picolcd_data *data) | ||
| 831 | { | ||
| 832 | if (!data->backlight) | ||
| 833 | return 0; | ||
| 834 | return picolcd_set_brightness(data->backlight); | ||
| 835 | } | ||
| 836 | |||
| 837 | #else | ||
| 838 | static inline int picolcd_init_backlight(struct picolcd_data *data, | ||
| 839 | struct hid_report *report) | ||
| 840 | { | ||
| 841 | return 0; | ||
| 842 | } | ||
| 843 | static inline void picolcd_exit_backlight(struct picolcd_data *data) | ||
| 844 | { | ||
| 845 | } | ||
| 846 | static inline int picolcd_resume_backlight(struct picolcd_data *data) | ||
| 847 | { | ||
| 848 | return 0; | ||
| 849 | } | ||
| 850 | #endif /* CONFIG_BACKLIGHT_CLASS_DEVICE */ | ||
| 851 | |||
| 747 | /* | 852 | /* |
| 748 | * input class device | 853 | * input class device |
| 749 | */ | 854 | */ |
| @@ -879,6 +984,7 @@ static int picolcd_reset(struct hid_device *hdev) | |||
| 879 | if (error) | 984 | if (error) |
| 880 | return error; | 985 | return error; |
| 881 | 986 | ||
| 987 | picolcd_resume_backlight(data); | ||
| 882 | #if defined(CONFIG_FB) || defined(CONFIG_FB_MODULE) | 988 | #if defined(CONFIG_FB) || defined(CONFIG_FB_MODULE) |
| 883 | if (data->fb_info) | 989 | if (data->fb_info) |
| 884 | schedule_delayed_work(&data->fb_info->deferred_work, 0); | 990 | schedule_delayed_work(&data->fb_info->deferred_work, 0); |
| @@ -1567,6 +1673,11 @@ static int picolcd_probe_lcd(struct hid_device *hdev, struct picolcd_data *data) | |||
| 1567 | if (error) | 1673 | if (error) |
| 1568 | goto err; | 1674 | goto err; |
| 1569 | 1675 | ||
| 1676 | /* Setup backlight class device */ | ||
| 1677 | error = picolcd_init_backlight(data, picolcd_out_report(REPORT_BRIGHTNESS, hdev)); | ||
| 1678 | if (error) | ||
| 1679 | goto err; | ||
| 1680 | |||
| 1570 | #ifdef CONFIG_DEBUG_FS | 1681 | #ifdef CONFIG_DEBUG_FS |
| 1571 | report = picolcd_out_report(REPORT_READ_MEMORY, hdev); | 1682 | report = picolcd_out_report(REPORT_READ_MEMORY, hdev); |
| 1572 | if (report && report->maxfield == 1 && report->field[0]->report_size == 8) | 1683 | if (report && report->maxfield == 1 && report->field[0]->report_size == 8) |
| @@ -1576,6 +1687,7 @@ static int picolcd_probe_lcd(struct hid_device *hdev, struct picolcd_data *data) | |||
| 1576 | #endif | 1687 | #endif |
| 1577 | return 0; | 1688 | return 0; |
| 1578 | err: | 1689 | err: |
| 1690 | picolcd_exit_backlight(data); | ||
| 1579 | picolcd_exit_framebuffer(data); | 1691 | picolcd_exit_framebuffer(data); |
| 1580 | picolcd_exit_cir(data); | 1692 | picolcd_exit_cir(data); |
| 1581 | picolcd_exit_keys(data); | 1693 | picolcd_exit_keys(data); |
| @@ -1707,6 +1819,7 @@ static void picolcd_remove(struct hid_device *hdev) | |||
| 1707 | spin_unlock_irqrestore(&data->lock, flags); | 1819 | spin_unlock_irqrestore(&data->lock, flags); |
| 1708 | 1820 | ||
| 1709 | /* Clean up the framebuffer */ | 1821 | /* Clean up the framebuffer */ |
| 1822 | picolcd_exit_backlight(data); | ||
| 1710 | picolcd_exit_framebuffer(data); | 1823 | picolcd_exit_framebuffer(data); |
| 1711 | /* Cleanup input */ | 1824 | /* Cleanup input */ |
| 1712 | picolcd_exit_cir(data); | 1825 | picolcd_exit_cir(data); |
