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: (64 commits) Input: tc3589x-keypad - add missing kerneldoc Input: ucb1400-ts - switch to using dev_xxx() for diagnostic messages Input: ucb1400_ts - convert to threaded IRQ Input: ucb1400_ts - drop inline annotations Input: usb1400_ts - add __devinit/__devexit section annotations Input: ucb1400_ts - set driver owner Input: ucb1400_ts - convert to use dev_pm_ops Input: psmouse - make sure we do not use stale methods Input: evdev - do not block waiting for an event if fd is nonblock Input: evdev - if no events and non-block, return EAGAIN not 0 Input: evdev - only allow reading events if a full packet is present Input: add driver for pixcir i2c touchscreens Input: samsung-keypad - implement runtime power management support Input: tegra-kbc - report wakeup key for some platforms Input: tegra-kbc - add device tree bindings Input: add driver for AUO In-Cell touchscreens using pixcir ICs Input: mpu3050 - configure the sampling method Input: mpu3050 - ensure we enable interrupts Input: mpu3050 - add of_match table for device-tree probing Input: sentelic - document the latest hardware ... Fix up fairly trivial conflicts (device tree matching conflicting with some independent cleanups) in drivers/input/keyboard/samsung-keypad.c
This commit is contained in:
@@ -217,18 +217,7 @@ static struct platform_driver pm860x_touch_driver = {
|
||||
.probe = pm860x_touch_probe,
|
||||
.remove = __devexit_p(pm860x_touch_remove),
|
||||
};
|
||||
|
||||
static int __init pm860x_touch_init(void)
|
||||
{
|
||||
return platform_driver_register(&pm860x_touch_driver);
|
||||
}
|
||||
module_init(pm860x_touch_init);
|
||||
|
||||
static void __exit pm860x_touch_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&pm860x_touch_driver);
|
||||
}
|
||||
module_exit(pm860x_touch_exit);
|
||||
module_platform_driver(pm860x_touch_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Touchscreen driver for Marvell Semiconductor 88PM860x");
|
||||
MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
|
||||
|
||||
@@ -98,6 +98,19 @@ config TOUCHSCREEN_ATMEL_MXT
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called atmel_mxt_ts.
|
||||
|
||||
config TOUCHSCREEN_AUO_PIXCIR
|
||||
tristate "AUO in-cell touchscreen using Pixcir ICs"
|
||||
depends on I2C
|
||||
depends on GPIOLIB
|
||||
help
|
||||
Say Y here if you have a AUO display with in-cell touchscreen
|
||||
using Pixcir ICs.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called auo-pixcir-ts.
|
||||
|
||||
config TOUCHSCREEN_BITSY
|
||||
tristate "Compaq iPAQ H3600 (Bitsy) touchscreen"
|
||||
depends on SA1100_BITSY
|
||||
@@ -177,6 +190,16 @@ config TOUCHSCREEN_EETI
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called eeti_ts.
|
||||
|
||||
config TOUCHSCREEN_EGALAX
|
||||
tristate "EETI eGalax multi-touch panel support"
|
||||
depends on I2C
|
||||
help
|
||||
Say Y here to enable support for I2C connected EETI
|
||||
eGalax multi-touch panels.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called egalax_ts.
|
||||
|
||||
config TOUCHSCREEN_FUJITSU
|
||||
tristate "Fujitsu serial touchscreen"
|
||||
select SERIO
|
||||
@@ -435,6 +458,18 @@ config TOUCHSCREEN_UCB1400
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ucb1400_ts.
|
||||
|
||||
config TOUCHSCREEN_PIXCIR
|
||||
tristate "PIXCIR I2C touchscreens"
|
||||
depends on I2C
|
||||
help
|
||||
Say Y here if you have a pixcir i2c touchscreen
|
||||
controller.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called pixcir_i2c_ts.
|
||||
|
||||
config TOUCHSCREEN_WM831X
|
||||
tristate "Support for WM831x touchscreen controllers"
|
||||
depends on MFD_WM831X
|
||||
@@ -541,6 +576,7 @@ config TOUCHSCREEN_USB_COMPOSITE
|
||||
- GoTop Super_Q2/GogoPen/PenPower tablets
|
||||
- JASTEC USB Touch Controller/DigiTech DTR-02U
|
||||
- Zytronic controllers
|
||||
- Elo TouchSystems 2700 IntelliTouch
|
||||
|
||||
Have a look at <http://linux.chapter7.ch/touchkit/> for
|
||||
a usage description and the required user-space stuff.
|
||||
@@ -620,6 +656,11 @@ config TOUCHSCREEN_USB_JASTEC
|
||||
bool "JASTEC/DigiTech DTR-02U USB touch controller device support" if EXPERT
|
||||
depends on TOUCHSCREEN_USB_COMPOSITE
|
||||
|
||||
config TOUCHSCREEN_USB_ELO
|
||||
default y
|
||||
bool "Elo TouchSystems 2700 IntelliTouch controller device support" if EXPERT
|
||||
depends on TOUCHSCREEN_USB_COMPOSITE
|
||||
|
||||
config TOUCHSCREEN_USB_E2I
|
||||
default y
|
||||
bool "e2i Touchscreen controller (e.g. from Mimo 740)"
|
||||
|
||||
@@ -14,6 +14,7 @@ obj-$(CONFIG_TOUCHSCREEN_AD7879_SPI) += ad7879-spi.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_ADS7846) += ads7846.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_ATMEL_MXT) += atmel_mxt_ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_ATMEL_TSADCC) += atmel_tsadcc.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_AUO_PIXCIR) += auo-pixcir-ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_BITSY) += h3600_ts_input.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_BU21013) += bu21013_ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_CY8CTMG110) += cy8ctmg110_ts.o
|
||||
@@ -23,6 +24,7 @@ obj-$(CONFIG_TOUCHSCREEN_HAMPSHIRE) += hampshire.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_GUNZE) += gunze.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_EETI) += eeti_ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_EGALAX) += egalax_ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_INEXIO) += inexio.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_INTEL_MID) += intel-mid-touch.o
|
||||
@@ -39,6 +41,7 @@ obj-$(CONFIG_TOUCHSCREEN_HTCPEN) += htcpen.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_USB_COMPOSITE) += usbtouchscreen.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_PCAP) += pcap_ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_PENMOUNT) += penmount.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_PIXCIR) += pixcir_i2c_ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_S3C2410) += s3c2410_ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_ST1232) += st1232.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_STMPE) += stmpe-ts.o
|
||||
|
||||
@@ -488,10 +488,10 @@ static ssize_t ad7877_disable_store(struct device *dev,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct ad7877 *ts = dev_get_drvdata(dev);
|
||||
unsigned long val;
|
||||
unsigned int val;
|
||||
int error;
|
||||
|
||||
error = strict_strtoul(buf, 10, &val);
|
||||
error = kstrtouint(buf, 10, &val);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
@@ -518,10 +518,10 @@ static ssize_t ad7877_dac_store(struct device *dev,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct ad7877 *ts = dev_get_drvdata(dev);
|
||||
unsigned long val;
|
||||
unsigned int val;
|
||||
int error;
|
||||
|
||||
error = strict_strtoul(buf, 10, &val);
|
||||
error = kstrtouint(buf, 10, &val);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
@@ -548,10 +548,10 @@ static ssize_t ad7877_gpio3_store(struct device *dev,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct ad7877 *ts = dev_get_drvdata(dev);
|
||||
unsigned long val;
|
||||
unsigned int val;
|
||||
int error;
|
||||
|
||||
error = strict_strtoul(buf, 10, &val);
|
||||
error = kstrtouint(buf, 10, &val);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
@@ -579,10 +579,10 @@ static ssize_t ad7877_gpio4_store(struct device *dev,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct ad7877 *ts = dev_get_drvdata(dev);
|
||||
unsigned long val;
|
||||
unsigned int val;
|
||||
int error;
|
||||
|
||||
error = strict_strtoul(buf, 10, &val);
|
||||
error = kstrtouint(buf, 10, &val);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
@@ -853,7 +853,6 @@ static SIMPLE_DEV_PM_OPS(ad7877_pm, ad7877_suspend, ad7877_resume);
|
||||
static struct spi_driver ad7877_driver = {
|
||||
.driver = {
|
||||
.name = "ad7877",
|
||||
.bus = &spi_bus_type,
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &ad7877_pm,
|
||||
},
|
||||
|
||||
@@ -16,30 +16,6 @@
|
||||
|
||||
#define AD7879_DEVID 0x79 /* AD7879-1/AD7889-1 */
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int ad7879_i2c_suspend(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct ad7879 *ts = i2c_get_clientdata(client);
|
||||
|
||||
ad7879_suspend(ts);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ad7879_i2c_resume(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct ad7879 *ts = i2c_get_clientdata(client);
|
||||
|
||||
ad7879_resume(ts);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(ad7879_i2c_pm, ad7879_i2c_suspend, ad7879_i2c_resume);
|
||||
|
||||
/* All registers are word-sized.
|
||||
* AD7879 uses a high-byte first convention.
|
||||
*/
|
||||
@@ -47,7 +23,7 @@ static int ad7879_i2c_read(struct device *dev, u8 reg)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
|
||||
return swab16(i2c_smbus_read_word_data(client, reg));
|
||||
return i2c_smbus_read_word_swapped(client, reg);
|
||||
}
|
||||
|
||||
static int ad7879_i2c_multi_read(struct device *dev,
|
||||
@@ -68,7 +44,7 @@ static int ad7879_i2c_write(struct device *dev, u8 reg, u16 val)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
|
||||
return i2c_smbus_write_word_data(client, reg, swab16(val));
|
||||
return i2c_smbus_write_word_swapped(client, reg, val);
|
||||
}
|
||||
|
||||
static const struct ad7879_bus_ops ad7879_i2c_bus_ops = {
|
||||
@@ -119,7 +95,7 @@ static struct i2c_driver ad7879_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "ad7879",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &ad7879_i2c_pm,
|
||||
.pm = &ad7879_pm_ops,
|
||||
},
|
||||
.probe = ad7879_i2c_probe,
|
||||
.remove = __devexit_p(ad7879_i2c_remove),
|
||||
@@ -141,4 +117,3 @@ module_exit(ad7879_i2c_exit);
|
||||
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
|
||||
MODULE_DESCRIPTION("AD7879(-1) touchscreen I2C bus driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("i2c:ad7879");
|
||||
|
||||
@@ -22,30 +22,6 @@
|
||||
#define AD7879_WRITECMD(reg) (AD7879_CMD(reg))
|
||||
#define AD7879_READCMD(reg) (AD7879_CMD(reg) | AD7879_CMD_READ)
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int ad7879_spi_suspend(struct device *dev)
|
||||
{
|
||||
struct spi_device *spi = to_spi_device(dev);
|
||||
struct ad7879 *ts = spi_get_drvdata(spi);
|
||||
|
||||
ad7879_suspend(ts);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ad7879_spi_resume(struct device *dev)
|
||||
{
|
||||
struct spi_device *spi = to_spi_device(dev);
|
||||
struct ad7879 *ts = spi_get_drvdata(spi);
|
||||
|
||||
ad7879_resume(ts);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(ad7879_spi_pm, ad7879_spi_suspend, ad7879_spi_resume);
|
||||
|
||||
/*
|
||||
* ad7879_read/write are only used for initial setup and for sysfs controls.
|
||||
* The main traffic is done in ad7879_collect().
|
||||
@@ -174,9 +150,8 @@ static int __devexit ad7879_spi_remove(struct spi_device *spi)
|
||||
static struct spi_driver ad7879_spi_driver = {
|
||||
.driver = {
|
||||
.name = "ad7879",
|
||||
.bus = &spi_bus_type,
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &ad7879_spi_pm,
|
||||
.pm = &ad7879_pm_ops,
|
||||
},
|
||||
.probe = ad7879_spi_probe,
|
||||
.remove = __devexit_p(ad7879_spi_remove),
|
||||
|
||||
@@ -281,8 +281,11 @@ static void ad7879_close(struct input_dev* input)
|
||||
__ad7879_disable(ts);
|
||||
}
|
||||
|
||||
void ad7879_suspend(struct ad7879 *ts)
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int ad7879_suspend(struct device *dev)
|
||||
{
|
||||
struct ad7879 *ts = dev_get_drvdata(dev);
|
||||
|
||||
mutex_lock(&ts->input->mutex);
|
||||
|
||||
if (!ts->suspended && !ts->disabled && ts->input->users)
|
||||
@@ -291,11 +294,14 @@ void ad7879_suspend(struct ad7879 *ts)
|
||||
ts->suspended = true;
|
||||
|
||||
mutex_unlock(&ts->input->mutex);
|
||||
}
|
||||
EXPORT_SYMBOL(ad7879_suspend);
|
||||
|
||||
void ad7879_resume(struct ad7879 *ts)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ad7879_resume(struct device *dev)
|
||||
{
|
||||
struct ad7879 *ts = dev_get_drvdata(dev);
|
||||
|
||||
mutex_lock(&ts->input->mutex);
|
||||
|
||||
if (ts->suspended && !ts->disabled && ts->input->users)
|
||||
@@ -304,8 +310,13 @@ void ad7879_resume(struct ad7879 *ts)
|
||||
ts->suspended = false;
|
||||
|
||||
mutex_unlock(&ts->input->mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(ad7879_resume);
|
||||
#endif
|
||||
|
||||
SIMPLE_DEV_PM_OPS(ad7879_pm_ops, ad7879_suspend, ad7879_resume);
|
||||
EXPORT_SYMBOL(ad7879_pm_ops);
|
||||
|
||||
static void ad7879_toggle(struct ad7879 *ts, bool disable)
|
||||
{
|
||||
@@ -340,10 +351,10 @@ static ssize_t ad7879_disable_store(struct device *dev,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct ad7879 *ts = dev_get_drvdata(dev);
|
||||
unsigned long val;
|
||||
unsigned int val;
|
||||
int error;
|
||||
|
||||
error = strict_strtoul(buf, 10, &val);
|
||||
error = kstrtouint(buf, 10, &val);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
|
||||
@@ -21,8 +21,8 @@ struct ad7879_bus_ops {
|
||||
int (*write)(struct device *dev, u8 reg, u16 val);
|
||||
};
|
||||
|
||||
void ad7879_suspend(struct ad7879 *);
|
||||
void ad7879_resume(struct ad7879 *);
|
||||
extern const struct dev_pm_ops ad7879_pm_ops;
|
||||
|
||||
struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned irq,
|
||||
const struct ad7879_bus_ops *bops);
|
||||
void ad7879_remove(struct ad7879 *);
|
||||
|
||||
@@ -602,10 +602,12 @@ static ssize_t ads7846_disable_store(struct device *dev,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct ads7846 *ts = dev_get_drvdata(dev);
|
||||
unsigned long i;
|
||||
unsigned int i;
|
||||
int err;
|
||||
|
||||
if (strict_strtoul(buf, 10, &i))
|
||||
return -EINVAL;
|
||||
err = kstrtouint(buf, 10, &i);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (i)
|
||||
ads7846_disable(ts);
|
||||
@@ -1424,7 +1426,6 @@ static int __devexit ads7846_remove(struct spi_device *spi)
|
||||
static struct spi_driver ads7846_driver = {
|
||||
.driver = {
|
||||
.name = "ads7846",
|
||||
.bus = &spi_bus_type,
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &ads7846_pm,
|
||||
},
|
||||
|
||||
@@ -429,18 +429,7 @@ static struct platform_driver atmel_wm97xx_driver = {
|
||||
.suspend = atmel_wm97xx_suspend,
|
||||
.resume = atmel_wm97xx_resume,
|
||||
};
|
||||
|
||||
static int __init atmel_wm97xx_init(void)
|
||||
{
|
||||
return platform_driver_probe(&atmel_wm97xx_driver, atmel_wm97xx_probe);
|
||||
}
|
||||
module_init(atmel_wm97xx_init);
|
||||
|
||||
static void __exit atmel_wm97xx_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&atmel_wm97xx_driver);
|
||||
}
|
||||
module_exit(atmel_wm97xx_exit);
|
||||
module_platform_driver(atmel_wm97xx_driver);
|
||||
|
||||
MODULE_AUTHOR("Hans-Christian Egtvedt <egtvedt@samfundet.no>");
|
||||
MODULE_DESCRIPTION("wm97xx continuous touch driver for Atmel AT91 and AVR32");
|
||||
|
||||
@@ -351,20 +351,7 @@ static struct platform_driver atmel_tsadcc_driver = {
|
||||
.name = "atmel_tsadcc",
|
||||
},
|
||||
};
|
||||
|
||||
static int __init atmel_tsadcc_init(void)
|
||||
{
|
||||
return platform_driver_register(&atmel_tsadcc_driver);
|
||||
}
|
||||
|
||||
static void __exit atmel_tsadcc_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&atmel_tsadcc_driver);
|
||||
}
|
||||
|
||||
module_init(atmel_tsadcc_init);
|
||||
module_exit(atmel_tsadcc_exit);
|
||||
|
||||
module_platform_driver(atmel_tsadcc_driver);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("Atmel TouchScreen Driver");
|
||||
|
||||
@@ -0,0 +1,652 @@
|
||||
/*
|
||||
* Driver for AUO in-cell touchscreens
|
||||
*
|
||||
* Copyright (c) 2011 Heiko Stuebner <heiko@sntech.de>
|
||||
*
|
||||
* loosely based on auo_touch.c from Dell Streak vendor-kernel
|
||||
*
|
||||
* Copyright (c) 2008 QUALCOMM Incorporated.
|
||||
* Copyright (c) 2008 QUALCOMM USA, INC.
|
||||
*
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* 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 <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/input/auo-pixcir-ts.h>
|
||||
|
||||
/*
|
||||
* Coordinate calculation:
|
||||
* X1 = X1_LSB + X1_MSB*256
|
||||
* Y1 = Y1_LSB + Y1_MSB*256
|
||||
* X2 = X2_LSB + X2_MSB*256
|
||||
* Y2 = Y2_LSB + Y2_MSB*256
|
||||
*/
|
||||
#define AUO_PIXCIR_REG_X1_LSB 0x00
|
||||
#define AUO_PIXCIR_REG_X1_MSB 0x01
|
||||
#define AUO_PIXCIR_REG_Y1_LSB 0x02
|
||||
#define AUO_PIXCIR_REG_Y1_MSB 0x03
|
||||
#define AUO_PIXCIR_REG_X2_LSB 0x04
|
||||
#define AUO_PIXCIR_REG_X2_MSB 0x05
|
||||
#define AUO_PIXCIR_REG_Y2_LSB 0x06
|
||||
#define AUO_PIXCIR_REG_Y2_MSB 0x07
|
||||
|
||||
#define AUO_PIXCIR_REG_STRENGTH 0x0d
|
||||
#define AUO_PIXCIR_REG_STRENGTH_X1_LSB 0x0e
|
||||
#define AUO_PIXCIR_REG_STRENGTH_X1_MSB 0x0f
|
||||
|
||||
#define AUO_PIXCIR_REG_RAW_DATA_X 0x2b
|
||||
#define AUO_PIXCIR_REG_RAW_DATA_Y 0x4f
|
||||
|
||||
#define AUO_PIXCIR_REG_X_SENSITIVITY 0x6f
|
||||
#define AUO_PIXCIR_REG_Y_SENSITIVITY 0x70
|
||||
#define AUO_PIXCIR_REG_INT_SETTING 0x71
|
||||
#define AUO_PIXCIR_REG_INT_WIDTH 0x72
|
||||
#define AUO_PIXCIR_REG_POWER_MODE 0x73
|
||||
|
||||
#define AUO_PIXCIR_REG_VERSION 0x77
|
||||
#define AUO_PIXCIR_REG_CALIBRATE 0x78
|
||||
|
||||
#define AUO_PIXCIR_REG_TOUCHAREA_X1 0x1e
|
||||
#define AUO_PIXCIR_REG_TOUCHAREA_Y1 0x1f
|
||||
#define AUO_PIXCIR_REG_TOUCHAREA_X2 0x20
|
||||
#define AUO_PIXCIR_REG_TOUCHAREA_Y2 0x21
|
||||
|
||||
#define AUO_PIXCIR_REG_EEPROM_CALIB_X 0x42
|
||||
#define AUO_PIXCIR_REG_EEPROM_CALIB_Y 0xad
|
||||
|
||||
#define AUO_PIXCIR_INT_TPNUM_MASK 0xe0
|
||||
#define AUO_PIXCIR_INT_TPNUM_SHIFT 5
|
||||
#define AUO_PIXCIR_INT_RELEASE (1 << 4)
|
||||
#define AUO_PIXCIR_INT_ENABLE (1 << 3)
|
||||
#define AUO_PIXCIR_INT_POL_HIGH (1 << 2)
|
||||
#define AUO_PIXCIR_INT_MODE_MASK 0x03
|
||||
|
||||
/*
|
||||
* Power modes:
|
||||
* active: scan speed 60Hz
|
||||
* sleep: scan speed 10Hz can be auto-activated, wakeup on 1st touch
|
||||
* deep sleep: scan speed 1Hz can only be entered or left manually.
|
||||
*/
|
||||
#define AUO_PIXCIR_POWER_ACTIVE 0x00
|
||||
#define AUO_PIXCIR_POWER_SLEEP 0x01
|
||||
#define AUO_PIXCIR_POWER_DEEP_SLEEP 0x02
|
||||
#define AUO_PIXCIR_POWER_MASK 0x03
|
||||
|
||||
#define AUO_PIXCIR_POWER_ALLOW_SLEEP (1 << 2)
|
||||
#define AUO_PIXCIR_POWER_IDLE_TIME(ms) ((ms & 0xf) << 4)
|
||||
|
||||
#define AUO_PIXCIR_CALIBRATE 0x03
|
||||
|
||||
#define AUO_PIXCIR_EEPROM_CALIB_X_LEN 62
|
||||
#define AUO_PIXCIR_EEPROM_CALIB_Y_LEN 36
|
||||
|
||||
#define AUO_PIXCIR_RAW_DATA_X_LEN 18
|
||||
#define AUO_PIXCIR_RAW_DATA_Y_LEN 11
|
||||
|
||||
#define AUO_PIXCIR_STRENGTH_ENABLE (1 << 0)
|
||||
|
||||
/* Touchscreen absolute values */
|
||||
#define AUO_PIXCIR_REPORT_POINTS 2
|
||||
#define AUO_PIXCIR_MAX_AREA 0xff
|
||||
#define AUO_PIXCIR_PENUP_TIMEOUT_MS 10
|
||||
|
||||
struct auo_pixcir_ts {
|
||||
struct i2c_client *client;
|
||||
struct input_dev *input;
|
||||
char phys[32];
|
||||
|
||||
/* special handling for touch_indicate interupt mode */
|
||||
bool touch_ind_mode;
|
||||
|
||||
wait_queue_head_t wait;
|
||||
bool stopped;
|
||||
};
|
||||
|
||||
struct auo_point_t {
|
||||
int coord_x;
|
||||
int coord_y;
|
||||
int area_major;
|
||||
int area_minor;
|
||||
int orientation;
|
||||
};
|
||||
|
||||
static int auo_pixcir_collect_data(struct auo_pixcir_ts *ts,
|
||||
struct auo_point_t *point)
|
||||
{
|
||||
struct i2c_client *client = ts->client;
|
||||
const struct auo_pixcir_ts_platdata *pdata = client->dev.platform_data;
|
||||
uint8_t raw_coord[8];
|
||||
uint8_t raw_area[4];
|
||||
int i, ret;
|
||||
|
||||
/* touch coordinates */
|
||||
ret = i2c_smbus_read_i2c_block_data(client, AUO_PIXCIR_REG_X1_LSB,
|
||||
8, raw_coord);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "failed to read coordinate, %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* touch area */
|
||||
ret = i2c_smbus_read_i2c_block_data(client, AUO_PIXCIR_REG_TOUCHAREA_X1,
|
||||
4, raw_area);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "could not read touch area, %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (i = 0; i < AUO_PIXCIR_REPORT_POINTS; i++) {
|
||||
point[i].coord_x =
|
||||
raw_coord[4 * i + 1] << 8 | raw_coord[4 * i];
|
||||
point[i].coord_y =
|
||||
raw_coord[4 * i + 3] << 8 | raw_coord[4 * i + 2];
|
||||
|
||||
if (point[i].coord_x > pdata->x_max ||
|
||||
point[i].coord_y > pdata->y_max) {
|
||||
dev_warn(&client->dev, "coordinates (%d,%d) invalid\n",
|
||||
point[i].coord_x, point[i].coord_y);
|
||||
point[i].coord_x = point[i].coord_y = 0;
|
||||
}
|
||||
|
||||
/* determine touch major, minor and orientation */
|
||||
point[i].area_major = max(raw_area[2 * i], raw_area[2 * i + 1]);
|
||||
point[i].area_minor = min(raw_area[2 * i], raw_area[2 * i + 1]);
|
||||
point[i].orientation = raw_area[2 * i] > raw_area[2 * i + 1];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t auo_pixcir_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct auo_pixcir_ts *ts = dev_id;
|
||||
struct i2c_client *client = ts->client;
|
||||
const struct auo_pixcir_ts_platdata *pdata = client->dev.platform_data;
|
||||
struct auo_point_t point[AUO_PIXCIR_REPORT_POINTS];
|
||||
int i;
|
||||
int ret;
|
||||
int fingers = 0;
|
||||
int abs = -1;
|
||||
|
||||
while (!ts->stopped) {
|
||||
|
||||
/* check for up event in touch touch_ind_mode */
|
||||
if (ts->touch_ind_mode) {
|
||||
if (gpio_get_value(pdata->gpio_int) == 0) {
|
||||
input_mt_sync(ts->input);
|
||||
input_report_key(ts->input, BTN_TOUCH, 0);
|
||||
input_sync(ts->input);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ret = auo_pixcir_collect_data(ts, point);
|
||||
if (ret < 0) {
|
||||
/* we want to loop only in touch_ind_mode */
|
||||
if (!ts->touch_ind_mode)
|
||||
break;
|
||||
|
||||
wait_event_timeout(ts->wait, ts->stopped,
|
||||
msecs_to_jiffies(AUO_PIXCIR_PENUP_TIMEOUT_MS));
|
||||
continue;
|
||||
}
|
||||
|
||||
for (i = 0; i < AUO_PIXCIR_REPORT_POINTS; i++) {
|
||||
if (point[i].coord_x > 0 || point[i].coord_y > 0) {
|
||||
input_report_abs(ts->input, ABS_MT_POSITION_X,
|
||||
point[i].coord_x);
|
||||
input_report_abs(ts->input, ABS_MT_POSITION_Y,
|
||||
point[i].coord_y);
|
||||
input_report_abs(ts->input, ABS_MT_TOUCH_MAJOR,
|
||||
point[i].area_major);
|
||||
input_report_abs(ts->input, ABS_MT_TOUCH_MINOR,
|
||||
point[i].area_minor);
|
||||
input_report_abs(ts->input, ABS_MT_ORIENTATION,
|
||||
point[i].orientation);
|
||||
input_mt_sync(ts->input);
|
||||
|
||||
/* use first finger as source for singletouch */
|
||||
if (fingers == 0)
|
||||
abs = i;
|
||||
|
||||
/* number of touch points could also be queried
|
||||
* via i2c but would require an additional call
|
||||
*/
|
||||
fingers++;
|
||||
}
|
||||
}
|
||||
|
||||
input_report_key(ts->input, BTN_TOUCH, fingers > 0);
|
||||
|
||||
if (abs > -1) {
|
||||
input_report_abs(ts->input, ABS_X, point[abs].coord_x);
|
||||
input_report_abs(ts->input, ABS_Y, point[abs].coord_y);
|
||||
}
|
||||
|
||||
input_sync(ts->input);
|
||||
|
||||
/* we want to loop only in touch_ind_mode */
|
||||
if (!ts->touch_ind_mode)
|
||||
break;
|
||||
|
||||
wait_event_timeout(ts->wait, ts->stopped,
|
||||
msecs_to_jiffies(AUO_PIXCIR_PENUP_TIMEOUT_MS));
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the power mode of the device.
|
||||
* Valid modes are
|
||||
* - AUO_PIXCIR_POWER_ACTIVE
|
||||
* - AUO_PIXCIR_POWER_SLEEP - automatically left on first touch
|
||||
* - AUO_PIXCIR_POWER_DEEP_SLEEP
|
||||
*/
|
||||
static int auo_pixcir_power_mode(struct auo_pixcir_ts *ts, int mode)
|
||||
{
|
||||
struct i2c_client *client = ts->client;
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_read_byte_data(client, AUO_PIXCIR_REG_POWER_MODE);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "unable to read reg %Xh, %d\n",
|
||||
AUO_PIXCIR_REG_POWER_MODE, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret &= ~AUO_PIXCIR_POWER_MASK;
|
||||
ret |= mode;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(client, AUO_PIXCIR_REG_POWER_MODE, ret);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "unable to write reg %Xh, %d\n",
|
||||
AUO_PIXCIR_REG_POWER_MODE, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static __devinit int auo_pixcir_int_config(struct auo_pixcir_ts *ts,
|
||||
int int_setting)
|
||||
{
|
||||
struct i2c_client *client = ts->client;
|
||||
struct auo_pixcir_ts_platdata *pdata = client->dev.platform_data;
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_read_byte_data(client, AUO_PIXCIR_REG_INT_SETTING);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "unable to read reg %Xh, %d\n",
|
||||
AUO_PIXCIR_REG_INT_SETTING, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret &= ~AUO_PIXCIR_INT_MODE_MASK;
|
||||
ret |= int_setting;
|
||||
ret |= AUO_PIXCIR_INT_POL_HIGH; /* always use high for interrupts */
|
||||
|
||||
ret = i2c_smbus_write_byte_data(client, AUO_PIXCIR_REG_INT_SETTING,
|
||||
ret);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "unable to write reg %Xh, %d\n",
|
||||
AUO_PIXCIR_REG_INT_SETTING, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ts->touch_ind_mode = pdata->int_setting == AUO_PIXCIR_INT_TOUCH_IND;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* control the generation of interrupts on the device side */
|
||||
static int auo_pixcir_int_toggle(struct auo_pixcir_ts *ts, bool enable)
|
||||
{
|
||||
struct i2c_client *client = ts->client;
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_read_byte_data(client, AUO_PIXCIR_REG_INT_SETTING);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "unable to read reg %Xh, %d\n",
|
||||
AUO_PIXCIR_REG_INT_SETTING, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (enable)
|
||||
ret |= AUO_PIXCIR_INT_ENABLE;
|
||||
else
|
||||
ret &= ~AUO_PIXCIR_INT_ENABLE;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(client, AUO_PIXCIR_REG_INT_SETTING,
|
||||
ret);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "unable to write reg %Xh, %d\n",
|
||||
AUO_PIXCIR_REG_INT_SETTING, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int auo_pixcir_start(struct auo_pixcir_ts *ts)
|
||||
{
|
||||
struct i2c_client *client = ts->client;
|
||||
int ret;
|
||||
|
||||
ret = auo_pixcir_power_mode(ts, AUO_PIXCIR_POWER_ACTIVE);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "could not set power mode, %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ts->stopped = false;
|
||||
mb();
|
||||
enable_irq(client->irq);
|
||||
|
||||
ret = auo_pixcir_int_toggle(ts, 1);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "could not enable interrupt, %d\n",
|
||||
ret);
|
||||
disable_irq(client->irq);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int auo_pixcir_stop(struct auo_pixcir_ts *ts)
|
||||
{
|
||||
struct i2c_client *client = ts->client;
|
||||
int ret;
|
||||
|
||||
ret = auo_pixcir_int_toggle(ts, 0);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "could not disable interrupt, %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* disable receiving of interrupts */
|
||||
disable_irq(client->irq);
|
||||
ts->stopped = true;
|
||||
mb();
|
||||
wake_up(&ts->wait);
|
||||
|
||||
return auo_pixcir_power_mode(ts, AUO_PIXCIR_POWER_DEEP_SLEEP);
|
||||
}
|
||||
|
||||
static int auo_pixcir_input_open(struct input_dev *dev)
|
||||
{
|
||||
struct auo_pixcir_ts *ts = input_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
ret = auo_pixcir_start(ts);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void auo_pixcir_input_close(struct input_dev *dev)
|
||||
{
|
||||
struct auo_pixcir_ts *ts = input_get_drvdata(dev);
|
||||
|
||||
auo_pixcir_stop(ts);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int auo_pixcir_suspend(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct auo_pixcir_ts *ts = i2c_get_clientdata(client);
|
||||
struct input_dev *input = ts->input;
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&input->mutex);
|
||||
|
||||
/* when configured as wakeup source, device should always wake system
|
||||
* therefore start device if necessary
|
||||
*/
|
||||
if (device_may_wakeup(&client->dev)) {
|
||||
/* need to start device if not open, to be wakeup source */
|
||||
if (!input->users) {
|
||||
ret = auo_pixcir_start(ts);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
enable_irq_wake(client->irq);
|
||||
ret = auo_pixcir_power_mode(ts, AUO_PIXCIR_POWER_SLEEP);
|
||||
} else if (input->users) {
|
||||
ret = auo_pixcir_stop(ts);
|
||||
}
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&input->mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int auo_pixcir_resume(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct auo_pixcir_ts *ts = i2c_get_clientdata(client);
|
||||
struct input_dev *input = ts->input;
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&input->mutex);
|
||||
|
||||
if (device_may_wakeup(&client->dev)) {
|
||||
disable_irq_wake(client->irq);
|
||||
|
||||
/* need to stop device if it was not open on suspend */
|
||||
if (!input->users) {
|
||||
ret = auo_pixcir_stop(ts);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
/* device wakes automatically from SLEEP */
|
||||
} else if (input->users) {
|
||||
ret = auo_pixcir_start(ts);
|
||||
}
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&input->mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(auo_pixcir_pm_ops, auo_pixcir_suspend,
|
||||
auo_pixcir_resume);
|
||||
|
||||
static int __devinit auo_pixcir_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
const struct auo_pixcir_ts_platdata *pdata = client->dev.platform_data;
|
||||
struct auo_pixcir_ts *ts;
|
||||
struct input_dev *input_dev;
|
||||
int ret;
|
||||
|
||||
if (!pdata)
|
||||
return -EINVAL;
|
||||
|
||||
ts = kzalloc(sizeof(struct auo_pixcir_ts), GFP_KERNEL);
|
||||
if (!ts)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = gpio_request(pdata->gpio_int, "auo_pixcir_ts_int");
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "request of gpio %d failed, %d\n",
|
||||
pdata->gpio_int, ret);
|
||||
goto err_gpio_int;
|
||||
}
|
||||
|
||||
if (pdata->init_hw)
|
||||
pdata->init_hw(client);
|
||||
|
||||
ts->client = client;
|
||||
ts->touch_ind_mode = 0;
|
||||
init_waitqueue_head(&ts->wait);
|
||||
|
||||
snprintf(ts->phys, sizeof(ts->phys),
|
||||
"%s/input0", dev_name(&client->dev));
|
||||
|
||||
input_dev = input_allocate_device();
|
||||
if (!input_dev) {
|
||||
dev_err(&client->dev, "could not allocate input device\n");
|
||||
goto err_input_alloc;
|
||||
}
|
||||
|
||||
ts->input = input_dev;
|
||||
|
||||
input_dev->name = "AUO-Pixcir touchscreen";
|
||||
input_dev->phys = ts->phys;
|
||||
input_dev->id.bustype = BUS_I2C;
|
||||
input_dev->dev.parent = &client->dev;
|
||||
|
||||
input_dev->open = auo_pixcir_input_open;
|
||||
input_dev->close = auo_pixcir_input_close;
|
||||
|
||||
__set_bit(EV_ABS, input_dev->evbit);
|
||||
__set_bit(EV_KEY, input_dev->evbit);
|
||||
|
||||
__set_bit(BTN_TOUCH, input_dev->keybit);
|
||||
|
||||
/* For single touch */
|
||||
input_set_abs_params(input_dev, ABS_X, 0, pdata->x_max, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_Y, 0, pdata->y_max, 0, 0);
|
||||
|
||||
/* For multi touch */
|
||||
input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0,
|
||||
pdata->x_max, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0,
|
||||
pdata->y_max, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0,
|
||||
AUO_PIXCIR_MAX_AREA, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_MT_TOUCH_MINOR, 0,
|
||||
AUO_PIXCIR_MAX_AREA, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_MT_ORIENTATION, 0, 1, 0, 0);
|
||||
|
||||
ret = i2c_smbus_read_byte_data(client, AUO_PIXCIR_REG_VERSION);
|
||||
if (ret < 0)
|
||||
goto err_fw_vers;
|
||||
dev_info(&client->dev, "firmware version 0x%X\n", ret);
|
||||
|
||||
ret = auo_pixcir_int_config(ts, pdata->int_setting);
|
||||
if (ret)
|
||||
goto err_fw_vers;
|
||||
|
||||
input_set_drvdata(ts->input, ts);
|
||||
ts->stopped = true;
|
||||
|
||||
ret = request_threaded_irq(client->irq, NULL, auo_pixcir_interrupt,
|
||||
IRQF_TRIGGER_RISING | IRQF_ONESHOT,
|
||||
input_dev->name, ts);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "irq %d requested failed\n", client->irq);
|
||||
goto err_fw_vers;
|
||||
}
|
||||
|
||||
/* stop device and put it into deep sleep until it is opened */
|
||||
ret = auo_pixcir_stop(ts);
|
||||
if (ret < 0)
|
||||
goto err_input_register;
|
||||
|
||||
ret = input_register_device(input_dev);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "could not register input device\n");
|
||||
goto err_input_register;
|
||||
}
|
||||
|
||||
i2c_set_clientdata(client, ts);
|
||||
|
||||
return 0;
|
||||
|
||||
err_input_register:
|
||||
free_irq(client->irq, ts);
|
||||
err_fw_vers:
|
||||
input_free_device(input_dev);
|
||||
err_input_alloc:
|
||||
if (pdata->exit_hw)
|
||||
pdata->exit_hw(client);
|
||||
gpio_free(pdata->gpio_int);
|
||||
err_gpio_int:
|
||||
kfree(ts);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devexit auo_pixcir_remove(struct i2c_client *client)
|
||||
{
|
||||
struct auo_pixcir_ts *ts = i2c_get_clientdata(client);
|
||||
const struct auo_pixcir_ts_platdata *pdata = client->dev.platform_data;
|
||||
|
||||
free_irq(client->irq, ts);
|
||||
|
||||
input_unregister_device(ts->input);
|
||||
|
||||
if (pdata->exit_hw)
|
||||
pdata->exit_hw(client);
|
||||
|
||||
gpio_free(pdata->gpio_int);
|
||||
|
||||
kfree(ts);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id auo_pixcir_idtable[] = {
|
||||
{ "auo_pixcir_ts", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, auo_pixcir_idtable);
|
||||
|
||||
static struct i2c_driver auo_pixcir_driver = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "auo_pixcir_ts",
|
||||
.pm = &auo_pixcir_pm_ops,
|
||||
},
|
||||
.probe = auo_pixcir_probe,
|
||||
.remove = __devexit_p(auo_pixcir_remove),
|
||||
.id_table = auo_pixcir_idtable,
|
||||
};
|
||||
|
||||
static int __init auo_pixcir_init(void)
|
||||
{
|
||||
return i2c_add_driver(&auo_pixcir_driver);
|
||||
}
|
||||
module_init(auo_pixcir_init);
|
||||
|
||||
static void __exit auo_pixcir_exit(void)
|
||||
{
|
||||
i2c_del_driver(&auo_pixcir_driver);
|
||||
}
|
||||
module_exit(auo_pixcir_exit);
|
||||
|
||||
MODULE_DESCRIPTION("AUO-PIXCIR touchscreen driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_AUTHOR("Heiko Stuebner <heiko@sntech.de>");
|
||||
@@ -379,18 +379,7 @@ static struct platform_driver da9034_touch_driver = {
|
||||
.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_platform_driver(da9034_touch_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Touchscreen driver for Dialog Semiconductor DA9034");
|
||||
MODULE_AUTHOR("Eric Miao <eric.miao@marvell.com>, Bin Yang <bin.yang@marvell.com>");
|
||||
|
||||
@@ -0,0 +1,303 @@
|
||||
/*
|
||||
* Driver for EETI eGalax Multiple Touch Controller
|
||||
*
|
||||
* Copyright (C) 2011 Freescale Semiconductor, Inc.
|
||||
*
|
||||
* based on max11801_ts.c
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* EETI eGalax serial touch screen controller is a I2C based multiple
|
||||
* touch screen controller, it supports 5 point multiple touch. */
|
||||
|
||||
/* TODO:
|
||||
- auto idle mode support
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/input/mt.h>
|
||||
|
||||
/*
|
||||
* Mouse Mode: some panel may configure the controller to mouse mode,
|
||||
* which can only report one point at a given time.
|
||||
* This driver will ignore events in this mode.
|
||||
*/
|
||||
#define REPORT_MODE_MOUSE 0x1
|
||||
/*
|
||||
* Vendor Mode: this mode is used to transfer some vendor specific
|
||||
* messages.
|
||||
* This driver will ignore events in this mode.
|
||||
*/
|
||||
#define REPORT_MODE_VENDOR 0x3
|
||||
/* Multiple Touch Mode */
|
||||
#define REPORT_MODE_MTTOUCH 0x4
|
||||
|
||||
#define MAX_SUPPORT_POINTS 5
|
||||
|
||||
#define EVENT_VALID_OFFSET 7
|
||||
#define EVENT_VALID_MASK (0x1 << EVENT_VALID_OFFSET)
|
||||
#define EVENT_ID_OFFSET 2
|
||||
#define EVENT_ID_MASK (0xf << EVENT_ID_OFFSET)
|
||||
#define EVENT_IN_RANGE (0x1 << 1)
|
||||
#define EVENT_DOWN_UP (0X1 << 0)
|
||||
|
||||
#define MAX_I2C_DATA_LEN 10
|
||||
|
||||
#define EGALAX_MAX_X 32760
|
||||
#define EGALAX_MAX_Y 32760
|
||||
#define EGALAX_MAX_TRIES 100
|
||||
|
||||
struct egalax_ts {
|
||||
struct i2c_client *client;
|
||||
struct input_dev *input_dev;
|
||||
};
|
||||
|
||||
static irqreturn_t egalax_ts_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct egalax_ts *ts = dev_id;
|
||||
struct input_dev *input_dev = ts->input_dev;
|
||||
struct i2c_client *client = ts->client;
|
||||
u8 buf[MAX_I2C_DATA_LEN];
|
||||
int id, ret, x, y, z;
|
||||
int tries = 0;
|
||||
bool down, valid;
|
||||
u8 state;
|
||||
|
||||
do {
|
||||
ret = i2c_master_recv(client, buf, MAX_I2C_DATA_LEN);
|
||||
} while (ret == -EAGAIN && tries++ < EGALAX_MAX_TRIES);
|
||||
|
||||
if (ret < 0)
|
||||
return IRQ_HANDLED;
|
||||
|
||||
if (buf[0] != REPORT_MODE_MTTOUCH) {
|
||||
/* ignore mouse events and vendor events */
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
state = buf[1];
|
||||
x = (buf[3] << 8) | buf[2];
|
||||
y = (buf[5] << 8) | buf[4];
|
||||
z = (buf[7] << 8) | buf[6];
|
||||
|
||||
valid = state & EVENT_VALID_MASK;
|
||||
id = (state & EVENT_ID_MASK) >> EVENT_ID_OFFSET;
|
||||
down = state & EVENT_DOWN_UP;
|
||||
|
||||
if (!valid || id > MAX_SUPPORT_POINTS) {
|
||||
dev_dbg(&client->dev, "point invalid\n");
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
input_mt_slot(input_dev, id);
|
||||
input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, down);
|
||||
|
||||
dev_dbg(&client->dev, "%s id:%d x:%d y:%d z:%d",
|
||||
down ? "down" : "up", id, x, y, z);
|
||||
|
||||
if (down) {
|
||||
input_report_abs(input_dev, ABS_MT_POSITION_X, x);
|
||||
input_report_abs(input_dev, ABS_MT_POSITION_Y, y);
|
||||
input_report_abs(input_dev, ABS_MT_PRESSURE, z);
|
||||
}
|
||||
|
||||
input_mt_report_pointer_emulation(input_dev, true);
|
||||
input_sync(input_dev);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/* wake up controller by an falling edge of interrupt gpio. */
|
||||
static int egalax_wake_up_device(struct i2c_client *client)
|
||||
{
|
||||
int gpio = irq_to_gpio(client->irq);
|
||||
int ret;
|
||||
|
||||
ret = gpio_request(gpio, "egalax_irq");
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev,
|
||||
"request gpio failed, cannot wake up controller: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* wake up controller via an falling edge on IRQ gpio. */
|
||||
gpio_direction_output(gpio, 0);
|
||||
gpio_set_value(gpio, 1);
|
||||
|
||||
/* controller should be waken up, return irq. */
|
||||
gpio_direction_input(gpio);
|
||||
gpio_free(gpio);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __devinit egalax_firmware_version(struct i2c_client *client)
|
||||
{
|
||||
static const u8 cmd[MAX_I2C_DATA_LEN] = { 0x03, 0x03, 0xa, 0x01, 0x41 };
|
||||
int ret;
|
||||
|
||||
ret = i2c_master_send(client, cmd, MAX_I2C_DATA_LEN);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __devinit egalax_ts_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct egalax_ts *ts;
|
||||
struct input_dev *input_dev;
|
||||
int ret;
|
||||
int error;
|
||||
|
||||
ts = kzalloc(sizeof(struct egalax_ts), GFP_KERNEL);
|
||||
if (!ts) {
|
||||
dev_err(&client->dev, "Failed to allocate memory\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
input_dev = input_allocate_device();
|
||||
if (!input_dev) {
|
||||
dev_err(&client->dev, "Failed to allocate memory\n");
|
||||
error = -ENOMEM;
|
||||
goto err_free_ts;
|
||||
}
|
||||
|
||||
ts->client = client;
|
||||
ts->input_dev = input_dev;
|
||||
|
||||
/* controller may be in sleep, wake it up. */
|
||||
egalax_wake_up_device(client);
|
||||
|
||||
ret = egalax_firmware_version(client);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "Failed to read firmware version\n");
|
||||
error = -EIO;
|
||||
goto err_free_dev;
|
||||
}
|
||||
|
||||
input_dev->name = "EETI eGalax Touch Screen";
|
||||
input_dev->id.bustype = BUS_I2C;
|
||||
input_dev->dev.parent = &client->dev;
|
||||
|
||||
__set_bit(EV_ABS, input_dev->evbit);
|
||||
__set_bit(EV_KEY, input_dev->evbit);
|
||||
__set_bit(BTN_TOUCH, input_dev->keybit);
|
||||
|
||||
input_set_abs_params(input_dev, ABS_X, 0, EGALAX_MAX_X, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_Y, 0, EGALAX_MAX_Y, 0, 0);
|
||||
input_set_abs_params(input_dev,
|
||||
ABS_MT_POSITION_X, 0, EGALAX_MAX_X, 0, 0);
|
||||
input_set_abs_params(input_dev,
|
||||
ABS_MT_POSITION_X, 0, EGALAX_MAX_Y, 0, 0);
|
||||
input_mt_init_slots(input_dev, MAX_SUPPORT_POINTS);
|
||||
|
||||
input_set_drvdata(input_dev, ts);
|
||||
|
||||
error = request_threaded_irq(client->irq, NULL, egalax_ts_interrupt,
|
||||
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
|
||||
"egalax_ts", ts);
|
||||
if (error < 0) {
|
||||
dev_err(&client->dev, "Failed to register interrupt\n");
|
||||
goto err_free_dev;
|
||||
}
|
||||
|
||||
error = input_register_device(ts->input_dev);
|
||||
if (error)
|
||||
goto err_free_irq;
|
||||
|
||||
i2c_set_clientdata(client, ts);
|
||||
return 0;
|
||||
|
||||
err_free_irq:
|
||||
free_irq(client->irq, ts);
|
||||
err_free_dev:
|
||||
input_free_device(input_dev);
|
||||
err_free_ts:
|
||||
kfree(ts);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static __devexit int egalax_ts_remove(struct i2c_client *client)
|
||||
{
|
||||
struct egalax_ts *ts = i2c_get_clientdata(client);
|
||||
|
||||
free_irq(client->irq, ts);
|
||||
|
||||
input_unregister_device(ts->input_dev);
|
||||
kfree(ts);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id egalax_ts_id[] = {
|
||||
{ "egalax_ts", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, egalax_ts_id);
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int egalax_ts_suspend(struct device *dev)
|
||||
{
|
||||
static const u8 suspend_cmd[MAX_I2C_DATA_LEN] = {
|
||||
0x3, 0x6, 0xa, 0x3, 0x36, 0x3f, 0x2, 0, 0, 0
|
||||
};
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
int ret;
|
||||
|
||||
ret = i2c_master_send(client, suspend_cmd, MAX_I2C_DATA_LEN);
|
||||
return ret > 0 ? 0 : ret;
|
||||
}
|
||||
|
||||
static int egalax_ts_resume(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
|
||||
return egalax_wake_up_device(client);
|
||||
}
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(egalax_ts_pm_ops, egalax_ts_suspend, egalax_ts_resume);
|
||||
|
||||
static struct i2c_driver egalax_ts_driver = {
|
||||
.driver = {
|
||||
.name = "egalax_ts",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &egalax_ts_pm_ops,
|
||||
},
|
||||
.id_table = egalax_ts_id,
|
||||
.probe = egalax_ts_probe,
|
||||
.remove = __devexit_p(egalax_ts_remove),
|
||||
};
|
||||
|
||||
static int __init egalax_ts_init(void)
|
||||
{
|
||||
return i2c_add_driver(&egalax_ts_driver);
|
||||
}
|
||||
|
||||
static void __exit egalax_ts_exit(void)
|
||||
{
|
||||
i2c_del_driver(&egalax_ts_driver);
|
||||
}
|
||||
|
||||
module_init(egalax_ts_init);
|
||||
module_exit(egalax_ts_exit);
|
||||
|
||||
MODULE_AUTHOR("Freescale Semiconductor, Inc.");
|
||||
MODULE_DESCRIPTION("Touchscreen driver for EETI eGalax touch controller");
|
||||
MODULE_LICENSE("GPL");
|
||||
@@ -47,12 +47,6 @@ static int invert_y;
|
||||
module_param(invert_y, bool, 0644);
|
||||
MODULE_PARM_DESC(invert_y, "If set, Y axis is inverted");
|
||||
|
||||
static struct pnp_device_id pnp_ids[] = {
|
||||
{ .id = "PNP0cc0" },
|
||||
{ .id = "" }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pnp, pnp_ids);
|
||||
|
||||
static irqreturn_t htcpen_interrupt(int irq, void *handle)
|
||||
{
|
||||
struct input_dev *htcpen_dev = handle;
|
||||
@@ -237,6 +231,7 @@ static struct dmi_system_id __initdata htcshift_dmi_table[] = {
|
||||
},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(dmi, htcshift_dmi_table);
|
||||
|
||||
static int __init htcpen_isa_init(void)
|
||||
{
|
||||
|
||||
@@ -664,18 +664,7 @@ static struct platform_driver mrstouch_driver = {
|
||||
.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_platform_driver(mrstouch_driver);
|
||||
|
||||
MODULE_AUTHOR("Sreedhara Murthy. D.S, sreedhara.ds@intel.com");
|
||||
MODULE_DESCRIPTION("Intel Moorestown Resistive Touch Screen Driver");
|
||||
|
||||
@@ -172,16 +172,4 @@ static struct platform_driver jornada720_ts_driver = {
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init jornada720_ts_init(void)
|
||||
{
|
||||
return platform_driver_register(&jornada720_ts_driver);
|
||||
}
|
||||
|
||||
static void __exit jornada720_ts_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&jornada720_ts_driver);
|
||||
}
|
||||
|
||||
module_init(jornada720_ts_init);
|
||||
module_exit(jornada720_ts_exit);
|
||||
module_platform_driver(jornada720_ts_driver);
|
||||
|
||||
@@ -392,18 +392,7 @@ static struct platform_driver lpc32xx_ts_driver = {
|
||||
.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_platform_driver(lpc32xx_ts_driver);
|
||||
|
||||
MODULE_AUTHOR("Kevin Wells <kevin.wells@nxp.com");
|
||||
MODULE_DESCRIPTION("LPC32XX TSC Driver");
|
||||
|
||||
@@ -302,19 +302,7 @@ static struct platform_driver mainstone_wm97xx_driver = {
|
||||
.name = "wm97xx-touch",
|
||||
},
|
||||
};
|
||||
|
||||
static int __init mainstone_wm97xx_init(void)
|
||||
{
|
||||
return platform_driver_register(&mainstone_wm97xx_driver);
|
||||
}
|
||||
|
||||
static void __exit mainstone_wm97xx_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&mainstone_wm97xx_driver);
|
||||
}
|
||||
|
||||
module_init(mainstone_wm97xx_init);
|
||||
module_exit(mainstone_wm97xx_exit);
|
||||
module_platform_driver(mainstone_wm97xx_driver);
|
||||
|
||||
/* Module information */
|
||||
MODULE_AUTHOR("Liam Girdwood <lrg@slimlogic.co.uk>");
|
||||
|
||||
@@ -240,18 +240,7 @@ static struct platform_driver mc13783_ts_driver = {
|
||||
.name = MC13783_TS_NAME,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init mc13783_ts_init(void)
|
||||
{
|
||||
return platform_driver_probe(&mc13783_ts_driver, &mc13783_ts_probe);
|
||||
}
|
||||
module_init(mc13783_ts_init);
|
||||
|
||||
static void __exit mc13783_ts_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&mc13783_ts_driver);
|
||||
}
|
||||
module_exit(mc13783_ts_exit);
|
||||
module_platform_driver(mc13783_ts_driver);
|
||||
|
||||
MODULE_DESCRIPTION("MC13783 input touchscreen driver");
|
||||
MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
|
||||
|
||||
@@ -36,7 +36,6 @@
|
||||
struct migor_ts_priv {
|
||||
struct i2c_client *client;
|
||||
struct input_dev *input;
|
||||
struct delayed_work work;
|
||||
int irq;
|
||||
};
|
||||
|
||||
@@ -44,15 +43,24 @@ static const u_int8_t migor_ts_ena_seq[17] = { 0x33, 0x22, 0x11,
|
||||
0x01, 0x06, 0x07, };
|
||||
static const u_int8_t migor_ts_dis_seq[17] = { };
|
||||
|
||||
static void migor_ts_poscheck(struct work_struct *work)
|
||||
static irqreturn_t migor_ts_isr(int irq, void *dev_id)
|
||||
{
|
||||
struct migor_ts_priv *priv = container_of(work,
|
||||
struct migor_ts_priv,
|
||||
work.work);
|
||||
struct migor_ts_priv *priv = dev_id;
|
||||
unsigned short xpos, ypos;
|
||||
unsigned char event;
|
||||
u_int8_t buf[16];
|
||||
|
||||
/*
|
||||
* The touch screen controller chip is hooked up to the CPU
|
||||
* using I2C and a single interrupt line. The interrupt line
|
||||
* is pulled low whenever someone taps the screen. To deassert
|
||||
* the interrupt line we need to acknowledge the interrupt by
|
||||
* communicating with the controller over the slow i2c bus.
|
||||
*
|
||||
* Since I2C bus controller may sleep we are using threaded
|
||||
* IRQ here.
|
||||
*/
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
|
||||
/* Set Index 0 */
|
||||
@@ -72,41 +80,25 @@ static void migor_ts_poscheck(struct work_struct *work)
|
||||
xpos = ((buf[11] & 0x03) << 8 | buf[10]);
|
||||
event = buf[12];
|
||||
|
||||
if (event == EVENT_PENDOWN || event == EVENT_REPEAT) {
|
||||
switch (event) {
|
||||
case EVENT_PENDOWN:
|
||||
case EVENT_REPEAT:
|
||||
input_report_key(priv->input, BTN_TOUCH, 1);
|
||||
input_report_abs(priv->input, ABS_X, ypos); /*X-Y swap*/
|
||||
input_report_abs(priv->input, ABS_Y, xpos);
|
||||
input_sync(priv->input);
|
||||
} else if (event == EVENT_PENUP) {
|
||||
break;
|
||||
|
||||
case EVENT_PENUP:
|
||||
input_report_key(priv->input, BTN_TOUCH, 0);
|
||||
input_sync(priv->input);
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
enable_irq(priv->irq);
|
||||
}
|
||||
|
||||
static irqreturn_t migor_ts_isr(int irq, void *dev_id)
|
||||
{
|
||||
struct migor_ts_priv *priv = dev_id;
|
||||
|
||||
/* the touch screen controller chip is hooked up to the cpu
|
||||
* using i2c and a single interrupt line. the interrupt line
|
||||
* is pulled low whenever someone taps the screen. to deassert
|
||||
* the interrupt line we need to acknowledge the interrupt by
|
||||
* communicating with the controller over the slow i2c bus.
|
||||
*
|
||||
* we can't acknowledge from interrupt context since the i2c
|
||||
* bus controller may sleep, so we just disable the interrupt
|
||||
* here and handle the acknowledge using delayed work.
|
||||
*/
|
||||
|
||||
disable_irq_nosync(irq);
|
||||
schedule_delayed_work(&priv->work, HZ / 20);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
|
||||
static int migor_ts_open(struct input_dev *dev)
|
||||
{
|
||||
struct migor_ts_priv *priv = input_get_drvdata(dev);
|
||||
@@ -131,15 +123,6 @@ static void migor_ts_close(struct input_dev *dev)
|
||||
|
||||
disable_irq(priv->irq);
|
||||
|
||||
/* cancel pending work and wait for migor_ts_poscheck() to finish */
|
||||
if (cancel_delayed_work_sync(&priv->work)) {
|
||||
/*
|
||||
* if migor_ts_poscheck was canceled we need to enable IRQ
|
||||
* here to balance disable done in migor_ts_isr.
|
||||
*/
|
||||
enable_irq(priv->irq);
|
||||
}
|
||||
|
||||
/* disable controller */
|
||||
i2c_master_send(client, migor_ts_dis_seq, sizeof(migor_ts_dis_seq));
|
||||
|
||||
@@ -154,23 +137,20 @@ static int migor_ts_probe(struct i2c_client *client,
|
||||
int error;
|
||||
|
||||
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv) {
|
||||
dev_err(&client->dev, "failed to allocate driver data\n");
|
||||
error = -ENOMEM;
|
||||
goto err0;
|
||||
}
|
||||
|
||||
dev_set_drvdata(&client->dev, priv);
|
||||
|
||||
input = input_allocate_device();
|
||||
if (!input) {
|
||||
dev_err(&client->dev, "Failed to allocate input device.\n");
|
||||
if (!priv || !input) {
|
||||
dev_err(&client->dev, "failed to allocate memory\n");
|
||||
error = -ENOMEM;
|
||||
goto err1;
|
||||
goto err_free_mem;
|
||||
}
|
||||
|
||||
priv->client = client;
|
||||
priv->input = input;
|
||||
priv->irq = client->irq;
|
||||
|
||||
input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
|
||||
input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
|
||||
|
||||
__set_bit(BTN_TOUCH, input->keybit);
|
||||
|
||||
input_set_abs_params(input, ABS_X, 95, 955, 0, 0);
|
||||
input_set_abs_params(input, ABS_Y, 85, 935, 0, 0);
|
||||
@@ -184,39 +164,34 @@ static int migor_ts_probe(struct i2c_client *client,
|
||||
|
||||
input_set_drvdata(input, priv);
|
||||
|
||||
priv->client = client;
|
||||
priv->input = input;
|
||||
INIT_DELAYED_WORK(&priv->work, migor_ts_poscheck);
|
||||
priv->irq = client->irq;
|
||||
error = request_threaded_irq(priv->irq, NULL, migor_ts_isr,
|
||||
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
|
||||
client->name, priv);
|
||||
if (error) {
|
||||
dev_err(&client->dev, "Unable to request touchscreen IRQ.\n");
|
||||
goto err_free_mem;
|
||||
}
|
||||
|
||||
error = input_register_device(input);
|
||||
if (error)
|
||||
goto err1;
|
||||
|
||||
error = request_irq(priv->irq, migor_ts_isr, IRQF_TRIGGER_LOW,
|
||||
client->name, priv);
|
||||
if (error) {
|
||||
dev_err(&client->dev, "Unable to request touchscreen IRQ.\n");
|
||||
goto err2;
|
||||
}
|
||||
goto err_free_irq;
|
||||
|
||||
i2c_set_clientdata(client, priv);
|
||||
device_init_wakeup(&client->dev, 1);
|
||||
|
||||
return 0;
|
||||
|
||||
err2:
|
||||
input_unregister_device(input);
|
||||
input = NULL; /* so we dont try to free it below */
|
||||
err1:
|
||||
err_free_irq:
|
||||
free_irq(priv->irq, priv);
|
||||
err_free_mem:
|
||||
input_free_device(input);
|
||||
kfree(priv);
|
||||
err0:
|
||||
dev_set_drvdata(&client->dev, NULL);
|
||||
return error;
|
||||
}
|
||||
|
||||
static int migor_ts_remove(struct i2c_client *client)
|
||||
{
|
||||
struct migor_ts_priv *priv = dev_get_drvdata(&client->dev);
|
||||
struct migor_ts_priv *priv = i2c_get_clientdata(client);
|
||||
|
||||
free_irq(priv->irq, priv);
|
||||
input_unregister_device(priv->input);
|
||||
@@ -230,7 +205,7 @@ static int migor_ts_remove(struct i2c_client *client)
|
||||
static int migor_ts_suspend(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct migor_ts_priv *priv = dev_get_drvdata(&client->dev);
|
||||
struct migor_ts_priv *priv = i2c_get_clientdata(client);
|
||||
|
||||
if (device_may_wakeup(&client->dev))
|
||||
enable_irq_wake(priv->irq);
|
||||
@@ -241,7 +216,7 @@ static int migor_ts_suspend(struct device *dev)
|
||||
static int migor_ts_resume(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct migor_ts_priv *priv = dev_get_drvdata(&client->dev);
|
||||
struct migor_ts_priv *priv = i2c_get_clientdata(client);
|
||||
|
||||
if (device_may_wakeup(&client->dev))
|
||||
disable_irq_wake(priv->irq);
|
||||
|
||||
@@ -252,19 +252,7 @@ static struct platform_driver pcap_ts_driver = {
|
||||
.pm = PCAP_TS_PM_OPS,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init pcap_ts_init(void)
|
||||
{
|
||||
return platform_driver_register(&pcap_ts_driver);
|
||||
}
|
||||
|
||||
static void __exit pcap_ts_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&pcap_ts_driver);
|
||||
}
|
||||
|
||||
module_init(pcap_ts_init);
|
||||
module_exit(pcap_ts_exit);
|
||||
module_platform_driver(pcap_ts_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Motorola PCAP2 touchscreen driver");
|
||||
MODULE_AUTHOR("Daniel Ribeiro / Harald Welte");
|
||||
|
||||
@@ -0,0 +1,239 @@
|
||||
/*
|
||||
* Driver for Pixcir I2C touchscreen controllers.
|
||||
*
|
||||
* Copyright (C) 2010-2011 Pixcir, Inc.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* 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 library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/input/pixcir_ts.h>
|
||||
|
||||
struct pixcir_i2c_ts_data {
|
||||
struct i2c_client *client;
|
||||
struct input_dev *input;
|
||||
const struct pixcir_ts_platform_data *chip;
|
||||
bool exiting;
|
||||
};
|
||||
|
||||
static void pixcir_ts_poscheck(struct pixcir_i2c_ts_data *data)
|
||||
{
|
||||
struct pixcir_i2c_ts_data *tsdata = data;
|
||||
u8 rdbuf[10], wrbuf[1] = { 0 };
|
||||
u8 touch;
|
||||
int ret;
|
||||
|
||||
ret = i2c_master_send(tsdata->client, wrbuf, sizeof(wrbuf));
|
||||
if (ret != sizeof(wrbuf)) {
|
||||
dev_err(&tsdata->client->dev,
|
||||
"%s: i2c_master_send failed(), ret=%d\n",
|
||||
__func__, ret);
|
||||
return;
|
||||
}
|
||||
|
||||
ret = i2c_master_recv(tsdata->client, rdbuf, sizeof(rdbuf));
|
||||
if (ret != sizeof(rdbuf)) {
|
||||
dev_err(&tsdata->client->dev,
|
||||
"%s: i2c_master_recv failed(), ret=%d\n",
|
||||
__func__, ret);
|
||||
return;
|
||||
}
|
||||
|
||||
touch = rdbuf[0];
|
||||
if (touch) {
|
||||
u16 posx1 = (rdbuf[3] << 8) | rdbuf[2];
|
||||
u16 posy1 = (rdbuf[5] << 8) | rdbuf[4];
|
||||
u16 posx2 = (rdbuf[7] << 8) | rdbuf[6];
|
||||
u16 posy2 = (rdbuf[9] << 8) | rdbuf[8];
|
||||
|
||||
input_report_key(tsdata->input, BTN_TOUCH, 1);
|
||||
input_report_abs(tsdata->input, ABS_X, posx1);
|
||||
input_report_abs(tsdata->input, ABS_Y, posy1);
|
||||
|
||||
input_report_abs(tsdata->input, ABS_MT_POSITION_X, posx1);
|
||||
input_report_abs(tsdata->input, ABS_MT_POSITION_Y, posy1);
|
||||
input_mt_sync(tsdata->input);
|
||||
|
||||
if (touch == 2) {
|
||||
input_report_abs(tsdata->input,
|
||||
ABS_MT_POSITION_X, posx2);
|
||||
input_report_abs(tsdata->input,
|
||||
ABS_MT_POSITION_Y, posy2);
|
||||
input_mt_sync(tsdata->input);
|
||||
}
|
||||
} else {
|
||||
input_report_key(tsdata->input, BTN_TOUCH, 0);
|
||||
}
|
||||
|
||||
input_sync(tsdata->input);
|
||||
}
|
||||
|
||||
static irqreturn_t pixcir_ts_isr(int irq, void *dev_id)
|
||||
{
|
||||
struct pixcir_i2c_ts_data *tsdata = dev_id;
|
||||
|
||||
while (!tsdata->exiting) {
|
||||
pixcir_ts_poscheck(tsdata);
|
||||
|
||||
if (tsdata->chip->attb_read_val())
|
||||
break;
|
||||
|
||||
msleep(20);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int pixcir_i2c_ts_suspend(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
|
||||
if (device_may_wakeup(&client->dev))
|
||||
enable_irq_wake(client->irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pixcir_i2c_ts_resume(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
|
||||
if (device_may_wakeup(&client->dev))
|
||||
disable_irq_wake(client->irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(pixcir_dev_pm_ops,
|
||||
pixcir_i2c_ts_suspend, pixcir_i2c_ts_resume);
|
||||
|
||||
static int __devinit pixcir_i2c_ts_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
const struct pixcir_ts_platform_data *pdata = client->dev.platform_data;
|
||||
struct pixcir_i2c_ts_data *tsdata;
|
||||
struct input_dev *input;
|
||||
int error;
|
||||
|
||||
if (!pdata) {
|
||||
dev_err(&client->dev, "platform data not defined\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
tsdata = kzalloc(sizeof(*tsdata), GFP_KERNEL);
|
||||
input = input_allocate_device();
|
||||
if (!tsdata || !input) {
|
||||
dev_err(&client->dev, "Failed to allocate driver data!\n");
|
||||
error = -ENOMEM;
|
||||
goto err_free_mem;
|
||||
}
|
||||
|
||||
tsdata->client = client;
|
||||
tsdata->input = input;
|
||||
tsdata->chip = pdata;
|
||||
|
||||
input->name = client->name;
|
||||
input->id.bustype = BUS_I2C;
|
||||
input->dev.parent = &client->dev;
|
||||
|
||||
__set_bit(EV_KEY, input->evbit);
|
||||
__set_bit(EV_ABS, input->evbit);
|
||||
__set_bit(BTN_TOUCH, input->keybit);
|
||||
input_set_abs_params(input, ABS_X, 0, pdata->x_max, 0, 0);
|
||||
input_set_abs_params(input, ABS_Y, 0, pdata->y_max, 0, 0);
|
||||
input_set_abs_params(input, ABS_MT_POSITION_X, 0, pdata->x_max, 0, 0);
|
||||
input_set_abs_params(input, ABS_MT_POSITION_Y, 0, pdata->y_max, 0, 0);
|
||||
|
||||
input_set_drvdata(input, tsdata);
|
||||
|
||||
error = request_threaded_irq(client->irq, NULL, pixcir_ts_isr,
|
||||
IRQF_TRIGGER_FALLING,
|
||||
client->name, tsdata);
|
||||
if (error) {
|
||||
dev_err(&client->dev, "Unable to request touchscreen IRQ.\n");
|
||||
goto err_free_mem;
|
||||
}
|
||||
|
||||
error = input_register_device(input);
|
||||
if (error)
|
||||
goto err_free_irq;
|
||||
|
||||
i2c_set_clientdata(client, tsdata);
|
||||
device_init_wakeup(&client->dev, 1);
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_irq:
|
||||
free_irq(client->irq, tsdata);
|
||||
err_free_mem:
|
||||
input_free_device(input);
|
||||
kfree(tsdata);
|
||||
return error;
|
||||
}
|
||||
|
||||
static int __devexit pixcir_i2c_ts_remove(struct i2c_client *client)
|
||||
{
|
||||
struct pixcir_i2c_ts_data *tsdata = i2c_get_clientdata(client);
|
||||
|
||||
device_init_wakeup(&client->dev, 0);
|
||||
|
||||
tsdata->exiting = true;
|
||||
mb();
|
||||
free_irq(client->irq, tsdata);
|
||||
|
||||
input_unregister_device(tsdata->input);
|
||||
kfree(tsdata);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id pixcir_i2c_ts_id[] = {
|
||||
{ "pixcir_ts", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, pixcir_i2c_ts_id);
|
||||
|
||||
static struct i2c_driver pixcir_i2c_ts_driver = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "pixcir_ts",
|
||||
.pm = &pixcir_dev_pm_ops,
|
||||
},
|
||||
.probe = pixcir_i2c_ts_probe,
|
||||
.remove = __devexit_p(pixcir_i2c_ts_remove),
|
||||
.id_table = pixcir_i2c_ts_id,
|
||||
};
|
||||
|
||||
static int __init pixcir_i2c_ts_init(void)
|
||||
{
|
||||
return i2c_add_driver(&pixcir_i2c_ts_driver);
|
||||
}
|
||||
module_init(pixcir_i2c_ts_init);
|
||||
|
||||
static void __exit pixcir_i2c_ts_exit(void)
|
||||
{
|
||||
i2c_del_driver(&pixcir_i2c_ts_driver);
|
||||
}
|
||||
module_exit(pixcir_i2c_ts_exit);
|
||||
|
||||
MODULE_AUTHOR("Jianchun Bian <jcbian@pixcir.com.cn>, Dequan Meng <dqmeng@pixcir.com.cn>");
|
||||
MODULE_DESCRIPTION("Pixcir I2C Touchscreen Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
@@ -432,19 +432,7 @@ static struct platform_driver s3c_ts_driver = {
|
||||
.probe = s3c2410ts_probe,
|
||||
.remove = __devexit_p(s3c2410ts_remove),
|
||||
};
|
||||
|
||||
static int __init s3c2410ts_init(void)
|
||||
{
|
||||
return platform_driver_register(&s3c_ts_driver);
|
||||
}
|
||||
|
||||
static void __exit s3c2410ts_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&s3c_ts_driver);
|
||||
}
|
||||
|
||||
module_init(s3c2410ts_init);
|
||||
module_exit(s3c2410ts_exit);
|
||||
module_platform_driver(s3c_ts_driver);
|
||||
|
||||
MODULE_AUTHOR("Arnaud Patard <arnaud.patard@rtp-net.org>, "
|
||||
"Ben Dooks <ben@simtec.co.uk>, "
|
||||
|
||||
@@ -379,20 +379,7 @@ static struct platform_driver stmpe_ts_driver = {
|
||||
.probe = stmpe_input_probe,
|
||||
.remove = __devexit_p(stmpe_ts_remove),
|
||||
};
|
||||
|
||||
static int __init stmpe_ts_init(void)
|
||||
{
|
||||
return platform_driver_register(&stmpe_ts_driver);
|
||||
}
|
||||
|
||||
module_init(stmpe_ts_init);
|
||||
|
||||
static void __exit stmpe_ts_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&stmpe_ts_driver);
|
||||
}
|
||||
|
||||
module_exit(stmpe_ts_exit);
|
||||
module_platform_driver(stmpe_ts_driver);
|
||||
|
||||
MODULE_AUTHOR("Luotao Fu <l.fu@pengutronix.de>");
|
||||
MODULE_DESCRIPTION("STMPEXXX touchscreen driver");
|
||||
|
||||
@@ -378,19 +378,7 @@ static struct platform_driver tsc_driver = {
|
||||
.driver.name = "tnetv107x-ts",
|
||||
.driver.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int __init tsc_init(void)
|
||||
{
|
||||
return platform_driver_register(&tsc_driver);
|
||||
}
|
||||
|
||||
static void __exit tsc_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&tsc_driver);
|
||||
}
|
||||
|
||||
module_init(tsc_init);
|
||||
module_exit(tsc_exit);
|
||||
module_platform_driver(tsc_driver);
|
||||
|
||||
MODULE_AUTHOR("Cyril Chemparathy");
|
||||
MODULE_DESCRIPTION("TNETV107X Touchscreen Driver");
|
||||
|
||||
@@ -371,18 +371,7 @@ static struct platform_driver tps6507x_ts_driver = {
|
||||
.probe = tps6507x_ts_probe,
|
||||
.remove = __devexit_p(tps6507x_ts_remove),
|
||||
};
|
||||
|
||||
static int __init tps6507x_ts_init(void)
|
||||
{
|
||||
return platform_driver_register(&tps6507x_ts_driver);
|
||||
}
|
||||
module_init(tps6507x_ts_init);
|
||||
|
||||
static void __exit tps6507x_ts_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&tps6507x_ts_driver);
|
||||
}
|
||||
module_exit(tps6507x_ts_exit);
|
||||
module_platform_driver(tps6507x_ts_driver);
|
||||
|
||||
MODULE_AUTHOR("Todd Fischer <todd.fischer@ridgerun.com>");
|
||||
MODULE_DESCRIPTION("TPS6507x - TouchScreen driver");
|
||||
|
||||
@@ -20,24 +20,24 @@
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/suspend.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/freezer.h>
|
||||
#include <linux/ucb1400.h>
|
||||
|
||||
#define UCB1400_TS_POLL_PERIOD 10 /* ms */
|
||||
|
||||
static int adcsync;
|
||||
static int ts_delay = 55; /* us */
|
||||
static int ts_delay_pressure; /* us */
|
||||
|
||||
/* Switch to interrupt mode. */
|
||||
static inline void ucb1400_ts_mode_int(struct snd_ac97 *ac97)
|
||||
static void ucb1400_ts_mode_int(struct ucb1400_ts *ucb)
|
||||
{
|
||||
ucb1400_reg_write(ac97, UCB_TS_CR,
|
||||
ucb1400_reg_write(ucb->ac97, UCB_TS_CR,
|
||||
UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW |
|
||||
UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND |
|
||||
UCB_TS_CR_MODE_INT);
|
||||
@@ -47,13 +47,15 @@ static inline void ucb1400_ts_mode_int(struct snd_ac97 *ac97)
|
||||
* Switch to pressure mode, and read pressure. We don't need to wait
|
||||
* here, since both plates are being driven.
|
||||
*/
|
||||
static inline unsigned int ucb1400_ts_read_pressure(struct ucb1400_ts *ucb)
|
||||
static unsigned int ucb1400_ts_read_pressure(struct ucb1400_ts *ucb)
|
||||
{
|
||||
ucb1400_reg_write(ucb->ac97, UCB_TS_CR,
|
||||
UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW |
|
||||
UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND |
|
||||
UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
|
||||
|
||||
udelay(ts_delay_pressure);
|
||||
|
||||
return ucb1400_adc_read(ucb->ac97, UCB_ADC_INP_TSPY, adcsync);
|
||||
}
|
||||
|
||||
@@ -63,7 +65,7 @@ static inline unsigned int ucb1400_ts_read_pressure(struct ucb1400_ts *ucb)
|
||||
* gives a faster response time. Even so, we need to wait about 55us
|
||||
* for things to stabilise.
|
||||
*/
|
||||
static inline unsigned int ucb1400_ts_read_xpos(struct ucb1400_ts *ucb)
|
||||
static unsigned int ucb1400_ts_read_xpos(struct ucb1400_ts *ucb)
|
||||
{
|
||||
ucb1400_reg_write(ucb->ac97, UCB_TS_CR,
|
||||
UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW |
|
||||
@@ -86,7 +88,7 @@ static inline unsigned int ucb1400_ts_read_xpos(struct ucb1400_ts *ucb)
|
||||
* gives a faster response time. Even so, we need to wait about 55us
|
||||
* for things to stabilise.
|
||||
*/
|
||||
static inline unsigned int ucb1400_ts_read_ypos(struct ucb1400_ts *ucb)
|
||||
static int ucb1400_ts_read_ypos(struct ucb1400_ts *ucb)
|
||||
{
|
||||
ucb1400_reg_write(ucb->ac97, UCB_TS_CR,
|
||||
UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW |
|
||||
@@ -107,7 +109,7 @@ static inline unsigned int ucb1400_ts_read_ypos(struct ucb1400_ts *ucb)
|
||||
* Switch to X plate resistance mode. Set MX to ground, PX to
|
||||
* supply. Measure current.
|
||||
*/
|
||||
static inline unsigned int ucb1400_ts_read_xres(struct ucb1400_ts *ucb)
|
||||
static unsigned int ucb1400_ts_read_xres(struct ucb1400_ts *ucb)
|
||||
{
|
||||
ucb1400_reg_write(ucb->ac97, UCB_TS_CR,
|
||||
UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW |
|
||||
@@ -119,7 +121,7 @@ static inline unsigned int ucb1400_ts_read_xres(struct ucb1400_ts *ucb)
|
||||
* Switch to Y plate resistance mode. Set MY to ground, PY to
|
||||
* supply. Measure current.
|
||||
*/
|
||||
static inline unsigned int ucb1400_ts_read_yres(struct ucb1400_ts *ucb)
|
||||
static unsigned int ucb1400_ts_read_yres(struct ucb1400_ts *ucb)
|
||||
{
|
||||
ucb1400_reg_write(ucb->ac97, UCB_TS_CR,
|
||||
UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW |
|
||||
@@ -127,26 +129,26 @@ static inline unsigned int ucb1400_ts_read_yres(struct ucb1400_ts *ucb)
|
||||
return ucb1400_adc_read(ucb->ac97, 0, adcsync);
|
||||
}
|
||||
|
||||
static inline int ucb1400_ts_pen_up(struct snd_ac97 *ac97)
|
||||
static int ucb1400_ts_pen_up(struct ucb1400_ts *ucb)
|
||||
{
|
||||
unsigned short val = ucb1400_reg_read(ac97, UCB_TS_CR);
|
||||
unsigned short val = ucb1400_reg_read(ucb->ac97, UCB_TS_CR);
|
||||
|
||||
return val & (UCB_TS_CR_TSPX_LOW | UCB_TS_CR_TSMX_LOW);
|
||||
}
|
||||
|
||||
static inline void ucb1400_ts_irq_enable(struct snd_ac97 *ac97)
|
||||
static void ucb1400_ts_irq_enable(struct ucb1400_ts *ucb)
|
||||
{
|
||||
ucb1400_reg_write(ac97, UCB_IE_CLEAR, UCB_IE_TSPX);
|
||||
ucb1400_reg_write(ac97, UCB_IE_CLEAR, 0);
|
||||
ucb1400_reg_write(ac97, UCB_IE_FAL, UCB_IE_TSPX);
|
||||
ucb1400_reg_write(ucb->ac97, UCB_IE_CLEAR, UCB_IE_TSPX);
|
||||
ucb1400_reg_write(ucb->ac97, UCB_IE_CLEAR, 0);
|
||||
ucb1400_reg_write(ucb->ac97, UCB_IE_FAL, UCB_IE_TSPX);
|
||||
}
|
||||
|
||||
static inline void ucb1400_ts_irq_disable(struct snd_ac97 *ac97)
|
||||
static void ucb1400_ts_irq_disable(struct ucb1400_ts *ucb)
|
||||
{
|
||||
ucb1400_reg_write(ac97, UCB_IE_FAL, 0);
|
||||
ucb1400_reg_write(ucb->ac97, UCB_IE_FAL, 0);
|
||||
}
|
||||
|
||||
static void ucb1400_ts_evt_add(struct input_dev *idev, u16 pressure, u16 x, u16 y)
|
||||
static void ucb1400_ts_report_event(struct input_dev *idev, u16 pressure, u16 x, u16 y)
|
||||
{
|
||||
input_report_abs(idev, ABS_X, x);
|
||||
input_report_abs(idev, ABS_Y, y);
|
||||
@@ -162,7 +164,7 @@ static void ucb1400_ts_event_release(struct input_dev *idev)
|
||||
input_sync(idev);
|
||||
}
|
||||
|
||||
static void ucb1400_handle_pending_irq(struct ucb1400_ts *ucb)
|
||||
static void ucb1400_clear_pending_irq(struct ucb1400_ts *ucb)
|
||||
{
|
||||
unsigned int isr;
|
||||
|
||||
@@ -171,32 +173,34 @@ static void ucb1400_handle_pending_irq(struct ucb1400_ts *ucb)
|
||||
ucb1400_reg_write(ucb->ac97, UCB_IE_CLEAR, 0);
|
||||
|
||||
if (isr & UCB_IE_TSPX)
|
||||
ucb1400_ts_irq_disable(ucb->ac97);
|
||||
ucb1400_ts_irq_disable(ucb);
|
||||
else
|
||||
dev_dbg(&ucb->ts_idev->dev, "ucb1400: unexpected IE_STATUS = %#x\n", isr);
|
||||
enable_irq(ucb->irq);
|
||||
dev_dbg(&ucb->ts_idev->dev,
|
||||
"ucb1400: unexpected IE_STATUS = %#x\n", isr);
|
||||
}
|
||||
|
||||
static int ucb1400_ts_thread(void *_ucb)
|
||||
/*
|
||||
* A restriction with interrupts exists when using the ucb1400, as
|
||||
* the codec read/write routines may sleep while waiting for codec
|
||||
* access completion and uses semaphores for access control to the
|
||||
* AC97 bus. Therefore the driver is forced to use threaded interrupt
|
||||
* handler.
|
||||
*/
|
||||
static irqreturn_t ucb1400_irq(int irqnr, void *devid)
|
||||
{
|
||||
struct ucb1400_ts *ucb = _ucb;
|
||||
struct task_struct *tsk = current;
|
||||
int valid = 0;
|
||||
struct sched_param param = { .sched_priority = 1 };
|
||||
struct ucb1400_ts *ucb = devid;
|
||||
unsigned int x, y, p;
|
||||
bool penup;
|
||||
|
||||
sched_setscheduler(tsk, SCHED_FIFO, ¶m);
|
||||
if (unlikely(irqnr != ucb->irq))
|
||||
return IRQ_NONE;
|
||||
|
||||
set_freezable();
|
||||
while (!kthread_should_stop()) {
|
||||
unsigned int x, y, p;
|
||||
long timeout;
|
||||
ucb1400_clear_pending_irq(ucb);
|
||||
|
||||
ucb->ts_restart = 0;
|
||||
/* Start with a small delay before checking pendown state */
|
||||
msleep(UCB1400_TS_POLL_PERIOD);
|
||||
|
||||
if (ucb->irq_pending) {
|
||||
ucb->irq_pending = 0;
|
||||
ucb1400_handle_pending_irq(ucb);
|
||||
}
|
||||
while (!ucb->stopped && !(penup = ucb1400_ts_pen_up(ucb))) {
|
||||
|
||||
ucb1400_adc_enable(ucb->ac97);
|
||||
x = ucb1400_ts_read_xpos(ucb);
|
||||
@@ -204,91 +208,62 @@ static int ucb1400_ts_thread(void *_ucb)
|
||||
p = ucb1400_ts_read_pressure(ucb);
|
||||
ucb1400_adc_disable(ucb->ac97);
|
||||
|
||||
/* Switch back to interrupt mode. */
|
||||
ucb1400_ts_mode_int(ucb->ac97);
|
||||
ucb1400_ts_report_event(ucb->ts_idev, p, x, y);
|
||||
|
||||
msleep(10);
|
||||
|
||||
if (ucb1400_ts_pen_up(ucb->ac97)) {
|
||||
ucb1400_ts_irq_enable(ucb->ac97);
|
||||
|
||||
/*
|
||||
* If we spat out a valid sample set last time,
|
||||
* spit out a "pen off" sample here.
|
||||
*/
|
||||
if (valid) {
|
||||
ucb1400_ts_event_release(ucb->ts_idev);
|
||||
valid = 0;
|
||||
}
|
||||
|
||||
timeout = MAX_SCHEDULE_TIMEOUT;
|
||||
} else {
|
||||
valid = 1;
|
||||
ucb1400_ts_evt_add(ucb->ts_idev, p, x, y);
|
||||
timeout = msecs_to_jiffies(10);
|
||||
}
|
||||
|
||||
wait_event_freezable_timeout(ucb->ts_wait,
|
||||
ucb->irq_pending || ucb->ts_restart ||
|
||||
kthread_should_stop(), timeout);
|
||||
wait_event_timeout(ucb->ts_wait, ucb->stopped,
|
||||
msecs_to_jiffies(UCB1400_TS_POLL_PERIOD));
|
||||
}
|
||||
|
||||
/* Send the "pen off" if we are stopping with the pen still active */
|
||||
if (valid)
|
||||
ucb1400_ts_event_release(ucb->ts_idev);
|
||||
ucb1400_ts_event_release(ucb->ts_idev);
|
||||
|
||||
ucb->ts_task = NULL;
|
||||
return 0;
|
||||
if (!ucb->stopped) {
|
||||
/* Switch back to interrupt mode. */
|
||||
ucb1400_ts_mode_int(ucb);
|
||||
ucb1400_ts_irq_enable(ucb);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/*
|
||||
* A restriction with interrupts exists when using the ucb1400, as
|
||||
* the codec read/write routines may sleep while waiting for codec
|
||||
* access completion and uses semaphores for access control to the
|
||||
* AC97 bus. A complete codec read cycle could take anywhere from
|
||||
* 60 to 100uSec so we *definitely* don't want to spin inside the
|
||||
* interrupt handler waiting for codec access. So, we handle the
|
||||
* interrupt by scheduling a RT kernel thread to run in process
|
||||
* context instead of interrupt context.
|
||||
*/
|
||||
static irqreturn_t ucb1400_hard_irq(int irqnr, void *devid)
|
||||
static void ucb1400_ts_stop(struct ucb1400_ts *ucb)
|
||||
{
|
||||
struct ucb1400_ts *ucb = devid;
|
||||
/* Signal IRQ thread to stop polling and disable the handler. */
|
||||
ucb->stopped = true;
|
||||
mb();
|
||||
wake_up(&ucb->ts_wait);
|
||||
disable_irq(ucb->irq);
|
||||
|
||||
if (irqnr == ucb->irq) {
|
||||
disable_irq_nosync(ucb->irq);
|
||||
ucb->irq_pending = 1;
|
||||
wake_up(&ucb->ts_wait);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
return IRQ_NONE;
|
||||
ucb1400_ts_irq_disable(ucb);
|
||||
ucb1400_reg_write(ucb->ac97, UCB_TS_CR, 0);
|
||||
}
|
||||
|
||||
/* Must be called with ts->lock held */
|
||||
static void ucb1400_ts_start(struct ucb1400_ts *ucb)
|
||||
{
|
||||
/* Tell IRQ thread that it may poll the device. */
|
||||
ucb->stopped = false;
|
||||
mb();
|
||||
|
||||
ucb1400_ts_mode_int(ucb);
|
||||
ucb1400_ts_irq_enable(ucb);
|
||||
|
||||
enable_irq(ucb->irq);
|
||||
}
|
||||
|
||||
static int ucb1400_ts_open(struct input_dev *idev)
|
||||
{
|
||||
struct ucb1400_ts *ucb = input_get_drvdata(idev);
|
||||
int ret = 0;
|
||||
|
||||
BUG_ON(ucb->ts_task);
|
||||
ucb1400_ts_start(ucb);
|
||||
|
||||
ucb->ts_task = kthread_run(ucb1400_ts_thread, ucb, "UCB1400_ts");
|
||||
if (IS_ERR(ucb->ts_task)) {
|
||||
ret = PTR_ERR(ucb->ts_task);
|
||||
ucb->ts_task = NULL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ucb1400_ts_close(struct input_dev *idev)
|
||||
{
|
||||
struct ucb1400_ts *ucb = input_get_drvdata(idev);
|
||||
|
||||
if (ucb->ts_task)
|
||||
kthread_stop(ucb->ts_task);
|
||||
|
||||
ucb1400_ts_irq_disable(ucb->ac97);
|
||||
ucb1400_reg_write(ucb->ac97, UCB_TS_CR, 0);
|
||||
ucb1400_ts_stop(ucb);
|
||||
}
|
||||
|
||||
#ifndef NO_IRQ
|
||||
@@ -299,7 +274,8 @@ static void ucb1400_ts_close(struct input_dev *idev)
|
||||
* Try to probe our interrupt, rather than relying on lots of
|
||||
* hard-coded machine dependencies.
|
||||
*/
|
||||
static int ucb1400_ts_detect_irq(struct ucb1400_ts *ucb)
|
||||
static int __devinit ucb1400_ts_detect_irq(struct ucb1400_ts *ucb,
|
||||
struct platform_device *pdev)
|
||||
{
|
||||
unsigned long mask, timeout;
|
||||
|
||||
@@ -321,7 +297,7 @@ static int ucb1400_ts_detect_irq(struct ucb1400_ts *ucb)
|
||||
UCB_ADC_DAT_VALID)) {
|
||||
cpu_relax();
|
||||
if (time_after(jiffies, timeout)) {
|
||||
printk(KERN_ERR "ucb1400: timed out in IRQ probe\n");
|
||||
dev_err(&pdev->dev, "timed out in IRQ probe\n");
|
||||
probe_irq_off(mask);
|
||||
return -ENODEV;
|
||||
}
|
||||
@@ -342,11 +318,11 @@ static int ucb1400_ts_detect_irq(struct ucb1400_ts *ucb)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ucb1400_ts_probe(struct platform_device *dev)
|
||||
static int __devinit ucb1400_ts_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct ucb1400_ts *ucb = pdev->dev.platform_data;
|
||||
int error, x_res, y_res;
|
||||
u16 fcsr;
|
||||
struct ucb1400_ts *ucb = dev->dev.platform_data;
|
||||
|
||||
ucb->ts_idev = input_allocate_device();
|
||||
if (!ucb->ts_idev) {
|
||||
@@ -356,27 +332,19 @@ static int ucb1400_ts_probe(struct platform_device *dev)
|
||||
|
||||
/* Only in case the IRQ line wasn't supplied, try detecting it */
|
||||
if (ucb->irq < 0) {
|
||||
error = ucb1400_ts_detect_irq(ucb);
|
||||
error = ucb1400_ts_detect_irq(ucb, pdev);
|
||||
if (error) {
|
||||
printk(KERN_ERR "UCB1400: IRQ probe failed\n");
|
||||
dev_err(&pdev->dev, "IRQ probe failed\n");
|
||||
goto err_free_devs;
|
||||
}
|
||||
}
|
||||
dev_dbg(&pdev->dev, "found IRQ %d\n", ucb->irq);
|
||||
|
||||
init_waitqueue_head(&ucb->ts_wait);
|
||||
|
||||
error = request_irq(ucb->irq, ucb1400_hard_irq, IRQF_TRIGGER_RISING,
|
||||
"UCB1400", ucb);
|
||||
if (error) {
|
||||
printk(KERN_ERR "ucb1400: unable to grab irq%d: %d\n",
|
||||
ucb->irq, error);
|
||||
goto err_free_devs;
|
||||
}
|
||||
printk(KERN_DEBUG "UCB1400: found IRQ %d\n", ucb->irq);
|
||||
|
||||
input_set_drvdata(ucb->ts_idev, ucb);
|
||||
|
||||
ucb->ts_idev->dev.parent = &dev->dev;
|
||||
ucb->ts_idev->dev.parent = &pdev->dev;
|
||||
ucb->ts_idev->name = "UCB1400 touchscreen interface";
|
||||
ucb->ts_idev->id.vendor = ucb1400_reg_read(ucb->ac97,
|
||||
AC97_VENDOR_ID1);
|
||||
@@ -398,12 +366,23 @@ static int ucb1400_ts_probe(struct platform_device *dev)
|
||||
x_res = ucb1400_ts_read_xres(ucb);
|
||||
y_res = ucb1400_ts_read_yres(ucb);
|
||||
ucb1400_adc_disable(ucb->ac97);
|
||||
printk(KERN_DEBUG "UCB1400: x/y = %d/%d\n", x_res, y_res);
|
||||
dev_dbg(&pdev->dev, "x/y = %d/%d\n", x_res, y_res);
|
||||
|
||||
input_set_abs_params(ucb->ts_idev, ABS_X, 0, x_res, 0, 0);
|
||||
input_set_abs_params(ucb->ts_idev, ABS_Y, 0, y_res, 0, 0);
|
||||
input_set_abs_params(ucb->ts_idev, ABS_PRESSURE, 0, 0, 0, 0);
|
||||
|
||||
ucb1400_ts_stop(ucb);
|
||||
|
||||
error = request_threaded_irq(ucb->irq, NULL, ucb1400_irq,
|
||||
IRQF_TRIGGER_RISING | IRQF_ONESHOT,
|
||||
"UCB1400", ucb);
|
||||
if (error) {
|
||||
dev_err(&pdev->dev,
|
||||
"unable to grab irq%d: %d\n", ucb->irq, error);
|
||||
goto err_free_devs;
|
||||
}
|
||||
|
||||
error = input_register_device(ucb->ts_idev);
|
||||
if (error)
|
||||
goto err_free_irq;
|
||||
@@ -416,56 +395,61 @@ err_free_devs:
|
||||
input_free_device(ucb->ts_idev);
|
||||
err:
|
||||
return error;
|
||||
|
||||
}
|
||||
|
||||
static int ucb1400_ts_remove(struct platform_device *dev)
|
||||
static int __devexit ucb1400_ts_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct ucb1400_ts *ucb = dev->dev.platform_data;
|
||||
struct ucb1400_ts *ucb = pdev->dev.platform_data;
|
||||
|
||||
free_irq(ucb->irq, ucb);
|
||||
input_unregister_device(ucb->ts_idev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int ucb1400_ts_resume(struct platform_device *dev)
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int ucb1400_ts_suspend(struct device *dev)
|
||||
{
|
||||
struct ucb1400_ts *ucb = dev->dev.platform_data;
|
||||
struct ucb1400_ts *ucb = dev->platform_data;
|
||||
struct input_dev *idev = ucb->ts_idev;
|
||||
|
||||
if (ucb->ts_task) {
|
||||
/*
|
||||
* Restart the TS thread to ensure the
|
||||
* TS interrupt mode is set up again
|
||||
* after sleep.
|
||||
*/
|
||||
ucb->ts_restart = 1;
|
||||
wake_up(&ucb->ts_wait);
|
||||
}
|
||||
mutex_lock(&idev->mutex);
|
||||
|
||||
if (idev->users)
|
||||
ucb1400_ts_start(ucb);
|
||||
|
||||
mutex_unlock(&idev->mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ucb1400_ts_resume(struct device *dev)
|
||||
{
|
||||
struct ucb1400_ts *ucb = dev->platform_data;
|
||||
struct input_dev *idev = ucb->ts_idev;
|
||||
|
||||
mutex_lock(&idev->mutex);
|
||||
|
||||
if (idev->users)
|
||||
ucb1400_ts_stop(ucb);
|
||||
|
||||
mutex_unlock(&idev->mutex);
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define ucb1400_ts_resume NULL
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(ucb1400_ts_pm_ops,
|
||||
ucb1400_ts_suspend, ucb1400_ts_resume);
|
||||
|
||||
static struct platform_driver ucb1400_ts_driver = {
|
||||
.probe = ucb1400_ts_probe,
|
||||
.remove = ucb1400_ts_remove,
|
||||
.resume = ucb1400_ts_resume,
|
||||
.remove = __devexit_p(ucb1400_ts_remove),
|
||||
.driver = {
|
||||
.name = "ucb1400_ts",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &ucb1400_ts_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init ucb1400_ts_init(void)
|
||||
{
|
||||
return platform_driver_register(&ucb1400_ts_driver);
|
||||
}
|
||||
|
||||
static void __exit ucb1400_ts_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&ucb1400_ts_driver);
|
||||
}
|
||||
module_platform_driver(ucb1400_ts_driver);
|
||||
|
||||
module_param(adcsync, bool, 0444);
|
||||
MODULE_PARM_DESC(adcsync, "Synchronize touch readings with ADCSYNC pin.");
|
||||
@@ -479,8 +463,5 @@ MODULE_PARM_DESC(ts_delay_pressure,
|
||||
"delay between panel setup and pressure read."
|
||||
" Default = 0us.");
|
||||
|
||||
module_init(ucb1400_ts_init);
|
||||
module_exit(ucb1400_ts_exit);
|
||||
|
||||
MODULE_DESCRIPTION("Philips UCB1400 touchscreen driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
* - JASTEC USB touch controller/DigiTech DTR-02U
|
||||
* - Zytronic capacitive touchscreen
|
||||
* - NEXIO/iNexio
|
||||
* - Elo TouchSystems 2700 IntelliTouch
|
||||
*
|
||||
* Copyright (C) 2004-2007 by Daniel Ritz <daniel.ritz@gmx.ch>
|
||||
* Copyright (C) by Todd E. Johnson (mtouchusb.c)
|
||||
@@ -138,6 +139,7 @@ enum {
|
||||
DEVTYPE_ZYTRONIC,
|
||||
DEVTYPE_TC45USB,
|
||||
DEVTYPE_NEXIO,
|
||||
DEVTYPE_ELO,
|
||||
};
|
||||
|
||||
#define USB_DEVICE_HID_CLASS(vend, prod) \
|
||||
@@ -239,6 +241,10 @@ static const struct usb_device_id usbtouch_devices[] = {
|
||||
.driver_info = DEVTYPE_NEXIO},
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_TOUCHSCREEN_USB_ELO
|
||||
{USB_DEVICE(0x04e7, 0x0020), .driver_info = DEVTYPE_ELO},
|
||||
#endif
|
||||
|
||||
{}
|
||||
};
|
||||
|
||||
@@ -944,6 +950,24 @@ static int nexio_read_data(struct usbtouch_usb *usbtouch, unsigned char *pkt)
|
||||
#endif
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* ELO part
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_TOUCHSCREEN_USB_ELO
|
||||
|
||||
static int elo_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
|
||||
{
|
||||
dev->x = (pkt[3] << 8) | pkt[2];
|
||||
dev->y = (pkt[5] << 8) | pkt[4];
|
||||
dev->touch = pkt[6] > 0;
|
||||
dev->press = pkt[6];
|
||||
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* the different device descriptors
|
||||
*/
|
||||
@@ -953,6 +977,18 @@ static void usbtouch_process_multi(struct usbtouch_usb *usbtouch,
|
||||
#endif
|
||||
|
||||
static struct usbtouch_device_info usbtouch_dev_info[] = {
|
||||
#ifdef CONFIG_TOUCHSCREEN_USB_ELO
|
||||
[DEVTYPE_ELO] = {
|
||||
.min_xc = 0x0,
|
||||
.max_xc = 0x0fff,
|
||||
.min_yc = 0x0,
|
||||
.max_yc = 0x0fff,
|
||||
.max_press = 0xff,
|
||||
.rept_size = 8,
|
||||
.read_data = elo_read_data,
|
||||
},
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_TOUCHSCREEN_USB_EGALAX
|
||||
[DEVTYPE_EGALAX] = {
|
||||
.min_xc = 0x0,
|
||||
|
||||
@@ -331,19 +331,7 @@ static struct platform_driver w90x900ts_driver = {
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init w90x900ts_init(void)
|
||||
{
|
||||
return platform_driver_register(&w90x900ts_driver);
|
||||
}
|
||||
|
||||
static void __exit w90x900ts_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&w90x900ts_driver);
|
||||
}
|
||||
|
||||
module_init(w90x900ts_init);
|
||||
module_exit(w90x900ts_exit);
|
||||
module_platform_driver(w90x900ts_driver);
|
||||
|
||||
MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>");
|
||||
MODULE_DESCRIPTION("w90p910 touch screen driver!");
|
||||
|
||||
@@ -401,18 +401,7 @@ static struct platform_driver wm831x_ts_driver = {
|
||||
.probe = wm831x_ts_probe,
|
||||
.remove = __devexit_p(wm831x_ts_remove),
|
||||
};
|
||||
|
||||
static int __init wm831x_ts_init(void)
|
||||
{
|
||||
return platform_driver_register(&wm831x_ts_driver);
|
||||
}
|
||||
module_init(wm831x_ts_init);
|
||||
|
||||
static void __exit wm831x_ts_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&wm831x_ts_driver);
|
||||
}
|
||||
module_exit(wm831x_ts_exit);
|
||||
module_platform_driver(wm831x_ts_driver);
|
||||
|
||||
/* Module information */
|
||||
MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
|
||||
|
||||
@@ -224,19 +224,7 @@ static struct platform_driver zylonite_wm97xx_driver = {
|
||||
.name = "wm97xx-touch",
|
||||
},
|
||||
};
|
||||
|
||||
static int __init zylonite_wm97xx_init(void)
|
||||
{
|
||||
return platform_driver_register(&zylonite_wm97xx_driver);
|
||||
}
|
||||
|
||||
static void __exit zylonite_wm97xx_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&zylonite_wm97xx_driver);
|
||||
}
|
||||
|
||||
module_init(zylonite_wm97xx_init);
|
||||
module_exit(zylonite_wm97xx_exit);
|
||||
module_platform_driver(zylonite_wm97xx_driver);
|
||||
|
||||
/* Module information */
|
||||
MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
|
||||
|
||||
Reference in New Issue
Block a user