162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/*************************************************************************** 362306a36Sopenharmony_ci * Copyright (C) 2010-2012 by Bruno Prémont <bonbons@linux-vserver.org> * 462306a36Sopenharmony_ci * * 562306a36Sopenharmony_ci * Based on Logitech G13 driver (v0.4) * 662306a36Sopenharmony_ci * Copyright (C) 2009 by Rick L. Vinyard, Jr. <rvinyard@cs.nmsu.edu> * 762306a36Sopenharmony_ci * * 862306a36Sopenharmony_ci ***************************************************************************/ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/hid.h> 1162306a36Sopenharmony_ci#include <linux/hid-debug.h> 1262306a36Sopenharmony_ci#include <linux/input.h> 1362306a36Sopenharmony_ci#include "hid-ids.h" 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include <linux/fb.h> 1662306a36Sopenharmony_ci#include <linux/vmalloc.h> 1762306a36Sopenharmony_ci#include <linux/backlight.h> 1862306a36Sopenharmony_ci#include <linux/lcd.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include <linux/leds.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include <linux/seq_file.h> 2362306a36Sopenharmony_ci#include <linux/debugfs.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#include <linux/completion.h> 2662306a36Sopenharmony_ci#include <linux/uaccess.h> 2762306a36Sopenharmony_ci#include <linux/module.h> 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#include "hid-picolcd.h" 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_civoid picolcd_leds_set(struct picolcd_data *data) 3362306a36Sopenharmony_ci{ 3462306a36Sopenharmony_ci struct hid_report *report; 3562306a36Sopenharmony_ci unsigned long flags; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci if (!data->led[0]) 3862306a36Sopenharmony_ci return; 3962306a36Sopenharmony_ci report = picolcd_out_report(REPORT_LED_STATE, data->hdev); 4062306a36Sopenharmony_ci if (!report || report->maxfield != 1 || report->field[0]->report_count != 1) 4162306a36Sopenharmony_ci return; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci spin_lock_irqsave(&data->lock, flags); 4462306a36Sopenharmony_ci hid_set_field(report->field[0], 0, data->led_state); 4562306a36Sopenharmony_ci if (!(data->status & PICOLCD_FAILED)) 4662306a36Sopenharmony_ci hid_hw_request(data->hdev, report, HID_REQ_SET_REPORT); 4762306a36Sopenharmony_ci spin_unlock_irqrestore(&data->lock, flags); 4862306a36Sopenharmony_ci} 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic void picolcd_led_set_brightness(struct led_classdev *led_cdev, 5162306a36Sopenharmony_ci enum led_brightness value) 5262306a36Sopenharmony_ci{ 5362306a36Sopenharmony_ci struct device *dev; 5462306a36Sopenharmony_ci struct hid_device *hdev; 5562306a36Sopenharmony_ci struct picolcd_data *data; 5662306a36Sopenharmony_ci int i, state = 0; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci dev = led_cdev->dev->parent; 5962306a36Sopenharmony_ci hdev = to_hid_device(dev); 6062306a36Sopenharmony_ci data = hid_get_drvdata(hdev); 6162306a36Sopenharmony_ci if (!data) 6262306a36Sopenharmony_ci return; 6362306a36Sopenharmony_ci for (i = 0; i < 8; i++) { 6462306a36Sopenharmony_ci if (led_cdev != data->led[i]) 6562306a36Sopenharmony_ci continue; 6662306a36Sopenharmony_ci state = (data->led_state >> i) & 1; 6762306a36Sopenharmony_ci if (value == LED_OFF && state) { 6862306a36Sopenharmony_ci data->led_state &= ~(1 << i); 6962306a36Sopenharmony_ci picolcd_leds_set(data); 7062306a36Sopenharmony_ci } else if (value != LED_OFF && !state) { 7162306a36Sopenharmony_ci data->led_state |= 1 << i; 7262306a36Sopenharmony_ci picolcd_leds_set(data); 7362306a36Sopenharmony_ci } 7462306a36Sopenharmony_ci break; 7562306a36Sopenharmony_ci } 7662306a36Sopenharmony_ci} 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_cistatic enum led_brightness picolcd_led_get_brightness(struct led_classdev *led_cdev) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci struct device *dev; 8162306a36Sopenharmony_ci struct hid_device *hdev; 8262306a36Sopenharmony_ci struct picolcd_data *data; 8362306a36Sopenharmony_ci int i, value = 0; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci dev = led_cdev->dev->parent; 8662306a36Sopenharmony_ci hdev = to_hid_device(dev); 8762306a36Sopenharmony_ci data = hid_get_drvdata(hdev); 8862306a36Sopenharmony_ci for (i = 0; i < 8; i++) 8962306a36Sopenharmony_ci if (led_cdev == data->led[i]) { 9062306a36Sopenharmony_ci value = (data->led_state >> i) & 1; 9162306a36Sopenharmony_ci break; 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci return value ? LED_FULL : LED_OFF; 9462306a36Sopenharmony_ci} 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ciint picolcd_init_leds(struct picolcd_data *data, struct hid_report *report) 9762306a36Sopenharmony_ci{ 9862306a36Sopenharmony_ci struct device *dev = &data->hdev->dev; 9962306a36Sopenharmony_ci struct led_classdev *led; 10062306a36Sopenharmony_ci size_t name_sz = strlen(dev_name(dev)) + 8; 10162306a36Sopenharmony_ci char *name; 10262306a36Sopenharmony_ci int i, ret = 0; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci if (!report) 10562306a36Sopenharmony_ci return -ENODEV; 10662306a36Sopenharmony_ci if (report->maxfield != 1 || report->field[0]->report_count != 1 || 10762306a36Sopenharmony_ci report->field[0]->report_size != 8) { 10862306a36Sopenharmony_ci dev_err(dev, "unsupported LED_STATE report"); 10962306a36Sopenharmony_ci return -EINVAL; 11062306a36Sopenharmony_ci } 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci for (i = 0; i < 8; i++) { 11362306a36Sopenharmony_ci led = kzalloc(sizeof(struct led_classdev)+name_sz, GFP_KERNEL); 11462306a36Sopenharmony_ci if (!led) { 11562306a36Sopenharmony_ci dev_err(dev, "can't allocate memory for LED %d\n", i); 11662306a36Sopenharmony_ci ret = -ENOMEM; 11762306a36Sopenharmony_ci goto err; 11862306a36Sopenharmony_ci } 11962306a36Sopenharmony_ci name = (void *)(&led[1]); 12062306a36Sopenharmony_ci snprintf(name, name_sz, "%s::GPO%d", dev_name(dev), i); 12162306a36Sopenharmony_ci led->name = name; 12262306a36Sopenharmony_ci led->brightness = 0; 12362306a36Sopenharmony_ci led->max_brightness = 1; 12462306a36Sopenharmony_ci led->brightness_get = picolcd_led_get_brightness; 12562306a36Sopenharmony_ci led->brightness_set = picolcd_led_set_brightness; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci data->led[i] = led; 12862306a36Sopenharmony_ci ret = led_classdev_register(dev, data->led[i]); 12962306a36Sopenharmony_ci if (ret) { 13062306a36Sopenharmony_ci data->led[i] = NULL; 13162306a36Sopenharmony_ci kfree(led); 13262306a36Sopenharmony_ci dev_err(dev, "can't register LED %d\n", i); 13362306a36Sopenharmony_ci goto err; 13462306a36Sopenharmony_ci } 13562306a36Sopenharmony_ci } 13662306a36Sopenharmony_ci return 0; 13762306a36Sopenharmony_cierr: 13862306a36Sopenharmony_ci for (i = 0; i < 8; i++) 13962306a36Sopenharmony_ci if (data->led[i]) { 14062306a36Sopenharmony_ci led = data->led[i]; 14162306a36Sopenharmony_ci data->led[i] = NULL; 14262306a36Sopenharmony_ci led_classdev_unregister(led); 14362306a36Sopenharmony_ci kfree(led); 14462306a36Sopenharmony_ci } 14562306a36Sopenharmony_ci return ret; 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_civoid picolcd_exit_leds(struct picolcd_data *data) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci struct led_classdev *led; 15162306a36Sopenharmony_ci int i; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci for (i = 0; i < 8; i++) { 15462306a36Sopenharmony_ci led = data->led[i]; 15562306a36Sopenharmony_ci data->led[i] = NULL; 15662306a36Sopenharmony_ci if (!led) 15762306a36Sopenharmony_ci continue; 15862306a36Sopenharmony_ci led_classdev_unregister(led); 15962306a36Sopenharmony_ci kfree(led); 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci 164