From 2072f8db625cbdaba839fe7bb1b607d06884e685 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Sat, 28 Aug 2010 22:00:05 -0700 Subject: Input: wacom_w8001 - send BTN_TOOL_PEN/RUBBER and BTN_STYLUS events The protocol used by the w8001 supports status fields for tip, side switch and eraser as well as a RDY field for proximity. The protocol has a double usage for the f2 bit in the packet. If set, the data is either pen + side2 button or eraser. Assume eraser if the device comes into proximity with the f2 bit set, otherwise trigger the side2 button. If the device comes into proximity with the f2 bit and that bit disappears afterwards, fake proximity out for the eraser and proximity in for the pen. Signed-off-by: Peter Hutterer Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/wacom_w8001.c | 54 +++++++++++++++++++++++++++++---- 1 file changed, 48 insertions(+), 6 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/wacom_w8001.c b/drivers/input/touchscreen/wacom_w8001.c index 56dc35c94bb1..bc642f1be376 100644 --- a/drivers/input/touchscreen/wacom_w8001.c +++ b/drivers/input/touchscreen/wacom_w8001.c @@ -62,6 +62,7 @@ struct w8001 { unsigned char response[W8001_MAX_LENGTH]; unsigned char data[W8001_MAX_LENGTH]; char phys[32]; + int type; }; static void parse_data(u8 *data, struct w8001_coord *coord) @@ -88,11 +89,52 @@ static void parse_data(u8 *data, struct w8001_coord *coord) coord->tilt_y = data[8] & 0x7F; } +static void report_pen_events(struct w8001 *w8001, struct w8001_coord *coord) +{ + struct input_dev *dev = w8001->dev; + + /* + * We have 1 bit for proximity (rdy) and 3 bits for tip, side, + * side2/eraser. If rdy && f2 are set, this can be either pen + side2, + * or eraser. assume + * - if dev is already in proximity and f2 is toggled → pen + side2 + * - if dev comes into proximity with f2 set → eraser + * If f2 disappears after assuming eraser, fake proximity out for + * eraser and in for pen. + */ + + if (!w8001->type) { + w8001->type = coord->f2 ? BTN_TOOL_RUBBER : BTN_TOOL_PEN; + } else if (w8001->type == BTN_TOOL_RUBBER) { + if (!coord->f2) { + input_report_abs(dev, ABS_PRESSURE, 0); + input_report_key(dev, BTN_TOUCH, 0); + input_report_key(dev, BTN_STYLUS, 0); + input_report_key(dev, BTN_STYLUS2, 0); + input_report_key(dev, BTN_TOOL_RUBBER, 0); + input_sync(dev); + w8001->type = BTN_TOOL_PEN; + } + } else { + input_report_key(dev, BTN_STYLUS2, coord->f2); + } + + input_report_abs(dev, ABS_X, coord->x); + input_report_abs(dev, ABS_Y, coord->y); + input_report_abs(dev, ABS_PRESSURE, coord->pen_pressure); + input_report_key(dev, BTN_TOUCH, coord->tsw); + input_report_key(dev, BTN_STYLUS, coord->f1); + input_report_key(dev, w8001->type, coord->rdy); + input_sync(dev); + + if (!coord->rdy) + w8001->type = 0; +} + static irqreturn_t w8001_interrupt(struct serio *serio, unsigned char data, unsigned int flags) { struct w8001 *w8001 = serio_get_drvdata(serio); - struct input_dev *dev = w8001->dev; struct w8001_coord coord; unsigned char tmp; @@ -112,11 +154,7 @@ static irqreturn_t w8001_interrupt(struct serio *serio, w8001->idx = 0; parse_data(w8001->data, &coord); - input_report_abs(dev, ABS_X, coord.x); - input_report_abs(dev, ABS_Y, coord.y); - input_report_abs(dev, ABS_PRESSURE, coord.pen_pressure); - input_report_key(dev, BTN_TOUCH, coord.tsw); - input_sync(dev); + report_pen_events(w8001, &coord); break; case 10: @@ -221,6 +259,10 @@ static int w8001_connect(struct serio *serio, struct serio_driver *drv) input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + input_dev->keybit[BIT_WORD(BTN_TOOL_PEN)] |= BIT_MASK(BTN_TOOL_PEN); + input_dev->keybit[BIT_WORD(BTN_TOOL_RUBBER)] |= BIT_MASK(BTN_TOOL_RUBBER); + input_dev->keybit[BIT_WORD(BTN_STYLUS)] |= BIT_MASK(BTN_STYLUS); + input_dev->keybit[BIT_WORD(BTN_STYLUS2)] |= BIT_MASK(BTN_STYLUS2); serio_set_drvdata(serio, w8001); err = serio_open(serio, drv); -- cgit v1.2.2 From aaba933eeb8d7f804508bc74baa58656240107f8 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Sat, 28 Aug 2010 22:00:47 -0700 Subject: Input: wacom_w8001 - support (and ignore) touch tablets Tablets that support touch input may report different sized packages, depending on the touch sensor in the tablet. For now, discard the packages until we report them as touch input proper. Signed-off-by: Peter Hutterer Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/wacom_w8001.c | 88 ++++++++++++++++++++++++++++++++- 1 file changed, 86 insertions(+), 2 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/wacom_w8001.c b/drivers/input/touchscreen/wacom_w8001.c index bc642f1be376..89abda06db74 100644 --- a/drivers/input/touchscreen/wacom_w8001.c +++ b/drivers/input/touchscreen/wacom_w8001.c @@ -2,6 +2,7 @@ * Wacom W8001 penabled serial touchscreen driver * * Copyright (c) 2008 Jaya Kumar + * Copyright (c) 2010 Red Hat, Inc. * * This file is subject to the terms and conditions of the GNU General Public * License. See the file COPYING in the main directory of this archive for @@ -30,11 +31,22 @@ MODULE_LICENSE("GPL"); #define W8001_LEAD_BYTE 0x80 #define W8001_TAB_MASK 0x40 #define W8001_TAB_BYTE 0x40 +/* set in first byte of touch data packets */ +#define W8001_TOUCH_MASK (0x10 | W8001_LEAD_MASK) +#define W8001_TOUCH_BYTE (0x10 | W8001_LEAD_BYTE) #define W8001_QUERY_PACKET 0x20 #define W8001_CMD_START '1' #define W8001_CMD_QUERY '*' +#define W8001_CMD_TOUCHQUERY '%' + +/* length of data packets in bytes, depends on device. */ +#define W8001_PKTLEN_TOUCH93 5 +#define W8001_PKTLEN_TOUCH9A 7 +#define W8001_PKTLEN_TPCPEN 9 +#define W8001_PKTLEN_TPCCTL 11 /* control packet */ +#define W8001_PKTLEN_TOUCH2FG 13 struct w8001_coord { u8 rdy; @@ -48,6 +60,15 @@ struct w8001_coord { u8 tilt_y; }; +/* touch query reply packet */ +struct w8001_touch_query { + u8 panel_res; + u8 capacity_res; + u8 sensor_id; + u16 x; + u16 y; +}; + /* * Per-touchscreen data. */ @@ -63,6 +84,7 @@ struct w8001 { unsigned char data[W8001_MAX_LENGTH]; char phys[32]; int type; + unsigned int pktlen; }; static void parse_data(u8 *data, struct w8001_coord *coord) @@ -89,6 +111,23 @@ static void parse_data(u8 *data, struct w8001_coord *coord) coord->tilt_y = data[8] & 0x7F; } +static void parse_touchquery(u8 *data, struct w8001_touch_query *query) +{ + memset(query, 0, sizeof(*query)); + + query->panel_res = data[1]; + query->sensor_id = data[2] & 0x7; + query->capacity_res = data[7]; + + query->x = data[3] << 9; + query->x |= data[4] << 2; + query->x |= (data[2] >> 5) & 0x3; + + query->y = data[5] << 9; + query->y |= data[6] << 2; + query->y |= (data[2] >> 3) & 0x3; +} + static void report_pen_events(struct w8001 *w8001, struct w8001_coord *coord) { struct input_dev *dev = w8001->dev; @@ -147,22 +186,45 @@ static irqreturn_t w8001_interrupt(struct serio *serio, } break; - case 8: + case W8001_PKTLEN_TOUCH93 - 1: + case W8001_PKTLEN_TOUCH9A - 1: + /* ignore one-finger touch packet. */ + if (w8001->pktlen == w8001->idx) + w8001->idx = 0; + break; + + /* Pen coordinates packet */ + case W8001_PKTLEN_TPCPEN - 1: tmp = w8001->data[0] & W8001_TAB_MASK; if (unlikely(tmp == W8001_TAB_BYTE)) break; + tmp = (w8001->data[0] & W8001_TOUCH_BYTE); + if (tmp == W8001_TOUCH_BYTE) + break; + w8001->idx = 0; parse_data(w8001->data, &coord); report_pen_events(w8001, &coord); break; - case 10: + /* control packet */ + case W8001_PKTLEN_TPCCTL - 1: + tmp = (w8001->data[0] & W8001_TOUCH_MASK); + if (tmp == W8001_TOUCH_BYTE) + break; + w8001->idx = 0; memcpy(w8001->response, w8001->data, W8001_MAX_LENGTH); w8001->response_type = W8001_QUERY_PACKET; complete(&w8001->cmd_done); break; + + case W8001_PKTLEN_TOUCH2FG - 1: + /* ignore two-finger touch packet. */ + if (w8001->pktlen == w8001->idx) + w8001->idx = 0; + break; } return IRQ_HANDLED; @@ -205,6 +267,28 @@ static int w8001_setup(struct w8001 *w8001) input_set_abs_params(dev, ABS_TILT_X, 0, coord.tilt_x, 0, 0); input_set_abs_params(dev, ABS_TILT_Y, 0, coord.tilt_y, 0, 0); + error = w8001_command(w8001, W8001_CMD_TOUCHQUERY, true); + if (!error) { + struct w8001_touch_query touch; + + parse_touchquery(w8001->response, &touch); + + switch (touch.sensor_id) { + case 0: + case 2: + w8001->pktlen = W8001_PKTLEN_TOUCH93; + break; + case 1: + case 3: + case 4: + w8001->pktlen = W8001_PKTLEN_TOUCH9A; + break; + case 5: + w8001->pktlen = W8001_PKTLEN_TOUCH2FG; + break; + } + } + return w8001_command(w8001, W8001_CMD_START, false); } -- cgit v1.2.2 From 5e8b9140f306ce30e7c56c568198720514efc872 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Sat, 28 Aug 2010 22:00:47 -0700 Subject: Input: wacom_w8001 - add multitouch slot support Some serial wacom devices support two-finger touch. Test for this during init and parse the touch packets accordingly. Touch packets are processed using Protocol B (MT Slots). Note: there are several wacom versions that do touch but not two-finger touch. These are not catered for here, touch events for these are simply discarded. Signed-off-by: Peter Hutterer Acked-by: Henrik Rydberg Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/wacom_w8001.c | 49 +++++++++++++++++++++++++++++++-- 1 file changed, 46 insertions(+), 3 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/wacom_w8001.c b/drivers/input/touchscreen/wacom_w8001.c index 89abda06db74..9ae4c7b16ba7 100644 --- a/drivers/input/touchscreen/wacom_w8001.c +++ b/drivers/input/touchscreen/wacom_w8001.c @@ -48,6 +48,8 @@ MODULE_LICENSE("GPL"); #define W8001_PKTLEN_TPCCTL 11 /* control packet */ #define W8001_PKTLEN_TOUCH2FG 13 +#define MAX_TRACKING_ID 0xFF /* arbitrarily chosen */ + struct w8001_coord { u8 rdy; u8 tsw; @@ -85,6 +87,7 @@ struct w8001 { char phys[32]; int type; unsigned int pktlen; + int trkid[2]; }; static void parse_data(u8 *data, struct w8001_coord *coord) @@ -111,6 +114,35 @@ static void parse_data(u8 *data, struct w8001_coord *coord) coord->tilt_y = data[8] & 0x7F; } +static void parse_touch(struct w8001 *w8001) +{ + static int trkid; + struct input_dev *dev = w8001->dev; + unsigned char *data = w8001->data; + int i; + + for (i = 0; i < 2; i++) { + input_mt_slot(dev, i); + + if (data[0] & (1 << i)) { + int x = (data[6 * i + 1] << 7) | (data[6 * i + 2]); + int y = (data[6 * i + 3] << 7) | (data[6 * i + 4]); + /* data[5,6] and [11,12] is finger capacity */ + + input_report_abs(dev, ABS_MT_POSITION_X, x); + input_report_abs(dev, ABS_MT_POSITION_Y, y); + input_report_abs(dev, ABS_MT_TOOL_TYPE, MT_TOOL_FINGER); + if (w8001->trkid[i] < 0) + w8001->trkid[i] = trkid++ & MAX_TRACKING_ID; + } else { + w8001->trkid[i] = -1; + } + input_report_abs(dev, ABS_MT_TRACKING_ID, w8001->trkid[i]); + } + + input_sync(dev); +} + static void parse_touchquery(u8 *data, struct w8001_touch_query *query) { memset(query, 0, sizeof(*query)); @@ -220,10 +252,10 @@ static irqreturn_t w8001_interrupt(struct serio *serio, complete(&w8001->cmd_done); break; + /* 2 finger touch packet */ case W8001_PKTLEN_TOUCH2FG - 1: - /* ignore two-finger touch packet. */ - if (w8001->pktlen == w8001->idx) - w8001->idx = 0; + w8001->idx = 0; + parse_touch(w8001); break; } @@ -285,6 +317,16 @@ static int w8001_setup(struct w8001 *w8001) break; case 5: w8001->pktlen = W8001_PKTLEN_TOUCH2FG; + + input_mt_create_slots(dev, 2); + input_set_abs_params(dev, ABS_MT_TRACKING_ID, + 0, MAX_TRACKING_ID, 0, 0); + input_set_abs_params(dev, ABS_MT_POSITION_X, + 0, touch.x, 0, 0); + input_set_abs_params(dev, ABS_MT_POSITION_Y, + 0, touch.y, 0, 0); + input_set_abs_params(dev, ABS_MT_TOOL_TYPE, + 0, 0, 0, 0); break; } } @@ -330,6 +372,7 @@ static int w8001_connect(struct serio *serio, struct serio_driver *drv) w8001->serio = serio; w8001->id = serio->id.id; w8001->dev = input_dev; + w8001->trkid[0] = w8001->trkid[1] = -1; init_completion(&w8001->cmd_done); snprintf(w8001->phys, sizeof(w8001->phys), "%s/input0", serio->phys); -- cgit v1.2.2 From 3045a5f5202a1e0ab6ba2bf90a786cf4cae6932a Mon Sep 17 00:00:00 2001 From: Kevin Wells Date: Sat, 28 Aug 2010 22:45:22 -0700 Subject: Input: add LPC32xx touchscreen controller driver This patch set introduces support for the LPC32xx touchscreen controller driver. The LPC32xx touchscreen controller supports automated event detection and X/Y data conversion for resistive touchscreens. Signed-off-by: Kevin Wells Signed-off-by: Durgesh Pattamatta Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/Kconfig | 10 + drivers/input/touchscreen/Makefile | 1 + drivers/input/touchscreen/lpc32xx_ts.c | 411 +++++++++++++++++++++++++++++++++ 3 files changed, 422 insertions(+) create mode 100644 drivers/input/touchscreen/lpc32xx_ts.c (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 0069d9703fda..400c99de0bb3 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -214,6 +214,16 @@ config TOUCHSCREEN_WACOM_W8001 To compile this driver as a module, choose M here: the module will be called wacom_w8001. +config TOUCHSCREEN_LPC32XX + tristate "LPC32XX touchscreen controller" + depends on ARCH_LPC32XX + help + Say Y here if you have a LPC32XX device and want + to support the built-in touchscreen. + + To compile this driver as a module, choose M here: the + module will be called lpc32xx_ts. + config TOUCHSCREEN_MCS5000 tristate "MELFAS MCS-5000 touchscreen" depends on I2C diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 28217e1dcafd..22e2d598dc69 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -23,6 +23,7 @@ obj-$(CONFIG_TOUCHSCREEN_EETI) += eeti_ts.o obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o obj-$(CONFIG_TOUCHSCREEN_INEXIO) += inexio.o +obj-$(CONFIG_TOUCHSCREEN_LPC32XX) += lpc32xx_ts.o obj-$(CONFIG_TOUCHSCREEN_MC13783) += mc13783_ts.o obj-$(CONFIG_TOUCHSCREEN_MCS5000) += mcs5000_ts.o obj-$(CONFIG_TOUCHSCREEN_MIGOR) += migor_ts.o diff --git a/drivers/input/touchscreen/lpc32xx_ts.c b/drivers/input/touchscreen/lpc32xx_ts.c new file mode 100644 index 000000000000..dcf803f5a1f7 --- /dev/null +++ b/drivers/input/touchscreen/lpc32xx_ts.c @@ -0,0 +1,411 @@ +/* + * LPC32xx built-in touchscreen driver + * + * Copyright (C) 2010 NXP Semiconductors + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Touchscreen controller register offsets + */ +#define LPC32XX_TSC_STAT 0x00 +#define LPC32XX_TSC_SEL 0x04 +#define LPC32XX_TSC_CON 0x08 +#define LPC32XX_TSC_FIFO 0x0C +#define LPC32XX_TSC_DTR 0x10 +#define LPC32XX_TSC_RTR 0x14 +#define LPC32XX_TSC_UTR 0x18 +#define LPC32XX_TSC_TTR 0x1C +#define LPC32XX_TSC_DXP 0x20 +#define LPC32XX_TSC_MIN_X 0x24 +#define LPC32XX_TSC_MAX_X 0x28 +#define LPC32XX_TSC_MIN_Y 0x2C +#define LPC32XX_TSC_MAX_Y 0x30 +#define LPC32XX_TSC_AUX_UTR 0x34 +#define LPC32XX_TSC_AUX_MIN 0x38 +#define LPC32XX_TSC_AUX_MAX 0x3C + +#define LPC32XX_TSC_STAT_FIFO_OVRRN (1 << 8) +#define LPC32XX_TSC_STAT_FIFO_EMPTY (1 << 7) + +#define LPC32XX_TSC_SEL_DEFVAL 0x0284 + +#define LPC32XX_TSC_ADCCON_IRQ_TO_FIFO_4 (0x1 << 11) +#define LPC32XX_TSC_ADCCON_X_SAMPLE_SIZE(s) ((10 - (s)) << 7) +#define LPC32XX_TSC_ADCCON_Y_SAMPLE_SIZE(s) ((10 - (s)) << 4) +#define LPC32XX_TSC_ADCCON_POWER_UP (1 << 2) +#define LPC32XX_TSC_ADCCON_AUTO_EN (1 << 0) + +#define LPC32XX_TSC_FIFO_TS_P_LEVEL (1 << 31) +#define LPC32XX_TSC_FIFO_NORMALIZE_X_VAL(x) (((x) & 0x03FF0000) >> 16) +#define LPC32XX_TSC_FIFO_NORMALIZE_Y_VAL(y) ((y) & 0x000003FF) + +#define LPC32XX_TSC_ADCDAT_VALUE_MASK 0x000003FF + +#define LPC32XX_TSC_MIN_XY_VAL 0x0 +#define LPC32XX_TSC_MAX_XY_VAL 0x3FF + +#define MOD_NAME "ts-lpc32xx" + +#define tsc_readl(dev, reg) \ + __raw_readl((dev)->tsc_base + (reg)) +#define tsc_writel(dev, reg, val) \ + __raw_writel((val), (dev)->tsc_base + (reg)) + +struct lpc32xx_tsc { + struct input_dev *dev; + void __iomem *tsc_base; + int irq; + struct clk *clk; +}; + +static void lpc32xx_fifo_clear(struct lpc32xx_tsc *tsc) +{ + while (!(tsc_readl(tsc, LPC32XX_TSC_STAT) & + LPC32XX_TSC_STAT_FIFO_EMPTY)) + tsc_readl(tsc, LPC32XX_TSC_FIFO); +} + +static irqreturn_t lpc32xx_ts_interrupt(int irq, void *dev_id) +{ + u32 tmp, rv[4], xs[4], ys[4]; + int idx; + struct lpc32xx_tsc *tsc = dev_id; + struct input_dev *input = tsc->dev; + + tmp = tsc_readl(tsc, LPC32XX_TSC_STAT); + + if (tmp & LPC32XX_TSC_STAT_FIFO_OVRRN) { + /* FIFO overflow - throw away samples */ + lpc32xx_fifo_clear(tsc); + return IRQ_HANDLED; + } + + /* + * Gather and normalize 4 samples. Pen-up events may have less + * than 4 samples, but its ok to pop 4 and let the last sample + * pen status check drop the samples. + */ + idx = 0; + while (idx < 4 && + !(tsc_readl(tsc, LPC32XX_TSC_STAT) & + LPC32XX_TSC_STAT_FIFO_EMPTY)) { + tmp = tsc_readl(tsc, LPC32XX_TSC_FIFO); + xs[idx] = LPC32XX_TSC_ADCDAT_VALUE_MASK - + LPC32XX_TSC_FIFO_NORMALIZE_X_VAL(tmp); + ys[idx] = LPC32XX_TSC_ADCDAT_VALUE_MASK - + LPC32XX_TSC_FIFO_NORMALIZE_Y_VAL(tmp); + rv[idx] = tmp; + idx++; + } + + /* Data is only valid if pen is still down in last sample */ + if (!(rv[3] & LPC32XX_TSC_FIFO_TS_P_LEVEL) && idx == 4) { + /* Use average of 2nd and 3rd sample for position */ + input_report_abs(input, ABS_X, (xs[1] + xs[2]) / 2); + input_report_abs(input, ABS_Y, (ys[1] + ys[2]) / 2); + input_report_key(input, BTN_TOUCH, 1); + } else { + input_report_key(input, BTN_TOUCH, 0); + } + + input_sync(input); + + return IRQ_HANDLED; +} + +static void lpc32xx_stop_tsc(struct lpc32xx_tsc *tsc) +{ + /* Disable auto mode */ + tsc_writel(tsc, LPC32XX_TSC_CON, + tsc_readl(tsc, LPC32XX_TSC_CON) & + ~LPC32XX_TSC_ADCCON_AUTO_EN); + + clk_disable(tsc->clk); +} + +static void lpc32xx_setup_tsc(struct lpc32xx_tsc *tsc) +{ + u32 tmp; + + clk_enable(tsc->clk); + + tmp = tsc_readl(tsc, LPC32XX_TSC_CON) & ~LPC32XX_TSC_ADCCON_POWER_UP; + + /* Set the TSC FIFO depth to 4 samples @ 10-bits per sample (max) */ + tmp = LPC32XX_TSC_ADCCON_IRQ_TO_FIFO_4 | + LPC32XX_TSC_ADCCON_X_SAMPLE_SIZE(10) | + LPC32XX_TSC_ADCCON_Y_SAMPLE_SIZE(10); + tsc_writel(tsc, LPC32XX_TSC_CON, tmp); + + /* These values are all preset */ + tsc_writel(tsc, LPC32XX_TSC_SEL, LPC32XX_TSC_SEL_DEFVAL); + tsc_writel(tsc, LPC32XX_TSC_MIN_X, LPC32XX_TSC_MIN_XY_VAL); + tsc_writel(tsc, LPC32XX_TSC_MAX_X, LPC32XX_TSC_MAX_XY_VAL); + tsc_writel(tsc, LPC32XX_TSC_MIN_Y, LPC32XX_TSC_MIN_XY_VAL); + tsc_writel(tsc, LPC32XX_TSC_MAX_Y, LPC32XX_TSC_MAX_XY_VAL); + + /* Aux support is not used */ + tsc_writel(tsc, LPC32XX_TSC_AUX_UTR, 0); + tsc_writel(tsc, LPC32XX_TSC_AUX_MIN, 0); + tsc_writel(tsc, LPC32XX_TSC_AUX_MAX, 0); + + /* + * Set sample rate to about 240Hz per X/Y pair. A single measurement + * consists of 4 pairs which gives about a 60Hz sample rate based on + * a stable 32768Hz clock source. Values are in clocks. + * Rate is (32768 / (RTR + XCONV + RTR + YCONV + DXP + TTR + UTR) / 4 + */ + tsc_writel(tsc, LPC32XX_TSC_RTR, 0x2); + tsc_writel(tsc, LPC32XX_TSC_DTR, 0x2); + tsc_writel(tsc, LPC32XX_TSC_TTR, 0x10); + tsc_writel(tsc, LPC32XX_TSC_DXP, 0x4); + tsc_writel(tsc, LPC32XX_TSC_UTR, 88); + + lpc32xx_fifo_clear(tsc); + + /* Enable automatic ts event capture */ + tsc_writel(tsc, LPC32XX_TSC_CON, tmp | LPC32XX_TSC_ADCCON_AUTO_EN); +} + +static int lpc32xx_ts_open(struct input_dev *dev) +{ + struct lpc32xx_tsc *tsc = input_get_drvdata(dev); + + lpc32xx_setup_tsc(tsc); + + return 0; +} + +static void lpc32xx_ts_close(struct input_dev *dev) +{ + struct lpc32xx_tsc *tsc = input_get_drvdata(dev); + + lpc32xx_stop_tsc(tsc); +} + +static int __devinit lpc32xx_ts_probe(struct platform_device *pdev) +{ + struct lpc32xx_tsc *tsc; + struct input_dev *input; + struct resource *res; + resource_size_t size; + int irq; + int error; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "Can't get memory resource\n"); + return -ENOENT; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "Can't get interrupt resource\n"); + return irq; + } + + tsc = kzalloc(sizeof(*tsc), GFP_KERNEL); + input = input_allocate_device(); + if (!tsc || !input) { + dev_err(&pdev->dev, "failed allocating memory\n"); + error = -ENOMEM; + goto err_free_mem; + } + + tsc->dev = input; + tsc->irq = irq; + + size = resource_size(res); + + if (!request_mem_region(res->start, size, pdev->name)) { + dev_err(&pdev->dev, "TSC registers are not free\n"); + error = -EBUSY; + goto err_free_mem; + } + + tsc->tsc_base = ioremap(res->start, size); + if (!tsc->tsc_base) { + dev_err(&pdev->dev, "Can't map memory\n"); + error = -ENOMEM; + goto err_release_mem; + } + + tsc->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(tsc->clk)) { + dev_err(&pdev->dev, "failed getting clock\n"); + error = PTR_ERR(tsc->clk); + goto err_unmap; + } + + input->name = MOD_NAME; + input->phys = "lpc32xx/input0"; + input->id.bustype = BUS_HOST; + input->id.vendor = 0x0001; + input->id.product = 0x0002; + input->id.version = 0x0100; + input->dev.parent = &pdev->dev; + input->open = lpc32xx_ts_open; + input->close = lpc32xx_ts_close; + + input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + input_set_abs_params(input, ABS_X, LPC32XX_TSC_MIN_XY_VAL, + LPC32XX_TSC_MAX_XY_VAL, 0, 0); + input_set_abs_params(input, ABS_Y, LPC32XX_TSC_MIN_XY_VAL, + LPC32XX_TSC_MAX_XY_VAL, 0, 0); + + input_set_drvdata(input, tsc); + + error = request_irq(tsc->irq, lpc32xx_ts_interrupt, + IRQF_DISABLED, pdev->name, tsc); + if (error) { + dev_err(&pdev->dev, "failed requesting interrupt\n"); + goto err_put_clock; + } + + error = input_register_device(input); + if (error) { + dev_err(&pdev->dev, "failed registering input device\n"); + goto err_free_irq; + } + + platform_set_drvdata(pdev, tsc); + device_init_wakeup(&pdev->dev, 1); + + return 0; + +err_free_irq: + free_irq(tsc->irq, tsc); +err_put_clock: + clk_put(tsc->clk); +err_unmap: + iounmap(tsc->tsc_base); +err_release_mem: + release_mem_region(res->start, size); +err_free_mem: + input_free_device(input); + kfree(tsc); + + return error; +} + +static int __devexit lpc32xx_ts_remove(struct platform_device *pdev) +{ + struct lpc32xx_tsc *tsc = platform_get_drvdata(pdev); + struct resource *res; + + device_init_wakeup(&pdev->dev, 0); + free_irq(tsc->irq, tsc); + + input_unregister_device(tsc->dev); + + clk_put(tsc->clk); + + iounmap(tsc->tsc_base); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(res->start, resource_size(res)); + + kfree(tsc); + + return 0; +} + +#ifdef CONFIG_PM +static int lpc32xx_ts_suspend(struct device *dev) +{ + struct lpc32xx_tsc *tsc = dev_get_drvdata(dev); + struct input_dev *input = tsc->dev; + + /* + * Suspend and resume can be called when the device hasn't been + * enabled. If there are no users that have the device open, then + * avoid calling the TSC stop and start functions as the TSC + * isn't yet clocked. + */ + mutex_lock(&input->mutex); + + if (input->users) { + if (device_may_wakeup(dev)) + enable_irq_wake(tsc->irq); + else + lpc32xx_stop_tsc(tsc); + } + + mutex_unlock(&input->mutex); + + return 0; +} + +static int lpc32xx_ts_resume(struct device *dev) +{ + struct lpc32xx_tsc *tsc = dev_get_drvdata(dev); + struct input_dev *input = tsc->dev; + + mutex_lock(&input->mutex); + + if (input->users) { + if (device_may_wakeup(dev)) + disable_irq_wake(tsc->irq); + else + lpc32xx_setup_tsc(tsc); + } + + mutex_unlock(&input->mutex); + + return 0; +} + +static const struct dev_pm_ops lpc32xx_ts_pm_ops = { + .suspend = lpc32xx_ts_suspend, + .resume = lpc32xx_ts_resume, +}; +#define LPC32XX_TS_PM_OPS (&lpc32xx_ts_pm_ops) +#else +#define LPC32XX_TS_PM_OPS NULL +#endif + +static struct platform_driver lpc32xx_ts_driver = { + .probe = lpc32xx_ts_probe, + .remove = __devexit_p(lpc32xx_ts_remove), + .driver = { + .name = MOD_NAME, + .owner = THIS_MODULE, + .pm = LPC32XX_TS_PM_OPS, + }, +}; + +static int __init lpc32xx_ts_init(void) +{ + return platform_driver_register(&lpc32xx_ts_driver); +} +module_init(lpc32xx_ts_init); + +static void __exit lpc32xx_ts_exit(void) +{ + platform_driver_unregister(&lpc32xx_ts_driver); +} +module_exit(lpc32xx_ts_exit); + +MODULE_AUTHOR("Kevin Wells Date: Tue, 31 Aug 2010 17:05:27 -0700 Subject: Input: add support for OMAP4 keyboard controller OMAP4 keyboard controller includes: - built-in scanning algorithm - debouncing feature Driver implementation is based on matrix_keypad.c Signed-off-by: Syed Rafiuddin Signed-off-by: Abraham Arce Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/Kconfig | 9 ++ drivers/input/keyboard/Makefile | 1 + drivers/input/keyboard/omap4-keypad.c | 287 ++++++++++++++++++++++++++++++++++ 3 files changed, 297 insertions(+) create mode 100644 drivers/input/keyboard/omap4-keypad.c (limited to 'drivers/input') diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index 9cc488d21490..4f300488e1f3 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -424,6 +424,15 @@ config KEYBOARD_OMAP To compile this driver as a module, choose M here: the module will be called omap-keypad. +config KEYBOARD_OMAP4 + tristate "TI OMAP4 keypad support" + depends on ARCH_OMAP4 + help + Say Y here if you want to use the OMAP4 keypad. + + To compile this driver as a module, choose M here: the + module will be called omap4-keypad. + config KEYBOARD_TWL4030 tristate "TI TWL4030/TWL5030/TPS659x0 keypad support" depends on TWL4030_CORE diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index 504b591be0cd..8ac01fb642c1 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -29,6 +29,7 @@ obj-$(CONFIG_KEYBOARD_MAX7359) += max7359_keypad.o obj-$(CONFIG_KEYBOARD_MCS) += mcs_touchkey.o obj-$(CONFIG_KEYBOARD_NEWTON) += newtonkbd.o obj-$(CONFIG_KEYBOARD_OMAP) += omap-keypad.o +obj-$(CONFIG_KEYBOARD_OMAP4) += omap4-keypad.o obj-$(CONFIG_KEYBOARD_OPENCORES) += opencores-kbd.o obj-$(CONFIG_KEYBOARD_PXA27x) += pxa27x_keypad.o obj-$(CONFIG_KEYBOARD_PXA930_ROTARY) += pxa930_rotary.o diff --git a/drivers/input/keyboard/omap4-keypad.c b/drivers/input/keyboard/omap4-keypad.c new file mode 100644 index 000000000000..975f8bbcc69b --- /dev/null +++ b/drivers/input/keyboard/omap4-keypad.c @@ -0,0 +1,287 @@ +/* + * OMAP4 Keypad Driver + * + * Copyright (C) 2010 Texas Instruments + * + * Author: Abraham Arce + * Initial Code: Syed Rafiuddin + * + * 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 +#include +#include + +#include + +/* OMAP4 registers */ +#define OMAP4_KBD_REVISION 0x00 +#define OMAP4_KBD_SYSCONFIG 0x10 +#define OMAP4_KBD_SYSSTATUS 0x14 +#define OMAP4_KBD_IRQSTATUS 0x18 +#define OMAP4_KBD_IRQENABLE 0x1C +#define OMAP4_KBD_WAKEUPENABLE 0x20 +#define OMAP4_KBD_PENDING 0x24 +#define OMAP4_KBD_CTRL 0x28 +#define OMAP4_KBD_DEBOUNCINGTIME 0x2C +#define OMAP4_KBD_LONGKEYTIME 0x30 +#define OMAP4_KBD_TIMEOUT 0x34 +#define OMAP4_KBD_STATEMACHINE 0x38 +#define OMAP4_KBD_ROWINPUTS 0x3C +#define OMAP4_KBD_COLUMNOUTPUTS 0x40 +#define OMAP4_KBD_FULLCODE31_0 0x44 +#define OMAP4_KBD_FULLCODE63_32 0x48 + +/* OMAP4 bit definitions */ +#define OMAP4_DEF_SYSCONFIG_SOFTRST (1 << 1) +#define OMAP4_DEF_SYSCONFIG_ENAWKUP (1 << 2) +#define OMAP4_DEF_IRQENABLE_EVENTEN (1 << 0) +#define OMAP4_DEF_IRQENABLE_LONGKEY (1 << 1) +#define OMAP4_DEF_IRQENABLE_TIMEOUTEN (1 << 2) +#define OMAP4_DEF_CTRL_NOSOFTMODE (1 << 1) +#define OMAP4_DEF_CTRLPTVVALUE (1 << 2) +#define OMAP4_DEF_CTRLPTV (1 << 1) +#define OMAP4_DEF_IRQDISABLE 0x00 + +/* OMAP4 values */ +#define OMAP4_VAL_DEBOUNCINGTIME 0x07 +#define OMAP4_VAL_FUNCTIONALCFG 0x1E + +#define OMAP4_MASK_IRQSTATUSDISABLE 0xFFFF + +struct omap4_keypad { + struct input_dev *input; + + void __iomem *base; + int irq; + + unsigned int rows; + unsigned int cols; + unsigned int row_shift; + unsigned char key_state[8]; + unsigned short keymap[]; +}; + +static void __devinit omap4_keypad_config(struct omap4_keypad *keypad_data) +{ + __raw_writel(OMAP4_DEF_SYSCONFIG_SOFTRST | OMAP4_DEF_SYSCONFIG_ENAWKUP, + keypad_data->base + OMAP4_KBD_SYSCONFIG); + __raw_writel(OMAP4_VAL_FUNCTIONALCFG, + keypad_data->base + OMAP4_KBD_CTRL); + __raw_writel(OMAP4_VAL_DEBOUNCINGTIME, + keypad_data->base + OMAP4_KBD_DEBOUNCINGTIME); + __raw_writel(OMAP4_DEF_IRQDISABLE, + keypad_data->base + OMAP4_KBD_IRQSTATUS); + __raw_writel(OMAP4_DEF_IRQENABLE_EVENTEN | OMAP4_DEF_IRQENABLE_LONGKEY, + keypad_data->base + OMAP4_KBD_IRQENABLE); +} + +/* Interrupt handler */ +static irqreturn_t omap4_keypad_interrupt(int irq, void *dev_id) +{ + struct omap4_keypad *keypad_data = dev_id; + struct input_dev *input_dev = keypad_data->input; + unsigned char key_state[ARRAY_SIZE(keypad_data->key_state)]; + unsigned int col, row, code, changed; + u32 *new_state = (u32 *) key_state; + + /* Disable interrupts */ + __raw_writel(OMAP4_DEF_IRQDISABLE, + keypad_data->base + OMAP4_KBD_IRQENABLE); + + *new_state = __raw_readl(keypad_data->base + OMAP4_KBD_FULLCODE31_0); + *(new_state + 1) = __raw_readl(keypad_data->base + OMAP4_KBD_FULLCODE63_32); + + for (row = 0; row < keypad_data->rows; row++) { + changed = key_state[row] ^ keypad_data->key_state[row]; + if (!changed) + continue; + + for (col = 0; col < keypad_data->cols; col++) { + if (changed & (1 << col)) { + code = MATRIX_SCAN_CODE(row, col, + keypad_data->row_shift); + input_event(input_dev, EV_MSC, MSC_SCAN, code); + input_report_key(input_dev, + keypad_data->keymap[code], + key_state[row] & (1 << col)); + } + } + } + + input_sync(input_dev); + + memcpy(keypad_data->key_state, key_state, + sizeof(keypad_data->key_state)); + + /* clear pending interrupts */ + __raw_writel(__raw_readl(keypad_data->base + OMAP4_KBD_IRQSTATUS), + keypad_data->base + OMAP4_KBD_IRQSTATUS); + + /* enable interrupts */ + __raw_writel(OMAP4_DEF_IRQENABLE_EVENTEN | OMAP4_DEF_IRQENABLE_LONGKEY, + keypad_data->base + OMAP4_KBD_IRQENABLE); + + return IRQ_HANDLED; +} + +static int __devinit omap4_keypad_probe(struct platform_device *pdev) +{ + const struct omap4_keypad_platform_data *pdata; + struct omap4_keypad *keypad_data; + struct input_dev *input_dev; + unsigned int row_shift, max_keys; + int error; + + /* platform data */ + pdata = pdev->dev.platform_data; + if (!pdata) { + dev_err(&pdev->dev, "no platform data defined\n"); + return -EINVAL; + } + + if (!pdata->base) { + dev_err(&pdev->dev, "no base address specified\n"); + return -EINVAL; + } + + if (!pdata->irq) { + dev_err(&pdev->dev, "no keyboard irq assigned\n"); + return -EINVAL; + } + + if (!pdata->keymap_data) { + dev_err(&pdev->dev, "no keymap data defined\n"); + return -EINVAL; + } + + row_shift = get_count_order(pdata->cols); + max_keys = pdata->rows << row_shift; + + keypad_data = kzalloc(sizeof(struct omap4_keypad) + + max_keys * sizeof(keypad_data->keymap[0]), + GFP_KERNEL); + if (!keypad_data) { + dev_err(&pdev->dev, "keypad_data memory allocation failed\n"); + return -ENOMEM; + } + + keypad_data->base = pdata->base; + keypad_data->irq = pdata->irq; + + keypad_data->row_shift = row_shift; + keypad_data->rows = pdata->rows; + keypad_data->cols = pdata->cols; + + /* input device allocation */ + keypad_data->input = input_dev = input_allocate_device(); + if (!input_dev) { + error = -ENOMEM; + goto err_free_keypad; + } + + input_dev->name = pdev->name; + input_dev->dev.parent = &pdev->dev; + input_dev->id.bustype = BUS_HOST; + input_dev->id.vendor = 0x0001; + input_dev->id.product = 0x0001; + input_dev->id.version = 0x0001; + + input_dev->keycode = keypad_data->keymap; + input_dev->keycodesize = sizeof(keypad_data->keymap[0]); + input_dev->keycodemax = max_keys; + + __set_bit(EV_KEY, input_dev->evbit); + __set_bit(EV_REP, input_dev->evbit); + + input_set_capability(input_dev, EV_MSC, MSC_SCAN); + + input_set_drvdata(input_dev, keypad_data); + + matrix_keypad_build_keymap(pdata->keymap_data, row_shift, + input_dev->keycode, input_dev->keybit); + + omap4_keypad_config(keypad_data); + + error = request_irq(keypad_data->irq, omap4_keypad_interrupt, + IRQF_TRIGGER_FALLING, + "omap4-keypad", keypad_data); + if (error) { + dev_err(&pdev->dev, "failed to register interrupt\n"); + goto err_free_input; + } + + error = input_register_device(keypad_data->input); + if (error < 0) { + dev_err(&pdev->dev, "failed to register input device\n"); + goto err_free_irq; + } + + + platform_set_drvdata(pdev, keypad_data); + return 0; + +err_free_irq: + free_irq(keypad_data->irq, keypad_data); +err_free_input: + input_free_device(input_dev); +err_free_keypad: + kfree(keypad_data); + return error; +} + +static int __devexit omap4_keypad_remove(struct platform_device *pdev) +{ + struct omap4_keypad *keypad_data = platform_get_drvdata(pdev); + + free_irq(keypad_data->irq, keypad_data); + input_unregister_device(keypad_data->input); + kfree(keypad_data); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static struct platform_driver omap4_keypad_driver = { + .probe = omap4_keypad_probe, + .remove = __devexit_p(omap4_keypad_remove), + .driver = { + .name = "omap4-keypad", + .owner = THIS_MODULE, + }, +}; + +static int __init omap4_keypad_init(void) +{ + return platform_driver_register(&omap4_keypad_driver); +} +module_init(omap4_keypad_init); + +static void __exit omap4_keypad_exit(void) +{ + platform_driver_unregister(&omap4_keypad_driver); +} +module_exit(omap4_keypad_exit); + +MODULE_AUTHOR("Texas Instruments"); +MODULE_DESCRIPTION("OMAP4 Keypad Driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:omap4-keypad"); -- cgit v1.2.2 From d4f5f937c3dfdea9f49c3cdeea8813b0d9f7715a Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 31 Aug 2010 23:00:52 -0700 Subject: Input: mrst-touchscreen - move out of staging The driver is in reasonable shape now so let's move it out of staging. Acked-by: Alan Cox Acked-by: Greg Kroah-Hartman Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/Kconfig | 12 + drivers/input/touchscreen/Makefile | 1 + drivers/input/touchscreen/intel-mid-touch.c | 687 ++++++++++++++++++++++++++++ 3 files changed, 700 insertions(+) create mode 100644 drivers/input/touchscreen/intel-mid-touch.c (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 3b9d5e2105d7..084f1982d38d 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -237,6 +237,18 @@ config TOUCHSCREEN_INEXIO To compile this driver as a module, choose M here: the module will be called inexio. +config TOUCHSCREEN_INTEL_MID + tristate "Intel MID platform resistive touchscreen" + depends on INTEL_SCU_IPC + help + Say Y here if you have a Intel MID based touchscreen in + your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called intel_mid_touch. + config TOUCHSCREEN_MK712 tristate "ICS MicroClock MK712 touchscreen" help diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 497964a7a214..e8b5cac8d7eb 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_TOUCHSCREEN_EETI) += eeti_ts.o obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o obj-$(CONFIG_TOUCHSCREEN_INEXIO) += inexio.o +obj-$(CONFIG_TOUCHSCREEN_INTEL_MID) += intel-mid-touch.o obj-$(CONFIG_TOUCHSCREEN_MC13783) += mc13783_ts.o obj-$(CONFIG_TOUCHSCREEN_MCS5000) += mcs5000_ts.o obj-$(CONFIG_TOUCHSCREEN_MIGOR) += migor_ts.o diff --git a/drivers/input/touchscreen/intel-mid-touch.c b/drivers/input/touchscreen/intel-mid-touch.c new file mode 100644 index 000000000000..c0307b22d86f --- /dev/null +++ b/drivers/input/touchscreen/intel-mid-touch.c @@ -0,0 +1,687 @@ +/* + * Intel MID Resistive Touch Screen Driver + * + * Copyright (C) 2008 Intel Corp + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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; version 2 of the License. + * + * 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. + * + * Questions/Comments/Bug fixes to Sreedhara (sreedhara.ds@intel.com) + * Ramesh Agarwal (ramesh.agarwal@intel.com) + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * TODO: + * review conversion of r/m/w sequences + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* PMIC Interrupt registers */ +#define PMIC_REG_ID1 0x00 /* PMIC ID1 register */ + +/* PMIC Interrupt registers */ +#define PMIC_REG_INT 0x04 /* PMIC interrupt register */ +#define PMIC_REG_MINT 0x05 /* PMIC interrupt mask register */ + +/* ADC Interrupt registers */ +#define PMIC_REG_ADCINT 0x5F /* ADC interrupt register */ +#define PMIC_REG_MADCINT 0x60 /* ADC interrupt mask register */ + +/* ADC Control registers */ +#define PMIC_REG_ADCCNTL1 0x61 /* ADC control register */ + +/* ADC Channel Selection registers */ +#define PMICADDR0 0xA4 +#define END_OF_CHANNEL 0x1F + +/* ADC Result register */ +#define PMIC_REG_ADCSNS0H 0x64 + +/* ADC channels for touch screen */ +#define MRST_TS_CHAN10 0xA /* Touch screen X+ connection */ +#define MRST_TS_CHAN11 0xB /* Touch screen X- connection */ +#define MRST_TS_CHAN12 0xC /* Touch screen Y+ connection */ +#define MRST_TS_CHAN13 0xD /* Touch screen Y- connection */ + +/* Touch screen channel BIAS constants */ +#define MRST_XBIAS 0x20 +#define MRST_YBIAS 0x40 +#define MRST_ZBIAS 0x80 + +/* Touch screen coordinates */ +#define MRST_X_MIN 10 +#define MRST_X_MAX 1024 +#define MRST_X_FUZZ 5 +#define MRST_Y_MIN 10 +#define MRST_Y_MAX 1024 +#define MRST_Y_FUZZ 5 +#define MRST_PRESSURE_MIN 0 +#define MRST_PRESSURE_NOMINAL 50 +#define MRST_PRESSURE_MAX 100 + +#define WAIT_ADC_COMPLETION 10 /* msec */ + +/* PMIC ADC round robin delays */ +#define ADC_LOOP_DELAY0 0x0 /* Continuous loop */ +#define ADC_LOOP_DELAY1 0x1 /* 4.5 ms approximate */ + +/* PMIC Vendor Identifiers */ +#define PMIC_VENDOR_FS 0 /* PMIC vendor FreeScale */ +#define PMIC_VENDOR_MAXIM 1 /* PMIC vendor MAXIM */ +#define PMIC_VENDOR_NEC 2 /* PMIC vendor NEC */ +#define MRSTOUCH_MAX_CHANNELS 32 /* Maximum ADC channels */ + +/* Touch screen device structure */ +struct mrstouch_dev { + struct device *dev; /* device associated with touch screen */ + struct input_dev *input; + char phys[32]; + u16 asr; /* Address selection register */ + int irq; + unsigned int vendor; /* PMIC vendor */ + unsigned int rev; /* PMIC revision */ + + int (*read_prepare)(struct mrstouch_dev *tsdev); + int (*read)(struct mrstouch_dev *tsdev, u16 *x, u16 *y, u16 *z); + int (*read_finish)(struct mrstouch_dev *tsdev); +}; + + +/*************************** NEC and Maxim Interface ************************/ + +static int mrstouch_nec_adc_read_prepare(struct mrstouch_dev *tsdev) +{ + return intel_scu_ipc_update_register(PMIC_REG_MADCINT, 0, 0x20); +} + +/* + * Enables PENDET interrupt. + */ +static int mrstouch_nec_adc_read_finish(struct mrstouch_dev *tsdev) +{ + int err; + + err = intel_scu_ipc_update_register(PMIC_REG_MADCINT, 0x20, 0x20); + if (!err) + err = intel_scu_ipc_update_register(PMIC_REG_ADCCNTL1, 0, 0x05); + + return err; +} + +/* + * Reads PMIC ADC touch screen result + * Reads ADC storage registers for higher 7 and lower 3 bits and + * converts the two readings into a single value and turns off gain bit + */ +static int mrstouch_ts_chan_read(u16 offset, u16 chan, u16 *vp, u16 *vm) +{ + int err; + u16 result; + u32 res; + + result = PMIC_REG_ADCSNS0H + offset; + + if (chan == MRST_TS_CHAN12) + result += 4; + + err = intel_scu_ipc_ioread32(result, &res); + if (err) + return err; + + /* Mash the bits up */ + + *vp = (res & 0xFF) << 3; /* Highest 7 bits */ + *vp |= (res >> 8) & 0x07; /* Lower 3 bits */ + *vp &= 0x3FF; + + res >>= 16; + + *vm = (res & 0xFF) << 3; /* Highest 7 bits */ + *vm |= (res >> 8) & 0x07; /* Lower 3 bits */ + *vm &= 0x3FF; + + return 0; +} + +/* + * Enables X, Y and Z bias values + * Enables YPYM for X channels and XPXM for Y channels + */ +static int mrstouch_ts_bias_set(uint offset, uint bias) +{ + int count; + u16 chan, start; + u16 reg[4]; + u8 data[4]; + + chan = PMICADDR0 + offset; + start = MRST_TS_CHAN10; + + for (count = 0; count <= 3; count++) { + reg[count] = chan++; + data[count] = bias | (start + count); + } + + return intel_scu_ipc_writev(reg, data, 4); +} + +/* To read touch screen channel values */ +static int mrstouch_nec_adc_read(struct mrstouch_dev *tsdev, + u16 *x, u16 *y, u16 *z) +{ + int err; + u16 xm, ym, zm; + + /* configure Y bias for X channels */ + err = mrstouch_ts_bias_set(tsdev->asr, MRST_YBIAS); + if (err) + goto ipc_error; + + msleep(WAIT_ADC_COMPLETION); + + /* read x+ and x- channels */ + err = mrstouch_ts_chan_read(tsdev->asr, MRST_TS_CHAN10, x, &xm); + if (err) + goto ipc_error; + + /* configure x bias for y channels */ + err = mrstouch_ts_bias_set(tsdev->asr, MRST_XBIAS); + if (err) + goto ipc_error; + + msleep(WAIT_ADC_COMPLETION); + + /* read y+ and y- channels */ + err = mrstouch_ts_chan_read(tsdev->asr, MRST_TS_CHAN12, y, &ym); + if (err) + goto ipc_error; + + /* configure z bias for x and y channels */ + err = mrstouch_ts_bias_set(tsdev->asr, MRST_ZBIAS); + if (err) + goto ipc_error; + + msleep(WAIT_ADC_COMPLETION); + + /* read z+ and z- channels */ + err = mrstouch_ts_chan_read(tsdev->asr, MRST_TS_CHAN10, z, &zm); + if (err) + goto ipc_error; + + return 0; + +ipc_error: + dev_err(tsdev->dev, "ipc error during adc read\n"); + return err; +} + + +/*************************** Freescale Interface ************************/ + +static int mrstouch_fs_adc_read_prepare(struct mrstouch_dev *tsdev) +{ + int err, count; + u16 chan; + u16 reg[5]; + u8 data[5]; + + /* Stop the ADC */ + err = intel_scu_ipc_update_register(PMIC_REG_MADCINT, 0x00, 0x02); + if (err) + goto ipc_error; + + chan = PMICADDR0 + tsdev->asr; + + /* Set X BIAS */ + for (count = 0; count <= 3; count++) { + reg[count] = chan++; + data[count] = 0x2A; + } + reg[count] = chan++; /* Dummy */ + data[count] = 0; + + err = intel_scu_ipc_writev(reg, data, 5); + if (err) + goto ipc_error; + + msleep(WAIT_ADC_COMPLETION); + + /* Set Y BIAS */ + for (count = 0; count <= 3; count++) { + reg[count] = chan++; + data[count] = 0x4A; + } + reg[count] = chan++; /* Dummy */ + data[count] = 0; + + err = intel_scu_ipc_writev(reg, data, 5); + if (err) + goto ipc_error; + + msleep(WAIT_ADC_COMPLETION); + + /* Set Z BIAS */ + err = intel_scu_ipc_iowrite32(chan + 2, 0x8A8A8A8A); + if (err) + goto ipc_error; + + msleep(WAIT_ADC_COMPLETION); + + return 0; + +ipc_error: + dev_err(tsdev->dev, "ipc error during %s\n", __func__); + return err; +} + +static int mrstouch_fs_adc_read(struct mrstouch_dev *tsdev, + u16 *x, u16 *y, u16 *z) +{ + int err; + u16 result; + u16 reg[4]; + u8 data[4]; + + result = PMIC_REG_ADCSNS0H + tsdev->asr; + + reg[0] = result + 4; + reg[1] = result + 5; + reg[2] = result + 16; + reg[3] = result + 17; + + err = intel_scu_ipc_readv(reg, data, 4); + if (err) + goto ipc_error; + + *x = data[0] << 3; /* Higher 7 bits */ + *x |= data[1] & 0x7; /* Lower 3 bits */ + *x &= 0x3FF; + + *y = data[2] << 3; /* Higher 7 bits */ + *y |= data[3] & 0x7; /* Lower 3 bits */ + *y &= 0x3FF; + + /* Read Z value */ + reg[0] = result + 28; + reg[1] = result + 29; + + err = intel_scu_ipc_readv(reg, data, 4); + if (err) + goto ipc_error; + + *z = data[0] << 3; /* Higher 7 bits */ + *z |= data[1] & 0x7; /* Lower 3 bits */ + *z &= 0x3FF; + + return 0; + +ipc_error: + dev_err(tsdev->dev, "ipc error during %s\n", __func__); + return err; +} + +static int mrstouch_fs_adc_read_finish(struct mrstouch_dev *tsdev) +{ + int err, count; + u16 chan; + u16 reg[5]; + u8 data[5]; + + /* Clear all TS channels */ + chan = PMICADDR0 + tsdev->asr; + for (count = 0; count <= 4; count++) { + reg[count] = chan++; + data[count] = 0; + } + err = intel_scu_ipc_writev(reg, data, 5); + if (err) + goto ipc_error; + + for (count = 0; count <= 4; count++) { + reg[count] = chan++; + data[count] = 0; + } + err = intel_scu_ipc_writev(reg, data, 5); + if (err) + goto ipc_error; + + err = intel_scu_ipc_iowrite32(chan + 2, 0x00000000); + if (err) + goto ipc_error; + + /* Start ADC */ + err = intel_scu_ipc_update_register(PMIC_REG_MADCINT, 0x02, 0x02); + if (err) + goto ipc_error; + + return 0; + +ipc_error: + dev_err(tsdev->dev, "ipc error during %s\n", __func__); + return err; +} + +static void mrstouch_report_event(struct input_dev *input, + unsigned int x, unsigned int y, unsigned int z) +{ + if (z > MRST_PRESSURE_NOMINAL) { + /* Pen touched, report button touch and coordinates */ + input_report_key(input, BTN_TOUCH, 1); + input_report_abs(input, ABS_X, x); + input_report_abs(input, ABS_Y, y); + } else { + input_report_key(input, BTN_TOUCH, 0); + } + + input_report_abs(input, ABS_PRESSURE, z); + input_sync(input); +} + +/* PENDET interrupt handler */ +static irqreturn_t mrstouch_pendet_irq(int irq, void *dev_id) +{ + struct mrstouch_dev *tsdev = dev_id; + u16 x, y, z; + + /* + * Should we lower thread priority? Probably not, since we are + * not spinning but sleeping... + */ + + if (tsdev->read_prepare(tsdev)) + goto out; + + do { + if (tsdev->read(tsdev, &x, &y, &z)) + break; + + mrstouch_report_event(tsdev->input, x, y, z); + } while (z > MRST_PRESSURE_NOMINAL); + + tsdev->read_finish(tsdev); + +out: + return IRQ_HANDLED; +} + +/* Utility to read PMIC ID */ +static int __devinit mrstouch_read_pmic_id(uint *vendor, uint *rev) +{ + int err; + u8 r; + + err = intel_scu_ipc_ioread8(PMIC_REG_ID1, &r); + if (err) + return err; + + *vendor = r & 0x7; + *rev = (r >> 3) & 0x7; + + return 0; +} + +/* + * Parse ADC channels to find end of the channel configured by other ADC user + * NEC and MAXIM requires 4 channels and FreeScale needs 18 channels + */ +static int __devinit mrstouch_chan_parse(struct mrstouch_dev *tsdev) +{ + int err, i, found; + u8 r8; + + found = -1; + + for (i = 0; i < MRSTOUCH_MAX_CHANNELS; i++) { + if (found >= 0) + break; + + err = intel_scu_ipc_ioread8(PMICADDR0 + i, &r8); + if (err) + return err; + + if (r8 == END_OF_CHANNEL) { + found = i; + break; + } + } + if (found < 0) + return 0; + + if (tsdev->vendor == PMIC_VENDOR_FS) { + if (found && found > (MRSTOUCH_MAX_CHANNELS - 18)) + return -ENOSPC; + } else { + if (found && found > (MRSTOUCH_MAX_CHANNELS - 4)) + return -ENOSPC; + } + return found; +} + + +/* + * Writes touch screen channels to ADC address selection registers + */ +static int __devinit mrstouch_ts_chan_set(uint offset) +{ + u16 chan; + + int ret, count; + + chan = PMICADDR0 + offset; + for (count = 0; count <= 3; count++) { + ret = intel_scu_ipc_iowrite8(chan++, MRST_TS_CHAN10 + count); + if (ret) + return ret; + } + return intel_scu_ipc_iowrite8(chan++, END_OF_CHANNEL); +} + +/* Initialize ADC */ +static int __devinit mrstouch_adc_init(struct mrstouch_dev *tsdev) +{ + int err, start; + u8 ra, rm; + + err = mrstouch_read_pmic_id(&tsdev->vendor, &tsdev->rev); + if (err) { + dev_err(tsdev->dev, "Unable to read PMIC id\n"); + return err; + } + + switch (tsdev->vendor) { + case PMIC_VENDOR_NEC: + case PMIC_VENDOR_MAXIM: + tsdev->read_prepare = mrstouch_nec_adc_read_prepare; + tsdev->read = mrstouch_nec_adc_read; + tsdev->read_finish = mrstouch_nec_adc_read_finish; + break; + + case PMIC_VENDOR_FS: + tsdev->read_prepare = mrstouch_fs_adc_read_prepare; + tsdev->read = mrstouch_fs_adc_read; + tsdev->read_finish = mrstouch_fs_adc_read_finish; + break; + + default: + dev_err(tsdev->dev, + "Unsupported touchscreen: %d\n", tsdev->vendor); + return -ENXIO; + } + + start = mrstouch_chan_parse(tsdev); + if (start < 0) { + dev_err(tsdev->dev, "Unable to parse channels\n"); + return start; + } + + tsdev->asr = start; + + /* + * ADC power on, start, enable PENDET and set loop delay + * ADC loop delay is set to 4.5 ms approximately + * Loop delay more than this results in jitter in adc readings + * Setting loop delay to 0 (continous loop) in MAXIM stops PENDET + * interrupt generation sometimes. + */ + + if (tsdev->vendor == PMIC_VENDOR_FS) { + ra = 0xE0 | ADC_LOOP_DELAY0; + rm = 0x5; + } else { + /* NEC and MAXIm not consistent with loop delay 0 */ + ra = 0xE0 | ADC_LOOP_DELAY1; + rm = 0x0; + + /* configure touch screen channels */ + err = mrstouch_ts_chan_set(tsdev->asr); + if (err) + return err; + } + + err = intel_scu_ipc_update_register(PMIC_REG_ADCCNTL1, ra, 0xE7); + if (err) + return err; + + err = intel_scu_ipc_update_register(PMIC_REG_MADCINT, rm, 0x03); + if (err) + return err; + + return 0; +} + + +/* Probe function for touch screen driver */ +static int __devinit mrstouch_probe(struct platform_device *pdev) +{ + struct mrstouch_dev *tsdev; + struct input_dev *input; + int err; + int irq; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "no interrupt assigned\n"); + return -EINVAL; + } + + tsdev = kzalloc(sizeof(struct mrstouch_dev), GFP_KERNEL); + input = input_allocate_device(); + if (!tsdev || !input) { + dev_err(&pdev->dev, "unable to allocate memory\n"); + err = -ENOMEM; + goto err_free_mem; + } + + tsdev->dev = &pdev->dev; + tsdev->input = input; + tsdev->irq = irq; + + snprintf(tsdev->phys, sizeof(tsdev->phys), + "%s/input0", dev_name(tsdev->dev)); + + err = mrstouch_adc_init(tsdev); + if (err) { + dev_err(&pdev->dev, "ADC initialization failed\n"); + goto err_free_mem; + } + + input->name = "mrst_touchscreen"; + input->phys = tsdev->phys; + input->dev.parent = tsdev->dev; + + input->id.vendor = tsdev->vendor; + input->id.version = tsdev->rev; + + input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + + input_set_abs_params(tsdev->input, ABS_X, + MRST_X_MIN, MRST_X_MAX, MRST_X_FUZZ, 0); + input_set_abs_params(tsdev->input, ABS_Y, + MRST_Y_MIN, MRST_Y_MAX, MRST_Y_FUZZ, 0); + input_set_abs_params(tsdev->input, ABS_PRESSURE, + MRST_PRESSURE_MIN, MRST_PRESSURE_MAX, 0, 0); + + err = request_threaded_irq(tsdev->irq, NULL, mrstouch_pendet_irq, + 0, "mrstouch", tsdev); + if (err) { + dev_err(tsdev->dev, "unable to allocate irq\n"); + goto err_free_mem; + } + + err = input_register_device(tsdev->input); + if (err) { + dev_err(tsdev->dev, "unable to register input device\n"); + goto err_free_irq; + } + + platform_set_drvdata(pdev, tsdev); + return 0; + +err_free_irq: + free_irq(tsdev->irq, tsdev); +err_free_mem: + input_free_device(input); + kfree(tsdev); + return err; +} + +static int __devexit mrstouch_remove(struct platform_device *pdev) +{ + struct mrstouch_dev *tsdev = platform_get_drvdata(pdev); + + free_irq(tsdev->irq, tsdev); + input_unregister_device(tsdev->input); + kfree(tsdev); + + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static struct platform_driver mrstouch_driver = { + .driver = { + .name = "pmic_touch", + .owner = THIS_MODULE, + }, + .probe = mrstouch_probe, + .remove = __devexit_p(mrstouch_remove), +}; + +static int __init mrstouch_init(void) +{ + return platform_driver_register(&mrstouch_driver); +} +module_init(mrstouch_init); + +static void __exit mrstouch_exit(void) +{ + platform_driver_unregister(&mrstouch_driver); +} +module_exit(mrstouch_exit); + +MODULE_AUTHOR("Sreedhara Murthy. D.S, sreedhara.ds@intel.com"); +MODULE_DESCRIPTION("Intel Moorestown Resistive Touch Screen Driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.2 From bba5394ad3bd23fff6987fc8bc3aaf9d12433c63 Mon Sep 17 00:00:00 2001 From: Xing Wei Date: Wed, 1 Sep 2010 23:25:35 -0700 Subject: Input: add support for Hanwang tablets Add support for Art Master III tablet of BeiJing HanwangTechnology Co, Ltd. Signed-off-by: Xing Wei Acked-by: Jiri Kosina Signed-off-by: Dmitry Torokhov --- drivers/input/tablet/Kconfig | 11 ++ drivers/input/tablet/Makefile | 1 + drivers/input/tablet/hanwang.c | 365 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 377 insertions(+) create mode 100644 drivers/input/tablet/hanwang.c (limited to 'drivers/input') diff --git a/drivers/input/tablet/Kconfig b/drivers/input/tablet/Kconfig index effb49ea24aa..58a87755b936 100644 --- a/drivers/input/tablet/Kconfig +++ b/drivers/input/tablet/Kconfig @@ -49,6 +49,17 @@ config TABLET_USB_GTCO To compile this driver as a module, choose M here: the module will be called gtco. +config TABLET_USB_HANWANG + tristate "Hanwang Art Master III 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 Hanwang Art + Master III tablet. + + To compile this driver as a module, choose M here: the + module will be called hanwang. + config TABLET_USB_KBTAB tristate "KB Gear JamStudio tablet support (USB)" depends on USB_ARCH_HAS_HCD diff --git a/drivers/input/tablet/Makefile b/drivers/input/tablet/Makefile index ce8b9a9cfa40..3f6c25220638 100644 --- a/drivers/input/tablet/Makefile +++ b/drivers/input/tablet/Makefile @@ -8,5 +8,6 @@ wacom-objs := wacom_wac.o wacom_sys.o obj-$(CONFIG_TABLET_USB_ACECAD) += acecad.o obj-$(CONFIG_TABLET_USB_AIPTEK) += aiptek.o obj-$(CONFIG_TABLET_USB_GTCO) += gtco.o +obj-$(CONFIG_TABLET_USB_HANWANG) += hanwang.o obj-$(CONFIG_TABLET_USB_KBTAB) += kbtab.o obj-$(CONFIG_TABLET_USB_WACOM) += wacom.o diff --git a/drivers/input/tablet/hanwang.c b/drivers/input/tablet/hanwang.c new file mode 100644 index 000000000000..3554e30ab41b --- /dev/null +++ b/drivers/input/tablet/hanwang.c @@ -0,0 +1,365 @@ +/* + * USB Hanwang tablet support + * + * Copyright (c) 2010 Xing Wei + * + */ + +/* + * 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_AUTHOR "Xing Wei " +#define DRIVER_DESC "USB Hanwang tablet driver" +#define DRIVER_LICENSE "GPL" + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE(DRIVER_LICENSE); + +#define USB_VENDOR_ID_HANWANG 0x0b57 +#define HANWANG_TABLET_INT_CLASS 0x0003 +#define HANWANG_TABLET_INT_SUB_CLASS 0x0001 +#define HANWANG_TABLET_INT_PROTOCOL 0x0002 + +#define ART_MASTERIII_PKGLEN_MAX 10 + +/* match vendor and interface info */ +#define HANWANG_TABLET_DEVICE(vend, cl, sc, pr) \ + .match_flags = USB_DEVICE_ID_MATCH_VENDOR \ + | USB_DEVICE_ID_MATCH_INT_INFO, \ + .idVendor = (vend), \ + .bInterfaceClass = (cl), \ + .bInterfaceSubClass = (sc), \ + .bInterfaceProtocol = (pr) + +struct hanwang { + unsigned char *data; + dma_addr_t data_dma; + struct input_dev *dev; + struct usb_device *usbdev; + struct urb *irq; + const struct hanwang_features *features; + unsigned int current_tool; + char name[64]; + char phys[32]; +}; + +struct hanwang_features { + unsigned short pid; + char *name; + int pkg_len; + int max_x; + int max_y; + int max_tilt_x; + int max_tilt_y; + int max_pressure; +}; + +static const struct hanwang_features features_array[] = { + { 0x8528, "Hanwang Art Master III 0906", + ART_MASTERIII_PKGLEN_MAX, 0x5757, 0x3692, 0x3f, 0x7f, 2048 }, + { 0x8529, "Hanwang Art Master III 0604", + ART_MASTERIII_PKGLEN_MAX, 0x3d84, 0x2672, 0x3f, 0x7f, 2048 }, + { 0x852a, "Hanwang Art Master III 1308", + ART_MASTERIII_PKGLEN_MAX, 0x7f00, 0x4f60, 0x3f, 0x7f, 2048 }, +}; + +static const int hw_eventtypes[] = { + EV_KEY, EV_ABS, +}; + +static const int hw_absevents[] = { + ABS_X, ABS_Y, ABS_TILT_X, ABS_TILT_Y, ABS_WHEEL, ABS_PRESSURE, +}; + +static const int hw_btnevents[] = { + BTN_STYLUS, BTN_STYLUS2, BTN_TOOL_PEN, BTN_TOOL_RUBBER, BTN_TOOL_MOUSE, + BTN_0, BTN_1, BTN_2, BTN_3, BTN_4, BTN_5, BTN_6, BTN_7, BTN_8 +}; + +static void hanwang_parse_packet(struct hanwang *hanwang) +{ + unsigned char *data = hanwang->data; + struct input_dev *input_dev = hanwang->dev; + struct usb_device *dev = hanwang->usbdev; + int i; + + switch (data[0]) { + case 0x02: /* data packet */ + switch (data[1]) { + case 0x80: /* tool prox out */ + input_report_key(input_dev, hanwang->current_tool, 0); + break; + + case 0xc2: /* first time tool prox in */ + switch (data[3] & 0xf0) { + case 0x20: + hanwang->current_tool = BTN_TOOL_PEN; + input_report_key(input_dev, BTN_TOOL_PEN, 1); + break; + case 0xa0: + hanwang->current_tool = BTN_TOOL_RUBBER; + input_report_key(input_dev, BTN_TOOL_RUBBER, 1); + break; + default: + dev_dbg(&dev->dev, + "unknown tablet tool %02x ", data[0]); + break; + } + break; + + default: /* tool data packet */ + input_report_abs(input_dev, ABS_X, + (data[2] << 8) | data[3]); + input_report_abs(input_dev, ABS_Y, + (data[4] << 8) | data[5]); + input_report_abs(input_dev, ABS_PRESSURE, + (data[6] << 3) | + ((data[7] & 0xc0) >> 5) | + (data[1] & 0x01)); + input_report_abs(input_dev, ABS_TILT_X, data[7] & 0x3f); + input_report_abs(input_dev, ABS_TILT_Y, data[8] & 0x7f); + input_report_key(input_dev, BTN_STYLUS, data[1] & 0x02); + input_report_key(input_dev, BTN_STYLUS2, data[1] & 0x04); + break; + } + break; + + case 0x0c: + /* roll wheel */ + input_report_abs(input_dev, ABS_WHEEL, data[1]); + input_report_key(input_dev, BTN_0, data[2]); + for (i = 0; i < 8; i++) + input_report_key(input_dev, + BTN_1 + i, data[3] & (1 << i)); + break; + + default: + dev_dbg(&dev->dev, "error packet %02x ", data[0]); + break; + } + + input_sync(input_dev); +} + +static void hanwang_irq(struct urb *urb) +{ + struct hanwang *hanwang = urb->context; + struct usb_device *dev = hanwang->usbdev; + int retval; + + switch (urb->status) { + case 0: + /* success */; + hanwang_parse_packet(hanwang); + break; + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* this urb is terminated, clean up */ + dev_err(&dev->dev, "%s - urb shutting down with status: %d", + __func__, urb->status); + return; + default: + dev_err(&dev->dev, "%s - nonzero urb status received: %d", + __func__, urb->status); + break; + } + + retval = usb_submit_urb(urb, GFP_ATOMIC); + if (retval) + dev_err(&dev->dev, "%s - usb_submit_urb failed with result %d", + __func__, retval); +} + +static int hanwang_open(struct input_dev *dev) +{ + struct hanwang *hanwang = input_get_drvdata(dev); + + hanwang->irq->dev = hanwang->usbdev; + if (usb_submit_urb(hanwang->irq, GFP_KERNEL)) + return -EIO; + + return 0; +} + +static void hanwang_close(struct input_dev *dev) +{ + struct hanwang *hanwang = input_get_drvdata(dev); + + usb_kill_urb(hanwang->irq); +} + +static bool get_features(struct usb_device *dev, struct hanwang *hanwang) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(features_array); i++) { + if (le16_to_cpu(dev->descriptor.idProduct) == + features_array[i].pid) { + hanwang->features = &features_array[i]; + return true; + } + } + + return false; +} + + +static int hanwang_probe(struct usb_interface *intf, const struct usb_device_id *id) +{ + struct usb_device *dev = interface_to_usbdev(intf); + struct usb_endpoint_descriptor *endpoint; + struct hanwang *hanwang; + struct input_dev *input_dev; + int error; + int i; + + hanwang = kzalloc(sizeof(struct hanwang), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!hanwang || !input_dev) { + error = -ENOMEM; + goto fail1; + } + + if (!get_features(dev, hanwang)) { + error = -ENXIO; + goto fail1; + } + + hanwang->data = usb_alloc_coherent(dev, hanwang->features->pkg_len, + GFP_KERNEL, &hanwang->data_dma); + if (!hanwang->data) { + error = -ENOMEM; + goto fail1; + } + + hanwang->irq = usb_alloc_urb(0, GFP_KERNEL); + if (!hanwang->irq) { + error = -ENOMEM; + goto fail2; + } + + hanwang->usbdev = dev; + hanwang->dev = input_dev; + + usb_make_path(dev, hanwang->phys, sizeof(hanwang->phys)); + strlcat(hanwang->phys, "/input0", sizeof(hanwang->phys)); + + strlcpy(hanwang->name, hanwang->features->name, sizeof(hanwang->name)); + input_dev->name = hanwang->name; + input_dev->phys = hanwang->phys; + usb_to_input_id(dev, &input_dev->id); + input_dev->dev.parent = &intf->dev; + + input_set_drvdata(input_dev, hanwang); + + input_dev->open = hanwang_open; + input_dev->close = hanwang_close; + + for (i = 0; i < ARRAY_SIZE(hw_eventtypes); ++i) + __set_bit(hw_eventtypes[i], input_dev->evbit); + + for (i = 0; i < ARRAY_SIZE(hw_absevents); ++i) + __set_bit(hw_absevents[i], input_dev->absbit); + + for (i = 0; i < ARRAY_SIZE(hw_btnevents); ++i) + __set_bit(hw_btnevents[i], input_dev->keybit); + + input_set_abs_params(input_dev, ABS_X, + 0, hanwang->features->max_x, 4, 0); + input_set_abs_params(input_dev, ABS_Y, + 0, hanwang->features->max_y, 4, 0); + input_set_abs_params(input_dev, ABS_TILT_X, + 0, hanwang->features->max_tilt_x, 0, 0); + input_set_abs_params(input_dev, ABS_TILT_Y, + 0, hanwang->features->max_tilt_y, 0, 0); + input_set_abs_params(input_dev, ABS_PRESSURE, + 0, hanwang->features->max_pressure, 0, 0); + + endpoint = &intf->cur_altsetting->endpoint[0].desc; + usb_fill_int_urb(hanwang->irq, dev, + usb_rcvintpipe(dev, endpoint->bEndpointAddress), + hanwang->data, hanwang->features->pkg_len, + hanwang_irq, hanwang, endpoint->bInterval); + hanwang->irq->transfer_dma = hanwang->data_dma; + hanwang->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + error = input_register_device(hanwang->dev); + if (error) + goto fail3; + + usb_set_intfdata(intf, hanwang); + + return 0; + + fail3: usb_free_urb(hanwang->irq); + fail2: usb_free_coherent(dev, hanwang->features->pkg_len, + hanwang->data, hanwang->data_dma); + fail1: input_free_device(input_dev); + kfree(hanwang); + return error; + +} + +static void hanwang_disconnect(struct usb_interface *intf) +{ + struct hanwang *hanwang = usb_get_intfdata(intf); + + input_unregister_device(hanwang->dev); + usb_free_urb(hanwang->irq); + usb_free_coherent(interface_to_usbdev(intf), + hanwang->features->pkg_len, hanwang->data, + hanwang->data_dma); + kfree(hanwang); + usb_set_intfdata(intf, NULL); +} + +static const struct usb_device_id hanwang_ids[] = { + { HANWANG_TABLET_DEVICE(USB_VENDOR_ID_HANWANG, HANWANG_TABLET_INT_CLASS, + HANWANG_TABLET_INT_SUB_CLASS, HANWANG_TABLET_INT_PROTOCOL) }, + {} +}; + +MODULE_DEVICE_TABLE(usb, hanwang_ids); + +static struct usb_driver hanwang_driver = { + .name = "hanwang", + .probe = hanwang_probe, + .disconnect = hanwang_disconnect, + .id_table = hanwang_ids, +}; + +static int __init hanwang_init(void) +{ + return usb_register(&hanwang_driver); +} + +static void __exit hanwang_exit(void) +{ + usb_deregister(&hanwang_driver); +} + +module_init(hanwang_init); +module_exit(hanwang_exit); -- cgit v1.2.2 From ec1496b39576a3374e5461caac17b83b3275dbb7 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Thu, 2 Sep 2010 10:26:31 -0700 Subject: Input: s3c2410_ts - fix s3c2410ts_probe error path Use input_free_device() to free devices that have not been registered. Signed-off-by: Axel Lin Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/s3c2410_ts.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/s3c2410_ts.c b/drivers/input/touchscreen/s3c2410_ts.c index 6085d12fd561..8feb7f3c8be1 100644 --- a/drivers/input/touchscreen/s3c2410_ts.c +++ b/drivers/input/touchscreen/s3c2410_ts.c @@ -350,7 +350,7 @@ static int __devinit s3c2410ts_probe(struct platform_device *pdev) err_tcirq: free_irq(ts.irq_tc, ts.input); err_inputdev: - input_unregister_device(ts.input); + input_free_device(ts.input); err_iomap: iounmap(ts.io); err_clk: -- cgit v1.2.2 From 98417884477eb3c2d9c19a69c070f7f347af6d76 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Thu, 2 Sep 2010 10:26:35 -0700 Subject: Input: tps6507x-ts - add missing call to platform_set_drvdata() We call platform_get_drvdata() in tps6507x_ts_remove(), thus we should call platform_set_drvdata() in tps6507x_ts_probe(). Signed-off-by: Axel Lin Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/tps6507x-ts.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/tps6507x-ts.c b/drivers/input/touchscreen/tps6507x-ts.c index a644d18c04dc..728620974dd8 100644 --- a/drivers/input/touchscreen/tps6507x-ts.c +++ b/drivers/input/touchscreen/tps6507x-ts.c @@ -335,6 +335,7 @@ static int tps6507x_ts_probe(struct platform_device *pdev) dev_err(tsc->dev, "schedule failed"); goto err2; } + platform_set_drvdata(pdev, tps6507x_dev); return 0; -- cgit v1.2.2 From 8a26f5d18d45d49ff38ad2ddfed9c783dd2a19ab Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Thu, 2 Sep 2010 19:52:37 -0700 Subject: Input: tps6507x-ts - properly unregister input device on removal Once device is registered we should call input_unregister_device() instead of input_free_device(). Signed-off-by: Axel Lin Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/tps6507x-ts.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/tps6507x-ts.c b/drivers/input/touchscreen/tps6507x-ts.c index 728620974dd8..c8c136cf7bbc 100644 --- a/drivers/input/touchscreen/tps6507x-ts.c +++ b/drivers/input/touchscreen/tps6507x-ts.c @@ -359,7 +359,7 @@ static int __devexit tps6507x_ts_remove(struct platform_device *pdev) cancel_delayed_work_sync(&tsc->work); destroy_workqueue(tsc->wq); - input_free_device(input_dev); + input_unregister_device(input_dev); tps6507x_dev->ts = NULL; kfree(tsc); -- cgit v1.2.2 From b496acb7fe567546d414fb9807524ab92828ca25 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Thu, 2 Sep 2010 19:52:41 -0700 Subject: Input: tsc2007 - fix a redundant assignment for pdata Signed-off-by: Axel Lin Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/tsc2007.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/tsc2007.c b/drivers/input/touchscreen/tsc2007.c index be23780e8a3e..80467f262331 100644 --- a/drivers/input/touchscreen/tsc2007.c +++ b/drivers/input/touchscreen/tsc2007.c @@ -265,7 +265,7 @@ static int __devinit tsc2007_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct tsc2007 *ts; - struct tsc2007_platform_data *pdata = pdata = client->dev.platform_data; + struct tsc2007_platform_data *pdata = client->dev.platform_data; struct input_dev *input_dev; int err; -- cgit v1.2.2 From 843cbfa74f1a443ce0def7fcc799803f34faa71c Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Thu, 2 Sep 2010 19:52:49 -0700 Subject: Input: stmpe-ts - remove input_free_device() in stmpe_ts_remove() It is forbidden to call input_free_device() after input_unregister_device(). Signed-off-by: Axel Lin Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/stmpe-ts.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/stmpe-ts.c b/drivers/input/touchscreen/stmpe-ts.c index 656148ec0027..0339d1d0d29e 100644 --- a/drivers/input/touchscreen/stmpe-ts.c +++ b/drivers/input/touchscreen/stmpe-ts.c @@ -361,7 +361,6 @@ static int __devexit stmpe_ts_remove(struct platform_device *pdev) platform_set_drvdata(pdev, NULL); input_unregister_device(ts->idev); - input_free_device(ts->idev); kfree(ts); -- cgit v1.2.2 From cdd194779b72fe6d5e1b5b67ef57a0fddbd60eac Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Thu, 2 Sep 2010 19:54:27 -0700 Subject: Input: stmpe-ts - return -ENOMEM when memory allocation fails Signed-off-by: Axel Lin Acked-by: Wolfram Sang Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/stmpe-ts.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/stmpe-ts.c b/drivers/input/touchscreen/stmpe-ts.c index 0339d1d0d29e..ae88e13c99ff 100644 --- a/drivers/input/touchscreen/stmpe-ts.c +++ b/drivers/input/touchscreen/stmpe-ts.c @@ -268,7 +268,7 @@ static int __devinit stmpe_input_probe(struct platform_device *pdev) struct stmpe_touch *ts; struct input_dev *idev; struct stmpe_ts_platform_data *ts_pdata = NULL; - int ret = 0; + int ret; int ts_irq; ts_irq = platform_get_irq_byname(pdev, "FIFO_TH"); @@ -276,12 +276,16 @@ static int __devinit stmpe_input_probe(struct platform_device *pdev) return ts_irq; ts = kzalloc(sizeof(*ts), GFP_KERNEL); - if (!ts) + if (!ts) { + ret = -ENOMEM; goto err_out; + } idev = input_allocate_device(); - if (!idev) + if (!idev) { + ret = -ENOMEM; goto err_free_ts; + } platform_set_drvdata(pdev, ts); ts->stmpe = stmpe; -- cgit v1.2.2 From d3622e6f05b2134deb5ba8ca2f6bcce5391e7e6d Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Fri, 3 Sep 2010 10:31:04 -0700 Subject: Input: wm97xx-core - simplify error path in wm97xx_probe() Use platform_device_del() instead of platform_device_unregister() in error handling path. Signed-off-by: Axel Lin Acked-by: Liam Girdwood Acked-by: Mark Brown Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/wm97xx-core.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/wm97xx-core.c b/drivers/input/touchscreen/wm97xx-core.c index cbfef1ea7e30..770537c14628 100644 --- a/drivers/input/touchscreen/wm97xx-core.c +++ b/drivers/input/touchscreen/wm97xx-core.c @@ -684,8 +684,7 @@ static int wm97xx_probe(struct device *dev) touch_reg_err: platform_device_put(wm->touch_dev); touch_err: - platform_device_unregister(wm->battery_dev); - wm->battery_dev = NULL; + platform_device_del(wm->battery_dev); batt_reg_err: platform_device_put(wm->battery_dev); batt_err: -- cgit v1.2.2 From eb54ddd4d78e62647b7096e4ada7389dbdf2cea7 Mon Sep 17 00:00:00 2001 From: Eric Millbrandt Date: Fri, 3 Sep 2010 10:31:05 -0700 Subject: Input: wm97xx-core - add retries to wm97xx_read_aux_adc Add logic to wm97xx_read_aux_adc() to retry reading the adc if the sample failed. This could occur if the previous sample was still in the return register or the sample timed-out. Also avoid a pathologic failure mode by disabling the digitizer and returning -EBUSY after 5 retries. Signed-off-by: Eric Millbrandt Acked-by: Mark Brown Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/wm97xx-core.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/wm97xx-core.c b/drivers/input/touchscreen/wm97xx-core.c index 770537c14628..6b75c9f660ae 100644 --- a/drivers/input/touchscreen/wm97xx-core.c +++ b/drivers/input/touchscreen/wm97xx-core.c @@ -125,6 +125,8 @@ int wm97xx_read_aux_adc(struct wm97xx *wm, u16 adcsel) { int power_adc = 0, auxval; u16 power = 0; + int rc = 0; + int timeout = 0; /* get codec */ mutex_lock(&wm->codec_mutex); @@ -143,7 +145,9 @@ int wm97xx_read_aux_adc(struct wm97xx *wm, u16 adcsel) /* Turn polling mode on to read AUX ADC */ wm->pen_probably_down = 1; - wm->codec->poll_sample(wm, adcsel, &auxval); + + while (rc != RC_VALID && timeout++ < 5) + rc = wm->codec->poll_sample(wm, adcsel, &auxval); if (power_adc) wm97xx_reg_write(wm, AC97_EXTENDED_MID, power | 0x8000); @@ -152,8 +156,15 @@ int wm97xx_read_aux_adc(struct wm97xx *wm, u16 adcsel) wm->pen_probably_down = 0; + if (timeout >= 5) { + dev_err(wm->dev, + "timeout reading auxadc %d, disabling digitiser\n", + adcsel); + wm->codec->dig_enable(wm, false); + } + mutex_unlock(&wm->codec_mutex); - return auxval & 0xfff; + return (rc == RC_VALID ? auxval & 0xfff : -EBUSY); } EXPORT_SYMBOL_GPL(wm97xx_read_aux_adc); -- cgit v1.2.2 From 144c0f8833d0458e4369a27a53aea8856c665c41 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 3 Sep 2010 10:31:05 -0700 Subject: Input: fix a few typos Signed-off-by: Dmitry Torokhov --- drivers/input/input.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/input.c b/drivers/input/input.c index ab6982056518..acb3c8095c65 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -171,7 +171,7 @@ static int input_handle_abs_event(struct input_dev *dev, if (code == ABS_MT_SLOT) { /* * "Stage" the event; we'll flush it later, when we - * get actiual touch data. + * get actual touch data. */ if (*pval >= 0 && *pval < dev->mtsize) dev->slot = *pval; @@ -188,7 +188,7 @@ static int input_handle_abs_event(struct input_dev *dev, pold = &mtslot->abs[code - ABS_MT_FIRST]; } else { /* - * Bypass filtering for multitouch events when + * Bypass filtering for multi-touch events when * not employing slots. */ pold = NULL; @@ -1601,7 +1601,7 @@ EXPORT_SYMBOL(input_free_device); * * This function allocates all necessary memory for MT slot handling in the * input device, and adds ABS_MT_SLOT to the device capabilities. All slots - * are initially marked as unused iby setting ABS_MT_TRACKING_ID to -1. + * are initially marked as unused by setting ABS_MT_TRACKING_ID to -1. */ int input_mt_create_slots(struct input_dev *dev, unsigned int num_slots) { -- cgit v1.2.2 From 77686517977e77d101c8a7b397717df00a88922b Mon Sep 17 00:00:00 2001 From: Sundar R Iyer Date: Sun, 5 Sep 2010 12:18:47 -0700 Subject: Input: add support for PowerOn button on the AB8500 MFD Add the PowerOn (PonKey) button support to detect power on/off events. Acked-by: Linus Walleij Signed-off-by: Sundar R Iyer Signed-off-by: Dmitry Torokhov --- drivers/input/misc/Kconfig | 10 +++ drivers/input/misc/Makefile | 1 + drivers/input/misc/ab8500-ponkey.c | 157 +++++++++++++++++++++++++++++++++++++ 3 files changed, 168 insertions(+) create mode 100644 drivers/input/misc/ab8500-ponkey.c (limited to 'drivers/input') diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index b49e23379723..b99b8cbde02f 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -22,6 +22,16 @@ config INPUT_88PM860X_ONKEY To compile this driver as a module, choose M here: the module will be called 88pm860x_onkey. +config INPUT_AB8500_PONKEY + tristate "AB8500 Pon (PowerOn) Key" + depends on AB8500_CORE + help + Say Y here to use the PowerOn Key for ST-Ericsson's AB8500 + Mix-Sig PMIC. + + To compile this driver as a module, choose M here: the module + will be called ab8500-ponkey. + config INPUT_AD714X tristate "Analog Devices AD714x Capacitance Touch Sensor" help diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index 19ccca78fa76..1fe1f6c8b737 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -5,6 +5,7 @@ # Each configuration option enables a list of files. obj-$(CONFIG_INPUT_88PM860X_ONKEY) += 88pm860x_onkey.o +obj-$(CONFIG_INPUT_AB8500_PONKEY) += ab8500-ponkey.o obj-$(CONFIG_INPUT_AD714X) += ad714x.o obj-$(CONFIG_INPUT_AD714X_I2C) += ad714x-i2c.o obj-$(CONFIG_INPUT_AD714X_SPI) += ad714x-spi.o diff --git a/drivers/input/misc/ab8500-ponkey.c b/drivers/input/misc/ab8500-ponkey.c new file mode 100644 index 000000000000..98f8ede504b8 --- /dev/null +++ b/drivers/input/misc/ab8500-ponkey.c @@ -0,0 +1,157 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * License Terms: GNU General Public License v2 + * Author: Sundar Iyer for ST-Ericsson + * + * AB8500 Power-On Key handler + */ + +#include +#include +#include +#include +#include +#include +#include + +/** + * struct ab8500_ponkey - ab8500 ponkey information + * @input_dev: pointer to input device + * @ab8500: ab8500 parent + * @irq_dbf: irq number for falling transition + * @irq_dbr: irq number for rising transition + */ +struct ab8500_ponkey { + struct input_dev *idev; + struct ab8500 *ab8500; + int irq_dbf; + int irq_dbr; +}; + +/* AB8500 gives us an interrupt when ONKEY is held */ +static irqreturn_t ab8500_ponkey_handler(int irq, void *data) +{ + struct ab8500_ponkey *ponkey = data; + + if (irq == ponkey->irq_dbf) + input_report_key(ponkey->idev, KEY_POWER, true); + else if (irq == ponkey->irq_dbr) + input_report_key(ponkey->idev, KEY_POWER, false); + + input_sync(ponkey->idev); + + return IRQ_HANDLED; +} + +static int __devinit ab8500_ponkey_probe(struct platform_device *pdev) +{ + struct ab8500 *ab8500 = dev_get_drvdata(pdev->dev.parent); + struct ab8500_ponkey *ponkey; + struct input_dev *input; + int irq_dbf, irq_dbr; + int error; + + irq_dbf = platform_get_irq_byname(pdev, "ONKEY_DBF"); + if (irq_dbf < 0) { + dev_err(&pdev->dev, "No IRQ for ONKEY_DBF, error=%d\n", irq_dbf); + return irq_dbf; + } + + irq_dbr = platform_get_irq_byname(pdev, "ONKEY_DBR"); + if (irq_dbr < 0) { + dev_err(&pdev->dev, "No IRQ for ONKEY_DBR, error=%d\n", irq_dbr); + return irq_dbr; + } + + ponkey = kzalloc(sizeof(struct ab8500_ponkey), GFP_KERNEL); + input = input_allocate_device(); + if (!ponkey || !input) { + error = -ENOMEM; + goto err_free_mem; + } + + ponkey->idev = input; + ponkey->ab8500 = ab8500; + ponkey->irq_dbf = irq_dbf; + ponkey->irq_dbr = irq_dbr; + + input->name = "AB8500 POn(PowerOn) Key"; + input->dev.parent = &pdev->dev; + + input_set_capability(input, EV_KEY, KEY_POWER); + + error = request_any_context_irq(ponkey->irq_dbf, ab8500_ponkey_handler, + 0, "ab8500-ponkey-dbf", ponkey); + if (error < 0) { + dev_err(ab8500->dev, "Failed to request dbf IRQ#%d: %d\n", + ponkey->irq_dbf, error); + goto err_free_mem; + } + + error = request_any_context_irq(ponkey->irq_dbr, ab8500_ponkey_handler, + 0, "ab8500-ponkey-dbr", ponkey); + if (error < 0) { + dev_err(ab8500->dev, "Failed to request dbr IRQ#%d: %d\n", + ponkey->irq_dbr, error); + goto err_free_dbf_irq; + } + + error = input_register_device(ponkey->idev); + if (error) { + dev_err(ab8500->dev, "Can't register input device: %d\n", error); + goto err_free_dbr_irq; + } + + platform_set_drvdata(pdev, ponkey); + return 0; + +err_free_dbr_irq: + free_irq(ponkey->irq_dbf, ponkey); +err_free_dbf_irq: + free_irq(ponkey->irq_dbf, ponkey); +err_free_mem: + input_free_device(input); + kfree(ponkey); + + return error; +} + +static int __devexit ab8500_ponkey_remove(struct platform_device *pdev) +{ + struct ab8500_ponkey *ponkey = platform_get_drvdata(pdev); + + free_irq(ponkey->irq_dbf, ponkey); + free_irq(ponkey->irq_dbr, ponkey); + input_unregister_device(ponkey->idev); + kfree(ponkey); + + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static struct platform_driver ab8500_ponkey_driver = { + .driver = { + .name = "ab8500-poweron-key", + .owner = THIS_MODULE, + }, + .probe = ab8500_ponkey_probe, + .remove = __devexit_p(ab8500_ponkey_remove), +}; + +static int __init ab8500_ponkey_init(void) +{ + return platform_driver_register(&ab8500_ponkey_driver); +} +module_init(ab8500_ponkey_init); + +static void __exit ab8500_ponkey_exit(void) +{ + platform_driver_unregister(&ab8500_ponkey_driver); +} +module_exit(ab8500_ponkey_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Sundar Iyer "); +MODULE_DESCRIPTION("ST-Ericsson AB8500 Power-ON(Pon) Key driver"); -- cgit v1.2.2 From fed87e655a2c20468d628b37424af58287803afe Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Sun, 5 Sep 2010 12:25:11 -0700 Subject: Input: wacom - add fuzz parameters to features The signal-to-noise ratio varies between devices, but currently all devices are treated the same way. Add fuzz parameters to the feature struct, allowing for tailored treatment of devices. Signed-off-by: Henrik Rydberg Acked-by: Ping Cheng Signed-off-by: Dmitry Torokhov --- drivers/input/tablet/wacom_sys.c | 6 +++++- drivers/input/tablet/wacom_wac.c | 9 ++++++--- drivers/input/tablet/wacom_wac.h | 4 ++++ 3 files changed, 15 insertions(+), 4 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/tablet/wacom_sys.c b/drivers/input/tablet/wacom_sys.c index 42ba3691d908..e510e4fb54f7 100644 --- a/drivers/input/tablet/wacom_sys.c +++ b/drivers/input/tablet/wacom_sys.c @@ -333,8 +333,12 @@ static int wacom_retrieve_hid_descriptor(struct usb_interface *intf, struct usb_host_interface *interface = intf->cur_altsetting; struct hid_descriptor *hid_desc; - /* default device to penabled */ + /* default features */ features->device_type = BTN_TOOL_PEN; + features->x_fuzz = 4; + features->y_fuzz = 4; + features->pressure_fuzz = 0; + features->distance_fuzz = 0; /* only Tablet PCs need to retrieve the info */ if ((features->type != TABLETPC) && (features->type != TABLETPC2FG)) diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/input/tablet/wacom_wac.c index 6e29badb969e..6d7e1647715f 100644 --- a/drivers/input/tablet/wacom_wac.c +++ b/drivers/input/tablet/wacom_wac.c @@ -951,9 +951,12 @@ void wacom_setup_input_capabilities(struct input_dev *input_dev, __set_bit(BTN_TOUCH, input_dev->keybit); - input_set_abs_params(input_dev, ABS_X, 0, features->x_max, 4, 0); - input_set_abs_params(input_dev, ABS_Y, 0, features->y_max, 4, 0); - input_set_abs_params(input_dev, ABS_PRESSURE, 0, features->pressure_max, 0, 0); + input_set_abs_params(input_dev, ABS_X, 0, features->x_max, + features->x_fuzz, 0); + input_set_abs_params(input_dev, ABS_Y, 0, features->y_max, + features->y_fuzz, 0); + input_set_abs_params(input_dev, ABS_PRESSURE, 0, features->pressure_max, + features->pressure_fuzz, 0); __set_bit(ABS_MISC, input_dev->absbit); diff --git a/drivers/input/tablet/wacom_wac.h b/drivers/input/tablet/wacom_wac.h index 99e1a54cd305..d769e9aa6f10 100644 --- a/drivers/input/tablet/wacom_wac.h +++ b/drivers/input/tablet/wacom_wac.h @@ -73,6 +73,10 @@ struct wacom_features { int y_phy; unsigned char unit; unsigned char unitExpo; + int x_fuzz; + int y_fuzz; + int pressure_fuzz; + int distance_fuzz; }; struct wacom_shared { -- cgit v1.2.2 From 4a88081e739a41d6d70bace7e0a027f9054ab540 Mon Sep 17 00:00:00 2001 From: Ping Cheng Date: Sun, 5 Sep 2010 12:25:40 -0700 Subject: Input: wacom - parse the Bamboo device family The Bamboo devices have multiple interfaces which need to be setup separately. Use the HID parsing mechanism to achieve that. Signed-off-by: Ping Cheng Signed-off-by: Henrik Rydberg Signed-off-by: Dmitry Torokhov --- drivers/input/tablet/wacom_sys.c | 44 ++++++++++++++++++++++++++++++++-------- drivers/input/tablet/wacom_wac.h | 2 ++ 2 files changed, 37 insertions(+), 9 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/tablet/wacom_sys.c b/drivers/input/tablet/wacom_sys.c index e510e4fb54f7..98cba08bb96e 100644 --- a/drivers/input/tablet/wacom_sys.c +++ b/drivers/input/tablet/wacom_sys.c @@ -195,17 +195,30 @@ static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hi features->pktlen = WACOM_PKGLEN_TPC2FG; features->device_type = BTN_TOOL_TRIPLETAP; } - features->x_max = - get_unaligned_le16(&report[i + 3]); - features->x_phy = - get_unaligned_le16(&report[i + 6]); - features->unit = report[i + 9]; - features->unitExpo = report[i + 11]; - i += 12; + if (features->type == BAMBOO_PT) { + /* need to reset back */ + features->pktlen = WACOM_PKGLEN_BBTOUCH; + features->device_type = BTN_TOOL_TRIPLETAP; + features->x_phy = + get_unaligned_le16(&report[i + 5]); + features->x_max = + get_unaligned_le16(&report[i + 8]); + i += 15; + } else { + features->x_max = + get_unaligned_le16(&report[i + 3]); + features->x_phy = + get_unaligned_le16(&report[i + 6]); + features->unit = report[i + 9]; + features->unitExpo = report[i + 11]; + i += 12; + } } else if (pen) { /* penabled only accepts exact bytes of data */ if (features->type == TABLETPC2FG) features->pktlen = WACOM_PKGLEN_GRAPHIRE; + if (features->type == BAMBOO_PT) + features->pktlen = WACOM_PKGLEN_BBFUN; features->device_type = BTN_TOOL_PEN; features->x_max = get_unaligned_le16(&report[i + 3]); @@ -234,6 +247,15 @@ static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hi features->y_phy = get_unaligned_le16(&report[i + 6]); i += 7; + } else if (features->type == BAMBOO_PT) { + /* need to reset back */ + features->pktlen = WACOM_PKGLEN_BBTOUCH; + features->device_type = BTN_TOOL_TRIPLETAP; + features->y_phy = + get_unaligned_le16(&report[i + 3]); + features->y_max = + get_unaligned_le16(&report[i + 6]); + i += 12; } else { features->y_max = features->x_max; @@ -245,6 +267,8 @@ static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hi /* penabled only accepts exact bytes of data */ if (features->type == TABLETPC2FG) features->pktlen = WACOM_PKGLEN_GRAPHIRE; + if (features->type == BAMBOO_PT) + features->pktlen = WACOM_PKGLEN_BBFUN; features->device_type = BTN_TOOL_PEN; features->y_max = get_unaligned_le16(&report[i + 3]); @@ -341,7 +365,8 @@ static int wacom_retrieve_hid_descriptor(struct usb_interface *intf, features->distance_fuzz = 0; /* only Tablet PCs need to retrieve the info */ - if ((features->type != TABLETPC) && (features->type != TABLETPC2FG)) + if ((features->type != TABLETPC) && (features->type != TABLETPC2FG) && + (features->type != BAMBOO_PT)) goto out; if (usb_get_extra_descriptor(interface, HID_DEVICET_HID, &hid_desc)) { @@ -499,7 +524,8 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i strlcpy(wacom_wac->name, features->name, sizeof(wacom_wac->name)); - if (features->type == TABLETPC || features->type == TABLETPC2FG) { + if (features->type == TABLETPC || features->type == TABLETPC2FG || + features->type == BAMBOO_PT) { /* Append the device type to the name */ strlcat(wacom_wac->name, features->device_type == BTN_TOOL_PEN ? diff --git a/drivers/input/tablet/wacom_wac.h b/drivers/input/tablet/wacom_wac.h index d769e9aa6f10..fb30895d63e3 100644 --- a/drivers/input/tablet/wacom_wac.h +++ b/drivers/input/tablet/wacom_wac.h @@ -21,6 +21,7 @@ #define WACOM_PKGLEN_INTUOS 10 #define WACOM_PKGLEN_TPC1FG 5 #define WACOM_PKGLEN_TPC2FG 14 +#define WACOM_PKGLEN_BBTOUCH 20 /* device IDs */ #define STYLUS_DEVICE_ID 0x02 @@ -44,6 +45,7 @@ enum { PTU, PL, DTU, + BAMBOO_PT, INTUOS, INTUOS3S, INTUOS3, -- cgit v1.2.2 From bc73dd39e78dd6e5b34cd938b7f037a8bc041bdd Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Sun, 5 Sep 2010 12:26:16 -0700 Subject: Input: wacom - collect device quirks into single function Collect device-specific code into a single function, and use quirks to flag specific behavior instead. Signed-off-by: Henrik Rydberg Acked-by: Ping Cheng Signed-off-by: Dmitry Torokhov --- drivers/input/tablet/wacom.h | 1 + drivers/input/tablet/wacom_sys.c | 11 +++-------- drivers/input/tablet/wacom_wac.c | 16 ++++++++++++++++ drivers/input/tablet/wacom_wac.h | 4 ++++ 4 files changed, 24 insertions(+), 8 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/tablet/wacom.h b/drivers/input/tablet/wacom.h index 284dfaab6b8c..de5adb109030 100644 --- a/drivers/input/tablet/wacom.h +++ b/drivers/input/tablet/wacom.h @@ -118,6 +118,7 @@ struct wacom { extern const struct usb_device_id wacom_ids[]; void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len); +void wacom_setup_device_quirks(struct wacom_features *features); void wacom_setup_input_capabilities(struct input_dev *input_dev, struct wacom_wac *wacom_wac); #endif diff --git a/drivers/input/tablet/wacom_sys.c b/drivers/input/tablet/wacom_sys.c index 98cba08bb96e..fc6fd53c1af1 100644 --- a/drivers/input/tablet/wacom_sys.c +++ b/drivers/input/tablet/wacom_sys.c @@ -381,12 +381,6 @@ static int wacom_retrieve_hid_descriptor(struct usb_interface *intf, if (error) goto out; - /* touch device found but size is not defined. use default */ - if (features->device_type == BTN_TOOL_DOUBLETAP && !features->x_max) { - features->x_max = 1023; - features->y_max = 1023; - } - out: return error; } @@ -522,10 +516,11 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i if (error) goto fail2; + wacom_setup_device_quirks(features); + strlcpy(wacom_wac->name, features->name, sizeof(wacom_wac->name)); - if (features->type == TABLETPC || features->type == TABLETPC2FG || - features->type == BAMBOO_PT) { + if (features->quirks & WACOM_QUIRK_MULTI_INPUT) { /* Append the device type to the name */ strlcat(wacom_wac->name, features->device_type == BTN_TOOL_PEN ? diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/input/tablet/wacom_wac.c index 6d7e1647715f..44b4a59750d8 100644 --- a/drivers/input/tablet/wacom_wac.c +++ b/drivers/input/tablet/wacom_wac.c @@ -941,6 +941,22 @@ static void wacom_setup_intuos(struct wacom_wac *wacom_wac) input_set_abs_params(input_dev, ABS_THROTTLE, -1023, 1023, 0, 0); } + +void wacom_setup_device_quirks(struct wacom_features *features) +{ + + /* touch device found but size is not defined. use default */ + if (features->device_type == BTN_TOOL_DOUBLETAP && !features->x_max) { + features->x_max = 1023; + features->y_max = 1023; + } + + /* these device have multiple inputs */ + if (features->type == TABLETPC || features->type == TABLETPC2FG || + features->type == BAMBOO_PT) + features->quirks |= WACOM_QUIRK_MULTI_INPUT; +} + void wacom_setup_input_capabilities(struct input_dev *input_dev, struct wacom_wac *wacom_wac) { diff --git a/drivers/input/tablet/wacom_wac.h b/drivers/input/tablet/wacom_wac.h index fb30895d63e3..6a1ff10b095a 100644 --- a/drivers/input/tablet/wacom_wac.h +++ b/drivers/input/tablet/wacom_wac.h @@ -38,6 +38,9 @@ #define WACOM_REPORT_TPC1FG 6 #define WACOM_REPORT_TPC2FG 13 +/* device quirks */ +#define WACOM_QUIRK_MULTI_INPUT 0x0001 + enum { PENPARTNER = 0, GRAPHIRE, @@ -79,6 +82,7 @@ struct wacom_features { int y_fuzz; int pressure_fuzz; int distance_fuzz; + unsigned quirks; }; struct wacom_shared { -- cgit v1.2.2 From cb734c03680eaaad64a20a666300eafd1ac260b2 Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Sun, 5 Sep 2010 12:53:16 -0700 Subject: Input: wacom - add support for the Bamboo Touch trackpad Add support for the Bamboo Touch trackpad, and make it work with both the Synaptics X Driver and the Multitouch X Driver. The device uses MT slots internally, so the choice of protocol is a given. Signed-off-by: Henrik Rydberg Acked-by: Ping Cheng Signed-off-by: Dmitry Torokhov --- drivers/input/tablet/wacom_wac.c | 88 ++++++++++++++++++++++++++++++++++++++++ drivers/input/tablet/wacom_wac.h | 4 ++ 2 files changed, 92 insertions(+) (limited to 'drivers/input') diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/input/tablet/wacom_wac.c index 44b4a59750d8..4e9b1ddf54e9 100644 --- a/drivers/input/tablet/wacom_wac.c +++ b/drivers/input/tablet/wacom_wac.c @@ -855,6 +855,53 @@ static int wacom_tpc_irq(struct wacom_wac *wacom, size_t len) return retval; } +static int wacom_bpt_irq(struct wacom_wac *wacom, size_t len) +{ + struct input_dev *input = wacom->input; + unsigned char *data = wacom->data; + int sp = 0, sx = 0, sy = 0, count = 0; + int i; + + if (len != WACOM_PKGLEN_BBTOUCH) + return 0; + + for (i = 0; i < 2; i++) { + int p = data[9 * i + 2]; + input_mt_slot(input, i); + if (p) { + int x = get_unaligned_be16(&data[9 * i + 3]) & 0x7ff; + int y = get_unaligned_be16(&data[9 * i + 5]) & 0x7ff; + input_report_abs(input, ABS_MT_PRESSURE, p); + input_report_abs(input, ABS_MT_POSITION_X, x); + input_report_abs(input, ABS_MT_POSITION_Y, y); + if (wacom->id[i] < 0) + wacom->id[i] = wacom->trk_id++ & MAX_TRACKING_ID; + if (!count++) + sp = p, sx = x, sy = y; + } else { + wacom->id[i] = -1; + } + input_report_abs(input, ABS_MT_TRACKING_ID, wacom->id[i]); + } + + input_report_key(input, BTN_TOUCH, count > 0); + input_report_key(input, BTN_TOOL_FINGER, count == 1); + input_report_key(input, BTN_TOOL_DOUBLETAP, count == 2); + + input_report_abs(input, ABS_PRESSURE, sp); + input_report_abs(input, ABS_X, sx); + input_report_abs(input, ABS_Y, sy); + + input_report_key(input, BTN_LEFT, (data[1] & 0x08) != 0); + input_report_key(input, BTN_FORWARD, (data[1] & 0x04) != 0); + input_report_key(input, BTN_BACK, (data[1] & 0x02) != 0); + input_report_key(input, BTN_RIGHT, (data[1] & 0x01) != 0); + + input_sync(input); + + return 0; +} + void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len) { bool sync; @@ -900,6 +947,10 @@ void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len) sync = wacom_tpc_irq(wacom_wac, len); break; + case BAMBOO_PT: + sync = wacom_bpt_irq(wacom_wac, len); + break; + default: sync = false; break; @@ -955,6 +1006,13 @@ void wacom_setup_device_quirks(struct wacom_features *features) if (features->type == TABLETPC || features->type == TABLETPC2FG || features->type == BAMBOO_PT) features->quirks |= WACOM_QUIRK_MULTI_INPUT; + + /* quirks for bamboo touch */ + if (features->type == BAMBOO_PT && + features->device_type == BTN_TOOL_TRIPLETAP) { + features->pressure_max = 256; + features->pressure_fuzz = 16; + } } void wacom_setup_input_capabilities(struct input_dev *input_dev, @@ -1095,6 +1153,33 @@ void wacom_setup_input_capabilities(struct input_dev *input_dev, case PENPARTNER: __set_bit(BTN_TOOL_RUBBER, input_dev->keybit); break; + + case BAMBOO_PT: + __clear_bit(ABS_MISC, input_dev->absbit); + + if (features->device_type == BTN_TOOL_TRIPLETAP) { + __set_bit(BTN_LEFT, input_dev->keybit); + __set_bit(BTN_FORWARD, input_dev->keybit); + __set_bit(BTN_BACK, input_dev->keybit); + __set_bit(BTN_RIGHT, input_dev->keybit); + + __set_bit(BTN_TOOL_FINGER, input_dev->keybit); + __set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit); + + input_mt_create_slots(input_dev, 2); + input_set_abs_params(input_dev, ABS_MT_POSITION_X, + 0, features->x_max, + features->x_fuzz, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_Y, + 0, features->y_max, + features->y_fuzz, 0); + input_set_abs_params(input_dev, ABS_MT_PRESSURE, + 0, features->pressure_max, + features->pressure_fuzz, 0); + input_set_abs_params(input_dev, ABS_MT_TRACKING_ID, 0, + MAX_TRACKING_ID, 0, 0); + } + break; } } @@ -1232,6 +1317,8 @@ static const struct wacom_features wacom_features_0xE3 = { "Wacom ISDv4 E3", WACOM_PKGLEN_TPC2FG, 26202, 16325, 255, 0, TABLETPC2FG }; static const struct wacom_features wacom_features_0x47 = { "Wacom Intuos2 6x8", WACOM_PKGLEN_INTUOS, 20320, 16240, 1023, 31, INTUOS }; +static struct wacom_features wacom_features_0xD0 = + { "Wacom Bamboo 2FG", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023, 63, BAMBOO_PT }; #define USB_DEVICE_WACOM(prod) \ USB_DEVICE(USB_VENDOR_ID_WACOM, prod), \ @@ -1296,6 +1383,7 @@ const struct usb_device_id wacom_ids[] = { { USB_DEVICE_WACOM(0xC6) }, { USB_DEVICE_WACOM(0xC7) }, { USB_DEVICE_WACOM(0xCE) }, + { USB_DEVICE_WACOM(0xD0) }, { USB_DEVICE_WACOM(0xF0) }, { USB_DEVICE_WACOM(0xCC) }, { USB_DEVICE_WACOM(0x90) }, diff --git a/drivers/input/tablet/wacom_wac.h b/drivers/input/tablet/wacom_wac.h index 6a1ff10b095a..a23d6a5fcc4f 100644 --- a/drivers/input/tablet/wacom_wac.h +++ b/drivers/input/tablet/wacom_wac.h @@ -41,6 +41,9 @@ /* device quirks */ #define WACOM_QUIRK_MULTI_INPUT 0x0001 +/* largest reported tracking id */ +#define MAX_TRACKING_ID 0xfff + enum { PENPARTNER = 0, GRAPHIRE, @@ -96,6 +99,7 @@ struct wacom_wac { int id[3]; __u32 serial[2]; int last_finger; + int trk_id; struct wacom_features features; struct wacom_shared *shared; struct input_dev *input; -- cgit v1.2.2 From f4ccbef2886968ed409939531f6dd0474d53a12a Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Sun, 5 Sep 2010 12:57:13 -0700 Subject: Input: wacom - add a quirk for low resolution Bamboo devices The Bamboo Touch reports a sub-screen resolution of 480x320. The signal-to-noise ratio is only about 100, so filtering is needed in order to reduce the jitter to a usable level. However, the low resolution leads to round-off errors in the EWMA filter, resulting in extremely jerky pointer motion. This patch explicitly sets a higher resolution for those devices, and tells this to the completion handler via a low-resolution quirk. Signed-off-by: Henrik Rydberg Acked-by: Ping Cheng Signed-off-by: Dmitry Torokhov --- drivers/input/tablet/wacom_wac.c | 10 ++++++++++ drivers/input/tablet/wacom_wac.h | 1 + 2 files changed, 11 insertions(+) (limited to 'drivers/input') diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/input/tablet/wacom_wac.c index 4e9b1ddf54e9..2f4411aee305 100644 --- a/drivers/input/tablet/wacom_wac.c +++ b/drivers/input/tablet/wacom_wac.c @@ -857,6 +857,7 @@ static int wacom_tpc_irq(struct wacom_wac *wacom, size_t len) static int wacom_bpt_irq(struct wacom_wac *wacom, size_t len) { + struct wacom_features *features = &wacom->features; struct input_dev *input = wacom->input; unsigned char *data = wacom->data; int sp = 0, sx = 0, sy = 0, count = 0; @@ -871,6 +872,10 @@ static int wacom_bpt_irq(struct wacom_wac *wacom, size_t len) if (p) { int x = get_unaligned_be16(&data[9 * i + 3]) & 0x7ff; int y = get_unaligned_be16(&data[9 * i + 5]) & 0x7ff; + if (features->quirks & WACOM_QUIRK_BBTOUCH_LOWRES) { + x <<= 5; + y <<= 5; + } input_report_abs(input, ABS_MT_PRESSURE, p); input_report_abs(input, ABS_MT_POSITION_X, x); input_report_abs(input, ABS_MT_POSITION_Y, y); @@ -1010,8 +1015,13 @@ void wacom_setup_device_quirks(struct wacom_features *features) /* quirks for bamboo touch */ if (features->type == BAMBOO_PT && features->device_type == BTN_TOOL_TRIPLETAP) { + features->x_max <<= 5; + features->y_max <<= 5; + features->x_fuzz <<= 5; + features->y_fuzz <<= 5; features->pressure_max = 256; features->pressure_fuzz = 16; + features->quirks |= WACOM_QUIRK_BBTOUCH_LOWRES; } } diff --git a/drivers/input/tablet/wacom_wac.h b/drivers/input/tablet/wacom_wac.h index a23d6a5fcc4f..00ca01541d89 100644 --- a/drivers/input/tablet/wacom_wac.h +++ b/drivers/input/tablet/wacom_wac.h @@ -40,6 +40,7 @@ /* device quirks */ #define WACOM_QUIRK_MULTI_INPUT 0x0001 +#define WACOM_QUIRK_BBTOUCH_LOWRES 0x0002 /* largest reported tracking id */ #define MAX_TRACKING_ID 0xfff -- cgit v1.2.2 From a4e6aad64735702256e4feaa4724eb776ca4e637 Mon Sep 17 00:00:00 2001 From: James Ketrenos Date: Mon, 6 Sep 2010 12:49:33 -0700 Subject: Input: cy8ctmg110 - add fuzz to ABS_X and ABS_Y to remove jitter Without this the jitter on the touchscreen makes it hard to use for most GUI toolkits. Signed-off-by: James Ketrenos Signed-off-by: Alan Cox Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/cy8ctmg110_ts.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/cy8ctmg110_ts.c b/drivers/input/touchscreen/cy8ctmg110_ts.c index 5ec0946938fe..d0c3a7229adf 100644 --- a/drivers/input/touchscreen/cy8ctmg110_ts.c +++ b/drivers/input/touchscreen/cy8ctmg110_ts.c @@ -206,9 +206,9 @@ static int __devinit cy8ctmg110_probe(struct i2c_client *client, input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); input_set_abs_params(input_dev, ABS_X, - CY8CTMG110_X_MIN, CY8CTMG110_X_MAX, 0, 0); + CY8CTMG110_X_MIN, CY8CTMG110_X_MAX, 4, 0); input_set_abs_params(input_dev, ABS_Y, - CY8CTMG110_Y_MIN, CY8CTMG110_Y_MAX, 0, 0); + CY8CTMG110_Y_MIN, CY8CTMG110_Y_MAX, 4, 0); if (ts->reset_pin) { err = gpio_request(ts->reset_pin, NULL); -- cgit v1.2.2 From 8613e4c2872a87cc309a42de2c7091744dc54d0e Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 9 Sep 2010 21:54:22 -0700 Subject: Input: add support for large scancodes Several devices use a high number of bits for scancodes. One important group is the Remote Controllers. Some new protocols like RC-6 define a scancode space of 64 bits. The current EVIO[CS]GKEYCODE ioctls allow replace the scancode/keycode translation tables, but it is limited to up to 32 bits for scancode. Also, if userspace wants to clean the existing table, replacing it by a new one, it needs to run a loop calling the ioctls over the entire sparse scancode space. To solve those problems, this patch extends the ioctls to allow drivers handle scancodes up to 32 bytes long (the length could be extended in the future should such need arise) and allow userspace to query and set scancode to keycode mappings not only by scancode but also by index. Compatibility code were also added to handle the old format of EVIO[CS]GKEYCODE ioctls. Folded fixes by: - Dan Carpenter: locking fixes for the original implementation - Jarod Wilson: fix crash when setting keycode and wiring up get/set handlers in original implementation. - Dmitry Torokhov: rework to consolidate old and new scancode handling, provide options to act either by index or scancode. Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Dan Carpenter Signed-off-by: Jarod Wilson Signed-off-by: Dmitry Torokhov --- drivers/input/evdev.c | 100 ++++++++++++++++++++------ drivers/input/input.c | 192 +++++++++++++++++++++++++++++++++++++------------- 2 files changed, 224 insertions(+), 68 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index c908c5f83645..1ce9bf663206 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -534,6 +534,80 @@ static int handle_eviocgbit(struct input_dev *dev, } #undef OLD_KEY_MAX +static int evdev_handle_get_keycode(struct input_dev *dev, + void __user *p, size_t size) +{ + struct input_keymap_entry ke; + int error; + + memset(&ke, 0, sizeof(ke)); + + if (size == sizeof(unsigned int[2])) { + /* legacy case */ + int __user *ip = (int __user *)p; + + if (copy_from_user(ke.scancode, p, sizeof(unsigned int))) + return -EFAULT; + + ke.len = sizeof(unsigned int); + ke.flags = 0; + + error = input_get_keycode(dev, &ke); + if (error) + return error; + + if (put_user(ke.keycode, ip + 1)) + return -EFAULT; + + } else { + size = min(size, sizeof(ke)); + + if (copy_from_user(&ke, p, size)) + return -EFAULT; + + error = input_get_keycode(dev, &ke); + if (error) + return error; + + if (copy_to_user(p, &ke, size)) + return -EFAULT; + } + return 0; +} + +static int evdev_handle_set_keycode(struct input_dev *dev, + void __user *p, size_t size) +{ + struct input_keymap_entry ke; + + memset(&ke, 0, sizeof(ke)); + + if (size == sizeof(unsigned int[2])) { + /* legacy case */ + int __user *ip = (int __user *)p; + + if (copy_from_user(ke.scancode, p, sizeof(unsigned int))) + return -EFAULT; + + if (get_user(ke.keycode, ip + 1)) + return -EFAULT; + + ke.len = sizeof(unsigned int); + ke.flags = 0; + + } else { + size = min(size, sizeof(ke)); + + if (copy_from_user(&ke, p, size)) + return -EFAULT; + + if (ke.len > sizeof(ke.scancode)) + return -EINVAL; + } + + return input_set_keycode(dev, &ke); +} + static long evdev_do_ioctl(struct file *file, unsigned int cmd, void __user *p, int compat_mode) { @@ -580,25 +654,6 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd, return 0; - case EVIOCGKEYCODE: - if (get_user(t, ip)) - return -EFAULT; - - error = input_get_keycode(dev, t, &v); - if (error) - return error; - - if (put_user(v, ip + 1)) - return -EFAULT; - - return 0; - - case EVIOCSKEYCODE: - if (get_user(t, ip) || get_user(v, ip + 1)) - return -EFAULT; - - return input_set_keycode(dev, t, v); - case EVIOCRMFF: return input_ff_erase(dev, (int)(unsigned long) p, file); @@ -620,7 +675,6 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd, /* Now check variable-length commands */ #define EVIOC_MASK_SIZE(nr) ((nr) & ~(_IOC_SIZEMASK << _IOC_SIZESHIFT)) - switch (EVIOC_MASK_SIZE(cmd)) { case EVIOCGKEY(0): @@ -654,6 +708,12 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd, return -EFAULT; return error; + + case EVIOC_MASK_SIZE(EVIOCGKEYCODE): + return evdev_handle_get_keycode(dev, p, size); + + case EVIOC_MASK_SIZE(EVIOCSKEYCODE): + return evdev_handle_set_keycode(dev, p, size); } /* Multi-number variable-length handlers */ diff --git a/drivers/input/input.c b/drivers/input/input.c index acb3c8095c65..832771e73663 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -634,78 +634,141 @@ static void input_disconnect_device(struct input_dev *dev) spin_unlock_irq(&dev->event_lock); } -static int input_fetch_keycode(struct input_dev *dev, int scancode) +/** + * input_scancode_to_scalar() - converts scancode in &struct input_keymap_entry + * @ke: keymap entry containing scancode to be converted. + * @scancode: pointer to the location where converted scancode should + * be stored. + * + * This function is used to convert scancode stored in &struct keymap_entry + * into scalar form understood by legacy keymap handling methods. These + * methods expect scancodes to be represented as 'unsigned int'. + */ +int input_scancode_to_scalar(const struct input_keymap_entry *ke, + unsigned int *scancode) +{ + switch (ke->len) { + case 1: + *scancode = *((u8 *)ke->scancode); + break; + + case 2: + *scancode = *((u16 *)ke->scancode); + break; + + case 4: + *scancode = *((u32 *)ke->scancode); + break; + + default: + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL(input_scancode_to_scalar); + +/* + * Those routines handle the default case where no [gs]etkeycode() is + * defined. In this case, an array indexed by the scancode is used. + */ + +static unsigned int input_fetch_keycode(struct input_dev *dev, + unsigned int index) { switch (dev->keycodesize) { - case 1: - return ((u8 *)dev->keycode)[scancode]; + case 1: + return ((u8 *)dev->keycode)[index]; - case 2: - return ((u16 *)dev->keycode)[scancode]; + case 2: + return ((u16 *)dev->keycode)[index]; - default: - return ((u32 *)dev->keycode)[scancode]; + default: + return ((u32 *)dev->keycode)[index]; } } static int input_default_getkeycode(struct input_dev *dev, - unsigned int scancode, - unsigned int *keycode) + struct input_keymap_entry *ke) { + unsigned int index; + int error; + if (!dev->keycodesize) return -EINVAL; - if (scancode >= dev->keycodemax) + if (ke->flags & INPUT_KEYMAP_BY_INDEX) + index = ke->index; + else { + error = input_scancode_to_scalar(ke, &index); + if (error) + return error; + } + + if (index >= dev->keycodemax) return -EINVAL; - *keycode = input_fetch_keycode(dev, scancode); + ke->keycode = input_fetch_keycode(dev, index); + ke->index = index; + ke->len = sizeof(index); + memcpy(ke->scancode, &index, sizeof(index)); return 0; } static int input_default_setkeycode(struct input_dev *dev, - unsigned int scancode, - unsigned int keycode) + const struct input_keymap_entry *ke, + unsigned int *old_keycode) { - int old_keycode; + unsigned int index; + int error; int i; - if (scancode >= dev->keycodemax) + if (!dev->keycodesize) return -EINVAL; - if (!dev->keycodesize) + if (ke->flags & INPUT_KEYMAP_BY_INDEX) { + index = ke->index; + } else { + error = input_scancode_to_scalar(ke, &index); + if (error) + return error; + } + + if (index >= dev->keycodemax) return -EINVAL; - if (dev->keycodesize < sizeof(keycode) && (keycode >> (dev->keycodesize * 8))) + if (dev->keycodesize < sizeof(dev->keycode) && + (ke->keycode >> (dev->keycodesize * 8))) return -EINVAL; switch (dev->keycodesize) { case 1: { u8 *k = (u8 *)dev->keycode; - old_keycode = k[scancode]; - k[scancode] = keycode; + *old_keycode = k[index]; + k[index] = ke->keycode; break; } case 2: { u16 *k = (u16 *)dev->keycode; - old_keycode = k[scancode]; - k[scancode] = keycode; + *old_keycode = k[index]; + k[index] = ke->keycode; break; } default: { u32 *k = (u32 *)dev->keycode; - old_keycode = k[scancode]; - k[scancode] = keycode; + *old_keycode = k[index]; + k[index] = ke->keycode; break; } } - __clear_bit(old_keycode, dev->keybit); - __set_bit(keycode, dev->keybit); + __clear_bit(*old_keycode, dev->keybit); + __set_bit(ke->keycode, dev->keybit); for (i = 0; i < dev->keycodemax; i++) { - if (input_fetch_keycode(dev, i) == old_keycode) { - __set_bit(old_keycode, dev->keybit); + if (input_fetch_keycode(dev, i) == *old_keycode) { + __set_bit(*old_keycode, dev->keybit); break; /* Setting the bit twice is useless, so break */ } } @@ -716,53 +779,86 @@ static int input_default_setkeycode(struct input_dev *dev, /** * input_get_keycode - retrieve keycode currently mapped to a given scancode * @dev: input device which keymap is being queried - * @scancode: scancode (or its equivalent for device in question) for which - * keycode is needed - * @keycode: result + * @ke: keymap entry * * This function should be called by anyone interested in retrieving current - * keymap. Presently keyboard and evdev handlers use it. + * keymap. Presently evdev handlers use it. */ -int input_get_keycode(struct input_dev *dev, - unsigned int scancode, unsigned int *keycode) +int input_get_keycode(struct input_dev *dev, struct input_keymap_entry *ke) { unsigned long flags; int retval; spin_lock_irqsave(&dev->event_lock, flags); - retval = dev->getkeycode(dev, scancode, keycode); - spin_unlock_irqrestore(&dev->event_lock, flags); + if (dev->getkeycode) { + /* + * Support for legacy drivers, that don't implement the new + * ioctls + */ + u32 scancode = ke->index; + + memcpy(ke->scancode, &scancode, sizeof(scancode)); + ke->len = sizeof(scancode); + retval = dev->getkeycode(dev, scancode, &ke->keycode); + } else { + retval = dev->getkeycode_new(dev, ke); + } + + spin_unlock_irqrestore(&dev->event_lock, flags); return retval; } EXPORT_SYMBOL(input_get_keycode); /** - * input_get_keycode - assign new keycode to a given scancode + * input_set_keycode - attribute a keycode to a given scancode * @dev: input device which keymap is being updated - * @scancode: scancode (or its equivalent for device in question) - * @keycode: new keycode to be assigned to the scancode + * @ke: new keymap entry * * This function should be called by anyone needing to update current * keymap. Presently keyboard and evdev handlers use it. */ int input_set_keycode(struct input_dev *dev, - unsigned int scancode, unsigned int keycode) + const struct input_keymap_entry *ke) { unsigned long flags; unsigned int old_keycode; int retval; - if (keycode > KEY_MAX) + if (ke->keycode > KEY_MAX) return -EINVAL; spin_lock_irqsave(&dev->event_lock, flags); - retval = dev->getkeycode(dev, scancode, &old_keycode); - if (retval) - goto out; + if (dev->setkeycode) { + /* + * Support for legacy drivers, that don't implement the new + * ioctls + */ + unsigned int scancode; + + retval = input_scancode_to_scalar(ke, &scancode); + if (retval) + goto out; + + /* + * We need to know the old scancode, in order to generate a + * keyup effect, if the set operation happens successfully + */ + if (!dev->getkeycode) { + retval = -EINVAL; + goto out; + } + + retval = dev->getkeycode(dev, scancode, &old_keycode); + if (retval) + goto out; + + retval = dev->setkeycode(dev, scancode, ke->keycode); + } else { + retval = dev->setkeycode_new(dev, ke, &old_keycode); + } - retval = dev->setkeycode(dev, scancode, keycode); if (retval) goto out; @@ -1759,11 +1855,11 @@ int input_register_device(struct input_dev *dev) dev->rep[REP_PERIOD] = 33; } - if (!dev->getkeycode) - dev->getkeycode = input_default_getkeycode; + if (!dev->getkeycode && !dev->getkeycode_new) + dev->getkeycode_new = input_default_getkeycode; - if (!dev->setkeycode) - dev->setkeycode = input_default_setkeycode; + if (!dev->setkeycode && !dev->setkeycode_new) + dev->setkeycode_new = input_default_setkeycode; dev_set_name(&dev->dev, "input%ld", (unsigned long) atomic_inc_return(&input_no) - 1); -- cgit v1.2.2 From 67127f3061cc486572a50990a1fd919ddde48c40 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 9 Sep 2010 21:54:22 -0700 Subject: Input: sparse-keymap - switch to using new keycode interface Switch sparse keymap library to use new style of getkeycode and setkeycode methods to allow retrieving and setting keycodes not only by their scancodes but also by index. Acked-by: Mauro Carvalho Chehab Signed-off-by: Dmitry Torokhov --- drivers/input/sparse-keymap.c | 81 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 65 insertions(+), 16 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/sparse-keymap.c b/drivers/input/sparse-keymap.c index 014248344763..a29a7812bd46 100644 --- a/drivers/input/sparse-keymap.c +++ b/drivers/input/sparse-keymap.c @@ -22,6 +22,37 @@ MODULE_DESCRIPTION("Generic support for sparse keymaps"); MODULE_LICENSE("GPL v2"); MODULE_VERSION("0.1"); +static unsigned int sparse_keymap_get_key_index(struct input_dev *dev, + const struct key_entry *k) +{ + struct key_entry *key; + unsigned int idx = 0; + + for (key = dev->keycode; key->type != KE_END; key++) { + if (key->type == KE_KEY) { + if (key == k) + break; + idx++; + } + } + + return idx; +} + +static struct key_entry *sparse_keymap_entry_by_index(struct input_dev *dev, + unsigned int index) +{ + struct key_entry *key; + unsigned int key_cnt = 0; + + for (key = dev->keycode; key->type != KE_END; key++) + if (key->type == KE_KEY) + if (key_cnt++ == index) + return key; + + return NULL; +} + /** * sparse_keymap_entry_from_scancode - perform sparse keymap lookup * @dev: Input device using sparse keymap @@ -64,16 +95,36 @@ struct key_entry *sparse_keymap_entry_from_keycode(struct input_dev *dev, } EXPORT_SYMBOL(sparse_keymap_entry_from_keycode); +static struct key_entry *sparse_keymap_locate(struct input_dev *dev, + const struct input_keymap_entry *ke) +{ + struct key_entry *key; + unsigned int scancode; + + if (ke->flags & INPUT_KEYMAP_BY_INDEX) + key = sparse_keymap_entry_by_index(dev, ke->index); + else if (input_scancode_to_scalar(ke, &scancode) == 0) + key = sparse_keymap_entry_from_scancode(dev, scancode); + else + key = NULL; + + return key; +} + static int sparse_keymap_getkeycode(struct input_dev *dev, - unsigned int scancode, - unsigned int *keycode) + struct input_keymap_entry *ke) { const struct key_entry *key; if (dev->keycode) { - key = sparse_keymap_entry_from_scancode(dev, scancode); + key = sparse_keymap_locate(dev, ke); if (key && key->type == KE_KEY) { - *keycode = key->keycode; + ke->keycode = key->keycode; + if (!(ke->flags & INPUT_KEYMAP_BY_INDEX)) + ke->index = + sparse_keymap_get_key_index(dev, key); + ke->len = sizeof(key->code); + memcpy(ke->scancode, &key->code, sizeof(key->code)); return 0; } } @@ -82,20 +133,19 @@ static int sparse_keymap_getkeycode(struct input_dev *dev, } static int sparse_keymap_setkeycode(struct input_dev *dev, - unsigned int scancode, - unsigned int keycode) + const struct input_keymap_entry *ke, + unsigned int *old_keycode) { struct key_entry *key; - int old_keycode; if (dev->keycode) { - key = sparse_keymap_entry_from_scancode(dev, scancode); + key = sparse_keymap_locate(dev, ke); if (key && key->type == KE_KEY) { - old_keycode = key->keycode; - key->keycode = keycode; - set_bit(keycode, dev->keybit); - if (!sparse_keymap_entry_from_keycode(dev, old_keycode)) - clear_bit(old_keycode, dev->keybit); + *old_keycode = key->keycode; + key->keycode = ke->keycode; + set_bit(ke->keycode, dev->keybit); + if (!sparse_keymap_entry_from_keycode(dev, *old_keycode)) + clear_bit(*old_keycode, dev->keybit); return 0; } } @@ -159,15 +209,14 @@ int sparse_keymap_setup(struct input_dev *dev, dev->keycode = map; dev->keycodemax = map_size; - dev->getkeycode = sparse_keymap_getkeycode; - dev->setkeycode = sparse_keymap_setkeycode; + dev->getkeycode_new = sparse_keymap_getkeycode; + dev->setkeycode_new = sparse_keymap_setkeycode; return 0; err_out: kfree(map); return error; - } EXPORT_SYMBOL(sparse_keymap_setup); -- cgit v1.2.2 From 3dc9f40de4dddf9147b80cf15be633189a2b70f4 Mon Sep 17 00:00:00 2001 From: Chris Bagwell Date: Sun, 12 Sep 2010 00:08:40 -0700 Subject: Input: wacom - request tablet data for Bamboo Pens Bamboo P&T need to use second form of usb_set_report() to ask to report tablet data. With previous addition of Bamboo Touch, BTN_TOOL_TRIPLETAP is now used for both TABLETPC2FG and BAMBOO_PT types. So reduced check to match type=TABLETPC2FG. This change shows redundant check for !TABLETPC2FG in else statement. Signed-off-by: Chris Bagwell Acked-by: Ping Cheng Signed-off-by: Dmitry Torokhov --- drivers/input/tablet/wacom_sys.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/tablet/wacom_sys.c b/drivers/input/tablet/wacom_sys.c index fc6fd53c1af1..1e3af299a1c2 100644 --- a/drivers/input/tablet/wacom_sys.c +++ b/drivers/input/tablet/wacom_sys.c @@ -319,8 +319,9 @@ static int wacom_query_tablet_data(struct usb_interface *intf, struct wacom_feat if (!rep_data) return error; - /* ask to report tablet data if it is 2FGT or not a Tablet PC */ - if (features->device_type == BTN_TOOL_TRIPLETAP) { + /* ask to report tablet data if it is 2FGT Tablet PC or + * not a Tablet PC */ + if (features->type == TABLETPC2FG) { do { rep_data[0] = 3; rep_data[1] = 4; @@ -332,7 +333,7 @@ static int wacom_query_tablet_data(struct usb_interface *intf, struct wacom_feat WAC_HID_FEATURE_REPORT, report_id, rep_data, 3); } while ((error < 0 || rep_data[1] != 4) && limit++ < 5); - } else if (features->type != TABLETPC && features->type != TABLETPC2FG) { + } else if (features->type != TABLETPC) { do { rep_data[0] = 2; rep_data[1] = 2; @@ -364,7 +365,7 @@ static int wacom_retrieve_hid_descriptor(struct usb_interface *intf, features->pressure_fuzz = 0; features->distance_fuzz = 0; - /* only Tablet PCs need to retrieve the info */ + /* only Tablet PCs and Bamboo P&T need to retrieve the info */ if ((features->type != TABLETPC) && (features->type != TABLETPC2FG) && (features->type != BAMBOO_PT)) goto out; -- cgit v1.2.2 From e1d38e49ad97eec5024342e1244279b645e36688 Mon Sep 17 00:00:00 2001 From: Chris Bagwell Date: Sun, 12 Sep 2010 00:09:27 -0700 Subject: Input: wacom - move Bamboo Touch irq to its own function This is in preparation of pen support in same irq handler. Signed-off-by: Chris Bagwell Acked-by: Ping Cheng Signed-off-by: Dmitry Torokhov --- drivers/input/tablet/wacom_wac.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/input/tablet/wacom_wac.c index 2f4411aee305..2f7ed9a631b9 100644 --- a/drivers/input/tablet/wacom_wac.c +++ b/drivers/input/tablet/wacom_wac.c @@ -855,7 +855,7 @@ static int wacom_tpc_irq(struct wacom_wac *wacom, size_t len) return retval; } -static int wacom_bpt_irq(struct wacom_wac *wacom, size_t len) +static int wacom_bpt_touch(struct wacom_wac *wacom) { struct wacom_features *features = &wacom->features; struct input_dev *input = wacom->input; @@ -863,9 +863,6 @@ static int wacom_bpt_irq(struct wacom_wac *wacom, size_t len) int sp = 0, sx = 0, sy = 0, count = 0; int i; - if (len != WACOM_PKGLEN_BBTOUCH) - return 0; - for (i = 0; i < 2; i++) { int p = data[9 * i + 2]; input_mt_slot(input, i); @@ -907,6 +904,14 @@ static int wacom_bpt_irq(struct wacom_wac *wacom, size_t len) return 0; } +static int wacom_bpt_irq(struct wacom_wac *wacom, size_t len) +{ + if (len == WACOM_PKGLEN_BBTOUCH) + return wacom_bpt_touch(wacom); + + return 0; +} + void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len) { bool sync; -- cgit v1.2.2 From 2aaacb153689dbe9064e4db7e9d00de0edfc1fa0 Mon Sep 17 00:00:00 2001 From: Chris Bagwell Date: Sun, 12 Sep 2010 00:11:35 -0700 Subject: Input: wacom - add support for Bamboo Pen This adds support for Pen on Bamboo Pen and Bamboo Pen&Touch devices. Touchpad is handled by previous Bamboo Touch logic. Signed-off-by: Chris Bagwell Acked-by: Ping Cheng Signed-off-by: Dmitry Torokhov --- drivers/input/tablet/wacom_wac.c | 79 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) (limited to 'drivers/input') diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/input/tablet/wacom_wac.c index 2f7ed9a631b9..536156baedfe 100644 --- a/drivers/input/tablet/wacom_wac.c +++ b/drivers/input/tablet/wacom_wac.c @@ -904,10 +904,75 @@ static int wacom_bpt_touch(struct wacom_wac *wacom) return 0; } +static int wacom_bpt_pen(struct wacom_wac *wacom) +{ + struct input_dev *input = wacom->input; + unsigned char *data = wacom->data; + int prox = 0, x = 0, y = 0, p = 0, d = 0, pen = 0, btn1 = 0, btn2 = 0; + + /* + * Similar to Graphire protocol, data[1] & 0x20 is proximity and + * data[1] & 0x18 is tool ID. 0x30 is safety check to ignore + * 2 unused tool ID's. + */ + prox = (data[1] & 0x30) == 0x30; + + /* + * All reports shared between PEN and RUBBER tool must be + * forced to a known starting value (zero) when transitioning to + * out-of-prox. + * + * If not reset then, to userspace, it will look like lost events + * if new tool comes in-prox with same values as previous tool sent. + * + * Hardware does report zero in most out-of-prox cases but not all. + */ + if (prox) { + if (!wacom->shared->stylus_in_proximity) { + if (data[1] & 0x08) { + wacom->tool[0] = BTN_TOOL_RUBBER; + wacom->id[0] = ERASER_DEVICE_ID; + } else { + wacom->tool[0] = BTN_TOOL_PEN; + wacom->id[0] = STYLUS_DEVICE_ID; + } + wacom->shared->stylus_in_proximity = true; + } + x = le16_to_cpup((__le16 *)&data[2]); + y = le16_to_cpup((__le16 *)&data[4]); + p = le16_to_cpup((__le16 *)&data[6]); + d = data[8]; + pen = data[1] & 0x01; + btn1 = data[1] & 0x02; + btn2 = data[1] & 0x04; + } + + input_report_key(input, BTN_TOUCH, pen); + input_report_key(input, BTN_STYLUS, btn1); + input_report_key(input, BTN_STYLUS2, btn2); + + input_report_abs(input, ABS_X, x); + input_report_abs(input, ABS_Y, y); + input_report_abs(input, ABS_PRESSURE, p); + input_report_abs(input, ABS_DISTANCE, d); + + if (!prox) { + wacom->id[0] = 0; + wacom->shared->stylus_in_proximity = false; + } + + input_report_key(input, wacom->tool[0], prox); /* PEN or RUBBER */ + input_report_abs(input, ABS_MISC, wacom->id[0]); /* TOOL ID */ + + return 1; +} + static int wacom_bpt_irq(struct wacom_wac *wacom, size_t len) { if (len == WACOM_PKGLEN_BBTOUCH) return wacom_bpt_touch(wacom); + else if (len == WACOM_PKGLEN_BBFUN) + return wacom_bpt_pen(wacom); return 0; } @@ -1193,6 +1258,11 @@ void wacom_setup_input_capabilities(struct input_dev *input_dev, features->pressure_fuzz, 0); input_set_abs_params(input_dev, ABS_MT_TRACKING_ID, 0, MAX_TRACKING_ID, 0, 0); + } else if (features->device_type == BTN_TOOL_PEN) { + __set_bit(BTN_TOOL_RUBBER, input_dev->keybit); + __set_bit(BTN_TOOL_PEN, input_dev->keybit); + __set_bit(BTN_STYLUS, input_dev->keybit); + __set_bit(BTN_STYLUS2, input_dev->keybit); } break; } @@ -1334,6 +1404,12 @@ static const struct wacom_features wacom_features_0x47 = { "Wacom Intuos2 6x8", WACOM_PKGLEN_INTUOS, 20320, 16240, 1023, 31, INTUOS }; static struct wacom_features wacom_features_0xD0 = { "Wacom Bamboo 2FG", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023, 63, BAMBOO_PT }; +static struct wacom_features wacom_features_0xD1 = + { "Wacom Bamboo 2FG 4x5", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023, 63, BAMBOO_PT }; +static struct wacom_features wacom_features_0xD2 = + { "Wacom Bamboo Craft", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023, 63, BAMBOO_PT }; +static struct wacom_features wacom_features_0xD3 = + { "Wacom Bamboo 2FG 6x8", WACOM_PKGLEN_BBFUN, 21648, 13530, 1023, 63, BAMBOO_PT }; #define USB_DEVICE_WACOM(prod) \ USB_DEVICE(USB_VENDOR_ID_WACOM, prod), \ @@ -1399,6 +1475,9 @@ const struct usb_device_id wacom_ids[] = { { USB_DEVICE_WACOM(0xC7) }, { USB_DEVICE_WACOM(0xCE) }, { USB_DEVICE_WACOM(0xD0) }, + { USB_DEVICE_WACOM(0xD1) }, + { USB_DEVICE_WACOM(0xD2) }, + { USB_DEVICE_WACOM(0xD3) }, { USB_DEVICE_WACOM(0xF0) }, { USB_DEVICE_WACOM(0xCC) }, { USB_DEVICE_WACOM(0x90) }, -- cgit v1.2.2 From 33d5f713a19b0f5cb93e0594f7206d2730cf39da Mon Sep 17 00:00:00 2001 From: Chris Bagwell Date: Sun, 12 Sep 2010 00:12:28 -0700 Subject: Input: wacom - disable Bamboo touchpad when pen is being used Signed-off-by: Chris Bagwell Acked-by: Ping Cheng Signed-off-by: Dmitry Torokhov --- drivers/input/tablet/wacom_wac.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/input/tablet/wacom_wac.c index 536156baedfe..e1b65ba84438 100644 --- a/drivers/input/tablet/wacom_wac.c +++ b/drivers/input/tablet/wacom_wac.c @@ -866,7 +866,13 @@ static int wacom_bpt_touch(struct wacom_wac *wacom) for (i = 0; i < 2; i++) { int p = data[9 * i + 2]; input_mt_slot(input, i); - if (p) { + /* + * Touch events need to be disabled while stylus is + * in proximity because user's hand is resting on touchpad + * and sending unwanted events. User expects tablet buttons + * to continue working though. + */ + if (p && !wacom->shared->stylus_in_proximity) { int x = get_unaligned_be16(&data[9 * i + 3]) & 0x7ff; int y = get_unaligned_be16(&data[9 * i + 5]) & 0x7ff; if (features->quirks & WACOM_QUIRK_BBTOUCH_LOWRES) { -- cgit v1.2.2 From 24dd3b58228d463254ee57c3811afea831b8a024 Mon Sep 17 00:00:00 2001 From: Xing Wei Date: Sun, 12 Sep 2010 00:24:17 -0700 Subject: Input: hanwang - make compatible with xf86-input-wacom driver Add necessary events so that Hanwang Art Master III tablet can be handled by the stock xf86-input-wacom driver. Signed-off-by: Xing Wei Signed-off-by: Dmitry Torokhov --- drivers/input/tablet/hanwang.c | 37 +++++++++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/tablet/hanwang.c b/drivers/input/tablet/hanwang.c index 3554e30ab41b..0701d948cdf3 100644 --- a/drivers/input/tablet/hanwang.c +++ b/drivers/input/tablet/hanwang.c @@ -44,6 +44,13 @@ MODULE_LICENSE(DRIVER_LICENSE); #define ART_MASTERIII_PKGLEN_MAX 10 +/* device IDs */ +#define STYLUS_DEVICE_ID 0x02 +#define TOUCH_DEVICE_ID 0x03 +#define CURSOR_DEVICE_ID 0x06 +#define ERASER_DEVICE_ID 0x0A +#define PAD_DEVICE_ID 0x0F + /* match vendor and interface info */ #define HANWANG_TABLET_DEVICE(vend, cl, sc, pr) \ .match_flags = USB_DEVICE_ID_MATCH_VENDOR \ @@ -61,6 +68,7 @@ struct hanwang { struct urb *irq; const struct hanwang_features *features; unsigned int current_tool; + unsigned int current_id; char name[64]; char phys[32]; }; @@ -86,16 +94,22 @@ static const struct hanwang_features features_array[] = { }; static const int hw_eventtypes[] = { - EV_KEY, EV_ABS, + EV_KEY, EV_ABS, EV_MSC, }; static const int hw_absevents[] = { - ABS_X, ABS_Y, ABS_TILT_X, ABS_TILT_Y, ABS_WHEEL, ABS_PRESSURE, + ABS_X, ABS_Y, ABS_TILT_X, ABS_TILT_Y, ABS_WHEEL, + ABS_PRESSURE, ABS_MISC, }; static const int hw_btnevents[] = { - BTN_STYLUS, BTN_STYLUS2, BTN_TOOL_PEN, BTN_TOOL_RUBBER, BTN_TOOL_MOUSE, - BTN_0, BTN_1, BTN_2, BTN_3, BTN_4, BTN_5, BTN_6, BTN_7, BTN_8 + BTN_STYLUS, BTN_STYLUS2, BTN_TOOL_PEN, BTN_TOOL_RUBBER, + BTN_TOOL_MOUSE, BTN_TOOL_FINGER, + BTN_0, BTN_1, BTN_2, BTN_3, BTN_4, BTN_5, BTN_6, BTN_7, BTN_8, +}; + +static const int hw_mscevents[] = { + MSC_SERIAL, }; static void hanwang_parse_packet(struct hanwang *hanwang) @@ -109,20 +123,24 @@ static void hanwang_parse_packet(struct hanwang *hanwang) case 0x02: /* data packet */ switch (data[1]) { case 0x80: /* tool prox out */ + hanwang->current_id = 0; input_report_key(input_dev, hanwang->current_tool, 0); break; case 0xc2: /* first time tool prox in */ switch (data[3] & 0xf0) { case 0x20: + hanwang->current_id = STYLUS_DEVICE_ID; hanwang->current_tool = BTN_TOOL_PEN; input_report_key(input_dev, BTN_TOOL_PEN, 1); break; case 0xa0: + hanwang->current_id = ERASER_DEVICE_ID; hanwang->current_tool = BTN_TOOL_RUBBER; input_report_key(input_dev, BTN_TOOL_RUBBER, 1); break; default: + hanwang->current_id = 0; dev_dbg(&dev->dev, "unknown tablet tool %02x ", data[0]); break; @@ -144,15 +162,23 @@ static void hanwang_parse_packet(struct hanwang *hanwang) input_report_key(input_dev, BTN_STYLUS2, data[1] & 0x04); break; } + input_report_abs(input_dev, ABS_MISC, hanwang->current_id); + input_event(input_dev, EV_MSC, MSC_SERIAL, + hanwang->features->pid); break; case 0x0c: /* roll wheel */ + hanwang->current_id = PAD_DEVICE_ID; + input_report_key(input_dev, BTN_TOOL_FINGER, data[1] || + data[2] || data[3]); input_report_abs(input_dev, ABS_WHEEL, data[1]); input_report_key(input_dev, BTN_0, data[2]); for (i = 0; i < 8; i++) input_report_key(input_dev, BTN_1 + i, data[3] & (1 << i)); + input_report_abs(input_dev, ABS_MISC, hanwang->current_id); + input_event(input_dev, EV_MSC, MSC_SERIAL, 0xffffffff); break; default: @@ -287,6 +313,9 @@ static int hanwang_probe(struct usb_interface *intf, const struct usb_device_id for (i = 0; i < ARRAY_SIZE(hw_btnevents); ++i) __set_bit(hw_btnevents[i], input_dev->keybit); + for (i = 0; i < ARRAY_SIZE(hw_mscevents); ++i) + __set_bit(hw_mscevents[i], input_dev->mscbit); + input_set_abs_params(input_dev, ABS_X, 0, hanwang->features->max_x, 4, 0); input_set_abs_params(input_dev, ABS_Y, -- cgit v1.2.2 From 53957b56d765f4602715fefb1c553f7a538b3230 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 13 Sep 2010 23:53:55 -0700 Subject: Input: serio - do not include linux/freezer.h Commit ea486e681188d64c6a101d8d06414aef0f9f0cd3 changed kseriod thread to become not freezable so we do not need this include anymore. Signed-off-by: Dmitry Torokhov --- drivers/input/serio/serio.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/serio/serio.c b/drivers/input/serio/serio.c index c3b626e9eae7..8a426375fcb3 100644 --- a/drivers/input/serio/serio.c +++ b/drivers/input/serio/serio.c @@ -37,7 +37,6 @@ #include #include #include -#include MODULE_AUTHOR("Vojtech Pavlik "); MODULE_DESCRIPTION("Serio abstraction core"); -- cgit v1.2.2 From 1f7930c55e1c1a2b6d5793a1002b31590356558c Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 15 Sep 2010 19:36:34 -0700 Subject: Input: ati_remote2 - switch to using new keycode interface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Switch the code to use new style of getkeycode and setkeycode methods to allow retrieving and setting keycodes not only by their scancodes but also by index. Acked-by: Ville Syrjälä Tested-by: Jarod Wilson Tested-by: Ville Syrjälä Signed-off-by: Dmitry Torokhov --- drivers/input/misc/ati_remote2.c | 93 ++++++++++++++++++++++++++++------------ 1 file changed, 65 insertions(+), 28 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/misc/ati_remote2.c b/drivers/input/misc/ati_remote2.c index 23257652b8e8..0b0e9be63542 100644 --- a/drivers/input/misc/ati_remote2.c +++ b/drivers/input/misc/ati_remote2.c @@ -483,51 +483,88 @@ static void ati_remote2_complete_key(struct urb *urb) } static int ati_remote2_getkeycode(struct input_dev *idev, - unsigned int scancode, unsigned int *keycode) + struct input_keymap_entry *ke) { struct ati_remote2 *ar2 = input_get_drvdata(idev); unsigned int mode; - int index; + int offset; + unsigned int index; + unsigned int scancode; + + if (ke->flags & INPUT_KEYMAP_BY_INDEX) { + index = ke->index; + if (index >= ATI_REMOTE2_MODES * + ARRAY_SIZE(ati_remote2_key_table)) + return -EINVAL; + + mode = ke->index / ARRAY_SIZE(ati_remote2_key_table); + offset = ke->index % ARRAY_SIZE(ati_remote2_key_table); + scancode = (mode << 8) + ati_remote2_key_table[offset].hw_code; + } else { + if (input_scancode_to_scalar(ke, &scancode)) + return -EINVAL; + + mode = scancode >> 8; + if (mode > ATI_REMOTE2_PC) + return -EINVAL; + + offset = ati_remote2_lookup(scancode & 0xff); + if (offset < 0) + return -EINVAL; + + index = mode * ARRAY_SIZE(ati_remote2_key_table) + offset; + } - mode = scancode >> 8; - if (mode > ATI_REMOTE2_PC || !((1 << mode) & ar2->mode_mask)) - return -EINVAL; + ke->keycode = ar2->keycode[mode][offset]; + ke->len = sizeof(scancode); + memcpy(&ke->scancode, &scancode, sizeof(scancode)); + ke->index = index; - index = ati_remote2_lookup(scancode & 0xFF); - if (index < 0) - return -EINVAL; - - *keycode = ar2->keycode[mode][index]; return 0; } static int ati_remote2_setkeycode(struct input_dev *idev, - unsigned int scancode, unsigned int keycode) + const struct input_keymap_entry *ke, + unsigned int *old_keycode) { struct ati_remote2 *ar2 = input_get_drvdata(idev); - unsigned int mode, old_keycode; - int index; - - mode = scancode >> 8; - if (mode > ATI_REMOTE2_PC || !((1 << mode) & ar2->mode_mask)) - return -EINVAL; - - index = ati_remote2_lookup(scancode & 0xFF); - if (index < 0) - return -EINVAL; + unsigned int mode; + int offset; + unsigned int index; + unsigned int scancode; + + if (ke->flags & INPUT_KEYMAP_BY_INDEX) { + if (ke->index >= ATI_REMOTE2_MODES * + ARRAY_SIZE(ati_remote2_key_table)) + return -EINVAL; + + mode = ke->index / ARRAY_SIZE(ati_remote2_key_table); + offset = ke->index % ARRAY_SIZE(ati_remote2_key_table); + } else { + if (input_scancode_to_scalar(ke, &scancode)) + return -EINVAL; + + mode = scancode >> 8; + if (mode > ATI_REMOTE2_PC) + return -EINVAL; + + offset = ati_remote2_lookup(scancode & 0xff); + if (offset < 0) + return -EINVAL; + } - old_keycode = ar2->keycode[mode][index]; - ar2->keycode[mode][index] = keycode; - __set_bit(keycode, idev->keybit); + *old_keycode = ar2->keycode[mode][offset]; + ar2->keycode[mode][offset] = ke->keycode; + __set_bit(ke->keycode, idev->keybit); for (mode = 0; mode < ATI_REMOTE2_MODES; mode++) { for (index = 0; index < ARRAY_SIZE(ati_remote2_key_table); index++) { - if (ar2->keycode[mode][index] == old_keycode) + if (ar2->keycode[mode][index] == *old_keycode) return 0; } } - __clear_bit(old_keycode, idev->keybit); + __clear_bit(*old_keycode, idev->keybit); return 0; } @@ -575,8 +612,8 @@ static int ati_remote2_input_init(struct ati_remote2 *ar2) idev->open = ati_remote2_open; idev->close = ati_remote2_close; - idev->getkeycode = ati_remote2_getkeycode; - idev->setkeycode = ati_remote2_setkeycode; + idev->getkeycode_new = ati_remote2_getkeycode; + idev->setkeycode_new = ati_remote2_setkeycode; idev->name = ar2->name; idev->phys = ar2->phys; -- cgit v1.2.2 From 727eeb7dbeac8a7ecd2ad943eb27a5566ab83601 Mon Sep 17 00:00:00 2001 From: Xing Wei Date: Wed, 29 Sep 2010 18:25:20 -0700 Subject: Input: hanwang - add support for Art Master HD 5012 tablet This adds support for hanwang Art Master HD 5012 electromagnetic tablet. Signed-off-by: Xing Wei Signed-off-by: Dmitry Torokhov --- drivers/input/tablet/hanwang.c | 94 ++++++++++++++++++++++++++++++++---------- 1 file changed, 73 insertions(+), 21 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/tablet/hanwang.c b/drivers/input/tablet/hanwang.c index 0701d948cdf3..6504b627b234 100644 --- a/drivers/input/tablet/hanwang.c +++ b/drivers/input/tablet/hanwang.c @@ -42,7 +42,7 @@ MODULE_LICENSE(DRIVER_LICENSE); #define HANWANG_TABLET_INT_SUB_CLASS 0x0001 #define HANWANG_TABLET_INT_PROTOCOL 0x0002 -#define ART_MASTERIII_PKGLEN_MAX 10 +#define ART_MASTER_PKGLEN_MAX 10 /* device IDs */ #define STYLUS_DEVICE_ID 0x02 @@ -60,6 +60,11 @@ MODULE_LICENSE(DRIVER_LICENSE); .bInterfaceSubClass = (sc), \ .bInterfaceProtocol = (pr) +enum hanwang_tablet_type { + HANWANG_ART_MASTER_III, + HANWANG_ART_MASTER_HD, +}; + struct hanwang { unsigned char *data; dma_addr_t data_dma; @@ -76,6 +81,7 @@ struct hanwang { struct hanwang_features { unsigned short pid; char *name; + enum hanwang_tablet_type type; int pkg_len; int max_x; int max_y; @@ -85,12 +91,14 @@ struct hanwang_features { }; static const struct hanwang_features features_array[] = { - { 0x8528, "Hanwang Art Master III 0906", - ART_MASTERIII_PKGLEN_MAX, 0x5757, 0x3692, 0x3f, 0x7f, 2048 }, - { 0x8529, "Hanwang Art Master III 0604", - ART_MASTERIII_PKGLEN_MAX, 0x3d84, 0x2672, 0x3f, 0x7f, 2048 }, - { 0x852a, "Hanwang Art Master III 1308", - ART_MASTERIII_PKGLEN_MAX, 0x7f00, 0x4f60, 0x3f, 0x7f, 2048 }, + { 0x8528, "Hanwang Art Master III 0906", HANWANG_ART_MASTER_III, + ART_MASTER_PKGLEN_MAX, 0x5757, 0x3692, 0x3f, 0x7f, 2048 }, + { 0x8529, "Hanwang Art Master III 0604", HANWANG_ART_MASTER_III, + ART_MASTER_PKGLEN_MAX, 0x3d84, 0x2672, 0x3f, 0x7f, 2048 }, + { 0x852a, "Hanwang Art Master III 1308", HANWANG_ART_MASTER_III, + ART_MASTER_PKGLEN_MAX, 0x7f00, 0x4f60, 0x3f, 0x7f, 2048 }, + { 0x8401, "Hanwang Art Master HD 5012", HANWANG_ART_MASTER_HD, + ART_MASTER_PKGLEN_MAX, 0x678e, 0x4150, 0x3f, 0x7f, 1024 }, }; static const int hw_eventtypes[] = { @@ -99,7 +107,7 @@ static const int hw_eventtypes[] = { static const int hw_absevents[] = { ABS_X, ABS_Y, ABS_TILT_X, ABS_TILT_Y, ABS_WHEEL, - ABS_PRESSURE, ABS_MISC, + ABS_RX, ABS_RY, ABS_PRESSURE, ABS_MISC, }; static const int hw_btnevents[] = { @@ -117,7 +125,9 @@ static void hanwang_parse_packet(struct hanwang *hanwang) unsigned char *data = hanwang->data; struct input_dev *input_dev = hanwang->dev; struct usb_device *dev = hanwang->usbdev; + enum hanwang_tablet_type type = hanwang->features->type; int i; + u16 x, y, p; switch (data[0]) { case 0x02: /* data packet */ @@ -129,12 +139,14 @@ static void hanwang_parse_packet(struct hanwang *hanwang) case 0xc2: /* first time tool prox in */ switch (data[3] & 0xf0) { - case 0x20: + case 0x20: /* art_master III */ + case 0x30: /* art_master_HD */ hanwang->current_id = STYLUS_DEVICE_ID; hanwang->current_tool = BTN_TOOL_PEN; input_report_key(input_dev, BTN_TOOL_PEN, 1); break; - case 0xa0: + case 0xa0: /* art_master III */ + case 0xb0: /* art_master_HD */ hanwang->current_id = ERASER_DEVICE_ID; hanwang->current_tool = BTN_TOOL_RUBBER; input_report_key(input_dev, BTN_TOOL_RUBBER, 1); @@ -148,14 +160,31 @@ static void hanwang_parse_packet(struct hanwang *hanwang) break; default: /* tool data packet */ + x = (data[2] << 8) | data[3]; + y = (data[4] << 8) | data[5]; + + switch (type) { + case HANWANG_ART_MASTER_III: + p = (data[6] << 3) | + ((data[7] & 0xc0) >> 5) | + (data[1] & 0x01); + break; + + case HANWANG_ART_MASTER_HD: + p = (data[7] >> 6) | (data[6] << 2); + break; + + default: + p = 0; + break; + } + input_report_abs(input_dev, ABS_X, - (data[2] << 8) | data[3]); + le16_to_cpup((__le16 *)&x)); input_report_abs(input_dev, ABS_Y, - (data[4] << 8) | data[5]); + le16_to_cpup((__le16 *)&y)); input_report_abs(input_dev, ABS_PRESSURE, - (data[6] << 3) | - ((data[7] & 0xc0) >> 5) | - (data[1] & 0x01)); + le16_to_cpup((__le16 *)&p)); input_report_abs(input_dev, ABS_TILT_X, data[7] & 0x3f); input_report_abs(input_dev, ABS_TILT_Y, data[8] & 0x7f); input_report_key(input_dev, BTN_STYLUS, data[1] & 0x02); @@ -170,13 +199,36 @@ static void hanwang_parse_packet(struct hanwang *hanwang) case 0x0c: /* roll wheel */ hanwang->current_id = PAD_DEVICE_ID; - input_report_key(input_dev, BTN_TOOL_FINGER, data[1] || - data[2] || data[3]); - input_report_abs(input_dev, ABS_WHEEL, data[1]); - input_report_key(input_dev, BTN_0, data[2]); - for (i = 0; i < 8; i++) - input_report_key(input_dev, + + switch (type) { + case HANWANG_ART_MASTER_III: + input_report_key(input_dev, BTN_TOOL_FINGER, data[1] || + data[2] || data[3]); + input_report_abs(input_dev, ABS_WHEEL, data[1]); + input_report_key(input_dev, BTN_0, data[2]); + for (i = 0; i < 8; i++) + input_report_key(input_dev, BTN_1 + i, data[3] & (1 << i)); + break; + + case HANWANG_ART_MASTER_HD: + input_report_key(input_dev, BTN_TOOL_FINGER, data[1] || + data[2] || data[3] || data[4] || + data[5] || data[6]); + input_report_abs(input_dev, ABS_RX, + ((data[1] & 0x1f) << 8) | data[2]); + input_report_abs(input_dev, ABS_RY, + ((data[3] & 0x1f) << 8) | data[4]); + input_report_key(input_dev, BTN_0, data[5] & 0x01); + for (i = 0; i < 4; i++) { + input_report_key(input_dev, + BTN_1 + i, data[5] & (1 << i)); + input_report_key(input_dev, + BTN_5 + i, data[6] & (1 << i)); + } + break; + } + input_report_abs(input_dev, ABS_MISC, hanwang->current_id); input_event(input_dev, EV_MSC, MSC_SERIAL, 0xffffffff); break; -- cgit v1.2.2 From d8daece8f457883e8f2e3065a1e02322ed444ddb Mon Sep 17 00:00:00 2001 From: Rahul Ruikar Date: Wed, 29 Sep 2010 18:12:46 -0700 Subject: Input: emu10k1-gp - add missing calls to pci_disable_device() pci_disable_device() is called for following 2 cases - error path in emu_probe() - in emu_remove() Signed-off-by: Rahul Ruikar Signed-off-by: Dmitry Torokhov --- drivers/input/gameport/emu10k1-gp.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/input') diff --git a/drivers/input/gameport/emu10k1-gp.c b/drivers/input/gameport/emu10k1-gp.c index 7392992da424..34615d40528a 100644 --- a/drivers/input/gameport/emu10k1-gp.c +++ b/drivers/input/gameport/emu10k1-gp.c @@ -77,6 +77,7 @@ static int __devinit emu_probe(struct pci_dev *pdev, const struct pci_device_id if (!emu || !port) { printk(KERN_ERR "emu10k1-gp: Memory allocation failed\n"); release_region(ioport, iolen); + pci_disable_device(pdev); kfree(emu); gameport_free_port(port); return -ENOMEM; @@ -105,6 +106,7 @@ static void __devexit emu_remove(struct pci_dev *pdev) gameport_unregister_port(emu->gameport); release_region(emu->io, emu->size); + pci_disable_device(pdev); kfree(emu); } -- cgit v1.2.2 From d345d97012c3f8fb72c0c9d2ee319ea958b63229 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 29 Sep 2010 18:17:16 -0700 Subject: Input: fm801-gp - add missing call to pci_disable_device() Signed-off-by: Dmitry Torokhov --- drivers/input/gameport/emu10k1-gp.c | 3 ++- drivers/input/gameport/fm801-gp.c | 10 +++++----- 2 files changed, 7 insertions(+), 6 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/gameport/emu10k1-gp.c b/drivers/input/gameport/emu10k1-gp.c index 34615d40528a..f85620590b66 100644 --- a/drivers/input/gameport/emu10k1-gp.c +++ b/drivers/input/gameport/emu10k1-gp.c @@ -106,8 +106,9 @@ static void __devexit emu_remove(struct pci_dev *pdev) gameport_unregister_port(emu->gameport); release_region(emu->io, emu->size); - pci_disable_device(pdev); kfree(emu); + + pci_disable_device(pdev); } static struct pci_driver emu_driver = { diff --git a/drivers/input/gameport/fm801-gp.c b/drivers/input/gameport/fm801-gp.c index 14d3f3e208a2..a3b70ff21018 100644 --- a/drivers/input/gameport/fm801-gp.c +++ b/drivers/input/gameport/fm801-gp.c @@ -133,11 +133,11 @@ static void __devexit fm801_gp_remove(struct pci_dev *pci) { struct fm801_gp *gp = pci_get_drvdata(pci); - if (gp) { - gameport_unregister_port(gp->gameport); - release_resource(gp->res_port); - kfree(gp); - } + gameport_unregister_port(gp->gameport); + release_resource(gp->res_port); + kfree(gp); + + pci_disable_device(pci); } static const struct pci_device_id fm801_gp_id_table[] = { -- cgit v1.2.2 From 594d63632024636a962be508dfe5ee6bfd309160 Mon Sep 17 00:00:00 2001 From: Christoph Fritz Date: Wed, 29 Sep 2010 18:04:21 -0700 Subject: Input: i8042 - use unsigned char for 0x90 Keep the file consistent and make clear that we mean 144 instead of -112. Signed-off-by: Christoph Fritz Signed-off-by: Dmitry Torokhov --- drivers/input/serio/i8042.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c index 46e4ba0b9246..7d67ad1b80ba 100644 --- a/drivers/input/serio/i8042.c +++ b/drivers/input/serio/i8042.c @@ -1063,7 +1063,7 @@ static long i8042_panic_blink(int state) #ifdef CONFIG_X86 static void i8042_dritek_enable(void) { - char param = 0x90; + unsigned char param = 0x90; int error; error = i8042_command(¶m, 0x1059); -- cgit v1.2.2 From 05e93a746a0781429de73117b2f2ef48d2312759 Mon Sep 17 00:00:00 2001 From: Dzianis Kahanovich Date: Wed, 29 Sep 2010 18:38:10 -0700 Subject: Input: hil_kbd - add missing MODULE_DEVICE_TABLE() Signed-off-by: Dzianis Kahanovich Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/hil_kbd.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/input') diff --git a/drivers/input/keyboard/hil_kbd.c b/drivers/input/keyboard/hil_kbd.c index 19fa94af207a..fed31e0947a1 100644 --- a/drivers/input/keyboard/hil_kbd.c +++ b/drivers/input/keyboard/hil_kbd.c @@ -570,6 +570,8 @@ static struct serio_device_id hil_dev_ids[] = { { 0 } }; +MODULE_DEVICE_TABLE(serio, hil_dev_ids); + static struct serio_driver hil_serio_drv = { .driver = { .name = "hil_dev", -- cgit v1.2.2 From 7aed3fb73f4ac7912ce9e0c232a15ee012bf4be5 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 29 Sep 2010 18:50:17 -0700 Subject: Input: emu10k1 - do not leave device enabled when probe fails Rework emu_probe() to make sure we leave the device disabled if probe fails for any reason. Signed-off-by: Dmitry Torokhov --- drivers/input/gameport/emu10k1-gp.c | 43 +++++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 18 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/gameport/emu10k1-gp.c b/drivers/input/gameport/emu10k1-gp.c index f85620590b66..422aa0a6b77f 100644 --- a/drivers/input/gameport/emu10k1-gp.c +++ b/drivers/input/gameport/emu10k1-gp.c @@ -59,45 +59,52 @@ MODULE_DEVICE_TABLE(pci, emu_tbl); static int __devinit emu_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { - int ioport, iolen; struct emu *emu; struct gameport *port; - - if (pci_enable_device(pdev)) - return -EBUSY; - - ioport = pci_resource_start(pdev, 0); - iolen = pci_resource_len(pdev, 0); - - if (!request_region(ioport, iolen, "emu10k1-gp")) - return -EBUSY; + int error; emu = kzalloc(sizeof(struct emu), GFP_KERNEL); port = gameport_allocate_port(); if (!emu || !port) { printk(KERN_ERR "emu10k1-gp: Memory allocation failed\n"); - release_region(ioport, iolen); - pci_disable_device(pdev); - kfree(emu); - gameport_free_port(port); - return -ENOMEM; + error = -ENOMEM; + goto err_out_free; } - emu->io = ioport; - emu->size = iolen; + error = pci_enable_device(pdev); + if (error) + goto err_out_free; + + emu->io = pci_resource_start(pdev, 0); + emu->size = pci_resource_len(pdev, 0); + emu->dev = pdev; emu->gameport = port; gameport_set_name(port, "EMU10K1"); gameport_set_phys(port, "pci%s/gameport0", pci_name(pdev)); port->dev.parent = &pdev->dev; - port->io = ioport; + port->io = emu->io; + + if (!request_region(emu->io, emu->size, "emu10k1-gp")) { + printk(KERN_ERR "emu10k1-gp: unable to grab region 0x%x-0x%x\n", + emu->io, emu->io + emu->size - 1); + error = -EBUSY; + goto err_out_disable_dev; + } pci_set_drvdata(pdev, emu); gameport_register_port(port); return 0; + + err_out_disable_dev: + pci_disable_device(pdev); + err_out_free: + gameport_free_port(port); + kfree(emu); + return error; } static void __devexit emu_remove(struct pci_dev *pdev) -- cgit v1.2.2 From 6792cbbb254712a8c0fa8a4c97c8d521c7c41c28 Mon Sep 17 00:00:00 2001 From: Davidlohr Bueso Date: Wed, 29 Sep 2010 18:53:35 -0700 Subject: Input: return -ENOMEM in select drivers when memory allocation fails Instead of using -1 let's start using proper error codes. Signed-off-by: Davidlohr Bueso Signed-off-by: Dmitry Torokhov --- drivers/input/misc/powermate.c | 2 +- drivers/input/mouse/elantech.c | 2 +- drivers/input/mouse/synaptics.c | 2 +- drivers/input/mouse/trackpoint.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/misc/powermate.c b/drivers/input/misc/powermate.c index bf170f6b4422..f45947190e4f 100644 --- a/drivers/input/misc/powermate.c +++ b/drivers/input/misc/powermate.c @@ -280,7 +280,7 @@ static int powermate_alloc_buffers(struct usb_device *udev, struct powermate_dev pm->configcr = kmalloc(sizeof(*(pm->configcr)), GFP_KERNEL); if (!pm->configcr) - return -1; + return -ENOMEM; return 0; } diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c index 48311204ba51..04d9bf320a4f 100644 --- a/drivers/input/mouse/elantech.c +++ b/drivers/input/mouse/elantech.c @@ -699,7 +699,7 @@ int elantech_init(struct psmouse *psmouse) psmouse->private = etd = kzalloc(sizeof(struct elantech_data), GFP_KERNEL); if (!etd) - return -1; + return -ENOMEM; etd->parity[0] = 1; for (i = 1; i < 256; i++) diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index 96b70a43515f..a79bc362dde5 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -731,7 +731,7 @@ int synaptics_init(struct psmouse *psmouse) psmouse->private = priv = kzalloc(sizeof(struct synaptics_data), GFP_KERNEL); if (!priv) - return -1; + return -ENOMEM; psmouse_reset(psmouse); diff --git a/drivers/input/mouse/trackpoint.c b/drivers/input/mouse/trackpoint.c index 0643e49ca603..54b2fa892e19 100644 --- a/drivers/input/mouse/trackpoint.c +++ b/drivers/input/mouse/trackpoint.c @@ -303,7 +303,7 @@ int trackpoint_detect(struct psmouse *psmouse, bool set_properties) psmouse->private = kzalloc(sizeof(struct trackpoint_data), GFP_KERNEL); if (!psmouse->private) - return -1; + return -ENOMEM; psmouse->vendor = "IBM"; psmouse->name = "TrackPoint"; -- cgit v1.2.2 From a9cc0202227caa4adb5e823d5fbce7790364941f Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 29 Sep 2010 18:36:00 -0700 Subject: Input: twl4030_keypad - fix error handling path We should not try to call free_irq() when request_irq() failed. Reported-by: G, Manjunath Kondaiah Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/twl4030_keypad.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/keyboard/twl4030_keypad.c b/drivers/input/keyboard/twl4030_keypad.c index fb16b5e5ea13..09bef79d9da1 100644 --- a/drivers/input/keyboard/twl4030_keypad.c +++ b/drivers/input/keyboard/twl4030_keypad.c @@ -406,23 +406,22 @@ static int __devinit twl4030_kp_probe(struct platform_device *pdev) if (error) { dev_info(kp->dbg_dev, "request_irq failed for irq no=%d\n", kp->irq); - goto err3; + goto err2; } /* Enable KP and TO interrupts now. */ reg = (u8) ~(KEYP_IMR1_KP | KEYP_IMR1_TO); if (twl4030_kpwrite_u8(kp, reg, KEYP_IMR1)) { error = -EIO; - goto err4; + goto err3; } platform_set_drvdata(pdev, kp); return 0; -err4: +err3: /* mask all events - we don't care about the result */ (void) twl4030_kpwrite_u8(kp, 0xff, KEYP_IMR1); -err3: free_irq(kp->irq, NULL); err2: input_unregister_device(input); -- cgit v1.2.2 From 1158f0f16224068e27e384c1a27218aa00243967 Mon Sep 17 00:00:00 2001 From: Sundar Iyer Date: Wed, 29 Sep 2010 19:42:14 -0700 Subject: Input: add support for Nomadik SKE keypad controller Add support for the keypad controller in the Scroll Key Encoder (SKE) module on the Nomadik family and the DB8500 SoC. Acked-by: Linus Walleij Signed-off-by: Sundar Iyer Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/Kconfig | 10 + drivers/input/keyboard/Makefile | 1 + drivers/input/keyboard/nomadik-ske-keypad.c | 408 ++++++++++++++++++++++++++++ 3 files changed, 419 insertions(+) create mode 100644 drivers/input/keyboard/nomadik-ske-keypad.c (limited to 'drivers/input') diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index 4f300488e1f3..d63f5664e3fb 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -327,6 +327,16 @@ config KEYBOARD_NEWTON To compile this driver as a module, choose M here: the module will be called newtonkbd. +config KEYBOARD_NOMADIK + tristate "ST-Ericsson Nomadik SKE keyboard" + depends on PLAT_NOMADIK + help + Say Y here if you want to use a keypad provided on the SKE controller + used on the Ux500 and Nomadik platforms + + To compile this driver as a module, choose M here: the + module will be called nmk-ske-keypad. + config KEYBOARD_OPENCORES tristate "OpenCores Keyboard Controller" help diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index 8ac01fb642c1..c13809c5c3a7 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -28,6 +28,7 @@ obj-$(CONFIG_KEYBOARD_MATRIX) += matrix_keypad.o obj-$(CONFIG_KEYBOARD_MAX7359) += max7359_keypad.o obj-$(CONFIG_KEYBOARD_MCS) += mcs_touchkey.o obj-$(CONFIG_KEYBOARD_NEWTON) += newtonkbd.o +obj-$(CONFIG_KEYBOARD_NOMADIK) += nomadik-ske-keypad.o obj-$(CONFIG_KEYBOARD_OMAP) += omap-keypad.o obj-$(CONFIG_KEYBOARD_OMAP4) += omap4-keypad.o obj-$(CONFIG_KEYBOARD_OPENCORES) += opencores-kbd.o diff --git a/drivers/input/keyboard/nomadik-ske-keypad.c b/drivers/input/keyboard/nomadik-ske-keypad.c new file mode 100644 index 000000000000..6e0f23091360 --- /dev/null +++ b/drivers/input/keyboard/nomadik-ske-keypad.c @@ -0,0 +1,408 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * Author: Naveen Kumar G for ST-Ericsson + * Author: Sundar Iyer for ST-Ericsson + * + * License terms:GNU General Public License (GPL) version 2 + * + * Keypad controller driver for the SKE (Scroll Key Encoder) module used in + * the Nomadik 8815 and Ux500 platforms. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* SKE_CR bits */ +#define SKE_KPMLT (0x1 << 6) +#define SKE_KPCN (0x7 << 3) +#define SKE_KPASEN (0x1 << 2) +#define SKE_KPASON (0x1 << 7) + +/* SKE_IMSC bits */ +#define SKE_KPIMA (0x1 << 2) + +/* SKE_ICR bits */ +#define SKE_KPICS (0x1 << 3) +#define SKE_KPICA (0x1 << 2) + +/* SKE_RIS bits */ +#define SKE_KPRISA (0x1 << 2) + +#define SKE_KEYPAD_ROW_SHIFT 3 +#define SKE_KPD_KEYMAP_SIZE (8 * 8) + +/* keypad auto scan registers */ +#define SKE_ASR0 0x20 +#define SKE_ASR1 0x24 +#define SKE_ASR2 0x28 +#define SKE_ASR3 0x2C + +#define SKE_NUM_ASRX_REGISTERS (4) + +/** + * struct ske_keypad - data structure used by keypad driver + * @irq: irq no + * @reg_base: ske regsiters base address + * @input: pointer to input device object + * @board: keypad platform device + * @keymap: matrix scan code table for keycodes + * @clk: clock structure pointer + */ +struct ske_keypad { + int irq; + void __iomem *reg_base; + struct input_dev *input; + const struct ske_keypad_platform_data *board; + unsigned short keymap[SKE_KPD_KEYMAP_SIZE]; + struct clk *clk; + spinlock_t ske_keypad_lock; +}; + +static void ske_keypad_set_bits(struct ske_keypad *keypad, u16 addr, + u8 mask, u8 data) +{ + u32 ret; + + spin_lock(&keypad->ske_keypad_lock); + + ret = readl(keypad->reg_base + addr); + ret &= ~mask; + ret |= data; + writel(ret, keypad->reg_base + addr); + + spin_unlock(&keypad->ske_keypad_lock); +} + +/* + * ske_keypad_chip_init: init keypad controller configuration + * + * Enable Multi key press detection, auto scan mode + */ +static int __devinit ske_keypad_chip_init(struct ske_keypad *keypad) +{ + u32 value; + int timeout = 50; + + /* check SKE_RIS to be 0 */ + while ((readl(keypad->reg_base + SKE_RIS) != 0x00000000) && timeout--) + cpu_relax(); + + if (!timeout) + return -EINVAL; + + /* + * set debounce value + * keypad dbounce is configured in DBCR[15:8] + * dbounce value in steps of 32/32.768 ms + */ + spin_lock(&keypad->ske_keypad_lock); + value = readl(keypad->reg_base + SKE_DBCR); + value = value & 0xff; + value |= ((keypad->board->debounce_ms * 32000)/32768) << 8; + writel(value, keypad->reg_base + SKE_DBCR); + spin_unlock(&keypad->ske_keypad_lock); + + /* enable multi key detection */ + ske_keypad_set_bits(keypad, SKE_CR, 0x0, SKE_KPMLT); + + /* + * set up the number of columns + * KPCN[5:3] defines no. of keypad columns to be auto scanned + */ + value = (keypad->board->kcol - 1) << 3; + ske_keypad_set_bits(keypad, SKE_CR, SKE_KPCN, value); + + /* clear keypad interrupt for auto(and pending SW) scans */ + ske_keypad_set_bits(keypad, SKE_ICR, 0x0, SKE_KPICA | SKE_KPICS); + + /* un-mask keypad interrupts */ + ske_keypad_set_bits(keypad, SKE_IMSC, 0x0, SKE_KPIMA); + + /* enable automatic scan */ + ske_keypad_set_bits(keypad, SKE_CR, 0x0, SKE_KPASEN); + + return 0; +} + +static void ske_keypad_read_data(struct ske_keypad *keypad) +{ + struct input_dev *input = keypad->input; + u16 status; + int col = 0, row = 0, code; + int ske_asr, ske_ris, key_pressed, i; + + /* + * Read the auto scan registers + * + * Each SKE_ASRx (x=0 to x=3) contains two row values. + * lower byte contains row value for column 2*x, + * upper byte contains row value for column 2*x + 1 + */ + for (i = 0; i < SKE_NUM_ASRX_REGISTERS; i++) { + ske_asr = readl(keypad->reg_base + SKE_ASR0 + (4 * i)); + if (!ske_asr) + continue; + + /* now that ASRx is zero, find out the column x and row y*/ + if (ske_asr & 0xff) { + col = i * 2; + status = ske_asr & 0xff; + } else { + col = (i * 2) + 1; + status = (ske_asr & 0xff00) >> 8; + } + + /* find out the row */ + row = __ffs(status); + + code = MATRIX_SCAN_CODE(row, col, SKE_KEYPAD_ROW_SHIFT); + ske_ris = readl(keypad->reg_base + SKE_RIS); + key_pressed = ske_ris & SKE_KPRISA; + + input_event(input, EV_MSC, MSC_SCAN, code); + input_report_key(input, keypad->keymap[code], key_pressed); + input_sync(input); + } +} + +static irqreturn_t ske_keypad_irq(int irq, void *dev_id) +{ + struct ske_keypad *keypad = dev_id; + int retries = 20; + + /* disable auto scan interrupt; mask the interrupt generated */ + ske_keypad_set_bits(keypad, SKE_IMSC, ~SKE_KPIMA, 0x0); + ske_keypad_set_bits(keypad, SKE_ICR, 0x0, SKE_KPICA); + + while ((readl(keypad->reg_base + SKE_CR) & SKE_KPASON) && --retries) + msleep(5); + + if (retries) { + /* SKEx registers are stable and can be read */ + ske_keypad_read_data(keypad); + } + + /* enable auto scan interrupts */ + ske_keypad_set_bits(keypad, SKE_IMSC, 0x0, SKE_KPIMA); + + return IRQ_HANDLED; +} + +static int __devinit ske_keypad_probe(struct platform_device *pdev) +{ + const struct ske_keypad_platform_data *plat = pdev->dev.platform_data; + struct ske_keypad *keypad; + struct input_dev *input; + struct resource *res; + int irq; + int error; + + if (!plat) { + dev_err(&pdev->dev, "invalid keypad platform data\n"); + return -EINVAL; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "failed to get keypad irq\n"); + return -EINVAL; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "missing platform resources\n"); + return -EINVAL; + } + + keypad = kzalloc(sizeof(struct ske_keypad), GFP_KERNEL); + input = input_allocate_device(); + if (!keypad || !input) { + dev_err(&pdev->dev, "failed to allocate keypad memory\n"); + error = -ENOMEM; + goto err_free_mem; + } + + keypad->irq = irq; + keypad->board = plat; + keypad->input = input; + spin_lock_init(&keypad->ske_keypad_lock); + + if (!request_mem_region(res->start, resource_size(res), pdev->name)) { + dev_err(&pdev->dev, "failed to request I/O memory\n"); + error = -EBUSY; + goto err_free_mem; + } + + keypad->reg_base = ioremap(res->start, resource_size(res)); + if (!keypad->reg_base) { + dev_err(&pdev->dev, "failed to remap I/O memory\n"); + error = -ENXIO; + goto err_free_mem_region; + } + + keypad->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(keypad->clk)) { + dev_err(&pdev->dev, "failed to get clk\n"); + error = PTR_ERR(keypad->clk); + goto err_iounmap; + } + + input->id.bustype = BUS_HOST; + input->name = "ux500-ske-keypad"; + input->dev.parent = &pdev->dev; + + input->keycode = keypad->keymap; + input->keycodesize = sizeof(keypad->keymap[0]); + input->keycodemax = ARRAY_SIZE(keypad->keymap); + + input_set_capability(input, EV_MSC, MSC_SCAN); + + __set_bit(EV_KEY, input->evbit); + if (!plat->no_autorepeat) + __set_bit(EV_REP, input->evbit); + + matrix_keypad_build_keymap(plat->keymap_data, SKE_KEYPAD_ROW_SHIFT, + input->keycode, input->keybit); + + clk_enable(keypad->clk); + + /* go through board initialization helpers */ + if (keypad->board->init) + keypad->board->init(); + + error = ske_keypad_chip_init(keypad); + if (error) { + dev_err(&pdev->dev, "unable to init keypad hardware\n"); + goto err_clk_disable; + } + + error = request_threaded_irq(keypad->irq, NULL, ske_keypad_irq, + IRQF_ONESHOT, "ske-keypad", keypad); + if (error) { + dev_err(&pdev->dev, "allocate irq %d failed\n", keypad->irq); + goto err_clk_disable; + } + + error = input_register_device(input); + if (error) { + dev_err(&pdev->dev, + "unable to register input device: %d\n", error); + goto err_free_irq; + } + + if (plat->wakeup_enable) + device_init_wakeup(&pdev->dev, true); + + platform_set_drvdata(pdev, keypad); + + return 0; + +err_free_irq: + free_irq(keypad->irq, keypad); +err_clk_disable: + clk_disable(keypad->clk); + clk_put(keypad->clk); +err_iounmap: + iounmap(keypad->reg_base); +err_free_mem_region: + release_mem_region(res->start, resource_size(res)); +err_free_mem: + input_free_device(input); + kfree(keypad); + return error; +} + +static int __devexit ske_keypad_remove(struct platform_device *pdev) +{ + struct ske_keypad *keypad = platform_get_drvdata(pdev); + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + free_irq(keypad->irq, keypad); + + input_unregister_device(keypad->input); + + clk_disable(keypad->clk); + clk_put(keypad->clk); + + if (keypad->board->exit) + keypad->board->exit(); + + iounmap(keypad->reg_base); + release_mem_region(res->start, resource_size(res)); + kfree(keypad); + + return 0; +} + +#ifdef CONFIG_PM +static int ske_keypad_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct ske_keypad *keypad = platform_get_drvdata(pdev); + int irq = platform_get_irq(pdev, 0); + + if (device_may_wakeup(dev)) + enable_irq_wake(irq); + else + ske_keypad_set_bits(keypad, SKE_IMSC, ~SKE_KPIMA, 0x0); + + return 0; +} + +static int ske_keypad_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct ske_keypad *keypad = platform_get_drvdata(pdev); + int irq = platform_get_irq(pdev, 0); + + if (device_may_wakeup(dev)) + disable_irq_wake(irq); + else + ske_keypad_set_bits(keypad, SKE_IMSC, 0x0, SKE_KPIMA); + + return 0; +} + +static const struct dev_pm_ops ske_keypad_dev_pm_ops = { + .suspend = ske_keypad_suspend, + .resume = ske_keypad_resume, +}; +#endif + +struct platform_driver ske_keypad_driver = { + .driver = { + .name = "nmk-ske-keypad", + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &ske_keypad_dev_pm_ops, +#endif + }, + .probe = ske_keypad_probe, + .remove = __devexit_p(ske_keypad_remove), +}; + +static int __init ske_keypad_init(void) +{ + return platform_driver_probe(&ske_keypad_driver, ske_keypad_probe); +} +module_init(ske_keypad_init); + +static void __exit ske_keypad_exit(void) +{ + platform_driver_unregister(&ske_keypad_driver); +} +module_exit(ske_keypad_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Naveen Kumar / Sundar Iyer "); +MODULE_DESCRIPTION("Nomadik Scroll-Key-Encoder Keypad Driver"); +MODULE_ALIAS("platform:nomadik-ske-keypad"); -- cgit v1.2.2 From f3a1ba60dbdbf46f2715cc4e3f8abc9d65c4d9bf Mon Sep 17 00:00:00 2001 From: Abraham Arce Date: Wed, 29 Sep 2010 23:35:57 -0700 Subject: Input: omap4-keypad - use platform device helpers Get mem and irq resources using platform helpers - platform_get_base - platform_get_irq Signed-off-by: Abraham Arce Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/omap4-keypad.c | 40 ++++++++++++++++++++++++++++++----- 1 file changed, 35 insertions(+), 5 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/keyboard/omap4-keypad.c b/drivers/input/keyboard/omap4-keypad.c index 975f8bbcc69b..788169d3e7c2 100644 --- a/drivers/input/keyboard/omap4-keypad.c +++ b/drivers/input/keyboard/omap4-keypad.c @@ -148,7 +148,10 @@ static int __devinit omap4_keypad_probe(struct platform_device *pdev) const struct omap4_keypad_platform_data *pdata; struct omap4_keypad *keypad_data; struct input_dev *input_dev; + struct resource *res; + resource_size_t size; unsigned int row_shift, max_keys; + int irq; int error; /* platform data */ @@ -158,12 +161,14 @@ static int __devinit omap4_keypad_probe(struct platform_device *pdev) return -EINVAL; } - if (!pdata->base) { + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { dev_err(&pdev->dev, "no base address specified\n"); return -EINVAL; } - if (!pdata->irq) { + irq = platform_get_irq(pdev, 0); + if (!irq) { dev_err(&pdev->dev, "no keyboard irq assigned\n"); return -EINVAL; } @@ -184,9 +189,23 @@ static int __devinit omap4_keypad_probe(struct platform_device *pdev) return -ENOMEM; } - keypad_data->base = pdata->base; - keypad_data->irq = pdata->irq; + size = resource_size(res); + + res = request_mem_region(res->start, size, pdev->name); + if (!res) { + dev_err(&pdev->dev, "can't request mem region\n"); + error = -EBUSY; + goto err_free_keypad; + } + keypad_data->base = ioremap(res->start, resource_size(res)); + if (!keypad_data->base) { + dev_err(&pdev->dev, "can't ioremap mem resource\n"); + error = -ENOMEM; + goto err_release_mem; + } + + keypad_data->irq = irq; keypad_data->row_shift = row_shift; keypad_data->rows = pdata->rows; keypad_data->cols = pdata->cols; @@ -195,7 +214,7 @@ static int __devinit omap4_keypad_probe(struct platform_device *pdev) keypad_data->input = input_dev = input_allocate_device(); if (!input_dev) { error = -ENOMEM; - goto err_free_keypad; + goto err_unmap; } input_dev->name = pdev->name; @@ -243,6 +262,10 @@ err_free_irq: free_irq(keypad_data->irq, keypad_data); err_free_input: input_free_device(input_dev); +err_unmap: + iounmap(keypad_data->base); +err_release_mem: + release_mem_region(res->start, size); err_free_keypad: kfree(keypad_data); return error; @@ -251,9 +274,16 @@ err_free_keypad: static int __devexit omap4_keypad_remove(struct platform_device *pdev) { struct omap4_keypad *keypad_data = platform_get_drvdata(pdev); + struct resource *res; free_irq(keypad_data->irq, keypad_data); input_unregister_device(keypad_data->input); + + iounmap(keypad_data->base); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(res->start, resource_size(res)); + kfree(keypad_data); platform_set_drvdata(pdev, NULL); -- cgit v1.2.2 From 05362f486bf6e6b8adc77424b6edf2f3cd6816ae Mon Sep 17 00:00:00 2001 From: Abraham Arce Date: Wed, 29 Sep 2010 23:36:48 -0700 Subject: Input: omap4-keypad - SYSCONFIG register configuration Remove SYSCONFIG register configuration, omap hwmod framework will use internal API to modify as required. Other minor updates: - Change a variable name from DEF to VAL, this represents a value - Break line width to 80 characters Signed-off-by: Abraham Arce Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/omap4-keypad.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/keyboard/omap4-keypad.c b/drivers/input/keyboard/omap4-keypad.c index 788169d3e7c2..2b61f5fb07b6 100644 --- a/drivers/input/keyboard/omap4-keypad.c +++ b/drivers/input/keyboard/omap4-keypad.c @@ -51,17 +51,15 @@ #define OMAP4_KBD_FULLCODE63_32 0x48 /* OMAP4 bit definitions */ -#define OMAP4_DEF_SYSCONFIG_SOFTRST (1 << 1) -#define OMAP4_DEF_SYSCONFIG_ENAWKUP (1 << 2) #define OMAP4_DEF_IRQENABLE_EVENTEN (1 << 0) #define OMAP4_DEF_IRQENABLE_LONGKEY (1 << 1) #define OMAP4_DEF_IRQENABLE_TIMEOUTEN (1 << 2) #define OMAP4_DEF_CTRL_NOSOFTMODE (1 << 1) #define OMAP4_DEF_CTRLPTVVALUE (1 << 2) #define OMAP4_DEF_CTRLPTV (1 << 1) -#define OMAP4_DEF_IRQDISABLE 0x00 /* OMAP4 values */ +#define OMAP4_VAL_IRQDISABLE 0x00 #define OMAP4_VAL_DEBOUNCINGTIME 0x07 #define OMAP4_VAL_FUNCTIONALCFG 0x1E @@ -82,13 +80,11 @@ struct omap4_keypad { static void __devinit omap4_keypad_config(struct omap4_keypad *keypad_data) { - __raw_writel(OMAP4_DEF_SYSCONFIG_SOFTRST | OMAP4_DEF_SYSCONFIG_ENAWKUP, - keypad_data->base + OMAP4_KBD_SYSCONFIG); __raw_writel(OMAP4_VAL_FUNCTIONALCFG, keypad_data->base + OMAP4_KBD_CTRL); __raw_writel(OMAP4_VAL_DEBOUNCINGTIME, keypad_data->base + OMAP4_KBD_DEBOUNCINGTIME); - __raw_writel(OMAP4_DEF_IRQDISABLE, + __raw_writel(OMAP4_VAL_IRQDISABLE, keypad_data->base + OMAP4_KBD_IRQSTATUS); __raw_writel(OMAP4_DEF_IRQENABLE_EVENTEN | OMAP4_DEF_IRQENABLE_LONGKEY, keypad_data->base + OMAP4_KBD_IRQENABLE); @@ -104,11 +100,12 @@ static irqreturn_t omap4_keypad_interrupt(int irq, void *dev_id) u32 *new_state = (u32 *) key_state; /* Disable interrupts */ - __raw_writel(OMAP4_DEF_IRQDISABLE, + __raw_writel(OMAP4_VAL_IRQDISABLE, keypad_data->base + OMAP4_KBD_IRQENABLE); *new_state = __raw_readl(keypad_data->base + OMAP4_KBD_FULLCODE31_0); - *(new_state + 1) = __raw_readl(keypad_data->base + OMAP4_KBD_FULLCODE63_32); + *(new_state + 1) = __raw_readl(keypad_data->base + + OMAP4_KBD_FULLCODE63_32); for (row = 0; row < keypad_data->rows; row++) { changed = key_state[row] ^ keypad_data->key_state[row]; -- cgit v1.2.2 From f8038c425148ddd50c7e7223a9a6e71ce5f7e42d Mon Sep 17 00:00:00 2001 From: Abraham Arce Date: Wed, 29 Sep 2010 23:37:04 -0700 Subject: Input: omap4-keypad - fix interrupt line configuration Select correct interrupt line behaviour. Signed-off-by: Abraham Arce Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/omap4-keypad.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/keyboard/omap4-keypad.c b/drivers/input/keyboard/omap4-keypad.c index 2b61f5fb07b6..07e792e960f1 100644 --- a/drivers/input/keyboard/omap4-keypad.c +++ b/drivers/input/keyboard/omap4-keypad.c @@ -238,7 +238,7 @@ static int __devinit omap4_keypad_probe(struct platform_device *pdev) omap4_keypad_config(keypad_data); error = request_irq(keypad_data->irq, omap4_keypad_interrupt, - IRQF_TRIGGER_FALLING, + IRQF_TRIGGER_RISING, "omap4-keypad", keypad_data); if (error) { dev_err(&pdev->dev, "failed to register interrupt\n"); -- cgit v1.2.2 From 9a34bc61160167319fcfa59842cc0373487d533b Mon Sep 17 00:00:00 2001 From: Mike Turquette Date: Wed, 29 Sep 2010 23:38:01 -0700 Subject: Input: omap4-keypad - wake-up on events & long presses Program keyboard controller to generate a wake-up request on events and on long key presses. It will not generate wake-up requests on timeouts since driver code does not handle them. This allows keyboard to wake-up OMAP from suspend. Signed-off-by: Mike Turquette Signed-off-by: Abraham Arce Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/omap4-keypad.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/input') diff --git a/drivers/input/keyboard/omap4-keypad.c b/drivers/input/keyboard/omap4-keypad.c index 07e792e960f1..45bd0977d006 100644 --- a/drivers/input/keyboard/omap4-keypad.c +++ b/drivers/input/keyboard/omap4-keypad.c @@ -54,6 +54,8 @@ #define OMAP4_DEF_IRQENABLE_EVENTEN (1 << 0) #define OMAP4_DEF_IRQENABLE_LONGKEY (1 << 1) #define OMAP4_DEF_IRQENABLE_TIMEOUTEN (1 << 2) +#define OMAP4_DEF_WUP_EVENT_ENA (1 << 0) +#define OMAP4_DEF_WUP_LONG_KEY_ENA (1 << 1) #define OMAP4_DEF_CTRL_NOSOFTMODE (1 << 1) #define OMAP4_DEF_CTRLPTVVALUE (1 << 2) #define OMAP4_DEF_CTRLPTV (1 << 1) @@ -88,6 +90,8 @@ static void __devinit omap4_keypad_config(struct omap4_keypad *keypad_data) keypad_data->base + OMAP4_KBD_IRQSTATUS); __raw_writel(OMAP4_DEF_IRQENABLE_EVENTEN | OMAP4_DEF_IRQENABLE_LONGKEY, keypad_data->base + OMAP4_KBD_IRQENABLE); + __raw_writel(OMAP4_DEF_WUP_EVENT_ENA | OMAP4_DEF_WUP_LONG_KEY_ENA, + keypad_data->base + OMAP4_KBD_WAKEUPENABLE); } /* Interrupt handler */ -- cgit v1.2.2 From 4780c8df3856398020be2928d9e9fa8c457a09a4 Mon Sep 17 00:00:00 2001 From: Naveen Kumar Gaddipati Date: Mon, 4 Oct 2010 22:32:48 -0700 Subject: Input: add ROHM BU21013 touch panel controller support Add the ROHM BU21013 capacitive touch panel controller support with i2c interface. Acked-by: Linus Walleij Signed-off-by: Naveen Kumar Gaddipati Acked-by: Henrik Rydberg Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/Kconfig | 12 + drivers/input/touchscreen/Makefile | 1 + drivers/input/touchscreen/bu21013_ts.c | 648 +++++++++++++++++++++++++++++++++ 3 files changed, 661 insertions(+) create mode 100644 drivers/input/touchscreen/bu21013_ts.c (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index d59feb7b90f6..0ea361f23fa7 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -98,6 +98,18 @@ config TOUCHSCREEN_BITSY To compile this driver as a module, choose M here: the module will be called h3600_ts_input. +config TOUCHSCREEN_BU21013 + tristate "BU21013 based touch panel controllers" + depends on I2C + help + Say Y here if you have a bu21013 touchscreen connected to + your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called bu21013_ts. + config TOUCHSCREEN_CY8CTMG110 tristate "cy8ctmg110 touchscreen" depends on I2C diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index f1bc8a416824..99b353c4e9ae 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_TOUCHSCREEN_AD7879_SPI) += ad7879-spi.o obj-$(CONFIG_TOUCHSCREEN_ADS7846) += ads7846.o obj-$(CONFIG_TOUCHSCREEN_ATMEL_TSADCC) += atmel_tsadcc.o obj-$(CONFIG_TOUCHSCREEN_BITSY) += h3600_ts_input.o +obj-$(CONFIG_TOUCHSCREEN_BU21013) += bu21013_ts.o obj-$(CONFIG_TOUCHSCREEN_CY8CTMG110) += cy8ctmg110_ts.o obj-$(CONFIG_TOUCHSCREEN_DA9034) += da9034-ts.o obj-$(CONFIG_TOUCHSCREEN_DYNAPRO) += dynapro.o diff --git a/drivers/input/touchscreen/bu21013_ts.c b/drivers/input/touchscreen/bu21013_ts.c new file mode 100644 index 000000000000..ccde58602563 --- /dev/null +++ b/drivers/input/touchscreen/bu21013_ts.c @@ -0,0 +1,648 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Naveen Kumar G for ST-Ericsson + * License terms:GNU General Public License (GPL) version 2 + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define PEN_DOWN_INTR 0 +#define MAX_FINGERS 2 +#define RESET_DELAY 30 +#define PENUP_TIMEOUT (10) +#define DELTA_MIN 16 +#define MASK_BITS 0x03 +#define SHIFT_8 8 +#define SHIFT_2 2 +#define LENGTH_OF_BUFFER 11 +#define I2C_RETRY_COUNT 5 + +#define BU21013_SENSORS_BTN_0_7_REG 0x70 +#define BU21013_SENSORS_BTN_8_15_REG 0x71 +#define BU21013_SENSORS_BTN_16_23_REG 0x72 +#define BU21013_X1_POS_MSB_REG 0x73 +#define BU21013_X1_POS_LSB_REG 0x74 +#define BU21013_Y1_POS_MSB_REG 0x75 +#define BU21013_Y1_POS_LSB_REG 0x76 +#define BU21013_X2_POS_MSB_REG 0x77 +#define BU21013_X2_POS_LSB_REG 0x78 +#define BU21013_Y2_POS_MSB_REG 0x79 +#define BU21013_Y2_POS_LSB_REG 0x7A +#define BU21013_INT_CLR_REG 0xE8 +#define BU21013_INT_MODE_REG 0xE9 +#define BU21013_GAIN_REG 0xEA +#define BU21013_OFFSET_MODE_REG 0xEB +#define BU21013_XY_EDGE_REG 0xEC +#define BU21013_RESET_REG 0xED +#define BU21013_CALIB_REG 0xEE +#define BU21013_DONE_REG 0xEF +#define BU21013_SENSOR_0_7_REG 0xF0 +#define BU21013_SENSOR_8_15_REG 0xF1 +#define BU21013_SENSOR_16_23_REG 0xF2 +#define BU21013_POS_MODE1_REG 0xF3 +#define BU21013_POS_MODE2_REG 0xF4 +#define BU21013_CLK_MODE_REG 0xF5 +#define BU21013_IDLE_REG 0xFA +#define BU21013_FILTER_REG 0xFB +#define BU21013_TH_ON_REG 0xFC +#define BU21013_TH_OFF_REG 0xFD + + +#define BU21013_RESET_ENABLE 0x01 + +#define BU21013_SENSORS_EN_0_7 0x3F +#define BU21013_SENSORS_EN_8_15 0xFC +#define BU21013_SENSORS_EN_16_23 0x1F + +#define BU21013_POS_MODE1_0 0x02 +#define BU21013_POS_MODE1_1 0x04 +#define BU21013_POS_MODE1_2 0x08 + +#define BU21013_POS_MODE2_ZERO 0x01 +#define BU21013_POS_MODE2_AVG1 0x02 +#define BU21013_POS_MODE2_AVG2 0x04 +#define BU21013_POS_MODE2_EN_XY 0x08 +#define BU21013_POS_MODE2_EN_RAW 0x10 +#define BU21013_POS_MODE2_MULTI 0x80 + +#define BU21013_CLK_MODE_DIV 0x01 +#define BU21013_CLK_MODE_EXT 0x02 +#define BU21013_CLK_MODE_CALIB 0x80 + +#define BU21013_IDLET_0 0x01 +#define BU21013_IDLET_1 0x02 +#define BU21013_IDLET_2 0x04 +#define BU21013_IDLET_3 0x08 +#define BU21013_IDLE_INTERMIT_EN 0x10 + +#define BU21013_DELTA_0_6 0x7F +#define BU21013_FILTER_EN 0x80 + +#define BU21013_INT_MODE_LEVEL 0x00 +#define BU21013_INT_MODE_EDGE 0x01 + +#define BU21013_GAIN_0 0x01 +#define BU21013_GAIN_1 0x02 +#define BU21013_GAIN_2 0x04 + +#define BU21013_OFFSET_MODE_DEFAULT 0x00 +#define BU21013_OFFSET_MODE_MOVE 0x01 +#define BU21013_OFFSET_MODE_DISABLE 0x02 + +#define BU21013_TH_ON_0 0x01 +#define BU21013_TH_ON_1 0x02 +#define BU21013_TH_ON_2 0x04 +#define BU21013_TH_ON_3 0x08 +#define BU21013_TH_ON_4 0x10 +#define BU21013_TH_ON_5 0x20 +#define BU21013_TH_ON_6 0x40 +#define BU21013_TH_ON_7 0x80 +#define BU21013_TH_ON_MAX 0xFF + +#define BU21013_TH_OFF_0 0x01 +#define BU21013_TH_OFF_1 0x02 +#define BU21013_TH_OFF_2 0x04 +#define BU21013_TH_OFF_3 0x08 +#define BU21013_TH_OFF_4 0x10 +#define BU21013_TH_OFF_5 0x20 +#define BU21013_TH_OFF_6 0x40 +#define BU21013_TH_OFF_7 0x80 +#define BU21013_TH_OFF_MAX 0xFF + +#define BU21013_X_EDGE_0 0x01 +#define BU21013_X_EDGE_1 0x02 +#define BU21013_X_EDGE_2 0x04 +#define BU21013_X_EDGE_3 0x08 +#define BU21013_Y_EDGE_0 0x10 +#define BU21013_Y_EDGE_1 0x20 +#define BU21013_Y_EDGE_2 0x40 +#define BU21013_Y_EDGE_3 0x80 + +#define BU21013_DONE 0x01 +#define BU21013_NUMBER_OF_X_SENSORS (6) +#define BU21013_NUMBER_OF_Y_SENSORS (11) + +#define DRIVER_TP "bu21013_tp" + +/** + * struct bu21013_ts_data - touch panel data structure + * @client: pointer to the i2c client + * @wait: variable to wait_queue_head_t structure + * @touch_stopped: touch stop flag + * @chip: pointer to the touch panel controller + * @in_dev: pointer to the input device structure + * @intr_pin: interrupt pin value + * + * Touch panel device data structure + */ +struct bu21013_ts_data { + struct i2c_client *client; + wait_queue_head_t wait; + bool touch_stopped; + const struct bu21013_platform_device *chip; + struct input_dev *in_dev; + unsigned int intr_pin; +}; + +/** + * bu21013_read_block_data(): read the touch co-ordinates + * @data: bu21013_ts_data structure pointer + * @buf: byte pointer + * + * Read the touch co-ordinates using i2c read block into buffer + * and returns integer. + */ +static int bu21013_read_block_data(struct bu21013_ts_data *data, u8 *buf) +{ + int ret, i; + + for (i = 0; i < I2C_RETRY_COUNT; i++) { + ret = i2c_smbus_read_i2c_block_data + (data->client, BU21013_SENSORS_BTN_0_7_REG, + LENGTH_OF_BUFFER, buf); + if (ret == LENGTH_OF_BUFFER) + return 0; + } + return -EINVAL; +} + +/** + * bu21013_do_touch_report(): Get the touch co-ordinates + * @data: bu21013_ts_data structure pointer + * + * Get the touch co-ordinates from touch sensor registers and writes + * into device structure and returns integer. + */ +static int bu21013_do_touch_report(struct bu21013_ts_data *data) +{ + u8 buf[LENGTH_OF_BUFFER]; + unsigned int pos_x[2], pos_y[2]; + bool has_x_sensors, has_y_sensors; + int finger_down_count = 0; + int i; + + if (data == NULL) + return -EINVAL; + + if (bu21013_read_block_data(data, buf) < 0) + return -EINVAL; + + has_x_sensors = hweight32(buf[0] & BU21013_SENSORS_EN_0_7); + has_y_sensors = hweight32(((buf[1] & BU21013_SENSORS_EN_8_15) | + ((buf[2] & BU21013_SENSORS_EN_16_23) << SHIFT_8)) >> SHIFT_2); + if (!has_x_sensors || !has_y_sensors) + return 0; + + for (i = 0; i < MAX_FINGERS; i++) { + const u8 *p = &buf[4 * i + 3]; + unsigned int x = p[0] << SHIFT_2 | (p[1] & MASK_BITS); + unsigned int y = p[2] << SHIFT_2 | (p[3] & MASK_BITS); + if (x == 0 || y == 0) + continue; + pos_x[finger_down_count] = x; + pos_y[finger_down_count] = y; + finger_down_count++; + } + + if (finger_down_count) { + if (finger_down_count == 2 && + (abs(pos_x[0] - pos_x[1]) < DELTA_MIN || + abs(pos_y[0] - pos_y[1]) < DELTA_MIN)) { + return 0; + } + + for (i = 0; i < finger_down_count; i++) { + if (data->chip->x_flip) + pos_x[i] = data->chip->touch_x_max - pos_x[i]; + if (data->chip->y_flip) + pos_y[i] = data->chip->touch_y_max - pos_y[i]; + + input_report_abs(data->in_dev, + ABS_MT_POSITION_X, pos_x[i]); + input_report_abs(data->in_dev, + ABS_MT_POSITION_Y, pos_y[i]); + input_mt_sync(data->in_dev); + } + } else + input_mt_sync(data->in_dev); + + input_sync(data->in_dev); + + return 0; +} +/** + * bu21013_gpio_irq() - gpio thread function for touch interrupt + * @irq: irq value + * @device_data: void pointer + * + * This gpio thread function for touch interrupt + * and returns irqreturn_t. + */ +static irqreturn_t bu21013_gpio_irq(int irq, void *device_data) +{ + struct bu21013_ts_data *data = device_data; + struct i2c_client *i2c = data->client; + int retval; + + do { + retval = bu21013_do_touch_report(data); + if (retval < 0) { + dev_err(&i2c->dev, "bu21013_do_touch_report failed\n"); + return IRQ_NONE; + } + + data->intr_pin = data->chip->irq_read_val(); + if (data->intr_pin == PEN_DOWN_INTR) + wait_event_timeout(data->wait, data->touch_stopped, + msecs_to_jiffies(2)); + } while (!data->intr_pin && !data->touch_stopped); + + return IRQ_HANDLED; +} + +/** + * bu21013_init_chip() - power on sequence for the bu21013 controller + * @data: device structure pointer + * + * This function is used to power on + * the bu21013 controller and returns integer. + */ +static int bu21013_init_chip(struct bu21013_ts_data *data) +{ + int retval; + struct i2c_client *i2c = data->client; + + retval = i2c_smbus_write_byte_data(i2c, BU21013_RESET_REG, + BU21013_RESET_ENABLE); + if (retval < 0) { + dev_err(&i2c->dev, "BU21013_RESET reg write failed\n"); + return retval; + } + msleep(RESET_DELAY); + + retval = i2c_smbus_write_byte_data(i2c, BU21013_SENSOR_0_7_REG, + BU21013_SENSORS_EN_0_7); + if (retval < 0) { + dev_err(&i2c->dev, "BU21013_SENSOR_0_7 reg write failed\n"); + return retval; + } + + retval = i2c_smbus_write_byte_data(i2c, BU21013_SENSOR_8_15_REG, + BU21013_SENSORS_EN_8_15); + if (retval < 0) { + dev_err(&i2c->dev, "BU21013_SENSOR_8_15 reg write failed\n"); + return retval; + } + + retval = i2c_smbus_write_byte_data(i2c, BU21013_SENSOR_16_23_REG, + BU21013_SENSORS_EN_16_23); + if (retval < 0) { + dev_err(&i2c->dev, "BU21013_SENSOR_16_23 reg write failed\n"); + return retval; + } + + retval = i2c_smbus_write_byte_data(i2c, BU21013_POS_MODE1_REG, + (BU21013_POS_MODE1_0 | BU21013_POS_MODE1_1)); + if (retval < 0) { + dev_err(&i2c->dev, "BU21013_POS_MODE1 reg write failed\n"); + return retval; + } + + retval = i2c_smbus_write_byte_data(i2c, BU21013_POS_MODE2_REG, + (BU21013_POS_MODE2_ZERO | BU21013_POS_MODE2_AVG1 | + BU21013_POS_MODE2_AVG2 | BU21013_POS_MODE2_EN_RAW | + BU21013_POS_MODE2_MULTI)); + if (retval < 0) { + dev_err(&i2c->dev, "BU21013_POS_MODE2 reg write failed\n"); + return retval; + } + + if (data->chip->ext_clk) + retval = i2c_smbus_write_byte_data(i2c, BU21013_CLK_MODE_REG, + (BU21013_CLK_MODE_EXT | BU21013_CLK_MODE_CALIB)); + else + retval = i2c_smbus_write_byte_data(i2c, BU21013_CLK_MODE_REG, + (BU21013_CLK_MODE_DIV | BU21013_CLK_MODE_CALIB)); + if (retval < 0) { + dev_err(&i2c->dev, "BU21013_CLK_MODE reg write failed\n"); + return retval; + } + + retval = i2c_smbus_write_byte_data(i2c, BU21013_IDLE_REG, + (BU21013_IDLET_0 | BU21013_IDLE_INTERMIT_EN)); + if (retval < 0) { + dev_err(&i2c->dev, "BU21013_IDLE reg write failed\n"); + return retval; + } + + retval = i2c_smbus_write_byte_data(i2c, BU21013_INT_MODE_REG, + BU21013_INT_MODE_LEVEL); + if (retval < 0) { + dev_err(&i2c->dev, "BU21013_INT_MODE reg write failed\n"); + return retval; + } + + retval = i2c_smbus_write_byte_data(i2c, BU21013_FILTER_REG, + (BU21013_DELTA_0_6 | + BU21013_FILTER_EN)); + if (retval < 0) { + dev_err(&i2c->dev, "BU21013_FILTER reg write failed\n"); + return retval; + } + + retval = i2c_smbus_write_byte_data(i2c, BU21013_TH_ON_REG, + BU21013_TH_ON_5); + if (retval < 0) { + dev_err(&i2c->dev, "BU21013_TH_ON reg write failed\n"); + return retval; + } + + retval = i2c_smbus_write_byte_data(i2c, BU21013_TH_OFF_REG, + BU21013_TH_OFF_4 || BU21013_TH_OFF_3); + if (retval < 0) { + dev_err(&i2c->dev, "BU21013_TH_OFF reg write failed\n"); + return retval; + } + + retval = i2c_smbus_write_byte_data(i2c, BU21013_GAIN_REG, + (BU21013_GAIN_0 | BU21013_GAIN_1)); + if (retval < 0) { + dev_err(&i2c->dev, "BU21013_GAIN reg write failed\n"); + return retval; + } + + retval = i2c_smbus_write_byte_data(i2c, BU21013_OFFSET_MODE_REG, + BU21013_OFFSET_MODE_DEFAULT); + if (retval < 0) { + dev_err(&i2c->dev, "BU21013_OFFSET_MODE reg write failed\n"); + return retval; + } + + retval = i2c_smbus_write_byte_data(i2c, BU21013_XY_EDGE_REG, + (BU21013_X_EDGE_0 | BU21013_X_EDGE_2 | + BU21013_Y_EDGE_1 | BU21013_Y_EDGE_3)); + if (retval < 0) { + dev_err(&i2c->dev, "BU21013_XY_EDGE reg write failed\n"); + return retval; + } + + retval = i2c_smbus_write_byte_data(i2c, BU21013_DONE_REG, + BU21013_DONE); + if (retval < 0) { + dev_err(&i2c->dev, "BU21013_REG_DONE reg write failed\n"); + return retval; + } + + return 0; +} + +/** + * bu21013_free_irq() - frees IRQ registered for touchscreen + * @bu21013_data: device structure pointer + * + * This function signals interrupt thread to stop processing and + * frees interrupt. + */ +static void bu21013_free_irq(struct bu21013_ts_data *bu21013_data) +{ + bu21013_data->touch_stopped = true; + wake_up(&bu21013_data->wait); + free_irq(bu21013_data->chip->irq, bu21013_data); +} + +/** + * bu21013_probe() - initializes the i2c-client touchscreen driver + * @client: i2c client structure pointer + * @id: i2c device id pointer + * + * This function used to initializes the i2c-client touchscreen + * driver and returns integer. + */ +static int __devinit bu21013_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct bu21013_ts_data *bu21013_data; + struct input_dev *in_dev; + const struct bu21013_platform_device *pdata = + client->dev.platform_data; + int error; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_err(&client->dev, "i2c smbus byte data not supported\n"); + return -EIO; + } + + if (!pdata) { + dev_err(&client->dev, "platform data not defined\n"); + return -EINVAL; + } + + bu21013_data = kzalloc(sizeof(struct bu21013_ts_data), GFP_KERNEL); + in_dev = input_allocate_device(); + if (!bu21013_data || !in_dev) { + dev_err(&client->dev, "device memory alloc failed\n"); + error = -ENOMEM; + goto err_free_mem; + } + + bu21013_data->in_dev = in_dev; + bu21013_data->chip = pdata; + bu21013_data->client = client; + bu21013_data->touch_stopped = false; + init_waitqueue_head(&bu21013_data->wait); + + /* configure the gpio pins */ + if (pdata->cs_en) { + error = pdata->cs_en(pdata->cs_pin); + if (error < 0) { + dev_err(&client->dev, "chip init failed\n"); + goto err_free_mem; + } + } + + /* configure the touch panel controller */ + error = bu21013_init_chip(bu21013_data); + if (error) { + dev_err(&client->dev, "error in bu21013 config\n"); + goto err_cs_disable; + } + + /* register the device to input subsystem */ + in_dev->name = DRIVER_TP; + in_dev->id.bustype = BUS_I2C; + in_dev->dev.parent = &client->dev; + + __set_bit(EV_SYN, in_dev->evbit); + __set_bit(EV_KEY, in_dev->evbit); + __set_bit(EV_ABS, in_dev->evbit); + + input_set_abs_params(in_dev, ABS_MT_POSITION_X, 0, + pdata->x_max_res, 0, 0); + input_set_abs_params(in_dev, ABS_MT_POSITION_Y, 0, + pdata->y_max_res, 0, 0); + input_set_drvdata(in_dev, bu21013_data); + + error = request_threaded_irq(pdata->irq, NULL, bu21013_gpio_irq, + IRQF_TRIGGER_FALLING | IRQF_SHARED, + DRIVER_TP, bu21013_data); + if (error) { + dev_err(&client->dev, "request irq %d failed\n", pdata->irq); + goto err_cs_disable; + } + + error = input_register_device(in_dev); + if (error) { + dev_err(&client->dev, "failed to register input device\n"); + goto err_free_irq; + } + + device_init_wakeup(&client->dev, pdata->wakeup); + i2c_set_clientdata(client, bu21013_data); + + return 0; + +err_free_irq: + bu21013_free_irq(bu21013_data); +err_cs_disable: + pdata->cs_dis(pdata->cs_pin); +err_free_mem: + input_free_device(bu21013_data->in_dev); + kfree(bu21013_data); + + return error; +} +/** + * bu21013_remove() - removes the i2c-client touchscreen driver + * @client: i2c client structure pointer + * + * This function uses to remove the i2c-client + * touchscreen driver and returns integer. + */ +static int __devexit bu21013_remove(struct i2c_client *client) +{ + struct bu21013_ts_data *bu21013_data = i2c_get_clientdata(client); + + bu21013_free_irq(bu21013_data); + + bu21013_data->chip->cs_dis(bu21013_data->chip->cs_pin); + + input_unregister_device(bu21013_data->in_dev); + kfree(bu21013_data); + + device_init_wakeup(&client->dev, false); + + return 0; +} + +#ifdef CONFIG_PM +/** + * bu21013_suspend() - suspend the touch screen controller + * @dev: pointer to device structure + * + * This function is used to suspend the + * touch panel controller and returns integer + */ +static int bu21013_suspend(struct device *dev) +{ + struct bu21013_ts_data *bu21013_data = dev_get_drvdata(dev); + struct i2c_client *client = bu21013_data->client; + + bu21013_data->touch_stopped = true; + if (device_may_wakeup(&client->dev)) + enable_irq_wake(bu21013_data->chip->irq); + else + disable_irq(bu21013_data->chip->irq); + + return 0; +} + +/** + * bu21013_resume() - resume the touch screen controller + * @dev: pointer to device structure + * + * This function is used to resume the touch panel + * controller and returns integer. + */ +static int bu21013_resume(struct device *dev) +{ + struct bu21013_ts_data *bu21013_data = dev_get_drvdata(dev); + struct i2c_client *client = bu21013_data->client; + int retval; + + retval = bu21013_init_chip(bu21013_data); + if (retval < 0) { + dev_err(&client->dev, "bu21013 controller config failed\n"); + return retval; + } + + bu21013_data->touch_stopped = false; + + if (device_may_wakeup(&client->dev)) + disable_irq_wake(bu21013_data->chip->irq); + else + enable_irq(bu21013_data->chip->irq); + + return 0; +} + +static const struct dev_pm_ops bu21013_dev_pm_ops = { + .suspend = bu21013_suspend, + .resume = bu21013_resume, +}; +#endif + +static const struct i2c_device_id bu21013_id[] = { + { DRIVER_TP, 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, bu21013_id); + +static struct i2c_driver bu21013_driver = { + .driver = { + .name = DRIVER_TP, + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &bu21013_dev_pm_ops, +#endif + }, + .probe = bu21013_probe, + .remove = __devexit_p(bu21013_remove), + .id_table = bu21013_id, +}; + +/** + * bu21013_init() - initializes the bu21013 touchscreen driver + * + * This function used to initializes the bu21013 + * touchscreen driver and returns integer. + */ +static int __init bu21013_init(void) +{ + return i2c_add_driver(&bu21013_driver); +} + +/** + * bu21013_exit() - de-initializes the bu21013 touchscreen driver + * + * This function uses to de-initializes the bu21013 + * touchscreen driver and returns none. + */ +static void __exit bu21013_exit(void) +{ + i2c_del_driver(&bu21013_driver); +} + +module_init(bu21013_init); +module_exit(bu21013_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Naveen Kumar G "); +MODULE_DESCRIPTION("bu21013 touch screen controller driver"); -- cgit v1.2.2 From a8b3c0f57beaba9035e5339175628b63e551b243 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 4 Oct 2010 21:46:10 -0700 Subject: Input: synaptics - simplify pass-through port handling There was too much knowledge about internals if serio in the pass-through handling, clean it up. Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/synaptics.c | 36 ++++++++++++++++++++++++++++++------ drivers/input/mouse/synaptics.h | 2 ++ 2 files changed, 32 insertions(+), 6 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index a79bc362dde5..2e300a460556 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -294,7 +294,29 @@ static int synaptics_pt_write(struct serio *serio, unsigned char c) return 0; } -static inline int synaptics_is_pt_packet(unsigned char *buf) +static int synaptics_pt_start(struct serio *serio) +{ + struct psmouse *parent = serio_get_drvdata(serio->parent); + struct synaptics_data *priv = parent->private; + + serio_pause_rx(parent->ps2dev.serio); + priv->pt_port = serio; + serio_continue_rx(parent->ps2dev.serio); + + return 0; +} + +static void synaptics_pt_stop(struct serio *serio) +{ + struct psmouse *parent = serio_get_drvdata(serio->parent); + struct synaptics_data *priv = parent->private; + + serio_pause_rx(parent->ps2dev.serio); + priv->pt_port = NULL; + serio_continue_rx(parent->ps2dev.serio); +} + +static int synaptics_is_pt_packet(unsigned char *buf) { return (buf[0] & 0xFC) == 0x84 && (buf[3] & 0xCC) == 0xC4; } @@ -315,9 +337,8 @@ static void synaptics_pass_pt_packet(struct serio *ptport, unsigned char *packet static void synaptics_pt_activate(struct psmouse *psmouse) { - struct serio *ptport = psmouse->ps2dev.serio->child; - struct psmouse *child = serio_get_drvdata(ptport); struct synaptics_data *priv = psmouse->private; + struct psmouse *child = serio_get_drvdata(priv->pt_port); /* adjust the touchpad to child's choice of protocol */ if (child) { @@ -345,6 +366,8 @@ static void synaptics_pt_create(struct psmouse *psmouse) strlcpy(serio->name, "Synaptics pass-through", sizeof(serio->name)); strlcpy(serio->phys, "synaptics-pt/serio0", sizeof(serio->name)); serio->write = synaptics_pt_write; + serio->start = synaptics_pt_start; + serio->stop = synaptics_pt_stop; serio->parent = psmouse->ps2dev.serio; psmouse->pt_activate = synaptics_pt_activate; @@ -578,9 +601,10 @@ static psmouse_ret_t synaptics_process_byte(struct psmouse *psmouse) if (unlikely(priv->pkt_type == SYN_NEWABS)) priv->pkt_type = synaptics_detect_pkt_type(psmouse); - if (SYN_CAP_PASS_THROUGH(priv->capabilities) && synaptics_is_pt_packet(psmouse->packet)) { - if (psmouse->ps2dev.serio->child) - synaptics_pass_pt_packet(psmouse->ps2dev.serio->child, psmouse->packet); + if (SYN_CAP_PASS_THROUGH(priv->capabilities) && + synaptics_is_pt_packet(psmouse->packet)) { + if (priv->pt_port) + synaptics_pass_pt_packet(priv->pt_port, psmouse->packet); } else synaptics_process_packet(psmouse); diff --git a/drivers/input/mouse/synaptics.h b/drivers/input/mouse/synaptics.h index b6aa7d20d8a3..613a3652f98f 100644 --- a/drivers/input/mouse/synaptics.h +++ b/drivers/input/mouse/synaptics.h @@ -110,6 +110,8 @@ struct synaptics_data { unsigned char pkt_type; /* packet type - old, new, etc */ unsigned char mode; /* current mode byte */ int scroll; + + struct serio *pt_port; /* Pass-through serio port */ }; void synaptics_module_init(void); -- cgit v1.2.2 From 0982258264d2f615612ab957634efdeb874f47c8 Mon Sep 17 00:00:00 2001 From: Dmitry Eremin-Solenikov Date: Mon, 4 Oct 2010 21:46:10 -0700 Subject: Input: serio - support multiple child devices per single parent Some (rare) serio devices need to have multiple serio children. One of the examples is PS/2 multiplexer present on several TQC STKxxx boards, which connect PS/2 keyboard and mouse to single tty port. Signed-off-by: Dmitry Eremin-Solenikov Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/psmouse-base.c | 4 +- drivers/input/serio/serio.c | 124 ++++++++++++++++++++++++------------- 2 files changed, 83 insertions(+), 45 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index 73a7af2542a8..cd9d0c97e429 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -1584,10 +1584,10 @@ static ssize_t psmouse_attr_set_protocol(struct psmouse *psmouse, void *data, co if (!new_dev) return -ENOMEM; - while (serio->child) { + while (!list_empty(&serio->children)) { if (++retry > 3) { printk(KERN_WARNING - "psmouse: failed to destroy child port, " + "psmouse: failed to destroy children ports, " "protocol change aborted.\n"); input_free_device(new_dev); return -EIO; diff --git a/drivers/input/serio/serio.c b/drivers/input/serio/serio.c index 8a426375fcb3..405bf214527c 100644 --- a/drivers/input/serio/serio.c +++ b/drivers/input/serio/serio.c @@ -55,7 +55,7 @@ static struct bus_type serio_bus; static void serio_add_port(struct serio *serio); static int serio_reconnect_port(struct serio *serio); static void serio_disconnect_port(struct serio *serio); -static void serio_reconnect_chain(struct serio *serio); +static void serio_reconnect_subtree(struct serio *serio); static void serio_attach_driver(struct serio_driver *drv); static int serio_connect_driver(struct serio *serio, struct serio_driver *drv) @@ -151,7 +151,7 @@ static void serio_find_driver(struct serio *serio) enum serio_event_type { SERIO_RESCAN_PORT, SERIO_RECONNECT_PORT, - SERIO_RECONNECT_CHAIN, + SERIO_RECONNECT_SUBTREE, SERIO_REGISTER_PORT, SERIO_ATTACH_DRIVER, }; @@ -291,8 +291,8 @@ static void serio_handle_event(void) serio_find_driver(event->object); break; - case SERIO_RECONNECT_CHAIN: - serio_reconnect_chain(event->object); + case SERIO_RECONNECT_SUBTREE: + serio_reconnect_subtree(event->object); break; case SERIO_ATTACH_DRIVER: @@ -329,12 +329,10 @@ static void serio_remove_pending_events(void *object) } /* - * Destroy child serio port (if any) that has not been fully registered yet. + * Locate child serio port (if any) that has not been fully registered yet. * - * Note that we rely on the fact that port can have only one child and therefore - * only one child registration request can be pending. Additionally, children - * are registered by driver's connect() handler so there can't be a grandchild - * pending registration together with a child. + * Children are registered by driver's connect() handler so there can't be a + * grandchild pending registration together with a child. */ static struct serio *serio_get_pending_child(struct serio *parent) { @@ -448,7 +446,7 @@ static ssize_t serio_rebind_driver(struct device *dev, struct device_attribute * if (!strncmp(buf, "none", count)) { serio_disconnect_port(serio); } else if (!strncmp(buf, "reconnect", count)) { - serio_reconnect_chain(serio); + serio_reconnect_subtree(serio); } else if (!strncmp(buf, "rescan", count)) { serio_disconnect_port(serio); serio_find_driver(serio); @@ -515,6 +513,8 @@ static void serio_init_port(struct serio *serio) __module_get(THIS_MODULE); INIT_LIST_HEAD(&serio->node); + INIT_LIST_HEAD(&serio->child_node); + INIT_LIST_HEAD(&serio->children); spin_lock_init(&serio->lock); mutex_init(&serio->drv_mutex); device_initialize(&serio->dev); @@ -537,12 +537,13 @@ static void serio_init_port(struct serio *serio) */ static void serio_add_port(struct serio *serio) { + struct serio *parent = serio->parent; int error; - if (serio->parent) { - serio_pause_rx(serio->parent); - serio->parent->child = serio; - serio_continue_rx(serio->parent); + if (parent) { + serio_pause_rx(parent); + list_add_tail(&serio->child_node, &parent->children); + serio_continue_rx(parent); } list_add_tail(&serio->node, &serio_list); @@ -558,15 +559,14 @@ static void serio_add_port(struct serio *serio) } /* - * serio_destroy_port() completes deregistration process and removes + * serio_destroy_port() completes unregistration process and removes * port from the system */ static void serio_destroy_port(struct serio *serio) { struct serio *child; - child = serio_get_pending_child(serio); - if (child) { + while ((child = serio_get_pending_child(serio)) != NULL) { serio_remove_pending_events(child); put_device(&child->dev); } @@ -576,7 +576,7 @@ static void serio_destroy_port(struct serio *serio) if (serio->parent) { serio_pause_rx(serio->parent); - serio->parent->child = NULL; + list_del_init(&serio->child_node); serio_continue_rx(serio->parent); serio->parent = NULL; } @@ -608,46 +608,82 @@ static int serio_reconnect_port(struct serio *serio) } /* - * Reconnect serio port and all its children (re-initialize attached devices) + * Reconnect serio port and all its children (re-initialize attached + * devices). */ -static void serio_reconnect_chain(struct serio *serio) +static void serio_reconnect_subtree(struct serio *root) { + struct serio *s = root; + int error; + do { - if (serio_reconnect_port(serio)) { - /* Ok, old children are now gone, we are done */ - break; + error = serio_reconnect_port(s); + if (!error) { + /* + * Reconnect was successful, move on to do the + * first child. + */ + if (!list_empty(&s->children)) { + s = list_first_entry(&s->children, + struct serio, child_node); + continue; + } } - serio = serio->child; - } while (serio); + + /* + * Either it was a leaf node or reconnect failed and it + * became a leaf node. Continue reconnecting starting with + * the next sibling of the parent node. + */ + while (s != root) { + struct serio *parent = s->parent; + + if (!list_is_last(&s->child_node, &parent->children)) { + s = list_entry(s->child_node.next, + struct serio, child_node); + break; + } + + s = parent; + } + } while (s != root); } /* * serio_disconnect_port() unbinds a port from its driver. As a side effect - * all child ports are unbound and destroyed. + * all children ports are unbound and destroyed. */ static void serio_disconnect_port(struct serio *serio) { - struct serio *s, *parent; + struct serio *s = serio; + + /* + * Children ports should be disconnected and destroyed + * first; we travel the tree in depth-first order. + */ + while (!list_empty(&serio->children)) { + + /* Locate a leaf */ + while (!list_empty(&s->children)) + s = list_first_entry(&s->children, + struct serio, child_node); - if (serio->child) { /* - * Children ports should be disconnected and destroyed - * first, staring with the leaf one, since we don't want - * to do recursion + * Prune this leaf node unless it is the one we + * started with. */ - for (s = serio; s->child; s = s->child) - /* empty */; - - do { - parent = s->parent; + if (s != serio) { + struct serio *parent = s->parent; device_release_driver(&s->dev); serio_destroy_port(s); - } while ((s = parent) != serio); + + s = parent; + } } /* - * Ok, no children left, now disconnect this port + * OK, no children left, now disconnect this port. */ device_release_driver(&serio->dev); } @@ -660,7 +696,7 @@ EXPORT_SYMBOL(serio_rescan); void serio_reconnect(struct serio *serio) { - serio_queue_event(serio, NULL, SERIO_RECONNECT_CHAIN); + serio_queue_event(serio, NULL, SERIO_RECONNECT_SUBTREE); } EXPORT_SYMBOL(serio_reconnect); @@ -688,14 +724,16 @@ void serio_unregister_port(struct serio *serio) EXPORT_SYMBOL(serio_unregister_port); /* - * Safely unregisters child port if one is present. + * Safely unregisters children ports if they are present. */ void serio_unregister_child_port(struct serio *serio) { + struct serio *s, *next; + mutex_lock(&serio_mutex); - if (serio->child) { - serio_disconnect_port(serio->child); - serio_destroy_port(serio->child); + list_for_each_entry_safe(s, next, &serio->children, child_node) { + serio_disconnect_port(s); + serio_destroy_port(s); } mutex_unlock(&serio_mutex); } -- cgit v1.2.2 From 2991a1ca6e9b13b639a82c0eec0cbc191bf1f42f Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Wed, 13 Oct 2010 11:35:40 -0700 Subject: Input: ads7846 - switch to using threaded IRQ Commit 9114337 introduces regulator operations in ads7846 touchscreen driver. Among these operations, some are called while holding a spinlock. On many platforms regulators reside on slow buses, such as I2C/SPI and require sleep while accessing them. The touchscreen itself is also a SPI device and currently relies on asynchronous SPI access to avoid sleeping in interrupt context. Let's switch to using threaded IRQ to be able to access SPI bus synchronously (which simplifies driver a bit); it also allows safe access to the regulators as well. This has been tested on the ti_omap3530evm board: 1) using ts_lib after normal boot 2) using ts_lib after "#echo 1/0 > /sys/bus/spi/devices/spi0.1/disable" 3) using ts_lib after "#echo mem > /sys/power/state" and "wake up" Also tested on pandora. Based on original patch by Dmitry Torokhov. Tested-by: Grazvydas Ignotas Signed-off-by: Jason Wang Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/ads7846.c | 886 +++++++++++++++++++----------------- 1 file changed, 456 insertions(+), 430 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c index 16031933a8f6..14ea54b78e46 100644 --- a/drivers/input/touchscreen/ads7846.c +++ b/drivers/input/touchscreen/ads7846.c @@ -17,9 +17,11 @@ * 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 @@ -52,22 +54,23 @@ * files. */ -#define TS_POLL_DELAY (1 * 1000000) /* ns delay before the first sample */ -#define TS_POLL_PERIOD (5 * 1000000) /* ns delay between samples */ +#define TS_POLL_DELAY 1 /* ms delay before the first sample */ +#define TS_POLL_PERIOD 5 /* ms delay between samples */ /* this driver doesn't aim at the peak continuous sample rate */ #define SAMPLE_BITS (8 /*cmd*/ + 16 /*sample*/ + 2 /* before, after */) struct ts_event { - /* For portability, we can't read 12 bit values using SPI (which - * would make the controller deliver them as native byteorder u16 + /* + * For portability, we can't read 12 bit values using SPI (which + * would make the controller deliver them as native byte order u16 * with msbs zeroed). Instead, we read them as two 8-bit values, * *** WHICH NEED BYTESWAPPING *** and range adjustment. */ u16 x; u16 y; u16 z1, z2; - int ignore; + bool ignore; u8 x_buf[3]; u8 y_buf[3]; }; @@ -110,8 +113,11 @@ struct ads7846 { struct spi_transfer xfer[18]; struct spi_message msg[5]; - struct spi_message *last_msg; - int msg_idx; + int msg_count; + wait_queue_head_t wait; + + bool pendown; + int read_cnt; int read_rep; int last_read; @@ -122,14 +128,10 @@ struct ads7846 { u16 penirq_recheck_delay_usecs; - spinlock_t lock; - struct hrtimer timer; - unsigned pendown:1; /* P: lock */ - unsigned pending:1; /* P: lock */ -// FIXME remove "irq_disabled" - unsigned irq_disabled:1; /* P: lock */ - unsigned disabled:1; - unsigned is_suspended:1; + struct mutex lock; + bool stopped; /* P: lock */ + bool disabled; /* P: lock */ + bool suspended; /* P: lock */ int (*filter)(void *data, int data_idx, int *val); void *filter_data; @@ -165,7 +167,7 @@ struct ads7846 { #define ADS_12_BIT (0 << 3) #define ADS_SER (1 << 2) /* non-differential */ #define ADS_DFR (0 << 2) /* differential */ -#define ADS_PD10_PDOWN (0 << 0) /* lowpower mode + penirq */ +#define ADS_PD10_PDOWN (0 << 0) /* low power mode + penirq */ #define ADS_PD10_ADC_ON (1 << 0) /* ADC on */ #define ADS_PD10_REF_ON (2 << 0) /* vREF on + penirq */ #define ADS_PD10_ALL_ON (3 << 0) /* ADC + vREF on */ @@ -193,6 +195,78 @@ struct ads7846 { #define REF_ON (READ_12BIT_DFR(x, 1, 1)) #define REF_OFF (READ_12BIT_DFR(y, 0, 0)) +/* Must be called with ts->lock held */ +static void ads7846_stop(struct ads7846 *ts) +{ + if (!ts->disabled && !ts->suspended) { + /* Signal IRQ thread to stop polling and disable the handler. */ + ts->stopped = true; + mb(); + wake_up(&ts->wait); + disable_irq(ts->spi->irq); + } +} + +/* Must be called with ts->lock held */ +static void ads7846_restart(struct ads7846 *ts) +{ + if (!ts->disabled && !ts->suspended) { + /* Tell IRQ thread that it may poll the device. */ + ts->stopped = false; + mb(); + enable_irq(ts->spi->irq); + } +} + +/* Must be called with ts->lock held */ +static void __ads7846_disable(struct ads7846 *ts) +{ + ads7846_stop(ts); + regulator_disable(ts->reg); + + /* + * We know the chip's in low power mode since we always + * leave it that way after every request + */ +} + +/* Must be called with ts->lock held */ +static void __ads7846_enable(struct ads7846 *ts) +{ + regulator_enable(ts->reg); + ads7846_restart(ts); +} + +static void ads7846_disable(struct ads7846 *ts) +{ + mutex_lock(&ts->lock); + + if (!ts->disabled) { + + if (!ts->suspended) + __ads7846_disable(ts); + + ts->disabled = true; + } + + mutex_unlock(&ts->lock); +} + +static void ads7846_enable(struct ads7846 *ts) +{ + mutex_lock(&ts->lock); + + if (ts->disabled) { + + ts->disabled = false; + + if (!ts->suspended) + __ads7846_enable(ts); + } + + mutex_unlock(&ts->lock); +} + /*--------------------------------------------------------------------------*/ /* @@ -219,23 +293,15 @@ struct ads7845_ser_req { struct spi_transfer xfer[2]; }; -static void ads7846_enable(struct ads7846 *ts); -static void ads7846_disable(struct ads7846 *ts); - -static int device_suspended(struct device *dev) -{ - struct ads7846 *ts = dev_get_drvdata(dev); - return ts->is_suspended || ts->disabled; -} - static int ads7846_read12_ser(struct device *dev, unsigned command) { - struct spi_device *spi = to_spi_device(dev); - struct ads7846 *ts = dev_get_drvdata(dev); - struct ser_req *req = kzalloc(sizeof *req, GFP_KERNEL); - int status; - int use_internal; + struct spi_device *spi = to_spi_device(dev); + struct ads7846 *ts = dev_get_drvdata(dev); + struct ser_req *req; + int status; + int use_internal; + req = kzalloc(sizeof *req, GFP_KERNEL); if (!req) return -ENOMEM; @@ -282,11 +348,11 @@ static int ads7846_read12_ser(struct device *dev, unsigned command) CS_CHANGE(req->xfer[5]); spi_message_add_tail(&req->xfer[5], &req->msg); - ts->irq_disabled = 1; - disable_irq(spi->irq); + mutex_lock(&ts->lock); + ads7846_stop(ts); status = spi_sync(spi, &req->msg); - ts->irq_disabled = 0; - enable_irq(spi->irq); + ads7846_restart(ts); + mutex_unlock(&ts->lock); if (status == 0) { /* on-wire is a must-ignore bit, a BE12 value, then padding */ @@ -301,11 +367,12 @@ static int ads7846_read12_ser(struct device *dev, unsigned command) static int ads7845_read12_ser(struct device *dev, unsigned command) { - struct spi_device *spi = to_spi_device(dev); - struct ads7846 *ts = dev_get_drvdata(dev); - struct ads7845_ser_req *req = kzalloc(sizeof *req, GFP_KERNEL); - int status; + struct spi_device *spi = to_spi_device(dev); + struct ads7846 *ts = dev_get_drvdata(dev); + struct ads7845_ser_req *req; + int status; + req = kzalloc(sizeof *req, GFP_KERNEL); if (!req) return -ENOMEM; @@ -317,11 +384,11 @@ static int ads7845_read12_ser(struct device *dev, unsigned command) req->xfer[0].len = 3; spi_message_add_tail(&req->xfer[0], &req->msg); - ts->irq_disabled = 1; - disable_irq(spi->irq); + mutex_lock(&ts->lock); + ads7846_stop(ts); status = spi_sync(spi, &req->msg); - ts->irq_disabled = 0; - enable_irq(spi->irq); + ads7846_restart(ts); + mutex_unlock(&ts->lock); if (status == 0) { /* BE12 value, then padding */ @@ -374,6 +441,7 @@ static inline unsigned vaux_adjust(struct ads7846 *ts, ssize_t v) /* external resistors may scale vAUX into 0..vREF */ retval *= ts->vref_mv; retval = retval >> 12; + return retval; } @@ -384,13 +452,13 @@ static inline unsigned vbatt_adjust(struct ads7846 *ts, ssize_t v) /* ads7846 has a resistor ladder to scale this signal down */ if (ts->model == 7846) retval *= 4; + return retval; } SHOW(in0_input, vaux, vaux_adjust) SHOW(in1_input, vbatt, vbatt_adjust) - static struct attribute *ads7846_attributes[] = { &dev_attr_temp0.attr, &dev_attr_temp1.attr, @@ -498,17 +566,12 @@ static inline void ads784x_hwmon_unregister(struct spi_device *spi, } #endif -static int is_pen_down(struct device *dev) -{ - struct ads7846 *ts = dev_get_drvdata(dev); - - return ts->pendown; -} - static ssize_t ads7846_pen_down_show(struct device *dev, struct device_attribute *attr, char *buf) { - return sprintf(buf, "%u\n", is_pen_down(dev)); + struct ads7846 *ts = dev_get_drvdata(dev); + + return sprintf(buf, "%u\n", ts->pendown); } static DEVICE_ATTR(pen_down, S_IRUGO, ads7846_pen_down_show, NULL); @@ -516,7 +579,7 @@ static DEVICE_ATTR(pen_down, S_IRUGO, ads7846_pen_down_show, NULL); static ssize_t ads7846_disable_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct ads7846 *ts = dev_get_drvdata(dev); + struct ads7846 *ts = dev_get_drvdata(dev); return sprintf(buf, "%u\n", ts->disabled); } @@ -531,15 +594,11 @@ static ssize_t ads7846_disable_store(struct device *dev, if (strict_strtoul(buf, 10, &i)) return -EINVAL; - spin_lock_irq(&ts->lock); - if (i) ads7846_disable(ts); else ads7846_enable(ts); - spin_unlock_irq(&ts->lock); - return count; } @@ -569,23 +628,141 @@ static void null_wait_for_sync(void) { } -/* - * PENIRQ only kicks the timer. The timer only reissues the SPI transfer, - * to retrieve touchscreen status. - * - * The SPI transfer completion callback does the real work. It reports - * touchscreen events and reactivates the timer (or IRQ) as appropriate. - */ +static int ads7846_debounce_filter(void *ads, int data_idx, int *val) +{ + struct ads7846 *ts = ads; + + if (!ts->read_cnt || (abs(ts->last_read - *val) > ts->debounce_tol)) { + /* Start over collecting consistent readings. */ + ts->read_rep = 0; + /* + * Repeat it, if this was the first read or the read + * wasn't consistent enough. + */ + if (ts->read_cnt < ts->debounce_max) { + ts->last_read = *val; + ts->read_cnt++; + return ADS7846_FILTER_REPEAT; + } else { + /* + * Maximum number of debouncing reached and still + * not enough number of consistent readings. Abort + * the whole sample, repeat it in the next sampling + * period. + */ + ts->read_cnt = 0; + return ADS7846_FILTER_IGNORE; + } + } else { + if (++ts->read_rep > ts->debounce_rep) { + /* + * Got a good reading for this coordinate, + * go for the next one. + */ + ts->read_cnt = 0; + ts->read_rep = 0; + return ADS7846_FILTER_OK; + } else { + /* Read more values that are consistent. */ + ts->read_cnt++; + return ADS7846_FILTER_REPEAT; + } + } +} + +static int ads7846_no_filter(void *ads, int data_idx, int *val) +{ + return ADS7846_FILTER_OK; +} + +static int ads7846_get_value(struct ads7846 *ts, struct spi_message *m) +{ + struct spi_transfer *t = + list_entry(m->transfers.prev, struct spi_transfer, transfer_list); + + if (ts->model == 7845) { + return be16_to_cpup((__be16 *)&(((char*)t->rx_buf)[1])) >> 3; + } else { + /* + * adjust: on-wire is a must-ignore bit, a BE12 value, then + * padding; built from two 8 bit values written msb-first. + */ + return be16_to_cpup((__be16 *)t->rx_buf) >> 3; + } +} + +static void ads7846_update_value(struct spi_message *m, int val) +{ + struct spi_transfer *t = + list_entry(m->transfers.prev, struct spi_transfer, transfer_list); + + *(u16 *)t->rx_buf = val; +} + +static void ads7846_read_state(struct ads7846 *ts) +{ + struct ads7846_packet *packet = ts->packet; + struct spi_message *m; + int msg_idx = 0; + int val; + int action; + int error; + + while (msg_idx < ts->msg_count) { + + ts->wait_for_sync(); + + m = &ts->msg[msg_idx]; + error = spi_sync(ts->spi, m); + if (error) { + dev_err(&ts->spi->dev, "spi_async --> %d\n", error); + packet->tc.ignore = true; + return; + } + + /* + * Last message is power down request, no need to convert + * or filter the value. + */ + if (msg_idx < ts->msg_count - 1) { -static void ads7846_rx(void *ads) + val = ads7846_get_value(ts, m); + + action = ts->filter(ts->filter_data, msg_idx, &val); + switch (action) { + case ADS7846_FILTER_REPEAT: + continue; + + case ADS7846_FILTER_IGNORE: + packet->tc.ignore = true; + msg_idx = ts->msg_count - 1; + continue; + + case ADS7846_FILTER_OK: + ads7846_update_value(m, val); + packet->tc.ignore = false; + msg_idx++; + break; + + default: + BUG(); + } + } else { + msg_idx++; + } + } +} + +static void ads7846_report_state(struct ads7846 *ts) { - struct ads7846 *ts = ads; - struct ads7846_packet *packet = ts->packet; - unsigned Rt; - u16 x, y, z1, z2; + struct ads7846_packet *packet = ts->packet; + unsigned int Rt; + u16 x, y, z1, z2; - /* ads7846_rx_val() did in-place conversion (including byteswap) from - * on-the-wire format as part of debouncing to get stable readings. + /* + * ads7846_get_value() does in-place conversion (including byte swap) + * from on-the-wire format as part of debouncing to get stable + * readings. */ if (ts->model == 7845) { x = *(u16 *)packet->tc.x_buf; @@ -623,19 +800,19 @@ static void ads7846_rx(void *ads) Rt = 0; } - /* Sample found inconsistent by debouncing or pressure is beyond + /* + * Sample found inconsistent by debouncing or pressure is beyond * the maximum. Don't report it to user space, repeat at least * once more the measurement */ if (packet->tc.ignore || Rt > ts->pressure_max) { dev_vdbg(&ts->spi->dev, "ignored %d pressure %d\n", packet->tc.ignore, Rt); - hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_PERIOD), - HRTIMER_MODE_REL); return; } - /* Maybe check the pendown state before reporting. This discards + /* + * Maybe check the pendown state before reporting. This discards * false readings when the pen is lifted. */ if (ts->penirq_recheck_delay_usecs) { @@ -644,8 +821,9 @@ static void ads7846_rx(void *ads) Rt = 0; } - /* NOTE: We can't rely on the pressure to determine the pen down - * state, even this controller has a pressure sensor. The pressure + /* + * NOTE: We can't rely on the pressure to determine the pen down + * state, even this controller has a pressure sensor. The pressure * value can fluctuate for quite a while after lifting the pen and * in some cases may not even settle at the expected value. * @@ -655,15 +833,15 @@ static void ads7846_rx(void *ads) if (Rt) { struct input_dev *input = ts->input; + if (ts->swap_xy) + swap(x, y); + if (!ts->pendown) { input_report_key(input, BTN_TOUCH, 1); - ts->pendown = 1; + ts->pendown = true; dev_vdbg(&ts->spi->dev, "DOWN\n"); } - if (ts->swap_xy) - swap(x, y); - input_report_abs(input, ABS_X, x); input_report_abs(input, ABS_Y, y); input_report_abs(input, ABS_PRESSURE, ts->pressure_max - Rt); @@ -671,246 +849,94 @@ static void ads7846_rx(void *ads) input_sync(input); dev_vdbg(&ts->spi->dev, "%4d/%4d/%4d\n", x, y, Rt); } - - hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_PERIOD), - HRTIMER_MODE_REL); -} - -static int ads7846_debounce(void *ads, int data_idx, int *val) -{ - struct ads7846 *ts = ads; - - if (!ts->read_cnt || (abs(ts->last_read - *val) > ts->debounce_tol)) { - /* Start over collecting consistent readings. */ - ts->read_rep = 0; - /* Repeat it, if this was the first read or the read - * wasn't consistent enough. */ - if (ts->read_cnt < ts->debounce_max) { - ts->last_read = *val; - ts->read_cnt++; - return ADS7846_FILTER_REPEAT; - } else { - /* Maximum number of debouncing reached and still - * not enough number of consistent readings. Abort - * the whole sample, repeat it in the next sampling - * period. - */ - ts->read_cnt = 0; - return ADS7846_FILTER_IGNORE; - } - } else { - if (++ts->read_rep > ts->debounce_rep) { - /* Got a good reading for this coordinate, - * go for the next one. */ - ts->read_cnt = 0; - ts->read_rep = 0; - return ADS7846_FILTER_OK; - } else { - /* Read more values that are consistent. */ - ts->read_cnt++; - return ADS7846_FILTER_REPEAT; - } - } } -static int ads7846_no_filter(void *ads, int data_idx, int *val) +static irqreturn_t ads7846_hard_irq(int irq, void *handle) { - return ADS7846_FILTER_OK; -} - -static void ads7846_rx_val(void *ads) -{ - struct ads7846 *ts = ads; - struct ads7846_packet *packet = ts->packet; - struct spi_message *m; - struct spi_transfer *t; - int val; - int action; - int status; - - m = &ts->msg[ts->msg_idx]; - t = list_entry(m->transfers.prev, struct spi_transfer, transfer_list); - - if (ts->model == 7845) { - val = be16_to_cpup((__be16 *)&(((char*)t->rx_buf)[1])) >> 3; - } else { - /* adjust: on-wire is a must-ignore bit, a BE12 value, then - * padding; built from two 8 bit values written msb-first. - */ - val = be16_to_cpup((__be16 *)t->rx_buf) >> 3; - } + struct ads7846 *ts = handle; - action = ts->filter(ts->filter_data, ts->msg_idx, &val); - switch (action) { - case ADS7846_FILTER_REPEAT: - break; - case ADS7846_FILTER_IGNORE: - packet->tc.ignore = 1; - /* Last message will contain ads7846_rx() as the - * completion function. - */ - m = ts->last_msg; - break; - case ADS7846_FILTER_OK: - *(u16 *)t->rx_buf = val; - packet->tc.ignore = 0; - m = &ts->msg[++ts->msg_idx]; - break; - default: - BUG(); - } - ts->wait_for_sync(); - status = spi_async(ts->spi, m); - if (status) - dev_err(&ts->spi->dev, "spi_async --> %d\n", - status); + return get_pendown_state(ts) ? IRQ_WAKE_THREAD : IRQ_HANDLED; } -static enum hrtimer_restart ads7846_timer(struct hrtimer *handle) -{ - struct ads7846 *ts = container_of(handle, struct ads7846, timer); - int status = 0; - - spin_lock(&ts->lock); - - if (unlikely(!get_pendown_state(ts) || - device_suspended(&ts->spi->dev))) { - if (ts->pendown) { - struct input_dev *input = ts->input; - - input_report_key(input, BTN_TOUCH, 0); - input_report_abs(input, ABS_PRESSURE, 0); - input_sync(input); - - ts->pendown = 0; - dev_vdbg(&ts->spi->dev, "UP\n"); - } - - /* measurement cycle ended */ - if (!device_suspended(&ts->spi->dev)) { - ts->irq_disabled = 0; - enable_irq(ts->spi->irq); - } - ts->pending = 0; - } else { - /* pen is still down, continue with the measurement */ - ts->msg_idx = 0; - ts->wait_for_sync(); - status = spi_async(ts->spi, &ts->msg[0]); - if (status) - dev_err(&ts->spi->dev, "spi_async --> %d\n", status); - } - - spin_unlock(&ts->lock); - return HRTIMER_NORESTART; -} static irqreturn_t ads7846_irq(int irq, void *handle) { struct ads7846 *ts = handle; - unsigned long flags; - - spin_lock_irqsave(&ts->lock, flags); - if (likely(get_pendown_state(ts))) { - if (!ts->irq_disabled) { - /* The ARM do_simple_IRQ() dispatcher doesn't act - * like the other dispatchers: it will report IRQs - * even after they've been disabled. We work around - * that here. (The "generic irq" framework may help...) - */ - ts->irq_disabled = 1; - disable_irq_nosync(ts->spi->irq); - ts->pending = 1; - hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_DELAY), - HRTIMER_MODE_REL); - } - } - spin_unlock_irqrestore(&ts->lock, flags); - return IRQ_HANDLED; -} + /* Start with a small delay before checking pendown state */ + msleep(TS_POLL_DELAY); -/*--------------------------------------------------------------------------*/ + while (!ts->stopped && get_pendown_state(ts)) { -/* Must be called with ts->lock held */ -static void ads7846_disable(struct ads7846 *ts) -{ - if (ts->disabled) - return; + /* pen is down, continue with the measurement */ + ads7846_read_state(ts); - ts->disabled = 1; + if (!ts->stopped) + ads7846_report_state(ts); - /* are we waiting for IRQ, or polling? */ - if (!ts->pending) { - ts->irq_disabled = 1; - disable_irq(ts->spi->irq); - } else { - /* the timer will run at least once more, and - * leave everything in a clean state, IRQ disabled - */ - while (ts->pending) { - spin_unlock_irq(&ts->lock); - msleep(1); - spin_lock_irq(&ts->lock); - } + wait_event_timeout(ts->wait, ts->stopped, + msecs_to_jiffies(TS_POLL_PERIOD)); } - regulator_disable(ts->reg); - - /* we know the chip's in lowpower mode since we always - * leave it that way after every request - */ -} + if (ts->pendown) { + struct input_dev *input = ts->input; -/* Must be called with ts->lock held */ -static void ads7846_enable(struct ads7846 *ts) -{ - if (!ts->disabled) - return; + input_report_key(input, BTN_TOUCH, 0); + input_report_abs(input, ABS_PRESSURE, 0); + input_sync(input); - regulator_enable(ts->reg); + ts->pendown = false; + dev_vdbg(&ts->spi->dev, "UP\n"); + } - ts->disabled = 0; - ts->irq_disabled = 0; - enable_irq(ts->spi->irq); + return IRQ_HANDLED; } static int ads7846_suspend(struct spi_device *spi, pm_message_t message) { struct ads7846 *ts = dev_get_drvdata(&spi->dev); - spin_lock_irq(&ts->lock); + mutex_lock(&ts->lock); - ts->is_suspended = 1; - ads7846_disable(ts); + if (!ts->suspended) { - spin_unlock_irq(&ts->lock); + if (!ts->disabled) + __ads7846_disable(ts); - if (device_may_wakeup(&ts->spi->dev)) - enable_irq_wake(ts->spi->irq); + if (device_may_wakeup(&ts->spi->dev)) + enable_irq_wake(ts->spi->irq); - return 0; + ts->suspended = true; + } + + mutex_unlock(&ts->lock); + return 0; } static int ads7846_resume(struct spi_device *spi) { struct ads7846 *ts = dev_get_drvdata(&spi->dev); - if (device_may_wakeup(&ts->spi->dev)) - disable_irq_wake(ts->spi->irq); + mutex_lock(&ts->lock); + + if (ts->suspended) { - spin_lock_irq(&ts->lock); + ts->suspended = false; - ts->is_suspended = 0; - ads7846_enable(ts); + if (device_may_wakeup(&ts->spi->dev)) + disable_irq_wake(ts->spi->irq); - spin_unlock_irq(&ts->lock); + if (!ts->disabled) + __ads7846_enable(ts); + } + + mutex_unlock(&ts->lock); return 0; } -static int __devinit setup_pendown(struct spi_device *spi, struct ads7846 *ts) +static int __devinit ads7846_setup_pendown(struct spi_device *spi, struct ads7846 *ts) { struct ads7846_platform_data *pdata = spi->dev.platform_data; int err; @@ -932,146 +958,40 @@ static int __devinit setup_pendown(struct spi_device *spi, struct ads7846 *ts) err = gpio_request(pdata->gpio_pendown, "ads7846_pendown"); if (err) { dev_err(&spi->dev, "failed to request pendown GPIO%d\n", - pdata->gpio_pendown); + pdata->gpio_pendown); return err; } ts->gpio_pendown = pdata->gpio_pendown; + return 0; } -static int __devinit ads7846_probe(struct spi_device *spi) +/* + * Set up the transfers to read touchscreen state; this assumes we + * use formula #2 for pressure, not #3. + */ +static void __devinit ads7846_setup_spi_msg(struct ads7846 *ts, + const struct ads7846_platform_data *pdata) { - struct ads7846 *ts; - struct ads7846_packet *packet; - struct input_dev *input_dev; - const struct ads7846_platform_data *pdata = spi->dev.platform_data; - struct spi_message *m; - struct spi_transfer *x; - unsigned long irq_flags; - int vref; - int err; - - if (!spi->irq) { - dev_dbg(&spi->dev, "no IRQ?\n"); - return -ENODEV; - } - - if (!pdata) { - dev_dbg(&spi->dev, "no platform data?\n"); - return -ENODEV; - } - - /* don't exceed max specified sample rate */ - if (spi->max_speed_hz > (125000 * SAMPLE_BITS)) { - dev_dbg(&spi->dev, "f(sample) %d KHz?\n", - (spi->max_speed_hz/SAMPLE_BITS)/1000); - return -EINVAL; - } - - /* We'd set TX wordsize 8 bits and RX wordsize to 13 bits ... except - * that even if the hardware can do that, the SPI controller driver - * may not. So we stick to very-portable 8 bit words, both RX and TX. - */ - spi->bits_per_word = 8; - spi->mode = SPI_MODE_0; - err = spi_setup(spi); - if (err < 0) - return err; - - ts = kzalloc(sizeof(struct ads7846), GFP_KERNEL); - packet = kzalloc(sizeof(struct ads7846_packet), GFP_KERNEL); - input_dev = input_allocate_device(); - if (!ts || !packet || !input_dev) { - err = -ENOMEM; - goto err_free_mem; - } - - dev_set_drvdata(&spi->dev, ts); - - ts->packet = packet; - ts->spi = spi; - ts->input = input_dev; - ts->vref_mv = pdata->vref_mv; - ts->swap_xy = pdata->swap_xy; - - hrtimer_init(&ts->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); - ts->timer.function = ads7846_timer; - - spin_lock_init(&ts->lock); - - ts->model = pdata->model ? : 7846; - ts->vref_delay_usecs = pdata->vref_delay_usecs ? : 100; - ts->x_plate_ohms = pdata->x_plate_ohms ? : 400; - ts->pressure_max = pdata->pressure_max ? : ~0; - - if (pdata->filter != NULL) { - if (pdata->filter_init != NULL) { - err = pdata->filter_init(pdata, &ts->filter_data); - if (err < 0) - goto err_free_mem; - } - ts->filter = pdata->filter; - ts->filter_cleanup = pdata->filter_cleanup; - } else if (pdata->debounce_max) { - ts->debounce_max = pdata->debounce_max; - if (ts->debounce_max < 2) - ts->debounce_max = 2; - ts->debounce_tol = pdata->debounce_tol; - ts->debounce_rep = pdata->debounce_rep; - ts->filter = ads7846_debounce; - ts->filter_data = ts; - } else - ts->filter = ads7846_no_filter; - - err = setup_pendown(spi, ts); - if (err) - goto err_cleanup_filter; - - if (pdata->penirq_recheck_delay_usecs) - ts->penirq_recheck_delay_usecs = - pdata->penirq_recheck_delay_usecs; - - ts->wait_for_sync = pdata->wait_for_sync ? : null_wait_for_sync; - - snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(&spi->dev)); - snprintf(ts->name, sizeof(ts->name), "ADS%d Touchscreen", ts->model); - - input_dev->name = ts->name; - input_dev->phys = ts->phys; - input_dev->dev.parent = &spi->dev; - - input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); - input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); - input_set_abs_params(input_dev, ABS_X, - pdata->x_min ? : 0, - pdata->x_max ? : MAX_12BIT, - 0, 0); - input_set_abs_params(input_dev, ABS_Y, - pdata->y_min ? : 0, - pdata->y_max ? : MAX_12BIT, - 0, 0); - input_set_abs_params(input_dev, ABS_PRESSURE, - pdata->pressure_min, pdata->pressure_max, 0, 0); - - vref = pdata->keep_vref_on; + struct spi_message *m = &ts->msg[0]; + struct spi_transfer *x = ts->xfer; + struct ads7846_packet *packet = ts->packet; + int vref = pdata->keep_vref_on; if (ts->model == 7873) { - /* The AD7873 is almost identical to the ADS7846 + /* + * The AD7873 is almost identical to the ADS7846 * keep VREF off during differential/ratiometric - * conversion modes + * conversion modes. */ ts->model = 7846; vref = 0; } - /* set up the transfers to read touchscreen state; this assumes we - * use formula #2 for pressure, not #3. - */ - m = &ts->msg[0]; - x = ts->xfer; - + ts->msg_count = 1; spi_message_init(m); + m->context = ts; if (ts->model == 7845) { packet->read_y_cmd[0] = READ_Y(vref); @@ -1094,7 +1014,8 @@ static int __devinit ads7846_probe(struct spi_device *spi) spi_message_add_tail(x, m); } - /* the first sample after switching drivers can be low quality; + /* + * The first sample after switching drivers can be low quality; * optionally discard it, using a second one after the signals * have had enough time to stabilize. */ @@ -1112,11 +1033,10 @@ static int __devinit ads7846_probe(struct spi_device *spi) spi_message_add_tail(x, m); } - m->complete = ads7846_rx_val; - m->context = ts; - + ts->msg_count++; m++; spi_message_init(m); + m->context = ts; if (ts->model == 7845) { x++; @@ -1156,13 +1076,12 @@ static int __devinit ads7846_probe(struct spi_device *spi) spi_message_add_tail(x, m); } - m->complete = ads7846_rx_val; - m->context = ts; - /* turn y+ off, x- on; we'll use formula #2 */ if (ts->model == 7846) { + ts->msg_count++; m++; spi_message_init(m); + m->context = ts; x++; packet->read_z1 = READ_Z1(vref); @@ -1190,11 +1109,10 @@ static int __devinit ads7846_probe(struct spi_device *spi) spi_message_add_tail(x, m); } - m->complete = ads7846_rx_val; - m->context = ts; - + ts->msg_count++; m++; spi_message_init(m); + m->context = ts; x++; packet->read_z2 = READ_Z2(vref); @@ -1221,14 +1139,13 @@ static int __devinit ads7846_probe(struct spi_device *spi) x->len = 2; spi_message_add_tail(x, m); } - - m->complete = ads7846_rx_val; - m->context = ts; } /* power down */ + ts->msg_count++; m++; spi_message_init(m); + m->context = ts; if (ts->model == 7845) { x++; @@ -1251,11 +1168,119 @@ static int __devinit ads7846_probe(struct spi_device *spi) CS_CHANGE(*x); spi_message_add_tail(x, m); +} - m->complete = ads7846_rx; - m->context = ts; +static int __devinit ads7846_probe(struct spi_device *spi) +{ + struct ads7846 *ts; + struct ads7846_packet *packet; + struct input_dev *input_dev; + struct ads7846_platform_data *pdata = spi->dev.platform_data; + unsigned long irq_flags; + int err; + + if (!spi->irq) { + dev_dbg(&spi->dev, "no IRQ?\n"); + return -ENODEV; + } + + if (!pdata) { + dev_dbg(&spi->dev, "no platform data?\n"); + return -ENODEV; + } + + /* don't exceed max specified sample rate */ + if (spi->max_speed_hz > (125000 * SAMPLE_BITS)) { + dev_dbg(&spi->dev, "f(sample) %d KHz?\n", + (spi->max_speed_hz/SAMPLE_BITS)/1000); + return -EINVAL; + } + + /* We'd set TX word size 8 bits and RX word size to 13 bits ... except + * that even if the hardware can do that, the SPI controller driver + * may not. So we stick to very-portable 8 bit words, both RX and TX. + */ + spi->bits_per_word = 8; + spi->mode = SPI_MODE_0; + err = spi_setup(spi); + if (err < 0) + return err; - ts->last_msg = m; + ts = kzalloc(sizeof(struct ads7846), GFP_KERNEL); + packet = kzalloc(sizeof(struct ads7846_packet), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!ts || !packet || !input_dev) { + err = -ENOMEM; + goto err_free_mem; + } + + dev_set_drvdata(&spi->dev, ts); + + ts->packet = packet; + ts->spi = spi; + ts->input = input_dev; + ts->vref_mv = pdata->vref_mv; + ts->swap_xy = pdata->swap_xy; + + mutex_init(&ts->lock); + init_waitqueue_head(&ts->wait); + + ts->model = pdata->model ? : 7846; + ts->vref_delay_usecs = pdata->vref_delay_usecs ? : 100; + ts->x_plate_ohms = pdata->x_plate_ohms ? : 400; + ts->pressure_max = pdata->pressure_max ? : ~0; + + if (pdata->filter != NULL) { + if (pdata->filter_init != NULL) { + err = pdata->filter_init(pdata, &ts->filter_data); + if (err < 0) + goto err_free_mem; + } + ts->filter = pdata->filter; + ts->filter_cleanup = pdata->filter_cleanup; + } else if (pdata->debounce_max) { + ts->debounce_max = pdata->debounce_max; + if (ts->debounce_max < 2) + ts->debounce_max = 2; + ts->debounce_tol = pdata->debounce_tol; + ts->debounce_rep = pdata->debounce_rep; + ts->filter = ads7846_debounce_filter; + ts->filter_data = ts; + } else { + ts->filter = ads7846_no_filter; + } + + err = ads7846_setup_pendown(spi, ts); + if (err) + goto err_cleanup_filter; + + if (pdata->penirq_recheck_delay_usecs) + ts->penirq_recheck_delay_usecs = + pdata->penirq_recheck_delay_usecs; + + ts->wait_for_sync = pdata->wait_for_sync ? : null_wait_for_sync; + + snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(&spi->dev)); + snprintf(ts->name, sizeof(ts->name), "ADS%d Touchscreen", ts->model); + + input_dev->name = ts->name; + input_dev->phys = ts->phys; + input_dev->dev.parent = &spi->dev; + + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + input_set_abs_params(input_dev, ABS_X, + pdata->x_min ? : 0, + pdata->x_max ? : MAX_12BIT, + 0, 0); + input_set_abs_params(input_dev, ABS_Y, + pdata->y_min ? : 0, + pdata->y_max ? : MAX_12BIT, + 0, 0); + input_set_abs_params(input_dev, ABS_PRESSURE, + pdata->pressure_min, pdata->pressure_max, 0, 0); + + ads7846_setup_spi_msg(ts, pdata); ts->reg = regulator_get(&spi->dev, "vcc"); if (IS_ERR(ts->reg)) { @@ -1271,16 +1296,17 @@ static int __devinit ads7846_probe(struct spi_device *spi) } irq_flags = pdata->irq_flags ? : IRQF_TRIGGER_FALLING; + irq_flags |= IRQF_ONESHOT; - err = request_irq(spi->irq, ads7846_irq, irq_flags, - spi->dev.driver->name, ts); - + err = request_threaded_irq(spi->irq, ads7846_hard_irq, ads7846_irq, + irq_flags, spi->dev.driver->name, ts); if (err && !pdata->irq_flags) { dev_info(&spi->dev, "trying pin change workaround on irq %d\n", spi->irq); - err = request_irq(spi->irq, ads7846_irq, - IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, - spi->dev.driver->name, ts); + irq_flags |= IRQF_TRIGGER_RISING; + err = request_threaded_irq(spi->irq, + ads7846_hard_irq, ads7846_irq, + irq_flags, spi->dev.driver->name, ts); } if (err) { @@ -1294,7 +1320,8 @@ static int __devinit ads7846_probe(struct spi_device *spi) dev_info(&spi->dev, "touchscreen, irq %d\n", spi->irq); - /* take a first sample, leaving nPENIRQ active and vREF off; avoid + /* + * Take a first sample, leaving nPENIRQ active and vREF off; avoid * the touchscreen, in case it's not connected. */ if (ts->model == 7845) @@ -1340,20 +1367,18 @@ static int __devinit ads7846_probe(struct spi_device *spi) static int __devexit ads7846_remove(struct spi_device *spi) { - struct ads7846 *ts = dev_get_drvdata(&spi->dev); + struct ads7846 *ts = dev_get_drvdata(&spi->dev); device_init_wakeup(&spi->dev, false); - ads784x_hwmon_unregister(spi, ts); - input_unregister_device(ts->input); - - ads7846_suspend(spi, PMSG_SUSPEND); - sysfs_remove_group(&spi->dev.kobj, &ads784x_attr_group); + ads7846_disable(ts); free_irq(ts->spi->irq, ts); - /* suspend left the IRQ disabled */ - enable_irq(ts->spi->irq); + + input_unregister_device(ts->input); + + ads784x_hwmon_unregister(spi, ts); regulator_disable(ts->reg); regulator_put(ts->reg); @@ -1368,6 +1393,7 @@ static int __devexit ads7846_remove(struct spi_device *spi) kfree(ts); dev_dbg(&spi->dev, "unregistered touchscreen\n"); + return 0; } -- cgit v1.2.2 From f74eef95e33a07379aa2b950c7f313cbfd55ebbe Mon Sep 17 00:00:00 2001 From: Hitoshi Mitake Date: Wed, 13 Oct 2010 11:35:40 -0700 Subject: Input: mousedev - correct lockdep annotation When annotating mutex to avoid false lockdep reports we should not be using MOUSEDEV_MIX as lock subclass but rather SINGLE_DEPTH_NESTING. Signed-off-by: Hitoshi Mitake Signed-off-by: Dmitry Torokhov --- drivers/input/mousedev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/mousedev.c b/drivers/input/mousedev.c index d528a2dba064..98973340d042 100644 --- a/drivers/input/mousedev.c +++ b/drivers/input/mousedev.c @@ -866,7 +866,7 @@ static struct mousedev *mousedev_create(struct input_dev *dev, spin_lock_init(&mousedev->client_lock); mutex_init(&mousedev->mutex); lockdep_set_subclass(&mousedev->mutex, - minor == MOUSEDEV_MIX ? MOUSEDEV_MIX : 0); + minor == MOUSEDEV_MIX ? SINGLE_DEPTH_NESTING : 0); init_waitqueue_head(&mousedev->wait); if (minor == MOUSEDEV_MIX) -- cgit v1.2.2 From 790d5c8dac35cf10cf6c52cc9b90ad20603ac2c1 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Fri, 15 Oct 2010 09:47:15 -0700 Subject: Input: hp680_ts_input - use cancel_delayed_work_sync() Make hp680_ts_init/exit() call cancel_delayed_work_sync() instead of calling cancel_delayed_work() followed by flush_scheduled_work(). This is to prepare for the deprecation and removal of flush_scheduled_work(). Signed-off-by: Tejun Heo Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/hp680_ts_input.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/hp680_ts_input.c b/drivers/input/touchscreen/hp680_ts_input.c index a89700e7ace4..498bd62af09a 100644 --- a/drivers/input/touchscreen/hp680_ts_input.c +++ b/drivers/input/touchscreen/hp680_ts_input.c @@ -107,8 +107,7 @@ static int __init hp680_ts_init(void) return 0; fail2: free_irq(HP680_TS_IRQ, NULL); - cancel_delayed_work(&work); - flush_scheduled_work(); + cancel_delayed_work_sync(&work); fail1: input_free_device(hp680_ts_dev); return err; } @@ -116,8 +115,7 @@ static int __init hp680_ts_init(void) static void __exit hp680_ts_exit(void) { free_irq(HP680_TS_IRQ, NULL); - cancel_delayed_work(&work); - flush_scheduled_work(); + cancel_delayed_work_sync(&work); input_unregister_device(hp680_ts_dev); } -- cgit v1.2.2 From e92c27fb5319d8b12f0c291c827e252f0e17afc4 Mon Sep 17 00:00:00 2001 From: Michael Hennerich Date: Fri, 15 Oct 2010 09:48:10 -0700 Subject: Input: ad7877 - implement specified chip select behavior According to the AD7877 datasheet: Each transfer operation is 16-bit. If multiple read/write operations are to be performed, CS must be taken high after the end of each read/write operation before another read/write operation can be performed by taking CS low again. Make sure CS toggles after each transfer in the message. Signed-off-by: Michael Hennerich Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/ad7877.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/ad7877.c b/drivers/input/touchscreen/ad7877.c index 5f0221cffef9..53f4d79ee530 100644 --- a/drivers/input/touchscreen/ad7877.c +++ b/drivers/input/touchscreen/ad7877.c @@ -230,6 +230,7 @@ static int ad7877_read(struct spi_device *spi, u16 reg) AD7877_READADD(reg)); req->xfer[0].tx_buf = &req->command; req->xfer[0].len = 2; + req->xfer[0].cs_change = 1; req->xfer[1].rx_buf = &req->sample; req->xfer[1].len = 2; @@ -295,20 +296,25 @@ static int ad7877_read_adc(struct spi_device *spi, unsigned command) req->xfer[0].tx_buf = &req->reset; req->xfer[0].len = 2; + req->xfer[0].cs_change = 1; req->xfer[1].tx_buf = &req->ref_on; req->xfer[1].len = 2; req->xfer[1].delay_usecs = ts->vref_delay_usecs; + req->xfer[1].cs_change = 1; req->xfer[2].tx_buf = &req->command; req->xfer[2].len = 2; req->xfer[2].delay_usecs = ts->vref_delay_usecs; + req->xfer[2].cs_change = 1; req->xfer[3].rx_buf = &req->sample; req->xfer[3].len = 2; + req->xfer[3].cs_change = 1; req->xfer[4].tx_buf = &ts->cmd_crtl2; /*REF OFF*/ req->xfer[4].len = 2; + req->xfer[4].cs_change = 1; req->xfer[5].tx_buf = &ts->cmd_crtl1; /*DEFAULT*/ req->xfer[5].len = 2; @@ -640,17 +646,21 @@ static void ad7877_setup_ts_def_msg(struct spi_device *spi, struct ad7877 *ts) ts->xfer[0].tx_buf = &ts->cmd_crtl1; ts->xfer[0].len = 2; + ts->xfer[0].cs_change = 1; spi_message_add_tail(&ts->xfer[0], m); ts->xfer[1].tx_buf = &ts->cmd_dummy; /* Send ZERO */ ts->xfer[1].len = 2; + ts->xfer[1].cs_change = 1; spi_message_add_tail(&ts->xfer[1], m); - for (i = 0; i < 11; i++) { + for (i = 0; i < AD7877_NR_SENSE; i++) { ts->xfer[i + 2].rx_buf = &ts->conversion_data[AD7877_SEQ_YPOS + i]; ts->xfer[i + 2].len = 2; + if (i < (AD7877_NR_SENSE - 1)) + ts->xfer[i + 2].cs_change = 1; spi_message_add_tail(&ts->xfer[i + 2], m); } } -- cgit v1.2.2 From 47026b25fc6cd2d7ec85d4a83c472bca20946a94 Mon Sep 17 00:00:00 2001 From: Michael Hennerich Date: Fri, 15 Oct 2010 09:49:07 -0700 Subject: Input: ad7877 - implement EV_KEY:BTN_TOUCH reporting Some input users such as Android or X require BTN_TOUCH events. Implement EV_KEY:BTN_TOUCH and make sure that the release event is not erroneous scheduled without a preceding valid touch. Signed-off-by: Michael Hennerich Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/ad7877.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/ad7877.c b/drivers/input/touchscreen/ad7877.c index 53f4d79ee530..96c0a0d4b69c 100644 --- a/drivers/input/touchscreen/ad7877.c +++ b/drivers/input/touchscreen/ad7877.c @@ -333,7 +333,7 @@ static int ad7877_read_adc(struct spi_device *spi, unsigned command) return status ? : sample; } -static void ad7877_rx(struct ad7877 *ts) +static int ad7877_rx(struct ad7877 *ts) { struct input_dev *input_dev = ts->input; unsigned Rt; @@ -360,11 +360,17 @@ static void ad7877_rx(struct ad7877 *ts) Rt /= z1; Rt = (Rt + 2047) >> 12; + if (!timer_pending(&ts->timer)) + input_report_key(input_dev, BTN_TOUCH, 1); + input_report_abs(input_dev, ABS_X, x); input_report_abs(input_dev, ABS_Y, y); input_report_abs(input_dev, ABS_PRESSURE, Rt); input_sync(input_dev); + return 0; } + + return -EINVAL; } static inline void ad7877_ts_event_release(struct ad7877 *ts) @@ -372,6 +378,7 @@ static inline void ad7877_ts_event_release(struct ad7877 *ts) struct input_dev *input_dev = ts->input; input_report_abs(input_dev, ABS_PRESSURE, 0); + input_report_key(input_dev, BTN_TOUCH, 0); input_sync(input_dev); } @@ -413,11 +420,9 @@ static void ad7877_callback(void *_ts) struct ad7877 *ts = _ts; spin_lock_irq(&ts->lock); - - ad7877_rx(ts); + if (!ad7877_rx(ts)) + mod_timer(&ts->timer, jiffies + TS_PEN_UP_TIMEOUT); ts->pending = 0; - mod_timer(&ts->timer, jiffies + TS_PEN_UP_TIMEOUT); - spin_unlock_irq(&ts->lock); } @@ -728,6 +733,8 @@ static int __devinit ad7877_probe(struct spi_device *spi) input_dev->phys = ts->phys; input_dev->dev.parent = &spi->dev; + __set_bit(EV_KEY, input_dev->evbit); + __set_bit(BTN_TOUCH, input_dev->keybit); __set_bit(EV_ABS, input_dev->evbit); __set_bit(ABS_X, input_dev->absbit); __set_bit(ABS_Y, input_dev->absbit); -- cgit v1.2.2 From 1d02ad436235080b8a95a2c86a66cb7b8f2e9df9 Mon Sep 17 00:00:00 2001 From: Michael Hennerich Date: Fri, 15 Oct 2010 09:49:08 -0700 Subject: Input: ad7877 - filter events where pressure is beyond the maximum Suppress events where pressure > pressure_max. These events come typically along with inaccurate X and Y samples. Signed-off-by: Michael Hennerich Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/ad7877.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/ad7877.c b/drivers/input/touchscreen/ad7877.c index 96c0a0d4b69c..b7de78e82737 100644 --- a/drivers/input/touchscreen/ad7877.c +++ b/drivers/input/touchscreen/ad7877.c @@ -360,6 +360,13 @@ static int ad7877_rx(struct ad7877 *ts) Rt /= z1; Rt = (Rt + 2047) >> 12; + /* + * Sample found inconsistent, pressure is beyond + * the maximum. Don't report it to user space. + */ + if (Rt > ts->pressure_max) + return -EINVAL; + if (!timer_pending(&ts->timer)) input_report_key(input_dev, BTN_TOUCH, 1); -- cgit v1.2.2 From 62ecae09a01df507ef52e1bc90fc233a1978c60a Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sun, 10 Oct 2010 14:24:16 -0700 Subject: Input: wacom - properly enable runtime PM We need to always call usb_autopm_put_interface() in wacom_open(), not only when initialization fails, otherwise the device will be marked as PM-busy and will never be put in suspended state. Based on patch by Oliver Neukum. Acked-by: Oliver Neukum Signed-off-by: Dmitry Torokhov --- drivers/input/tablet/wacom_sys.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/tablet/wacom_sys.c b/drivers/input/tablet/wacom_sys.c index 02de65357233..fc381498b798 100644 --- a/drivers/input/tablet/wacom_sys.c +++ b/drivers/input/tablet/wacom_sys.c @@ -120,14 +120,16 @@ static int wacom_open(struct input_dev *dev) out: mutex_unlock(&wacom->lock); - if (retval) - usb_autopm_put_interface(wacom->intf); + usb_autopm_put_interface(wacom->intf); return retval; } static void wacom_close(struct input_dev *dev) { struct wacom *wacom = input_get_drvdata(dev); + int autopm_error; + + autopm_error = usb_autopm_get_interface(wacom->intf); mutex_lock(&wacom->lock); usb_kill_urb(wacom->irq); @@ -135,7 +137,8 @@ static void wacom_close(struct input_dev *dev) wacom->intf->needs_remote_wakeup = 0; mutex_unlock(&wacom->lock); - usb_autopm_put_interface(wacom->intf); + if (!autopm_error) + usb_autopm_put_interface(wacom->intf); } static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hid_desc, -- cgit v1.2.2 From fc58d12be416eb51932eec594667ca3181903b9e Mon Sep 17 00:00:00 2001 From: Dmitry Eremin-Solenikov Date: Mon, 18 Oct 2010 09:18:13 -0700 Subject: Input: serio - add support for PS2Mult multiplexer protocol PS2Mult is a simple serial protocol used for multiplexing several PS/2 streams into one serial data stream. It's used e.g. on TQM85xx series of boards. Signed-off-by: Dmitry Eremin-Solenikov Signed-off-by: Dmitry Torokhov --- drivers/input/serio/Kconfig | 9 ++ drivers/input/serio/Makefile | 1 + drivers/input/serio/ps2mult.c | 318 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 328 insertions(+) create mode 100644 drivers/input/serio/ps2mult.c (limited to 'drivers/input') diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig index 3bfe8fafc6ad..6256233d2bfb 100644 --- a/drivers/input/serio/Kconfig +++ b/drivers/input/serio/Kconfig @@ -226,4 +226,13 @@ config SERIO_AMS_DELTA To compile this driver as a module, choose M here; the module will be called ams_delta_serio. +config SERIO_PS2MULT + tristate "TQC PS/2 multiplexer" + help + Say Y here if you have the PS/2 line multiplexer like the one + present on TQC boads. + + To compile this driver as a module, choose M here: the + module will be called ps2mult. + endif diff --git a/drivers/input/serio/Makefile b/drivers/input/serio/Makefile index 84c80bf7185e..dbbe37616c92 100644 --- a/drivers/input/serio/Makefile +++ b/drivers/input/serio/Makefile @@ -18,6 +18,7 @@ obj-$(CONFIG_SERIO_GSCPS2) += gscps2.o obj-$(CONFIG_HP_SDC) += hp_sdc.o obj-$(CONFIG_HIL_MLC) += hp_sdc_mlc.o hil_mlc.o obj-$(CONFIG_SERIO_PCIPS2) += pcips2.o +obj-$(CONFIG_SERIO_PS2MULT) += ps2mult.o obj-$(CONFIG_SERIO_MACEPS2) += maceps2.o obj-$(CONFIG_SERIO_LIBPS2) += libps2.o obj-$(CONFIG_SERIO_RAW) += serio_raw.o diff --git a/drivers/input/serio/ps2mult.c b/drivers/input/serio/ps2mult.c new file mode 100644 index 000000000000..6bce22e4e495 --- /dev/null +++ b/drivers/input/serio/ps2mult.c @@ -0,0 +1,318 @@ +/* + * TQC PS/2 Multiplexer driver + * + * Copyright (C) 2010 Dmitry Eremin-Solenikov + * + * 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 + +MODULE_AUTHOR("Dmitry Eremin-Solenikov "); +MODULE_DESCRIPTION("TQC PS/2 Multiplexer driver"); +MODULE_LICENSE("GPL"); + +#define PS2MULT_KB_SELECTOR 0xA0 +#define PS2MULT_MS_SELECTOR 0xA1 +#define PS2MULT_ESCAPE 0x7D +#define PS2MULT_BSYNC 0x7E +#define PS2MULT_SESSION_START 0x55 +#define PS2MULT_SESSION_END 0x56 + +struct ps2mult_port { + struct serio *serio; + unsigned char sel; + bool registered; +}; + +#define PS2MULT_NUM_PORTS 2 +#define PS2MULT_KBD_PORT 0 +#define PS2MULT_MOUSE_PORT 1 + +struct ps2mult { + struct serio *mx_serio; + struct ps2mult_port ports[PS2MULT_NUM_PORTS]; + + spinlock_t lock; + struct ps2mult_port *in_port; + struct ps2mult_port *out_port; + bool escape; +}; + +/* First MUST come PS2MULT_NUM_PORTS selectors */ +static const unsigned char ps2mult_controls[] = { + PS2MULT_KB_SELECTOR, PS2MULT_MS_SELECTOR, + PS2MULT_ESCAPE, PS2MULT_BSYNC, + PS2MULT_SESSION_START, PS2MULT_SESSION_END, +}; + +static const struct serio_device_id ps2mult_serio_ids[] = { + { + .type = SERIO_RS232, + .proto = SERIO_PS2MULT, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { 0 } +}; + +MODULE_DEVICE_TABLE(serio, ps2mult_serio_ids); + +static void ps2mult_select_port(struct ps2mult *psm, struct ps2mult_port *port) +{ + struct serio *mx_serio = psm->mx_serio; + + serio_write(mx_serio, port->sel); + psm->out_port = port; + dev_dbg(&mx_serio->dev, "switched to sel %02x\n", port->sel); +} + +static int ps2mult_serio_write(struct serio *serio, unsigned char data) +{ + struct serio *mx_port = serio->parent; + struct ps2mult *psm = serio_get_drvdata(mx_port); + struct ps2mult_port *port = serio->port_data; + bool need_escape; + unsigned long flags; + + spin_lock_irqsave(&psm->lock, flags); + + if (psm->out_port != port) + ps2mult_select_port(psm, port); + + need_escape = memchr(ps2mult_controls, data, sizeof(ps2mult_controls)); + + dev_dbg(&serio->dev, + "write: %s%02x\n", need_escape ? "ESC " : "", data); + + if (need_escape) + serio_write(mx_port, PS2MULT_ESCAPE); + + serio_write(mx_port, data); + + spin_unlock_irqrestore(&psm->lock, flags); + + return 0; +} + +static int ps2mult_serio_start(struct serio *serio) +{ + struct ps2mult *psm = serio_get_drvdata(serio->parent); + struct ps2mult_port *port = serio->port_data; + unsigned long flags; + + spin_lock_irqsave(&psm->lock, flags); + port->registered = true; + spin_unlock_irqrestore(&psm->lock, flags); + + return 0; +} + +static void ps2mult_serio_stop(struct serio *serio) +{ + struct ps2mult *psm = serio_get_drvdata(serio->parent); + struct ps2mult_port *port = serio->port_data; + unsigned long flags; + + spin_lock_irqsave(&psm->lock, flags); + port->registered = false; + spin_unlock_irqrestore(&psm->lock, flags); +} + +static int ps2mult_create_port(struct ps2mult *psm, int i) +{ + struct serio *mx_serio = psm->mx_serio; + struct serio *serio; + + serio = kzalloc(sizeof(struct serio), GFP_KERNEL); + if (!serio) + return -ENOMEM; + + strlcpy(serio->name, "TQC PS/2 Multiplexer", sizeof(serio->name)); + snprintf(serio->phys, sizeof(serio->phys), + "%s/port%d", mx_serio->phys, i); + serio->id.type = SERIO_8042; + serio->write = ps2mult_serio_write; + serio->start = ps2mult_serio_start; + serio->stop = ps2mult_serio_stop; + serio->parent = psm->mx_serio; + serio->port_data = &psm->ports[i]; + + psm->ports[i].serio = serio; + + return 0; +} + +static void ps2mult_reset(struct ps2mult *psm) +{ + unsigned long flags; + + spin_lock_irqsave(&psm->lock, flags); + + serio_write(psm->mx_serio, PS2MULT_SESSION_END); + serio_write(psm->mx_serio, PS2MULT_SESSION_START); + + ps2mult_select_port(psm, &psm->ports[PS2MULT_KBD_PORT]); + + spin_unlock_irqrestore(&psm->lock, flags); +} + +static int ps2mult_connect(struct serio *serio, struct serio_driver *drv) +{ + struct ps2mult *psm; + int i; + int error; + + if (!serio->write) + return -EINVAL; + + psm = kzalloc(sizeof(*psm), GFP_KERNEL); + if (!psm) + return -ENOMEM; + + spin_lock_init(&psm->lock); + psm->mx_serio = serio; + + for (i = 0; i < PS2MULT_NUM_PORTS; i++) { + psm->ports[i].sel = ps2mult_controls[i]; + error = ps2mult_create_port(psm, i); + if (error) + goto err_out; + } + + psm->in_port = psm->out_port = &psm->ports[PS2MULT_KBD_PORT]; + + serio_set_drvdata(serio, psm); + error = serio_open(serio, drv); + if (error) + goto err_out; + + ps2mult_reset(psm); + + for (i = 0; i < PS2MULT_NUM_PORTS; i++) { + struct serio *s = psm->ports[i].serio; + + dev_info(&serio->dev, "%s port at %s\n", s->name, serio->phys); + serio_register_port(s); + } + + return 0; + +err_out: + while (--i >= 0) + kfree(psm->ports[i].serio); + kfree(serio); + return error; +} + +static void ps2mult_disconnect(struct serio *serio) +{ + struct ps2mult *psm = serio_get_drvdata(serio); + + /* Note that serio core already take care of children ports */ + serio_write(serio, PS2MULT_SESSION_END); + serio_close(serio); + kfree(psm); + + serio_set_drvdata(serio, NULL); +} + +static int ps2mult_reconnect(struct serio *serio) +{ + struct ps2mult *psm = serio_get_drvdata(serio); + + ps2mult_reset(psm); + + return 0; +} + +static irqreturn_t ps2mult_interrupt(struct serio *serio, + unsigned char data, unsigned int dfl) +{ + struct ps2mult *psm = serio_get_drvdata(serio); + struct ps2mult_port *in_port; + unsigned long flags; + + dev_dbg(&serio->dev, "Received %02x flags %02x\n", data, dfl); + + spin_lock_irqsave(&psm->lock, flags); + + if (psm->escape) { + psm->escape = false; + in_port = psm->in_port; + if (in_port->registered) + serio_interrupt(in_port->serio, data, dfl); + goto out; + } + + switch (data) { + case PS2MULT_ESCAPE: + dev_dbg(&serio->dev, "ESCAPE\n"); + psm->escape = true; + break; + + case PS2MULT_BSYNC: + dev_dbg(&serio->dev, "BSYNC\n"); + psm->in_port = psm->out_port; + break; + + case PS2MULT_SESSION_START: + dev_dbg(&serio->dev, "SS\n"); + break; + + case PS2MULT_SESSION_END: + dev_dbg(&serio->dev, "SE\n"); + break; + + case PS2MULT_KB_SELECTOR: + dev_dbg(&serio->dev, "KB\n"); + psm->in_port = &psm->ports[PS2MULT_KBD_PORT]; + break; + + case PS2MULT_MS_SELECTOR: + dev_dbg(&serio->dev, "MS\n"); + psm->in_port = &psm->ports[PS2MULT_MOUSE_PORT]; + break; + + default: + in_port = psm->in_port; + if (in_port->registered) + serio_interrupt(in_port->serio, data, dfl); + break; + } + + out: + spin_unlock_irqrestore(&psm->lock, flags); + return IRQ_HANDLED; +} + +static struct serio_driver ps2mult_drv = { + .driver = { + .name = "ps2mult", + }, + .description = "TQC PS/2 Multiplexer driver", + .id_table = ps2mult_serio_ids, + .interrupt = ps2mult_interrupt, + .connect = ps2mult_connect, + .disconnect = ps2mult_disconnect, + .reconnect = ps2mult_reconnect, +}; + +static int __init ps2mult_init(void) +{ + return serio_register_driver(&ps2mult_drv); +} + +static void __exit ps2mult_exit(void) +{ + serio_unregister_driver(&ps2mult_drv); +} + +module_init(ps2mult_init); +module_exit(ps2mult_exit); -- cgit v1.2.2 From 2fd18abad179b11cbd881f2bd271b193ababfb65 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 18 Oct 2010 09:22:38 -0700 Subject: Input: ad7877 - use attribute group to control visibility of attributes Instead of manually creating one set of attributes or another set up is_visible method in attribute group structure to control whether aux3 or gpio3 attribute is presented to userspace. Acked-by: Michael Hennerich Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/ad7877.c | 49 ++++++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 20 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/ad7877.c b/drivers/input/touchscreen/ad7877.c index b7de78e82737..326d7336a372 100644 --- a/drivers/input/touchscreen/ad7877.c +++ b/drivers/input/touchscreen/ad7877.c @@ -206,8 +206,8 @@ struct ad7877 { u16 conversion_data[AD7877_NR_SENSE] ____cacheline_aligned; }; -static int gpio3; -module_param(gpio3, int, 0); +static bool gpio3; +module_param(gpio3, bool, 0); MODULE_PARM_DESC(gpio3, "If gpio3 is set to 1 AUX3 acts as GPIO3"); /* @@ -471,7 +471,7 @@ static void ad7877_enable(struct ad7877 *ts) #define SHOW(name) static ssize_t \ name ## _show(struct device *dev, struct device_attribute *attr, char *buf) \ { \ - struct ad7877 *ts = dev_get_drvdata(dev); \ + struct ad7877 *ts = dev_get_drvdata(dev); \ ssize_t v = ad7877_read_adc(ts->spi, \ AD7877_READ_CHAN(name)); \ if (v < 0) \ @@ -491,7 +491,7 @@ SHOW(temp2) static ssize_t ad7877_disable_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct ad7877 *ts = dev_get_drvdata(dev); + struct ad7877 *ts = dev_get_drvdata(dev); return sprintf(buf, "%u\n", ts->disabled); } @@ -521,7 +521,7 @@ static DEVICE_ATTR(disable, 0664, ad7877_disable_show, ad7877_disable_store); static ssize_t ad7877_dac_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct ad7877 *ts = dev_get_drvdata(dev); + struct ad7877 *ts = dev_get_drvdata(dev); return sprintf(buf, "%u\n", ts->dac); } @@ -551,7 +551,7 @@ static DEVICE_ATTR(dac, 0664, ad7877_dac_show, ad7877_dac_store); static ssize_t ad7877_gpio3_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct ad7877 *ts = dev_get_drvdata(dev); + struct ad7877 *ts = dev_get_drvdata(dev); return sprintf(buf, "%u\n", ts->gpio3); } @@ -582,7 +582,7 @@ static DEVICE_ATTR(gpio3, 0664, ad7877_gpio3_show, ad7877_gpio3_store); static ssize_t ad7877_gpio4_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct ad7877 *ts = dev_get_drvdata(dev); + struct ad7877 *ts = dev_get_drvdata(dev); return sprintf(buf, "%u\n", ts->gpio4); } @@ -615,16 +615,35 @@ static struct attribute *ad7877_attributes[] = { &dev_attr_temp2.attr, &dev_attr_aux1.attr, &dev_attr_aux2.attr, + &dev_attr_aux3.attr, &dev_attr_bat1.attr, &dev_attr_bat2.attr, &dev_attr_disable.attr, &dev_attr_dac.attr, + &dev_attr_gpio3.attr, &dev_attr_gpio4.attr, NULL }; +static mode_t ad7877_attr_is_visible(struct kobject *kobj, + struct attribute *attr, int n) +{ + mode_t mode = attr->mode; + + if (attr == &dev_attr_aux3.attr) { + if (gpio3) + mode = 0; + } else if (attr == &dev_attr_gpio3.attr) { + if (!gpio3) + mode = 0; + } + + return mode; +} + static const struct attribute_group ad7877_attr_group = { - .attrs = ad7877_attributes, + .is_visible = ad7877_attr_is_visible, + .attrs = ad7877_attributes, }; static void ad7877_setup_ts_def_msg(struct spi_device *spi, struct ad7877 *ts) @@ -787,20 +806,12 @@ static int __devinit ad7877_probe(struct spi_device *spi) if (err) goto err_free_irq; - err = device_create_file(&spi->dev, - gpio3 ? &dev_attr_gpio3 : &dev_attr_aux3); - if (err) - goto err_remove_attr_group; - err = input_register_device(input_dev); if (err) - goto err_remove_attr; + goto err_remove_attr_group; return 0; -err_remove_attr: - device_remove_file(&spi->dev, - gpio3 ? &dev_attr_gpio3 : &dev_attr_aux3); err_remove_attr_group: sysfs_remove_group(&spi->dev.kobj, &ad7877_attr_group); err_free_irq: @@ -814,11 +825,9 @@ err_free_mem: static int __devexit ad7877_remove(struct spi_device *spi) { - struct ad7877 *ts = dev_get_drvdata(&spi->dev); + struct ad7877 *ts = dev_get_drvdata(&spi->dev); sysfs_remove_group(&spi->dev.kobj, &ad7877_attr_group); - device_remove_file(&spi->dev, - gpio3 ? &dev_attr_gpio3 : &dev_attr_aux3); ad7877_disable(ts); free_irq(ts->spi->irq, ts); -- cgit v1.2.2 From b534422b2d1189740c6144c3c7a296be89f581c7 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 18 Oct 2010 09:24:22 -0700 Subject: Input: ad7877 - switch to using threaded IRQ Instead of using asynchronous SPI API and then spinning waiting for SPI transfer to complete when disabling the device, let's use threaded IRQ model and spi_sync(). Acked-by: Michael Hennerich Tested-by: Michael Hennerich Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/ad7877.c | 65 +++++++++++++++----------------------- 1 file changed, 25 insertions(+), 40 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/ad7877.c b/drivers/input/touchscreen/ad7877.c index 326d7336a372..a1952fcc083e 100644 --- a/drivers/input/touchscreen/ad7877.c +++ b/drivers/input/touchscreen/ad7877.c @@ -191,13 +191,12 @@ struct ad7877 { struct spi_message msg; struct mutex mutex; - unsigned disabled:1; /* P: mutex */ - unsigned gpio3:1; /* P: mutex */ - unsigned gpio4:1; /* P: mutex */ + bool disabled; /* P: mutex */ + bool gpio3; /* P: mutex */ + bool gpio4; /* P: mutex */ spinlock_t lock; struct timer_list timer; /* P: lock */ - unsigned pending:1; /* P: lock */ /* * DMA (thus cache coherency maintenance) requires the @@ -333,7 +332,7 @@ static int ad7877_read_adc(struct spi_device *spi, unsigned command) return status ? : sample; } -static int ad7877_rx(struct ad7877 *ts) +static int ad7877_process_data(struct ad7877 *ts) { struct input_dev *input_dev = ts->input; unsigned Rt; @@ -374,6 +373,7 @@ static int ad7877_rx(struct ad7877 *ts) input_report_abs(input_dev, ABS_Y, y); input_report_abs(input_dev, ABS_PRESSURE, Rt); input_sync(input_dev); + return 0; } @@ -392,64 +392,49 @@ static inline void ad7877_ts_event_release(struct ad7877 *ts) static void ad7877_timer(unsigned long handle) { struct ad7877 *ts = (void *)handle; + unsigned long flags; + spin_lock_irqsave(&ts->lock, flags); ad7877_ts_event_release(ts); + spin_unlock_irqrestore(&ts->lock, flags); } static irqreturn_t ad7877_irq(int irq, void *handle) { struct ad7877 *ts = handle; unsigned long flags; - int status; + int error; - /* - * The repeated conversion sequencer controlled by TMR kicked off - * too fast. We ignore the last and process the sample sequence - * currently in the queue. It can't be older than 9.4ms, and we - * need to avoid that ts->msg doesn't get issued twice while in work. - */ + error = spi_sync(ts->spi, &ts->msg); + if (error) { + dev_err(&ts->spi->dev, "spi_sync --> %d\n", error); + goto out; + } spin_lock_irqsave(&ts->lock, flags); - if (!ts->pending) { - ts->pending = 1; - - status = spi_async(ts->spi, &ts->msg); - if (status) - dev_err(&ts->spi->dev, "spi_sync --> %d\n", status); - } + error = ad7877_process_data(ts); + if (!error) + mod_timer(&ts->timer, jiffies + TS_PEN_UP_TIMEOUT); spin_unlock_irqrestore(&ts->lock, flags); +out: return IRQ_HANDLED; } -static void ad7877_callback(void *_ts) -{ - struct ad7877 *ts = _ts; - - spin_lock_irq(&ts->lock); - if (!ad7877_rx(ts)) - mod_timer(&ts->timer, jiffies + TS_PEN_UP_TIMEOUT); - ts->pending = 0; - spin_unlock_irq(&ts->lock); -} - static void ad7877_disable(struct ad7877 *ts) { mutex_lock(&ts->mutex); if (!ts->disabled) { - ts->disabled = 1; + ts->disabled = true; disable_irq(ts->spi->irq); - /* Wait for spi_async callback */ - while (ts->pending) - msleep(1); - if (del_timer_sync(&ts->timer)) ad7877_ts_event_release(ts); } - /* we know the chip's in lowpower mode since we always + /* + * We know the chip's in lowpower mode since we always * leave it that way after every request */ @@ -461,7 +446,7 @@ static void ad7877_enable(struct ad7877 *ts) mutex_lock(&ts->mutex); if (ts->disabled) { - ts->disabled = 0; + ts->disabled = false; enable_irq(ts->spi->irq); } @@ -672,7 +657,6 @@ static void ad7877_setup_ts_def_msg(struct spi_device *spi, struct ad7877 *ts) spi_message_init(m); - m->complete = ad7877_callback; m->context = ts; ts->xfer[0].tx_buf = &ts->cmd_crtl1; @@ -795,8 +779,9 @@ static int __devinit ad7877_probe(struct spi_device *spi) /* Request AD7877 /DAV GPIO interrupt */ - err = request_irq(spi->irq, ad7877_irq, IRQF_TRIGGER_FALLING, - spi->dev.driver->name, ts); + err = request_threaded_irq(spi->irq, NULL, ad7877_irq, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + spi->dev.driver->name, ts); if (err) { dev_dbg(&spi->dev, "irq %d busy?\n", spi->irq); goto err_free_mem; -- cgit v1.2.2 From d537155a09cd69f309fa31fc8bcc7a4b1d5a9f6c Mon Sep 17 00:00:00 2001 From: Michael Hennerich Date: Mon, 18 Oct 2010 17:46:03 -0700 Subject: Input: adp5588-keys - use more obvious i2c_device_id name string KBUILD_MODNAME normalizes "-" to "_". This is non-obvious and results in the id name for ADP5588 being "adp5588_keys" while the other supported id is "adp5587-keys". So avoid this define and use an explicit string as the id name. Signed-off-by: Michael Hennerich Signed-off-by: Mike Frysinger Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/adp5588-keys.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/keyboard/adp5588-keys.c b/drivers/input/keyboard/adp5588-keys.c index d6918cb966c0..b92d1cd5cba1 100644 --- a/drivers/input/keyboard/adp5588-keys.c +++ b/drivers/input/keyboard/adp5588-keys.c @@ -660,7 +660,7 @@ static const struct dev_pm_ops adp5588_dev_pm_ops = { #endif static const struct i2c_device_id adp5588_id[] = { - { KBUILD_MODNAME, 0 }, + { "adp5588-keys", 0 }, { "adp5587-keys", 0 }, { } }; -- cgit v1.2.2 From c5fb514d2f0496ce58caed63bceb05fac40324b3 Mon Sep 17 00:00:00 2001 From: Nicolas Kaiser Date: Sun, 24 Oct 2010 21:48:11 -0700 Subject: Input: ab8500-ponkey - fix IRQ freeing in error path Looks like an obvious typo to me. Signed-off-by: Nicolas Kaiser Signed-off-by: Dmitry Torokhov --- drivers/input/misc/ab8500-ponkey.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/misc/ab8500-ponkey.c b/drivers/input/misc/ab8500-ponkey.c index 98f8ede504b8..3d3288a78fdc 100644 --- a/drivers/input/misc/ab8500-ponkey.c +++ b/drivers/input/misc/ab8500-ponkey.c @@ -107,7 +107,7 @@ static int __devinit ab8500_ponkey_probe(struct platform_device *pdev) return 0; err_free_dbr_irq: - free_irq(ponkey->irq_dbf, ponkey); + free_irq(ponkey->irq_dbr, ponkey); err_free_dbf_irq: free_irq(ponkey->irq_dbf, ponkey); err_free_mem: -- cgit v1.2.2 From 6521d0bf984ab1cc25795d312e21c438aea8b5d5 Mon Sep 17 00:00:00 2001 From: Ping Cheng Date: Sun, 24 Oct 2010 21:53:40 -0700 Subject: Input: wacom - specify Cinitq supported tools Cintiq, being a display tablet, doesn't have mouse and associated BTN_s. Make sure we do not specify them when registering Cintiq's input device so that userland can retrieve the exact tool set the device supports. Signed-off-by: Ping Cheng Signed-off-by: Dmitry Torokhov --- drivers/input/tablet/wacom_wac.c | 42 ++++++++++++++++++++++++++++------------ 1 file changed, 30 insertions(+), 12 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/input/tablet/wacom_wac.c index 121fdf7a73d2..b3252ef1e279 100644 --- a/drivers/input/tablet/wacom_wac.c +++ b/drivers/input/tablet/wacom_wac.c @@ -1043,26 +1043,17 @@ void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len) input_sync(wacom_wac->input); } -static void wacom_setup_intuos(struct wacom_wac *wacom_wac) +static void wacom_setup_cintiq(struct wacom_wac *wacom_wac) { struct input_dev *input_dev = wacom_wac->input; input_set_capability(input_dev, EV_MSC, MSC_SERIAL); - input_set_capability(input_dev, EV_REL, REL_WHEEL); - - __set_bit(BTN_LEFT, input_dev->keybit); - __set_bit(BTN_RIGHT, input_dev->keybit); - __set_bit(BTN_MIDDLE, input_dev->keybit); - __set_bit(BTN_SIDE, input_dev->keybit); - __set_bit(BTN_EXTRA, input_dev->keybit); __set_bit(BTN_TOOL_RUBBER, input_dev->keybit); __set_bit(BTN_TOOL_PEN, input_dev->keybit); - __set_bit(BTN_TOOL_MOUSE, input_dev->keybit); __set_bit(BTN_TOOL_BRUSH, input_dev->keybit); __set_bit(BTN_TOOL_PENCIL, input_dev->keybit); __set_bit(BTN_TOOL_AIRBRUSH, input_dev->keybit); - __set_bit(BTN_TOOL_LENS, input_dev->keybit); __set_bit(BTN_STYLUS, input_dev->keybit); __set_bit(BTN_STYLUS2, input_dev->keybit); @@ -1071,11 +1062,28 @@ static void wacom_setup_intuos(struct wacom_wac *wacom_wac) input_set_abs_params(input_dev, ABS_WHEEL, 0, 1023, 0, 0); input_set_abs_params(input_dev, ABS_TILT_X, 0, 127, 0, 0); input_set_abs_params(input_dev, ABS_TILT_Y, 0, 127, 0, 0); +} + +static void wacom_setup_intuos(struct wacom_wac *wacom_wac) +{ + struct input_dev *input_dev = wacom_wac->input; + + input_set_capability(input_dev, EV_REL, REL_WHEEL); + + wacom_setup_cintiq(wacom_wac); + + __set_bit(BTN_LEFT, input_dev->keybit); + __set_bit(BTN_RIGHT, input_dev->keybit); + __set_bit(BTN_MIDDLE, input_dev->keybit); + __set_bit(BTN_SIDE, input_dev->keybit); + __set_bit(BTN_EXTRA, input_dev->keybit); + __set_bit(BTN_TOOL_MOUSE, input_dev->keybit); + __set_bit(BTN_TOOL_LENS, input_dev->keybit); + input_set_abs_params(input_dev, ABS_RZ, -900, 899, 0, 0); input_set_abs_params(input_dev, ABS_THROTTLE, -1023, 1023, 0, 0); } - void wacom_setup_device_quirks(struct wacom_features *features) { @@ -1168,9 +1176,19 @@ void wacom_setup_input_capabilities(struct input_dev *input_dev, __set_bit(BTN_9, input_dev->keybit); /* fall through */ + case CINTIQ: + for (i = 0; i < 8; i++) + __set_bit(BTN_0 + i, input_dev->keybit); + __set_bit(BTN_TOOL_FINGER, input_dev->keybit); + + input_set_abs_params(input_dev, ABS_RX, 0, 4096, 0, 0); + input_set_abs_params(input_dev, ABS_RY, 0, 4096, 0, 0); + input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0); + wacom_setup_cintiq(wacom_wac); + break; + case INTUOS3: case INTUOS3L: - case CINTIQ: __set_bit(BTN_4, input_dev->keybit); __set_bit(BTN_5, input_dev->keybit); __set_bit(BTN_6, input_dev->keybit); -- cgit v1.2.2