1fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont/*************************************************************************** 2fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont * Copyright (C) 2010-2012 by Bruno Prémont <bonbons@linux-vserver.org> * 3fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont * * 4fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont * Based on Logitech G13 driver (v0.4) * 5fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont * Copyright (C) 2009 by Rick L. Vinyard, Jr. <rvinyard@cs.nmsu.edu> * 6fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont * * 7fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont * This program is free software: you can redistribute it and/or modify * 8fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont * it under the terms of the GNU General Public License as published by * 9fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont * the Free Software Foundation, version 2 of the License. * 10fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont * * 11fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont * This driver is distributed in the hope that it will be useful, but * 12fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont * WITHOUT ANY WARRANTY; without even the implied warranty of * 13fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * 14fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont * General Public License for more details. * 15fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont * * 16fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont * You should have received a copy of the GNU General Public License * 17fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont * along with this software. If not see <http://www.gnu.org/licenses/>. * 18fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont ***************************************************************************/ 19fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont 20fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont#include <linux/hid.h> 21fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont#include <linux/hid-debug.h> 22fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont#include <linux/input.h> 23fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont#include "hid-ids.h" 24fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont 25fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont#include <linux/fb.h> 26fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont#include <linux/vmalloc.h> 27fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont#include <linux/backlight.h> 28fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont#include <linux/lcd.h> 29fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont 30fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont#include <linux/leds.h> 31fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont 32fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont#include <linux/seq_file.h> 33fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont#include <linux/debugfs.h> 34fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont 35fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont#include <linux/completion.h> 36fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont#include <linux/uaccess.h> 37fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont#include <linux/module.h> 38fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont 39fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont#include "hid-picolcd.h" 40fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont 41fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont 42fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémontvoid picolcd_leds_set(struct picolcd_data *data) 43fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont{ 44fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont struct hid_report *report; 45fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont unsigned long flags; 46fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont 47fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont if (!data->led[0]) 48fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont return; 49fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont report = picolcd_out_report(REPORT_LED_STATE, data->hdev); 50fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont if (!report || report->maxfield != 1 || report->field[0]->report_count != 1) 51fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont return; 52fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont 53fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont spin_lock_irqsave(&data->lock, flags); 54fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont hid_set_field(report->field[0], 0, data->led_state); 55a93ab8494873a88622bf74be861a93f875643524Bruno Prémont if (!(data->status & PICOLCD_FAILED)) 56d881427253da011495f4193663d809d0e9dfa215Benjamin Tissoires hid_hw_request(data->hdev, report, HID_REQ_SET_REPORT); 57fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont spin_unlock_irqrestore(&data->lock, flags); 58fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont} 59fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont 60fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémontstatic void picolcd_led_set_brightness(struct led_classdev *led_cdev, 61fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont enum led_brightness value) 62fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont{ 63fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont struct device *dev; 64fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont struct hid_device *hdev; 65fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont struct picolcd_data *data; 66fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont int i, state = 0; 67fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont 68fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont dev = led_cdev->dev->parent; 69fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont hdev = container_of(dev, struct hid_device, dev); 70fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont data = hid_get_drvdata(hdev); 71b07072e6a27253b5eb7d21049a108449920b4c50Bruno Prémont if (!data) 72b07072e6a27253b5eb7d21049a108449920b4c50Bruno Prémont return; 73fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont for (i = 0; i < 8; i++) { 74fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont if (led_cdev != data->led[i]) 75fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont continue; 76fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont state = (data->led_state >> i) & 1; 77fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont if (value == LED_OFF && state) { 78fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont data->led_state &= ~(1 << i); 79fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont picolcd_leds_set(data); 80fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont } else if (value != LED_OFF && !state) { 81fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont data->led_state |= 1 << i; 82fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont picolcd_leds_set(data); 83fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont } 84fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont break; 85fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont } 86fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont} 87fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont 88fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémontstatic enum led_brightness picolcd_led_get_brightness(struct led_classdev *led_cdev) 89fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont{ 90fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont struct device *dev; 91fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont struct hid_device *hdev; 92fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont struct picolcd_data *data; 93fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont int i, value = 0; 94fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont 95fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont dev = led_cdev->dev->parent; 96fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont hdev = container_of(dev, struct hid_device, dev); 97fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont data = hid_get_drvdata(hdev); 98fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont for (i = 0; i < 8; i++) 99fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont if (led_cdev == data->led[i]) { 100fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont value = (data->led_state >> i) & 1; 101fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont break; 102fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont } 103fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont return value ? LED_FULL : LED_OFF; 104fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont} 105fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont 106fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémontint picolcd_init_leds(struct picolcd_data *data, struct hid_report *report) 107fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont{ 108fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont struct device *dev = &data->hdev->dev; 109fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont struct led_classdev *led; 110fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont size_t name_sz = strlen(dev_name(dev)) + 8; 111fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont char *name; 112fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont int i, ret = 0; 113fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont 114fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont if (!report) 115fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont return -ENODEV; 116fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont if (report->maxfield != 1 || report->field[0]->report_count != 1 || 117fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont report->field[0]->report_size != 8) { 118fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont dev_err(dev, "unsupported LED_STATE report"); 119fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont return -EINVAL; 120fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont } 121fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont 122fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont for (i = 0; i < 8; i++) { 123fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont led = kzalloc(sizeof(struct led_classdev)+name_sz, GFP_KERNEL); 124fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont if (!led) { 125fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont dev_err(dev, "can't allocate memory for LED %d\n", i); 126fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont ret = -ENOMEM; 127fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont goto err; 128fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont } 129fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont name = (void *)(&led[1]); 130fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont snprintf(name, name_sz, "%s::GPO%d", dev_name(dev), i); 131fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont led->name = name; 132fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont led->brightness = 0; 133fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont led->max_brightness = 1; 134fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont led->brightness_get = picolcd_led_get_brightness; 135fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont led->brightness_set = picolcd_led_set_brightness; 136fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont 137fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont data->led[i] = led; 138fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont ret = led_classdev_register(dev, data->led[i]); 139fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont if (ret) { 140fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont data->led[i] = NULL; 141fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont kfree(led); 142fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont dev_err(dev, "can't register LED %d\n", i); 143fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont goto err; 144fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont } 145fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont } 146fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont return 0; 147fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémonterr: 148fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont for (i = 0; i < 8; i++) 149fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont if (data->led[i]) { 150fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont led = data->led[i]; 151fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont data->led[i] = NULL; 152fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont led_classdev_unregister(led); 153fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont kfree(led); 154fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont } 155fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont return ret; 156fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont} 157fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont 158fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémontvoid picolcd_exit_leds(struct picolcd_data *data) 159fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont{ 160fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont struct led_classdev *led; 161fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont int i; 162fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont 163fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont for (i = 0; i < 8; i++) { 164fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont led = data->led[i]; 165fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont data->led[i] = NULL; 166fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont if (!led) 167fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont continue; 168fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont led_classdev_unregister(led); 169fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont kfree(led); 170fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont } 171fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont} 172fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont 173fabdbf2fd22fa170b4c5340dbdda5c8cd88fb205Bruno Prémont 174