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 12 #include <linux/fb.h> 13 #include <linux/backlight.h> 14 15 #include "hid-picolcd.h" 16 17 static int picolcd_get_brightness(struct backlight_device *bdev) 18 { 19 struct picolcd_data *data = bl_get_data(bdev); 20 return data->lcd_brightness; 21 } 22 23 static int picolcd_set_brightness(struct backlight_device *bdev) 24 { 25 struct picolcd_data *data = bl_get_data(bdev); 26 struct hid_report *report = picolcd_out_report(REPORT_BRIGHTNESS, data->hdev); 27 unsigned long flags; 28 29 if (!report || report->maxfield != 1 || report->field[0]->report_count != 1) 30 return -ENODEV; 31 32 data->lcd_brightness = bdev->props.brightness & 0x0ff; 33 data->lcd_power = bdev->props.power; 34 spin_lock_irqsave(&data->lock, flags); 35 hid_set_field(report->field[0], 0, data->lcd_power == FB_BLANK_UNBLANK ? data->lcd_brightness : 0); 36 if (!(data->status & PICOLCD_FAILED)) 37 hid_hw_request(data->hdev, report, HID_REQ_SET_REPORT); 38 spin_unlock_irqrestore(&data->lock, flags); 39 return 0; 40 } 41 42 static int picolcd_check_bl_fb(struct backlight_device *bdev, struct fb_info *fb) 43 { 44 return fb && fb == picolcd_fbinfo((struct picolcd_data *)bl_get_data(bdev)); 45 } 46 47 static const struct backlight_ops picolcd_blops = { 48 .update_status = picolcd_set_brightness, 49 .get_brightness = picolcd_get_brightness, 50 .check_fb = picolcd_check_bl_fb, 51 }; 52 53 int picolcd_init_backlight(struct picolcd_data *data, struct hid_report *report) 54 { 55 struct device *dev = &data->hdev->dev; 56 struct backlight_device *bdev; 57 struct backlight_properties props; 58 if (!report) 59 return -ENODEV; 60 if (report->maxfield != 1 || report->field[0]->report_count != 1 || 61 report->field[0]->report_size != 8) { 62 dev_err(dev, "unsupported BRIGHTNESS report"); 63 return -EINVAL; 64 } 65 66 memset(&props, 0, sizeof(props)); 67 props.type = BACKLIGHT_RAW; 68 props.max_brightness = 0xff; 69 bdev = backlight_device_register(dev_name(dev), dev, data, 70 &picolcd_blops, &props); 71 if (IS_ERR(bdev)) { 72 dev_err(dev, "failed to register backlight\n"); 73 return PTR_ERR(bdev); 74 } 75 bdev->props.brightness = 0xff; 76 data->lcd_brightness = 0xff; 77 data->backlight = bdev; 78 picolcd_set_brightness(bdev); 79 return 0; 80 } 81 82 void picolcd_exit_backlight(struct picolcd_data *data) 83 { 84 struct backlight_device *bdev = data->backlight; 85 86 data->backlight = NULL; 87 backlight_device_unregister(bdev); 88 } 89 90 int picolcd_resume_backlight(struct picolcd_data *data) 91 { 92 if (!data->backlight) 93 return 0; 94 return picolcd_set_brightness(data->backlight); 95 } 96 97 #ifdef CONFIG_PM 98 void picolcd_suspend_backlight(struct picolcd_data *data) 99 { 100 int bl_power = data->lcd_power; 101 if (!data->backlight) 102 return; 103 104 data->backlight->props.power = FB_BLANK_POWERDOWN; 105 picolcd_set_brightness(data->backlight); 106 data->lcd_power = data->backlight->props.power = bl_power; 107 } 108 #endif /* CONFIG_PM */ 109 110