Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input: (29 commits) Input: i8042 - add Dell Vostro 1510 to nomux list Input: gtco - use USB endpoint API Input: add support for Maple controller as a joystick Input: atkbd - broaden the Dell DMI signatures Input: HIL drivers - add MODULE_ALIAS() Input: map_to_7segment.h - convert to __inline__ for userspace Input: add support for enhanced rotary controller on pxa930 and pxa935 Input: add support for trackball on pxa930 and pxa935 Input: add da9034 touchscreen support Input: ads7846 - strict_strtoul takes unsigned long Input: make some variables and functions static Input: add tsc2007 based touchscreen driver Input: psmouse - add module parameters to control OLPC touchpad delays Input: i8042 - add Gigabyte M912 netbook to noloop exception table Input: atkbd - Samsung NC10 key repeat fix Input: atkbd - add keyboard quirk for HP Pavilion ZV6100 laptop Input: libps2 - handle 0xfc responses from devices Input: add support for Wacom W8001 penabled serial touchscreen Input: synaptics - report multi-taps only if supported by the device Input: add joystick driver for Walkera WK-0701 RC transmitter ...
This commit is contained in:
@@ -58,6 +58,14 @@ config TOUCHSCREEN_CORGI
|
||||
NOTE: this driver is deprecated, try enable SPI and generic
|
||||
ADS7846-based touchscreen driver.
|
||||
|
||||
config TOUCHSCREEN_DA9034
|
||||
tristate "Touchscreen support for Dialog Semiconductor DA9034"
|
||||
depends on PMIC_DA903X
|
||||
default y
|
||||
help
|
||||
Say Y here to enable the support for the touchscreen found
|
||||
on Dialog Semiconductor DA9034 PMIC.
|
||||
|
||||
config TOUCHSCREEN_FUJITSU
|
||||
tristate "Fujitsu serial touchscreen"
|
||||
select SERIO
|
||||
@@ -95,6 +103,19 @@ config TOUCHSCREEN_ELO
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called elo.
|
||||
|
||||
config TOUCHSCREEN_WACOM_W8001
|
||||
tristate "Wacom W8001 penabled serial touchscreen"
|
||||
select SERIO
|
||||
help
|
||||
Say Y here if you have an Wacom W8001 penabled serial touchscreen
|
||||
connected to your system.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called wacom_w8001.
|
||||
|
||||
|
||||
config TOUCHSCREEN_MTOUCH
|
||||
tristate "MicroTouch serial touchscreens"
|
||||
select SERIO
|
||||
@@ -376,4 +397,15 @@ config TOUCHSCREEN_TOUCHIT213
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called touchit213.
|
||||
|
||||
config TOUCHSCREEN_TSC2007
|
||||
tristate "TSC2007 based touchscreens"
|
||||
depends on I2C
|
||||
help
|
||||
Say Y here if you have a TSC2007 based touchscreen.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called tsc2007.
|
||||
|
||||
endif
|
||||
|
||||
@@ -25,8 +25,11 @@ obj-$(CONFIG_TOUCHSCREEN_PENMOUNT) += penmount.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213) += touchit213.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN) += touchwin.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_TSC2007) += tsc2007.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_UCB1400) += ucb1400_ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_WACOM_W8001) += wacom_w8001.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_WM97XX) += wm97xx-ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_DA9034) += da9034-ts.o
|
||||
wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9705) += wm9705.o
|
||||
wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9712) += wm9712.o
|
||||
wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9713) += wm9713.o
|
||||
|
||||
@@ -472,7 +472,7 @@ static ssize_t ads7846_disable_store(struct device *dev,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct ads7846 *ts = dev_get_drvdata(dev);
|
||||
long i;
|
||||
unsigned long i;
|
||||
|
||||
if (strict_strtoul(buf, 10, &i))
|
||||
return -EINVAL;
|
||||
@@ -559,7 +559,7 @@ static void ads7846_rx(void *ads)
|
||||
if (packet->tc.ignore || Rt > ts->pressure_max) {
|
||||
#ifdef VERBOSE
|
||||
pr_debug("%s: ignored %d pressure %d\n",
|
||||
ts->spi->dev.bus_id, packet->tc.ignore, Rt);
|
||||
dev_name(&ts->spi->dev), packet->tc.ignore, Rt);
|
||||
#endif
|
||||
hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_PERIOD),
|
||||
HRTIMER_MODE_REL);
|
||||
@@ -947,7 +947,7 @@ static int __devinit ads7846_probe(struct spi_device *spi)
|
||||
ts->penirq_recheck_delay_usecs =
|
||||
pdata->penirq_recheck_delay_usecs;
|
||||
|
||||
snprintf(ts->phys, sizeof(ts->phys), "%s/input0", spi->dev.bus_id);
|
||||
snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(&spi->dev));
|
||||
|
||||
input_dev->name = "ADS784x Touchscreen";
|
||||
input_dev->phys = ts->phys;
|
||||
|
||||
@@ -0,0 +1,389 @@
|
||||
/*
|
||||
* Touchscreen driver for Dialog Semiconductor DA9034
|
||||
*
|
||||
* Copyright (C) 2006-2008 Marvell International Ltd.
|
||||
* Fengwei Yin <fengwei.yin@marvell.com>
|
||||
* Eric Miao <eric.miao@marvell.com>
|
||||
*
|
||||
* 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 <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/mfd/da903x.h>
|
||||
|
||||
#define DA9034_MANUAL_CTRL 0x50
|
||||
#define DA9034_LDO_ADC_EN (1 << 4)
|
||||
|
||||
#define DA9034_AUTO_CTRL1 0x51
|
||||
|
||||
#define DA9034_AUTO_CTRL2 0x52
|
||||
#define DA9034_AUTO_TSI_EN (1 << 3)
|
||||
#define DA9034_PEN_DETECT (1 << 4)
|
||||
|
||||
#define DA9034_TSI_CTRL1 0x53
|
||||
#define DA9034_TSI_CTRL2 0x54
|
||||
#define DA9034_TSI_X_MSB 0x6c
|
||||
#define DA9034_TSI_Y_MSB 0x6d
|
||||
#define DA9034_TSI_XY_LSB 0x6e
|
||||
|
||||
enum {
|
||||
STATE_IDLE, /* wait for pendown */
|
||||
STATE_BUSY, /* TSI busy sampling */
|
||||
STATE_STOP, /* sample available */
|
||||
STATE_WAIT, /* Wait to start next sample */
|
||||
};
|
||||
|
||||
enum {
|
||||
EVENT_PEN_DOWN,
|
||||
EVENT_PEN_UP,
|
||||
EVENT_TSI_READY,
|
||||
EVENT_TIMEDOUT,
|
||||
};
|
||||
|
||||
struct da9034_touch {
|
||||
struct device *da9034_dev;
|
||||
struct input_dev *input_dev;
|
||||
|
||||
struct delayed_work tsi_work;
|
||||
struct notifier_block notifier;
|
||||
|
||||
int state;
|
||||
|
||||
int interval_ms;
|
||||
int x_inverted;
|
||||
int y_inverted;
|
||||
|
||||
int last_x;
|
||||
int last_y;
|
||||
};
|
||||
|
||||
static inline int is_pen_down(struct da9034_touch *touch)
|
||||
{
|
||||
return da903x_query_status(touch->da9034_dev, DA9034_STATUS_PEN_DOWN);
|
||||
}
|
||||
|
||||
static inline int detect_pen_down(struct da9034_touch *touch, int on)
|
||||
{
|
||||
if (on)
|
||||
return da903x_set_bits(touch->da9034_dev,
|
||||
DA9034_AUTO_CTRL2, DA9034_PEN_DETECT);
|
||||
else
|
||||
return da903x_clr_bits(touch->da9034_dev,
|
||||
DA9034_AUTO_CTRL2, DA9034_PEN_DETECT);
|
||||
}
|
||||
|
||||
static int read_tsi(struct da9034_touch *touch)
|
||||
{
|
||||
uint8_t _x, _y, _v;
|
||||
int ret;
|
||||
|
||||
ret = da903x_read(touch->da9034_dev, DA9034_TSI_X_MSB, &_x);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = da903x_read(touch->da9034_dev, DA9034_TSI_Y_MSB, &_y);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = da903x_read(touch->da9034_dev, DA9034_TSI_XY_LSB, &_v);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
touch->last_x = ((_x << 2) & 0x3fc) | (_v & 0x3);
|
||||
touch->last_y = ((_y << 2) & 0x3fc) | ((_v & 0xc) >> 2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int start_tsi(struct da9034_touch *touch)
|
||||
{
|
||||
return da903x_set_bits(touch->da9034_dev,
|
||||
DA9034_AUTO_CTRL2, DA9034_AUTO_TSI_EN);
|
||||
}
|
||||
|
||||
static inline int stop_tsi(struct da9034_touch *touch)
|
||||
{
|
||||
return da903x_clr_bits(touch->da9034_dev,
|
||||
DA9034_AUTO_CTRL2, DA9034_AUTO_TSI_EN);
|
||||
}
|
||||
|
||||
static inline void report_pen_down(struct da9034_touch *touch)
|
||||
{
|
||||
int x = touch->last_x;
|
||||
int y = touch->last_y;
|
||||
|
||||
x &= 0xfff;
|
||||
if (touch->x_inverted)
|
||||
x = 1024 - x;
|
||||
y &= 0xfff;
|
||||
if (touch->y_inverted)
|
||||
y = 1024 - y;
|
||||
|
||||
input_report_abs(touch->input_dev, ABS_X, x);
|
||||
input_report_abs(touch->input_dev, ABS_Y, y);
|
||||
input_report_key(touch->input_dev, BTN_TOUCH, 1);
|
||||
|
||||
input_sync(touch->input_dev);
|
||||
}
|
||||
|
||||
static inline void report_pen_up(struct da9034_touch *touch)
|
||||
{
|
||||
input_report_key(touch->input_dev, BTN_TOUCH, 0);
|
||||
input_sync(touch->input_dev);
|
||||
}
|
||||
|
||||
static void da9034_event_handler(struct da9034_touch *touch, int event)
|
||||
{
|
||||
int err;
|
||||
|
||||
switch (touch->state) {
|
||||
case STATE_IDLE:
|
||||
if (event != EVENT_PEN_DOWN)
|
||||
break;
|
||||
|
||||
/* Enable auto measurement of the TSI, this will
|
||||
* automatically disable pen down detection
|
||||
*/
|
||||
err = start_tsi(touch);
|
||||
if (err)
|
||||
goto err_reset;
|
||||
|
||||
touch->state = STATE_BUSY;
|
||||
break;
|
||||
|
||||
case STATE_BUSY:
|
||||
if (event != EVENT_TSI_READY)
|
||||
break;
|
||||
|
||||
err = read_tsi(touch);
|
||||
if (err)
|
||||
goto err_reset;
|
||||
|
||||
/* Disable auto measurement of the TSI, so that
|
||||
* pen down status will be available
|
||||
*/
|
||||
err = stop_tsi(touch);
|
||||
if (err)
|
||||
goto err_reset;
|
||||
|
||||
touch->state = STATE_STOP;
|
||||
break;
|
||||
|
||||
case STATE_STOP:
|
||||
if (event == EVENT_PEN_DOWN) {
|
||||
report_pen_down(touch);
|
||||
schedule_delayed_work(&touch->tsi_work,
|
||||
msecs_to_jiffies(touch->interval_ms));
|
||||
touch->state = STATE_WAIT;
|
||||
}
|
||||
|
||||
if (event == EVENT_PEN_UP) {
|
||||
report_pen_up(touch);
|
||||
touch->state = STATE_IDLE;
|
||||
}
|
||||
|
||||
input_sync(touch->input_dev);
|
||||
break;
|
||||
|
||||
case STATE_WAIT:
|
||||
if (event != EVENT_TIMEDOUT)
|
||||
break;
|
||||
|
||||
if (is_pen_down(touch)) {
|
||||
start_tsi(touch);
|
||||
touch->state = STATE_BUSY;
|
||||
} else
|
||||
touch->state = STATE_IDLE;
|
||||
break;
|
||||
}
|
||||
return;
|
||||
|
||||
err_reset:
|
||||
touch->state = STATE_IDLE;
|
||||
stop_tsi(touch);
|
||||
detect_pen_down(touch, 1);
|
||||
}
|
||||
|
||||
static void da9034_tsi_work(struct work_struct *work)
|
||||
{
|
||||
struct da9034_touch *touch =
|
||||
container_of(work, struct da9034_touch, tsi_work.work);
|
||||
|
||||
da9034_event_handler(touch, EVENT_TIMEDOUT);
|
||||
}
|
||||
|
||||
static int da9034_touch_notifier(struct notifier_block *nb,
|
||||
unsigned long event, void *data)
|
||||
{
|
||||
struct da9034_touch *touch =
|
||||
container_of(nb, struct da9034_touch, notifier);
|
||||
|
||||
if (event & DA9034_EVENT_PEN_DOWN) {
|
||||
if (is_pen_down(touch))
|
||||
da9034_event_handler(touch, EVENT_PEN_DOWN);
|
||||
else
|
||||
da9034_event_handler(touch, EVENT_PEN_UP);
|
||||
}
|
||||
|
||||
if (event & DA9034_EVENT_TSI_READY)
|
||||
da9034_event_handler(touch, EVENT_TSI_READY);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int da9034_touch_open(struct input_dev *dev)
|
||||
{
|
||||
struct da9034_touch *touch = input_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
ret = da903x_register_notifier(touch->da9034_dev, &touch->notifier,
|
||||
DA9034_EVENT_PEN_DOWN | DA9034_EVENT_TSI_READY);
|
||||
if (ret)
|
||||
return -EBUSY;
|
||||
|
||||
/* Enable ADC LDO */
|
||||
ret = da903x_set_bits(touch->da9034_dev,
|
||||
DA9034_MANUAL_CTRL, DA9034_LDO_ADC_EN);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* TSI_DELAY: 3 slots, TSI_SKIP: 3 slots */
|
||||
ret = da903x_write(touch->da9034_dev, DA9034_TSI_CTRL1, 0x1b);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = da903x_write(touch->da9034_dev, DA9034_TSI_CTRL2, 0x00);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
touch->state = STATE_IDLE;
|
||||
detect_pen_down(touch, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void da9034_touch_close(struct input_dev *dev)
|
||||
{
|
||||
struct da9034_touch *touch = input_get_drvdata(dev);
|
||||
|
||||
da903x_unregister_notifier(touch->da9034_dev, &touch->notifier,
|
||||
DA9034_EVENT_PEN_DOWN | DA9034_EVENT_TSI_READY);
|
||||
|
||||
cancel_delayed_work_sync(&touch->tsi_work);
|
||||
|
||||
touch->state = STATE_IDLE;
|
||||
stop_tsi(touch);
|
||||
detect_pen_down(touch, 0);
|
||||
|
||||
/* Disable ADC LDO */
|
||||
da903x_clr_bits(touch->da9034_dev,
|
||||
DA9034_MANUAL_CTRL, DA9034_LDO_ADC_EN);
|
||||
}
|
||||
|
||||
|
||||
static int __devinit da9034_touch_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct da9034_touch_pdata *pdata = pdev->dev.platform_data;
|
||||
struct da9034_touch *touch;
|
||||
struct input_dev *input_dev;
|
||||
int ret;
|
||||
|
||||
touch = kzalloc(sizeof(struct da9034_touch), GFP_KERNEL);
|
||||
if (touch == NULL) {
|
||||
dev_err(&pdev->dev, "failed to allocate driver data\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
touch->da9034_dev = pdev->dev.parent;
|
||||
|
||||
if (pdata) {
|
||||
touch->interval_ms = pdata->interval_ms;
|
||||
touch->x_inverted = pdata->x_inverted;
|
||||
touch->y_inverted = pdata->y_inverted;
|
||||
} else
|
||||
/* fallback into default */
|
||||
touch->interval_ms = 10;
|
||||
|
||||
INIT_DELAYED_WORK(&touch->tsi_work, da9034_tsi_work);
|
||||
touch->notifier.notifier_call = da9034_touch_notifier;
|
||||
|
||||
input_dev = input_allocate_device();
|
||||
if (!input_dev) {
|
||||
dev_err(&pdev->dev, "failed to allocate input device\n");
|
||||
ret = -ENOMEM;
|
||||
goto err_free_touch;
|
||||
}
|
||||
|
||||
input_dev->name = pdev->name;
|
||||
input_dev->open = da9034_touch_open;
|
||||
input_dev->close = da9034_touch_close;
|
||||
input_dev->dev.parent = &pdev->dev;
|
||||
|
||||
__set_bit(EV_ABS, input_dev->evbit);
|
||||
__set_bit(ABS_X, input_dev->absbit);
|
||||
__set_bit(ABS_Y, input_dev->absbit);
|
||||
input_set_abs_params(input_dev, ABS_X, 0, 1023, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_Y, 0, 1023, 0, 0);
|
||||
|
||||
__set_bit(EV_KEY, input_dev->evbit);
|
||||
__set_bit(BTN_TOUCH, input_dev->keybit);
|
||||
|
||||
touch->input_dev = input_dev;
|
||||
input_set_drvdata(input_dev, touch);
|
||||
|
||||
ret = input_register_device(input_dev);
|
||||
if (ret)
|
||||
goto err_free_input;
|
||||
|
||||
platform_set_drvdata(pdev, touch);
|
||||
return 0;
|
||||
|
||||
err_free_input:
|
||||
input_free_device(input_dev);
|
||||
err_free_touch:
|
||||
kfree(touch);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devexit da9034_touch_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct da9034_touch *touch = platform_get_drvdata(pdev);
|
||||
|
||||
input_unregister_device(touch->input_dev);
|
||||
kfree(touch);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver da9034_touch_driver = {
|
||||
.driver = {
|
||||
.name = "da9034-touch",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = da9034_touch_probe,
|
||||
.remove = __devexit_p(da9034_touch_remove),
|
||||
};
|
||||
|
||||
static int __init da9034_touch_init(void)
|
||||
{
|
||||
return platform_driver_register(&da9034_touch_driver);
|
||||
}
|
||||
module_init(da9034_touch_init);
|
||||
|
||||
static void __exit da9034_touch_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&da9034_touch_driver);
|
||||
}
|
||||
module_exit(da9034_touch_exit);
|
||||
|
||||
MODULE_DESCRIPTION("Touchscreen driver for Dialog Semiconductor DA9034");
|
||||
MODULE_AUTHOR("Eric Miao <eric.miao@marvell.com>");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:da9034-touch");
|
||||
@@ -0,0 +1,381 @@
|
||||
/*
|
||||
* drivers/input/touchscreen/tsc2007.c
|
||||
*
|
||||
* Copyright (c) 2008 MtekVision Co., Ltd.
|
||||
* Kwangwoo Lee <kwlee@mtekvision.com>
|
||||
*
|
||||
* Using code from:
|
||||
* - ads7846.c
|
||||
* Copyright (c) 2005 David Brownell
|
||||
* Copyright (c) 2006 Nokia Corporation
|
||||
* - corgi_ts.c
|
||||
* Copyright (C) 2004-2005 Richard Purdie
|
||||
* - omap_ts.[hc], ads7846.h, ts_osk.c
|
||||
* Copyright (C) 2002 MontaVista Software
|
||||
* Copyright (C) 2004 Texas Instruments
|
||||
* Copyright (C) 2005 Dirk Behme
|
||||
*
|
||||
* 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 <linux/module.h>
|
||||
#include <linux/hrtimer.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/i2c/tsc2007.h>
|
||||
|
||||
#define TS_POLL_DELAY (10 * 1000) /* ns delay before the first sample */
|
||||
#define TS_POLL_PERIOD (5 * 1000) /* ns delay between samples */
|
||||
|
||||
#define TSC2007_MEASURE_TEMP0 (0x0 << 4)
|
||||
#define TSC2007_MEASURE_AUX (0x2 << 4)
|
||||
#define TSC2007_MEASURE_TEMP1 (0x4 << 4)
|
||||
#define TSC2007_ACTIVATE_XN (0x8 << 4)
|
||||
#define TSC2007_ACTIVATE_YN (0x9 << 4)
|
||||
#define TSC2007_ACTIVATE_YP_XN (0xa << 4)
|
||||
#define TSC2007_SETUP (0xb << 4)
|
||||
#define TSC2007_MEASURE_X (0xc << 4)
|
||||
#define TSC2007_MEASURE_Y (0xd << 4)
|
||||
#define TSC2007_MEASURE_Z1 (0xe << 4)
|
||||
#define TSC2007_MEASURE_Z2 (0xf << 4)
|
||||
|
||||
#define TSC2007_POWER_OFF_IRQ_EN (0x0 << 2)
|
||||
#define TSC2007_ADC_ON_IRQ_DIS0 (0x1 << 2)
|
||||
#define TSC2007_ADC_OFF_IRQ_EN (0x2 << 2)
|
||||
#define TSC2007_ADC_ON_IRQ_DIS1 (0x3 << 2)
|
||||
|
||||
#define TSC2007_12BIT (0x0 << 1)
|
||||
#define TSC2007_8BIT (0x1 << 1)
|
||||
|
||||
#define MAX_12BIT ((1 << 12) - 1)
|
||||
|
||||
#define ADC_ON_12BIT (TSC2007_12BIT | TSC2007_ADC_ON_IRQ_DIS0)
|
||||
|
||||
#define READ_Y (ADC_ON_12BIT | TSC2007_MEASURE_Y)
|
||||
#define READ_Z1 (ADC_ON_12BIT | TSC2007_MEASURE_Z1)
|
||||
#define READ_Z2 (ADC_ON_12BIT | TSC2007_MEASURE_Z2)
|
||||
#define READ_X (ADC_ON_12BIT | TSC2007_MEASURE_X)
|
||||
#define PWRDOWN (TSC2007_12BIT | TSC2007_POWER_OFF_IRQ_EN)
|
||||
|
||||
struct ts_event {
|
||||
u16 x;
|
||||
u16 y;
|
||||
u16 z1, z2;
|
||||
};
|
||||
|
||||
struct tsc2007 {
|
||||
struct input_dev *input;
|
||||
char phys[32];
|
||||
struct hrtimer timer;
|
||||
struct ts_event tc;
|
||||
|
||||
struct i2c_client *client;
|
||||
|
||||
spinlock_t lock;
|
||||
|
||||
u16 model;
|
||||
u16 x_plate_ohms;
|
||||
|
||||
unsigned pendown;
|
||||
int irq;
|
||||
|
||||
int (*get_pendown_state)(void);
|
||||
void (*clear_penirq)(void);
|
||||
};
|
||||
|
||||
static inline int tsc2007_xfer(struct tsc2007 *tsc, u8 cmd)
|
||||
{
|
||||
s32 data;
|
||||
u16 val;
|
||||
|
||||
data = i2c_smbus_read_word_data(tsc->client, cmd);
|
||||
if (data < 0) {
|
||||
dev_err(&tsc->client->dev, "i2c io error: %d\n", data);
|
||||
return data;
|
||||
}
|
||||
|
||||
/* The protocol and raw data format from i2c interface:
|
||||
* S Addr Wr [A] Comm [A] S Addr Rd [A] [DataLow] A [DataHigh] NA P
|
||||
* Where DataLow has [D11-D4], DataHigh has [D3-D0 << 4 | Dummy 4bit].
|
||||
*/
|
||||
val = swab16(data) >> 4;
|
||||
|
||||
dev_dbg(&tsc->client->dev, "data: 0x%x, val: 0x%x\n", data, val);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static void tsc2007_send_event(void *tsc)
|
||||
{
|
||||
struct tsc2007 *ts = tsc;
|
||||
u32 rt;
|
||||
u16 x, y, z1, z2;
|
||||
|
||||
x = ts->tc.x;
|
||||
y = ts->tc.y;
|
||||
z1 = ts->tc.z1;
|
||||
z2 = ts->tc.z2;
|
||||
|
||||
/* range filtering */
|
||||
if (x == MAX_12BIT)
|
||||
x = 0;
|
||||
|
||||
if (likely(x && z1)) {
|
||||
/* compute touch pressure resistance using equation #1 */
|
||||
rt = z2;
|
||||
rt -= z1;
|
||||
rt *= x;
|
||||
rt *= ts->x_plate_ohms;
|
||||
rt /= z1;
|
||||
rt = (rt + 2047) >> 12;
|
||||
} else
|
||||
rt = 0;
|
||||
|
||||
/* 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 (rt > MAX_12BIT) {
|
||||
dev_dbg(&ts->client->dev, "ignored pressure %d\n", rt);
|
||||
|
||||
hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_PERIOD),
|
||||
HRTIMER_MODE_REL);
|
||||
return;
|
||||
}
|
||||
|
||||
/* 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.
|
||||
*
|
||||
* The only safe way to check for the pen up condition is in the
|
||||
* timer by reading the pen signal state (it's a GPIO _and_ IRQ).
|
||||
*/
|
||||
if (rt) {
|
||||
struct input_dev *input = ts->input;
|
||||
|
||||
if (!ts->pendown) {
|
||||
dev_dbg(&ts->client->dev, "DOWN\n");
|
||||
|
||||
input_report_key(input, BTN_TOUCH, 1);
|
||||
ts->pendown = 1;
|
||||
}
|
||||
|
||||
input_report_abs(input, ABS_X, x);
|
||||
input_report_abs(input, ABS_Y, y);
|
||||
input_report_abs(input, ABS_PRESSURE, rt);
|
||||
|
||||
input_sync(input);
|
||||
|
||||
dev_dbg(&ts->client->dev, "point(%4d,%4d), pressure (%4u)\n",
|
||||
x, y, rt);
|
||||
}
|
||||
|
||||
hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_PERIOD),
|
||||
HRTIMER_MODE_REL);
|
||||
}
|
||||
|
||||
static int tsc2007_read_values(struct tsc2007 *tsc)
|
||||
{
|
||||
/* y- still on; turn on only y+ (and ADC) */
|
||||
tsc->tc.y = tsc2007_xfer(tsc, READ_Y);
|
||||
|
||||
/* turn y- off, x+ on, then leave in lowpower */
|
||||
tsc->tc.x = tsc2007_xfer(tsc, READ_X);
|
||||
|
||||
/* turn y+ off, x- on; we'll use formula #1 */
|
||||
tsc->tc.z1 = tsc2007_xfer(tsc, READ_Z1);
|
||||
tsc->tc.z2 = tsc2007_xfer(tsc, READ_Z2);
|
||||
|
||||
/* power down */
|
||||
tsc2007_xfer(tsc, PWRDOWN);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static enum hrtimer_restart tsc2007_timer(struct hrtimer *handle)
|
||||
{
|
||||
struct tsc2007 *ts = container_of(handle, struct tsc2007, timer);
|
||||
|
||||
spin_lock_irq(&ts->lock);
|
||||
|
||||
if (unlikely(!ts->get_pendown_state() && ts->pendown)) {
|
||||
struct input_dev *input = ts->input;
|
||||
|
||||
dev_dbg(&ts->client->dev, "UP\n");
|
||||
|
||||
input_report_key(input, BTN_TOUCH, 0);
|
||||
input_report_abs(input, ABS_PRESSURE, 0);
|
||||
input_sync(input);
|
||||
|
||||
ts->pendown = 0;
|
||||
enable_irq(ts->irq);
|
||||
} else {
|
||||
/* pen is still down, continue with the measurement */
|
||||
dev_dbg(&ts->client->dev, "pen is still down\n");
|
||||
|
||||
tsc2007_read_values(ts);
|
||||
tsc2007_send_event(ts);
|
||||
}
|
||||
|
||||
spin_unlock_irq(&ts->lock);
|
||||
|
||||
return HRTIMER_NORESTART;
|
||||
}
|
||||
|
||||
static irqreturn_t tsc2007_irq(int irq, void *handle)
|
||||
{
|
||||
struct tsc2007 *ts = handle;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&ts->lock, flags);
|
||||
|
||||
if (likely(ts->get_pendown_state())) {
|
||||
disable_irq(ts->irq);
|
||||
hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_DELAY),
|
||||
HRTIMER_MODE_REL);
|
||||
}
|
||||
|
||||
if (ts->clear_penirq)
|
||||
ts->clear_penirq();
|
||||
|
||||
spin_unlock_irqrestore(&ts->lock, flags);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int 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 input_dev *input_dev;
|
||||
int err;
|
||||
|
||||
if (!pdata) {
|
||||
dev_err(&client->dev, "platform data is required!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!i2c_check_functionality(client->adapter,
|
||||
I2C_FUNC_SMBUS_READ_WORD_DATA))
|
||||
return -EIO;
|
||||
|
||||
ts = kzalloc(sizeof(struct tsc2007), GFP_KERNEL);
|
||||
input_dev = input_allocate_device();
|
||||
if (!ts || !input_dev) {
|
||||
err = -ENOMEM;
|
||||
goto err_free_mem;
|
||||
}
|
||||
|
||||
ts->client = client;
|
||||
i2c_set_clientdata(client, ts);
|
||||
|
||||
ts->input = input_dev;
|
||||
|
||||
hrtimer_init(&ts->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
||||
ts->timer.function = tsc2007_timer;
|
||||
|
||||
spin_lock_init(&ts->lock);
|
||||
|
||||
ts->model = pdata->model;
|
||||
ts->x_plate_ohms = pdata->x_plate_ohms;
|
||||
ts->get_pendown_state = pdata->get_pendown_state;
|
||||
ts->clear_penirq = pdata->clear_penirq;
|
||||
|
||||
pdata->init_platform_hw();
|
||||
|
||||
snprintf(ts->phys, sizeof(ts->phys), "%s/input0", client->dev.bus_id);
|
||||
|
||||
input_dev->name = "TSC2007 Touchscreen";
|
||||
input_dev->phys = ts->phys;
|
||||
input_dev->id.bustype = BUS_I2C;
|
||||
|
||||
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, 0, MAX_12BIT, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_12BIT, 0, 0);
|
||||
|
||||
tsc2007_read_values(ts);
|
||||
|
||||
ts->irq = client->irq;
|
||||
|
||||
err = request_irq(ts->irq, tsc2007_irq, 0,
|
||||
client->dev.driver->name, ts);
|
||||
if (err < 0) {
|
||||
dev_err(&client->dev, "irq %d busy?\n", ts->irq);
|
||||
goto err_free_mem;
|
||||
}
|
||||
|
||||
err = input_register_device(input_dev);
|
||||
if (err)
|
||||
goto err_free_irq;
|
||||
|
||||
dev_info(&client->dev, "registered with irq (%d)\n", ts->irq);
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_irq:
|
||||
free_irq(ts->irq, ts);
|
||||
hrtimer_cancel(&ts->timer);
|
||||
err_free_mem:
|
||||
input_free_device(input_dev);
|
||||
kfree(ts);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int tsc2007_remove(struct i2c_client *client)
|
||||
{
|
||||
struct tsc2007 *ts = i2c_get_clientdata(client);
|
||||
struct tsc2007_platform_data *pdata;
|
||||
|
||||
pdata = client->dev.platform_data;
|
||||
pdata->exit_platform_hw();
|
||||
|
||||
free_irq(ts->irq, ts);
|
||||
hrtimer_cancel(&ts->timer);
|
||||
input_unregister_device(ts->input);
|
||||
kfree(ts);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct i2c_device_id tsc2007_idtable[] = {
|
||||
{ "tsc2007", 0 },
|
||||
{ }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(i2c, tsc2007_idtable);
|
||||
|
||||
static struct i2c_driver tsc2007_driver = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "tsc2007"
|
||||
},
|
||||
.id_table = tsc2007_idtable,
|
||||
.probe = tsc2007_probe,
|
||||
.remove = tsc2007_remove,
|
||||
};
|
||||
|
||||
static int __init tsc2007_init(void)
|
||||
{
|
||||
return i2c_add_driver(&tsc2007_driver);
|
||||
}
|
||||
|
||||
static void __exit tsc2007_exit(void)
|
||||
{
|
||||
i2c_del_driver(&tsc2007_driver);
|
||||
}
|
||||
|
||||
module_init(tsc2007_init);
|
||||
module_exit(tsc2007_exit);
|
||||
|
||||
MODULE_AUTHOR("Kwangwoo Lee <kwlee@mtekvision.com>");
|
||||
MODULE_DESCRIPTION("TSC2007 TouchScreen Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
@@ -424,7 +424,7 @@ static int dmc_tsc10_init(struct usbtouch_usb *usbtouch)
|
||||
0, 0, buf, 2, USB_CTRL_SET_TIMEOUT);
|
||||
if (ret < 0)
|
||||
goto err_out;
|
||||
if (buf[0] != 0x06 || buf[1] != 0x00) {
|
||||
if (buf[0] != 0x06) {
|
||||
ret = -ENODEV;
|
||||
goto err_out;
|
||||
}
|
||||
@@ -437,8 +437,7 @@ static int dmc_tsc10_init(struct usbtouch_usb *usbtouch)
|
||||
TSC10_RATE_150, 0, buf, 2, USB_CTRL_SET_TIMEOUT);
|
||||
if (ret < 0)
|
||||
goto err_out;
|
||||
if ((buf[0] != 0x06 || buf[1] != 0x00) &&
|
||||
(buf[0] != 0x15 || buf[1] != 0x01)) {
|
||||
if ((buf[0] != 0x06) && (buf[0] != 0x15 || buf[1] != 0x01)) {
|
||||
ret = -ENODEV;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,325 @@
|
||||
/*
|
||||
* Wacom W8001 penabled serial touchscreen driver
|
||||
*
|
||||
* Copyright (c) 2008 Jaya Kumar
|
||||
*
|
||||
* 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
|
||||
* more details.
|
||||
*
|
||||
* Layout based on Elo serial touchscreen driver by Vojtech Pavlik
|
||||
*/
|
||||
|
||||
#include <linux/errno.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/serio.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/ctype.h>
|
||||
|
||||
#define DRIVER_DESC "Wacom W8001 serial touchscreen driver"
|
||||
|
||||
MODULE_AUTHOR("Jaya Kumar <jayakumar.lkml@gmail.com>");
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
/*
|
||||
* Definitions & global arrays.
|
||||
*/
|
||||
|
||||
#define W8001_MAX_LENGTH 11
|
||||
#define W8001_PACKET_LEN 11
|
||||
#define W8001_LEAD_MASK 0x80
|
||||
#define W8001_LEAD_BYTE 0x80
|
||||
#define W8001_TAB_MASK 0x40
|
||||
#define W8001_TAB_BYTE 0x40
|
||||
|
||||
#define W8001_QUERY_PACKET 0x20
|
||||
|
||||
struct w8001_coord {
|
||||
u8 rdy;
|
||||
u8 tsw;
|
||||
u8 f1;
|
||||
u8 f2;
|
||||
u16 x;
|
||||
u16 y;
|
||||
u16 pen_pressure;
|
||||
u8 tilt_x;
|
||||
u8 tilt_y;
|
||||
};
|
||||
|
||||
/*
|
||||
* Per-touchscreen data.
|
||||
*/
|
||||
|
||||
struct w8001 {
|
||||
struct input_dev *dev;
|
||||
struct serio *serio;
|
||||
struct mutex cmd_mutex;
|
||||
struct completion cmd_done;
|
||||
int id;
|
||||
int idx;
|
||||
unsigned char expected_packet;
|
||||
unsigned char data[W8001_MAX_LENGTH];
|
||||
unsigned char response[W8001_PACKET_LEN];
|
||||
char phys[32];
|
||||
};
|
||||
|
||||
static int parse_data(u8 *data, struct w8001_coord *coord)
|
||||
{
|
||||
coord->rdy = data[0] & 0x20;
|
||||
coord->tsw = data[0] & 0x01;
|
||||
coord->f1 = data[0] & 0x02;
|
||||
coord->f2 = data[0] & 0x04;
|
||||
|
||||
coord->x = (data[1] & 0x7F) << 9;
|
||||
coord->x |= (data[2] & 0x7F) << 2;
|
||||
coord->x |= (data[6] & 0x60) >> 5;
|
||||
|
||||
coord->y = (data[3] & 0x7F) << 9;
|
||||
coord->y |= (data[4] & 0x7F) << 2;
|
||||
coord->y |= (data[6] & 0x18) >> 3;
|
||||
|
||||
coord->pen_pressure = data[5] & 0x7F;
|
||||
coord->pen_pressure |= (data[6] & 0x07) << 7 ;
|
||||
|
||||
coord->tilt_x = data[7] & 0x7F;
|
||||
coord->tilt_y = data[8] & 0x7F;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void w8001_process_data(struct w8001 *w8001, unsigned char data)
|
||||
{
|
||||
struct input_dev *dev = w8001->dev;
|
||||
u8 tmp;
|
||||
struct w8001_coord coord;
|
||||
|
||||
w8001->data[w8001->idx] = data;
|
||||
switch (w8001->idx++) {
|
||||
case 0:
|
||||
if ((data & W8001_LEAD_MASK) != W8001_LEAD_BYTE) {
|
||||
pr_debug("w8001: unsynchronized data: 0x%02x\n", data);
|
||||
w8001->idx = 0;
|
||||
}
|
||||
break;
|
||||
case 8:
|
||||
tmp = w8001->data[0] & W8001_TAB_MASK;
|
||||
if (unlikely(tmp == W8001_TAB_BYTE))
|
||||
break;
|
||||
w8001->idx = 0;
|
||||
memset(&coord, 0, sizeof(coord));
|
||||
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);
|
||||
break;
|
||||
case 10:
|
||||
w8001->idx = 0;
|
||||
memcpy(w8001->response, &w8001->data, W8001_PACKET_LEN);
|
||||
w8001->expected_packet = W8001_QUERY_PACKET;
|
||||
complete(&w8001->cmd_done);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static irqreturn_t w8001_interrupt(struct serio *serio,
|
||||
unsigned char data, unsigned int flags)
|
||||
{
|
||||
struct w8001 *w8001 = serio_get_drvdata(serio);
|
||||
|
||||
w8001_process_data(w8001, data);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int w8001_async_command(struct w8001 *w8001, unsigned char *packet,
|
||||
int len)
|
||||
{
|
||||
int rc = -1;
|
||||
int i;
|
||||
|
||||
mutex_lock(&w8001->cmd_mutex);
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
if (serio_write(w8001->serio, packet[i]))
|
||||
goto out;
|
||||
}
|
||||
rc = 0;
|
||||
|
||||
out:
|
||||
mutex_unlock(&w8001->cmd_mutex);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int w8001_command(struct w8001 *w8001, unsigned char *packet, int len)
|
||||
{
|
||||
int rc = -1;
|
||||
int i;
|
||||
|
||||
mutex_lock(&w8001->cmd_mutex);
|
||||
|
||||
serio_pause_rx(w8001->serio);
|
||||
init_completion(&w8001->cmd_done);
|
||||
serio_continue_rx(w8001->serio);
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
if (serio_write(w8001->serio, packet[i]))
|
||||
goto out;
|
||||
}
|
||||
|
||||
wait_for_completion_timeout(&w8001->cmd_done, HZ);
|
||||
|
||||
if (w8001->expected_packet == W8001_QUERY_PACKET) {
|
||||
/* We are back in reporting mode, the query was ACKed */
|
||||
memcpy(packet, w8001->response, W8001_PACKET_LEN);
|
||||
rc = 0;
|
||||
}
|
||||
|
||||
out:
|
||||
mutex_unlock(&w8001->cmd_mutex);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int w8001_setup(struct w8001 *w8001)
|
||||
{
|
||||
struct w8001_coord coord;
|
||||
struct input_dev *dev = w8001->dev;
|
||||
unsigned char start[1] = { '1' };
|
||||
unsigned char query[11] = { '*' };
|
||||
|
||||
if (w8001_command(w8001, query, 1))
|
||||
return -1;
|
||||
|
||||
memset(&coord, 0, sizeof(coord));
|
||||
parse_data(query, &coord);
|
||||
|
||||
input_set_abs_params(dev, ABS_X, 0, coord.x, 0, 0);
|
||||
input_set_abs_params(dev, ABS_Y, 0, coord.y, 0, 0);
|
||||
input_set_abs_params(dev, ABS_PRESSURE, 0, coord.pen_pressure, 0, 0);
|
||||
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);
|
||||
|
||||
if (w8001_async_command(w8001, start, 1))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* w8001_disconnect() is the opposite of w8001_connect()
|
||||
*/
|
||||
|
||||
static void w8001_disconnect(struct serio *serio)
|
||||
{
|
||||
struct w8001 *w8001 = serio_get_drvdata(serio);
|
||||
|
||||
input_get_device(w8001->dev);
|
||||
input_unregister_device(w8001->dev);
|
||||
serio_close(serio);
|
||||
serio_set_drvdata(serio, NULL);
|
||||
input_put_device(w8001->dev);
|
||||
kfree(w8001);
|
||||
}
|
||||
|
||||
/*
|
||||
* w8001_connect() is the routine that is called when someone adds a
|
||||
* new serio device that supports the w8001 protocol and registers it as
|
||||
* an input device.
|
||||
*/
|
||||
|
||||
static int w8001_connect(struct serio *serio, struct serio_driver *drv)
|
||||
{
|
||||
struct w8001 *w8001;
|
||||
struct input_dev *input_dev;
|
||||
int err;
|
||||
|
||||
w8001 = kzalloc(sizeof(struct w8001), GFP_KERNEL);
|
||||
input_dev = input_allocate_device();
|
||||
if (!w8001 || !input_dev) {
|
||||
err = -ENOMEM;
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
w8001->serio = serio;
|
||||
w8001->id = serio->id.id;
|
||||
w8001->dev = input_dev;
|
||||
mutex_init(&w8001->cmd_mutex);
|
||||
init_completion(&w8001->cmd_done);
|
||||
snprintf(w8001->phys, sizeof(w8001->phys), "%s/input0", serio->phys);
|
||||
|
||||
input_dev->name = "Wacom W8001 Penabled Serial TouchScreen";
|
||||
input_dev->phys = w8001->phys;
|
||||
input_dev->id.bustype = BUS_RS232;
|
||||
input_dev->id.vendor = SERIO_W8001;
|
||||
input_dev->id.product = w8001->id;
|
||||
input_dev->id.version = 0x0100;
|
||||
input_dev->dev.parent = &serio->dev;
|
||||
|
||||
input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
|
||||
input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
|
||||
|
||||
serio_set_drvdata(serio, w8001);
|
||||
err = serio_open(serio, drv);
|
||||
if (err)
|
||||
goto fail2;
|
||||
|
||||
if (w8001_setup(w8001))
|
||||
goto fail3;
|
||||
|
||||
err = input_register_device(w8001->dev);
|
||||
if (err)
|
||||
goto fail3;
|
||||
|
||||
return 0;
|
||||
|
||||
fail3:
|
||||
serio_close(serio);
|
||||
fail2:
|
||||
serio_set_drvdata(serio, NULL);
|
||||
fail1:
|
||||
input_free_device(input_dev);
|
||||
kfree(w8001);
|
||||
return err;
|
||||
}
|
||||
|
||||
static struct serio_device_id w8001_serio_ids[] = {
|
||||
{
|
||||
.type = SERIO_RS232,
|
||||
.proto = SERIO_W8001,
|
||||
.id = SERIO_ANY,
|
||||
.extra = SERIO_ANY,
|
||||
},
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(serio, w8001_serio_ids);
|
||||
|
||||
static struct serio_driver w8001_drv = {
|
||||
.driver = {
|
||||
.name = "w8001",
|
||||
},
|
||||
.description = DRIVER_DESC,
|
||||
.id_table = w8001_serio_ids,
|
||||
.interrupt = w8001_interrupt,
|
||||
.connect = w8001_connect,
|
||||
.disconnect = w8001_disconnect,
|
||||
};
|
||||
|
||||
static int __init w8001_init(void)
|
||||
{
|
||||
return serio_register_driver(&w8001_drv);
|
||||
}
|
||||
|
||||
static void __exit w8001_exit(void)
|
||||
{
|
||||
serio_unregister_driver(&w8001_drv);
|
||||
}
|
||||
|
||||
module_init(w8001_init);
|
||||
module_exit(w8001_exit);
|
||||
Reference in New Issue
Block a user