From 3797fec17193e05dee9666b990d6c84e16b188b3 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 2 Apr 2008 00:41:00 -0400 Subject: Input: remove private member from input_dev structure Everyone should be using input_{get|set}_drvdata() by now. Alias them to dev_{get|set}_drvdata() and remove ->private. Signed-off-by: Dmitry Torokhov --- drivers/input/input-polldev.c | 6 +++--- include/linux/input.h | 8 ++------ 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/drivers/input/input-polldev.c b/drivers/input/input-polldev.c index 490918a5d192..0d3ce7a50fb1 100644 --- a/drivers/input/input-polldev.c +++ b/drivers/input/input-polldev.c @@ -73,7 +73,7 @@ static void input_polled_device_work(struct work_struct *work) static int input_open_polled_device(struct input_dev *input) { - struct input_polled_dev *dev = input->private; + struct input_polled_dev *dev = input_get_drvdata(input); int error; error = input_polldev_start_workqueue(); @@ -91,7 +91,7 @@ static int input_open_polled_device(struct input_dev *input) static void input_close_polled_device(struct input_dev *input) { - struct input_polled_dev *dev = input->private; + struct input_polled_dev *dev = input_get_drvdata(input); cancel_delayed_work_sync(&dev->work); input_polldev_stop_workqueue(); @@ -151,10 +151,10 @@ int input_register_polled_device(struct input_polled_dev *dev) { struct input_dev *input = dev->input; + input_set_drvdata(input, dev); INIT_DELAYED_WORK(&dev->work, input_polled_device_work); if (!dev->poll_interval) dev->poll_interval = 500; - input->private = dev; input->open = input_open_polled_device; input->close = input_close_polled_device; diff --git a/include/linux/input.h b/include/linux/input.h index cae2c35d1206..28a094fcfe20 100644 --- a/include/linux/input.h +++ b/include/linux/input.h @@ -1025,10 +1025,6 @@ struct ff_effect { * @node: used to place the device onto input_dev_list */ struct input_dev { - /* private: */ - void *private; /* do not use */ - /* public: */ - const char *name; const char *phys; const char *uniq; @@ -1238,12 +1234,12 @@ static inline void input_put_device(struct input_dev *dev) static inline void *input_get_drvdata(struct input_dev *dev) { - return dev->private; + return dev_get_drvdata(&dev->dev); } static inline void input_set_drvdata(struct input_dev *dev, void *data) { - dev->private = data; + dev_set_drvdata(&dev->dev, data); } int __must_check input_register_device(struct input_dev *); -- cgit v1.2.3-70-g09d2 From e6cdd15629a5a99d805fa3cbf0f5174bcfc685bb Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Wed, 2 Apr 2008 00:42:42 -0400 Subject: Input: locomokbd - add 'off' button support for Sharp Collie/Poodle Enables the Sharp Zaurus Collie and Poodle devices to be turned off by pressing the "Cancel" button for a few seconds (as designed by Sharp). Additional small cleanups: - removal of unused #defines and variables - add missing __devinit/__devexit/__devinitconst annotations - reorganized copyright notice Signed-off-by: Helge Deller Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/locomokbd.c | 73 +++++++++++++++++++++++++------------- 1 file changed, 48 insertions(+), 25 deletions(-) diff --git a/drivers/input/keyboard/locomokbd.c b/drivers/input/keyboard/locomokbd.c index 5a0ca18d6755..9caed30f3bbb 100644 --- a/drivers/input/keyboard/locomokbd.c +++ b/drivers/input/keyboard/locomokbd.c @@ -1,14 +1,12 @@ /* - * Copyright (c) 2005 John Lenz + * LoCoMo keyboard driver for Linux-based ARM PDAs: + * - SHARP Zaurus Collie (SL-5500) + * - SHARP Zaurus Poodle (SL-5600) * + * Copyright (c) 2005 John Lenz * Based on from xtkbd.c - */ - -/* - * LoCoMo keyboard driver for Linux/ARM - */ - -/* + * + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -47,7 +45,8 @@ MODULE_LICENSE("GPL"); #define KEY_CONTACT KEY_F18 #define KEY_CENTER KEY_F15 -static unsigned char locomokbd_keycode[LOCOMOKBD_NUMKEYS] = { +static const unsigned char +locomokbd_keycode[LOCOMOKBD_NUMKEYS] __devinitconst = { 0, KEY_ESC, KEY_ACTIVITY, 0, 0, 0, 0, 0, 0, 0, /* 0 - 9 */ 0, 0, 0, 0, 0, 0, 0, KEY_MENU, KEY_HOME, KEY_CONTACT, /* 10 - 19 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 20 - 29 */ @@ -67,22 +66,21 @@ static unsigned char locomokbd_keycode[LOCOMOKBD_NUMKEYS] = { #define KB_COLS 8 #define KB_ROWMASK(r) (1 << (r)) #define SCANCODE(c,r) ( ((c)<<4) + (r) + 1 ) -#define NR_SCANCODES 128 #define KB_DELAY 8 #define SCAN_INTERVAL (HZ/10) -#define LOCOMOKBD_PRESSED 1 struct locomokbd { unsigned char keycode[LOCOMOKBD_NUMKEYS]; struct input_dev *input; char phys[32]; - struct locomo_dev *ldev; unsigned long base; spinlock_t lock; struct timer_list timer; + unsigned long suspend_jiffies; + unsigned int count_cancel; }; /* helper functions for reading the keyboard matrix */ @@ -128,7 +126,7 @@ static inline void locomokbd_reset_col(unsigned long membase, int col) /* Scan the hardware keyboard and push any changes up through the input layer */ static void locomokbd_scankeyboard(struct locomokbd *locomokbd) { - unsigned int row, col, rowd, scancode; + unsigned int row, col, rowd; unsigned long flags; unsigned int num_pressed; unsigned long membase = locomokbd->base; @@ -145,13 +143,33 @@ static void locomokbd_scankeyboard(struct locomokbd *locomokbd) rowd = ~locomo_readl(membase + LOCOMO_KIB); for (row = 0; row < KB_ROWS; row++) { + unsigned int scancode, pressed, key; + scancode = SCANCODE(col, row); - if (rowd & KB_ROWMASK(row)) { - num_pressed += 1; - input_report_key(locomokbd->input, locomokbd->keycode[scancode], 1); - } else { - input_report_key(locomokbd->input, locomokbd->keycode[scancode], 0); - } + pressed = rowd & KB_ROWMASK(row); + key = locomokbd->keycode[scancode]; + + input_report_key(locomokbd->input, key, pressed); + if (likely(!pressed)) + continue; + + num_pressed++; + + /* The "Cancel/ESC" key is labeled "On/Off" on + * Collie and Poodle and should suspend the device + * if it was pressed for more than a second. */ + if (unlikely(key == KEY_ESC)) { + if (!time_after(jiffies, + locomokbd->suspend_jiffies + HZ)) + continue; + if (locomokbd->count_cancel++ + != (HZ/SCAN_INTERVAL + 1)) + continue; + input_event(locomokbd->input, EV_PWR, + KEY_SUSPEND, 1); + locomokbd->suspend_jiffies = jiffies; + } else + locomokbd->count_cancel = 0; } locomokbd_reset_col(membase, col); } @@ -162,6 +180,8 @@ static void locomokbd_scankeyboard(struct locomokbd *locomokbd) /* if any keys are pressed, enable the timer */ if (num_pressed) mod_timer(&locomokbd->timer, jiffies + SCAN_INTERVAL); + else + locomokbd->count_cancel = 0; spin_unlock_irqrestore(&locomokbd->lock, flags); } @@ -186,10 +206,11 @@ static irqreturn_t locomokbd_interrupt(int irq, void *dev_id) static void locomokbd_timer_callback(unsigned long data) { struct locomokbd *locomokbd = (struct locomokbd *) data; + locomokbd_scankeyboard(locomokbd); } -static int locomokbd_probe(struct locomo_dev *dev) +static int __devinit locomokbd_probe(struct locomo_dev *dev) { struct locomokbd *locomokbd; struct input_dev *input_dev; @@ -211,7 +232,6 @@ static int locomokbd_probe(struct locomo_dev *dev) goto err_free_mem; } - locomokbd->ldev = dev; locomo_set_drvdata(dev, locomokbd); locomokbd->base = (unsigned long) dev->mapbase; @@ -222,6 +242,8 @@ static int locomokbd_probe(struct locomo_dev *dev) locomokbd->timer.function = locomokbd_timer_callback; locomokbd->timer.data = (unsigned long) locomokbd; + locomokbd->suspend_jiffies = jiffies; + locomokbd->input = input_dev; strcpy(locomokbd->phys, "locomokbd/input0"); @@ -233,9 +255,10 @@ static int locomokbd_probe(struct locomo_dev *dev) input_dev->id.version = 0x0100; input_dev->dev.parent = &dev->dev; - input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP) | + BIT_MASK(EV_PWR); input_dev->keycode = locomokbd->keycode; - input_dev->keycodesize = sizeof(unsigned char); + input_dev->keycodesize = sizeof(locomokbd_keycode[0]); input_dev->keycodemax = ARRAY_SIZE(locomokbd_keycode); memcpy(locomokbd->keycode, locomokbd_keycode, sizeof(locomokbd->keycode)); @@ -268,7 +291,7 @@ static int locomokbd_probe(struct locomo_dev *dev) return err; } -static int locomokbd_remove(struct locomo_dev *dev) +static int __devexit locomokbd_remove(struct locomo_dev *dev) { struct locomokbd *locomokbd = locomo_get_drvdata(dev); @@ -292,7 +315,7 @@ static struct locomo_driver keyboard_driver = { }, .devid = LOCOMO_DEVID_KEYBOARD, .probe = locomokbd_probe, - .remove = locomokbd_remove, + .remove = __devexit_p(locomokbd_remove), }; static int __init locomokbd_init(void) -- cgit v1.2.3-70-g09d2 From 7c6d0ee14cb7a4cfad4864dc196256da5749bc0c Mon Sep 17 00:00:00 2001 From: David Brownell Date: Wed, 2 Apr 2008 00:43:01 -0400 Subject: Input: ads7846 - simplify support of external vREF (and ads7843) This updates the ads7846 driver to handle external vREF (required on boards using ads7843 chips) without module parameters, and also removes a needless variable with its associated bogus gcc warning. Signed-off-by: David Brownell Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/ads7846.c | 22 ++++++++++------------ include/linux/spi/ads7846.h | 3 ++- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c index 57a1c28bf122..a571aa965da0 100644 --- a/drivers/input/touchscreen/ads7846.c +++ b/drivers/input/touchscreen/ads7846.c @@ -87,6 +87,7 @@ struct ads7846 { #endif u16 model; + u16 vref_mv; u16 vref_delay_usecs; u16 x_plate_ohms; u16 pressure_max; @@ -184,9 +185,6 @@ struct ads7846 { * The range is GND..vREF. The ads7843 and ads7835 must use external vREF; * ads7846 lets that pin be unconnected, to use internal vREF. */ -static unsigned vREF_mV; -module_param(vREF_mV, uint, 0); -MODULE_PARM_DESC(vREF_mV, "external vREF voltage, in milliVolts"); struct ser_req { u8 ref_on; @@ -213,7 +211,6 @@ static int ads7846_read12_ser(struct device *dev, unsigned command) struct ads7846 *ts = dev_get_drvdata(dev); struct ser_req *req = kzalloc(sizeof *req, GFP_KERNEL); int status; - int uninitialized_var(sample); int use_internal; if (!req) @@ -270,13 +267,13 @@ static int ads7846_read12_ser(struct device *dev, unsigned command) if (status == 0) { /* on-wire is a must-ignore bit, a BE12 value, then padding */ - sample = be16_to_cpu(req->sample); - sample = sample >> 3; - sample &= 0x0fff; + status = be16_to_cpu(req->sample); + status = status >> 3; + status &= 0x0fff; } kfree(req); - return status ? status : sample; + return status; } #if defined(CONFIG_HWMON) || defined(CONFIG_HWMON_MODULE) @@ -317,7 +314,7 @@ static inline unsigned vaux_adjust(struct ads7846 *ts, ssize_t v) unsigned retval = v; /* external resistors may scale vAUX into 0..vREF */ - retval *= vREF_mV; + retval *= ts->vref_mv; retval = retval >> 12; return retval; } @@ -375,14 +372,14 @@ static int ads784x_hwmon_register(struct spi_device *spi, struct ads7846 *ts) /* hwmon sensors need a reference voltage */ switch (ts->model) { case 7846: - if (!vREF_mV) { + if (!ts->vref_mv) { dev_dbg(&spi->dev, "assuming 2.5V internal vREF\n"); - vREF_mV = 2500; + ts->vref_mv = 2500; } break; case 7845: case 7843: - if (!vREF_mV) { + if (!ts->vref_mv) { dev_warn(&spi->dev, "external vREF for ADS%d not specified\n", ts->model); @@ -875,6 +872,7 @@ static int __devinit ads7846_probe(struct spi_device *spi) ts->spi = spi; ts->input = input_dev; + ts->vref_mv = pdata->vref_mv; hrtimer_init(&ts->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); ts->timer.function = ads7846_timer; diff --git a/include/linux/spi/ads7846.h b/include/linux/spi/ads7846.h index 334d31411629..daf744017a31 100644 --- a/include/linux/spi/ads7846.h +++ b/include/linux/spi/ads7846.h @@ -14,7 +14,8 @@ enum ads7846_filter { struct ads7846_platform_data { u16 model; /* 7843, 7845, 7846. */ u16 vref_delay_usecs; /* 0 for external vref; etc */ - int keep_vref_on:1; /* set to keep vref on for differential + u16 vref_mv; /* external vref value, milliVolts */ + bool keep_vref_on; /* set to keep vref on for differential * measurements as well */ /* Settling time of the analog signals; a function of Vcc and the -- cgit v1.2.3-70-g09d2 From febf1dff119ef27ee22a54d40f284d2454f00d8d Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 2 Apr 2008 00:51:09 -0400 Subject: Input: add support for WM97xx familty touchscreens Add support for the touchscreen controllers provided by Wolfson Microelectronics WM97xx series chips in both polled and streaming modes. These drivers have been maintained out of tree since 2003. During that time the driver the primary maintainer was Liam Girdwood and a number of people have made contributions including Dmitry Baryshkov, Stanley Cai, Rodolfo Giometti, Russell King, Marc Kleine-Budde, Ian Molton, Vincent Sanders, Andrew Zabolotny, Graeme Gregory, Mike Arthur and myself. Apologies to anyone I have omitted. Signed-off-by: Liam Girdwood Signed-off-by: Graeme Gregory Signed-off-by: Mike Arthur Signed-off-by: Mark Brown Signed-off-by: Dmitry Torokhov --- MAINTAINERS | 10 + drivers/input/touchscreen/Kconfig | 14 + drivers/input/touchscreen/Makefile | 3 + drivers/input/touchscreen/wm97xx-core.c | 789 ++++++++++++++++++++++++++++++++ include/linux/wm97xx.h | 311 +++++++++++++ 5 files changed, 1127 insertions(+) create mode 100644 drivers/input/touchscreen/wm97xx-core.c create mode 100644 include/linux/wm97xx.h diff --git a/MAINTAINERS b/MAINTAINERS index 90dcbbcad91c..87bdced169a4 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4343,6 +4343,16 @@ L: linux-wireless@vger.kernel.org W: http://oops.ghostprotocols.net:81/blog S: Maintained +WM97XX TOUCHSCREEN DRIVERS +P: Mark Brown +M: broonie@opensource.wolfsonmicro.com +P: Liam Girdwood +M: liam.girdwood@wolfsonmicro.com +L: linux-input@vger.kernel.org +T: git git://opensource.wolfsonmicro.com/linux-2.6-touch +W: http://opensource.wolfsonmicro.com/node/7 +S: Supported + X.25 NETWORK LAYER P: Henner Eisen M: eis@baty.hanse.de diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 90e8e92dfe47..a95a87cd1d59 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -185,6 +185,20 @@ config TOUCHSCREEN_UCB1400 To compile this driver as a module, choose M here: the module will be called ucb1400_ts. +config TOUCHSCREEN_WM97XX + tristate "Support for WM97xx AC97 touchscreen controllers" + depends on AC97_BUS + help + Say Y here if you have a Wolfson Microelectronics WM97xx + touchscreen connected to your system. Note that this option + only enables core driver, you will also need to select + support for appropriate chip below. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called wm97xx-ts. + config TOUCHSCREEN_USB_COMPOSITE tristate "USB Touchscreen Driver" depends on USB_ARCH_HAS_HCD diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 35d4097df35a..b4e7b5dbf649 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -4,6 +4,8 @@ # Each configuration option enables a list of files. +wm97xx-ts-y := wm97xx-core.o + obj-$(CONFIG_TOUCHSCREEN_ADS7846) += ads7846.o obj-$(CONFIG_TOUCHSCREEN_BITSY) += h3600_ts_input.o obj-$(CONFIG_TOUCHSCREEN_CORGI) += corgi_ts.o @@ -19,3 +21,4 @@ obj-$(CONFIG_TOUCHSCREEN_PENMOUNT) += penmount.o obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN) += touchwin.o obj-$(CONFIG_TOUCHSCREEN_UCB1400) += ucb1400_ts.o +obj-$(CONFIG_TOUCHSCREEN_WM97XX) += wm97xx-ts.o diff --git a/drivers/input/touchscreen/wm97xx-core.c b/drivers/input/touchscreen/wm97xx-core.c new file mode 100644 index 000000000000..2910999e05a0 --- /dev/null +++ b/drivers/input/touchscreen/wm97xx-core.c @@ -0,0 +1,789 @@ +/* + * wm97xx-core.c -- Touch screen driver core for Wolfson WM9705, WM9712 + * and WM9713 AC97 Codecs. + * + * Copyright 2003, 2004, 2005, 2006, 2007, 2008 Wolfson Microelectronics PLC. + * Author: Liam Girdwood + * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com + * Parts Copyright : Ian Molton + * Andrew Zabolotny + * Russell King + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * Notes: + * + * Features: + * - supports WM9705, WM9712, WM9713 + * - polling mode + * - continuous mode (arch-dependent) + * - adjustable rpu/dpp settings + * - adjustable pressure current + * - adjustable sample settle delay + * - 4 and 5 wire touchscreens (5 wire is WM9712 only) + * - pen down detection + * - battery monitor + * - sample AUX adcs + * - power management + * - codec GPIO + * - codec event notification + * Todo + * - Support for async sampling control for noisy LCDs. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TS_NAME "wm97xx" +#define WM_CORE_VERSION "1.00" +#define DEFAULT_PRESSURE 0xb0c0 + + +/* + * Touchscreen absolute values + * + * These parameters are used to help the input layer discard out of + * range readings and reduce jitter etc. + * + * o min, max:- indicate the min and max values your touch screen returns + * o fuzz:- use a higher number to reduce jitter + * + * The default values correspond to Mainstone II in QVGA mode + * + * Please read + * Documentation/input/input-programming.txt for more details. + */ + +static int abs_x[3] = {350, 3900, 5}; +module_param_array(abs_x, int, NULL, 0); +MODULE_PARM_DESC(abs_x, "Touchscreen absolute X min, max, fuzz"); + +static int abs_y[3] = {320, 3750, 40}; +module_param_array(abs_y, int, NULL, 0); +MODULE_PARM_DESC(abs_y, "Touchscreen absolute Y min, max, fuzz"); + +static int abs_p[3] = {0, 150, 4}; +module_param_array(abs_p, int, NULL, 0); +MODULE_PARM_DESC(abs_p, "Touchscreen absolute Pressure min, max, fuzz"); + +/* + * wm97xx IO access, all IO locking done by AC97 layer + */ +int wm97xx_reg_read(struct wm97xx *wm, u16 reg) +{ + if (wm->ac97) + return wm->ac97->bus->ops->read(wm->ac97, reg); + else + return -1; +} +EXPORT_SYMBOL_GPL(wm97xx_reg_read); + +void wm97xx_reg_write(struct wm97xx *wm, u16 reg, u16 val) +{ + /* cache digitiser registers */ + if (reg >= AC97_WM9713_DIG1 && reg <= AC97_WM9713_DIG3) + wm->dig[(reg - AC97_WM9713_DIG1) >> 1] = val; + + /* cache gpio regs */ + if (reg >= AC97_GPIO_CFG && reg <= AC97_MISC_AFE) + wm->gpio[(reg - AC97_GPIO_CFG) >> 1] = val; + + /* wm9713 irq reg */ + if (reg == 0x5a) + wm->misc = val; + + if (wm->ac97) + wm->ac97->bus->ops->write(wm->ac97, reg, val); +} +EXPORT_SYMBOL_GPL(wm97xx_reg_write); + +/** + * wm97xx_read_aux_adc - Read the aux adc. + * @wm: wm97xx device. + * @adcsel: codec ADC to be read + * + * Reads the selected AUX ADC. + */ + +int wm97xx_read_aux_adc(struct wm97xx *wm, u16 adcsel) +{ + int power_adc = 0, auxval; + u16 power = 0; + + /* get codec */ + mutex_lock(&wm->codec_mutex); + + /* When the touchscreen is not in use, we may have to power up + * the AUX ADC before we can use sample the AUX inputs-> + */ + if (wm->id == WM9713_ID2 && + (power = wm97xx_reg_read(wm, AC97_EXTENDED_MID)) & 0x8000) { + power_adc = 1; + wm97xx_reg_write(wm, AC97_EXTENDED_MID, power & 0x7fff); + } + + /* Prepare the codec for AUX reading */ + wm->codec->aux_prepare(wm); + + /* Turn polling mode on to read AUX ADC */ + wm->pen_probably_down = 1; + wm->codec->poll_sample(wm, adcsel, &auxval); + + if (power_adc) + wm97xx_reg_write(wm, AC97_EXTENDED_MID, power | 0x8000); + + wm->codec->dig_restore(wm); + + wm->pen_probably_down = 0; + + mutex_unlock(&wm->codec_mutex); + return auxval & 0xfff; +} +EXPORT_SYMBOL_GPL(wm97xx_read_aux_adc); + +/** + * wm97xx_get_gpio - Get the status of a codec GPIO. + * @wm: wm97xx device. + * @gpio: gpio + * + * Get the status of a codec GPIO pin + */ + +enum wm97xx_gpio_status wm97xx_get_gpio(struct wm97xx *wm, u32 gpio) +{ + u16 status; + enum wm97xx_gpio_status ret; + + mutex_lock(&wm->codec_mutex); + status = wm97xx_reg_read(wm, AC97_GPIO_STATUS); + + if (status & gpio) + ret = WM97XX_GPIO_HIGH; + else + ret = WM97XX_GPIO_LOW; + + mutex_unlock(&wm->codec_mutex); + return ret; +} +EXPORT_SYMBOL_GPL(wm97xx_get_gpio); + +/** + * wm97xx_set_gpio - Set the status of a codec GPIO. + * @wm: wm97xx device. + * @gpio: gpio + * + * + * Set the status of a codec GPIO pin + */ + +void wm97xx_set_gpio(struct wm97xx *wm, u32 gpio, + enum wm97xx_gpio_status status) +{ + u16 reg; + + mutex_lock(&wm->codec_mutex); + reg = wm97xx_reg_read(wm, AC97_GPIO_STATUS); + + if (status & WM97XX_GPIO_HIGH) + reg |= gpio; + else + reg &= ~gpio; + + if (wm->id == WM9712_ID2) + wm97xx_reg_write(wm, AC97_GPIO_STATUS, reg << 1); + else + wm97xx_reg_write(wm, AC97_GPIO_STATUS, reg); + mutex_unlock(&wm->codec_mutex); +} +EXPORT_SYMBOL_GPL(wm97xx_set_gpio); + +/* + * Codec GPIO pin configuration, this sets pin direction, polarity, + * stickyness and wake up. + */ +void wm97xx_config_gpio(struct wm97xx *wm, u32 gpio, enum wm97xx_gpio_dir dir, + enum wm97xx_gpio_pol pol, enum wm97xx_gpio_sticky sticky, + enum wm97xx_gpio_wake wake) +{ + u16 reg; + + mutex_lock(&wm->codec_mutex); + reg = wm97xx_reg_read(wm, AC97_GPIO_POLARITY); + + if (pol == WM97XX_GPIO_POL_HIGH) + reg |= gpio; + else + reg &= ~gpio; + + wm97xx_reg_write(wm, AC97_GPIO_POLARITY, reg); + reg = wm97xx_reg_read(wm, AC97_GPIO_STICKY); + + if (sticky == WM97XX_GPIO_STICKY) + reg |= gpio; + else + reg &= ~gpio; + + wm97xx_reg_write(wm, AC97_GPIO_STICKY, reg); + reg = wm97xx_reg_read(wm, AC97_GPIO_WAKEUP); + + if (wake == WM97XX_GPIO_WAKE) + reg |= gpio; + else + reg &= ~gpio; + + wm97xx_reg_write(wm, AC97_GPIO_WAKEUP, reg); + reg = wm97xx_reg_read(wm, AC97_GPIO_CFG); + + if (dir == WM97XX_GPIO_IN) + reg |= gpio; + else + reg &= ~gpio; + + wm97xx_reg_write(wm, AC97_GPIO_CFG, reg); + mutex_unlock(&wm->codec_mutex); +} +EXPORT_SYMBOL_GPL(wm97xx_config_gpio); + +/* + * Handle a pen down interrupt. + */ +static void wm97xx_pen_irq_worker(struct work_struct *work) +{ + struct wm97xx *wm = container_of(work, struct wm97xx, pen_event_work); + int pen_was_down = wm->pen_is_down; + + /* do we need to enable the touch panel reader */ + if (wm->id == WM9705_ID2) { + if (wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD) & + WM97XX_PEN_DOWN) + wm->pen_is_down = 1; + else + wm->pen_is_down = 0; + } else { + u16 status, pol; + mutex_lock(&wm->codec_mutex); + status = wm97xx_reg_read(wm, AC97_GPIO_STATUS); + pol = wm97xx_reg_read(wm, AC97_GPIO_POLARITY); + + if (WM97XX_GPIO_13 & pol & status) { + wm->pen_is_down = 1; + wm97xx_reg_write(wm, AC97_GPIO_POLARITY, pol & + ~WM97XX_GPIO_13); + } else { + wm->pen_is_down = 0; + wm97xx_reg_write(wm, AC97_GPIO_POLARITY, pol | + WM97XX_GPIO_13); + } + + if (wm->id == WM9712_ID2) + wm97xx_reg_write(wm, AC97_GPIO_STATUS, (status & + ~WM97XX_GPIO_13) << 1); + else + wm97xx_reg_write(wm, AC97_GPIO_STATUS, status & + ~WM97XX_GPIO_13); + mutex_unlock(&wm->codec_mutex); + } + + /* If the system is not using continuous mode or it provides a + * pen down operation then we need to schedule polls while the + * pen is down. Otherwise the machine driver is responsible + * for scheduling reads. + */ + if (!wm->mach_ops->acc_enabled || wm->mach_ops->acc_pen_down) { + if (wm->pen_is_down && !pen_was_down) { + /* Data is not availiable immediately on pen down */ + queue_delayed_work(wm->ts_workq, &wm->ts_reader, 1); + } + + /* Let ts_reader report the pen up for debounce. */ + if (!wm->pen_is_down && pen_was_down) + wm->pen_is_down = 1; + } + + if (!wm->pen_is_down && wm->mach_ops->acc_enabled) + wm->mach_ops->acc_pen_up(wm); + + wm->mach_ops->irq_enable(wm, 1); +} + +/* + * Codec PENDOWN irq handler + * + * We have to disable the codec interrupt in the handler because it + * can take upto 1ms to clear the interrupt source. We schedule a task + * in a work queue to do the actual interaction with the chip (it + * doesn't matter if we end up reenqueing it before it is executed + * since we don't touch the chip until it has run). The interrupt is + * then enabled again in the slow handler when the source has been + * cleared. + */ +static irqreturn_t wm97xx_pen_interrupt(int irq, void *dev_id) +{ + struct wm97xx *wm = dev_id; + + wm->mach_ops->irq_enable(wm, 0); + queue_work(wm->ts_workq, &wm->pen_event_work); + + return IRQ_HANDLED; +} + +/* + * initialise pen IRQ handler and workqueue + */ +static int wm97xx_init_pen_irq(struct wm97xx *wm) +{ + u16 reg; + + /* If an interrupt is supplied an IRQ enable operation must also be + * provided. */ + BUG_ON(!wm->mach_ops->irq_enable); + + if (request_irq(wm->pen_irq, wm97xx_pen_interrupt, IRQF_SHARED, + "wm97xx-pen", wm)) { + dev_err(wm->dev, + "Failed to register pen down interrupt, polling"); + wm->pen_irq = 0; + return -EINVAL; + } + + /* Configure GPIO as interrupt source on WM971x */ + if (wm->id != WM9705_ID2) { + BUG_ON(!wm->mach_ops->irq_gpio); + reg = wm97xx_reg_read(wm, AC97_MISC_AFE); + wm97xx_reg_write(wm, AC97_MISC_AFE, + reg & ~(wm->mach_ops->irq_gpio)); + reg = wm97xx_reg_read(wm, 0x5a); + wm97xx_reg_write(wm, 0x5a, reg & ~0x0001); + } + + return 0; +} + +static int wm97xx_read_samples(struct wm97xx *wm) +{ + struct wm97xx_data data; + int rc; + + mutex_lock(&wm->codec_mutex); + + if (wm->mach_ops && wm->mach_ops->acc_enabled) + rc = wm->mach_ops->acc_pen_down(wm); + else + rc = wm->codec->poll_touch(wm, &data); + + if (rc & RC_PENUP) { + if (wm->pen_is_down) { + wm->pen_is_down = 0; + dev_dbg(wm->dev, "pen up\n"); + input_report_abs(wm->input_dev, ABS_PRESSURE, 0); + input_sync(wm->input_dev); + } else if (!(rc & RC_AGAIN)) { + /* We need high frequency updates only while + * pen is down, the user never will be able to + * touch screen faster than a few times per + * second... On the other hand, when the user + * is actively working with the touchscreen we + * don't want to lose the quick response. So we + * will slowly increase sleep time after the + * pen is up and quicky restore it to ~one task + * switch when pen is down again. + */ + if (wm->ts_reader_interval < HZ / 10) + wm->ts_reader_interval++; + } + + } else if (rc & RC_VALID) { + dev_dbg(wm->dev, + "pen down: x=%x:%d, y=%x:%d, pressure=%x:%d\n", + data.x >> 12, data.x & 0xfff, data.y >> 12, + data.y & 0xfff, data.p >> 12, data.p & 0xfff); + input_report_abs(wm->input_dev, ABS_X, data.x & 0xfff); + input_report_abs(wm->input_dev, ABS_Y, data.y & 0xfff); + input_report_abs(wm->input_dev, ABS_PRESSURE, data.p & 0xfff); + input_sync(wm->input_dev); + wm->pen_is_down = 1; + wm->ts_reader_interval = wm->ts_reader_min_interval; + } else if (rc & RC_PENDOWN) { + dev_dbg(wm->dev, "pen down\n"); + wm->pen_is_down = 1; + wm->ts_reader_interval = wm->ts_reader_min_interval; + } + + mutex_unlock(&wm->codec_mutex); + return rc; +} + +/* +* The touchscreen sample reader. +*/ +static void wm97xx_ts_reader(struct work_struct *work) +{ + int rc; + struct wm97xx *wm = container_of(work, struct wm97xx, ts_reader.work); + + BUG_ON(!wm->codec); + + do { + rc = wm97xx_read_samples(wm); + } while (rc & RC_AGAIN); + + if (wm->pen_is_down || !wm->pen_irq) + queue_delayed_work(wm->ts_workq, &wm->ts_reader, + wm->ts_reader_interval); +} + +/** + * wm97xx_ts_input_open - Open the touch screen input device. + * @idev: Input device to be opened. + * + * Called by the input sub system to open a wm97xx touchscreen device. + * Starts the touchscreen thread and touch digitiser. + */ +static int wm97xx_ts_input_open(struct input_dev *idev) +{ + struct wm97xx *wm = input_get_drvdata(idev); + + wm->ts_workq = create_singlethread_workqueue("kwm97xx"); + if (wm->ts_workq == NULL) { + dev_err(wm->dev, + "Failed to create workqueue\n"); + return -EINVAL; + } + + /* start digitiser */ + if (wm->mach_ops && wm->mach_ops->acc_enabled) + wm->codec->acc_enable(wm, 1); + wm->codec->dig_enable(wm, 1); + + INIT_DELAYED_WORK(&wm->ts_reader, wm97xx_ts_reader); + INIT_WORK(&wm->pen_event_work, wm97xx_pen_irq_worker); + + wm->ts_reader_min_interval = HZ >= 100 ? HZ / 100 : 1; + if (wm->ts_reader_min_interval < 1) + wm->ts_reader_min_interval = 1; + wm->ts_reader_interval = wm->ts_reader_min_interval; + + wm->pen_is_down = 0; + if (wm->pen_irq) + wm97xx_init_pen_irq(wm); + else + dev_err(wm->dev, "No IRQ specified\n"); + + /* If we either don't have an interrupt for pen down events or + * failed to acquire it then we need to poll. + */ + if (wm->pen_irq == 0) + queue_delayed_work(wm->ts_workq, &wm->ts_reader, + wm->ts_reader_interval); + + return 0; +} + +/** + * wm97xx_ts_input_close - Close the touch screen input device. + * @idev: Input device to be closed. + * + * Called by the input sub system to close a wm97xx touchscreen + * device. Kills the touchscreen thread and stops the touch + * digitiser. + */ + +static void wm97xx_ts_input_close(struct input_dev *idev) +{ + struct wm97xx *wm = input_get_drvdata(idev); + u16 reg; + + if (wm->pen_irq) { + /* Return the interrupt to GPIO usage (disabling it) */ + if (wm->id != WM9705_ID2) { + BUG_ON(!wm->mach_ops->irq_gpio); + reg = wm97xx_reg_read(wm, AC97_MISC_AFE); + wm97xx_reg_write(wm, AC97_MISC_AFE, + reg | wm->mach_ops->irq_gpio); + } + + free_irq(wm->pen_irq, wm); + } + + wm->pen_is_down = 0; + + /* Balance out interrupt disables/enables */ + if (cancel_work_sync(&wm->pen_event_work)) + wm->mach_ops->irq_enable(wm, 1); + + /* ts_reader rearms itself so we need to explicitly stop it + * before we destroy the workqueue. + */ + cancel_delayed_work_sync(&wm->ts_reader); + + destroy_workqueue(wm->ts_workq); + + /* stop digitiser */ + wm->codec->dig_enable(wm, 0); + if (wm->mach_ops && wm->mach_ops->acc_enabled) + wm->codec->acc_enable(wm, 0); +} + +static int wm97xx_probe(struct device *dev) +{ + struct wm97xx *wm; + int ret = 0, id = 0; + + wm = kzalloc(sizeof(struct wm97xx), GFP_KERNEL); + if (!wm) + return -ENOMEM; + mutex_init(&wm->codec_mutex); + + wm->dev = dev; + dev->driver_data = wm; + wm->ac97 = to_ac97_t(dev); + + /* check that we have a supported codec */ + id = wm97xx_reg_read(wm, AC97_VENDOR_ID1); + if (id != WM97XX_ID1) { + dev_err(dev, "Device with vendor %04x is not a wm97xx\n", id); + ret = -ENODEV; + goto alloc_err; + } + + wm->id = wm97xx_reg_read(wm, AC97_VENDOR_ID2); + + dev_info(wm->dev, "detected a wm97%02x codec\n", wm->id & 0xff); + + switch (wm->id & 0xff) { +#ifdef CONFIG_TOUCHSCREEN_WM9705 + case 0x05: + wm->codec = &wm9705_codec; + break; +#endif +#ifdef CONFIG_TOUCHSCREEN_WM9712 + case 0x12: + wm->codec = &wm9712_codec; + break; +#endif +#ifdef CONFIG_TOUCHSCREEN_WM9713 + case 0x13: + wm->codec = &wm9713_codec; + break; +#endif + default: + dev_err(wm->dev, "Support for wm97%02x not compiled in.\n", + wm->id & 0xff); + ret = -ENODEV; + goto alloc_err; + } + + wm->input_dev = input_allocate_device(); + if (wm->input_dev == NULL) { + ret = -ENOMEM; + goto alloc_err; + } + + /* set up touch configuration */ + wm->input_dev->name = "wm97xx touchscreen"; + wm->input_dev->open = wm97xx_ts_input_open; + wm->input_dev->close = wm97xx_ts_input_close; + set_bit(EV_ABS, wm->input_dev->evbit); + set_bit(ABS_X, wm->input_dev->absbit); + set_bit(ABS_Y, wm->input_dev->absbit); + set_bit(ABS_PRESSURE, wm->input_dev->absbit); + input_set_abs_params(wm->input_dev, ABS_X, abs_x[0], abs_x[1], + abs_x[2], 0); + input_set_abs_params(wm->input_dev, ABS_Y, abs_y[0], abs_y[1], + abs_y[2], 0); + input_set_abs_params(wm->input_dev, ABS_PRESSURE, abs_p[0], abs_p[1], + abs_p[2], 0); + input_set_drvdata(wm->input_dev, wm); + wm->input_dev->dev.parent = dev; + ret = input_register_device(wm->input_dev); + if (ret < 0) + goto dev_alloc_err; + + /* set up physical characteristics */ + wm->codec->phy_init(wm); + + /* load gpio cache */ + wm->gpio[0] = wm97xx_reg_read(wm, AC97_GPIO_CFG); + wm->gpio[1] = wm97xx_reg_read(wm, AC97_GPIO_POLARITY); + wm->gpio[2] = wm97xx_reg_read(wm, AC97_GPIO_STICKY); + wm->gpio[3] = wm97xx_reg_read(wm, AC97_GPIO_WAKEUP); + wm->gpio[4] = wm97xx_reg_read(wm, AC97_GPIO_STATUS); + wm->gpio[5] = wm97xx_reg_read(wm, AC97_MISC_AFE); + + /* register our battery device */ + wm->battery_dev = platform_device_alloc("wm97xx-battery", -1); + if (!wm->battery_dev) { + ret = -ENOMEM; + goto batt_err; + } + platform_set_drvdata(wm->battery_dev, wm); + wm->battery_dev->dev.parent = dev; + ret = platform_device_add(wm->battery_dev); + if (ret < 0) + goto batt_reg_err; + + /* register our extended touch device (for machine specific + * extensions) */ + wm->touch_dev = platform_device_alloc("wm97xx-touch", -1); + if (!wm->touch_dev) { + ret = -ENOMEM; + goto touch_err; + } + platform_set_drvdata(wm->touch_dev, wm); + wm->touch_dev->dev.parent = dev; + ret = platform_device_add(wm->touch_dev); + if (ret < 0) + goto touch_reg_err; + + return ret; + + touch_reg_err: + platform_device_put(wm->touch_dev); + touch_err: + platform_device_unregister(wm->battery_dev); + wm->battery_dev = NULL; + batt_reg_err: + platform_device_put(wm->battery_dev); + batt_err: + input_unregister_device(wm->input_dev); + wm->input_dev = NULL; + dev_alloc_err: + input_free_device(wm->input_dev); + alloc_err: + kfree(wm); + + return ret; +} + +static int wm97xx_remove(struct device *dev) +{ + struct wm97xx *wm = dev_get_drvdata(dev); + + platform_device_unregister(wm->battery_dev); + platform_device_unregister(wm->touch_dev); + input_unregister_device(wm->input_dev); + kfree(wm); + + return 0; +} + +#ifdef CONFIG_PM +static int wm97xx_suspend(struct device *dev, pm_message_t state) +{ + struct wm97xx *wm = dev_get_drvdata(dev); + + if (wm->input_dev->users) + cancel_delayed_work_sync(&wm->ts_reader); + + return 0; +} + +static int wm97xx_resume(struct device *dev) +{ + struct wm97xx *wm = dev_get_drvdata(dev); + + /* restore digitiser and gpios */ + if (wm->id == WM9713_ID2) { + wm97xx_reg_write(wm, AC97_WM9713_DIG1, wm->dig[0]); + wm97xx_reg_write(wm, 0x5a, wm->misc); + if (wm->input_dev->users) { + u16 reg; + reg = wm97xx_reg_read(wm, AC97_EXTENDED_MID) & 0x7fff; + wm97xx_reg_write(wm, AC97_EXTENDED_MID, reg); + } + } + + wm97xx_reg_write(wm, AC97_WM9713_DIG2, wm->dig[1]); + wm97xx_reg_write(wm, AC97_WM9713_DIG3, wm->dig[2]); + + wm97xx_reg_write(wm, AC97_GPIO_CFG, wm->gpio[0]); + wm97xx_reg_write(wm, AC97_GPIO_POLARITY, wm->gpio[1]); + wm97xx_reg_write(wm, AC97_GPIO_STICKY, wm->gpio[2]); + wm97xx_reg_write(wm, AC97_GPIO_WAKEUP, wm->gpio[3]); + wm97xx_reg_write(wm, AC97_GPIO_STATUS, wm->gpio[4]); + wm97xx_reg_write(wm, AC97_MISC_AFE, wm->gpio[5]); + + if (wm->input_dev->users && !wm->pen_irq) { + wm->ts_reader_interval = wm->ts_reader_min_interval; + queue_delayed_work(wm->ts_workq, &wm->ts_reader, + wm->ts_reader_interval); + } + + return 0; +} + +#else +#define wm97xx_suspend NULL +#define wm97xx_resume NULL +#endif + +/* + * Machine specific operations + */ +int wm97xx_register_mach_ops(struct wm97xx *wm, + struct wm97xx_mach_ops *mach_ops) +{ + mutex_lock(&wm->codec_mutex); + if (wm->mach_ops) { + mutex_unlock(&wm->codec_mutex); + return -EINVAL; + } + wm->mach_ops = mach_ops; + mutex_unlock(&wm->codec_mutex); + + return 0; +} +EXPORT_SYMBOL_GPL(wm97xx_register_mach_ops); + +void wm97xx_unregister_mach_ops(struct wm97xx *wm) +{ + mutex_lock(&wm->codec_mutex); + wm->mach_ops = NULL; + mutex_unlock(&wm->codec_mutex); +} +EXPORT_SYMBOL_GPL(wm97xx_unregister_mach_ops); + +static struct device_driver wm97xx_driver = { + .name = "ac97", + .bus = &ac97_bus_type, + .owner = THIS_MODULE, + .probe = wm97xx_probe, + .remove = wm97xx_remove, + .suspend = wm97xx_suspend, + .resume = wm97xx_resume, +}; + +static int __init wm97xx_init(void) +{ + return driver_register(&wm97xx_driver); +} + +static void __exit wm97xx_exit(void) +{ + driver_unregister(&wm97xx_driver); +} + +module_init(wm97xx_init); +module_exit(wm97xx_exit); + +/* Module information */ +MODULE_AUTHOR("Liam Girdwood "); +MODULE_DESCRIPTION("WM97xx Core - Touch Screen / AUX ADC / GPIO Driver"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/wm97xx.h b/include/linux/wm97xx.h new file mode 100644 index 000000000000..ed01c7df54a3 --- /dev/null +++ b/include/linux/wm97xx.h @@ -0,0 +1,311 @@ + +/* + * Register bits and API for Wolfson WM97xx series of codecs + */ + +#ifndef _LINUX_WM97XX_H +#define _LINUX_WM97XX_H + +#include +#include +#include +#include +#include +#include +#include /* Input device layer */ +#include + +/* + * WM97xx AC97 Touchscreen registers + */ +#define AC97_WM97XX_DIGITISER1 0x76 +#define AC97_WM97XX_DIGITISER2 0x78 +#define AC97_WM97XX_DIGITISER_RD 0x7a +#define AC97_WM9713_DIG1 0x74 +#define AC97_WM9713_DIG2 AC97_WM97XX_DIGITISER1 +#define AC97_WM9713_DIG3 AC97_WM97XX_DIGITISER2 + +/* + * WM97xx register bits + */ +#define WM97XX_POLL 0x8000 /* initiate a polling measurement */ +#define WM97XX_ADCSEL_X 0x1000 /* x coord measurement */ +#define WM97XX_ADCSEL_Y 0x2000 /* y coord measurement */ +#define WM97XX_ADCSEL_PRES 0x3000 /* pressure measurement */ +#define WM97XX_ADCSEL_MASK 0x7000 +#define WM97XX_COO 0x0800 /* enable coordinate mode */ +#define WM97XX_CTC 0x0400 /* enable continuous mode */ +#define WM97XX_CM_RATE_93 0x0000 /* 93.75Hz continuous rate */ +#define WM97XX_CM_RATE_187 0x0100 /* 187.5Hz continuous rate */ +#define WM97XX_CM_RATE_375 0x0200 /* 375Hz continuous rate */ +#define WM97XX_CM_RATE_750 0x0300 /* 750Hz continuous rate */ +#define WM97XX_CM_RATE_8K 0x00f0 /* 8kHz continuous rate */ +#define WM97XX_CM_RATE_12K 0x01f0 /* 12kHz continuous rate */ +#define WM97XX_CM_RATE_24K 0x02f0 /* 24kHz continuous rate */ +#define WM97XX_CM_RATE_48K 0x03f0 /* 48kHz continuous rate */ +#define WM97XX_CM_RATE_MASK 0x03f0 +#define WM97XX_RATE(i) (((i & 3) << 8) | ((i & 4) ? 0xf0 : 0)) +#define WM97XX_DELAY(i) ((i << 4) & 0x00f0) /* sample delay times */ +#define WM97XX_DELAY_MASK 0x00f0 +#define WM97XX_SLEN 0x0008 /* slot read back enable */ +#define WM97XX_SLT(i) ((i - 5) & 0x7) /* panel slot (5-11) */ +#define WM97XX_SLT_MASK 0x0007 +#define WM97XX_PRP_DETW 0x4000 /* detect on, digitise off, wake */ +#define WM97XX_PRP_DET 0x8000 /* detect on, digitise off, no wake */ +#define WM97XX_PRP_DET_DIG 0xc000 /* setect on, digitise on */ +#define WM97XX_RPR 0x2000 /* wake up on pen down */ +#define WM97XX_PEN_DOWN 0x8000 /* pen is down */ +#define WM97XX_ADCSRC_MASK 0x7000 /* ADC source mask */ + +#define WM97XX_AUX_ID1 0x8001 +#define WM97XX_AUX_ID2 0x8002 +#define WM97XX_AUX_ID3 0x8003 +#define WM97XX_AUX_ID4 0x8004 + + +/* WM9712 Bits */ +#define WM9712_45W 0x1000 /* set for 5-wire touchscreen */ +#define WM9712_PDEN 0x0800 /* measure only when pen down */ +#define WM9712_WAIT 0x0200 /* wait until adc is read before next sample */ +#define WM9712_PIL 0x0100 /* current used for pressure measurement. set 400uA else 200uA */ +#define WM9712_MASK_HI 0x0040 /* hi on mask pin (47) stops conversions */ +#define WM9712_MASK_EDGE 0x0080 /* rising/falling edge on pin delays sample */ +#define WM9712_MASK_SYNC 0x00c0 /* rising/falling edge on mask initiates sample */ +#define WM9712_RPU(i) (i&0x3f) /* internal pull up on pen detect (64k / rpu) */ +#define WM9712_PD(i) (0x1 << i) /* power management */ + +/* WM9712 Registers */ +#define AC97_WM9712_POWER 0x24 +#define AC97_WM9712_REV 0x58 + +/* WM9705 Bits */ +#define WM9705_PDEN 0x1000 /* measure only when pen is down */ +#define WM9705_PINV 0x0800 /* inverts sense of pen down output */ +#define WM9705_BSEN 0x0400 /* BUSY flag enable, pin47 is 1 when busy */ +#define WM9705_BINV 0x0200 /* invert BUSY (pin47) output */ +#define WM9705_WAIT 0x0100 /* wait until adc is read before next sample */ +#define WM9705_PIL 0x0080 /* current used for pressure measurement. set 400uA else 200uA */ +#define WM9705_PHIZ 0x0040 /* set PHONE and PCBEEP inputs to high impedance */ +#define WM9705_MASK_HI 0x0010 /* hi on mask stops conversions */ +#define WM9705_MASK_EDGE 0x0020 /* rising/falling edge on pin delays sample */ +#define WM9705_MASK_SYNC 0x0030 /* rising/falling edge on mask initiates sample */ +#define WM9705_PDD(i) (i & 0x000f) /* pen detect comparator threshold */ + + +/* WM9713 Bits */ +#define WM9713_PDPOL 0x0400 /* Pen down polarity */ +#define WM9713_POLL 0x0200 /* initiate a polling measurement */ +#define WM9713_CTC 0x0100 /* enable continuous mode */ +#define WM9713_ADCSEL_X 0x0002 /* X measurement */ +#define WM9713_ADCSEL_Y 0x0004 /* Y measurement */ +#define WM9713_ADCSEL_PRES 0x0008 /* Pressure measurement */ +#define WM9713_COO 0x0001 /* enable coordinate mode */ +#define WM9713_PDEN 0x0800 /* measure only when pen down */ +#define WM9713_ADCSEL_MASK 0x00fe /* ADC selection mask */ +#define WM9713_WAIT 0x0200 /* coordinate wait */ + +/* AUX ADC ID's */ +#define TS_COMP1 0x0 +#define TS_COMP2 0x1 +#define TS_BMON 0x2 +#define TS_WIPER 0x3 + +/* ID numbers */ +#define WM97XX_ID1 0x574d +#define WM9712_ID2 0x4c12 +#define WM9705_ID2 0x4c05 +#define WM9713_ID2 0x4c13 + +/* Codec GPIO's */ +#define WM97XX_MAX_GPIO 16 +#define WM97XX_GPIO_1 (1 << 1) +#define WM97XX_GPIO_2 (1 << 2) +#define WM97XX_GPIO_3 (1 << 3) +#define WM97XX_GPIO_4 (1 << 4) +#define WM97XX_GPIO_5 (1 << 5) +#define WM97XX_GPIO_6 (1 << 6) +#define WM97XX_GPIO_7 (1 << 7) +#define WM97XX_GPIO_8 (1 << 8) +#define WM97XX_GPIO_9 (1 << 9) +#define WM97XX_GPIO_10 (1 << 10) +#define WM97XX_GPIO_11 (1 << 11) +#define WM97XX_GPIO_12 (1 << 12) +#define WM97XX_GPIO_13 (1 << 13) +#define WM97XX_GPIO_14 (1 << 14) +#define WM97XX_GPIO_15 (1 << 15) + + +#define AC97_LINK_FRAME 21 /* time in uS for AC97 link frame */ + + +/*---------------- Return codes from sample reading functions ---------------*/ + +/* More data is available; call the sample gathering function again */ +#define RC_AGAIN 0x00000001 +/* The returned sample is valid */ +#define RC_VALID 0x00000002 +/* The pen is up (the first RC_VALID without RC_PENUP means pen is down) */ +#define RC_PENUP 0x00000004 +/* The pen is down (RC_VALID implies RC_PENDOWN, but sometimes it is helpful + to tell the handler that the pen is down but we don't know yet his coords, + so the handler should not sleep or wait for pendown irq) */ +#define RC_PENDOWN 0x00000008 + +/* + * The wm97xx driver provides a private API for writing platform-specific + * drivers. + */ + +/* The structure used to return arch specific sampled data into */ +struct wm97xx_data { + int x; + int y; + int p; +}; + +/* + * Codec GPIO status + */ +enum wm97xx_gpio_status { + WM97XX_GPIO_HIGH, + WM97XX_GPIO_LOW +}; + +/* + * Codec GPIO direction + */ +enum wm97xx_gpio_dir { + WM97XX_GPIO_IN, + WM97XX_GPIO_OUT +}; + +/* + * Codec GPIO polarity + */ +enum wm97xx_gpio_pol { + WM97XX_GPIO_POL_HIGH, + WM97XX_GPIO_POL_LOW +}; + +/* + * Codec GPIO sticky + */ +enum wm97xx_gpio_sticky { + WM97XX_GPIO_STICKY, + WM97XX_GPIO_NOTSTICKY +}; + +/* + * Codec GPIO wake + */ +enum wm97xx_gpio_wake { + WM97XX_GPIO_WAKE, + WM97XX_GPIO_NOWAKE +}; + +/* + * Digitiser ioctl commands + */ +#define WM97XX_DIG_START 0x1 +#define WM97XX_DIG_STOP 0x2 +#define WM97XX_PHY_INIT 0x3 +#define WM97XX_AUX_PREPARE 0x4 +#define WM97XX_DIG_RESTORE 0x5 + +struct wm97xx; + +extern struct wm97xx_codec_drv wm9705_codec; +extern struct wm97xx_codec_drv wm9712_codec; +extern struct wm97xx_codec_drv wm9713_codec; + +/* + * Codec driver interface - allows mapping to WM9705/12/13 and newer codecs + */ +struct wm97xx_codec_drv { + u16 id; + char *name; + + /* read 1 sample */ + int (*poll_sample) (struct wm97xx *, int adcsel, int *sample); + + /* read X,Y,[P] in poll */ + int (*poll_touch) (struct wm97xx *, struct wm97xx_data *); + + int (*acc_enable) (struct wm97xx *, int enable); + void (*phy_init) (struct wm97xx *); + void (*dig_enable) (struct wm97xx *, int enable); + void (*dig_restore) (struct wm97xx *); + void (*aux_prepare) (struct wm97xx *); +}; + + +/* Machine specific and accelerated touch operations */ +struct wm97xx_mach_ops { + + /* accelerated touch readback - coords are transmited on AC97 link */ + int acc_enabled; + void (*acc_pen_up) (struct wm97xx *); + int (*acc_pen_down) (struct wm97xx *); + int (*acc_startup) (struct wm97xx *); + void (*acc_shutdown) (struct wm97xx *); + + /* interrupt mask control - required for accelerated operation */ + void (*irq_enable) (struct wm97xx *, int enable); + + /* GPIO pin used for accelerated operation */ + int irq_gpio; + + /* pre and post sample - can be used to minimise any analog noise */ + void (*pre_sample) (int); /* function to run before sampling */ + void (*post_sample) (int); /* function to run after sampling */ +}; + +struct wm97xx { + u16 dig[3], id, gpio[6], misc; /* Cached codec registers */ + u16 dig_save[3]; /* saved during aux reading */ + struct wm97xx_codec_drv *codec; /* attached codec driver*/ + struct input_dev *input_dev; /* touchscreen input device */ + struct snd_ac97 *ac97; /* ALSA codec access */ + struct device *dev; /* ALSA device */ + struct platform_device *battery_dev; + struct platform_device *touch_dev; + struct wm97xx_mach_ops *mach_ops; + struct mutex codec_mutex; + struct delayed_work ts_reader; /* Used to poll touchscreen */ + unsigned long ts_reader_interval; /* Current interval for timer */ + unsigned long ts_reader_min_interval; /* Minimum interval */ + unsigned int pen_irq; /* Pen IRQ number in use */ + struct workqueue_struct *ts_workq; + struct work_struct pen_event_work; + u16 acc_slot; /* AC97 slot used for acc touch data */ + u16 acc_rate; /* acc touch data rate */ + unsigned pen_is_down:1; /* Pen is down */ + unsigned aux_waiting:1; /* aux measurement waiting */ + unsigned pen_probably_down:1; /* used in polling mode */ +}; + +/* + * Codec GPIO access (not supported on WM9705) + * This can be used to set/get codec GPIO and Virtual GPIO status. + */ +enum wm97xx_gpio_status wm97xx_get_gpio(struct wm97xx *wm, u32 gpio); +void wm97xx_set_gpio(struct wm97xx *wm, u32 gpio, + enum wm97xx_gpio_status status); +void wm97xx_config_gpio(struct wm97xx *wm, u32 gpio, + enum wm97xx_gpio_dir dir, + enum wm97xx_gpio_pol pol, + enum wm97xx_gpio_sticky sticky, + enum wm97xx_gpio_wake wake); + +/* codec AC97 IO access */ +int wm97xx_reg_read(struct wm97xx *wm, u16 reg); +void wm97xx_reg_write(struct wm97xx *wm, u16 reg, u16 val); + +/* aux adc readback */ +int wm97xx_read_aux_adc(struct wm97xx *wm, u16 adcsel); + +/* machine ops */ +int wm97xx_register_mach_ops(struct wm97xx *, struct wm97xx_mach_ops *); +void wm97xx_unregister_mach_ops(struct wm97xx *); + +#endif -- cgit v1.2.3-70-g09d2 From 9448cefc6689aa51f1cd1cfe8b701dc94789c7ee Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 2 Apr 2008 00:51:21 -0400 Subject: Input: WM97xx - add chip driver for WM9705 touchscreen Signed-off-by: Liam Girdwood Signed-off-by: Graeme Gregory Signed-off-by: Mike Arthur Signed-off-by: Mark Brown Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/Kconfig | 9 + drivers/input/touchscreen/Makefile | 1 + drivers/input/touchscreen/wm9705.c | 353 +++++++++++++++++++++++++++++++++++++ 3 files changed, 363 insertions(+) create mode 100644 drivers/input/touchscreen/wm9705.c diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index a95a87cd1d59..4939a96d12b9 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -199,6 +199,15 @@ config TOUCHSCREEN_WM97XX To compile this driver as a module, choose M here: the module will be called wm97xx-ts. +config TOUCHSCREEN_WM9705 + bool "WM9705 Touchscreen interface support" + depends on TOUCHSCREEN_WM97XX + help + Say Y here if you have a Wolfson Microelectronics WM9705 + touchscreen controller connected to your system. + + If unsure, say N. + config TOUCHSCREEN_USB_COMPOSITE tristate "USB Touchscreen Driver" depends on USB_ARCH_HAS_HCD diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index b4e7b5dbf649..5829a435efd1 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -22,3 +22,4 @@ obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN) += touchwin.o obj-$(CONFIG_TOUCHSCREEN_UCB1400) += ucb1400_ts.o obj-$(CONFIG_TOUCHSCREEN_WM97XX) += wm97xx-ts.o +wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9705) += wm9705.o diff --git a/drivers/input/touchscreen/wm9705.c b/drivers/input/touchscreen/wm9705.c new file mode 100644 index 000000000000..978e1a13ffc7 --- /dev/null +++ b/drivers/input/touchscreen/wm9705.c @@ -0,0 +1,353 @@ +/* + * wm9705.c -- Codec driver for Wolfson WM9705 AC97 Codec. + * + * Copyright 2003, 2004, 2005, 2006, 2007 Wolfson Microelectronics PLC. + * Author: Liam Girdwood + * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com + * Parts Copyright : Ian Molton + * Andrew Zabolotny + * Russell King + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define TS_NAME "wm97xx" +#define WM9705_VERSION "1.00" +#define DEFAULT_PRESSURE 0xb0c0 + +/* + * Module parameters + */ + +/* + * Set current used for pressure measurement. + * + * Set pil = 2 to use 400uA + * pil = 1 to use 200uA and + * pil = 0 to disable pressure measurement. + * + * This is used to increase the range of values returned by the adc + * when measureing touchpanel pressure. + */ +static int pil; +module_param(pil, int, 0); +MODULE_PARM_DESC(pil, "Set current used for pressure measurement."); + +/* + * Set threshold for pressure measurement. + * + * Pen down pressure below threshold is ignored. + */ +static int pressure = DEFAULT_PRESSURE & 0xfff; +module_param(pressure, int, 0); +MODULE_PARM_DESC(pressure, "Set threshold for pressure measurement."); + +/* + * Set adc sample delay. + * + * For accurate touchpanel measurements, some settling time may be + * required between the switch matrix applying a voltage across the + * touchpanel plate and the ADC sampling the signal. + * + * This delay can be set by setting delay = n, where n is the array + * position of the delay in the array delay_table below. + * Long delays > 1ms are supported for completeness, but are not + * recommended. + */ +static int delay = 4; +module_param(delay, int, 0); +MODULE_PARM_DESC(delay, "Set adc sample delay."); + +/* + * Pen detect comparator threshold. + * + * 0 to Vmid in 15 steps, 0 = use zero power comparator with Vmid threshold + * i.e. 1 = Vmid/15 threshold + * 15 = Vmid/1 threshold + * + * Adjust this value if you are having problems with pen detect not + * detecting any down events. + */ +static int pdd = 8; +module_param(pdd, int, 0); +MODULE_PARM_DESC(pdd, "Set pen detect comparator threshold"); + +/* + * Set adc mask function. + * + * Sources of glitch noise, such as signals driving an LCD display, may feed + * through to the touch screen plates and affect measurement accuracy. In + * order to minimise this, a signal may be applied to the MASK pin to delay or + * synchronise the sampling. + * + * 0 = No delay or sync + * 1 = High on pin stops conversions + * 2 = Edge triggered, edge on pin delays conversion by delay param (above) + * 3 = Edge triggered, edge on pin starts conversion after delay param + */ +static int mask; +module_param(mask, int, 0); +MODULE_PARM_DESC(mask, "Set adc mask function."); + +/* + * ADC sample delay times in uS + */ +static const int delay_table[] = { + 21, /* 1 AC97 Link frames */ + 42, /* 2 */ + 84, /* 4 */ + 167, /* 8 */ + 333, /* 16 */ + 667, /* 32 */ + 1000, /* 48 */ + 1333, /* 64 */ + 2000, /* 96 */ + 2667, /* 128 */ + 3333, /* 160 */ + 4000, /* 192 */ + 4667, /* 224 */ + 5333, /* 256 */ + 6000, /* 288 */ + 0 /* No delay, switch matrix always on */ +}; + +/* + * Delay after issuing a POLL command. + * + * The delay is 3 AC97 link frames + the touchpanel settling delay + */ +static inline void poll_delay(int d) +{ + udelay(3 * AC97_LINK_FRAME + delay_table[d]); +} + +/* + * set up the physical settings of the WM9705 + */ +static void wm9705_phy_init(struct wm97xx *wm) +{ + u16 dig1 = 0, dig2 = WM97XX_RPR; + + /* + * mute VIDEO and AUX as they share X and Y touchscreen + * inputs on the WM9705 + */ + wm97xx_reg_write(wm, AC97_AUX, 0x8000); + wm97xx_reg_write(wm, AC97_VIDEO, 0x8000); + + /* touchpanel pressure current*/ + if (pil == 2) { + dig2 |= WM9705_PIL; + dev_dbg(wm->dev, + "setting pressure measurement current to 400uA."); + } else if (pil) + dev_dbg(wm->dev, + "setting pressure measurement current to 200uA."); + if (!pil) + pressure = 0; + + /* polling mode sample settling delay */ + if (delay != 4) { + if (delay < 0 || delay > 15) { + dev_dbg(wm->dev, "supplied delay out of range."); + delay = 4; + } + } + dig1 &= 0xff0f; + dig1 |= WM97XX_DELAY(delay); + dev_dbg(wm->dev, "setting adc sample delay to %d u Secs.", + delay_table[delay]); + + /* WM9705 pdd */ + dig2 |= (pdd & 0x000f); + dev_dbg(wm->dev, "setting pdd to Vmid/%d", 1 - (pdd & 0x000f)); + + /* mask */ + dig2 |= ((mask & 0x3) << 4); + + wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, dig1); + wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2); +} + +static void wm9705_dig_enable(struct wm97xx *wm, int enable) +{ + if (enable) { + wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, + wm->dig[2] | WM97XX_PRP_DET_DIG); + wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); /* dummy read */ + } else + wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, + wm->dig[2] & ~WM97XX_PRP_DET_DIG); +} + +static void wm9705_aux_prepare(struct wm97xx *wm) +{ + memcpy(wm->dig_save, wm->dig, sizeof(wm->dig)); + wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, 0); + wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, WM97XX_PRP_DET_DIG); +} + +static void wm9705_dig_restore(struct wm97xx *wm) +{ + wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, wm->dig_save[1]); + wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, wm->dig_save[2]); +} + +static inline int is_pden(struct wm97xx *wm) +{ + return wm->dig[2] & WM9705_PDEN; +} + +/* + * Read a sample from the WM9705 adc in polling mode. + */ +static int wm9705_poll_sample(struct wm97xx *wm, int adcsel, int *sample) +{ + int timeout = 5 * delay; + + if (!wm->pen_probably_down) { + u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); + if (!(data & WM97XX_PEN_DOWN)) + return RC_PENUP; + wm->pen_probably_down = 1; + } + + /* set up digitiser */ + if (adcsel & 0x8000) + adcsel = ((adcsel & 0x7fff) + 3) << 12; + + if (wm->mach_ops && wm->mach_ops->pre_sample) + wm->mach_ops->pre_sample(adcsel); + wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, + adcsel | WM97XX_POLL | WM97XX_DELAY(delay)); + + /* wait 3 AC97 time slots + delay for conversion */ + poll_delay(delay); + + /* wait for POLL to go low */ + while ((wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER1) & WM97XX_POLL) + && timeout) { + udelay(AC97_LINK_FRAME); + timeout--; + } + + if (timeout == 0) { + /* If PDEN is set, we can get a timeout when pen goes up */ + if (is_pden(wm)) + wm->pen_probably_down = 0; + else + dev_dbg(wm->dev, "adc sample timeout"); + return RC_PENUP; + } + + *sample = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); + if (wm->mach_ops && wm->mach_ops->post_sample) + wm->mach_ops->post_sample(adcsel); + + /* check we have correct sample */ + if ((*sample & WM97XX_ADCSEL_MASK) != adcsel) { + dev_dbg(wm->dev, "adc wrong sample, read %x got %x", adcsel, + *sample & WM97XX_ADCSEL_MASK); + return RC_PENUP; + } + + if (!(*sample & WM97XX_PEN_DOWN)) { + wm->pen_probably_down = 0; + return RC_PENUP; + } + + return RC_VALID; +} + +/* + * Sample the WM9705 touchscreen in polling mode + */ +static int wm9705_poll_touch(struct wm97xx *wm, struct wm97xx_data *data) +{ + int rc; + + rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_X, &data->x); + if (rc != RC_VALID) + return rc; + rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_Y, &data->y); + if (rc != RC_VALID) + return rc; + if (pil) { + rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_PRES, &data->p); + if (rc != RC_VALID) + return rc; + } else + data->p = DEFAULT_PRESSURE; + + return RC_VALID; +} + +/* + * Enable WM9705 continuous mode, i.e. touch data is streamed across + * an AC97 slot + */ +static int wm9705_acc_enable(struct wm97xx *wm, int enable) +{ + u16 dig1, dig2; + int ret = 0; + + dig1 = wm->dig[1]; + dig2 = wm->dig[2]; + + if (enable) { + /* continous mode */ + if (wm->mach_ops->acc_startup && + (ret = wm->mach_ops->acc_startup(wm)) < 0) + return ret; + dig1 &= ~(WM97XX_CM_RATE_MASK | WM97XX_ADCSEL_MASK | + WM97XX_DELAY_MASK | WM97XX_SLT_MASK); + dig1 |= WM97XX_CTC | WM97XX_COO | WM97XX_SLEN | + WM97XX_DELAY(delay) | + WM97XX_SLT(wm->acc_slot) | + WM97XX_RATE(wm->acc_rate); + if (pil) + dig1 |= WM97XX_ADCSEL_PRES; + dig2 |= WM9705_PDEN; + } else { + dig1 &= ~(WM97XX_CTC | WM97XX_COO | WM97XX_SLEN); + dig2 &= ~WM9705_PDEN; + if (wm->mach_ops->acc_shutdown) + wm->mach_ops->acc_shutdown(wm); + } + + wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, dig1); + wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2); + + return ret; +} + +struct wm97xx_codec_drv wm9705_codec = { + .id = WM9705_ID2, + .name = "wm9705", + .poll_sample = wm9705_poll_sample, + .poll_touch = wm9705_poll_touch, + .acc_enable = wm9705_acc_enable, + .phy_init = wm9705_phy_init, + .dig_enable = wm9705_dig_enable, + .dig_restore = wm9705_dig_restore, + .aux_prepare = wm9705_aux_prepare, +}; +EXPORT_SYMBOL_GPL(wm9705_codec); + +/* Module information */ +MODULE_AUTHOR("Liam Girdwood "); +MODULE_DESCRIPTION("WM9705 Touch Screen Driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3-70-g09d2 From de22b9ef1020ffdb9e1ed6f4686e2e62eaeb0958 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 2 Apr 2008 00:51:26 -0400 Subject: Input: WM97xx - add chip driver for WM9712 touchscreen Signed-off-by: Liam Girdwood Signed-off-by: Graeme Gregory Signed-off-by: Mike Arthur Signed-off-by: Mark Brown Signed-off-by: Lars Munch Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/Kconfig | 9 + drivers/input/touchscreen/Makefile | 1 + drivers/input/touchscreen/wm9712.c | 462 +++++++++++++++++++++++++++++++++++++ 3 files changed, 472 insertions(+) create mode 100644 drivers/input/touchscreen/wm9712.c diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 4939a96d12b9..240e441c118a 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -208,6 +208,15 @@ config TOUCHSCREEN_WM9705 If unsure, say N. +config TOUCHSCREEN_WM9712 + bool "WM9712 Touchscreen interface support" + depends on TOUCHSCREEN_WM97XX + help + Say Y here if you have a Wolfson Microelectronics WM9712 + touchscreen controller connected to your system. + + If unsure, say N. + config TOUCHSCREEN_USB_COMPOSITE tristate "USB Touchscreen Driver" depends on USB_ARCH_HAS_HCD diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 5829a435efd1..eb57f66e57f3 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -23,3 +23,4 @@ obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN) += touchwin.o obj-$(CONFIG_TOUCHSCREEN_UCB1400) += ucb1400_ts.o obj-$(CONFIG_TOUCHSCREEN_WM97XX) += wm97xx-ts.o wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9705) += wm9705.o +wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9712) += wm9712.o diff --git a/drivers/input/touchscreen/wm9712.c b/drivers/input/touchscreen/wm9712.c new file mode 100644 index 000000000000..0b6e4cfa6a21 --- /dev/null +++ b/drivers/input/touchscreen/wm9712.c @@ -0,0 +1,462 @@ +/* + * wm9712.c -- Codec driver for Wolfson WM9712 AC97 Codecs. + * + * Copyright 2003, 2004, 2005, 2006, 2007 Wolfson Microelectronics PLC. + * Author: Liam Girdwood + * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com + * Parts Copyright : Ian Molton + * Andrew Zabolotny + * Russell King + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define TS_NAME "wm97xx" +#define WM9712_VERSION "1.00" +#define DEFAULT_PRESSURE 0xb0c0 + +/* + * Module parameters + */ + +/* + * Set internal pull up for pen detect. + * + * Pull up is in the range 1.02k (least sensitive) to 64k (most sensitive) + * i.e. pull up resistance = 64k Ohms / rpu. + * + * Adjust this value if you are having problems with pen detect not + * detecting any down event. + */ +static int rpu = 8; +module_param(rpu, int, 0); +MODULE_PARM_DESC(rpu, "Set internal pull up resitor for pen detect."); + +/* + * Set current used for pressure measurement. + * + * Set pil = 2 to use 400uA + * pil = 1 to use 200uA and + * pil = 0 to disable pressure measurement. + * + * This is used to increase the range of values returned by the adc + * when measureing touchpanel pressure. + */ +static int pil; +module_param(pil, int, 0); +MODULE_PARM_DESC(pil, "Set current used for pressure measurement."); + +/* + * Set threshold for pressure measurement. + * + * Pen down pressure below threshold is ignored. + */ +static int pressure = DEFAULT_PRESSURE & 0xfff; +module_param(pressure, int, 0); +MODULE_PARM_DESC(pressure, "Set threshold for pressure measurement."); + +/* + * Set adc sample delay. + * + * For accurate touchpanel measurements, some settling time may be + * required between the switch matrix applying a voltage across the + * touchpanel plate and the ADC sampling the signal. + * + * This delay can be set by setting delay = n, where n is the array + * position of the delay in the array delay_table below. + * Long delays > 1ms are supported for completeness, but are not + * recommended. + */ +static int delay = 3; +module_param(delay, int, 0); +MODULE_PARM_DESC(delay, "Set adc sample delay."); + +/* + * Set five_wire = 1 to use a 5 wire touchscreen. + * + * NOTE: Five wire mode does not allow for readback of pressure. + */ +static int five_wire; +module_param(five_wire, int, 0); +MODULE_PARM_DESC(five_wire, "Set to '1' to use 5-wire touchscreen."); + +/* + * Set adc mask function. + * + * Sources of glitch noise, such as signals driving an LCD display, may feed + * through to the touch screen plates and affect measurement accuracy. In + * order to minimise this, a signal may be applied to the MASK pin to delay or + * synchronise the sampling. + * + * 0 = No delay or sync + * 1 = High on pin stops conversions + * 2 = Edge triggered, edge on pin delays conversion by delay param (above) + * 3 = Edge triggered, edge on pin starts conversion after delay param + */ +static int mask; +module_param(mask, int, 0); +MODULE_PARM_DESC(mask, "Set adc mask function."); + +/* + * Coordinate Polling Enable. + * + * Set to 1 to enable coordinate polling. e.g. x,y[,p] is sampled together + * for every poll. + */ +static int coord; +module_param(coord, int, 0); +MODULE_PARM_DESC(coord, "Polling coordinate mode"); + +/* + * ADC sample delay times in uS + */ +static const int delay_table[] = { + 21, /* 1 AC97 Link frames */ + 42, /* 2 */ + 84, /* 4 */ + 167, /* 8 */ + 333, /* 16 */ + 667, /* 32 */ + 1000, /* 48 */ + 1333, /* 64 */ + 2000, /* 96 */ + 2667, /* 128 */ + 3333, /* 160 */ + 4000, /* 192 */ + 4667, /* 224 */ + 5333, /* 256 */ + 6000, /* 288 */ + 0 /* No delay, switch matrix always on */ +}; + +/* + * Delay after issuing a POLL command. + * + * The delay is 3 AC97 link frames + the touchpanel settling delay + */ +static inline void poll_delay(int d) +{ + udelay(3 * AC97_LINK_FRAME + delay_table[d]); +} + +/* + * set up the physical settings of the WM9712 + */ +static void wm9712_phy_init(struct wm97xx *wm) +{ + u16 dig1 = 0; + u16 dig2 = WM97XX_RPR | WM9712_RPU(1); + + /* WM9712 rpu */ + if (rpu) { + dig2 &= 0xffc0; + dig2 |= WM9712_RPU(rpu); + dev_dbg(wm->dev, "setting pen detect pull-up to %d Ohms", + 64000 / rpu); + } + + /* touchpanel pressure current*/ + if (pil == 2) { + dig2 |= WM9712_PIL; + dev_dbg(wm->dev, + "setting pressure measurement current to 400uA."); + } else if (pil) + dev_dbg(wm->dev, + "setting pressure measurement current to 200uA."); + if (!pil) + pressure = 0; + + /* WM9712 five wire */ + if (five_wire) { + dig2 |= WM9712_45W; + dev_dbg(wm->dev, "setting 5-wire touchscreen mode."); + } + + /* polling mode sample settling delay */ + if (delay < 0 || delay > 15) { + dev_dbg(wm->dev, "supplied delay out of range."); + delay = 4; + } + dig1 &= 0xff0f; + dig1 |= WM97XX_DELAY(delay); + dev_dbg(wm->dev, "setting adc sample delay to %d u Secs.", + delay_table[delay]); + + /* mask */ + dig2 |= ((mask & 0x3) << 6); + if (mask) { + u16 reg; + /* Set GPIO4 as Mask Pin*/ + reg = wm97xx_reg_read(wm, AC97_MISC_AFE); + wm97xx_reg_write(wm, AC97_MISC_AFE, reg | WM97XX_GPIO_4); + reg = wm97xx_reg_read(wm, AC97_GPIO_CFG); + wm97xx_reg_write(wm, AC97_GPIO_CFG, reg | WM97XX_GPIO_4); + } + + /* wait - coord mode */ + if (coord) + dig2 |= WM9712_WAIT; + + wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, dig1); + wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2); +} + +static void wm9712_dig_enable(struct wm97xx *wm, int enable) +{ + u16 dig2 = wm->dig[2]; + + if (enable) { + wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, + dig2 | WM97XX_PRP_DET_DIG); + wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); /* dummy read */ + } else + wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, + dig2 & ~WM97XX_PRP_DET_DIG); +} + +static void wm9712_aux_prepare(struct wm97xx *wm) +{ + memcpy(wm->dig_save, wm->dig, sizeof(wm->dig)); + wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, 0); + wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, WM97XX_PRP_DET_DIG); +} + +static void wm9712_dig_restore(struct wm97xx *wm) +{ + wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, wm->dig_save[1]); + wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, wm->dig_save[2]); +} + +static inline int is_pden(struct wm97xx *wm) +{ + return wm->dig[2] & WM9712_PDEN; +} + +/* + * Read a sample from the WM9712 adc in polling mode. + */ +static int wm9712_poll_sample(struct wm97xx *wm, int adcsel, int *sample) +{ + int timeout = 5 * delay; + + if (!wm->pen_probably_down) { + u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); + if (!(data & WM97XX_PEN_DOWN)) + return RC_PENUP; + wm->pen_probably_down = 1; + } + + /* set up digitiser */ + if (adcsel & 0x8000) + adcsel = ((adcsel & 0x7fff) + 3) << 12; + + if (wm->mach_ops && wm->mach_ops->pre_sample) + wm->mach_ops->pre_sample(adcsel); + wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, + adcsel | WM97XX_POLL | WM97XX_DELAY(delay)); + + /* wait 3 AC97 time slots + delay for conversion */ + poll_delay(delay); + + /* wait for POLL to go low */ + while ((wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER1) & WM97XX_POLL) + && timeout) { + udelay(AC97_LINK_FRAME); + timeout--; + } + + if (timeout <= 0) { + /* If PDEN is set, we can get a timeout when pen goes up */ + if (is_pden(wm)) + wm->pen_probably_down = 0; + else + dev_dbg(wm->dev, "adc sample timeout"); + return RC_PENUP; + } + + *sample = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); + if (wm->mach_ops && wm->mach_ops->post_sample) + wm->mach_ops->post_sample(adcsel); + + /* check we have correct sample */ + if ((*sample & WM97XX_ADCSEL_MASK) != adcsel) { + dev_dbg(wm->dev, "adc wrong sample, read %x got %x", adcsel, + *sample & WM97XX_ADCSEL_MASK); + return RC_PENUP; + } + + if (!(*sample & WM97XX_PEN_DOWN)) { + wm->pen_probably_down = 0; + return RC_PENUP; + } + + return RC_VALID; +} + +/* + * Read a coord from the WM9712 adc in polling mode. + */ +static int wm9712_poll_coord(struct wm97xx *wm, struct wm97xx_data *data) +{ + int timeout = 5 * delay; + + if (!wm->pen_probably_down) { + u16 data_rd = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); + if (!(data_rd & WM97XX_PEN_DOWN)) + return RC_PENUP; + wm->pen_probably_down = 1; + } + + /* set up digitiser */ + if (wm->mach_ops && wm->mach_ops->pre_sample) + wm->mach_ops->pre_sample(WM97XX_ADCSEL_X | WM97XX_ADCSEL_Y); + + wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, + WM97XX_COO | WM97XX_POLL | WM97XX_DELAY(delay)); + + /* wait 3 AC97 time slots + delay for conversion and read x */ + poll_delay(delay); + data->x = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); + /* wait for POLL to go low */ + while ((wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER1) & WM97XX_POLL) + && timeout) { + udelay(AC97_LINK_FRAME); + timeout--; + } + + if (timeout <= 0) { + /* If PDEN is set, we can get a timeout when pen goes up */ + if (is_pden(wm)) + wm->pen_probably_down = 0; + else + dev_dbg(wm->dev, "adc sample timeout"); + return RC_PENUP; + } + + /* read back y data */ + data->y = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); + if (pil) + data->p = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); + else + data->p = DEFAULT_PRESSURE; + + if (wm->mach_ops && wm->mach_ops->post_sample) + wm->mach_ops->post_sample(WM97XX_ADCSEL_X | WM97XX_ADCSEL_Y); + + /* check we have correct sample */ + if (!(data->x & WM97XX_ADCSEL_X) || !(data->y & WM97XX_ADCSEL_Y)) + goto err; + if (pil && !(data->p & WM97XX_ADCSEL_PRES)) + goto err; + + if (!(data->x & WM97XX_PEN_DOWN) || !(data->y & WM97XX_PEN_DOWN)) { + wm->pen_probably_down = 0; + return RC_PENUP; + } + return RC_VALID; +err: + return 0; +} + +/* + * Sample the WM9712 touchscreen in polling mode + */ +static int wm9712_poll_touch(struct wm97xx *wm, struct wm97xx_data *data) +{ + int rc; + + if (coord) { + rc = wm9712_poll_coord(wm, data); + if (rc != RC_VALID) + return rc; + } else { + rc = wm9712_poll_sample(wm, WM97XX_ADCSEL_X, &data->x); + if (rc != RC_VALID) + return rc; + + rc = wm9712_poll_sample(wm, WM97XX_ADCSEL_Y, &data->y); + if (rc != RC_VALID) + return rc; + + if (pil && !five_wire) { + rc = wm9712_poll_sample(wm, WM97XX_ADCSEL_PRES, + &data->p); + if (rc != RC_VALID) + return rc; + } else + data->p = DEFAULT_PRESSURE; + } + return RC_VALID; +} + +/* + * Enable WM9712 continuous mode, i.e. touch data is streamed across + * an AC97 slot + */ +static int wm9712_acc_enable(struct wm97xx *wm, int enable) +{ + u16 dig1, dig2; + int ret = 0; + + dig1 = wm->dig[1]; + dig2 = wm->dig[2]; + + if (enable) { + /* continous mode */ + if (wm->mach_ops->acc_startup) { + ret = wm->mach_ops->acc_startup(wm); + if (ret < 0) + return ret; + } + dig1 &= ~(WM97XX_CM_RATE_MASK | WM97XX_ADCSEL_MASK | + WM97XX_DELAY_MASK | WM97XX_SLT_MASK); + dig1 |= WM97XX_CTC | WM97XX_COO | WM97XX_SLEN | + WM97XX_DELAY(delay) | + WM97XX_SLT(wm->acc_slot) | + WM97XX_RATE(wm->acc_rate); + if (pil) + dig1 |= WM97XX_ADCSEL_PRES; + dig2 |= WM9712_PDEN; + } else { + dig1 &= ~(WM97XX_CTC | WM97XX_COO | WM97XX_SLEN); + dig2 &= ~WM9712_PDEN; + if (wm->mach_ops->acc_shutdown) + wm->mach_ops->acc_shutdown(wm); + } + + wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, dig1); + wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2); + + return 0; +} + +struct wm97xx_codec_drv wm9712_codec = { + .id = WM9712_ID2, + .name = "wm9712", + .poll_sample = wm9712_poll_sample, + .poll_touch = wm9712_poll_touch, + .acc_enable = wm9712_acc_enable, + .phy_init = wm9712_phy_init, + .dig_enable = wm9712_dig_enable, + .dig_restore = wm9712_dig_restore, + .aux_prepare = wm9712_aux_prepare, +}; +EXPORT_SYMBOL_GPL(wm9712_codec); + +/* Module information */ +MODULE_AUTHOR("Liam Girdwood "); +MODULE_DESCRIPTION("WM9712 Touch Screen Driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3-70-g09d2 From dca98e91fb83a43fc430893f349fd8248fa0ba38 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 2 Apr 2008 00:51:30 -0400 Subject: Input: WM97xx - add chip driver for WM97123 touchscreen Signed-off-by: Liam Girdwood Signed-off-by: Graeme Gregory Signed-off-by: Mike Arthur Signed-off-by: Mark Brown Signed-off-by: Lars Munch Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/Kconfig | 9 + drivers/input/touchscreen/Makefile | 1 + drivers/input/touchscreen/wm9713.c | 460 +++++++++++++++++++++++++++++++++++++ 3 files changed, 470 insertions(+) create mode 100644 drivers/input/touchscreen/wm9713.c diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 240e441c118a..f6e8dad9726b 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -217,6 +217,15 @@ config TOUCHSCREEN_WM9712 If unsure, say N. +config TOUCHSCREEN_WM9713 + bool "WM9713 Touchscreen interface support" + depends on TOUCHSCREEN_WM97XX + help + Say Y here if you have a Wolfson Microelectronics WM9713 touchscreen + controller connected to your system. + + If unsure, say N. + config TOUCHSCREEN_USB_COMPOSITE tristate "USB Touchscreen Driver" depends on USB_ARCH_HAS_HCD diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index eb57f66e57f3..f1bc82941cc6 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -24,3 +24,4 @@ obj-$(CONFIG_TOUCHSCREEN_UCB1400) += ucb1400_ts.o obj-$(CONFIG_TOUCHSCREEN_WM97XX) += wm97xx-ts.o wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9705) += wm9705.o wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9712) += wm9712.o +wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9713) += wm9713.o diff --git a/drivers/input/touchscreen/wm9713.c b/drivers/input/touchscreen/wm9713.c new file mode 100644 index 000000000000..01278bd7e65c --- /dev/null +++ b/drivers/input/touchscreen/wm9713.c @@ -0,0 +1,460 @@ +/* + * wm9713.c -- Codec touch driver for Wolfson WM9713 AC97 Codec. + * + * Copyright 2003, 2004, 2005, 2006, 2007, 2008 Wolfson Microelectronics PLC. + * Author: Liam Girdwood + * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com + * Parts Copyright : Ian Molton + * Andrew Zabolotny + * Russell King + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define TS_NAME "wm97xx" +#define WM9713_VERSION "1.00" +#define DEFAULT_PRESSURE 0xb0c0 + +/* + * Module parameters + */ + +/* + * Set internal pull up for pen detect. + * + * Pull up is in the range 1.02k (least sensitive) to 64k (most sensitive) + * i.e. pull up resistance = 64k Ohms / rpu. + * + * Adjust this value if you are having problems with pen detect not + * detecting any down event. + */ +static int rpu = 8; +module_param(rpu, int, 0); +MODULE_PARM_DESC(rpu, "Set internal pull up resitor for pen detect."); + +/* + * Set current used for pressure measurement. + * + * Set pil = 2 to use 400uA + * pil = 1 to use 200uA and + * pil = 0 to disable pressure measurement. + * + * This is used to increase the range of values returned by the adc + * when measureing touchpanel pressure. + */ +static int pil; +module_param(pil, int, 0); +MODULE_PARM_DESC(pil, "Set current used for pressure measurement."); + +/* + * Set threshold for pressure measurement. + * + * Pen down pressure below threshold is ignored. + */ +static int pressure = DEFAULT_PRESSURE & 0xfff; +module_param(pressure, int, 0); +MODULE_PARM_DESC(pressure, "Set threshold for pressure measurement."); + +/* + * Set adc sample delay. + * + * For accurate touchpanel measurements, some settling time may be + * required between the switch matrix applying a voltage across the + * touchpanel plate and the ADC sampling the signal. + * + * This delay can be set by setting delay = n, where n is the array + * position of the delay in the array delay_table below. + * Long delays > 1ms are supported for completeness, but are not + * recommended. + */ +static int delay = 4; +module_param(delay, int, 0); +MODULE_PARM_DESC(delay, "Set adc sample delay."); + +/* + * Set adc mask function. + * + * Sources of glitch noise, such as signals driving an LCD display, may feed + * through to the touch screen plates and affect measurement accuracy. In + * order to minimise this, a signal may be applied to the MASK pin to delay or + * synchronise the sampling. + * + * 0 = No delay or sync + * 1 = High on pin stops conversions + * 2 = Edge triggered, edge on pin delays conversion by delay param (above) + * 3 = Edge triggered, edge on pin starts conversion after delay param + */ +static int mask; +module_param(mask, int, 0); +MODULE_PARM_DESC(mask, "Set adc mask function."); + +/* + * Coordinate Polling Enable. + * + * Set to 1 to enable coordinate polling. e.g. x,y[,p] is sampled together + * for every poll. + */ +static int coord; +module_param(coord, int, 0); +MODULE_PARM_DESC(coord, "Polling coordinate mode"); + +/* + * ADC sample delay times in uS + */ +static const int delay_table[] = { + 21, /* 1 AC97 Link frames */ + 42, /* 2 */ + 84, /* 4 */ + 167, /* 8 */ + 333, /* 16 */ + 667, /* 32 */ + 1000, /* 48 */ + 1333, /* 64 */ + 2000, /* 96 */ + 2667, /* 128 */ + 3333, /* 160 */ + 4000, /* 192 */ + 4667, /* 224 */ + 5333, /* 256 */ + 6000, /* 288 */ + 0 /* No delay, switch matrix always on */ +}; + +/* + * Delay after issuing a POLL command. + * + * The delay is 3 AC97 link frames + the touchpanel settling delay + */ +static inline void poll_delay(int d) +{ + udelay(3 * AC97_LINK_FRAME + delay_table[d]); +} + +/* + * set up the physical settings of the WM9713 + */ +static void wm9713_phy_init(struct wm97xx *wm) +{ + u16 dig1 = 0, dig2, dig3; + + /* default values */ + dig2 = WM97XX_DELAY(4) | WM97XX_SLT(5); + dig3 = WM9712_RPU(1); + + /* rpu */ + if (rpu) { + dig3 &= 0xffc0; + dig3 |= WM9712_RPU(rpu); + dev_info(wm->dev, "setting pen detect pull-up to %d Ohms\n", + 64000 / rpu); + } + + /* touchpanel pressure */ + if (pil == 2) { + dig3 |= WM9712_PIL; + dev_info(wm->dev, + "setting pressure measurement current to 400uA."); + } else if (pil) + dev_info(wm->dev, + "setting pressure measurement current to 200uA."); + if (!pil) + pressure = 0; + + /* sample settling delay */ + if (delay < 0 || delay > 15) { + dev_info(wm->dev, "supplied delay out of range."); + delay = 4; + dev_info(wm->dev, "setting adc sample delay to %d u Secs.", + delay_table[delay]); + } + dig2 &= 0xff0f; + dig2 |= WM97XX_DELAY(delay); + + /* mask */ + dig3 |= ((mask & 0x3) << 4); + if (coord) + dig3 |= WM9713_WAIT; + + wm->misc = wm97xx_reg_read(wm, 0x5a); + + wm97xx_reg_write(wm, AC97_WM9713_DIG1, dig1); + wm97xx_reg_write(wm, AC97_WM9713_DIG2, dig2); + wm97xx_reg_write(wm, AC97_WM9713_DIG3, dig3); + wm97xx_reg_write(wm, AC97_GPIO_STICKY, 0x0); +} + +static void wm9713_dig_enable(struct wm97xx *wm, int enable) +{ + u16 val; + + if (enable) { + val = wm97xx_reg_read(wm, AC97_EXTENDED_MID); + wm97xx_reg_write(wm, AC97_EXTENDED_MID, val & 0x7fff); + wm97xx_reg_write(wm, AC97_WM9713_DIG3, wm->dig[2] | + WM97XX_PRP_DET_DIG); + wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); /* dummy read */ + } else { + wm97xx_reg_write(wm, AC97_WM9713_DIG3, wm->dig[2] & + ~WM97XX_PRP_DET_DIG); + val = wm97xx_reg_read(wm, AC97_EXTENDED_MID); + wm97xx_reg_write(wm, AC97_EXTENDED_MID, val | 0x8000); + } +} + +static void wm9713_dig_restore(struct wm97xx *wm) +{ + wm97xx_reg_write(wm, AC97_WM9713_DIG1, wm->dig_save[0]); + wm97xx_reg_write(wm, AC97_WM9713_DIG2, wm->dig_save[1]); + wm97xx_reg_write(wm, AC97_WM9713_DIG3, wm->dig_save[2]); +} + +static void wm9713_aux_prepare(struct wm97xx *wm) +{ + memcpy(wm->dig_save, wm->dig, sizeof(wm->dig)); + wm97xx_reg_write(wm, AC97_WM9713_DIG1, 0); + wm97xx_reg_write(wm, AC97_WM9713_DIG2, 0); + wm97xx_reg_write(wm, AC97_WM9713_DIG3, WM97XX_PRP_DET_DIG); +} + +static inline int is_pden(struct wm97xx *wm) +{ + return wm->dig[2] & WM9713_PDEN; +} + +/* + * Read a sample from the WM9713 adc in polling mode. + */ +static int wm9713_poll_sample(struct wm97xx *wm, int adcsel, int *sample) +{ + u16 dig1; + int timeout = 5 * delay; + + if (!wm->pen_probably_down) { + u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); + if (!(data & WM97XX_PEN_DOWN)) + return RC_PENUP; + wm->pen_probably_down = 1; + } + + /* set up digitiser */ + if (adcsel & 0x8000) + adcsel = 1 << ((adcsel & 0x7fff) + 3); + + dig1 = wm97xx_reg_read(wm, AC97_WM9713_DIG1); + dig1 &= ~WM9713_ADCSEL_MASK; + + if (wm->mach_ops && wm->mach_ops->pre_sample) + wm->mach_ops->pre_sample(adcsel); + wm97xx_reg_write(wm, AC97_WM9713_DIG1, dig1 | adcsel | WM9713_POLL); + + /* wait 3 AC97 time slots + delay for conversion */ + poll_delay(delay); + + /* wait for POLL to go low */ + while ((wm97xx_reg_read(wm, AC97_WM9713_DIG1) & WM9713_POLL) && + timeout) { + udelay(AC97_LINK_FRAME); + timeout--; + } + + if (timeout <= 0) { + /* If PDEN is set, we can get a timeout when pen goes up */ + if (is_pden(wm)) + wm->pen_probably_down = 0; + else + dev_dbg(wm->dev, "adc sample timeout"); + return RC_PENUP; + } + + *sample = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); + if (wm->mach_ops && wm->mach_ops->post_sample) + wm->mach_ops->post_sample(adcsel); + + /* check we have correct sample */ + if ((*sample & WM97XX_ADCSRC_MASK) != ffs(adcsel >> 1) << 12) { + dev_dbg(wm->dev, "adc wrong sample, read %x got %x", adcsel, + *sample & WM97XX_ADCSRC_MASK); + return RC_PENUP; + } + + if (!(*sample & WM97XX_PEN_DOWN)) { + wm->pen_probably_down = 0; + return RC_PENUP; + } + + return RC_VALID; +} + +/* + * Read a coordinate from the WM9713 adc in polling mode. + */ +static int wm9713_poll_coord(struct wm97xx *wm, struct wm97xx_data *data) +{ + u16 dig1; + int timeout = 5 * delay; + + if (!wm->pen_probably_down) { + u16 val = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); + if (!(val & WM97XX_PEN_DOWN)) + return RC_PENUP; + wm->pen_probably_down = 1; + } + + /* set up digitiser */ + dig1 = wm97xx_reg_read(wm, AC97_WM9713_DIG1); + dig1 &= ~WM9713_ADCSEL_MASK; + if (pil) + dig1 |= WM9713_ADCSEL_PRES; + + if (wm->mach_ops && wm->mach_ops->pre_sample) + wm->mach_ops->pre_sample(WM97XX_ADCSEL_X | WM97XX_ADCSEL_Y); + wm97xx_reg_write(wm, AC97_WM9713_DIG1, + dig1 | WM9713_POLL | WM9713_COO); + + /* wait 3 AC97 time slots + delay for conversion */ + poll_delay(delay); + data->x = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); + /* wait for POLL to go low */ + while ((wm97xx_reg_read(wm, AC97_WM9713_DIG1) & WM9713_POLL) + && timeout) { + udelay(AC97_LINK_FRAME); + timeout--; + } + + if (timeout <= 0) { + /* If PDEN is set, we can get a timeout when pen goes up */ + if (is_pden(wm)) + wm->pen_probably_down = 0; + else + dev_dbg(wm->dev, "adc sample timeout"); + return RC_PENUP; + } + + /* read back data */ + data->y = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); + if (pil) + data->p = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); + else + data->p = DEFAULT_PRESSURE; + + if (wm->mach_ops && wm->mach_ops->post_sample) + wm->mach_ops->post_sample(WM97XX_ADCSEL_X | WM97XX_ADCSEL_Y); + + /* check we have correct sample */ + if (!(data->x & WM97XX_ADCSEL_X) || !(data->y & WM97XX_ADCSEL_Y)) + goto err; + if (pil && !(data->p & WM97XX_ADCSEL_PRES)) + goto err; + + if (!(data->x & WM97XX_PEN_DOWN) || !(data->y & WM97XX_PEN_DOWN)) { + wm->pen_probably_down = 0; + return RC_PENUP; + } + return RC_VALID; +err: + return 0; +} + +/* + * Sample the WM9713 touchscreen in polling mode + */ +static int wm9713_poll_touch(struct wm97xx *wm, struct wm97xx_data *data) +{ + int rc; + + if (coord) { + rc = wm9713_poll_coord(wm, data); + if (rc != RC_VALID) + return rc; + } else { + rc = wm9713_poll_sample(wm, WM9713_ADCSEL_X, &data->x); + if (rc != RC_VALID) + return rc; + rc = wm9713_poll_sample(wm, WM9713_ADCSEL_Y, &data->y); + if (rc != RC_VALID) + return rc; + if (pil) { + rc = wm9713_poll_sample(wm, WM9713_ADCSEL_PRES, + &data->p); + if (rc != RC_VALID) + return rc; + } else + data->p = DEFAULT_PRESSURE; + } + return RC_VALID; +} + +/* + * Enable WM9713 continuous mode, i.e. touch data is streamed across + * an AC97 slot + */ +static int wm9713_acc_enable(struct wm97xx *wm, int enable) +{ + u16 dig1, dig2, dig3; + int ret = 0; + + dig1 = wm->dig[0]; + dig2 = wm->dig[1]; + dig3 = wm->dig[2]; + + if (enable) { + /* continous mode */ + if (wm->mach_ops->acc_startup && + (ret = wm->mach_ops->acc_startup(wm)) < 0) + return ret; + + dig1 &= ~WM9713_ADCSEL_MASK; + dig1 |= WM9713_CTC | WM9713_COO | WM9713_ADCSEL_X | + WM9713_ADCSEL_Y; + if (pil) + dig1 |= WM9713_ADCSEL_PRES; + dig2 &= ~(WM97XX_DELAY_MASK | WM97XX_SLT_MASK | + WM97XX_CM_RATE_MASK); + dig2 |= WM97XX_SLEN | WM97XX_DELAY(delay) | + WM97XX_SLT(wm->acc_slot) | WM97XX_RATE(wm->acc_rate); + dig3 |= WM9713_PDEN; + } else { + dig1 &= ~(WM9713_CTC | WM9713_COO); + dig2 &= ~WM97XX_SLEN; + dig3 &= ~WM9713_PDEN; + if (wm->mach_ops->acc_shutdown) + wm->mach_ops->acc_shutdown(wm); + } + + wm97xx_reg_write(wm, AC97_WM9713_DIG1, dig1); + wm97xx_reg_write(wm, AC97_WM9713_DIG2, dig2); + wm97xx_reg_write(wm, AC97_WM9713_DIG3, dig3); + + return ret; +} + +struct wm97xx_codec_drv wm9713_codec = { + .id = WM9713_ID2, + .name = "wm9713", + .poll_sample = wm9713_poll_sample, + .poll_touch = wm9713_poll_touch, + .acc_enable = wm9713_acc_enable, + .phy_init = wm9713_phy_init, + .dig_enable = wm9713_dig_enable, + .dig_restore = wm9713_dig_restore, + .aux_prepare = wm9713_aux_prepare, +}; +EXPORT_SYMBOL_GPL(wm9713_codec); + +/* Module information */ +MODULE_AUTHOR("Liam Girdwood "); +MODULE_DESCRIPTION("WM9713 Touch Screen Driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3-70-g09d2 From 4db8a5f21e5149e09949516eef98b78b68880075 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 2 Apr 2008 00:51:46 -0400 Subject: Input: WM97xx - add support for streaming mode on Mainstone Signed-off-by: Liam Girdwood Signed-off-by: Graeme Gregory Signed-off-by: Mike Arthur Signed-off-by: Mark Brown Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/Kconfig | 12 ++ drivers/input/touchscreen/Makefile | 1 + drivers/input/touchscreen/mainstone-wm97xx.c | 302 +++++++++++++++++++++++++++ 3 files changed, 315 insertions(+) create mode 100644 drivers/input/touchscreen/mainstone-wm97xx.c diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index f6e8dad9726b..565ec711c2ee 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -226,6 +226,18 @@ config TOUCHSCREEN_WM9713 If unsure, say N. +config TOUCHSCREEN_WM97XX_MAINSTONE + tristate "WM97xx Mainstone accelerated touch" + depends on TOUCHSCREEN_WM97XX && ARCH_PXA + help + Say Y here for support for streaming mode with WM97xx touchscreens + on Mainstone systems. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called mainstone-wm97xx. + config TOUCHSCREEN_USB_COMPOSITE tristate "USB Touchscreen Driver" depends on USB_ARCH_HAS_HCD diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index f1bc82941cc6..3c096d75651d 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -25,3 +25,4 @@ obj-$(CONFIG_TOUCHSCREEN_WM97XX) += wm97xx-ts.o wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9705) += wm9705.o wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9712) += wm9712.o wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9713) += wm9713.o +obj-$(CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE) += mainstone-wm97xx.o diff --git a/drivers/input/touchscreen/mainstone-wm97xx.c b/drivers/input/touchscreen/mainstone-wm97xx.c new file mode 100644 index 000000000000..a79f029b91c0 --- /dev/null +++ b/drivers/input/touchscreen/mainstone-wm97xx.c @@ -0,0 +1,302 @@ +/* + * mainstone-wm97xx.c -- Mainstone Continuous Touch screen driver for + * Wolfson WM97xx AC97 Codecs. + * + * Copyright 2004, 2007 Wolfson Microelectronics PLC. + * Author: Liam Girdwood + * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com + * Parts Copyright : Ian Molton + * Andrew Zabolotny + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * Notes: + * This is a wm97xx extended touch driver to capture touch + * data in a continuous manner on the Intel XScale archictecture + * + * Features: + * - codecs supported:- WM9705, WM9712, WM9713 + * - processors supported:- Intel XScale PXA25x, PXA26x, PXA27x + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define VERSION "0.13" + +struct continuous { + u16 id; /* codec id */ + u8 code; /* continuous code */ + u8 reads; /* number of coord reads per read cycle */ + u32 speed; /* number of coords per second */ +}; + +#define WM_READS(sp) ((sp / HZ) + 1) + +static const struct continuous cinfo[] = { + {WM9705_ID2, 0, WM_READS(94), 94}, + {WM9705_ID2, 1, WM_READS(188), 188}, + {WM9705_ID2, 2, WM_READS(375), 375}, + {WM9705_ID2, 3, WM_READS(750), 750}, + {WM9712_ID2, 0, WM_READS(94), 94}, + {WM9712_ID2, 1, WM_READS(188), 188}, + {WM9712_ID2, 2, WM_READS(375), 375}, + {WM9712_ID2, 3, WM_READS(750), 750}, + {WM9713_ID2, 0, WM_READS(94), 94}, + {WM9713_ID2, 1, WM_READS(120), 120}, + {WM9713_ID2, 2, WM_READS(154), 154}, + {WM9713_ID2, 3, WM_READS(188), 188}, +}; + +/* continuous speed index */ +static int sp_idx; +static u16 last, tries; + +/* + * Pen sampling frequency (Hz) in continuous mode. + */ +static int cont_rate = 200; +module_param(cont_rate, int, 0); +MODULE_PARM_DESC(cont_rate, "Sampling rate in continuous mode (Hz)"); + +/* + * Pen down detection. + * + * This driver can either poll or use an interrupt to indicate a pen down + * event. If the irq request fails then it will fall back to polling mode. + */ +static int pen_int; +module_param(pen_int, int, 0); +MODULE_PARM_DESC(pen_int, "Pen down detection (1 = interrupt, 0 = polling)"); + +/* + * Pressure readback. + * + * Set to 1 to read back pen down pressure + */ +static int pressure; +module_param(pressure, int, 0); +MODULE_PARM_DESC(pressure, "Pressure readback (1 = pressure, 0 = no pressure)"); + +/* + * AC97 touch data slot. + * + * Touch screen readback data ac97 slot + */ +static int ac97_touch_slot = 5; +module_param(ac97_touch_slot, int, 0); +MODULE_PARM_DESC(ac97_touch_slot, "Touch screen data slot AC97 number"); + + +/* flush AC97 slot 5 FIFO on pxa machines */ +#ifdef CONFIG_PXA27x +static void wm97xx_acc_pen_up(struct wm97xx *wm) +{ + schedule_timeout_uninterruptible(1); + + while (MISR & (1 << 2)) + MODR; +} +#else +static void wm97xx_acc_pen_up(struct wm97xx *wm) +{ + int count = 16; + schedule_timeout_uninterruptible(1); + + while (count < 16) { + MODR; + count--; + } +} +#endif + +static int wm97xx_acc_pen_down(struct wm97xx *wm) +{ + u16 x, y, p = 0x100 | WM97XX_ADCSEL_PRES; + int reads = 0; + + /* When the AC97 queue has been drained we need to allow time + * to buffer up samples otherwise we end up spinning polling + * for samples. The controller can't have a suitably low + * threashold set to use the notifications it gives. + */ + schedule_timeout_uninterruptible(1); + + if (tries > 5) { + tries = 0; + return RC_PENUP; + } + + x = MODR; + if (x == last) { + tries++; + return RC_AGAIN; + } + last = x; + do { + if (reads) + x = MODR; + y = MODR; + if (pressure) + p = MODR; + + /* are samples valid */ + if ((x & WM97XX_ADCSRC_MASK) != WM97XX_ADCSEL_X || + (y & WM97XX_ADCSRC_MASK) != WM97XX_ADCSEL_Y || + (p & WM97XX_ADCSRC_MASK) != WM97XX_ADCSEL_PRES) + goto up; + + /* coordinate is good */ + tries = 0; + input_report_abs(wm->input_dev, ABS_X, x & 0xfff); + input_report_abs(wm->input_dev, ABS_Y, y & 0xfff); + input_report_abs(wm->input_dev, ABS_PRESSURE, p & 0xfff); + input_sync(wm->input_dev); + reads++; + } while (reads < cinfo[sp_idx].reads); +up: + return RC_PENDOWN | RC_AGAIN; +} + +static int wm97xx_acc_startup(struct wm97xx *wm) +{ + int idx = 0; + + /* check we have a codec */ + if (wm->ac97 == NULL) + return -ENODEV; + + /* Go you big red fire engine */ + for (idx = 0; idx < ARRAY_SIZE(cinfo); idx++) { + if (wm->id != cinfo[idx].id) + continue; + sp_idx = idx; + if (cont_rate <= cinfo[idx].speed) + break; + } + wm->acc_rate = cinfo[sp_idx].code; + wm->acc_slot = ac97_touch_slot; + dev_info(wm->dev, + "mainstone accelerated touchscreen driver, %d samples/sec\n", + cinfo[sp_idx].speed); + + /* codec specific irq config */ + if (pen_int) { + switch (wm->id) { + case WM9705_ID2: + wm->pen_irq = IRQ_GPIO(4); + set_irq_type(IRQ_GPIO(4), IRQT_BOTHEDGE); + break; + case WM9712_ID2: + case WM9713_ID2: + /* enable pen down interrupt */ + /* use PEN_DOWN GPIO 13 to assert IRQ on GPIO line 2 */ + wm->pen_irq = MAINSTONE_AC97_IRQ; + wm97xx_config_gpio(wm, WM97XX_GPIO_13, WM97XX_GPIO_IN, + WM97XX_GPIO_POL_HIGH, + WM97XX_GPIO_STICKY, + WM97XX_GPIO_WAKE); + wm97xx_config_gpio(wm, WM97XX_GPIO_2, WM97XX_GPIO_OUT, + WM97XX_GPIO_POL_HIGH, + WM97XX_GPIO_NOTSTICKY, + WM97XX_GPIO_NOWAKE); + break; + default: + dev_err(wm->dev, + "pen down irq not supported on this device\n"); + pen_int = 0; + break; + } + } + + return 0; +} + +static void wm97xx_acc_shutdown(struct wm97xx *wm) +{ + /* codec specific deconfig */ + if (pen_int) { + switch (wm->id & 0xffff) { + case WM9705_ID2: + wm->pen_irq = 0; + break; + case WM9712_ID2: + case WM9713_ID2: + /* disable interrupt */ + wm->pen_irq = 0; + break; + } + } +} + +static void wm97xx_irq_enable(struct wm97xx *wm, int enable) +{ + if (enable) + enable_irq(wm->pen_irq); + else + disable_irq(wm->pen_irq); +} + +static struct wm97xx_mach_ops mainstone_mach_ops = { + .acc_enabled = 1, + .acc_pen_up = wm97xx_acc_pen_up, + .acc_pen_down = wm97xx_acc_pen_down, + .acc_startup = wm97xx_acc_startup, + .acc_shutdown = wm97xx_acc_shutdown, + .irq_enable = wm97xx_irq_enable, + .irq_gpio = WM97XX_GPIO_2, +}; + +static int mainstone_wm97xx_probe(struct platform_device *pdev) +{ + struct wm97xx *wm = platform_get_drvdata(pdev); + + return wm97xx_register_mach_ops(wm, &mainstone_mach_ops); +} + +static int mainstone_wm97xx_remove(struct platform_device *pdev) +{ + struct wm97xx *wm = platform_get_drvdata(pdev); + + wm97xx_unregister_mach_ops(wm); + return 0; +} + +static struct platform_driver mainstone_wm97xx_driver = { + .probe = mainstone_wm97xx_probe, + .remove = mainstone_wm97xx_remove, + .driver = { + .name = "wm97xx-touch", + }, +}; + +static int __init mainstone_wm97xx_init(void) +{ + return platform_driver_register(&mainstone_wm97xx_driver); +} + +static void __exit mainstone_wm97xx_exit(void) +{ + platform_driver_unregister(&mainstone_wm97xx_driver); +} + +module_init(mainstone_wm97xx_init); +module_exit(mainstone_wm97xx_exit); + +/* Module information */ +MODULE_AUTHOR("Liam Girdwood "); +MODULE_DESCRIPTION("wm97xx continuous touch driver for mainstone"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3-70-g09d2 From f23c1d7579211c801494c7a8d6fca12905f7949f Mon Sep 17 00:00:00 2001 From: Tobias Mueller Date: Wed, 2 Apr 2008 10:02:06 -0400 Subject: Input: appletouch - add product IDs for the 4th generation MacBooks Signed-off-by: Tobias Mueller Acked-by: Johannes Berg Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/appletouch.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/input/mouse/appletouch.c b/drivers/input/mouse/appletouch.c index b4423a471f02..8dd3942f3022 100644 --- a/drivers/input/mouse/appletouch.c +++ b/drivers/input/mouse/appletouch.c @@ -62,6 +62,10 @@ #define GEYSER4_ISO_PRODUCT_ID 0x021B #define GEYSER4_JIS_PRODUCT_ID 0x021C +#define GEYSER4_HF_ANSI_PRODUCT_ID 0x0229 +#define GEYSER4_HF_ISO_PRODUCT_ID 0x022A +#define GEYSER4_HF_JIS_PRODUCT_ID 0x022B + #define ATP_DEVICE(prod) \ .match_flags = USB_DEVICE_ID_MATCH_DEVICE | \ USB_DEVICE_ID_MATCH_INT_CLASS | \ @@ -93,6 +97,10 @@ static struct usb_device_id atp_table [] = { { ATP_DEVICE(GEYSER4_ISO_PRODUCT_ID) }, { ATP_DEVICE(GEYSER4_JIS_PRODUCT_ID) }, + { ATP_DEVICE(GEYSER4_HF_ANSI_PRODUCT_ID) }, + { ATP_DEVICE(GEYSER4_HF_ISO_PRODUCT_ID) }, + { ATP_DEVICE(GEYSER4_HF_JIS_PRODUCT_ID) }, + /* Terminating entry */ { } }; @@ -217,7 +225,10 @@ static inline int atp_is_geyser_3(struct atp *dev) (productId == GEYSER3_JIS_PRODUCT_ID) || (productId == GEYSER4_ANSI_PRODUCT_ID) || (productId == GEYSER4_ISO_PRODUCT_ID) || - (productId == GEYSER4_JIS_PRODUCT_ID); + (productId == GEYSER4_JIS_PRODUCT_ID) || + (productId == GEYSER4_HF_ANSI_PRODUCT_ID) || + (productId == GEYSER4_HF_ISO_PRODUCT_ID) || + (productId == GEYSER4_HF_JIS_PRODUCT_ID); } /* -- cgit v1.2.3-70-g09d2 From 5550fbaeb3cc88fe2982e9b5351073173d733f30 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Wed, 2 Apr 2008 11:22:51 -0400 Subject: Input: tosakbd - fix suspend Signed-off-by: Dmitry Baryshkov Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/tosakbd.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/drivers/input/keyboard/tosakbd.c b/drivers/input/keyboard/tosakbd.c index 3884d1e3f070..a247006757de 100644 --- a/drivers/input/keyboard/tosakbd.c +++ b/drivers/input/keyboard/tosakbd.c @@ -52,7 +52,7 @@ KEY_X, KEY_F, KEY_SPACE, KEY_APOSTROPHE, TOSA_KEY_MAIL, KEY_LEFT, KEY_DOWN, KEY_ struct tosakbd { unsigned int keycode[ARRAY_SIZE(tosakbd_keycode)]; struct input_dev *input; - + int suspended; spinlock_t lock; /* protect kbd scanning */ struct timer_list timer; }; @@ -133,6 +133,9 @@ static void tosakbd_scankeyboard(struct platform_device *dev) spin_lock_irqsave(&tosakbd->lock, flags); + if (tosakbd->suspended) + goto out; + for (col = 0; col < TOSA_KEY_STROBE_NUM; col++) { /* * Discharge the output driver capacitatance @@ -174,6 +177,7 @@ static void tosakbd_scankeyboard(struct platform_device *dev) if (num_pressed) mod_timer(&tosakbd->timer, jiffies + SCAN_INTERVAL); + out: spin_unlock_irqrestore(&tosakbd->lock, flags); } @@ -200,6 +204,7 @@ static irqreturn_t tosakbd_interrupt(int irq, void *__dev) static void tosakbd_timer_callback(unsigned long __dev) { struct platform_device *dev = (struct platform_device *)__dev; + tosakbd_scankeyboard(dev); } @@ -207,6 +212,13 @@ static void tosakbd_timer_callback(unsigned long __dev) static int tosakbd_suspend(struct platform_device *dev, pm_message_t state) { struct tosakbd *tosakbd = platform_get_drvdata(dev); + unsigned long flags; + + spin_lock_irqsave(&tosakbd->lock, flags); + PGSR1 = (PGSR1 & ~TOSA_GPIO_LOW_STROBE_BIT); + PGSR2 = (PGSR2 & ~TOSA_GPIO_HIGH_STROBE_BIT); + tosakbd->suspended = 1; + spin_unlock_irqrestore(&tosakbd->lock, flags); del_timer_sync(&tosakbd->timer); @@ -215,6 +227,9 @@ static int tosakbd_suspend(struct platform_device *dev, pm_message_t state) static int tosakbd_resume(struct platform_device *dev) { + struct tosakbd *tosakbd = platform_get_drvdata(dev); + + tosakbd->suspended = 0; tosakbd_scankeyboard(dev); return 0; @@ -365,8 +380,8 @@ fail: return error; } -static int __devexit tosakbd_remove(struct platform_device *dev) { - +static int __devexit tosakbd_remove(struct platform_device *dev) +{ int i; struct tosakbd *tosakbd = platform_get_drvdata(dev); -- cgit v1.2.3-70-g09d2 From 8a0f83eacc1bb8899094b17483de95ddf2d8fcc6 Mon Sep 17 00:00:00 2001 From: Anssi Hannula Date: Thu, 3 Apr 2008 16:17:52 -0400 Subject: Input: xpad - match xbox 360 devices with interface info Match Xbox 360 controllers using the interface info, i.e. interface class 255 (Vendor specific), subclass 93 and protocol 1, instead of specifying the device ids individually. As the class is vendor-specific, we have to still match against vendor id as well, though. Signed-off-by: Anssi Hannula Signed-off-by: Dmitry Torokhov --- drivers/input/joystick/xpad.c | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c index 0380597249bb..8804ad30dae2 100644 --- a/drivers/input/joystick/xpad.c +++ b/drivers/input/joystick/xpad.c @@ -91,6 +91,7 @@ #define XTYPE_XBOX 0 #define XTYPE_XBOX360 1 +#define XTYPE_UNKNOWN 2 static int dpad_to_buttons; module_param(dpad_to_buttons, bool, S_IRUGO); @@ -138,7 +139,7 @@ static const struct xpad_device { { 0x1430, 0x8888, "TX6500+ Dance Pad (first generation)", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX }, { 0x045e, 0x028e, "Microsoft X-Box 360 pad", MAP_DPAD_TO_AXES, XTYPE_XBOX360 }, { 0xffff, 0xffff, "Chinese-made Xbox Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX }, - { 0x0000, 0x0000, "Generic X-Box pad", MAP_DPAD_UNKNOWN, XTYPE_XBOX } + { 0x0000, 0x0000, "Generic X-Box pad", MAP_DPAD_UNKNOWN, XTYPE_UNKNOWN } }; static const signed short xpad_btn[] = { @@ -173,12 +174,20 @@ static const signed short xpad_abs_pad[] = { -1 /* terminating entry */ }; -/* Xbox 360 has a vendor-specific (sub)class, so we cannot match it with only - * USB_INTERFACE_INFO, more to that this device has 4 InterfaceProtocols, - * but we need only one of them. */ +/* Xbox 360 has a vendor-specific class, so we cannot match it with only + * USB_INTERFACE_INFO (also specifically refused by USB subsystem), so we + * match against vendor id as well. Also, some Xbox 360 devices have multiple + * interface protocols, we only need protocol 1. */ +#define XPAD_XBOX360_VENDOR(vend) \ + .match_flags = USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_INT_INFO, \ + .idVendor = (vend), \ + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, \ + .bInterfaceSubClass = 93, \ + .bInterfaceProtocol = 1 + static struct usb_device_id xpad_table [] = { { USB_INTERFACE_INFO('X', 'B', 0) }, /* X-Box USB-IF not approved class */ - { USB_DEVICE_INTERFACE_PROTOCOL(0x045e, 0x028e, 1) }, /* X-Box 360 controller */ + { XPAD_XBOX360_VENDOR(0x045e) }, /* Microsoft X-Box 360 controllers */ { } }; @@ -645,6 +654,8 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id xpad->xtype = xpad_device[i].xtype; if (xpad->dpad_mapping == MAP_DPAD_UNKNOWN) xpad->dpad_mapping = dpad_to_buttons; + if (xpad->xtype == XTYPE_UNKNOWN) + xpad->xtype = (intf->cur_altsetting->desc.bInterfaceClass == USB_CLASS_VENDOR_SPEC); xpad->dev = input_dev; usb_make_path(udev, xpad->phys, sizeof(xpad->phys)); strlcat(xpad->phys, "/input0", sizeof(xpad->phys)); -- cgit v1.2.3-70-g09d2 From a0979923d7c34c9c60d0ee8a533f9502dcfbd42b Mon Sep 17 00:00:00 2001 From: Anssi Hannula Date: Thu, 3 Apr 2008 16:18:10 -0400 Subject: Input: xpad - fix dpad handling of unknown devices For devices not specifically listed in xpad.c, xpad->dpad_mapping is initially set to MAP_DPAD_UNKNOWN. In xpad_probe() it gets changed to either MAP_DPAD_TO_BUTTONS or MAP_DPAD_TO_AXES, depending on the module parameter dpad_to_buttons. However, MAP_DPAD_UNKNOWN is defined as -1, while the field is u8. This results in actual value of 255, causing the MAP_DPAD_UNKNOWN check in xpad_probe() to fail. Fix that by defining MAP_DPAD_UNKNOWN as 2 instead. Also, setting module parameter dpad_to_buttons to 1 should obviously map dpad to buttons, while the default behaviour (0) should be to map dpad to axes. However, dpad_to_buttons is directly assigned to xpad->dpad_mapping, and as MAP_DPAD_TO_BUTTONS is 0, the actual behaviour is reversed. Fix that by negating dpad_to_buttons in assignment. Signed-off-by: Anssi Hannula Signed-off-by: Dmitry Torokhov --- drivers/input/joystick/xpad.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c index 8804ad30dae2..6288c4f6e3a3 100644 --- a/drivers/input/joystick/xpad.c +++ b/drivers/input/joystick/xpad.c @@ -87,7 +87,7 @@ but we map them to axes when possible to simplify things */ #define MAP_DPAD_TO_BUTTONS 0 #define MAP_DPAD_TO_AXES 1 -#define MAP_DPAD_UNKNOWN -1 +#define MAP_DPAD_UNKNOWN 2 #define XTYPE_XBOX 0 #define XTYPE_XBOX360 1 @@ -653,7 +653,7 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id xpad->dpad_mapping = xpad_device[i].dpad_mapping; xpad->xtype = xpad_device[i].xtype; if (xpad->dpad_mapping == MAP_DPAD_UNKNOWN) - xpad->dpad_mapping = dpad_to_buttons; + xpad->dpad_mapping = !dpad_to_buttons; if (xpad->xtype == XTYPE_UNKNOWN) xpad->xtype = (intf->cur_altsetting->desc.bInterfaceClass == USB_CLASS_VENDOR_SPEC); xpad->dev = input_dev; -- cgit v1.2.3-70-g09d2 From 97f09cb53da583cefc1ce2930de8f64b52cfc54b Mon Sep 17 00:00:00 2001 From: Anssi Hannula Date: Thu, 3 Apr 2008 16:18:23 -0400 Subject: Input: xpad - fix inverted Y and RY axes The commit ae91d10aab2762f81733e9194cb56eff99c8d808 inverted Y and RY axes on xbox360 so that up is positive and down is negative. This is wrong, as axes on game controllers have up as negative per convention. Also, even xpad itself reports HAT0X with up as negative. Fix that by inverting them again. Also, according to http://bugzilla.kernel.org/show_bug.cgi?id=10337 the original xbox controllers also have the Y and RY axes inverted. Fix that by inverting them as well. Cc: Brian Magnuson Signed-off-by: Anssi Hannula Signed-off-by: Dmitry Torokhov --- drivers/input/joystick/xpad.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c index 6288c4f6e3a3..316d98755440 100644 --- a/drivers/input/joystick/xpad.c +++ b/drivers/input/joystick/xpad.c @@ -236,13 +236,13 @@ static void xpad_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *d input_report_abs(dev, ABS_X, (__s16) le16_to_cpup((__le16 *)(data + 12))); input_report_abs(dev, ABS_Y, - (__s16) le16_to_cpup((__le16 *)(data + 14))); + ~(__s16) le16_to_cpup((__le16 *)(data + 14))); /* right stick */ input_report_abs(dev, ABS_RX, (__s16) le16_to_cpup((__le16 *)(data + 16))); input_report_abs(dev, ABS_RY, - (__s16) le16_to_cpup((__le16 *)(data + 18))); + ~(__s16) le16_to_cpup((__le16 *)(data + 18))); /* triggers left/right */ input_report_abs(dev, ABS_Z, data[10]); @@ -330,13 +330,13 @@ static void xpad360_process_packet(struct usb_xpad *xpad, input_report_abs(dev, ABS_X, (__s16) le16_to_cpup((__le16 *)(data + 6))); input_report_abs(dev, ABS_Y, - (__s16) le16_to_cpup((__le16 *)(data + 8))); + ~(__s16) le16_to_cpup((__le16 *)(data + 8))); /* right stick */ input_report_abs(dev, ABS_RX, (__s16) le16_to_cpup((__le16 *)(data + 10))); input_report_abs(dev, ABS_RY, - (__s16) le16_to_cpup((__le16 *)(data + 12))); + ~(__s16) le16_to_cpup((__le16 *)(data + 12))); /* triggers left/right */ input_report_abs(dev, ABS_Z, data[4]); -- cgit v1.2.3-70-g09d2 From 8a7ae2a890852b133342a6d72f84b0dac1bc8e8e Mon Sep 17 00:00:00 2001 From: Anssi Hannula Date: Thu, 3 Apr 2008 16:18:35 -0400 Subject: Input: xpad - add more xbox 360 controller ids Add Mad Catz and 0x0e6f xbox360 controllers which are already found in xpad_device[] table in xpad.c into the vendor id list. Also add Logitech into the vendor list for Logitech Chillstream gamepads. Also add the RedOctane Guitar Hero X-plorer. Signed-off-by: Anssi Hannula Signed-off-by: Dmitry Torokhov --- drivers/input/joystick/xpad.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c index 316d98755440..89524954ab46 100644 --- a/drivers/input/joystick/xpad.c +++ b/drivers/input/joystick/xpad.c @@ -110,6 +110,7 @@ static const struct xpad_device { { 0x045e, 0x0287, "Microsoft Xbox Controller S", MAP_DPAD_TO_AXES, XTYPE_XBOX }, { 0x0c12, 0x8809, "RedOctane Xbox Dance Pad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX }, { 0x044f, 0x0f07, "Thrustmaster, Inc. Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX }, + { 0x046d, 0xc242, "Logitech Chillstream Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX360 }, { 0x046d, 0xca84, "Logitech Xbox Cordless Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX }, { 0x046d, 0xca88, "Logitech Compact Controller for Xbox", MAP_DPAD_TO_AXES, XTYPE_XBOX }, { 0x05fd, 0x1007, "Mad Catz Controller (unverified)", MAP_DPAD_TO_AXES, XTYPE_XBOX }, @@ -136,6 +137,7 @@ static const struct xpad_device { { 0x0f30, 0x8888, "BigBen XBMiniPad Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX }, { 0x102c, 0xff0c, "Joytech Wireless Advanced Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX }, { 0x12ab, 0x8809, "Xbox DDR dancepad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX }, + { 0x1430, 0x4748, "RedOctane Guitar Hero X-plorer", MAP_DPAD_TO_AXES, XTYPE_XBOX360 }, { 0x1430, 0x8888, "TX6500+ Dance Pad (first generation)", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX }, { 0x045e, 0x028e, "Microsoft X-Box 360 pad", MAP_DPAD_TO_AXES, XTYPE_XBOX360 }, { 0xffff, 0xffff, "Chinese-made Xbox Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX }, @@ -188,6 +190,10 @@ static const signed short xpad_abs_pad[] = { static struct usb_device_id xpad_table [] = { { USB_INTERFACE_INFO('X', 'B', 0) }, /* X-Box USB-IF not approved class */ { XPAD_XBOX360_VENDOR(0x045e) }, /* Microsoft X-Box 360 controllers */ + { XPAD_XBOX360_VENDOR(0x046d) }, /* Logitech X-Box 360 style controllers */ + { XPAD_XBOX360_VENDOR(0x0738) }, /* Mad Catz X-Box 360 controllers */ + { XPAD_XBOX360_VENDOR(0x0e6f) }, /* 0x0e6f X-Box 360 controllers */ + { XPAD_XBOX360_VENDOR(0x1430) }, /* RedOctane X-Box 360 controllers */ { } }; -- cgit v1.2.3-70-g09d2 From fc55e95214f1e8384e48cff88279d16507fa5358 Mon Sep 17 00:00:00 2001 From: Anssi Hannula Date: Thu, 3 Apr 2008 16:18:44 -0400 Subject: Input: xpad - do not report nonexistent buttons for xbox360 The buttons BTN_C and BTN_Z are only used in the original xbox controller, not in xbox360 controller. Therefore only add them to keybit when the controller is a non-360 one. Signed-off-by: Anssi Hannula Signed-off-by: Dmitry Torokhov --- drivers/input/joystick/xpad.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c index 89524954ab46..b48f7051bf30 100644 --- a/drivers/input/joystick/xpad.c +++ b/drivers/input/joystick/xpad.c @@ -144,12 +144,19 @@ static const struct xpad_device { { 0x0000, 0x0000, "Generic X-Box pad", MAP_DPAD_UNKNOWN, XTYPE_UNKNOWN } }; -static const signed short xpad_btn[] = { - BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, /* "analog" buttons */ +/* buttons shared with xbox and xbox360 */ +static const signed short xpad_common_btn[] = { + BTN_A, BTN_B, BTN_X, BTN_Y, /* "analog" buttons */ BTN_START, BTN_BACK, BTN_THUMBL, BTN_THUMBR, /* start/back/sticks */ -1 /* terminating entry */ }; +/* original xbox controllers only */ +static const signed short xpad_btn[] = { + BTN_C, BTN_Z, /* "analog" buttons */ + -1 /* terminating entry */ +}; + /* only used if MAP_DPAD_TO_BUTTONS */ static const signed short xpad_btn_pad[] = { BTN_LEFT, BTN_RIGHT, /* d-pad left, right */ @@ -679,11 +686,14 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); /* set up buttons */ - for (i = 0; xpad_btn[i] >= 0; i++) - set_bit(xpad_btn[i], input_dev->keybit); + for (i = 0; xpad_common_btn[i] >= 0; i++) + set_bit(xpad_common_btn[i], input_dev->keybit); if (xpad->xtype == XTYPE_XBOX360) for (i = 0; xpad360_btn[i] >= 0; i++) set_bit(xpad360_btn[i], input_dev->keybit); + else + for (i = 0; xpad_btn[i] >= 0; i++) + set_bit(xpad_btn[i], input_dev->keybit); if (xpad->dpad_mapping == MAP_DPAD_TO_BUTTONS) for (i = 0; xpad_btn_pad[i] >= 0; i++) set_bit(xpad_btn_pad[i], input_dev->keybit); -- cgit v1.2.3-70-g09d2 From cfbe20106fa00e89c1fb2c74dbff0ba80e0e539d Mon Sep 17 00:00:00 2001 From: Anssi Hannula Date: Thu, 3 Apr 2008 16:18:57 -0400 Subject: Input: xpad - enable force feedback on xbox 360 controllers only Commit 4994cd8dadcf9d484ab3ec19f3c7c7a4e5353c1c introduced a regression which causes xpad to report force feedback cababilities for non-360 controllers too, even while there is no actual support for those. Fix that by adding a check for XTYPE_XBOX360 to xpad_init_ff(). Signed-off-by: Anssi Hannula Signed-off-by: Dmitry Torokhov --- drivers/input/joystick/xpad.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c index b48f7051bf30..fd801135eebb 100644 --- a/drivers/input/joystick/xpad.c +++ b/drivers/input/joystick/xpad.c @@ -499,6 +499,9 @@ static int xpad_play_effect(struct input_dev *dev, void *data, static int xpad_init_ff(struct usb_xpad *xpad) { + if (xpad->xtype != XTYPE_XBOX360) + return 0; + input_set_capability(xpad->dev, EV_FF, FF_RUMBLE); return input_ff_create_memless(xpad->dev, NULL, xpad_play_effect); -- cgit v1.2.3-70-g09d2 From bf8cb3141884138c2e4a2ecb56300ece6e8020a2 Mon Sep 17 00:00:00 2001 From: Anssi Hannula Date: Thu, 3 Apr 2008 16:19:10 -0400 Subject: Input: xpad - drop obsolete driver versioning The driver version numbers and changelog have not been updated in a long while to reflect actual changes. Remove the version number and add a notice that later changes can be tracked in SCM. Signed-off-by: Anssi Hannula Signed-off-by: Dmitry Torokhov --- drivers/input/joystick/xpad.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c index fd801135eebb..7188eec53cc6 100644 --- a/drivers/input/joystick/xpad.c +++ b/drivers/input/joystick/xpad.c @@ -1,5 +1,5 @@ /* - * X-Box gamepad - v0.0.6 + * X-Box gamepad driver * * Copyright (c) 2002 Marko Friedemann * 2004 Oliver Schwartz , @@ -68,6 +68,8 @@ * - dance pads will map D-PAD to buttons, not axes * - pass the module paramater 'dpad_to_buttons' to force * the D-PAD to map to buttons if your pad is not detected + * + * Later changes can be tracked in SCM. */ #include @@ -77,7 +79,6 @@ #include #include -#define DRIVER_VERSION "v0.0.6" #define DRIVER_AUTHOR "Marko Friedemann " #define DRIVER_DESC "X-Box pad driver" @@ -771,7 +772,7 @@ static int __init usb_xpad_init(void) { int result = usb_register(&xpad_driver); if (result == 0) - info(DRIVER_DESC ":" DRIVER_VERSION); + info(DRIVER_DESC); return result; } -- cgit v1.2.3-70-g09d2 From 99de0912be6f384fc31c8e8e7ba0850d0d670385 Mon Sep 17 00:00:00 2001 From: Brian Magnuson Date: Thu, 3 Apr 2008 16:19:23 -0400 Subject: Input: xpad - add support for wireless xbox360 controllers Signed-off-by: Brian Magnuson Signed-off-by: Anssi Hannula Signed-off-by: Dmitry Torokhov --- drivers/input/joystick/xpad.c | 164 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 148 insertions(+), 16 deletions(-) diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c index 7188eec53cc6..ebf8303d6f56 100644 --- a/drivers/input/joystick/xpad.c +++ b/drivers/input/joystick/xpad.c @@ -92,7 +92,8 @@ #define XTYPE_XBOX 0 #define XTYPE_XBOX360 1 -#define XTYPE_UNKNOWN 2 +#define XTYPE_XBOX360W 2 +#define XTYPE_UNKNOWN 3 static int dpad_to_buttons; module_param(dpad_to_buttons, bool, S_IRUGO); @@ -109,6 +110,7 @@ static const struct xpad_device { { 0x045e, 0x0289, "Microsoft X-Box pad v2 (US)", MAP_DPAD_TO_AXES, XTYPE_XBOX }, { 0x045e, 0x0285, "Microsoft X-Box pad (Japan)", MAP_DPAD_TO_AXES, XTYPE_XBOX }, { 0x045e, 0x0287, "Microsoft Xbox Controller S", MAP_DPAD_TO_AXES, XTYPE_XBOX }, + { 0x045e, 0x0719, "Xbox 360 Wireless Receiver", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360W }, { 0x0c12, 0x8809, "RedOctane Xbox Dance Pad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX }, { 0x044f, 0x0f07, "Thrustmaster, Inc. Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX }, { 0x046d, 0xc242, "Logitech Chillstream Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX360 }, @@ -186,22 +188,25 @@ static const signed short xpad_abs_pad[] = { /* Xbox 360 has a vendor-specific class, so we cannot match it with only * USB_INTERFACE_INFO (also specifically refused by USB subsystem), so we - * match against vendor id as well. Also, some Xbox 360 devices have multiple - * interface protocols, we only need protocol 1. */ -#define XPAD_XBOX360_VENDOR(vend) \ + * match against vendor id as well. Wired Xbox 360 devices have protocol 1, + * wireless controllers have protocol 129. */ +#define XPAD_XBOX360_VENDOR_PROTOCOL(vend,pr) \ .match_flags = USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_INT_INFO, \ .idVendor = (vend), \ .bInterfaceClass = USB_CLASS_VENDOR_SPEC, \ .bInterfaceSubClass = 93, \ - .bInterfaceProtocol = 1 + .bInterfaceProtocol = (pr) +#define XPAD_XBOX360_VENDOR(vend) \ + { XPAD_XBOX360_VENDOR_PROTOCOL(vend,1) }, \ + { XPAD_XBOX360_VENDOR_PROTOCOL(vend,129) } static struct usb_device_id xpad_table [] = { { USB_INTERFACE_INFO('X', 'B', 0) }, /* X-Box USB-IF not approved class */ - { XPAD_XBOX360_VENDOR(0x045e) }, /* Microsoft X-Box 360 controllers */ - { XPAD_XBOX360_VENDOR(0x046d) }, /* Logitech X-Box 360 style controllers */ - { XPAD_XBOX360_VENDOR(0x0738) }, /* Mad Catz X-Box 360 controllers */ - { XPAD_XBOX360_VENDOR(0x0e6f) }, /* 0x0e6f X-Box 360 controllers */ - { XPAD_XBOX360_VENDOR(0x1430) }, /* RedOctane X-Box 360 controllers */ + XPAD_XBOX360_VENDOR(0x045e), /* Microsoft X-Box 360 controllers */ + XPAD_XBOX360_VENDOR(0x046d), /* Logitech X-Box 360 style controllers */ + XPAD_XBOX360_VENDOR(0x0738), /* Mad Catz X-Box 360 controllers */ + XPAD_XBOX360_VENDOR(0x0e6f), /* 0x0e6f X-Box 360 controllers */ + XPAD_XBOX360_VENDOR(0x1430), /* RedOctane X-Box 360 controllers */ { } }; @@ -211,10 +216,15 @@ struct usb_xpad { struct input_dev *dev; /* input device interface */ struct usb_device *udev; /* usb device */ + int pad_present; + struct urb *irq_in; /* urb for interrupt in report */ unsigned char *idata; /* input data */ dma_addr_t idata_dma; + struct urb *bulk_out; + unsigned char *bdata; + #if defined(CONFIG_JOYSTICK_XPAD_FF) || defined(CONFIG_JOYSTICK_XPAD_LEDS) struct urb *irq_out; /* urb for interrupt out report */ unsigned char *odata; /* output data */ @@ -359,6 +369,39 @@ static void xpad360_process_packet(struct usb_xpad *xpad, input_sync(dev); } +/* + * xpad360w_process_packet + * + * Completes a request by converting the data into events for the + * input subsystem. It is version for xbox 360 wireless controller. + * + * Byte.Bit + * 00.1 - Status change: The controller or headset has connected/disconnected + * Bits 01.7 and 01.6 are valid + * 01.7 - Controller present + * 01.6 - Headset present + * 01.1 - Pad state (Bytes 4+) valid + * + */ + +static void xpad360w_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *data) +{ + /* Presence change */ + if (data[0] & 0x08) { + if (data[1] & 0x80) { + xpad->pad_present = 1; + usb_submit_urb(xpad->bulk_out, GFP_ATOMIC); + } else + xpad->pad_present = 0; + } + + /* Valid pad data */ + if (!(data[1] & 0x1)) + return; + + xpad360_process_packet(xpad, cmd, &data[4]); +} + static void xpad_irq_in(struct urb *urb) { struct usb_xpad *xpad = urb->context; @@ -381,10 +424,16 @@ static void xpad_irq_in(struct urb *urb) goto exit; } - if (xpad->xtype == XTYPE_XBOX360) + switch (xpad->xtype) { + case XTYPE_XBOX360: xpad360_process_packet(xpad, 0, xpad->idata); - else + break; + case XTYPE_XBOX360W: + xpad360w_process_packet(xpad, 0, xpad->idata); + break; + default: xpad_process_packet(xpad, 0, xpad->idata); + } exit: retval = usb_submit_urb (urb, GFP_ATOMIC); @@ -422,6 +471,23 @@ exit: __FUNCTION__, retval); } +static void xpad_bulk_out(struct urb *urb) +{ + switch (urb->status) { + case 0: + /* success */ + break; + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* this urb is terminated, clean up */ + dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status); + break; + default: + dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status); + } +} + static int xpad_init_output(struct usb_interface *intf, struct usb_xpad *xpad) { struct usb_endpoint_descriptor *ep_irq_out; @@ -600,6 +666,10 @@ static int xpad_open(struct input_dev *dev) { struct usb_xpad *xpad = input_get_drvdata(dev); + /* URB was submitted in probe */ + if(xpad->xtype == XTYPE_XBOX360W) + return 0; + xpad->irq_in->dev = xpad->udev; if (usb_submit_urb(xpad->irq_in, GFP_KERNEL)) return -EIO; @@ -611,7 +681,8 @@ static void xpad_close(struct input_dev *dev) { struct usb_xpad *xpad = input_get_drvdata(dev); - usb_kill_urb(xpad->irq_in); + if(xpad->xtype != XTYPE_XBOX360W) + usb_kill_urb(xpad->irq_in); xpad_stop_output(xpad); } @@ -671,8 +742,15 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id xpad->xtype = xpad_device[i].xtype; if (xpad->dpad_mapping == MAP_DPAD_UNKNOWN) xpad->dpad_mapping = !dpad_to_buttons; - if (xpad->xtype == XTYPE_UNKNOWN) - xpad->xtype = (intf->cur_altsetting->desc.bInterfaceClass == USB_CLASS_VENDOR_SPEC); + if (xpad->xtype == XTYPE_UNKNOWN) { + if (intf->cur_altsetting->desc.bInterfaceClass == USB_CLASS_VENDOR_SPEC) { + if (intf->cur_altsetting->desc.bInterfaceProtocol == 129) + xpad->xtype = XTYPE_XBOX360W; + else + xpad->xtype = XTYPE_XBOX360; + } else + xpad->xtype = XTYPE_XBOX; + } xpad->dev = input_dev; usb_make_path(udev, xpad->phys, sizeof(xpad->phys)); strlcat(xpad->phys, "/input0", sizeof(xpad->phys)); @@ -692,7 +770,7 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id /* set up buttons */ for (i = 0; xpad_common_btn[i] >= 0; i++) set_bit(xpad_common_btn[i], input_dev->keybit); - if (xpad->xtype == XTYPE_XBOX360) + if ((xpad->xtype == XTYPE_XBOX360) || (xpad->xtype == XTYPE_XBOX360W)) for (i = 0; xpad360_btn[i] >= 0; i++) set_bit(xpad360_btn[i], input_dev->keybit); else @@ -734,8 +812,57 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id goto fail4; usb_set_intfdata(intf, xpad); + + /* + * Submit the int URB immediatly rather than waiting for open + * because we get status messages from the device whether + * or not any controllers are attached. In fact, it's + * exactly the message that a controller has arrived that + * we're waiting for. + */ + if (xpad->xtype == XTYPE_XBOX360W) { + xpad->irq_in->dev = xpad->udev; + error = usb_submit_urb(xpad->irq_in, GFP_KERNEL); + if (error) + goto fail4; + + /* + * Setup the message to set the LEDs on the + * controller when it shows up + */ + xpad->bulk_out = usb_alloc_urb(0, GFP_KERNEL); + if(!xpad->bulk_out) + goto fail5; + + xpad->bdata = kzalloc(XPAD_PKT_LEN, GFP_KERNEL); + if(!xpad->bdata) + goto fail6; + + xpad->bdata[2] = 0x08; + switch (intf->cur_altsetting->desc.bInterfaceNumber) { + case 0: + xpad->bdata[3] = 0x42; + break; + case 2: + xpad->bdata[3] = 0x43; + break; + case 4: + xpad->bdata[3] = 0x44; + break; + case 6: + xpad->bdata[3] = 0x45; + } + + ep_irq_in = &intf->cur_altsetting->endpoint[1].desc; + usb_fill_bulk_urb(xpad->bulk_out, udev, + usb_sndbulkpipe(udev, ep_irq_in->bEndpointAddress), + xpad->bdata, XPAD_PKT_LEN, xpad_bulk_out, xpad); + } + return 0; + fail6: usb_free_urb(xpad->bulk_out); + fail5: usb_kill_urb(xpad->irq_in); fail4: usb_free_urb(xpad->irq_in); fail3: xpad_deinit_output(xpad); fail2: usb_buffer_free(udev, XPAD_PKT_LEN, xpad->idata, xpad->idata_dma); @@ -754,6 +881,11 @@ static void xpad_disconnect(struct usb_interface *intf) xpad_led_disconnect(xpad); input_unregister_device(xpad->dev); xpad_deinit_output(xpad); + if (xpad->xtype == XTYPE_XBOX360W) { + usb_kill_urb(xpad->bulk_out); + usb_free_urb(xpad->bulk_out); + usb_kill_urb(xpad->irq_in); + } usb_free_urb(xpad->irq_in); usb_buffer_free(xpad->udev, XPAD_PKT_LEN, xpad->idata, xpad->idata_dma); -- cgit v1.2.3-70-g09d2 From 6eae9b0acdb6f03ed87ef882760e0ef8f440dc1a Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Thu, 3 Apr 2008 16:19:33 -0400 Subject: Input: xpad - don't use GFP_ATOMIC GFP_ATOMIC should not be used when GFP_KERNEL can be used. Signed-off-by: Oliver Neukum Signed-off-by: Dmitry Torokhov --- drivers/input/joystick/xpad.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c index ebf8303d6f56..d4d289e75dc2 100644 --- a/drivers/input/joystick/xpad.c +++ b/drivers/input/joystick/xpad.c @@ -497,7 +497,7 @@ static int xpad_init_output(struct usb_interface *intf, struct usb_xpad *xpad) return 0; xpad->odata = usb_buffer_alloc(xpad->udev, XPAD_PKT_LEN, - GFP_ATOMIC, &xpad->odata_dma ); + GFP_KERNEL, &xpad->odata_dma); if (!xpad->odata) goto fail1; @@ -729,7 +729,7 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id goto fail1; xpad->idata = usb_buffer_alloc(udev, XPAD_PKT_LEN, - GFP_ATOMIC, &xpad->idata_dma); + GFP_KERNEL, &xpad->idata_dma); if (!xpad->idata) goto fail1; -- cgit v1.2.3-70-g09d2 From 76d057ce5a48034c97f604a0a25a87093e072c71 Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Fri, 4 Apr 2008 15:31:47 -0400 Subject: Input: usbtouchscreen - don't use DMA on stack DMA on the stack is not allowed. The buffer must be kmalloced. Signed-off-by: Oliver Neukum Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/usbtouchscreen.c | 31 ++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/drivers/input/touchscreen/usbtouchscreen.c b/drivers/input/touchscreen/usbtouchscreen.c index 63f9664a066f..3a0a8ca57076 100644 --- a/drivers/input/touchscreen/usbtouchscreen.c +++ b/drivers/input/touchscreen/usbtouchscreen.c @@ -396,9 +396,12 @@ static int gunze_read_data(struct usbtouch_usb *dev, unsigned char *pkt) static int dmc_tsc10_init(struct usbtouch_usb *usbtouch) { struct usb_device *dev = usbtouch->udev; - int ret; - unsigned char buf[2]; + int ret = -ENOMEM; + unsigned char *buf; + buf = kmalloc(2, GFP_KERNEL); + if (!buf) + goto err_nobuf; /* reset */ buf[0] = buf[1] = 0xFF; ret = usb_control_msg(dev, usb_rcvctrlpipe (dev, 0), @@ -406,9 +409,11 @@ static int dmc_tsc10_init(struct usbtouch_usb *usbtouch) USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0, 0, buf, 2, USB_CTRL_SET_TIMEOUT); if (ret < 0) - return ret; - if (buf[0] != 0x06 || buf[1] != 0x00) - return -ENODEV; + goto err_out; + if (buf[0] != 0x06 || buf[1] != 0x00) { + ret = -ENODEV; + goto err_out; + } /* set coordinate output rate */ buf[0] = buf[1] = 0xFF; @@ -417,20 +422,22 @@ static int dmc_tsc10_init(struct usbtouch_usb *usbtouch) USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, TSC10_RATE_150, 0, buf, 2, USB_CTRL_SET_TIMEOUT); if (ret < 0) - return ret; + goto err_out; if ((buf[0] != 0x06 || buf[1] != 0x00) && - (buf[0] != 0x15 || buf[1] != 0x01)) - return -ENODEV; + (buf[0] != 0x15 || buf[1] != 0x01)) { + ret = -ENODEV; + goto err_out; + } /* start sending data */ ret = usb_control_msg(dev, usb_rcvctrlpipe (dev, 0), TSC10_CMD_DATA1, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0, 0, NULL, 0, USB_CTRL_SET_TIMEOUT); - if (ret < 0) - return ret; - - return 0; +err_out: + kfree(buf); +err_nobuf: + return ret; } -- cgit v1.2.3-70-g09d2 From 0beb4f6f294b0f2dde07fa9da9c00abd4f9c8b50 Mon Sep 17 00:00:00 2001 From: Karl Dahlke Date: Tue, 15 Apr 2008 01:30:32 -0400 Subject: Input: put ledstate in the keyboard notifier Led state should be part of the key event, like shiftstate, and not grabbed asynchronously after the fact. [samuel.thibault@ens-lyon.org: various fixes] Signed-off-by: Samuel Thibault Signed-off-by: Andrew Morton Signed-off-by: Dmitry Torokhov --- drivers/char/keyboard.c | 2 ++ include/linux/keyboard.h | 1 + 2 files changed, 3 insertions(+) diff --git a/drivers/char/keyboard.c b/drivers/char/keyboard.c index 4dbd3425e928..59608e341385 100644 --- a/drivers/char/keyboard.c +++ b/drivers/char/keyboard.c @@ -1237,6 +1237,7 @@ static void kbd_keycode(unsigned int keycode, int down, int hw_raw) } param.shift = shift_final = (shift_state | kbd->slockstate) ^ kbd->lockstate; + param.ledstate = kbd->ledflagstate; key_map = key_maps[shift_final]; if (atomic_notifier_call_chain(&keyboard_notifier_list, KBD_KEYCODE, ¶m) == NOTIFY_STOP || !key_map) { @@ -1285,6 +1286,7 @@ static void kbd_keycode(unsigned int keycode, int down, int hw_raw) (*k_handler[type])(vc, keysym & 0xff, !down); + param.ledstate = kbd->ledflagstate; atomic_notifier_call_chain(&keyboard_notifier_list, KBD_POST_KEYSYM, ¶m); if (type != KT_SLOCK) diff --git a/include/linux/keyboard.h b/include/linux/keyboard.h index 65c2d70853e9..a3c984d780f0 100644 --- a/include/linux/keyboard.h +++ b/include/linux/keyboard.h @@ -33,6 +33,7 @@ struct keyboard_notifier_param { struct vc_data *vc; /* VC on which the keyboard press was done */ int down; /* Pressure of the key? */ int shift; /* Current shift mask */ + int ledstate; /* Current led state */ unsigned int value; /* keycode, unicode value or keysym */ }; -- cgit v1.2.3-70-g09d2 From 9f9439e92a7fb057d31a19636b99e43306192756 Mon Sep 17 00:00:00 2001 From: Hans-Christian Egtvedt Date: Tue, 15 Apr 2008 01:30:47 -0400 Subject: Input: add PS/2 serio driver for AVR32 devices Add support for the PSIF peripheral on AVR32 AP7 devices. It is implemented as a serio driver and will behave like a serio 8042 device. The driver has been tested with a Dell keyboard capable of running on 3.3 volts and a Logitech mouse on the STK1000 + STK1002 starter kit. The Logitech mouse was hacked by cutting the cord and using a bi-directional voltage converter to get the required 5 volt I/O level. For more information about the PSIF module, see the datasheet for AT32AP700X at http://www.atmel.com/dyn/products/datasheets.asp?family_id=682 Signed-off-by: Hans-Christian Egtvedt Signed-off-by: Dmitry Torokhov --- drivers/input/serio/Kconfig | 10 ++ drivers/input/serio/Makefile | 1 + drivers/input/serio/at32psif.c | 375 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 386 insertions(+) create mode 100644 drivers/input/serio/at32psif.c diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig index b88569e21d60..ec4b6610f730 100644 --- a/drivers/input/serio/Kconfig +++ b/drivers/input/serio/Kconfig @@ -88,6 +88,16 @@ config SERIO_RPCKBD To compile this driver as a module, choose M here: the module will be called rpckbd. +config SERIO_AT32PSIF + tristate "AVR32 PSIF PS/2 keyboard and mouse controller" + depends on AVR32 + help + Say Y here if you want to use the PSIF peripheral on AVR32 devices + and connect a PS/2 keyboard and/or mouse to it. + + To compile this driver as a module, choose M here: the module will + be called at32psif. + config SERIO_AMBAKMI tristate "AMBA KMI keyboard controller" depends on ARM_AMBA diff --git a/drivers/input/serio/Makefile b/drivers/input/serio/Makefile index 4155197867a3..38b886887cbc 100644 --- a/drivers/input/serio/Makefile +++ b/drivers/input/serio/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_SERIO_CT82C710) += ct82c710.o obj-$(CONFIG_SERIO_RPCKBD) += rpckbd.o obj-$(CONFIG_SERIO_SA1111) += sa1111ps2.o obj-$(CONFIG_SERIO_AMBAKMI) += ambakmi.o +obj-$(CONFIG_SERIO_AT32PSIF) += at32psif.o obj-$(CONFIG_SERIO_Q40KBD) += q40kbd.o obj-$(CONFIG_SERIO_GSCPS2) += gscps2.o obj-$(CONFIG_HP_SDC) += hp_sdc.o diff --git a/drivers/input/serio/at32psif.c b/drivers/input/serio/at32psif.c new file mode 100644 index 000000000000..c267588f7308 --- /dev/null +++ b/drivers/input/serio/at32psif.c @@ -0,0 +1,375 @@ +/* + * Copyright (C) 2007 Atmel Corporation + * + * Driver for the AT32AP700X PS/2 controller (PSIF). + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* PSIF register offsets */ +#define PSIF_CR 0x00 +#define PSIF_RHR 0x04 +#define PSIF_THR 0x08 +#define PSIF_SR 0x10 +#define PSIF_IER 0x14 +#define PSIF_IDR 0x18 +#define PSIF_IMR 0x1c +#define PSIF_PSR 0x24 + +/* Bitfields in control register. */ +#define PSIF_CR_RXDIS_OFFSET 1 +#define PSIF_CR_RXDIS_SIZE 1 +#define PSIF_CR_RXEN_OFFSET 0 +#define PSIF_CR_RXEN_SIZE 1 +#define PSIF_CR_SWRST_OFFSET 15 +#define PSIF_CR_SWRST_SIZE 1 +#define PSIF_CR_TXDIS_OFFSET 9 +#define PSIF_CR_TXDIS_SIZE 1 +#define PSIF_CR_TXEN_OFFSET 8 +#define PSIF_CR_TXEN_SIZE 1 + +/* Bitfields in interrupt disable, enable, mask and status register. */ +#define PSIF_NACK_OFFSET 8 +#define PSIF_NACK_SIZE 1 +#define PSIF_OVRUN_OFFSET 5 +#define PSIF_OVRUN_SIZE 1 +#define PSIF_PARITY_OFFSET 9 +#define PSIF_PARITY_SIZE 1 +#define PSIF_RXRDY_OFFSET 4 +#define PSIF_RXRDY_SIZE 1 +#define PSIF_TXEMPTY_OFFSET 1 +#define PSIF_TXEMPTY_SIZE 1 +#define PSIF_TXRDY_OFFSET 0 +#define PSIF_TXRDY_SIZE 1 + +/* Bitfields in prescale register. */ +#define PSIF_PSR_PRSCV_OFFSET 0 +#define PSIF_PSR_PRSCV_SIZE 12 + +/* Bitfields in receive hold register. */ +#define PSIF_RHR_RXDATA_OFFSET 0 +#define PSIF_RHR_RXDATA_SIZE 8 + +/* Bitfields in transmit hold register. */ +#define PSIF_THR_TXDATA_OFFSET 0 +#define PSIF_THR_TXDATA_SIZE 8 + +/* Bit manipulation macros */ +#define PSIF_BIT(name) \ + (1 << PSIF_##name##_OFFSET) + +#define PSIF_BF(name, value) \ + (((value) & ((1 << PSIF_##name##_SIZE) - 1)) \ + << PSIF_##name##_OFFSET) + +#define PSIF_BFEXT(name, value) \ + (((value) >> PSIF_##name##_OFFSET) \ + & ((1 << PSIF_##name##_SIZE) - 1)) + +#define PSIF_BFINS(name, value, old) \ + (((old) & ~(((1 << PSIF_##name##_SIZE) - 1) \ + << PSIF_##name##_OFFSET)) \ + | PSIF_BF(name, value)) + +/* Register access macros */ +#define psif_readl(port, reg) \ + __raw_readl((port)->regs + PSIF_##reg) + +#define psif_writel(port, reg, value) \ + __raw_writel((value), (port)->regs + PSIF_##reg) + +struct psif { + struct platform_device *pdev; + struct clk *pclk; + struct serio *io; + void __iomem *regs; + unsigned int irq; + unsigned int open; + /* Prevent concurrent writes to PSIF THR. */ + spinlock_t lock; +}; + +static irqreturn_t psif_interrupt(int irq, void *_ptr) +{ + struct psif *psif = _ptr; + int retval = IRQ_NONE; + unsigned int io_flags = 0; + unsigned long status; + + status = psif_readl(psif, SR); + + if (status & PSIF_BIT(RXRDY)) { + unsigned char val = (unsigned char) psif_readl(psif, RHR); + + if (status & PSIF_BIT(PARITY)) + io_flags |= SERIO_PARITY; + if (status & PSIF_BIT(OVRUN)) + dev_err(&psif->pdev->dev, "overrun read error\n"); + + serio_interrupt(psif->io, val, io_flags); + + retval = IRQ_HANDLED; + } + + return retval; +} + +static int psif_write(struct serio *io, unsigned char val) +{ + struct psif *psif = io->port_data; + unsigned long flags; + int timeout = 10; + int retval = 0; + + spin_lock_irqsave(&psif->lock, flags); + + while (!(psif_readl(psif, SR) & PSIF_BIT(TXEMPTY)) && timeout--) + msleep(10); + + if (timeout >= 0) { + psif_writel(psif, THR, val); + } else { + dev_dbg(&psif->pdev->dev, "timeout writing to THR\n"); + retval = -EBUSY; + } + + spin_unlock_irqrestore(&psif->lock, flags); + + return retval; +} + +static int psif_open(struct serio *io) +{ + struct psif *psif = io->port_data; + int retval; + + retval = clk_enable(psif->pclk); + if (retval) + goto out; + + psif_writel(psif, CR, PSIF_BIT(CR_TXEN) | PSIF_BIT(CR_RXEN)); + psif_writel(psif, IER, PSIF_BIT(RXRDY)); + + psif->open = 1; +out: + return retval; +} + +static void psif_close(struct serio *io) +{ + struct psif *psif = io->port_data; + + psif->open = 0; + + psif_writel(psif, IDR, ~0UL); + psif_writel(psif, CR, PSIF_BIT(CR_TXDIS) | PSIF_BIT(CR_RXDIS)); + + clk_disable(psif->pclk); +} + +static void psif_set_prescaler(struct psif *psif) +{ + unsigned long prscv; + unsigned long rate = clk_get_rate(psif->pclk); + + /* PRSCV = Pulse length (100 us) * PSIF module frequency. */ + prscv = 100 * (rate / 1000000UL); + + if (prscv > ((1<pdev->dev, "pclk too fast, " + "prescaler set to max\n"); + } + + clk_enable(psif->pclk); + psif_writel(psif, PSR, prscv); + clk_disable(psif->pclk); +} + +static int __init psif_probe(struct platform_device *pdev) +{ + struct resource *regs; + struct psif *psif; + struct serio *io; + struct clk *pclk; + int irq; + int ret; + + psif = kzalloc(sizeof(struct psif), GFP_KERNEL); + if (!psif) { + dev_dbg(&pdev->dev, "out of memory\n"); + ret = -ENOMEM; + goto out; + } + psif->pdev = pdev; + + io = kzalloc(sizeof(struct serio), GFP_KERNEL); + if (!io) { + dev_dbg(&pdev->dev, "out of memory\n"); + ret = -ENOMEM; + goto out_free_psif; + } + psif->io = io; + + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!regs) { + dev_dbg(&pdev->dev, "no mmio resources defined\n"); + ret = -ENOMEM; + goto out_free_io; + } + + psif->regs = ioremap(regs->start, regs->end - regs->start + 1); + if (!psif->regs) { + ret = -ENOMEM; + dev_dbg(&pdev->dev, "could not map I/O memory\n"); + goto out_free_io; + } + + pclk = clk_get(&pdev->dev, "pclk"); + if (IS_ERR(pclk)) { + dev_dbg(&pdev->dev, "could not get peripheral clock\n"); + ret = PTR_ERR(pclk); + goto out_iounmap; + } + psif->pclk = pclk; + + /* Reset the PSIF to enter at a known state. */ + ret = clk_enable(pclk); + if (ret) { + dev_dbg(&pdev->dev, "could not enable pclk\n"); + goto out_put_clk; + } + psif_writel(psif, CR, PSIF_BIT(CR_SWRST)); + clk_disable(pclk); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_dbg(&pdev->dev, "could not get irq\n"); + ret = -ENXIO; + goto out_put_clk; + } + ret = request_irq(irq, psif_interrupt, IRQF_SHARED, "at32psif", psif); + if (ret) { + dev_dbg(&pdev->dev, "could not request irq %d\n", irq); + goto out_put_clk; + } + psif->irq = irq; + + io->id.type = SERIO_8042; + io->write = psif_write; + io->open = psif_open; + io->close = psif_close; + snprintf(io->name, sizeof(io->name), "AVR32 PS/2 port%d", pdev->id); + snprintf(io->phys, sizeof(io->phys), "at32psif/serio%d", pdev->id); + io->port_data = psif; + io->dev.parent = &pdev->dev; + + psif_set_prescaler(psif); + + spin_lock_init(&psif->lock); + serio_register_port(psif->io); + platform_set_drvdata(pdev, psif); + + dev_info(&pdev->dev, "Atmel AVR32 PSIF PS/2 driver on 0x%08x irq %d\n", + (int)psif->regs, psif->irq); + + return 0; + +out_put_clk: + clk_put(psif->pclk); +out_iounmap: + iounmap(psif->regs); +out_free_io: + kfree(io); +out_free_psif: + kfree(psif); +out: + return ret; +} + +static int __exit psif_remove(struct platform_device *pdev) +{ + struct psif *psif = platform_get_drvdata(pdev); + + psif_writel(psif, IDR, ~0UL); + psif_writel(psif, CR, PSIF_BIT(CR_TXDIS) | PSIF_BIT(CR_RXDIS)); + + serio_unregister_port(psif->io); + iounmap(psif->regs); + free_irq(psif->irq, psif); + clk_put(psif->pclk); + kfree(psif); + + platform_set_drvdata(pdev, NULL); + + return 0; +} + +#ifdef CONFIG_PM +static int psif_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct psif *psif = platform_get_drvdata(pdev); + + if (psif->open) { + psif_writel(psif, CR, PSIF_BIT(CR_RXDIS) | PSIF_BIT(CR_TXDIS)); + clk_disable(psif->pclk); + } + + return 0; +} + +static int psif_resume(struct platform_device *pdev) +{ + struct psif *psif = platform_get_drvdata(pdev); + + if (psif->open) { + clk_enable(psif->pclk); + psif_set_prescaler(psif); + psif_writel(psif, CR, PSIF_BIT(CR_RXEN) | PSIF_BIT(CR_TXEN)); + } + + return 0; +} +#else +#define psif_suspend NULL +#define psif_resume NULL +#endif + +static struct platform_driver psif_driver = { + .remove = __exit_p(psif_remove), + .driver = { + .name = "atmel_psif", + }, + .suspend = psif_suspend, + .resume = psif_resume, +}; + +static int __init psif_init(void) +{ + return platform_driver_probe(&psif_driver, psif_probe); +} + +static void __exit psif_exit(void) +{ + platform_driver_unregister(&psif_driver); +} + +module_init(psif_init); +module_exit(psif_exit); + +MODULE_AUTHOR("Hans-Christian Egtvedt "); +MODULE_DESCRIPTION("Atmel AVR32 PSIF PS/2 driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3-70-g09d2 From 1164ec1ae43770db6ea5450c6cac0761b11d6d1d Mon Sep 17 00:00:00 2001 From: David Brownell Date: Tue, 15 Apr 2008 01:31:13 -0400 Subject: Input: gpio_keys - irq handling cleanup Cleanup IRQ handling in gpio_keys: bail after handling the IRQ, and report IRQ_NONE if we never handle it. Signed-off-by: David Brownell Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/gpio_keys.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c index 6a9ca4bdcb74..a54dc15f9005 100644 --- a/drivers/input/keyboard/gpio_keys.c +++ b/drivers/input/keyboard/gpio_keys.c @@ -43,10 +43,11 @@ static irqreturn_t gpio_keys_isr(int irq, void *dev_id) input_event(input, type, button->code, !!state); input_sync(input); + return IRQ_HANDLED; } } - return IRQ_HANDLED; + return IRQ_NONE; } static int __devinit gpio_keys_probe(struct platform_device *pdev) -- cgit v1.2.3-70-g09d2 From da3e4c885e5ebbccc8181f53d8ae74c4c22d506f Mon Sep 17 00:00:00 2001 From: David Brownell Date: Tue, 15 Apr 2008 01:31:33 -0400 Subject: Input: omap-keypad - fix build warning Fix the following build warning: drivers/input/keyboard/omap-keypad.c: In function 'omap_kp_probe': drivers/input/keyboard/omap-keypad.c:418: warning: 'row_idx' is used uninitialized in this function drivers/input/keyboard/omap-keypad.c:421: warning: 'col_idx' is used uninitialized in this function These variables are useful when cpu_is_omap24xx(), and otherwise just for useless cleanup. Signed-off-by: David Brownell Signed-off-by: Tony Lindgren Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/omap-keypad.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/input/keyboard/omap-keypad.c b/drivers/input/keyboard/omap-keypad.c index babc913d5492..eec328167f8d 100644 --- a/drivers/input/keyboard/omap-keypad.c +++ b/drivers/input/keyboard/omap-keypad.c @@ -352,6 +352,9 @@ static int __init omap_kp_probe(struct platform_device *pdev) } omap_set_gpio_direction(row_gpios[row_idx], 1); } + } else { + col_idx = 0; + row_idx = 0; } setup_timer(&omap_kp->timer, omap_kp_timer, (unsigned long)omap_kp); @@ -415,10 +418,10 @@ err4: err3: device_remove_file(&pdev->dev, &dev_attr_enable); err2: - for (i = row_idx-1; i >=0; i--) + for (i = row_idx - 1; i >=0; i--) omap_free_gpio(row_gpios[i]); err1: - for (i = col_idx-1; i >=0; i--) + for (i = col_idx - 1; i >=0; i--) omap_free_gpio(col_gpios[i]); kfree(omap_kp); -- cgit v1.2.3-70-g09d2 From 04021e4e401d7ac2051839dd5b00a701c9119dd9 Mon Sep 17 00:00:00 2001 From: Michael Gruber Date: Tue, 15 Apr 2008 01:31:47 -0400 Subject: Input: xpad - set proper buffer length for outgoing requests The messages for led/rumble are exactly 3 and 8 bytes respectively. Hence set up the transfer_buffer_length accordingly. Signed-off-by: Michael Gruber Acked-by: Anssi Hannula Signed-off-by: Dmitry Torokhov --- drivers/input/joystick/xpad.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c index d4d289e75dc2..52ddb04644ab 100644 --- a/drivers/input/joystick/xpad.c +++ b/drivers/input/joystick/xpad.c @@ -558,6 +558,7 @@ static int xpad_play_effect(struct input_dev *dev, void *data, xpad->odata[5] = 0x00; xpad->odata[6] = 0x00; xpad->odata[7] = 0x00; + xpad->irq_out->transfer_buffer_length = 8; usb_submit_urb(xpad->irq_out, GFP_KERNEL); } @@ -594,6 +595,7 @@ static void xpad_send_led_command(struct usb_xpad *xpad, int command) xpad->odata[0] = 0x01; xpad->odata[1] = 0x03; xpad->odata[2] = command; + xpad->irq_out->transfer_buffer_length = 3; usb_submit_urb(xpad->irq_out, GFP_KERNEL); mutex_unlock(&xpad->odata_mutex); } -- cgit v1.2.3-70-g09d2 From e722409445fbe718f09f6d5e03d0ae84cf0954d0 Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Tue, 15 Apr 2008 01:31:57 -0400 Subject: Input: wacom - implement suspend and autosuspend This implements suspend and autosuspend support for wacom devices. It works by using the usb last busy functionality triggered in the completion callback. Signed-off-by: Oliver Neukum Signed-off-by: Ping Cheng Signed-off-by: Dmitry Torokhov --- drivers/input/tablet/wacom.h | 3 ++ drivers/input/tablet/wacom_sys.c | 76 ++++++++++++++++++++++++++++++++++------ 2 files changed, 69 insertions(+), 10 deletions(-) diff --git a/drivers/input/tablet/wacom.h b/drivers/input/tablet/wacom.h index acf9830698cb..706619d06f71 100644 --- a/drivers/input/tablet/wacom.h +++ b/drivers/input/tablet/wacom.h @@ -101,8 +101,11 @@ struct wacom { dma_addr_t data_dma; struct input_dev *dev; struct usb_device *usbdev; + struct usb_interface *intf; struct urb *irq; struct wacom_wac * wacom_wac; + struct mutex lock; + int open:1; char phys[32]; }; diff --git a/drivers/input/tablet/wacom_sys.c b/drivers/input/tablet/wacom_sys.c index 41caaef8e2d7..71cc0c140790 100644 --- a/drivers/input/tablet/wacom_sys.c +++ b/drivers/input/tablet/wacom_sys.c @@ -70,6 +70,7 @@ static void wacom_sys_irq(struct urb *urb) input_sync(get_input_dev(&wcombo)); exit: + usb_mark_last_busy(wacom->usbdev); retval = usb_submit_urb (urb, GFP_ATOMIC); if (retval) err ("%s - usb_submit_urb failed with result %d", @@ -124,10 +125,25 @@ static int wacom_open(struct input_dev *dev) { struct wacom *wacom = input_get_drvdata(dev); + mutex_lock(&wacom->lock); + wacom->irq->dev = wacom->usbdev; - if (usb_submit_urb(wacom->irq, GFP_KERNEL)) + + if (usb_autopm_get_interface(wacom->intf) < 0) { + mutex_unlock(&wacom->lock); return -EIO; + } + + if (usb_submit_urb(wacom->irq, GFP_KERNEL)) { + usb_autopm_put_interface(wacom->intf); + mutex_unlock(&wacom->lock); + return -EIO; + } + + wacom->open = 1; + wacom->intf->needs_remote_wakeup = 1; + mutex_unlock(&wacom->lock); return 0; } @@ -135,7 +151,11 @@ static void wacom_close(struct input_dev *dev) { struct wacom *wacom = input_get_drvdata(dev); + mutex_lock(&wacom->lock); usb_kill_urb(wacom->irq); + wacom->open = 0; + wacom->intf->needs_remote_wakeup = 0; + mutex_unlock(&wacom->lock); } void input_dev_mo(struct input_dev *input_dev, struct wacom_wac *wacom_wac) @@ -243,6 +263,8 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i wacom->usbdev = dev; wacom->dev = input_dev; + wacom->intf = intf; + mutex_init(&wacom->lock); usb_make_path(dev, wacom->phys, sizeof(wacom->phys)); strlcat(wacom->phys, "/input0", sizeof(wacom->phys)); @@ -304,23 +326,57 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i static void wacom_disconnect(struct usb_interface *intf) { - struct wacom *wacom = usb_get_intfdata (intf); + struct wacom *wacom = usb_get_intfdata(intf); usb_set_intfdata(intf, NULL); - if (wacom) { - usb_kill_urb(wacom->irq); - input_unregister_device(wacom->dev); - usb_free_urb(wacom->irq); - usb_buffer_free(interface_to_usbdev(intf), 10, wacom->wacom_wac->data, wacom->data_dma); - kfree(wacom->wacom_wac); - kfree(wacom); - } + + usb_kill_urb(wacom->irq); + input_unregister_device(wacom->dev); + usb_free_urb(wacom->irq); + usb_buffer_free(interface_to_usbdev(intf), 10, wacom->wacom_wac->data, wacom->data_dma); + kfree(wacom->wacom_wac); + kfree(wacom); +} + +static int wacom_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct wacom *wacom = usb_get_intfdata(intf); + + mutex_lock(&wacom->lock); + usb_kill_urb(wacom->irq); + mutex_unlock(&wacom->lock); + + return 0; +} + +static int wacom_resume(struct usb_interface *intf) +{ + struct wacom *wacom = usb_get_intfdata(intf); + int rv; + + mutex_lock(&wacom->lock); + if (wacom->open) + rv = usb_submit_urb(wacom->irq, GFP_NOIO); + else + rv = 0; + mutex_unlock(&wacom->lock); + + return rv; +} + +static int wacom_reset_resume(struct usb_interface *intf) +{ + return wacom_resume(intf); } static struct usb_driver wacom_driver = { .name = "wacom", .probe = wacom_probe, .disconnect = wacom_disconnect, + .suspend = wacom_suspend, + .resume = wacom_resume, + .reset_resume = wacom_reset_resume, + .supports_autosuspend = 1, }; static int __init wacom_init(void) -- cgit v1.2.3-70-g09d2 From a32bcc45b9e9d8021b5936c45dc3f8db7a044466 Mon Sep 17 00:00:00 2001 From: Guryanov Dmitry Date: Mon, 10 Mar 2008 03:08:58 -0700 Subject: Input: aiptek - add support for Genius G-PEN 560 tablet USBHID driver only supports relative mode with this tablet so let aiptek module handle it. Signed-off-by: Dmitry Guryanov Signed-off-by: Andrew Morton Signed-off-by: Jiri Kosina Signed-off-by: Dmitry Torokhov --- drivers/hid/usbhid/hid-quirks.c | 4 ++++ drivers/input/tablet/Kconfig | 10 +++++----- drivers/input/tablet/aiptek.c | 2 ++ 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index e29a057cbea2..17b2f49675d8 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -405,6 +405,9 @@ #define USB_VENDOR_ID_YEALINK 0x6993 #define USB_DEVICE_ID_YEALINK_P1K_P4K_B2K 0xb001 +#define USB_VENDOR_ID_KYE 0x0458 +#define USB_DEVICE_ID_KYE_GPEN_560 0x5003 + /* * Alphabetically sorted blacklist by quirk type. */ @@ -703,6 +706,7 @@ static const struct hid_blacklist { { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_63, HID_QUIRK_IGNORE }, { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_64, HID_QUIRK_IGNORE }, { USB_VENDOR_ID_NATIONAL_SEMICONDUCTOR, USB_DEVICE_ID_N_S_HARMONY, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_GPEN_560, HID_QUIRK_IGNORE }, { 0, 0 } }; diff --git a/drivers/input/tablet/Kconfig b/drivers/input/tablet/Kconfig index d371c0bdc0bd..effb49ea24aa 100644 --- a/drivers/input/tablet/Kconfig +++ b/drivers/input/tablet/Kconfig @@ -25,14 +25,14 @@ config TABLET_USB_ACECAD module will be called acecad. config TABLET_USB_AIPTEK - tristate "Aiptek 6000U/8000U tablet support (USB)" + tristate "Aiptek 6000U/8000U and Genius G_PEN tablet support (USB)" depends on USB_ARCH_HAS_HCD select USB help - Say Y here if you want to use the USB version of the Aiptek 6000U - or Aiptek 8000U tablet. Make sure to say Y to "Mouse support" - (CONFIG_INPUT_MOUSEDEV) and/or "Event interface support" - (CONFIG_INPUT_EVDEV) as well. + Say Y here if you want to use the USB version of the Aiptek 6000U, + Aiptek 8000U or Genius G-PEN 560 tablet. Make sure to say Y to + "Mouse support" (CONFIG_INPUT_MOUSEDEV) and/or "Event interface + support" (CONFIG_INPUT_EVDEV) as well. To compile this driver as a module, choose M here: the module will be called aiptek. diff --git a/drivers/input/tablet/aiptek.c b/drivers/input/tablet/aiptek.c index 94683f58c9e1..1d759f6f8076 100644 --- a/drivers/input/tablet/aiptek.c +++ b/drivers/input/tablet/aiptek.c @@ -184,6 +184,7 @@ */ #define USB_VENDOR_ID_AIPTEK 0x08ca +#define USB_VENDOR_ID_KYE 0x0458 #define USB_REQ_GET_REPORT 0x01 #define USB_REQ_SET_REPORT 0x09 @@ -832,6 +833,7 @@ static const struct usb_device_id aiptek_ids[] = { {USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x22)}, {USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x23)}, {USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x24)}, + {USB_DEVICE(USB_VENDOR_ID_KYE, 0x5003)}, {} }; -- cgit v1.2.3-70-g09d2 From 3e24e2b5ae03394d9510530f9dd973050fd18730 Mon Sep 17 00:00:00 2001 From: Martin Kebert Date: Mon, 10 Mar 2008 13:40:36 +0100 Subject: Input: add Zhen Hua driver This is a driver for Zhen Hua PPM-4CH RC transmitter (commonly used in cheap Ready To Fly RC helicopters by Walkera) which using "Zhen Hua 5-byte protocol" for using them as a four axis joystick via serial port. Transmitter connected to serial port (19200 8N1) sending periodically 5 bytes where first byte is for synchronization and next four bytes are values of axis. Signed-off-by: Martin Kebert Signed-off-by: Jiri Kosina Signed-off-by: Dmitry Torokhov --- drivers/input/joystick/Kconfig | 12 ++ drivers/input/joystick/Makefile | 1 + drivers/input/joystick/zhenhua.c | 243 +++++++++++++++++++++++++++++++++++++++ include/linux/serio.h | 1 + 4 files changed, 257 insertions(+) create mode 100644 drivers/input/joystick/zhenhua.c diff --git a/drivers/input/joystick/Kconfig b/drivers/input/joystick/Kconfig index 7c662ee594a3..be5c14a5a0a4 100644 --- a/drivers/input/joystick/Kconfig +++ b/drivers/input/joystick/Kconfig @@ -193,6 +193,18 @@ config JOYSTICK_TWIDJOY To compile this driver as a module, choose M here: the module will be called twidjoy. +config JOYSTICK_ZHENHUA + tristate "5-byte Zhenhua RC transmitter" + select SERIO + help + Say Y here if you have a Zhen Hua PPM-4CH transmitter which is + supplied with a ready to fly micro electric indoor helicopters + such as EasyCopter, Lama, MiniCopter, DragonFly or Jabo and want + to use it via serial cable as a joystick. + + To compile this driver as a module, choose M here: the + module will be called zhenhua. + config JOYSTICK_DB9 tristate "Multisystem, Sega Genesis, Saturn joysticks and gamepads" depends on PARPORT diff --git a/drivers/input/joystick/Makefile b/drivers/input/joystick/Makefile index e855abb0cc51..868b746e757a 100644 --- a/drivers/input/joystick/Makefile +++ b/drivers/input/joystick/Makefile @@ -27,5 +27,6 @@ obj-$(CONFIG_JOYSTICK_TURBOGRAFX) += turbografx.o obj-$(CONFIG_JOYSTICK_TWIDJOY) += twidjoy.o obj-$(CONFIG_JOYSTICK_WARRIOR) += warrior.o obj-$(CONFIG_JOYSTICK_XPAD) += xpad.o +obj-$(CONFIG_JOYSTICK_ZHENHUA) += zhenhua.o obj-$(CONFIG_JOYSTICK_IFORCE) += iforce/ diff --git a/drivers/input/joystick/zhenhua.c b/drivers/input/joystick/zhenhua.c new file mode 100644 index 000000000000..b5853125c898 --- /dev/null +++ b/drivers/input/joystick/zhenhua.c @@ -0,0 +1,243 @@ +/* + * derived from "twidjoy.c" + * + * Copyright (c) 2008 Martin Kebert + * Copyright (c) 2001 Arndt Schoenewald + * Copyright (c) 2000-2001 Vojtech Pavlik + * Copyright (c) 2000 Mark Fletcher + * + */ + +/* + * Driver to use 4CH RC transmitter using Zhen Hua 5-byte protocol (Walkera Lama, + * EasyCopter etc.) as a joystick under Linux. + * + * RC transmitters using Zhen Hua 5-byte protocol are cheap four channels + * transmitters for control a RC planes or RC helicopters with possibility to + * connect on a serial port. + * Data coming from transmitter is in this order: + * 1. byte = synchronisation byte + * 2. byte = X axis + * 3. byte = Y axis + * 4. byte = RZ axis + * 5. byte = Z axis + * (and this is repeated) + * + * For questions or feedback regarding this driver module please contact: + * Martin Kebert - but I am not a C-programmer nor kernel + * coder :-( + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include + +#define DRIVER_DESC "RC transmitter with 5-byte Zhen Hua protocol joystick driver" + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +/* + * Constants. + */ + +#define ZHENHUA_MAX_LENGTH 5 + +/* + * Zhen Hua data. + */ + +struct zhenhua { + struct input_dev *dev; + int idx; + unsigned char data[ZHENHUA_MAX_LENGTH]; + char phys[32]; +}; + + +/* bits in all incoming bytes needs to be "reversed" */ +static int zhenhua_bitreverse(int x) +{ + x = ((x & 0xaa) >> 1) | ((x & 0x55) << 1); + x = ((x & 0xcc) >> 2) | ((x & 0x33) << 2); + x = ((x & 0xf0) >> 4) | ((x & 0x0f) << 4); + return x; +} + +/* + * zhenhua_process_packet() decodes packets the driver receives from the + * RC transmitter. It updates the data accordingly. + */ + +static void zhenhua_process_packet(struct zhenhua *zhenhua) +{ + struct input_dev *dev = zhenhua->dev; + unsigned char *data = zhenhua->data; + + input_report_abs(dev, ABS_Y, data[1]); + input_report_abs(dev, ABS_X, data[2]); + input_report_abs(dev, ABS_RZ, data[3]); + input_report_abs(dev, ABS_Z, data[4]); + + input_sync(dev); +} + +/* + * zhenhua_interrupt() is called by the low level driver when characters + * are ready for us. We then buffer them for further processing, or call the + * packet processing routine. + */ + +static irqreturn_t zhenhua_interrupt(struct serio *serio, unsigned char data, unsigned int flags) +{ + struct zhenhua *zhenhua = serio_get_drvdata(serio); + + /* All Zhen Hua packets are 5 bytes. The fact that the first byte + * is allways 0xf7 and all others are in range 0x32 - 0xc8 (50-200) + * can be used to check and regain sync. */ + + if (data == 0xef) + zhenhua->idx = 0; /* this byte starts a new packet */ + else if (zhenhua->idx == 0) + return IRQ_HANDLED; /* wrong MSB -- ignore this byte */ + + if (zhenhua->idx < ZHENHUA_MAX_LENGTH) + zhenhua->data[zhenhua->idx++] = zhenhua_bitreverse(data); + + if (zhenhua->idx == ZHENHUA_MAX_LENGTH) { + zhenhua_process_packet(zhenhua); + zhenhua->idx = 0; + } + + return IRQ_HANDLED; +} + +/* + * zhenhua_disconnect() is the opposite of zhenhua_connect() + */ + +static void zhenhua_disconnect(struct serio *serio) +{ + struct zhenhua *zhenhua = serio_get_drvdata(serio); + + serio_close(serio); + serio_set_drvdata(serio, NULL); + input_unregister_device(zhenhua->dev); + kfree(zhenhua); +} + +/* + * zhenhua_connect() is the routine that is called when someone adds a + * new serio device. It looks for the Twiddler, and if found, registers + * it as an input device. + */ + +static int zhenhua_connect(struct serio *serio, struct serio_driver *drv) +{ + struct zhenhua *zhenhua; + struct input_dev *input_dev; + int err = -ENOMEM; + + zhenhua = kzalloc(sizeof(struct zhenhua), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!zhenhua || !input_dev) + goto fail1; + + zhenhua->dev = input_dev; + snprintf(zhenhua->phys, sizeof(zhenhua->phys), "%s/input0", serio->phys); + + input_dev->name = "Zhen Hua 5-byte device"; + input_dev->phys = zhenhua->phys; + input_dev->id.bustype = BUS_RS232; + input_dev->id.vendor = SERIO_ZHENHUA; + input_dev->id.product = 0x0001; + input_dev->id.version = 0x0100; + input_dev->dev.parent = &serio->dev; + + input_dev->evbit[0] = BIT(EV_ABS); + input_set_abs_params(input_dev, ABS_X, 50, 200, 0, 0); + input_set_abs_params(input_dev, ABS_Y, 50, 200, 0, 0); + input_set_abs_params(input_dev, ABS_Z, 50, 200, 0, 0); + input_set_abs_params(input_dev, ABS_RZ, 50, 200, 0, 0); + + serio_set_drvdata(serio, zhenhua); + + err = serio_open(serio, drv); + if (err) + goto fail2; + + err = input_register_device(zhenhua->dev); + if (err) + goto fail3; + + return 0; + + fail3: serio_close(serio); + fail2: serio_set_drvdata(serio, NULL); + fail1: input_free_device(input_dev); + kfree(zhenhua); + return err; +} + +/* + * The serio driver structure. + */ + +static struct serio_device_id zhenhua_serio_ids[] = { + { + .type = SERIO_RS232, + .proto = SERIO_ZHENHUA, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { 0 } +}; + +MODULE_DEVICE_TABLE(serio, zhenhua_serio_ids); + +static struct serio_driver zhenhua_drv = { + .driver = { + .name = "zhenhua", + }, + .description = DRIVER_DESC, + .id_table = zhenhua_serio_ids, + .interrupt = zhenhua_interrupt, + .connect = zhenhua_connect, + .disconnect = zhenhua_disconnect, +}; + +/* + * The functions for inserting/removing us as a module. + */ + +static int __init zhenhua_init(void) +{ + return serio_register_driver(&zhenhua_drv); +} + +static void __exit zhenhua_exit(void) +{ + serio_unregister_driver(&zhenhua_drv); +} + +module_init(zhenhua_init); +module_exit(zhenhua_exit); diff --git a/include/linux/serio.h b/include/linux/serio.h index 9f3825014674..95674d97dabd 100644 --- a/include/linux/serio.h +++ b/include/linux/serio.h @@ -211,5 +211,6 @@ static inline void serio_unpin_driver(struct serio *serio) #define SERIO_TOUCHWIN 0x33 #define SERIO_TAOSEVM 0x34 #define SERIO_FUJITSU 0x35 +#define SERIO_ZHENHUA 0x36 #endif -- cgit v1.2.3-70-g09d2 From d808fbe5b404854374917bd0d1db937a0a524665 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 17 Apr 2008 09:24:39 -0400 Subject: Input: wm97xx-core - only schedule interrupt handler if not already scheduled As well as clarifying the fact that the driver can cope if a second interrupt occurs before the IRQ work is scheduled this also ensures that calls to the machine irq_enable() are balanced, making that easier to implement. Normally this is redundant due to the interrupt disabling but some unusal board configurations can trigger it. Signed-off-by: Mark Brown Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/wm97xx-core.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/input/touchscreen/wm97xx-core.c b/drivers/input/touchscreen/wm97xx-core.c index 2910999e05a0..e27b1e060b33 100644 --- a/drivers/input/touchscreen/wm97xx-core.c +++ b/drivers/input/touchscreen/wm97xx-core.c @@ -328,18 +328,18 @@ static void wm97xx_pen_irq_worker(struct work_struct *work) * * We have to disable the codec interrupt in the handler because it * can take upto 1ms to clear the interrupt source. We schedule a task - * in a work queue to do the actual interaction with the chip (it - * doesn't matter if we end up reenqueing it before it is executed - * since we don't touch the chip until it has run). The interrupt is - * then enabled again in the slow handler when the source has been - * cleared. + * in a work queue to do the actual interaction with the chip. The + * interrupt is then enabled again in the slow handler when the source + * has been cleared. */ static irqreturn_t wm97xx_pen_interrupt(int irq, void *dev_id) { struct wm97xx *wm = dev_id; - wm->mach_ops->irq_enable(wm, 0); - queue_work(wm->ts_workq, &wm->pen_event_work); + if (!work_pending(&wm->pen_event_work)) { + wm->mach_ops->irq_enable(wm, 0); + queue_work(wm->ts_workq, &wm->pen_event_work); + } return IRQ_HANDLED; } -- cgit v1.2.3-70-g09d2 From db7c10e708b9bdd1618c034591d27c33cb341222 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 17 Apr 2008 09:24:48 -0400 Subject: Input: wm97xx-core - use IRQF_SAMPLE_RANDOM The touchscreen interrupt is driven by human input which can reasonably be used to provide entropy. Signed-off-by: Mark Brown Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/wm97xx-core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/input/touchscreen/wm97xx-core.c b/drivers/input/touchscreen/wm97xx-core.c index e27b1e060b33..fec07c28281a 100644 --- a/drivers/input/touchscreen/wm97xx-core.c +++ b/drivers/input/touchscreen/wm97xx-core.c @@ -355,7 +355,8 @@ static int wm97xx_init_pen_irq(struct wm97xx *wm) * provided. */ BUG_ON(!wm->mach_ops->irq_enable); - if (request_irq(wm->pen_irq, wm97xx_pen_interrupt, IRQF_SHARED, + if (request_irq(wm->pen_irq, wm97xx_pen_interrupt, + IRQF_SHARED | IRQF_SAMPLE_RANDOM, "wm97xx-pen", wm)) { dev_err(wm->dev, "Failed to register pen down interrupt, polling"); -- cgit v1.2.3-70-g09d2 From 34d278534db050b93d79175d59a32a70ac25f9b5 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 17 Apr 2008 09:24:58 -0400 Subject: Input: wm97xx-core - support use as a wakeup source The WM97xx touch screen controllers can be used to generate a wakeup event when the system is suspended. Provide a new core API call wm97xx_set_suspend_mode() allowing machine drivers to enable this. If no suspend_mode is provided then the touch panel will be powered down when the system is suspended. Signed-off-by: Mark Brown Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/wm97xx-core.c | 39 +++++++++++++++++++++++++++++++++ include/linux/wm97xx.h | 3 +++ 2 files changed, 42 insertions(+) diff --git a/drivers/input/touchscreen/wm97xx-core.c b/drivers/input/touchscreen/wm97xx-core.c index fec07c28281a..e9c7ea46b6e3 100644 --- a/drivers/input/touchscreen/wm97xx-core.c +++ b/drivers/input/touchscreen/wm97xx-core.c @@ -261,6 +261,23 @@ void wm97xx_config_gpio(struct wm97xx *wm, u32 gpio, enum wm97xx_gpio_dir dir, } EXPORT_SYMBOL_GPL(wm97xx_config_gpio); +/* + * Configure the WM97XX_PRP value to use while system is suspended. + * If a value other than 0 is set then WM97xx pen detection will be + * left enabled in the configured mode while the system is in suspend, + * the device has users and suspend has not been disabled via the + * wakeup sysfs entries. + * + * @wm: WM97xx device to configure + * @mode: WM97XX_PRP value to configure while suspended + */ +void wm97xx_set_suspend_mode(struct wm97xx *wm, u16 mode) +{ + wm->suspend_mode = mode; + device_init_wakeup(&wm->input_dev->dev, mode != 0); +} +EXPORT_SYMBOL_GPL(wm97xx_set_suspend_mode); + /* * Handle a pen down interrupt. */ @@ -689,10 +706,32 @@ static int wm97xx_remove(struct device *dev) static int wm97xx_suspend(struct device *dev, pm_message_t state) { struct wm97xx *wm = dev_get_drvdata(dev); + u16 reg; + int suspend_mode; + + if (device_may_wakeup(&wm->input_dev->dev)) + suspend_mode = wm->suspend_mode; + else + suspend_mode = 0; if (wm->input_dev->users) cancel_delayed_work_sync(&wm->ts_reader); + /* Power down the digitiser (bypassing the cache for resume) */ + reg = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER2); + reg &= ~WM97XX_PRP_DET_DIG; + if (wm->input_dev->users) + reg |= suspend_mode; + wm->ac97->bus->ops->write(wm->ac97, AC97_WM97XX_DIGITISER2, reg); + + /* WM9713 has an additional power bit - turn it off if there + * are no users or if suspend mode is zero. */ + if (wm->id == WM9713_ID2 && + (!wm->input_dev->users || !suspend_mode)) { + reg = wm97xx_reg_read(wm, AC97_EXTENDED_MID) | 0x8000; + wm97xx_reg_write(wm, AC97_EXTENDED_MID, reg); + } + return 0; } diff --git a/include/linux/wm97xx.h b/include/linux/wm97xx.h index ed01c7df54a3..4d13732e9cf0 100644 --- a/include/linux/wm97xx.h +++ b/include/linux/wm97xx.h @@ -282,6 +282,7 @@ struct wm97xx { unsigned pen_is_down:1; /* Pen is down */ unsigned aux_waiting:1; /* aux measurement waiting */ unsigned pen_probably_down:1; /* used in polling mode */ + u16 suspend_mode; /* PRP in suspend mode */ }; /* @@ -297,6 +298,8 @@ void wm97xx_config_gpio(struct wm97xx *wm, u32 gpio, enum wm97xx_gpio_sticky sticky, enum wm97xx_gpio_wake wake); +void wm97xx_set_suspend_mode(struct wm97xx *wm, u16 mode); + /* codec AC97 IO access */ int wm97xx_reg_read(struct wm97xx *wm, u16 reg); void wm97xx_reg_write(struct wm97xx *wm, u16 reg, u16 val); -- cgit v1.2.3-70-g09d2 From 81e329cdddd63d66e2b3c3dc51d429ba074cdbb8 Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Mon, 10 Mar 2008 13:43:05 +0100 Subject: Input: fix ordering in joystick Makefile Make entries in drivers/input/joystick/Makefile properly alphabetically ordered. Signed-off-by: Jiri Kosina Signed-off-by: Dmitry Torokhov --- drivers/input/joystick/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/joystick/Makefile b/drivers/input/joystick/Makefile index 868b746e757a..fdbf8c4c2876 100644 --- a/drivers/input/joystick/Makefile +++ b/drivers/input/joystick/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_JOYSTICK_GF2K) += gf2k.o obj-$(CONFIG_JOYSTICK_GRIP) += grip.o obj-$(CONFIG_JOYSTICK_GRIP_MP) += grip_mp.o obj-$(CONFIG_JOYSTICK_GUILLEMOT) += guillemot.o +obj-$(CONFIG_JOYSTICK_IFORCE) += iforce/ obj-$(CONFIG_JOYSTICK_INTERACT) += interact.o obj-$(CONFIG_JOYSTICK_JOYDUMP) += joydump.o obj-$(CONFIG_JOYSTICK_MAGELLAN) += magellan.o @@ -29,4 +30,3 @@ obj-$(CONFIG_JOYSTICK_WARRIOR) += warrior.o obj-$(CONFIG_JOYSTICK_XPAD) += xpad.o obj-$(CONFIG_JOYSTICK_ZHENHUA) += zhenhua.o -obj-$(CONFIG_JOYSTICK_IFORCE) += iforce/ -- cgit v1.2.3-70-g09d2 From b39b04403bba4f807ee6e57ae2f4407187588fcd Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Thu, 17 Apr 2008 09:28:25 -0400 Subject: Input: drivers/char/keyboard.c - use time_after The functions time_before, time_before_eq, time_after, and time_after_eq are more robust for comparing jiffies against other values. Signed-off-by: Julia Lawall Signed-off-by: Andrew Morton Signed-off-by: Dmitry Torokhov --- drivers/char/keyboard.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/char/keyboard.c b/drivers/char/keyboard.c index 59608e341385..45806d27579a 100644 --- a/drivers/char/keyboard.c +++ b/drivers/char/keyboard.c @@ -42,6 +42,7 @@ #include #include #include +#include extern void ctrl_alt_del(void); @@ -928,7 +929,8 @@ static void k_brl(struct vc_data *vc, unsigned char value, char up_flag) if (up_flag) { if (brl_timeout) { if (!committing || - jiffies - releasestart > (brl_timeout * HZ) / 1000) { + time_after(jiffies, + releasestart + msecs_to_jiffies(brl_timeout))) { committing = pressed; releasestart = jiffies; } -- cgit v1.2.3-70-g09d2 From d7b5247bbcfba2bc96d4b3dec9086a4f1a31363b Mon Sep 17 00:00:00 2001 From: Kay Sievers Date: Fri, 18 Apr 2008 00:24:42 -0400 Subject: Input: add MODULE_ALIAS() to hotpluggable platform modules Since 43cc71eed1250755986da4c0f9898f9a635cb3bf, the platform modalias is prefixed with "platform:". Add MODULE_ALIAS() to the hotpluggable "input" platform drivers, to re-enable auto loading. [dbrownell@users.sourceforge.net: more drivers, registration fixes] Signed-off-by: Kay Sievers Signed-off-by: David Brownell Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/aaed2000_kbd.c | 4 ++++ drivers/input/keyboard/bf54x-keys.c | 2 ++ drivers/input/keyboard/corgikbd.c | 2 ++ drivers/input/keyboard/gpio_keys.c | 2 ++ drivers/input/keyboard/jornada680_kbd.c | 2 ++ drivers/input/keyboard/jornada720_kbd.c | 4 ++++ drivers/input/keyboard/omap-keypad.c | 2 ++ drivers/input/keyboard/pxa27x_keypad.c | 4 ++++ drivers/input/keyboard/spitzkbd.c | 1 + drivers/input/keyboard/tosakbd.c | 2 ++ drivers/input/misc/cobalt_btns.c | 3 +++ drivers/input/mouse/gpio_mouse.c | 4 ++++ drivers/input/serio/rpckbd.c | 2 ++ drivers/input/touchscreen/corgi_ts.c | 2 ++ drivers/input/touchscreen/jornada720_ts.c | 4 ++++ 15 files changed, 40 insertions(+) diff --git a/drivers/input/keyboard/aaed2000_kbd.c b/drivers/input/keyboard/aaed2000_kbd.c index 72abc196ce66..a293e8b3f508 100644 --- a/drivers/input/keyboard/aaed2000_kbd.c +++ b/drivers/input/keyboard/aaed2000_kbd.c @@ -156,11 +156,15 @@ static int __devexit aaedkbd_remove(struct platform_device *pdev) return 0; } +/* work with hotplug and coldplug */ +MODULE_ALIAS("platform:aaed2000-keyboard"); + static struct platform_driver aaedkbd_driver = { .probe = aaedkbd_probe, .remove = __devexit_p(aaedkbd_remove), .driver = { .name = "aaed2000-keyboard", + .owner = THIS_MODULE, }, }; diff --git a/drivers/input/keyboard/bf54x-keys.c b/drivers/input/keyboard/bf54x-keys.c index 05e3494cf8b8..d87ac3322a6d 100644 --- a/drivers/input/keyboard/bf54x-keys.c +++ b/drivers/input/keyboard/bf54x-keys.c @@ -359,6 +359,7 @@ struct platform_driver bfin_kpad_device_driver = { .remove = __devexit_p(bfin_kpad_remove), .driver = { .name = DRV_NAME, + .owner = THIS_MODULE, } }; @@ -378,3 +379,4 @@ module_exit(bfin_kpad_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Michael Hennerich "); MODULE_DESCRIPTION("Keypad driver for BF54x Processors"); +MODULE_ALIAS("platform:bf54x-keys"); diff --git a/drivers/input/keyboard/corgikbd.c b/drivers/input/keyboard/corgikbd.c index 790fed368aae..5187c0c7a222 100644 --- a/drivers/input/keyboard/corgikbd.c +++ b/drivers/input/keyboard/corgikbd.c @@ -392,6 +392,7 @@ static struct platform_driver corgikbd_driver = { .resume = corgikbd_resume, .driver = { .name = "corgi-keyboard", + .owner = THIS_MODULE, }, }; @@ -411,3 +412,4 @@ module_exit(corgikbd_exit); MODULE_AUTHOR("Richard Purdie "); MODULE_DESCRIPTION("Corgi Keyboard Driver"); MODULE_LICENSE("GPLv2"); +MODULE_ALIAS("platform:corgi-keyboard"); diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c index a54dc15f9005..bbd00c3fe98c 100644 --- a/drivers/input/keyboard/gpio_keys.c +++ b/drivers/input/keyboard/gpio_keys.c @@ -214,6 +214,7 @@ struct platform_driver gpio_keys_device_driver = { .resume = gpio_keys_resume, .driver = { .name = "gpio-keys", + .owner = THIS_MODULE, } }; @@ -233,3 +234,4 @@ module_exit(gpio_keys_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Phil Blundell "); MODULE_DESCRIPTION("Keyboard driver for CPU GPIOs"); +MODULE_ALIAS("platform:gpio-keys"); diff --git a/drivers/input/keyboard/jornada680_kbd.c b/drivers/input/keyboard/jornada680_kbd.c index a23633a2e1b4..9387da343f97 100644 --- a/drivers/input/keyboard/jornada680_kbd.c +++ b/drivers/input/keyboard/jornada680_kbd.c @@ -254,6 +254,7 @@ static int __devexit jornada680kbd_remove(struct platform_device *pdev) static struct platform_driver jornada680kbd_driver = { .driver = { .name = "jornada680_kbd", + .owner = THIS_MODULE, }, .probe = jornada680kbd_probe, .remove = __devexit_p(jornada680kbd_remove), @@ -275,3 +276,4 @@ module_exit(jornada680kbd_exit); MODULE_AUTHOR("Kristoffer Ericson "); MODULE_DESCRIPTION("HP Jornada 620/660/680/690 Keyboard Driver"); MODULE_LICENSE("GPLv2"); +MODULE_ALIAS("platform:jornada680_kbd"); diff --git a/drivers/input/keyboard/jornada720_kbd.c b/drivers/input/keyboard/jornada720_kbd.c index 986f93cfc6b8..a1164a0c7736 100644 --- a/drivers/input/keyboard/jornada720_kbd.c +++ b/drivers/input/keyboard/jornada720_kbd.c @@ -162,9 +162,13 @@ static int __devexit jornada720_kbd_remove(struct platform_device *pdev) return 0; } +/* work with hotplug and coldplug */ +MODULE_ALIAS("platform:jornada720_kbd"); + static struct platform_driver jornada720_kbd_driver = { .driver = { .name = "jornada720_kbd", + .owner = THIS_MODULE, }, .probe = jornada720_kbd_probe, .remove = __devexit_p(jornada720_kbd_remove), diff --git a/drivers/input/keyboard/omap-keypad.c b/drivers/input/keyboard/omap-keypad.c index eec328167f8d..10afd2068068 100644 --- a/drivers/input/keyboard/omap-keypad.c +++ b/drivers/input/keyboard/omap-keypad.c @@ -467,6 +467,7 @@ static struct platform_driver omap_kp_driver = { .resume = omap_kp_resume, .driver = { .name = "omap-keypad", + .owner = THIS_MODULE, }, }; @@ -487,3 +488,4 @@ module_exit(omap_kp_exit); MODULE_AUTHOR("Timo Teräs"); MODULE_DESCRIPTION("OMAP Keypad Driver"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:omap-keypad"); diff --git a/drivers/input/keyboard/pxa27x_keypad.c b/drivers/input/keyboard/pxa27x_keypad.c index 4e651c11c1da..3dea0c5077a9 100644 --- a/drivers/input/keyboard/pxa27x_keypad.c +++ b/drivers/input/keyboard/pxa27x_keypad.c @@ -545,6 +545,9 @@ static int __devexit pxa27x_keypad_remove(struct platform_device *pdev) return 0; } +/* work with hotplug and coldplug */ +MODULE_ALIAS("platform:pxa27x-keypad"); + static struct platform_driver pxa27x_keypad_driver = { .probe = pxa27x_keypad_probe, .remove = __devexit_p(pxa27x_keypad_remove), @@ -552,6 +555,7 @@ static struct platform_driver pxa27x_keypad_driver = { .resume = pxa27x_keypad_resume, .driver = { .name = "pxa27x-keypad", + .owner = THIS_MODULE, }, }; diff --git a/drivers/input/keyboard/spitzkbd.c b/drivers/input/keyboard/spitzkbd.c index 1d59a2dc3c17..92102f9e4b8b 100644 --- a/drivers/input/keyboard/spitzkbd.c +++ b/drivers/input/keyboard/spitzkbd.c @@ -494,3 +494,4 @@ module_exit(spitzkbd_exit); MODULE_AUTHOR("Richard Purdie "); MODULE_DESCRIPTION("Spitz Keyboard Driver"); MODULE_LICENSE("GPLv2"); +MODULE_ALIAS("platform:spitz-keyboard"); diff --git a/drivers/input/keyboard/tosakbd.c b/drivers/input/keyboard/tosakbd.c index a247006757de..94e444b4ee15 100644 --- a/drivers/input/keyboard/tosakbd.c +++ b/drivers/input/keyboard/tosakbd.c @@ -409,6 +409,7 @@ static struct platform_driver tosakbd_driver = { .resume = tosakbd_resume, .driver = { .name = "tosa-keyboard", + .owner = THIS_MODULE, }, }; @@ -428,3 +429,4 @@ module_exit(tosakbd_exit); MODULE_AUTHOR("Dirk Opfer "); MODULE_DESCRIPTION("Tosa Keyboard Driver"); MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:tosa-keyboard"); diff --git a/drivers/input/misc/cobalt_btns.c b/drivers/input/misc/cobalt_btns.c index 5511ef006a66..6a1f48b76e32 100644 --- a/drivers/input/misc/cobalt_btns.c +++ b/drivers/input/misc/cobalt_btns.c @@ -148,6 +148,9 @@ static int __devexit cobalt_buttons_remove(struct platform_device *pdev) return 0; } +/* work with hotplug and coldplug */ +MODULE_ALIAS("platform:Cobalt buttons"); + static struct platform_driver cobalt_buttons_driver = { .probe = cobalt_buttons_probe, .remove = __devexit_p(cobalt_buttons_remove), diff --git a/drivers/input/mouse/gpio_mouse.c b/drivers/input/mouse/gpio_mouse.c index 0936d6ba015c..339290184871 100644 --- a/drivers/input/mouse/gpio_mouse.c +++ b/drivers/input/mouse/gpio_mouse.c @@ -171,10 +171,14 @@ static int __devexit gpio_mouse_remove(struct platform_device *pdev) return 0; } +/* work with hotplug and coldplug */ +MODULE_ALIAS("platform:gpio_mouse"); + struct platform_driver gpio_mouse_device_driver = { .remove = __devexit_p(gpio_mouse_remove), .driver = { .name = "gpio_mouse", + .owner = THIS_MODULE, } }; diff --git a/drivers/input/serio/rpckbd.c b/drivers/input/serio/rpckbd.c index 49f84315cb32..34c59d9c6205 100644 --- a/drivers/input/serio/rpckbd.c +++ b/drivers/input/serio/rpckbd.c @@ -45,6 +45,7 @@ MODULE_AUTHOR("Vojtech Pavlik, Russell King"); MODULE_DESCRIPTION("Acorn RiscPC PS/2 keyboard controller driver"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:kart"); static int rpckbd_write(struct serio *port, unsigned char val) { @@ -140,6 +141,7 @@ static struct platform_driver rpckbd_driver = { .remove = __devexit_p(rpckbd_remove), .driver = { .name = "kart", + .owner = THIS_MODULE, }, }; diff --git a/drivers/input/touchscreen/corgi_ts.c b/drivers/input/touchscreen/corgi_ts.c index 99d92f5c93d6..765e964b7969 100644 --- a/drivers/input/touchscreen/corgi_ts.c +++ b/drivers/input/touchscreen/corgi_ts.c @@ -361,6 +361,7 @@ static struct platform_driver corgits_driver = { .resume = corgits_resume, .driver = { .name = "corgi-ts", + .owner = THIS_MODULE, }, }; @@ -380,3 +381,4 @@ module_exit(corgits_exit); MODULE_AUTHOR("Richard Purdie "); MODULE_DESCRIPTION("Corgi TouchScreen Driver"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:corgi-ts"); diff --git a/drivers/input/touchscreen/jornada720_ts.c b/drivers/input/touchscreen/jornada720_ts.c index 42a1c9a1940e..742242111bf1 100644 --- a/drivers/input/touchscreen/jornada720_ts.c +++ b/drivers/input/touchscreen/jornada720_ts.c @@ -160,11 +160,15 @@ static int __devexit jornada720_ts_remove(struct platform_device *pdev) return 0; } +/* work with hotplug and coldplug */ +MODULE_ALIAS("platform:jornada_ts"); + static struct platform_driver jornada720_ts_driver = { .probe = jornada720_ts_probe, .remove = __devexit_p(jornada720_ts_remove), .driver = { .name = "jornada_ts", + .owner = THIS_MODULE, }, }; -- cgit v1.2.3-70-g09d2 From d0478d0ad7a58f36afa03e57afe14955c2943466 Mon Sep 17 00:00:00 2001 From: Michael Hennerich Date: Fri, 18 Apr 2008 00:25:00 -0400 Subject: Input: bf54x-keys - add infrastructure for keypad wakeups Signed-off-by: Michael Hennerich Signed-off-by: Bryan Wu Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/bf54x-keys.c | 35 ++++++++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/drivers/input/keyboard/bf54x-keys.c b/drivers/input/keyboard/bf54x-keys.c index d87ac3322a6d..54ed8e2e1c02 100644 --- a/drivers/input/keyboard/bf54x-keys.c +++ b/drivers/input/keyboard/bf54x-keys.c @@ -312,6 +312,8 @@ static int __devinit bfin_kpad_probe(struct platform_device *pdev) bfin_write_KPAD_CTL(bfin_read_KPAD_CTL() | KPAD_EN); + device_init_wakeup(&pdev->dev, 1); + printk(KERN_ERR DRV_NAME ": Blackfin BF54x Keypad registered IRQ %d\n", bf54x_kpad->irq); @@ -354,13 +356,40 @@ static int __devexit bfin_kpad_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM +static int bfin_kpad_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct bf54x_kpad *bf54x_kpad = platform_get_drvdata(pdev); + + if (device_may_wakeup(&pdev->dev)) + enable_irq_wake(bf54x_kpad->irq); + + return 0; +} + +static int bfin_kpad_resume(struct platform_device *pdev) +{ + struct bf54x_kpad *bf54x_kpad = platform_get_drvdata(pdev); + + if (device_may_wakeup(&pdev->dev)) + disable_irq_wake(bf54x_kpad->irq); + + return 0; +} +#else +# define bfin_kpad_suspend NULL +# define bfin_kpad_resume NULL +#endif + struct platform_driver bfin_kpad_device_driver = { - .probe = bfin_kpad_probe, - .remove = __devexit_p(bfin_kpad_remove), .driver = { .name = DRV_NAME, .owner = THIS_MODULE, - } + }, + .probe = bfin_kpad_probe, + .remove = __devexit_p(bfin_kpad_remove), + .suspend = bfin_kpad_suspend, + .resume = bfin_kpad_resume, }; static int __init bfin_kpad_init(void) -- cgit v1.2.3-70-g09d2 From 8c6deb9c8fd29feaeae3aae500608beac777ea9e Mon Sep 17 00:00:00 2001 From: Roel Kluin <12o3l@tiscali.nl> Date: Fri, 18 Apr 2008 00:25:18 -0400 Subject: Input: i8042 - fix incorrect usage of strncpy and strncat Fix incorrect length argument for strncpy and strncat by replacing them with strlcpy and strlcat Signed-off-by: Roel Kluin <12o3l@tiscali.nl> Signed-off-by: Dmitry Torokhov --- drivers/input/serio/i8042-x86ia64io.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h index 60931aceb828..5ece9f56babc 100644 --- a/drivers/input/serio/i8042-x86ia64io.h +++ b/drivers/input/serio/i8042-x86ia64io.h @@ -370,10 +370,10 @@ static int i8042_pnp_kbd_probe(struct pnp_dev *dev, const struct pnp_device_id * if (pnp_irq_valid(dev,0)) i8042_pnp_kbd_irq = pnp_irq(dev, 0); - strncpy(i8042_pnp_kbd_name, did->id, sizeof(i8042_pnp_kbd_name)); + strlcpy(i8042_pnp_kbd_name, did->id, sizeof(i8042_pnp_kbd_name)); if (strlen(pnp_dev_name(dev))) { - strncat(i8042_pnp_kbd_name, ":", sizeof(i8042_pnp_kbd_name)); - strncat(i8042_pnp_kbd_name, pnp_dev_name(dev), sizeof(i8042_pnp_kbd_name)); + strlcat(i8042_pnp_kbd_name, ":", sizeof(i8042_pnp_kbd_name)); + strlcat(i8042_pnp_kbd_name, pnp_dev_name(dev), sizeof(i8042_pnp_kbd_name)); } i8042_pnp_kbd_devices++; @@ -391,10 +391,10 @@ static int i8042_pnp_aux_probe(struct pnp_dev *dev, const struct pnp_device_id * if (pnp_irq_valid(dev, 0)) i8042_pnp_aux_irq = pnp_irq(dev, 0); - strncpy(i8042_pnp_aux_name, did->id, sizeof(i8042_pnp_aux_name)); + strlcpy(i8042_pnp_aux_name, did->id, sizeof(i8042_pnp_aux_name)); if (strlen(pnp_dev_name(dev))) { - strncat(i8042_pnp_aux_name, ":", sizeof(i8042_pnp_aux_name)); - strncat(i8042_pnp_aux_name, pnp_dev_name(dev), sizeof(i8042_pnp_aux_name)); + strlcat(i8042_pnp_aux_name, ":", sizeof(i8042_pnp_aux_name)); + strlcat(i8042_pnp_aux_name, pnp_dev_name(dev), sizeof(i8042_pnp_aux_name)); } i8042_pnp_aux_devices++; -- cgit v1.2.3-70-g09d2 From 8fd76c4506817a93718fab0d6b3a55b9becc9f2c Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Fri, 18 Apr 2008 00:25:38 -0400 Subject: Input: mac_hid - add lockdep annotation to emumousebtn The mouse button emulation calls input device methods from an input device. This causes funny lock nesting which is harmless as each device has its own locks. Give the nesting device its own lock classes so that lockdep will not consider them the same. Signed-off-by: Peter Zijlstra Signed-off-by: Dmitry Torokhov --- drivers/macintosh/mac_hid.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/macintosh/mac_hid.c b/drivers/macintosh/mac_hid.c index 89302309da92..f972ff377b63 100644 --- a/drivers/macintosh/mac_hid.c +++ b/drivers/macintosh/mac_hid.c @@ -103,6 +103,9 @@ int mac_hid_mouse_emulate_buttons(int caller, unsigned int keycode, int down) return 0; } +static struct lock_class_key emumousebtn_event_class; +static struct lock_class_key emumousebtn_mutex_class; + static int emumousebtn_input_register(void) { int ret; @@ -111,6 +114,9 @@ static int emumousebtn_input_register(void) if (!emumousebtn) return -ENOMEM; + lockdep_set_class(emumousebtn->event_lock, &emumousebtn_event_class); + lockdep_set_class(emumousebtn->mutex, &emumousebtn_mutex_class); + emumousebtn->name = "Macintosh mouse button emulation"; emumousebtn->id.bustype = BUS_ADB; emumousebtn->id.vendor = 0x0001; -- cgit v1.2.3-70-g09d2 From a22b4b2f408f7958ffb3a9e62defc5168db1e15e Mon Sep 17 00:00:00 2001 From: Hans-Christian Egtvedt Date: Mon, 21 Apr 2008 10:00:46 -0400 Subject: Input: at32psif - update MODULE_AUTHOR with new email Signed-off-by: Hans-Christian Egtvedt Signed-off-by: Dmitry Torokhov --- drivers/input/serio/at32psif.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/serio/at32psif.c b/drivers/input/serio/at32psif.c index c267588f7308..41fda8c67b1e 100644 --- a/drivers/input/serio/at32psif.c +++ b/drivers/input/serio/at32psif.c @@ -370,6 +370,6 @@ static void __exit psif_exit(void) module_init(psif_init); module_exit(psif_exit); -MODULE_AUTHOR("Hans-Christian Egtvedt "); +MODULE_AUTHOR("Hans-Christian Egtvedt "); MODULE_DESCRIPTION("Atmel AVR32 PSIF PS/2 driver"); MODULE_LICENSE("GPL"); -- cgit v1.2.3-70-g09d2 From 48bdce4a2e0b1d3be6ed6da14d25adfe9385d2dc Mon Sep 17 00:00:00 2001 From: Vernon Sauder Date: Mon, 21 Apr 2008 12:13:21 -0400 Subject: Input: ucb1400_ts - IRQ probe fix The UCB1400 driver IRQ probe code fails to find an interrupt if all the interrupts in the range 0-31 are nonprobe-able. This patch removes the check of the return value so interrupts above 31 can be detected. Tested on InHand Fingertip4 PXA270 board. Signed-off-by: Vernon Sauder Acked-by: Nicolas Pitre Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/ucb1400_ts.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/input/touchscreen/ucb1400_ts.c b/drivers/input/touchscreen/ucb1400_ts.c index 607f9933aa1f..bce018e45bce 100644 --- a/drivers/input/touchscreen/ucb1400_ts.c +++ b/drivers/input/touchscreen/ucb1400_ts.c @@ -427,10 +427,6 @@ static int ucb1400_detect_irq(struct ucb1400 *ucb) unsigned long mask, timeout; mask = probe_irq_on(); - if (!mask) { - probe_irq_off(mask); - return -EBUSY; - } /* Enable the ADC interrupt. */ ucb1400_reg_write(ucb, UCB_IE_RIS, UCB_IE_ADC); -- cgit v1.2.3-70-g09d2 From b345dc7da026016b65162b1ca7cfcd2c7212a285 Mon Sep 17 00:00:00 2001 From: Ping Cheng Date: Thu, 24 Apr 2008 23:34:05 -0400 Subject: Input: wacom - add support for Cintiq 20WSX Signed-off-by: Ping Cheng Signed-off-by: Dmitry Torokhov --- drivers/input/tablet/wacom_wac.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/input/tablet/wacom_wac.c index ffe33842143f..192513e1f04c 100644 --- a/drivers/input/tablet/wacom_wac.c +++ b/drivers/input/tablet/wacom_wac.c @@ -649,6 +649,7 @@ static struct wacom_features wacom_features[] = { { "Wacom Intuos3 6x11", 10, 54204, 31750, 1023, 63, INTUOS3 }, { "Wacom Intuos3 4x6", 10, 31496, 19685, 1023, 63, INTUOS3S }, { "Wacom Cintiq 21UX", 10, 87200, 65600, 1023, 63, CINTIQ }, + { "Wacom Cintiq 20WSX", 10, 86680, 54180, 1023, 63, WACOM_BEE }, { "Wacom Cintiq 12WX", 10, 53020, 33440, 1023, 63, WACOM_BEE }, { "Wacom Intuos2 6x8", 10, 20320, 16240, 1023, 31, INTUOS }, { } @@ -702,6 +703,7 @@ static struct usb_device_id wacom_ids[] = { { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB5) }, { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB7) }, { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x3F) }, + { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xC5) }, { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xC6) }, { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x47) }, { } -- cgit v1.2.3-70-g09d2