Creation of Cybook 2416 (actually Gen4) repository
This commit is contained in:
188
drivers/input/touchscreen/Kconfig
Normal file
188
drivers/input/touchscreen/Kconfig
Normal file
@@ -0,0 +1,188 @@
|
||||
#
|
||||
# Mouse driver configuration
|
||||
#
|
||||
menuconfig INPUT_TOUCHSCREEN
|
||||
bool "Touchscreens"
|
||||
help
|
||||
Say Y here, and a list of supported touchscreens will be displayed.
|
||||
This option doesn't affect the kernel.
|
||||
|
||||
If unsure, say Y.
|
||||
|
||||
if INPUT_TOUCHSCREEN
|
||||
|
||||
config TOUCHSCREEN_ADS7846
|
||||
tristate "ADS 7846/7843 based touchscreens"
|
||||
depends on SPI_MASTER
|
||||
depends on HWMON = n || HWMON
|
||||
help
|
||||
Say Y here if you have a touchscreen interface using the
|
||||
ADS7846 or ADS7843 controller, and your board-specific setup
|
||||
code includes that in its table of SPI devices.
|
||||
|
||||
If HWMON is selected, and the driver is told the reference voltage
|
||||
on your board, you will also get hwmon interfaces for the voltage
|
||||
(and on ads7846, temperature) sensors of this chip.
|
||||
|
||||
If unsure, say N (but it's safe to say "Y").
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ads7846.
|
||||
|
||||
config TOUCHSCREEN_BITSY
|
||||
tristate "Compaq iPAQ H3600 (Bitsy) touchscreen"
|
||||
depends on SA1100_BITSY
|
||||
select SERIO
|
||||
help
|
||||
Say Y here if you have the h3600 (Bitsy) touchscreen.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called h3600_ts_input.
|
||||
|
||||
config TOUCHSCREEN_CORGI
|
||||
tristate "SharpSL (Corgi and Spitz series) touchscreen driver"
|
||||
depends on PXA_SHARPSL
|
||||
default y
|
||||
help
|
||||
Say Y here to enable the driver for the touchscreen on the
|
||||
Sharp SL-C7xx and SL-Cxx00 series of PDAs.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called corgi_ts.
|
||||
|
||||
config TOUCHSCREEN_S3C
|
||||
tristate "S3C touchscreen driver"
|
||||
depends on ARCH_S3C2410
|
||||
default y
|
||||
help
|
||||
Say Y here to enable the driver for the touchscreen on the
|
||||
S3C SMDK board.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called s3c24xx_ts.
|
||||
|
||||
config TOUCHSCREEN_GUNZE
|
||||
tristate "Gunze AHL-51S touchscreen"
|
||||
select SERIO
|
||||
help
|
||||
Say Y here if you have the Gunze AHL-51 touchscreen connected to
|
||||
your system.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called gunze.
|
||||
|
||||
config TOUCHSCREEN_ELO
|
||||
tristate "Elo serial touchscreens"
|
||||
select SERIO
|
||||
help
|
||||
Say Y here if you have an Elo 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 elo.
|
||||
|
||||
config TOUCHSCREEN_MTOUCH
|
||||
tristate "MicroTouch serial touchscreens"
|
||||
select SERIO
|
||||
help
|
||||
Say Y here if you have a MicroTouch (3M) 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 mtouch.
|
||||
|
||||
config TOUCHSCREEN_MK712
|
||||
tristate "ICS MicroClock MK712 touchscreen"
|
||||
help
|
||||
Say Y here if you have the ICS MicroClock MK712 touchscreen
|
||||
controller chip in your system.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called mk712.
|
||||
|
||||
config TOUCHSCREEN_HP600
|
||||
tristate "HP Jornada 680/690 touchscreen"
|
||||
depends on SH_HP6XX && SH_ADC
|
||||
help
|
||||
Say Y here if you have a HP Jornada 680 or 690 and want to
|
||||
support the built-in touchscreen.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called hp680_ts_input.
|
||||
|
||||
config TOUCHSCREEN_PENMOUNT
|
||||
tristate "Penmount serial touchscreen"
|
||||
select SERIO
|
||||
help
|
||||
Say Y here if you have a Penmount 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 penmount.
|
||||
|
||||
config TOUCHSCREEN_TOUCHRIGHT
|
||||
tristate "Touchright serial touchscreen"
|
||||
select SERIO
|
||||
help
|
||||
Say Y here if you have a Touchright 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 touchright.
|
||||
|
||||
config TOUCHSCREEN_TOUCHWIN
|
||||
tristate "Touchwin serial touchscreen"
|
||||
select SERIO
|
||||
help
|
||||
Say Y here if you have a Touchwin 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 touchwin.
|
||||
|
||||
config TOUCHSCREEN_UCB1400
|
||||
tristate "Philips UCB1400 touchscreen"
|
||||
select AC97_BUS
|
||||
help
|
||||
This enables support for the Philips UCB1400 touchscreen interface.
|
||||
The UCB1400 is an AC97 audio codec. The touchscreen interface
|
||||
will be initialized only after the ALSA subsystem has been
|
||||
brought up and the UCB1400 detected. You therefore have to
|
||||
configure ALSA support as well (either built-in or modular,
|
||||
independently of whether this driver is itself built-in or
|
||||
modular) for this driver to work.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ucb1400_ts.
|
||||
#Qisda Tony 090406, add Auo touch i2c driver [
|
||||
config TOUCHSCREEN_IIC_QISDA
|
||||
tristate "Touch Screen for IIC Qisda"
|
||||
default n
|
||||
help
|
||||
It will use IIC to read/write the data from touch panel.
|
||||
Instead of ADC detcection.
|
||||
Tony.YC.Huang@qisda.com
|
||||
#Qisda Tony 090406, add Auo touch i2c driver ]
|
||||
endif
|
||||
22
drivers/input/touchscreen/Makefile
Normal file
22
drivers/input/touchscreen/Makefile
Normal file
@@ -0,0 +1,22 @@
|
||||
#
|
||||
# Makefile for the mouse drivers.
|
||||
#
|
||||
|
||||
# Each configuration option enables a list of files.
|
||||
|
||||
obj-$(CONFIG_TOUCHSCREEN_ADS7846) += ads7846.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_BITSY) += h3600_ts_input.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_CORGI) += corgi_ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_GUNZE) += gunze.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_MTOUCH) += mtouch.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_MK712) += mk712.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_HP600) += hp680_ts_input.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_PENMOUNT) += penmount.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN) += touchwin.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_UCB1400) += ucb1400_ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_S3C) += s3c-ts.o
|
||||
#Qisda Tony 090406, add Auo touch i2c driver [
|
||||
obj-$(CONFIG_TOUCHSCREEN_IIC_QISDA) += s3c_ts_iic.o
|
||||
#Qisda Tony 090406, add Auo touch i2c driver ]
|
||||
1141
drivers/input/touchscreen/ads7846.c
Normal file
1141
drivers/input/touchscreen/ads7846.c
Normal file
File diff suppressed because it is too large
Load Diff
383
drivers/input/touchscreen/corgi_ts.c
Normal file
383
drivers/input/touchscreen/corgi_ts.c
Normal file
@@ -0,0 +1,383 @@
|
||||
/*
|
||||
* Touchscreen driver for Sharp SL-C7xx and SL-Cxx00 models
|
||||
*
|
||||
* Copyright (c) 2004-2005 Richard Purdie
|
||||
*
|
||||
* 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/delay.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/irq.h>
|
||||
|
||||
#include <asm/arch/sharpsl.h>
|
||||
#include <asm/arch/hardware.h>
|
||||
#include <asm/arch/pxa-regs.h>
|
||||
|
||||
|
||||
#define PWR_MODE_ACTIVE 0
|
||||
#define PWR_MODE_SUSPEND 1
|
||||
|
||||
#define X_AXIS_MAX 3830
|
||||
#define X_AXIS_MIN 150
|
||||
#define Y_AXIS_MAX 3830
|
||||
#define Y_AXIS_MIN 190
|
||||
#define PRESSURE_MIN 0
|
||||
#define PRESSURE_MAX 15000
|
||||
|
||||
struct ts_event {
|
||||
short pressure;
|
||||
short x;
|
||||
short y;
|
||||
};
|
||||
|
||||
struct corgi_ts {
|
||||
struct input_dev *input;
|
||||
struct timer_list timer;
|
||||
struct ts_event tc;
|
||||
int pendown;
|
||||
int power_mode;
|
||||
int irq_gpio;
|
||||
struct corgits_machinfo *machinfo;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_PXA25x
|
||||
#define CCNT(a) asm volatile ("mrc p14, 0, %0, C1, C0, 0" : "=r"(a))
|
||||
#define PMNC_GET(x) asm volatile ("mrc p14, 0, %0, C0, C0, 0" : "=r"(x))
|
||||
#define PMNC_SET(x) asm volatile ("mcr p14, 0, %0, C0, C0, 0" : : "r"(x))
|
||||
#endif
|
||||
#ifdef CONFIG_PXA27x
|
||||
#define CCNT(a) asm volatile ("mrc p14, 0, %0, C1, C1, 0" : "=r"(a))
|
||||
#define PMNC_GET(x) asm volatile ("mrc p14, 0, %0, C0, C1, 0" : "=r"(x))
|
||||
#define PMNC_SET(x) asm volatile ("mcr p14, 0, %0, C0, C1, 0" : : "r"(x))
|
||||
#endif
|
||||
|
||||
/* ADS7846 Touch Screen Controller bit definitions */
|
||||
#define ADSCTRL_PD0 (1u << 0) /* PD0 */
|
||||
#define ADSCTRL_PD1 (1u << 1) /* PD1 */
|
||||
#define ADSCTRL_DFR (1u << 2) /* SER/DFR */
|
||||
#define ADSCTRL_MOD (1u << 3) /* Mode */
|
||||
#define ADSCTRL_ADR_SH 4 /* Address setting */
|
||||
#define ADSCTRL_STS (1u << 7) /* Start Bit */
|
||||
|
||||
/* External Functions */
|
||||
extern unsigned int get_clk_frequency_khz(int info);
|
||||
|
||||
static unsigned long calc_waittime(struct corgi_ts *corgi_ts)
|
||||
{
|
||||
unsigned long hsync_len = corgi_ts->machinfo->get_hsync_len();
|
||||
|
||||
if (hsync_len)
|
||||
return get_clk_frequency_khz(0)*1000/hsync_len;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sync_receive_data_send_cmd(struct corgi_ts *corgi_ts, int doRecive, int doSend,
|
||||
unsigned int address, unsigned long wait_time)
|
||||
{
|
||||
unsigned long timer1 = 0, timer2, pmnc = 0;
|
||||
int pos = 0;
|
||||
|
||||
if (wait_time && doSend) {
|
||||
PMNC_GET(pmnc);
|
||||
if (!(pmnc & 0x01))
|
||||
PMNC_SET(0x01);
|
||||
|
||||
/* polling HSync */
|
||||
corgi_ts->machinfo->wait_hsync();
|
||||
/* get CCNT */
|
||||
CCNT(timer1);
|
||||
}
|
||||
|
||||
if (doRecive)
|
||||
pos = corgi_ssp_ads7846_get();
|
||||
|
||||
if (doSend) {
|
||||
int cmd = ADSCTRL_PD0 | ADSCTRL_PD1 | (address << ADSCTRL_ADR_SH) | ADSCTRL_STS;
|
||||
/* dummy command */
|
||||
corgi_ssp_ads7846_put(cmd);
|
||||
corgi_ssp_ads7846_get();
|
||||
|
||||
if (wait_time) {
|
||||
/* Wait after HSync */
|
||||
CCNT(timer2);
|
||||
if (timer2-timer1 > wait_time) {
|
||||
/* too slow - timeout, try again */
|
||||
corgi_ts->machinfo->wait_hsync();
|
||||
/* get OSCR */
|
||||
CCNT(timer1);
|
||||
/* Wait after HSync */
|
||||
CCNT(timer2);
|
||||
}
|
||||
while (timer2 - timer1 < wait_time)
|
||||
CCNT(timer2);
|
||||
}
|
||||
corgi_ssp_ads7846_put(cmd);
|
||||
if (wait_time && !(pmnc & 0x01))
|
||||
PMNC_SET(pmnc);
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
static int read_xydata(struct corgi_ts *corgi_ts)
|
||||
{
|
||||
unsigned int x, y, z1, z2;
|
||||
unsigned long flags, wait_time;
|
||||
|
||||
/* critical section */
|
||||
local_irq_save(flags);
|
||||
corgi_ssp_ads7846_lock();
|
||||
wait_time = calc_waittime(corgi_ts);
|
||||
|
||||
/* Y-axis */
|
||||
sync_receive_data_send_cmd(corgi_ts, 0, 1, 1u, wait_time);
|
||||
|
||||
/* Y-axis */
|
||||
sync_receive_data_send_cmd(corgi_ts, 1, 1, 1u, wait_time);
|
||||
|
||||
/* X-axis */
|
||||
y = sync_receive_data_send_cmd(corgi_ts, 1, 1, 5u, wait_time);
|
||||
|
||||
/* Z1 */
|
||||
x = sync_receive_data_send_cmd(corgi_ts, 1, 1, 3u, wait_time);
|
||||
|
||||
/* Z2 */
|
||||
z1 = sync_receive_data_send_cmd(corgi_ts, 1, 1, 4u, wait_time);
|
||||
z2 = sync_receive_data_send_cmd(corgi_ts, 1, 0, 4u, wait_time);
|
||||
|
||||
/* Power-Down Enable */
|
||||
corgi_ssp_ads7846_put((1u << ADSCTRL_ADR_SH) | ADSCTRL_STS);
|
||||
corgi_ssp_ads7846_get();
|
||||
|
||||
corgi_ssp_ads7846_unlock();
|
||||
local_irq_restore(flags);
|
||||
|
||||
if (x== 0 || y == 0 || z1 == 0 || (x * (z2 - z1) / z1) >= 15000) {
|
||||
corgi_ts->tc.pressure = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
corgi_ts->tc.x = x;
|
||||
corgi_ts->tc.y = y;
|
||||
corgi_ts->tc.pressure = (x * (z2 - z1)) / z1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void new_data(struct corgi_ts *corgi_ts)
|
||||
{
|
||||
struct input_dev *dev = corgi_ts->input;
|
||||
|
||||
if (corgi_ts->power_mode != PWR_MODE_ACTIVE)
|
||||
return;
|
||||
|
||||
if (!corgi_ts->tc.pressure && corgi_ts->pendown == 0)
|
||||
return;
|
||||
|
||||
input_report_abs(dev, ABS_X, corgi_ts->tc.x);
|
||||
input_report_abs(dev, ABS_Y, corgi_ts->tc.y);
|
||||
input_report_abs(dev, ABS_PRESSURE, corgi_ts->tc.pressure);
|
||||
input_report_key(dev, BTN_TOUCH, corgi_ts->pendown);
|
||||
input_sync(dev);
|
||||
}
|
||||
|
||||
static void ts_interrupt_main(struct corgi_ts *corgi_ts, int isTimer)
|
||||
{
|
||||
if ((GPLR(IRQ_TO_GPIO(corgi_ts->irq_gpio)) & GPIO_bit(IRQ_TO_GPIO(corgi_ts->irq_gpio))) == 0) {
|
||||
/* Disable Interrupt */
|
||||
set_irq_type(corgi_ts->irq_gpio, IRQT_NOEDGE);
|
||||
if (read_xydata(corgi_ts)) {
|
||||
corgi_ts->pendown = 1;
|
||||
new_data(corgi_ts);
|
||||
}
|
||||
mod_timer(&corgi_ts->timer, jiffies + HZ / 100);
|
||||
} else {
|
||||
if (corgi_ts->pendown == 1 || corgi_ts->pendown == 2) {
|
||||
mod_timer(&corgi_ts->timer, jiffies + HZ / 100);
|
||||
corgi_ts->pendown++;
|
||||
return;
|
||||
}
|
||||
|
||||
if (corgi_ts->pendown) {
|
||||
corgi_ts->tc.pressure = 0;
|
||||
new_data(corgi_ts);
|
||||
}
|
||||
|
||||
/* Enable Falling Edge */
|
||||
set_irq_type(corgi_ts->irq_gpio, IRQT_FALLING);
|
||||
corgi_ts->pendown = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void corgi_ts_timer(unsigned long data)
|
||||
{
|
||||
struct corgi_ts *corgits_data = (struct corgi_ts *) data;
|
||||
|
||||
ts_interrupt_main(corgits_data, 1);
|
||||
}
|
||||
|
||||
static irqreturn_t ts_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct corgi_ts *corgits_data = dev_id;
|
||||
|
||||
ts_interrupt_main(corgits_data, 0);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int corgits_suspend(struct platform_device *dev, pm_message_t state)
|
||||
{
|
||||
struct corgi_ts *corgi_ts = platform_get_drvdata(dev);
|
||||
|
||||
if (corgi_ts->pendown) {
|
||||
del_timer_sync(&corgi_ts->timer);
|
||||
corgi_ts->tc.pressure = 0;
|
||||
new_data(corgi_ts);
|
||||
corgi_ts->pendown = 0;
|
||||
}
|
||||
corgi_ts->power_mode = PWR_MODE_SUSPEND;
|
||||
|
||||
corgi_ssp_ads7846_putget((1u << ADSCTRL_ADR_SH) | ADSCTRL_STS);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int corgits_resume(struct platform_device *dev)
|
||||
{
|
||||
struct corgi_ts *corgi_ts = platform_get_drvdata(dev);
|
||||
|
||||
corgi_ssp_ads7846_putget((4u << ADSCTRL_ADR_SH) | ADSCTRL_STS);
|
||||
/* Enable Falling Edge */
|
||||
set_irq_type(corgi_ts->irq_gpio, IRQT_FALLING);
|
||||
corgi_ts->power_mode = PWR_MODE_ACTIVE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define corgits_suspend NULL
|
||||
#define corgits_resume NULL
|
||||
#endif
|
||||
|
||||
static int __init corgits_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct corgi_ts *corgi_ts;
|
||||
struct input_dev *input_dev;
|
||||
int err = -ENOMEM;
|
||||
|
||||
corgi_ts = kzalloc(sizeof(struct corgi_ts), GFP_KERNEL);
|
||||
input_dev = input_allocate_device();
|
||||
if (!corgi_ts || !input_dev)
|
||||
goto fail1;
|
||||
|
||||
platform_set_drvdata(pdev, corgi_ts);
|
||||
|
||||
corgi_ts->machinfo = pdev->dev.platform_data;
|
||||
corgi_ts->irq_gpio = platform_get_irq(pdev, 0);
|
||||
|
||||
if (corgi_ts->irq_gpio < 0) {
|
||||
err = -ENODEV;
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
corgi_ts->input = input_dev;
|
||||
|
||||
init_timer(&corgi_ts->timer);
|
||||
corgi_ts->timer.data = (unsigned long) corgi_ts;
|
||||
corgi_ts->timer.function = corgi_ts_timer;
|
||||
|
||||
input_dev->name = "Corgi Touchscreen";
|
||||
input_dev->phys = "corgits/input0";
|
||||
input_dev->id.bustype = BUS_HOST;
|
||||
input_dev->id.vendor = 0x0001;
|
||||
input_dev->id.product = 0x0002;
|
||||
input_dev->id.version = 0x0100;
|
||||
input_dev->cdev.dev = &pdev->dev;
|
||||
input_dev->private = corgi_ts;
|
||||
|
||||
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
|
||||
input_dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
|
||||
input_set_abs_params(input_dev, ABS_X, X_AXIS_MIN, X_AXIS_MAX, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_Y, Y_AXIS_MIN, Y_AXIS_MAX, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_PRESSURE, PRESSURE_MIN, PRESSURE_MAX, 0, 0);
|
||||
|
||||
pxa_gpio_mode(IRQ_TO_GPIO(corgi_ts->irq_gpio) | GPIO_IN);
|
||||
|
||||
/* Initiaize ADS7846 Difference Reference mode */
|
||||
corgi_ssp_ads7846_putget((1u << ADSCTRL_ADR_SH) | ADSCTRL_STS);
|
||||
mdelay(5);
|
||||
corgi_ssp_ads7846_putget((3u << ADSCTRL_ADR_SH) | ADSCTRL_STS);
|
||||
mdelay(5);
|
||||
corgi_ssp_ads7846_putget((4u << ADSCTRL_ADR_SH) | ADSCTRL_STS);
|
||||
mdelay(5);
|
||||
corgi_ssp_ads7846_putget((5u << ADSCTRL_ADR_SH) | ADSCTRL_STS);
|
||||
mdelay(5);
|
||||
|
||||
if (request_irq(corgi_ts->irq_gpio, ts_interrupt, IRQF_DISABLED, "ts", corgi_ts)) {
|
||||
err = -EBUSY;
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
err = input_register_device(corgi_ts->input);
|
||||
if (err)
|
||||
goto fail2;
|
||||
|
||||
corgi_ts->power_mode = PWR_MODE_ACTIVE;
|
||||
|
||||
/* Enable Falling Edge */
|
||||
set_irq_type(corgi_ts->irq_gpio, IRQT_FALLING);
|
||||
|
||||
return 0;
|
||||
|
||||
fail2: free_irq(corgi_ts->irq_gpio, corgi_ts);
|
||||
fail1: input_free_device(input_dev);
|
||||
kfree(corgi_ts);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int corgits_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct corgi_ts *corgi_ts = platform_get_drvdata(pdev);
|
||||
|
||||
free_irq(corgi_ts->irq_gpio, corgi_ts);
|
||||
del_timer_sync(&corgi_ts->timer);
|
||||
corgi_ts->machinfo->put_hsync();
|
||||
input_unregister_device(corgi_ts->input);
|
||||
kfree(corgi_ts);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver corgits_driver = {
|
||||
.probe = corgits_probe,
|
||||
.remove = corgits_remove,
|
||||
.suspend = corgits_suspend,
|
||||
.resume = corgits_resume,
|
||||
.driver = {
|
||||
.name = "corgi-ts",
|
||||
},
|
||||
};
|
||||
|
||||
static int __devinit corgits_init(void)
|
||||
{
|
||||
return platform_driver_register(&corgits_driver);
|
||||
}
|
||||
|
||||
static void __exit corgits_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&corgits_driver);
|
||||
}
|
||||
|
||||
module_init(corgits_init);
|
||||
module_exit(corgits_exit);
|
||||
|
||||
MODULE_AUTHOR("Richard Purdie <rpurdie@rpsys.net>");
|
||||
MODULE_DESCRIPTION("Corgi TouchScreen Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
409
drivers/input/touchscreen/elo.c
Normal file
409
drivers/input/touchscreen/elo.c
Normal file
@@ -0,0 +1,409 @@
|
||||
/*
|
||||
* Elo serial touchscreen driver
|
||||
*
|
||||
* Copyright (c) 2004 Vojtech Pavlik
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This driver can handle serial Elo touchscreens using either the Elo standard
|
||||
* 'E271-2210' 10-byte protocol, Elo legacy 'E281A-4002' 6-byte protocol, Elo
|
||||
* legacy 'E271-140' 4-byte protocol and Elo legacy 'E261-280' 3-byte protocol.
|
||||
*/
|
||||
|
||||
#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 "Elo serial touchscreen driver"
|
||||
|
||||
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
/*
|
||||
* Definitions & global arrays.
|
||||
*/
|
||||
|
||||
#define ELO_MAX_LENGTH 10
|
||||
|
||||
#define ELO10_PACKET_LEN 8
|
||||
#define ELO10_TOUCH 0x03
|
||||
#define ELO10_PRESSURE 0x80
|
||||
|
||||
#define ELO10_LEAD_BYTE 'U'
|
||||
|
||||
#define ELO10_ID_CMD 'i'
|
||||
|
||||
#define ELO10_TOUCH_PACKET 'T'
|
||||
#define ELO10_ACK_PACKET 'A'
|
||||
#define ELI10_ID_PACKET 'I'
|
||||
|
||||
/*
|
||||
* Per-touchscreen data.
|
||||
*/
|
||||
|
||||
struct elo {
|
||||
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 csum;
|
||||
unsigned char data[ELO_MAX_LENGTH];
|
||||
unsigned char response[ELO10_PACKET_LEN];
|
||||
char phys[32];
|
||||
};
|
||||
|
||||
static void elo_process_data_10(struct elo *elo, unsigned char data)
|
||||
{
|
||||
struct input_dev *dev = elo->dev;
|
||||
|
||||
elo->data[elo->idx] = data;
|
||||
switch (elo->idx++) {
|
||||
case 0:
|
||||
elo->csum = 0xaa;
|
||||
if (data != ELO10_LEAD_BYTE) {
|
||||
pr_debug("elo: unsynchronized data: 0x%02x\n", data);
|
||||
elo->idx = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case 9:
|
||||
elo->idx = 0;
|
||||
if (data != elo->csum) {
|
||||
pr_debug("elo: bad checksum: 0x%02x, expected 0x%02x\n",
|
||||
data, elo->csum);
|
||||
break;
|
||||
}
|
||||
if (elo->data[1] != elo->expected_packet) {
|
||||
if (elo->data[1] != ELO10_TOUCH_PACKET)
|
||||
pr_debug("elo: unexpected packet: 0x%02x\n",
|
||||
elo->data[1]);
|
||||
break;
|
||||
}
|
||||
if (likely(elo->data[1] == ELO10_TOUCH_PACKET)) {
|
||||
input_report_abs(dev, ABS_X, (elo->data[4] << 8) | elo->data[3]);
|
||||
input_report_abs(dev, ABS_Y, (elo->data[6] << 8) | elo->data[5]);
|
||||
if (elo->data[2] & ELO10_PRESSURE)
|
||||
input_report_abs(dev, ABS_PRESSURE,
|
||||
(elo->data[8] << 8) | elo->data[7]);
|
||||
input_report_key(dev, BTN_TOUCH, elo->data[2] & ELO10_TOUCH);
|
||||
input_sync(dev);
|
||||
} else if (elo->data[1] == ELO10_ACK_PACKET) {
|
||||
if (elo->data[2] == '0')
|
||||
elo->expected_packet = ELO10_TOUCH_PACKET;
|
||||
complete(&elo->cmd_done);
|
||||
} else {
|
||||
memcpy(elo->response, &elo->data[1], ELO10_PACKET_LEN);
|
||||
elo->expected_packet = ELO10_ACK_PACKET;
|
||||
}
|
||||
break;
|
||||
}
|
||||
elo->csum += data;
|
||||
}
|
||||
|
||||
static void elo_process_data_6(struct elo *elo, unsigned char data)
|
||||
{
|
||||
struct input_dev *dev = elo->dev;
|
||||
|
||||
elo->data[elo->idx] = data;
|
||||
|
||||
switch (elo->idx++) {
|
||||
|
||||
case 0: if ((data & 0xc0) != 0xc0) elo->idx = 0; break;
|
||||
case 1: if ((data & 0xc0) != 0x80) elo->idx = 0; break;
|
||||
case 2: if ((data & 0xc0) != 0x40) elo->idx = 0; break;
|
||||
|
||||
case 3:
|
||||
if (data & 0xc0) {
|
||||
elo->idx = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
input_report_abs(dev, ABS_X, ((elo->data[0] & 0x3f) << 6) | (elo->data[1] & 0x3f));
|
||||
input_report_abs(dev, ABS_Y, ((elo->data[2] & 0x3f) << 6) | (elo->data[3] & 0x3f));
|
||||
|
||||
if (elo->id == 2) {
|
||||
input_report_key(dev, BTN_TOUCH, 1);
|
||||
input_sync(dev);
|
||||
elo->idx = 0;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 4:
|
||||
if (data) {
|
||||
input_sync(dev);
|
||||
elo->idx = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case 5:
|
||||
if ((data & 0xf0) == 0) {
|
||||
input_report_abs(dev, ABS_PRESSURE, elo->data[5]);
|
||||
input_report_key(dev, BTN_TOUCH, !!elo->data[5]);
|
||||
}
|
||||
input_sync(dev);
|
||||
elo->idx = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void elo_process_data_3(struct elo *elo, unsigned char data)
|
||||
{
|
||||
struct input_dev *dev = elo->dev;
|
||||
|
||||
elo->data[elo->idx] = data;
|
||||
|
||||
switch (elo->idx++) {
|
||||
|
||||
case 0:
|
||||
if ((data & 0x7f) != 0x01)
|
||||
elo->idx = 0;
|
||||
break;
|
||||
case 2:
|
||||
input_report_key(dev, BTN_TOUCH, !(elo->data[1] & 0x80));
|
||||
input_report_abs(dev, ABS_X, elo->data[1]);
|
||||
input_report_abs(dev, ABS_Y, elo->data[2]);
|
||||
input_sync(dev);
|
||||
elo->idx = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static irqreturn_t elo_interrupt(struct serio *serio,
|
||||
unsigned char data, unsigned int flags)
|
||||
{
|
||||
struct elo *elo = serio_get_drvdata(serio);
|
||||
|
||||
switch(elo->id) {
|
||||
case 0:
|
||||
elo_process_data_10(elo, data);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
case 2:
|
||||
elo_process_data_6(elo, data);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
elo_process_data_3(elo, data);
|
||||
break;
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int elo_command_10(struct elo *elo, unsigned char *packet)
|
||||
{
|
||||
int rc = -1;
|
||||
int i;
|
||||
unsigned char csum = 0xaa + ELO10_LEAD_BYTE;
|
||||
|
||||
mutex_lock(&elo->cmd_mutex);
|
||||
|
||||
serio_pause_rx(elo->serio);
|
||||
elo->expected_packet = toupper(packet[0]);
|
||||
init_completion(&elo->cmd_done);
|
||||
serio_continue_rx(elo->serio);
|
||||
|
||||
if (serio_write(elo->serio, ELO10_LEAD_BYTE))
|
||||
goto out;
|
||||
|
||||
for (i = 0; i < ELO10_PACKET_LEN; i++) {
|
||||
csum += packet[i];
|
||||
if (serio_write(elo->serio, packet[i]))
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (serio_write(elo->serio, csum))
|
||||
goto out;
|
||||
|
||||
wait_for_completion_timeout(&elo->cmd_done, HZ);
|
||||
|
||||
if (elo->expected_packet == ELO10_TOUCH_PACKET) {
|
||||
/* We are back in reporting mode, the command was ACKed */
|
||||
memcpy(packet, elo->response, ELO10_PACKET_LEN);
|
||||
rc = 0;
|
||||
}
|
||||
|
||||
out:
|
||||
mutex_unlock(&elo->cmd_mutex);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int elo_setup_10(struct elo *elo)
|
||||
{
|
||||
static const char *elo_types[] = { "Accu", "Dura", "Intelli", "Carroll" };
|
||||
struct input_dev *dev = elo->dev;
|
||||
unsigned char packet[ELO10_PACKET_LEN] = { ELO10_ID_CMD };
|
||||
|
||||
if (elo_command_10(elo, packet))
|
||||
return -1;
|
||||
|
||||
dev->id.version = (packet[5] << 8) | packet[4];
|
||||
|
||||
input_set_abs_params(dev, ABS_X, 96, 4000, 0, 0);
|
||||
input_set_abs_params(dev, ABS_Y, 96, 4000, 0, 0);
|
||||
if (packet[3] & ELO10_PRESSURE)
|
||||
input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0);
|
||||
|
||||
printk(KERN_INFO "elo: %sTouch touchscreen, fw: %02x.%02x, "
|
||||
"features: %x02x, controller: 0x%02x\n",
|
||||
elo_types[(packet[1] -'0') & 0x03],
|
||||
packet[5], packet[4], packet[3], packet[7]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* elo_disconnect() is the opposite of elo_connect()
|
||||
*/
|
||||
|
||||
static void elo_disconnect(struct serio *serio)
|
||||
{
|
||||
struct elo *elo = serio_get_drvdata(serio);
|
||||
|
||||
input_get_device(elo->dev);
|
||||
input_unregister_device(elo->dev);
|
||||
serio_close(serio);
|
||||
serio_set_drvdata(serio, NULL);
|
||||
input_put_device(elo->dev);
|
||||
kfree(elo);
|
||||
}
|
||||
|
||||
/*
|
||||
* elo_connect() is the routine that is called when someone adds a
|
||||
* new serio device that supports Gunze protocol and registers it as
|
||||
* an input device.
|
||||
*/
|
||||
|
||||
static int elo_connect(struct serio *serio, struct serio_driver *drv)
|
||||
{
|
||||
struct elo *elo;
|
||||
struct input_dev *input_dev;
|
||||
int err;
|
||||
|
||||
elo = kzalloc(sizeof(struct elo), GFP_KERNEL);
|
||||
input_dev = input_allocate_device();
|
||||
if (!elo || !input_dev) {
|
||||
err = -ENOMEM;
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
elo->serio = serio;
|
||||
elo->id = serio->id.id;
|
||||
elo->dev = input_dev;
|
||||
elo->expected_packet = ELO10_TOUCH_PACKET;
|
||||
mutex_init(&elo->cmd_mutex);
|
||||
init_completion(&elo->cmd_done);
|
||||
snprintf(elo->phys, sizeof(elo->phys), "%s/input0", serio->phys);
|
||||
|
||||
input_dev->private = elo;
|
||||
input_dev->name = "Elo Serial TouchScreen";
|
||||
input_dev->phys = elo->phys;
|
||||
input_dev->id.bustype = BUS_RS232;
|
||||
input_dev->id.vendor = SERIO_ELO;
|
||||
input_dev->id.product = elo->id;
|
||||
input_dev->id.version = 0x0100;
|
||||
input_dev->cdev.dev = &serio->dev;
|
||||
|
||||
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
|
||||
input_dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
|
||||
|
||||
serio_set_drvdata(serio, elo);
|
||||
err = serio_open(serio, drv);
|
||||
if (err)
|
||||
goto fail2;
|
||||
|
||||
switch (elo->id) {
|
||||
|
||||
case 0: /* 10-byte protocol */
|
||||
if (elo_setup_10(elo))
|
||||
goto fail3;
|
||||
|
||||
break;
|
||||
|
||||
case 1: /* 6-byte protocol */
|
||||
input_set_abs_params(input_dev, ABS_PRESSURE, 0, 15, 0, 0);
|
||||
|
||||
case 2: /* 4-byte protocol */
|
||||
input_set_abs_params(input_dev, ABS_X, 96, 4000, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_Y, 96, 4000, 0, 0);
|
||||
break;
|
||||
|
||||
case 3: /* 3-byte protocol */
|
||||
input_set_abs_params(input_dev, ABS_X, 0, 255, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_Y, 0, 255, 0, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
err = input_register_device(elo->dev);
|
||||
if (err)
|
||||
goto fail3;
|
||||
|
||||
return 0;
|
||||
|
||||
fail3: serio_close(serio);
|
||||
fail2: serio_set_drvdata(serio, NULL);
|
||||
fail1: input_free_device(input_dev);
|
||||
kfree(elo);
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* The serio driver structure.
|
||||
*/
|
||||
|
||||
static struct serio_device_id elo_serio_ids[] = {
|
||||
{
|
||||
.type = SERIO_RS232,
|
||||
.proto = SERIO_ELO,
|
||||
.id = SERIO_ANY,
|
||||
.extra = SERIO_ANY,
|
||||
},
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(serio, elo_serio_ids);
|
||||
|
||||
static struct serio_driver elo_drv = {
|
||||
.driver = {
|
||||
.name = "elo",
|
||||
},
|
||||
.description = DRIVER_DESC,
|
||||
.id_table = elo_serio_ids,
|
||||
.interrupt = elo_interrupt,
|
||||
.connect = elo_connect,
|
||||
.disconnect = elo_disconnect,
|
||||
};
|
||||
|
||||
/*
|
||||
* The functions for inserting/removing us as a module.
|
||||
*/
|
||||
|
||||
static int __init elo_init(void)
|
||||
{
|
||||
return serio_register_driver(&elo_drv);
|
||||
}
|
||||
|
||||
static void __exit elo_exit(void)
|
||||
{
|
||||
serio_unregister_driver(&elo_drv);
|
||||
}
|
||||
|
||||
module_init(elo_init);
|
||||
module_exit(elo_exit);
|
||||
206
drivers/input/touchscreen/gunze.c
Normal file
206
drivers/input/touchscreen/gunze.c
Normal file
@@ -0,0 +1,206 @@
|
||||
/*
|
||||
* $Id: gunze.c,v 1.1.1.1 2007/06/12 07:27:09 eyryu Exp $
|
||||
*
|
||||
* Copyright (c) 2000-2001 Vojtech Pavlik
|
||||
*/
|
||||
|
||||
/*
|
||||
* Gunze AHL-51S touchscreen driver for Linux
|
||||
*/
|
||||
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
* Should you need to contact me, the author, you can do so either by
|
||||
* e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
|
||||
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
|
||||
*/
|
||||
|
||||
#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>
|
||||
|
||||
#define DRIVER_DESC "Gunze AHL-51S touchscreen driver"
|
||||
|
||||
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
/*
|
||||
* Definitions & global arrays.
|
||||
*/
|
||||
|
||||
#define GUNZE_MAX_LENGTH 10
|
||||
|
||||
/*
|
||||
* Per-touchscreen data.
|
||||
*/
|
||||
|
||||
struct gunze {
|
||||
struct input_dev *dev;
|
||||
struct serio *serio;
|
||||
int idx;
|
||||
unsigned char data[GUNZE_MAX_LENGTH];
|
||||
char phys[32];
|
||||
};
|
||||
|
||||
static void gunze_process_packet(struct gunze* gunze)
|
||||
{
|
||||
struct input_dev *dev = gunze->dev;
|
||||
|
||||
if (gunze->idx != GUNZE_MAX_LENGTH || gunze->data[5] != ',' ||
|
||||
(gunze->data[0] != 'T' && gunze->data[0] != 'R')) {
|
||||
printk(KERN_WARNING "gunze.c: bad packet: >%.*s<\n", GUNZE_MAX_LENGTH, gunze->data);
|
||||
return;
|
||||
}
|
||||
|
||||
input_report_abs(dev, ABS_X, simple_strtoul(gunze->data + 1, NULL, 10));
|
||||
input_report_abs(dev, ABS_Y, 1024 - simple_strtoul(gunze->data + 6, NULL, 10));
|
||||
input_report_key(dev, BTN_TOUCH, gunze->data[0] == 'T');
|
||||
input_sync(dev);
|
||||
}
|
||||
|
||||
static irqreturn_t gunze_interrupt(struct serio *serio,
|
||||
unsigned char data, unsigned int flags)
|
||||
{
|
||||
struct gunze* gunze = serio_get_drvdata(serio);
|
||||
|
||||
if (data == '\r') {
|
||||
gunze_process_packet(gunze);
|
||||
gunze->idx = 0;
|
||||
} else {
|
||||
if (gunze->idx < GUNZE_MAX_LENGTH)
|
||||
gunze->data[gunze->idx++] = data;
|
||||
}
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/*
|
||||
* gunze_disconnect() is the opposite of gunze_connect()
|
||||
*/
|
||||
|
||||
static void gunze_disconnect(struct serio *serio)
|
||||
{
|
||||
struct gunze *gunze = serio_get_drvdata(serio);
|
||||
|
||||
input_get_device(gunze->dev);
|
||||
input_unregister_device(gunze->dev);
|
||||
serio_close(serio);
|
||||
serio_set_drvdata(serio, NULL);
|
||||
input_put_device(gunze->dev);
|
||||
kfree(gunze);
|
||||
}
|
||||
|
||||
/*
|
||||
* gunze_connect() is the routine that is called when someone adds a
|
||||
* new serio device that supports Gunze protocol and registers it as
|
||||
* an input device.
|
||||
*/
|
||||
|
||||
static int gunze_connect(struct serio *serio, struct serio_driver *drv)
|
||||
{
|
||||
struct gunze *gunze;
|
||||
struct input_dev *input_dev;
|
||||
int err;
|
||||
|
||||
gunze = kzalloc(sizeof(struct gunze), GFP_KERNEL);
|
||||
input_dev = input_allocate_device();
|
||||
if (!gunze || !input_dev) {
|
||||
err = -ENOMEM;
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
gunze->serio = serio;
|
||||
gunze->dev = input_dev;
|
||||
snprintf(gunze->phys, sizeof(serio->phys), "%s/input0", serio->phys);
|
||||
|
||||
input_dev->private = gunze;
|
||||
input_dev->name = "Gunze AHL-51S TouchScreen";
|
||||
input_dev->phys = gunze->phys;
|
||||
input_dev->id.bustype = BUS_RS232;
|
||||
input_dev->id.vendor = SERIO_GUNZE;
|
||||
input_dev->id.product = 0x0051;
|
||||
input_dev->id.version = 0x0100;
|
||||
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
|
||||
input_dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
|
||||
input_set_abs_params(input_dev, ABS_X, 24, 1000, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_Y, 24, 1000, 0, 0);
|
||||
|
||||
serio_set_drvdata(serio, gunze);
|
||||
|
||||
err = serio_open(serio, drv);
|
||||
if (err)
|
||||
goto fail2;
|
||||
|
||||
err = input_register_device(gunze->dev);
|
||||
if (err)
|
||||
goto fail3;
|
||||
|
||||
return 0;
|
||||
|
||||
fail3: serio_close(serio);
|
||||
fail2: serio_set_drvdata(serio, NULL);
|
||||
fail1: input_free_device(input_dev);
|
||||
kfree(gunze);
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* The serio driver structure.
|
||||
*/
|
||||
|
||||
static struct serio_device_id gunze_serio_ids[] = {
|
||||
{
|
||||
.type = SERIO_RS232,
|
||||
.proto = SERIO_GUNZE,
|
||||
.id = SERIO_ANY,
|
||||
.extra = SERIO_ANY,
|
||||
},
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(serio, gunze_serio_ids);
|
||||
|
||||
static struct serio_driver gunze_drv = {
|
||||
.driver = {
|
||||
.name = "gunze",
|
||||
},
|
||||
.description = DRIVER_DESC,
|
||||
.id_table = gunze_serio_ids,
|
||||
.interrupt = gunze_interrupt,
|
||||
.connect = gunze_connect,
|
||||
.disconnect = gunze_disconnect,
|
||||
};
|
||||
|
||||
/*
|
||||
* The functions for inserting/removing us as a module.
|
||||
*/
|
||||
|
||||
static int __init gunze_init(void)
|
||||
{
|
||||
return serio_register_driver(&gunze_drv);
|
||||
}
|
||||
|
||||
static void __exit gunze_exit(void)
|
||||
{
|
||||
serio_unregister_driver(&gunze_drv);
|
||||
}
|
||||
|
||||
module_init(gunze_init);
|
||||
module_exit(gunze_exit);
|
||||
490
drivers/input/touchscreen/h3600_ts_input.c
Normal file
490
drivers/input/touchscreen/h3600_ts_input.c
Normal file
@@ -0,0 +1,490 @@
|
||||
/*
|
||||
* $Id: h3600_ts_input.c,v 1.1.1.1 2007/06/12 07:27:09 eyryu Exp $
|
||||
*
|
||||
* Copyright (c) 2001 "Crazy" James Simmons jsimmons@transvirtual.com
|
||||
*
|
||||
* Sponsored by Transvirtual Technology.
|
||||
*
|
||||
* Derived from the code in h3600_ts.[ch] by Charles Flynn
|
||||
*/
|
||||
|
||||
/*
|
||||
* Driver for the h3600 Touch Screen and other Atmel controlled devices.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
* Should you need to contact me, the author, you can do so by
|
||||
* e-mail - mail your message to <jsimmons@transvirtual.com>.
|
||||
*/
|
||||
|
||||
#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/delay.h>
|
||||
|
||||
/* SA1100 serial defines */
|
||||
#include <asm/arch/hardware.h>
|
||||
#include <asm/arch/irqs.h>
|
||||
|
||||
#define DRIVER_DESC "H3600 touchscreen driver"
|
||||
|
||||
MODULE_AUTHOR("James Simmons <jsimmons@transvirtual.com>");
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
/*
|
||||
* Definitions & global arrays.
|
||||
*/
|
||||
|
||||
/* The start and end of frame characters SOF and EOF */
|
||||
#define CHAR_SOF 0x02
|
||||
#define CHAR_EOF 0x03
|
||||
#define FRAME_OVERHEAD 3 /* CHAR_SOF,CHAR_EOF,LENGTH = 3 */
|
||||
|
||||
/*
|
||||
Atmel events and response IDs contained in frame.
|
||||
Programmer has no control over these numbers.
|
||||
TODO there are holes - specifically 1,7,0x0a
|
||||
*/
|
||||
#define VERSION_ID 0 /* Get Version (request/respose) */
|
||||
#define KEYBD_ID 2 /* Keyboard (event) */
|
||||
#define TOUCHS_ID 3 /* Touch Screen (event)*/
|
||||
#define EEPROM_READ_ID 4 /* (request/response) */
|
||||
#define EEPROM_WRITE_ID 5 /* (request/response) */
|
||||
#define THERMAL_ID 6 /* (request/response) */
|
||||
#define NOTIFY_LED_ID 8 /* (request/response) */
|
||||
#define BATTERY_ID 9 /* (request/response) */
|
||||
#define SPI_READ_ID 0x0b /* ( request/response) */
|
||||
#define SPI_WRITE_ID 0x0c /* ( request/response) */
|
||||
#define FLITE_ID 0x0d /* backlight ( request/response) */
|
||||
#define STX_ID 0xa1 /* extension pack status (req/resp) */
|
||||
|
||||
#define MAX_ID 14
|
||||
|
||||
#define H3600_MAX_LENGTH 16
|
||||
#define H3600_KEY 0xf
|
||||
|
||||
#define H3600_SCANCODE_RECORD 1 /* 1 -> record button */
|
||||
#define H3600_SCANCODE_CALENDAR 2 /* 2 -> calendar */
|
||||
#define H3600_SCANCODE_CONTACTS 3 /* 3 -> contact */
|
||||
#define H3600_SCANCODE_Q 4 /* 4 -> Q button */
|
||||
#define H3600_SCANCODE_START 5 /* 5 -> start menu */
|
||||
#define H3600_SCANCODE_UP 6 /* 6 -> up */
|
||||
#define H3600_SCANCODE_RIGHT 7 /* 7 -> right */
|
||||
#define H3600_SCANCODE_LEFT 8 /* 8 -> left */
|
||||
#define H3600_SCANCODE_DOWN 9 /* 9 -> down */
|
||||
|
||||
/*
|
||||
* Per-touchscreen data.
|
||||
*/
|
||||
struct h3600_dev {
|
||||
struct input_dev *dev;
|
||||
struct serio *serio;
|
||||
unsigned char event; /* event ID from packet */
|
||||
unsigned char chksum;
|
||||
unsigned char len;
|
||||
unsigned char idx;
|
||||
unsigned char buf[H3600_MAX_LENGTH];
|
||||
char phys[32];
|
||||
};
|
||||
|
||||
static irqreturn_t action_button_handler(int irq, void *dev_id)
|
||||
{
|
||||
int down = (GPLR & GPIO_BITSY_ACTION_BUTTON) ? 0 : 1;
|
||||
struct input_dev *dev = (struct input_dev *) dev_id;
|
||||
|
||||
input_report_key(dev, KEY_ENTER, down);
|
||||
input_sync(dev);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t npower_button_handler(int irq, void *dev_id)
|
||||
{
|
||||
int down = (GPLR & GPIO_BITSY_NPOWER_BUTTON) ? 0 : 1;
|
||||
struct input_dev *dev = (struct input_dev *) dev_id;
|
||||
|
||||
/*
|
||||
* This interrupt is only called when we release the key. So we have
|
||||
* to fake a key press.
|
||||
*/
|
||||
input_report_key(dev, KEY_SUSPEND, 1);
|
||||
input_report_key(dev, KEY_SUSPEND, down);
|
||||
input_sync(dev);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
static int flite_brightness = 25;
|
||||
|
||||
enum flite_pwr {
|
||||
FLITE_PWR_OFF = 0,
|
||||
FLITE_PWR_ON = 1
|
||||
};
|
||||
|
||||
/*
|
||||
* h3600_flite_power: enables or disables power to frontlight, using last bright */
|
||||
unsigned int h3600_flite_power(struct input_dev *dev, enum flite_pwr pwr)
|
||||
{
|
||||
unsigned char brightness = (pwr == FLITE_PWR_OFF) ? 0 : flite_brightness;
|
||||
struct h3600_dev *ts = dev->private;
|
||||
|
||||
/* Must be in this order */
|
||||
ts->serio->write(ts->serio, 1);
|
||||
ts->serio->write(ts->serio, pwr);
|
||||
ts->serio->write(ts->serio, brightness);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This function translates the native event packets to linux input event
|
||||
* packets. Some packets coming from serial are not touchscreen related. In
|
||||
* this case we send them off to be processed elsewhere.
|
||||
*/
|
||||
static void h3600ts_process_packet(struct h3600_dev *ts)
|
||||
{
|
||||
struct input_dev *dev = ts->dev;
|
||||
static int touched = 0;
|
||||
int key, down = 0;
|
||||
|
||||
switch (ts->event) {
|
||||
/*
|
||||
Buttons - returned as a single byte
|
||||
7 6 5 4 3 2 1 0
|
||||
S x x x N N N N
|
||||
|
||||
S switch state ( 0=pressed 1=released)
|
||||
x Unused.
|
||||
NNNN switch number 0-15
|
||||
|
||||
Note: This is true for non interrupt generated key events.
|
||||
*/
|
||||
case KEYBD_ID:
|
||||
down = (ts->buf[0] & 0x80) ? 0 : 1;
|
||||
|
||||
switch (ts->buf[0] & 0x7f) {
|
||||
case H3600_SCANCODE_RECORD:
|
||||
key = KEY_RECORD;
|
||||
break;
|
||||
case H3600_SCANCODE_CALENDAR:
|
||||
key = KEY_PROG1;
|
||||
break;
|
||||
case H3600_SCANCODE_CONTACTS:
|
||||
key = KEY_PROG2;
|
||||
break;
|
||||
case H3600_SCANCODE_Q:
|
||||
key = KEY_Q;
|
||||
break;
|
||||
case H3600_SCANCODE_START:
|
||||
key = KEY_PROG3;
|
||||
break;
|
||||
case H3600_SCANCODE_UP:
|
||||
key = KEY_UP;
|
||||
break;
|
||||
case H3600_SCANCODE_RIGHT:
|
||||
key = KEY_RIGHT;
|
||||
break;
|
||||
case H3600_SCANCODE_LEFT:
|
||||
key = KEY_LEFT;
|
||||
break;
|
||||
case H3600_SCANCODE_DOWN:
|
||||
key = KEY_DOWN;
|
||||
break;
|
||||
default:
|
||||
key = 0;
|
||||
}
|
||||
if (key)
|
||||
input_report_key(dev, key, down);
|
||||
break;
|
||||
/*
|
||||
* Native touchscreen event data is formatted as shown below:-
|
||||
*
|
||||
* +-------+-------+-------+-------+
|
||||
* | Xmsb | Xlsb | Ymsb | Ylsb |
|
||||
* +-------+-------+-------+-------+
|
||||
* byte 0 1 2 3
|
||||
*/
|
||||
case TOUCHS_ID:
|
||||
if (!touched) {
|
||||
input_report_key(dev, BTN_TOUCH, 1);
|
||||
touched = 1;
|
||||
}
|
||||
|
||||
if (ts->len) {
|
||||
unsigned short x, y;
|
||||
|
||||
x = ts->buf[0]; x <<= 8; x += ts->buf[1];
|
||||
y = ts->buf[2]; y <<= 8; y += ts->buf[3];
|
||||
|
||||
input_report_abs(dev, ABS_X, x);
|
||||
input_report_abs(dev, ABS_Y, y);
|
||||
} else {
|
||||
input_report_key(dev, BTN_TOUCH, 0);
|
||||
touched = 0;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
/* Send a non input event elsewhere */
|
||||
break;
|
||||
}
|
||||
|
||||
input_sync(dev);
|
||||
}
|
||||
|
||||
/*
|
||||
* h3600ts_event() handles events from the input module.
|
||||
*/
|
||||
static int h3600ts_event(struct input_dev *dev, unsigned int type,
|
||||
unsigned int code, int value)
|
||||
{
|
||||
#if 0
|
||||
struct h3600_dev *ts = dev->private;
|
||||
|
||||
switch (type) {
|
||||
case EV_LED: {
|
||||
// ts->serio->write(ts->serio, SOME_CMD);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
Frame format
|
||||
byte 1 2 3 len + 4
|
||||
+-------+---------------+---------------+--=------------+
|
||||
|SOF |id |len | len bytes | Chksum |
|
||||
+-------+---------------+---------------+--=------------+
|
||||
bit 0 7 8 11 12 15 16
|
||||
|
||||
+-------+---------------+-------+
|
||||
|SOF |id |0 |Chksum | - Note Chksum does not include SOF
|
||||
+-------+---------------+-------+
|
||||
bit 0 7 8 11 12 15 16
|
||||
|
||||
*/
|
||||
|
||||
static int state;
|
||||
|
||||
/* decode States */
|
||||
#define STATE_SOF 0 /* start of FRAME */
|
||||
#define STATE_ID 1 /* state where we decode the ID & len */
|
||||
#define STATE_DATA 2 /* state where we decode data */
|
||||
#define STATE_EOF 3 /* state where we decode checksum or EOF */
|
||||
|
||||
static irqreturn_t h3600ts_interrupt(struct serio *serio, unsigned char data,
|
||||
unsigned int flags)
|
||||
{
|
||||
struct h3600_dev *ts = serio_get_drvdata(serio);
|
||||
|
||||
/*
|
||||
* We have a new frame coming in.
|
||||
*/
|
||||
switch (state) {
|
||||
case STATE_SOF:
|
||||
if (data == CHAR_SOF)
|
||||
state = STATE_ID;
|
||||
break;
|
||||
case STATE_ID:
|
||||
ts->event = (data & 0xf0) >> 4;
|
||||
ts->len = (data & 0xf);
|
||||
ts->idx = 0;
|
||||
if (ts->event >= MAX_ID) {
|
||||
state = STATE_SOF;
|
||||
break;
|
||||
}
|
||||
ts->chksum = data;
|
||||
state = (ts->len > 0) ? STATE_DATA : STATE_EOF;
|
||||
break;
|
||||
case STATE_DATA:
|
||||
ts->chksum += data;
|
||||
ts->buf[ts->idx]= data;
|
||||
if (++ts->idx == ts->len)
|
||||
state = STATE_EOF;
|
||||
break;
|
||||
case STATE_EOF:
|
||||
state = STATE_SOF;
|
||||
if (data == CHAR_EOF || data == ts->chksum)
|
||||
h3600ts_process_packet(ts);
|
||||
break;
|
||||
default:
|
||||
printk("Error3\n");
|
||||
break;
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/*
|
||||
* h3600ts_connect() is the routine that is called when someone adds a
|
||||
* new serio device that supports H3600 protocol and registers it as
|
||||
* an input device.
|
||||
*/
|
||||
static int h3600ts_connect(struct serio *serio, struct serio_driver *drv)
|
||||
{
|
||||
struct h3600_dev *ts;
|
||||
struct input_dev *input_dev;
|
||||
int err;
|
||||
|
||||
ts = kzalloc(sizeof(struct h3600_dev), GFP_KERNEL);
|
||||
input_dev = input_allocate_device();
|
||||
if (!ts || !input_dev) {
|
||||
err = -ENOMEM;
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
ts->serio = serio;
|
||||
ts->dev = input_dev;
|
||||
snprintf(ts->phys, sizeof(ts->phys), "%s/input0", serio->phys);
|
||||
|
||||
input_dev->name = "H3600 TouchScreen";
|
||||
input_dev->phys = ts->phys;
|
||||
input_dev->id.bustype = BUS_RS232;
|
||||
input_dev->id.vendor = SERIO_H3600;
|
||||
input_dev->id.product = 0x0666; /* FIXME !!! We can ask the hardware */
|
||||
input_dev->id.version = 0x0100;
|
||||
input_dev->cdev.dev = &serio->dev;
|
||||
input_dev->private = ts;
|
||||
|
||||
input_dev->event = h3600ts_event;
|
||||
|
||||
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS) | BIT(EV_LED) | BIT(EV_PWR);
|
||||
input_dev->ledbit[0] = BIT(LED_SLEEP);
|
||||
input_set_abs_params(input_dev, ABS_X, 60, 985, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_Y, 35, 1024, 0, 0);
|
||||
|
||||
set_bit(KEY_RECORD, input_dev->keybit);
|
||||
set_bit(KEY_Q, input_dev->keybit);
|
||||
set_bit(KEY_PROG1, input_dev->keybit);
|
||||
set_bit(KEY_PROG2, input_dev->keybit);
|
||||
set_bit(KEY_PROG3, input_dev->keybit);
|
||||
set_bit(KEY_UP, input_dev->keybit);
|
||||
set_bit(KEY_RIGHT, input_dev->keybit);
|
||||
set_bit(KEY_LEFT, input_dev->keybit);
|
||||
set_bit(KEY_DOWN, input_dev->keybit);
|
||||
set_bit(KEY_ENTER, input_dev->keybit);
|
||||
set_bit(KEY_SUSPEND, input_dev->keybit);
|
||||
set_bit(BTN_TOUCH, input_dev->keybit);
|
||||
|
||||
/* Device specific stuff */
|
||||
set_GPIO_IRQ_edge(GPIO_BITSY_ACTION_BUTTON, GPIO_BOTH_EDGES);
|
||||
set_GPIO_IRQ_edge(GPIO_BITSY_NPOWER_BUTTON, GPIO_RISING_EDGE);
|
||||
|
||||
if (request_irq(IRQ_GPIO_BITSY_ACTION_BUTTON, action_button_handler,
|
||||
IRQF_SHARED | IRQF_DISABLED, "h3600_action", &ts->dev)) {
|
||||
printk(KERN_ERR "h3600ts.c: Could not allocate Action Button IRQ!\n");
|
||||
err = -EBUSY;
|
||||
goto fail2;
|
||||
}
|
||||
|
||||
if (request_irq(IRQ_GPIO_BITSY_NPOWER_BUTTON, npower_button_handler,
|
||||
IRQF_SHARED | IRQF_DISABLED, "h3600_suspend", &ts->dev)) {
|
||||
printk(KERN_ERR "h3600ts.c: Could not allocate Power Button IRQ!\n");
|
||||
err = -EBUSY;
|
||||
goto fail3;
|
||||
}
|
||||
|
||||
serio_set_drvdata(serio, ts);
|
||||
|
||||
err = serio_open(serio, drv);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
//h3600_flite_control(1, 25); /* default brightness */
|
||||
input_register_device(ts->dev);
|
||||
|
||||
return 0;
|
||||
|
||||
fail3: free_irq(IRQ_GPIO_BITSY_NPOWER_BUTTON, ts->dev);
|
||||
fail2: free_irq(IRQ_GPIO_BITSY_ACTION_BUTTON, ts->dev);
|
||||
fail1: serio_set_drvdata(serio, NULL);
|
||||
input_free_device(input_dev);
|
||||
kfree(ts);
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* h3600ts_disconnect() is the opposite of h3600ts_connect()
|
||||
*/
|
||||
|
||||
static void h3600ts_disconnect(struct serio *serio)
|
||||
{
|
||||
struct h3600_dev *ts = serio_get_drvdata(serio);
|
||||
|
||||
free_irq(IRQ_GPIO_BITSY_ACTION_BUTTON, &ts->dev);
|
||||
free_irq(IRQ_GPIO_BITSY_NPOWER_BUTTON, &ts->dev);
|
||||
input_get_device(ts->dev);
|
||||
input_unregister_device(ts->dev);
|
||||
serio_close(serio);
|
||||
serio_set_drvdata(serio, NULL);
|
||||
input_put_device(ts->dev);
|
||||
kfree(ts);
|
||||
}
|
||||
|
||||
/*
|
||||
* The serio driver structure.
|
||||
*/
|
||||
|
||||
static struct serio_device_id h3600ts_serio_ids[] = {
|
||||
{
|
||||
.type = SERIO_RS232,
|
||||
.proto = SERIO_H3600,
|
||||
.id = SERIO_ANY,
|
||||
.extra = SERIO_ANY,
|
||||
},
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(serio, h3600ts_serio_ids);
|
||||
|
||||
static struct serio_driver h3600ts_drv = {
|
||||
.driver = {
|
||||
.name = "h3600ts",
|
||||
},
|
||||
.description = DRIVER_DESC,
|
||||
.id_table = h3600ts_serio_ids,
|
||||
.interrupt = h3600ts_interrupt,
|
||||
.connect = h3600ts_connect,
|
||||
.disconnect = h3600ts_disconnect,
|
||||
};
|
||||
|
||||
/*
|
||||
* The functions for inserting/removing us as a module.
|
||||
*/
|
||||
|
||||
static int __init h3600ts_init(void)
|
||||
{
|
||||
return serio_register_driver(&h3600ts_drv);
|
||||
}
|
||||
|
||||
static void __exit h3600ts_exit(void)
|
||||
{
|
||||
serio_unregister_driver(&h3600ts_drv);
|
||||
}
|
||||
|
||||
module_init(h3600ts_init);
|
||||
module_exit(h3600ts_exit);
|
||||
130
drivers/input/touchscreen/hp680_ts_input.c
Normal file
130
drivers/input/touchscreen/hp680_ts_input.c
Normal file
@@ -0,0 +1,130 @@
|
||||
#include <linux/input.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/delay.h>
|
||||
#include <asm/adc.h>
|
||||
#include <asm/hp6xx.h>
|
||||
|
||||
#define MODNAME "hp680_ts_input"
|
||||
|
||||
#define HP680_TS_ABS_X_MIN 40
|
||||
#define HP680_TS_ABS_X_MAX 950
|
||||
#define HP680_TS_ABS_Y_MIN 80
|
||||
#define HP680_TS_ABS_Y_MAX 910
|
||||
|
||||
#define PHDR 0xa400012e
|
||||
#define SCPDR 0xa4000136
|
||||
|
||||
static void do_softint(void *data);
|
||||
|
||||
static struct input_dev *hp680_ts_dev;
|
||||
static DECLARE_WORK(work, do_softint, 0);
|
||||
|
||||
static void do_softint(void *data)
|
||||
{
|
||||
int absx = 0, absy = 0;
|
||||
u8 scpdr;
|
||||
int touched = 0;
|
||||
|
||||
if (ctrl_inb(PHDR) & PHDR_TS_PEN_DOWN) {
|
||||
scpdr = ctrl_inb(SCPDR);
|
||||
scpdr |= SCPDR_TS_SCAN_ENABLE;
|
||||
scpdr &= ~SCPDR_TS_SCAN_Y;
|
||||
ctrl_outb(scpdr, SCPDR);
|
||||
udelay(30);
|
||||
|
||||
absy = adc_single(ADC_CHANNEL_TS_Y);
|
||||
|
||||
scpdr = ctrl_inb(SCPDR);
|
||||
scpdr |= SCPDR_TS_SCAN_Y;
|
||||
scpdr &= ~SCPDR_TS_SCAN_X;
|
||||
ctrl_outb(scpdr, SCPDR);
|
||||
udelay(30);
|
||||
|
||||
absx = adc_single(ADC_CHANNEL_TS_X);
|
||||
|
||||
scpdr = ctrl_inb(SCPDR);
|
||||
scpdr |= SCPDR_TS_SCAN_X;
|
||||
scpdr &= ~SCPDR_TS_SCAN_ENABLE;
|
||||
ctrl_outb(scpdr, SCPDR);
|
||||
udelay(100);
|
||||
touched = ctrl_inb(PHDR) & PHDR_TS_PEN_DOWN;
|
||||
}
|
||||
|
||||
if (touched) {
|
||||
input_report_key(hp680_ts_dev, BTN_TOUCH, 1);
|
||||
input_report_abs(hp680_ts_dev, ABS_X, absx);
|
||||
input_report_abs(hp680_ts_dev, ABS_Y, absy);
|
||||
} else {
|
||||
input_report_key(hp680_ts_dev, BTN_TOUCH, 0);
|
||||
}
|
||||
|
||||
input_sync(hp680_ts_dev);
|
||||
enable_irq(HP680_TS_IRQ);
|
||||
}
|
||||
|
||||
static irqreturn_t hp680_ts_interrupt(int irq, void *dev)
|
||||
{
|
||||
disable_irq_nosync(irq);
|
||||
schedule_delayed_work(&work, HZ / 20);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int __init hp680_ts_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
hp680_ts_dev = input_allocate_device();
|
||||
if (!hp680_ts_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
hp680_ts_dev->evbit[0] = BIT(EV_ABS) | BIT(EV_KEY);
|
||||
hp680_ts_dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
|
||||
|
||||
input_set_abs_params(hp680_ts_dev, ABS_X,
|
||||
HP680_TS_ABS_X_MIN, HP680_TS_ABS_X_MAX, 0, 0);
|
||||
input_set_abs_params(hp680_ts_dev, ABS_Y,
|
||||
HP680_TS_ABS_Y_MIN, HP680_TS_ABS_Y_MAX, 0, 0);
|
||||
|
||||
hp680_ts_dev->name = "HP Jornada touchscreen";
|
||||
hp680_ts_dev->phys = "hp680_ts/input0";
|
||||
|
||||
if (request_irq(HP680_TS_IRQ, hp680_ts_interrupt,
|
||||
IRQF_DISABLED, MODNAME, 0) < 0) {
|
||||
printk(KERN_ERR "hp680_touchscreen.c: Can't allocate irq %d\n",
|
||||
HP680_TS_IRQ);
|
||||
err = -EBUSY;
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
err = input_register_device(hp680_ts_dev);
|
||||
if (err)
|
||||
goto fail2;
|
||||
|
||||
return 0;
|
||||
|
||||
fail2: free_irq(HP680_TS_IRQ, NULL);
|
||||
cancel_delayed_work(&work);
|
||||
flush_scheduled_work();
|
||||
fail1: input_free_device(hp680_ts_dev);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void __exit hp680_ts_exit(void)
|
||||
{
|
||||
free_irq(HP680_TS_IRQ, NULL);
|
||||
cancel_delayed_work(&work);
|
||||
flush_scheduled_work();
|
||||
input_unregister_device(hp680_ts_dev);
|
||||
}
|
||||
|
||||
module_init(hp680_ts_init);
|
||||
module_exit(hp680_ts_exit);
|
||||
|
||||
MODULE_AUTHOR("Andriy Skulysh, askulysh@image.kiev.ua");
|
||||
MODULE_DESCRIPTION("HP Jornada 680 touchscreen driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
220
drivers/input/touchscreen/mk712.c
Normal file
220
drivers/input/touchscreen/mk712.c
Normal file
@@ -0,0 +1,220 @@
|
||||
/*
|
||||
* ICS MK712 touchscreen controller driver
|
||||
*
|
||||
* Copyright (c) 1999-2002 Transmeta Corporation
|
||||
* Copyright (c) 2005 Rick Koch <n1gp@hotmail.com>
|
||||
* Copyright (c) 2005 Vojtech Pavlik <vojtech@suse.cz>
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This driver supports the ICS MicroClock MK712 TouchScreen controller,
|
||||
* found in Gateway AOL Connected Touchpad computers.
|
||||
*
|
||||
* Documentation for ICS MK712 can be found at:
|
||||
* http://www.icst.com/pdf/mk712.pdf
|
||||
*/
|
||||
|
||||
/*
|
||||
* 1999-12-18: original version, Daniel Quinlan
|
||||
* 1999-12-19: added anti-jitter code, report pen-up events, fixed mk712_poll
|
||||
* to use queue_empty, Nathan Laredo
|
||||
* 1999-12-20: improved random point rejection, Nathan Laredo
|
||||
* 2000-01-05: checked in new anti-jitter code, changed mouse protocol, fixed
|
||||
* queue code, added module options, other fixes, Daniel Quinlan
|
||||
* 2002-03-15: Clean up for kernel merge <alan@redhat.com>
|
||||
* Fixed multi open race, fixed memory checks, fixed resource
|
||||
* allocation, fixed close/powerdown bug, switched to new init
|
||||
* 2005-01-18: Ported to 2.6 from 2.4.28, Rick Koch
|
||||
* 2005-02-05: Rewritten for the input layer, Vojtech Pavlik
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/input.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
MODULE_AUTHOR("Daniel Quinlan <quinlan@pathname.com>, Vojtech Pavlik <vojtech@suse.cz>");
|
||||
MODULE_DESCRIPTION("ICS MicroClock MK712 TouchScreen driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
static unsigned int mk712_io = 0x260; /* Also 0x200, 0x208, 0x300 */
|
||||
module_param_named(io, mk712_io, uint, 0);
|
||||
MODULE_PARM_DESC(io, "I/O base address of MK712 touchscreen controller");
|
||||
|
||||
static unsigned int mk712_irq = 10; /* Also 12, 14, 15 */
|
||||
module_param_named(irq, mk712_irq, uint, 0);
|
||||
MODULE_PARM_DESC(irq, "IRQ of MK712 touchscreen controller");
|
||||
|
||||
/* eight 8-bit registers */
|
||||
#define MK712_STATUS 0
|
||||
#define MK712_X 2
|
||||
#define MK712_Y 4
|
||||
#define MK712_CONTROL 6
|
||||
#define MK712_RATE 7
|
||||
|
||||
/* status */
|
||||
#define MK712_STATUS_TOUCH 0x10
|
||||
#define MK712_CONVERSION_COMPLETE 0x80
|
||||
|
||||
/* control */
|
||||
#define MK712_ENABLE_INT 0x01
|
||||
#define MK712_INT_ON_CONVERSION_COMPLETE 0x02
|
||||
#define MK712_INT_ON_CHANGE_IN_TOUCH_STATUS 0x04
|
||||
#define MK712_ENABLE_PERIODIC_CONVERSIONS 0x10
|
||||
#define MK712_READ_ONE_POINT 0x20
|
||||
#define MK712_POWERUP 0x40
|
||||
|
||||
static struct input_dev *mk712_dev;
|
||||
static DEFINE_SPINLOCK(mk712_lock);
|
||||
|
||||
static irqreturn_t mk712_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
unsigned char status;
|
||||
static int debounce = 1;
|
||||
static unsigned short last_x;
|
||||
static unsigned short last_y;
|
||||
|
||||
spin_lock(&mk712_lock);
|
||||
|
||||
status = inb(mk712_io + MK712_STATUS);
|
||||
|
||||
if (~status & MK712_CONVERSION_COMPLETE) {
|
||||
debounce = 1;
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (~status & MK712_STATUS_TOUCH) {
|
||||
debounce = 1;
|
||||
input_report_key(mk712_dev, BTN_TOUCH, 0);
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (debounce) {
|
||||
debounce = 0;
|
||||
goto end;
|
||||
}
|
||||
|
||||
input_report_key(mk712_dev, BTN_TOUCH, 1);
|
||||
input_report_abs(mk712_dev, ABS_X, last_x);
|
||||
input_report_abs(mk712_dev, ABS_Y, last_y);
|
||||
|
||||
end:
|
||||
last_x = inw(mk712_io + MK712_X) & 0x0fff;
|
||||
last_y = inw(mk712_io + MK712_Y) & 0x0fff;
|
||||
input_sync(mk712_dev);
|
||||
spin_unlock(&mk712_lock);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int mk712_open(struct input_dev *dev)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&mk712_lock, flags);
|
||||
|
||||
outb(0, mk712_io + MK712_CONTROL); /* Reset */
|
||||
|
||||
outb(MK712_ENABLE_INT | MK712_INT_ON_CONVERSION_COMPLETE |
|
||||
MK712_INT_ON_CHANGE_IN_TOUCH_STATUS |
|
||||
MK712_ENABLE_PERIODIC_CONVERSIONS |
|
||||
MK712_POWERUP, mk712_io + MK712_CONTROL);
|
||||
|
||||
outb(10, mk712_io + MK712_RATE); /* 187 points per second */
|
||||
|
||||
spin_unlock_irqrestore(&mk712_lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mk712_close(struct input_dev *dev)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&mk712_lock, flags);
|
||||
|
||||
outb(0, mk712_io + MK712_CONTROL);
|
||||
|
||||
spin_unlock_irqrestore(&mk712_lock, flags);
|
||||
}
|
||||
|
||||
static int __init mk712_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (!request_region(mk712_io, 8, "mk712")) {
|
||||
printk(KERN_WARNING "mk712: unable to get IO region\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
outb(0, mk712_io + MK712_CONTROL);
|
||||
|
||||
if ((inw(mk712_io + MK712_X) & 0xf000) || /* Sanity check */
|
||||
(inw(mk712_io + MK712_Y) & 0xf000) ||
|
||||
(inw(mk712_io + MK712_STATUS) & 0xf333)) {
|
||||
printk(KERN_WARNING "mk712: device not present\n");
|
||||
err = -ENODEV;
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
mk712_dev = input_allocate_device();
|
||||
if (!mk712_dev) {
|
||||
printk(KERN_ERR "mk712: not enough memory\n");
|
||||
err = -ENOMEM;
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
mk712_dev->name = "ICS MicroClock MK712 TouchScreen";
|
||||
mk712_dev->phys = "isa0260/input0";
|
||||
mk712_dev->id.bustype = BUS_ISA;
|
||||
mk712_dev->id.vendor = 0x0005;
|
||||
mk712_dev->id.product = 0x0001;
|
||||
mk712_dev->id.version = 0x0100;
|
||||
|
||||
mk712_dev->open = mk712_open;
|
||||
mk712_dev->close = mk712_close;
|
||||
|
||||
mk712_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
|
||||
mk712_dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
|
||||
input_set_abs_params(mk712_dev, ABS_X, 0, 0xfff, 88, 0);
|
||||
input_set_abs_params(mk712_dev, ABS_Y, 0, 0xfff, 88, 0);
|
||||
|
||||
if (request_irq(mk712_irq, mk712_interrupt, 0, "mk712", mk712_dev)) {
|
||||
printk(KERN_WARNING "mk712: unable to get IRQ\n");
|
||||
err = -EBUSY;
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
err = input_register_device(mk712_dev);
|
||||
if (err)
|
||||
goto fail2;
|
||||
|
||||
return 0;
|
||||
|
||||
fail2: free_irq(mk712_irq, mk712_dev);
|
||||
fail1: input_free_device(mk712_dev);
|
||||
release_region(mk712_io, 8);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void __exit mk712_exit(void)
|
||||
{
|
||||
input_unregister_device(mk712_dev);
|
||||
free_irq(mk712_irq, mk712_dev);
|
||||
release_region(mk712_io, 8);
|
||||
}
|
||||
|
||||
module_init(mk712_init);
|
||||
module_exit(mk712_exit);
|
||||
220
drivers/input/touchscreen/mtouch.c
Normal file
220
drivers/input/touchscreen/mtouch.c
Normal file
@@ -0,0 +1,220 @@
|
||||
/*
|
||||
* MicroTouch (3M) serial touchscreen driver
|
||||
*
|
||||
* Copyright (c) 2004 Vojtech Pavlik
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* 2005/02/19 Dan Streetman <ddstreet@ieee.org>
|
||||
* Copied elo.c and edited for MicroTouch protocol
|
||||
*/
|
||||
|
||||
#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>
|
||||
|
||||
#define DRIVER_DESC "MicroTouch serial touchscreen driver"
|
||||
|
||||
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
/*
|
||||
* Definitions & global arrays.
|
||||
*/
|
||||
|
||||
#define MTOUCH_FORMAT_TABLET_STATUS_BIT 0x80
|
||||
#define MTOUCH_FORMAT_TABLET_TOUCH_BIT 0x40
|
||||
#define MTOUCH_FORMAT_TABLET_LENGTH 5
|
||||
#define MTOUCH_RESPONSE_BEGIN_BYTE 0x01
|
||||
#define MTOUCH_RESPONSE_END_BYTE 0x0d
|
||||
|
||||
/* todo: check specs for max length of all responses */
|
||||
#define MTOUCH_MAX_LENGTH 16
|
||||
|
||||
#define MTOUCH_MIN_XC 0
|
||||
#define MTOUCH_MAX_XC 0x3fff
|
||||
#define MTOUCH_MIN_YC 0
|
||||
#define MTOUCH_MAX_YC 0x3fff
|
||||
|
||||
#define MTOUCH_GET_XC(data) (((data[2])<<7) | data[1])
|
||||
#define MTOUCH_GET_YC(data) (((data[4])<<7) | data[3])
|
||||
#define MTOUCH_GET_TOUCHED(data) (MTOUCH_FORMAT_TABLET_TOUCH_BIT & data[0])
|
||||
|
||||
/*
|
||||
* Per-touchscreen data.
|
||||
*/
|
||||
|
||||
struct mtouch {
|
||||
struct input_dev *dev;
|
||||
struct serio *serio;
|
||||
int idx;
|
||||
unsigned char data[MTOUCH_MAX_LENGTH];
|
||||
char phys[32];
|
||||
};
|
||||
|
||||
static void mtouch_process_format_tablet(struct mtouch *mtouch)
|
||||
{
|
||||
struct input_dev *dev = mtouch->dev;
|
||||
|
||||
if (MTOUCH_FORMAT_TABLET_LENGTH == ++mtouch->idx) {
|
||||
input_report_abs(dev, ABS_X, MTOUCH_GET_XC(mtouch->data));
|
||||
input_report_abs(dev, ABS_Y, MTOUCH_MAX_YC - MTOUCH_GET_YC(mtouch->data));
|
||||
input_report_key(dev, BTN_TOUCH, MTOUCH_GET_TOUCHED(mtouch->data));
|
||||
input_sync(dev);
|
||||
|
||||
mtouch->idx = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void mtouch_process_response(struct mtouch *mtouch)
|
||||
{
|
||||
if (MTOUCH_RESPONSE_END_BYTE == mtouch->data[mtouch->idx++]) {
|
||||
/* FIXME - process response */
|
||||
mtouch->idx = 0;
|
||||
} else if (MTOUCH_MAX_LENGTH == mtouch->idx) {
|
||||
printk(KERN_ERR "mtouch.c: too many response bytes\n");
|
||||
mtouch->idx = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static irqreturn_t mtouch_interrupt(struct serio *serio,
|
||||
unsigned char data, unsigned int flags)
|
||||
{
|
||||
struct mtouch* mtouch = serio_get_drvdata(serio);
|
||||
|
||||
mtouch->data[mtouch->idx] = data;
|
||||
|
||||
if (MTOUCH_FORMAT_TABLET_STATUS_BIT & mtouch->data[0])
|
||||
mtouch_process_format_tablet(mtouch);
|
||||
else if (MTOUCH_RESPONSE_BEGIN_BYTE == mtouch->data[0])
|
||||
mtouch_process_response(mtouch);
|
||||
else
|
||||
printk(KERN_DEBUG "mtouch.c: unknown/unsynchronized data from device, byte %x\n",mtouch->data[0]);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/*
|
||||
* mtouch_disconnect() is the opposite of mtouch_connect()
|
||||
*/
|
||||
|
||||
static void mtouch_disconnect(struct serio *serio)
|
||||
{
|
||||
struct mtouch* mtouch = serio_get_drvdata(serio);
|
||||
|
||||
input_get_device(mtouch->dev);
|
||||
input_unregister_device(mtouch->dev);
|
||||
serio_close(serio);
|
||||
serio_set_drvdata(serio, NULL);
|
||||
input_put_device(mtouch->dev);
|
||||
kfree(mtouch);
|
||||
}
|
||||
|
||||
/*
|
||||
* mtouch_connect() is the routine that is called when someone adds a
|
||||
* new serio device that supports MicroTouch (Format Tablet) protocol and registers it as
|
||||
* an input device.
|
||||
*/
|
||||
|
||||
static int mtouch_connect(struct serio *serio, struct serio_driver *drv)
|
||||
{
|
||||
struct mtouch *mtouch;
|
||||
struct input_dev *input_dev;
|
||||
int err;
|
||||
|
||||
mtouch = kzalloc(sizeof(struct mtouch), GFP_KERNEL);
|
||||
input_dev = input_allocate_device();
|
||||
if (!mtouch || !input_dev) {
|
||||
err = -ENOMEM;
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
mtouch->serio = serio;
|
||||
mtouch->dev = input_dev;
|
||||
snprintf(mtouch->phys, sizeof(mtouch->phys), "%s/input0", serio->phys);
|
||||
|
||||
input_dev->private = mtouch;
|
||||
input_dev->name = "MicroTouch Serial TouchScreen";
|
||||
input_dev->phys = mtouch->phys;
|
||||
input_dev->id.bustype = BUS_RS232;
|
||||
input_dev->id.vendor = SERIO_MICROTOUCH;
|
||||
input_dev->id.product = 0;
|
||||
input_dev->id.version = 0x0100;
|
||||
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
|
||||
input_dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
|
||||
input_set_abs_params(mtouch->dev, ABS_X, MTOUCH_MIN_XC, MTOUCH_MAX_XC, 0, 0);
|
||||
input_set_abs_params(mtouch->dev, ABS_Y, MTOUCH_MIN_YC, MTOUCH_MAX_YC, 0, 0);
|
||||
|
||||
serio_set_drvdata(serio, mtouch);
|
||||
|
||||
err = serio_open(serio, drv);
|
||||
if (err)
|
||||
goto fail2;
|
||||
|
||||
err = input_register_device(mtouch->dev);
|
||||
if (err)
|
||||
goto fail3;
|
||||
|
||||
return 0;
|
||||
|
||||
fail3: serio_close(serio);
|
||||
fail2: serio_set_drvdata(serio, NULL);
|
||||
fail1: input_free_device(input_dev);
|
||||
kfree(mtouch);
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* The serio driver structure.
|
||||
*/
|
||||
|
||||
static struct serio_device_id mtouch_serio_ids[] = {
|
||||
{
|
||||
.type = SERIO_RS232,
|
||||
.proto = SERIO_MICROTOUCH,
|
||||
.id = SERIO_ANY,
|
||||
.extra = SERIO_ANY,
|
||||
},
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(serio, mtouch_serio_ids);
|
||||
|
||||
static struct serio_driver mtouch_drv = {
|
||||
.driver = {
|
||||
.name = "mtouch",
|
||||
},
|
||||
.description = DRIVER_DESC,
|
||||
.id_table = mtouch_serio_ids,
|
||||
.interrupt = mtouch_interrupt,
|
||||
.connect = mtouch_connect,
|
||||
.disconnect = mtouch_disconnect,
|
||||
};
|
||||
|
||||
/*
|
||||
* The functions for inserting/removing us as a module.
|
||||
*/
|
||||
|
||||
static int __init mtouch_init(void)
|
||||
{
|
||||
return serio_register_driver(&mtouch_drv);
|
||||
}
|
||||
|
||||
static void __exit mtouch_exit(void)
|
||||
{
|
||||
serio_unregister_driver(&mtouch_drv);
|
||||
}
|
||||
|
||||
module_init(mtouch_init);
|
||||
module_exit(mtouch_exit);
|
||||
183
drivers/input/touchscreen/penmount.c
Normal file
183
drivers/input/touchscreen/penmount.c
Normal file
@@ -0,0 +1,183 @@
|
||||
/*
|
||||
* Penmount serial touchscreen driver
|
||||
*
|
||||
* Copyright (c) 2006 Rick Koch <n1gp@hotmail.com>
|
||||
*
|
||||
* Based on ELO driver (drivers/input/touchscreen/elo.c)
|
||||
* Copyright (c) 2004 Vojtech Pavlik
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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/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>
|
||||
|
||||
#define DRIVER_DESC "Penmount serial touchscreen driver"
|
||||
|
||||
MODULE_AUTHOR("Rick Koch <n1gp@hotmail.com>");
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
/*
|
||||
* Definitions & global arrays.
|
||||
*/
|
||||
|
||||
#define PM_MAX_LENGTH 5
|
||||
|
||||
/*
|
||||
* Per-touchscreen data.
|
||||
*/
|
||||
|
||||
struct pm {
|
||||
struct input_dev *dev;
|
||||
struct serio *serio;
|
||||
int idx;
|
||||
unsigned char data[PM_MAX_LENGTH];
|
||||
char phys[32];
|
||||
};
|
||||
|
||||
static irqreturn_t pm_interrupt(struct serio *serio,
|
||||
unsigned char data, unsigned int flags)
|
||||
{
|
||||
struct pm *pm = serio_get_drvdata(serio);
|
||||
struct input_dev *dev = pm->dev;
|
||||
|
||||
pm->data[pm->idx] = data;
|
||||
|
||||
if (pm->data[0] & 0x80) {
|
||||
if (PM_MAX_LENGTH == ++pm->idx) {
|
||||
input_report_abs(dev, ABS_X, pm->data[2] * 128 + pm->data[1]);
|
||||
input_report_abs(dev, ABS_Y, pm->data[4] * 128 + pm->data[3]);
|
||||
input_report_key(dev, BTN_TOUCH, !!(pm->data[0] & 0x40));
|
||||
input_sync(dev);
|
||||
pm->idx = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/*
|
||||
* pm_disconnect() is the opposite of pm_connect()
|
||||
*/
|
||||
|
||||
static void pm_disconnect(struct serio *serio)
|
||||
{
|
||||
struct pm *pm = serio_get_drvdata(serio);
|
||||
|
||||
input_get_device(pm->dev);
|
||||
input_unregister_device(pm->dev);
|
||||
serio_close(serio);
|
||||
serio_set_drvdata(serio, NULL);
|
||||
input_put_device(pm->dev);
|
||||
kfree(pm);
|
||||
}
|
||||
|
||||
/*
|
||||
* pm_connect() is the routine that is called when someone adds a
|
||||
* new serio device that supports Gunze protocol and registers it as
|
||||
* an input device.
|
||||
*/
|
||||
|
||||
static int pm_connect(struct serio *serio, struct serio_driver *drv)
|
||||
{
|
||||
struct pm *pm;
|
||||
struct input_dev *input_dev;
|
||||
int err;
|
||||
|
||||
pm = kzalloc(sizeof(struct pm), GFP_KERNEL);
|
||||
input_dev = input_allocate_device();
|
||||
if (!pm || !input_dev) {
|
||||
err = -ENOMEM;
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
pm->serio = serio;
|
||||
pm->dev = input_dev;
|
||||
snprintf(pm->phys, sizeof(pm->phys), "%s/input0", serio->phys);
|
||||
|
||||
input_dev->private = pm;
|
||||
input_dev->name = "Penmount Serial TouchScreen";
|
||||
input_dev->phys = pm->phys;
|
||||
input_dev->id.bustype = BUS_RS232;
|
||||
input_dev->id.vendor = SERIO_PENMOUNT;
|
||||
input_dev->id.product = 0;
|
||||
input_dev->id.version = 0x0100;
|
||||
input_dev->cdev.dev = &serio->dev;
|
||||
|
||||
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
|
||||
input_dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
|
||||
input_set_abs_params(pm->dev, ABS_X, 0, 0x3ff, 0, 0);
|
||||
input_set_abs_params(pm->dev, ABS_Y, 0, 0x3ff, 0, 0);
|
||||
|
||||
serio_set_drvdata(serio, pm);
|
||||
|
||||
err = serio_open(serio, drv);
|
||||
if (err)
|
||||
goto fail2;
|
||||
|
||||
err = input_register_device(pm->dev);
|
||||
if (err)
|
||||
goto fail3;
|
||||
|
||||
return 0;
|
||||
|
||||
fail3: serio_close(serio);
|
||||
fail2: serio_set_drvdata(serio, NULL);
|
||||
fail1: input_free_device(input_dev);
|
||||
kfree(pm);
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* The serio driver structure.
|
||||
*/
|
||||
|
||||
static struct serio_device_id pm_serio_ids[] = {
|
||||
{
|
||||
.type = SERIO_RS232,
|
||||
.proto = SERIO_PENMOUNT,
|
||||
.id = SERIO_ANY,
|
||||
.extra = SERIO_ANY,
|
||||
},
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(serio, pm_serio_ids);
|
||||
|
||||
static struct serio_driver pm_drv = {
|
||||
.driver = {
|
||||
.name = "penmountlpc",
|
||||
},
|
||||
.description = DRIVER_DESC,
|
||||
.id_table = pm_serio_ids,
|
||||
.interrupt = pm_interrupt,
|
||||
.connect = pm_connect,
|
||||
.disconnect = pm_disconnect,
|
||||
};
|
||||
|
||||
/*
|
||||
* The functions for inserting/removing us as a module.
|
||||
*/
|
||||
|
||||
static int __init pm_init(void)
|
||||
{
|
||||
return serio_register_driver(&pm_drv);
|
||||
}
|
||||
|
||||
static void __exit pm_exit(void)
|
||||
{
|
||||
serio_unregister_driver(&pm_drv);
|
||||
}
|
||||
|
||||
module_init(pm_init);
|
||||
module_exit(pm_exit);
|
||||
435
drivers/input/touchscreen/s3c-ts.c
Normal file
435
drivers/input/touchscreen/s3c-ts.c
Normal file
@@ -0,0 +1,435 @@
|
||||
/* linux/drivers/input/touchscreen/s3c-ts.c
|
||||
*
|
||||
* $Id: s3c-ts.c,v 1.9 2008/05/21 04:43:51 ihlee215 Exp $
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
* Copyright (c) 2004 Arnaud Patard <arnaud.patard@rtp-net.org>
|
||||
* iPAQ H1940 touchscreen support
|
||||
*
|
||||
* ChangeLog
|
||||
*
|
||||
* 2004-09-05: Herbert Potzl <herbert@13thfloor.at>
|
||||
* - added clock (de-)allocation code
|
||||
*
|
||||
* 2005-03-06: Arnaud Patard <arnaud.patard@rtp-net.org>
|
||||
* - h1940_ -> s3c24xx (this driver is now also used on the n30
|
||||
* machines :P)
|
||||
* - Debug messages are now enabled with the config option
|
||||
* TOUCHSCREEN_S3C_DEBUG
|
||||
* - Changed the way the value are read
|
||||
* - Input subsystem should now work
|
||||
* - Use ioremap and readl/writel
|
||||
*
|
||||
* 2005-03-23: Arnaud Patard <arnaud.patard@rtp-net.org>
|
||||
* - Make use of some undocumented features of the touchscreen
|
||||
* controller
|
||||
*
|
||||
* 2006-09-05: Ryu Euiyoul <ryu.real@gmail.com>
|
||||
* - added power management suspend and resume code
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/errno.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/serio.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/clk.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/hardware.h>
|
||||
|
||||
#include <asm/arch/regs-adc.h>
|
||||
#include <asm/arch/irqs.h>
|
||||
|
||||
#define CONFIG_TOUCHSCREEN_S3C_DEBUG
|
||||
#undef CONFIG_TOUCHSCREEN_S3C_DEBUG
|
||||
|
||||
/* For ts->dev.id.version */
|
||||
#define S3C_TSVERSION 0x0101
|
||||
|
||||
#define WAIT4INT(x) (((x)<<8) | \
|
||||
S3C2410_ADCTSC_YM_SEN | S3C2410_ADCTSC_YP_SEN | S3C2410_ADCTSC_XP_SEN | \
|
||||
S3C2410_ADCTSC_XY_PST(3))
|
||||
|
||||
#define AUTOPST (S3C2410_ADCTSC_YM_SEN | S3C2410_ADCTSC_YP_SEN | S3C2410_ADCTSC_XP_SEN | \
|
||||
S3C2410_ADCTSC_AUTO_PST | S3C2410_ADCTSC_XY_PST(0))
|
||||
|
||||
|
||||
#define DEBUG_LVL KERN_DEBUG
|
||||
|
||||
struct s3c_ts_mach_info {
|
||||
int delay;
|
||||
int presc;
|
||||
int oversampling_shift;
|
||||
int resol_bit;
|
||||
};
|
||||
|
||||
|
||||
/* Touchscreen default configuration */
|
||||
struct s3c_ts_mach_info s3c_ts_cfg __initdata = {
|
||||
.delay = 10000,
|
||||
.presc = 49,
|
||||
.oversampling_shift = 2,
|
||||
#if defined(CONFIG_CPU_S3C2450) || defined(CONFIG_CPU_S3C2416) || defined(CONFIG_CPU_S3C6410)
|
||||
.resol_bit = 12,
|
||||
#else
|
||||
.resol_bit = 10,
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Definitions & global arrays.
|
||||
*/
|
||||
static char *s3c_ts_name = "s3c TouchScreen";
|
||||
|
||||
|
||||
/*
|
||||
* Per-touchscreen data.
|
||||
*/
|
||||
struct s3c_ts {
|
||||
struct input_dev *dev;
|
||||
long xp;
|
||||
long yp;
|
||||
int count;
|
||||
int shift;
|
||||
char phys[32];
|
||||
};
|
||||
|
||||
static struct s3c_ts *ts;
|
||||
static void __iomem *base_addr;
|
||||
static struct clk *adc_clock;
|
||||
|
||||
static void touch_timer_fire(unsigned long data)
|
||||
{
|
||||
unsigned long data0;
|
||||
unsigned long data1;
|
||||
int updown;
|
||||
|
||||
data0 = readl(base_addr+S3C2410_ADCDAT0);
|
||||
data1 = readl(base_addr+S3C2410_ADCDAT1);
|
||||
|
||||
updown = (!(data0 & S3C2410_ADCDAT0_UPDOWN)) && (!(data1 & S3C2410_ADCDAT1_UPDOWN));
|
||||
|
||||
if (updown) {
|
||||
if (ts->count) {
|
||||
|
||||
#ifdef CONFIG_TOUCHSCREEN_S3C_DEBUG
|
||||
{
|
||||
struct timeval tv;
|
||||
do_gettimeofday(&tv);
|
||||
printk(KERN_INFO "T: %06d, X: %03ld, Y: %03ld\n", (int)tv.tv_usec, ts->xp, ts->yp);
|
||||
}
|
||||
#endif
|
||||
|
||||
input_report_abs(ts->dev, ABS_X, ts->xp);
|
||||
input_report_abs(ts->dev, ABS_Y, ts->yp);
|
||||
|
||||
input_report_key(ts->dev, BTN_TOUCH, 1);
|
||||
input_report_abs(ts->dev, ABS_PRESSURE, 1);
|
||||
input_sync(ts->dev);
|
||||
}
|
||||
|
||||
ts->xp = 0;
|
||||
ts->yp = 0;
|
||||
ts->count = 0;
|
||||
|
||||
writel(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST, base_addr+S3C2410_ADCTSC);
|
||||
writel(readl(base_addr+S3C2410_ADCCON) | S3C2410_ADCCON_ENABLE_START, base_addr+S3C2410_ADCCON);
|
||||
}
|
||||
else {
|
||||
|
||||
ts->count = 0;
|
||||
|
||||
input_report_key(ts->dev, BTN_TOUCH, 0);
|
||||
input_report_abs(ts->dev, ABS_PRESSURE, 0);
|
||||
input_sync(ts->dev);
|
||||
|
||||
writel(WAIT4INT(0), base_addr+S3C2410_ADCTSC);
|
||||
}
|
||||
}
|
||||
|
||||
static struct timer_list touch_timer =
|
||||
TIMER_INITIALIZER(touch_timer_fire, 0, 0);
|
||||
|
||||
static irqreturn_t stylus_updown(int irqno, void *param)
|
||||
{
|
||||
unsigned long data0;
|
||||
unsigned long data1;
|
||||
int updown;
|
||||
|
||||
|
||||
data0 = readl(base_addr+S3C2410_ADCDAT0);
|
||||
data1 = readl(base_addr+S3C2410_ADCDAT1);
|
||||
|
||||
updown = (!(data0 & S3C2410_ADCDAT0_UPDOWN)) && (!(data1 & S3C2410_ADCDAT1_UPDOWN));
|
||||
|
||||
#ifdef CONFIG_TOUCHSCREEN_S3C_DEBUG
|
||||
printk(KERN_INFO " %c\n", updown ? 'D' : 'U');
|
||||
#endif
|
||||
|
||||
/* TODO we should never get an interrupt with updown set while
|
||||
* the timer is running, but maybe we ought to verify that the
|
||||
* timer isn't running anyways. */
|
||||
|
||||
if (updown)
|
||||
touch_timer_fire(0);
|
||||
|
||||
#if defined(CONFIG_CPU_S3C6400) || defined(CONFIG_CPU_S3C6410)
|
||||
__raw_writel(0x0, base_addr+S3C6400_ADCCLRWK);
|
||||
__raw_writel(0x0, base_addr+S3C6400_ADCCLRINT);
|
||||
#endif
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t stylus_action(int irqno, void *param)
|
||||
{
|
||||
unsigned long data0;
|
||||
unsigned long data1;
|
||||
|
||||
data0 = readl(base_addr+S3C2410_ADCDAT0);
|
||||
data1 = readl(base_addr+S3C2410_ADCDAT1);
|
||||
|
||||
#if defined(CONFIG_CPU_S3C2450) || defined(CONFIG_CPU_S3C2416) || defined(CONFIG_CPU_S3C6410)
|
||||
ts->xp += data0 & S3C_ADCDAT0_XPDATA_MASK_12BIT;
|
||||
ts->yp += data1 & S3C_ADCDAT1_YPDATA_MASK_12BIT;
|
||||
#else
|
||||
ts->xp += data0 & S3C2410_ADCDAT0_XPDATA_MASK;
|
||||
ts->yp += data1 & S3C2410_ADCDAT1_YPDATA_MASK;
|
||||
#endif
|
||||
|
||||
ts->count++;
|
||||
|
||||
if (ts->count < (1<<ts->shift)) {
|
||||
writel(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST, base_addr+S3C2410_ADCTSC);
|
||||
writel(readl(base_addr+S3C2410_ADCCON) | S3C2410_ADCCON_ENABLE_START, base_addr+S3C2410_ADCCON);
|
||||
} else {
|
||||
mod_timer(&touch_timer, jiffies+1);
|
||||
writel(WAIT4INT(1), base_addr+S3C2410_ADCTSC);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_CPU_S3C6400) || defined(CONFIG_CPU_S3C6410)
|
||||
__raw_writel(0x0, base_addr+S3C6400_ADCCLRWK);
|
||||
__raw_writel(0x0, base_addr+S3C6400_ADCCLRINT);
|
||||
#endif
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/*
|
||||
* The functions for inserting/removing us as a module.
|
||||
*/
|
||||
static int __init s3c_ts_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct input_dev *input_dev;
|
||||
int err;
|
||||
|
||||
adc_clock = clk_get(NULL, "adc");
|
||||
if (!adc_clock) {
|
||||
printk(KERN_ERR "failed to get adc clock source\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
clk_enable(adc_clock);
|
||||
|
||||
base_addr=ioremap(S3C24XX_PA_ADC, 0x20);
|
||||
|
||||
if (base_addr == NULL) {
|
||||
printk(KERN_ERR "Failed to remap register block\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if ((s3c_ts_cfg.presc&0xff) > 0)
|
||||
writel(S3C2410_ADCCON_PRSCEN | S3C2410_ADCCON_PRSCVL(s3c_ts_cfg.presc&0xFF),\
|
||||
base_addr+S3C2410_ADCCON);
|
||||
else
|
||||
writel(0, base_addr+S3C2410_ADCCON);
|
||||
|
||||
|
||||
/* Initialise registers */
|
||||
if ((s3c_ts_cfg.delay&0xffff) > 0)
|
||||
writel(s3c_ts_cfg.delay & 0xffff, base_addr+S3C2410_ADCDLY);
|
||||
|
||||
if (s3c_ts_cfg.resol_bit == 12) {
|
||||
#if defined(CONFIG_CPU_S3C6410)
|
||||
writel(readl(base_addr+S3C2410_ADCCON)|S3C6410_ADCCON_RESSEL_12BIT, base_addr+S3C2410_ADCCON);
|
||||
#elif defined(CONFIG_CPU_S3C2450) || defined(CONFIG_CPU_S3C2416)
|
||||
writel(readl(base_addr+S3C2410_ADCCON)|S3C2450_ADCCON_RESSEL_12BIT, base_addr+S3C2410_ADCCON);
|
||||
#endif
|
||||
}
|
||||
|
||||
writel(WAIT4INT(0), base_addr+S3C2410_ADCTSC);
|
||||
|
||||
ts = kzalloc(sizeof(struct s3c_ts), GFP_KERNEL);
|
||||
|
||||
input_dev = input_allocate_device();
|
||||
|
||||
if (!input_dev) {
|
||||
err = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
ts->dev = input_dev;
|
||||
|
||||
ts->dev->evbit[0] = ts->dev->evbit[0] = BIT(EV_SYN) | BIT(EV_KEY) | BIT(EV_ABS);
|
||||
ts->dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
|
||||
|
||||
#if defined(CONFIG_CPU_S3C2450) || defined(CONFIG_CPU_S3C2416) || defined(CONFIG_CPU_S3C6410)
|
||||
input_set_abs_params(ts->dev, ABS_X, 0, 0xFFF, 0, 0);
|
||||
input_set_abs_params(ts->dev, ABS_Y, 0, 0xFFF, 0, 0);
|
||||
#else
|
||||
input_set_abs_params(ts->dev, ABS_X, 0, 0x3FF, 0, 0);
|
||||
input_set_abs_params(ts->dev, ABS_Y, 0, 0x3FF, 0, 0);
|
||||
#endif
|
||||
input_set_abs_params(ts->dev, ABS_PRESSURE, 0, 1, 0, 0);
|
||||
|
||||
sprintf(ts->phys, "input(ts)");
|
||||
|
||||
ts->dev->private = ts;
|
||||
ts->dev->name = s3c_ts_name;
|
||||
ts->dev->phys = ts->phys;
|
||||
ts->dev->id.bustype = BUS_RS232;
|
||||
ts->dev->id.vendor = 0xDEAD;
|
||||
ts->dev->id.product = 0xBEEF;
|
||||
ts->dev->id.version = S3C_TSVERSION;
|
||||
|
||||
ts->shift = s3c_ts_cfg.oversampling_shift;
|
||||
|
||||
/* Get irqs */
|
||||
if (request_irq(IRQ_ADC, stylus_action, SA_SAMPLE_RANDOM, "s3c_action", ts->dev)) {
|
||||
printk(KERN_ERR "s3c_ts.c: Could not allocate ts IRQ_ADC !\n");
|
||||
iounmap(base_addr);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (request_irq(IRQ_TC, stylus_updown, SA_SAMPLE_RANDOM, "s3c_updown", ts->dev)) {
|
||||
printk(KERN_ERR "s3c_ts.c: Could not allocate ts IRQ_TC !\n");
|
||||
iounmap(base_addr);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
printk(KERN_INFO "%s got loaded successfully : %d bits\n", s3c_ts_name, s3c_ts_cfg.resol_bit);
|
||||
|
||||
/* All went ok, so register to the input system */
|
||||
err = input_register_device(ts->dev);
|
||||
|
||||
if(err) {
|
||||
free_irq(IRQ_TC, ts->dev);
|
||||
free_irq(IRQ_ADC, ts->dev);
|
||||
clk_disable(adc_clock);
|
||||
iounmap(base_addr);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail: input_free_device(input_dev);
|
||||
kfree(ts);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int s3c_ts_remove(struct platform_device *dev)
|
||||
{
|
||||
printk(KERN_INFO "s3c_ts_remove() of TS called !\n");
|
||||
|
||||
disable_irq(IRQ_ADC);
|
||||
disable_irq(IRQ_TC);
|
||||
free_irq(IRQ_TC, ts->dev);
|
||||
free_irq(IRQ_ADC, ts->dev);
|
||||
|
||||
if (adc_clock) {
|
||||
clk_disable(adc_clock);
|
||||
clk_put(adc_clock);
|
||||
adc_clock = NULL;
|
||||
}
|
||||
|
||||
input_unregister_device(ts->dev);
|
||||
iounmap(base_addr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static unsigned int adccon, adctsc, adcdly;
|
||||
|
||||
static int s3c_ts_suspend(struct platform_device *dev, pm_message_t state)
|
||||
{
|
||||
adccon = readl(base_addr+S3C2410_ADCCON);
|
||||
adctsc = readl(base_addr+S3C2410_ADCTSC);
|
||||
adcdly = readl(base_addr+S3C2410_ADCDLY);
|
||||
|
||||
disable_irq(IRQ_ADC);
|
||||
disable_irq(IRQ_TC);
|
||||
|
||||
clk_disable(adc_clock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int s3c_ts_resume(struct platform_device *pdev)
|
||||
{
|
||||
clk_enable(adc_clock);
|
||||
|
||||
writel(adccon, base_addr+S3C2410_ADCCON);
|
||||
writel(adctsc, base_addr+S3C2410_ADCTSC);
|
||||
writel(adcdly, base_addr+S3C2410_ADCDLY);
|
||||
writel(WAIT4INT(0), base_addr+S3C2410_ADCTSC);
|
||||
|
||||
enable_irq(IRQ_ADC);
|
||||
enable_irq(IRQ_TC);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define s3c_ts_suspend NULL
|
||||
#define s3c_ts_resume NULL
|
||||
#endif
|
||||
|
||||
static struct platform_driver s3c_ts_driver = {
|
||||
.probe = s3c_ts_probe,
|
||||
.remove = s3c_ts_remove,
|
||||
.suspend = s3c_ts_suspend,
|
||||
.resume = s3c_ts_resume,
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "s3c2410-adc",
|
||||
},
|
||||
};
|
||||
|
||||
static char banner[] __initdata = KERN_INFO "S3C Touchscreen driver, (c) 2007 Samsung Electronics\n";
|
||||
|
||||
static int __init s3c_ts_init(void)
|
||||
{
|
||||
printk(banner);
|
||||
return platform_driver_register(&s3c_ts_driver);
|
||||
}
|
||||
|
||||
static void __exit s3c_ts_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&s3c_ts_driver);
|
||||
}
|
||||
|
||||
module_init(s3c_ts_init);
|
||||
module_exit(s3c_ts_exit);
|
||||
|
||||
MODULE_AUTHOR("Samsung AP");
|
||||
MODULE_DESCRIPTION("s3c touchscreen driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
863
drivers/input/touchscreen/s3c_ts_iic.c
Normal file
863
drivers/input/touchscreen/s3c_ts_iic.c
Normal file
@@ -0,0 +1,863 @@
|
||||
/* Qisda Tony 090406, add Auo touch i2c driver [
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
* Copyright (c) 2009 Tony YC Huang <tony.yc.huang@qisda.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/errno.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/delay.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/arch/regs-gpio.h>
|
||||
#include <asm/arch/regs-irq.h>
|
||||
#include <asm/arch/map.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include "s3c_ts_iic.h"
|
||||
#include <asm/arch/regs-gpio.h>
|
||||
//Tony test
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/sched.h>
|
||||
|
||||
//Tony test
|
||||
//#define IN_2416_EMU_BOARD
|
||||
//#define QISDA_TEST_GPIO_PROBE
|
||||
//Qisda Tony 090415 [
|
||||
//#define POLLING_MODE
|
||||
//#define KTHREAD_USE
|
||||
//Qisda Tony 090415 ]
|
||||
#define DEVICE_NAME "s3c2410-ts-iic"
|
||||
#define TRUE 1
|
||||
#define FALSE 0
|
||||
|
||||
//#ifdef IN_2416_EMU_BOARD
|
||||
//#define GPIO_NUM (0x3)
|
||||
//#else
|
||||
//#define GPIO_NUM (0x2)
|
||||
//#endif
|
||||
|
||||
#define KEY_BUF_SIZE 1
|
||||
struct s3c_ts_iic {
|
||||
struct input_dev *dev;
|
||||
struct i2c_client *client;
|
||||
struct platform_device *pdev;
|
||||
long xp;
|
||||
long yp;
|
||||
unsigned int polling_time;
|
||||
};
|
||||
|
||||
struct t_point {
|
||||
short x;
|
||||
short y;
|
||||
};
|
||||
|
||||
static struct platform_driver s3c_ts_iic_driver;
|
||||
static struct work_struct workq_readdata;
|
||||
static struct s3c_ts_iic *ts_iic;
|
||||
static char *s3c_ts_iic_name = "TouchScreen Of IIC Type";
|
||||
#ifdef QISDA_TEST_GPIO_PROBE
|
||||
static void __iomem *g_pGPIOReg_GPFCON;
|
||||
#endif
|
||||
struct t_point keybuffer[KEY_BUF_SIZE];
|
||||
static short IdxBottom =0;
|
||||
static struct timer_list touch_iic_timer_qisda;
|
||||
|
||||
#ifdef POLLING_MODE
|
||||
static int need_calibration = FALSE;
|
||||
#endif
|
||||
|
||||
#ifdef KTHREAD_USE
|
||||
static wait_queue_head_t wait;
|
||||
static struct task_struct *kmain_task=NULL;
|
||||
#endif
|
||||
|
||||
//Tony test
|
||||
#define NEW_TCOM_BOARD
|
||||
|
||||
//static void touch_iic_disable_interrupt(void) { printk("touch_iic_disable_interrupt\n"); writel(readl(S3C24XX_EINTMASK) | (0x1<<GPIO_NUM), S3C24XX_EINTMASK); }
|
||||
//static void touch_iic_enable_interrupt(void) { printk("touch_iic_enable_interrupt\n"); writel(readl(S3C24XX_EINTMASK) & ~(0x1<<GPIO_NUM), S3C24XX_EINTMASK); }
|
||||
//static void touch_iic_clear_interrupt_pending(void) { printk("touch_iic_clear_interrupt_pending\n"); writel(0x1<<GPIO_NUM, S3C24XX_EINTPEND); }
|
||||
//static void touch_iic_set_interrupt_method(int eSM) {writel(readl(S3C24XX_EXTINT0) & ~(0x7<<(GPIO_NUM<<2)) | (eSM<<(GPIO_NUM<<2)),S3C24XX_EXTINT0);}
|
||||
static void touhc_iic_timer_handler_qisda(unsigned long data) { schedule_work(&workq_readdata); }
|
||||
|
||||
/*kit.mod 2009/11/17*/
|
||||
#define TOUCH_IIC_CALIBRATION 1
|
||||
#define TOUCH_IIC_SET_POLLING_TIME 2
|
||||
#define TOUCH_DEVICE_POWER_STATE 3
|
||||
#define TOUCH_DEVICE_ACTIVE_STATE 0 //Power State D0
|
||||
#define TOUCH_DEVICE_SLEEP_STATE 1 //Power State D1
|
||||
#define TOUCH_DEVICE_DEEPSLEEP_STATE 2 //Power State D2
|
||||
#define TOUCH_DEVICE_POWEROFF_STATE 3 //Power State D3
|
||||
/*kit.end*/
|
||||
|
||||
/*kit.add 2009/11/04*/
|
||||
static short isTouchPowerOff = 0;
|
||||
static short touchPowerState = TOUCH_DEVICE_ACTIVE_STATE;
|
||||
extern unsigned int wkup_srce;
|
||||
/*kit.end*/
|
||||
|
||||
/*kit.add 2009/11/16*/
|
||||
#define TOUCH_ACTIVE_POLLING_TIME 20 //default value
|
||||
#define TOUCH_SLEEP_POLLING_TIME 10
|
||||
#define TOUCH_DEEPSLEEP_POLLING_TIME 1
|
||||
/*kit.end*/
|
||||
|
||||
/*
|
||||
static void touch_iic_port_initialize(void)
|
||||
{
|
||||
#ifndef IN_2416_EMU_BOARD
|
||||
//Set GPG3 pull-up, enable touch panel functions
|
||||
writel(readl(S3C2410_GPGDAT) | (1<<3), S3C2410_GPGDAT);
|
||||
writel(readl(S3C2410_GPGUP) | (0x2<<6),S3C2410_GPGUP);
|
||||
writel(readl(S3C2410_GPGCON) | (0x1<<6),S3C2410_GPGCON);
|
||||
#endif
|
||||
|
||||
//Qisda Tony 090415 [
|
||||
#ifndef POLLING_MODE
|
||||
#ifdef IN_2416_EMU_BOARD
|
||||
s3c2410_gpio_cfgpin(S3C2410_GPF3, S3C2410_GPF3_EINT3);
|
||||
s3c2410_gpio_pullup(S3C2410_GPF3, 2);
|
||||
#else
|
||||
s3c2410_gpio_cfgpin(S3C2410_GPF2, S3C2410_GPF2_EINT2);
|
||||
s3c2410_gpio_pullup(S3C2410_GPF2, 2);
|
||||
#endif
|
||||
#endif
|
||||
//Qisda Tony 090415 ]
|
||||
|
||||
}
|
||||
*/
|
||||
/*
|
||||
static void touch_iic_init(void)
|
||||
{
|
||||
printk("touch_iic_init\n");
|
||||
touch_iic_port_initialize();
|
||||
//Qisda Tony 090415 [
|
||||
#ifndef POLLING_MODE
|
||||
touch_iic_disable_interrupt();
|
||||
touch_iic_set_interrupt_method(S3C2410_EXTINT_FALLEDGE);
|
||||
//Tony test
|
||||
#ifndef KTHREAD_USE
|
||||
init_timer(&touch_iic_timer_qisda);
|
||||
touch_iic_timer_qisda.function =touhc_iic_timer_handler_qisda;
|
||||
#endif
|
||||
#endif
|
||||
//Qisda Tony 090415 ]
|
||||
memset(keybuffer,0x0,sizeof(keybuffer));
|
||||
}
|
||||
*/
|
||||
|
||||
static void ts_iic_workqueue(struct work_struct *work)
|
||||
{
|
||||
static int startpoint[2];
|
||||
static int emptyKeyBuffer=TRUE;
|
||||
int curpoint[2];
|
||||
short i=0;
|
||||
|
||||
#ifdef NEW_TCOM_BOARD
|
||||
static int zeroCount=0;
|
||||
static int prevpoint[2];
|
||||
#endif
|
||||
|
||||
|
||||
//Qisda Tony 090415 [
|
||||
#ifdef POLLING_MODE
|
||||
#ifdef KTHREAD_USE
|
||||
for(;;){
|
||||
//This only get the interrupt periodically. No while loop in workqueue, use polling....
|
||||
//Tony test
|
||||
//Tony test
|
||||
#ifndef NEW_TCOM_BOARD
|
||||
#else
|
||||
ts_iic->client->driver->command(ts_iic->client,QISDA_IIC_READ_POSITION,curpoint);
|
||||
if((curpoint[0]==0) ||(curpoint[1]==0))
|
||||
{
|
||||
if(emptyKeyBuffer==TRUE) goto finished;
|
||||
#ifdef NEW_TCOM_BOARD
|
||||
else
|
||||
{
|
||||
if(zeroCount<3)
|
||||
{
|
||||
zeroCount++;
|
||||
goto finished;
|
||||
}
|
||||
else
|
||||
{
|
||||
//printk("\nooooo %d,%d\n",curpoint[0],curpoint[1]);
|
||||
goto release;
|
||||
}
|
||||
}
|
||||
#else
|
||||
else goto release;
|
||||
#endif
|
||||
}
|
||||
//key input
|
||||
ts_iic->xp = keybuffer[IdxBottom].x = curpoint[0];
|
||||
ts_iic->yp =keybuffer[IdxBottom].y = curpoint[1];
|
||||
input_report_abs(ts_iic->dev, ABS_X, ts_iic->xp);
|
||||
input_report_abs(ts_iic->dev, ABS_Y, ts_iic->yp);
|
||||
input_report_key(ts_iic->dev, BTN_TOUCH, 1);
|
||||
input_report_abs(ts_iic->dev, ABS_PRESSURE,1);
|
||||
input_sync(ts_iic->dev);
|
||||
//printk("\n--%d,%d,%d\n",curpoint[0],curpoint[1],IdxBottom);
|
||||
#ifdef NEW_TCOM_BOARD
|
||||
prevpoint[0]=curpoint[0];
|
||||
prevpoint[1]=curpoint[1];
|
||||
#endif
|
||||
if(emptyKeyBuffer==TRUE)
|
||||
{
|
||||
emptyKeyBuffer=FALSE;
|
||||
memcpy(startpoint,curpoint,sizeof(startpoint));
|
||||
printk("\n--%d,%d,%d\n",startpoint[0],startpoint[1],IdxBottom);
|
||||
}
|
||||
IdxBottom++;
|
||||
if( IdxBottom >= KEY_BUF_SIZE )
|
||||
IdxBottom = 0;
|
||||
goto finished;
|
||||
release:
|
||||
printk("++%d,%d,%d\n",startpoint[0],startpoint[1],IdxBottom);
|
||||
for(i=0;i<KEY_BUF_SIZE;++i)
|
||||
printk("%d,%d\n",keybuffer[i].x,keybuffer[i].y);
|
||||
i = IdxBottom -1;
|
||||
if(i<=-1)
|
||||
i = KEY_BUF_SIZE -1;
|
||||
startpoint[0]-=keybuffer[i].x;
|
||||
startpoint[1]-=keybuffer[i].y;
|
||||
//Put your gesture function here!!!!
|
||||
if(( startpoint[1]<-50) || (startpoint[1]>50))
|
||||
printk(KERN_ERR "ts_iic_workqueue okokokokokokokokok\n");
|
||||
memset(keybuffer,0x0,sizeof(keybuffer));
|
||||
memset(startpoint,0x0,sizeof(startpoint));
|
||||
IdxBottom = 0;
|
||||
emptyKeyBuffer = TRUE;
|
||||
//Key input
|
||||
ts_iic->xp =ts_iic->yp = 0;
|
||||
input_report_key(ts_iic->dev, BTN_TOUCH, 0);
|
||||
input_report_abs(ts_iic->dev, ABS_PRESSURE,0);
|
||||
input_sync(ts_iic->dev);
|
||||
#ifdef NEW_TCOM_BOARD
|
||||
prevpoint[0]=0;
|
||||
prevpoint[1]=0;
|
||||
zeroCount = 0;
|
||||
#endif
|
||||
finished:
|
||||
msleep(15);
|
||||
#endif //#if NEW_TCOM_BOARD
|
||||
}
|
||||
|
||||
#else //Not Ktherad_use
|
||||
//This only get the interrupt periodically. No while loop in workqueue, use polling....
|
||||
ts_iic->client->driver->command(ts_iic->client,QISDA_IIC_READ_POSITION,curpoint);
|
||||
|
||||
if((curpoint[0]==0) ||(curpoint[1]==0))
|
||||
{
|
||||
if(emptyKeyBuffer==TRUE) goto finished;
|
||||
|
||||
#ifdef NEW_TCOM_BOARD
|
||||
else
|
||||
{
|
||||
if(zeroCount<3)
|
||||
{
|
||||
zeroCount++;
|
||||
goto finished;
|
||||
}
|
||||
else
|
||||
{
|
||||
//printk("\nooooo %d,%d\n", curpoint[0], curpoint[1]);
|
||||
goto release;
|
||||
}
|
||||
}
|
||||
#else
|
||||
else goto release;
|
||||
#endif
|
||||
|
||||
}
|
||||
//key input
|
||||
ts_iic->xp = keybuffer[IdxBottom].x = curpoint[0];
|
||||
ts_iic->yp =keybuffer[IdxBottom].y = curpoint[1];
|
||||
input_report_abs(ts_iic->dev, ABS_X, ts_iic->xp);
|
||||
input_report_abs(ts_iic->dev, ABS_Y, ts_iic->yp);
|
||||
input_report_key(ts_iic->dev, BTN_TOUCH, 1);
|
||||
input_report_abs(ts_iic->dev, ABS_PRESSURE,1);
|
||||
input_sync(ts_iic->dev);
|
||||
//printk("\n--%d,%d,%d\n",curpoint[0],curpoint[1],IdxBottom);
|
||||
if(emptyKeyBuffer==TRUE)
|
||||
{
|
||||
emptyKeyBuffer=FALSE;
|
||||
memcpy(startpoint,curpoint,sizeof(startpoint));
|
||||
printk("\n++%d,%d,%d\n",startpoint[0],startpoint[1],IdxBottom);
|
||||
}
|
||||
IdxBottom++;
|
||||
if( IdxBottom >= KEY_BUF_SIZE )
|
||||
IdxBottom = 0;
|
||||
goto finished;
|
||||
release:
|
||||
printk("--%d,%d,%d\n", curpoint[0], curpoint[1],IdxBottom);
|
||||
for(i=0;i<KEY_BUF_SIZE;++i)
|
||||
printk("%d,%d\n",keybuffer[i].x,keybuffer[i].y);
|
||||
i = IdxBottom -1;
|
||||
if(i<=-1)
|
||||
i = KEY_BUF_SIZE -1;
|
||||
startpoint[0]-=keybuffer[i].x;
|
||||
startpoint[1]-=keybuffer[i].y;
|
||||
//Put your gesture function here!!!!
|
||||
if(( startpoint[1]<-50) || (startpoint[1]>50))
|
||||
printk(KERN_ERR "ts_iic_workqueue okokokokokokokokok\n");
|
||||
memset(keybuffer,0x0,sizeof(keybuffer));
|
||||
memset(startpoint,0x0,sizeof(startpoint));
|
||||
IdxBottom = 0;
|
||||
emptyKeyBuffer = TRUE;
|
||||
//Key input
|
||||
ts_iic->xp =ts_iic->yp = 0;
|
||||
input_report_key(ts_iic->dev, BTN_TOUCH, 0);
|
||||
input_report_abs(ts_iic->dev, ABS_PRESSURE,0);
|
||||
input_sync(ts_iic->dev);
|
||||
#ifdef NEW_TCOM_BOARD
|
||||
prevpoint[0]=0;
|
||||
prevpoint[1]=0;
|
||||
zeroCount = 0;
|
||||
#endif
|
||||
|
||||
finished:
|
||||
if(need_calibration){
|
||||
printk("TOUCH_IIC_CALIBRATION...");
|
||||
ts_iic->client->driver->command(ts_iic->client, QISDA_IIC_CALIBRATION, NULL);
|
||||
need_calibration = FALSE;
|
||||
printk("done\n");
|
||||
}
|
||||
|
||||
touch_iic_timer_qisda.expires = jiffies + (HZ/ts_iic->polling_time);
|
||||
mod_timer(&touch_iic_timer_qisda,touch_iic_timer_qisda.expires);
|
||||
return;
|
||||
#endif //KTHREAD_USE
|
||||
|
||||
|
||||
#else // NOT POLLING_MODE
|
||||
//This only get the interrupt
|
||||
ts_iic->client->driver->command(ts_iic->client,QISDA_IIC_READ_POSITION,curpoint);
|
||||
|
||||
if((curpoint[0]==0) ||(curpoint[1]==0))
|
||||
{
|
||||
//printk("\n[%d] --%d,%d,%d\n", __LINE__, curpoint[0], curpoint[1], emptyKeyBuffer);
|
||||
if(emptyKeyBuffer==TRUE){
|
||||
emptyKeyBuffer = FALSE;
|
||||
goto finished;
|
||||
}
|
||||
else{
|
||||
//printk("goto release \n");
|
||||
goto release;
|
||||
}
|
||||
}
|
||||
//key input
|
||||
ts_iic->xp = keybuffer[IdxBottom].x = curpoint[0];
|
||||
ts_iic->yp =keybuffer[IdxBottom].y = curpoint[1];
|
||||
input_report_abs(ts_iic->dev, ABS_X, ts_iic->xp);
|
||||
input_report_abs(ts_iic->dev, ABS_Y, ts_iic->yp);
|
||||
input_report_key(ts_iic->dev, BTN_TOUCH, 1);
|
||||
input_report_abs(ts_iic->dev, ABS_PRESSURE,1);
|
||||
input_sync(ts_iic->dev);
|
||||
//printk("\n[%d] --%d,%d,%d\n", __LINE__, curpoint[0], curpoint[1], emptyKeyBuffer);
|
||||
if((startpoint[0]==0) || (startpoint[1]==0))
|
||||
{
|
||||
emptyKeyBuffer=FALSE;
|
||||
memcpy(startpoint,curpoint,sizeof(startpoint));
|
||||
printk("\n++%d,%d,%d\n",startpoint[0],startpoint[1],IdxBottom);
|
||||
}
|
||||
IdxBottom++;
|
||||
if( IdxBottom >= KEY_BUF_SIZE )
|
||||
IdxBottom = 0;
|
||||
goto finished;
|
||||
release:
|
||||
printk("--%d,%d,%d\n", curpoint[0], curpoint[1],IdxBottom);
|
||||
for(i=0;i<KEY_BUF_SIZE;++i)
|
||||
printk("%d,%d\n",keybuffer[i].x,keybuffer[i].y);
|
||||
i = IdxBottom -1;
|
||||
if(i<=-1)
|
||||
i = KEY_BUF_SIZE -1;
|
||||
startpoint[0]-=keybuffer[i].x;
|
||||
startpoint[1]-=keybuffer[i].y;
|
||||
//Put your gesture function here!!!!
|
||||
if(( startpoint[1]<-50) || (startpoint[1]>50))
|
||||
printk(KERN_ERR "ts_iic_workqueue okokokokokokokokok\n");
|
||||
|
||||
memset(keybuffer,0x0,sizeof(keybuffer));
|
||||
memset(startpoint,0x0,sizeof(startpoint));
|
||||
IdxBottom = 0;
|
||||
emptyKeyBuffer = TRUE;
|
||||
//Key input
|
||||
ts_iic->xp =ts_iic->yp = 0;
|
||||
input_report_key(ts_iic->dev, BTN_TOUCH, 0);
|
||||
input_report_abs(ts_iic->dev, ABS_PRESSURE,0);
|
||||
input_sync(ts_iic->dev);
|
||||
#ifdef NEW_TCOM_BOARD
|
||||
prevpoint[0]=0;
|
||||
prevpoint[1]=0;
|
||||
zeroCount = 0;
|
||||
#endif
|
||||
del_timer(&touch_iic_timer_qisda);
|
||||
enable_irq(IRQ_EINT2);
|
||||
//touch_iic_clear_interrupt_pending();
|
||||
//touch_iic_enable_interrupt();
|
||||
return;
|
||||
finished:
|
||||
//msleep(15);
|
||||
touch_iic_timer_qisda.expires = jiffies + (HZ/ts_iic->polling_time);
|
||||
mod_timer(&touch_iic_timer_qisda,touch_iic_timer_qisda.expires);
|
||||
return;
|
||||
#endif
|
||||
//Qisda Tony 090415 ]
|
||||
}
|
||||
|
||||
//static void ts_iic_port_deinit(void)
|
||||
//{
|
||||
//}
|
||||
|
||||
//Qisda Tony 090415 [
|
||||
#ifndef POLLING_MODE
|
||||
static irqreturn_t touch_iic_isr(int irq, void *dev_id)
|
||||
{
|
||||
//static int count=0;
|
||||
printk(KERN_ERR "\ntouch_iic_isr\n");
|
||||
disable_irq(IRQ_EINT2);
|
||||
//touch_iic_disable_interrupt();
|
||||
//touch_iic_clear_interrupt_pending();
|
||||
touch_iic_timer_qisda.expires = jiffies + (HZ/ts_iic->polling_time);
|
||||
add_timer(&touch_iic_timer_qisda);
|
||||
//It wait for kernel timer launch.
|
||||
//schedule_work(&workq_readdata);
|
||||
//Enable interrupt funciton will postphone at workqueue stage
|
||||
//touch_iic_enable_interrupt();
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
#endif
|
||||
//Qisda Tony 090415 ]
|
||||
|
||||
static int s3c_ts_iic_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*************************/
|
||||
/*** GPF2 => TP_INT ***/
|
||||
/*** GPF5 => TOUCH_EN ***/
|
||||
/*************************/
|
||||
static int s3c_ts_iic_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
switch(cmd){
|
||||
case TOUCH_IIC_CALIBRATION:
|
||||
#ifdef POLLING_MODE
|
||||
need_calibration = TRUE;
|
||||
#else
|
||||
printk("TOUCH_IIC_CALIBRATION...");
|
||||
disable_irq(IRQ_EINT2);
|
||||
ts_iic->client->driver->command(ts_iic->client, QISDA_IIC_CALIBRATION, NULL);
|
||||
printk("done\n");
|
||||
enable_irq(IRQ_EINT2);
|
||||
#endif
|
||||
break;
|
||||
|
||||
case TOUCH_IIC_SET_POLLING_TIME:
|
||||
if((arg<1) || (arg>80)){
|
||||
printk("polling time should be 1-80\n");
|
||||
}
|
||||
else{
|
||||
ts_iic->polling_time = arg;
|
||||
}
|
||||
break;
|
||||
/*kit.mod 2009/11/04*/
|
||||
case TOUCH_DEVICE_POWER_STATE:
|
||||
switch(arg)
|
||||
{
|
||||
case TOUCH_DEVICE_ACTIVE_STATE:
|
||||
if(isTouchPowerOff)
|
||||
{
|
||||
isTouchPowerOff = 0;
|
||||
s3c2410_gpio_cfgpin(S3C2410_GPF5, S3C2410_GPF5_OUTP);
|
||||
s3c2410_gpio_setpin(S3C2410_GPF5, 1);
|
||||
msleep(200);
|
||||
ts_iic->client->driver->command(ts_iic->client, QISDA_IIC_INIT, NULL);
|
||||
enable_irq(IRQ_EINT2);
|
||||
}
|
||||
ts_iic->client->driver->command(ts_iic->client, QISDA_IIC_ACTIVE_MODE, NULL);
|
||||
|
||||
/*kit.add 2009/11/16*/
|
||||
ts_iic->polling_time = TOUCH_ACTIVE_POLLING_TIME;
|
||||
touchPowerState = TOUCH_DEVICE_ACTIVE_STATE;
|
||||
/*kit.end*/
|
||||
|
||||
printk("Touch Device => Active State\n");
|
||||
break;
|
||||
|
||||
case TOUCH_DEVICE_SLEEP_STATE:
|
||||
if(isTouchPowerOff)
|
||||
{
|
||||
isTouchPowerOff = 0;
|
||||
s3c2410_gpio_cfgpin(S3C2410_GPF5, S3C2410_GPF5_OUTP);
|
||||
s3c2410_gpio_setpin(S3C2410_GPF5, 1);
|
||||
msleep(200);
|
||||
ts_iic->client->driver->command(ts_iic->client, QISDA_IIC_INIT,NULL);
|
||||
enable_irq(IRQ_EINT2);
|
||||
}
|
||||
ts_iic->client->driver->command(ts_iic->client, QISDA_IIC_SLEEP_MODE, NULL);
|
||||
|
||||
/*kit.add 2009/11/16*/
|
||||
ts_iic->polling_time = TOUCH_SLEEP_POLLING_TIME;
|
||||
touchPowerState = TOUCH_DEVICE_SLEEP_STATE;
|
||||
/*kit.end*/
|
||||
|
||||
printk("Touch Device => Sleep State\n");
|
||||
break;
|
||||
|
||||
case TOUCH_DEVICE_DEEPSLEEP_STATE:
|
||||
if(isTouchPowerOff)
|
||||
{
|
||||
isTouchPowerOff = 0;
|
||||
s3c2410_gpio_cfgpin(S3C2410_GPF5, S3C2410_GPF5_OUTP);
|
||||
s3c2410_gpio_setpin(S3C2410_GPF5, 1);
|
||||
msleep(200);
|
||||
ts_iic->client->driver->command(ts_iic->client, QISDA_IIC_INIT,NULL);
|
||||
enable_irq(IRQ_EINT2);
|
||||
}
|
||||
ts_iic->client->driver->command(ts_iic->client, QISDA_IIC_DEEPSLEEP_MODE, NULL);
|
||||
|
||||
/*kit.add 2009/11/16*/
|
||||
ts_iic->polling_time = TOUCH_DEEPSLEEP_POLLING_TIME;
|
||||
touchPowerState = TOUCH_DEVICE_DEEPSLEEP_STATE;
|
||||
/*kit.end*/
|
||||
|
||||
printk("Touch Device => Deep Sleep State\n");
|
||||
break;
|
||||
|
||||
case TOUCH_DEVICE_POWEROFF_STATE:
|
||||
if(isTouchPowerOff)
|
||||
printk("Touch Device => Already Power Off State\n");
|
||||
else
|
||||
{
|
||||
isTouchPowerOff = 1;
|
||||
disable_irq(IRQ_EINT2);
|
||||
s3c2410_gpio_cfgpin(S3C2410_GPF5, S3C2410_GPF5_OUTP);
|
||||
s3c2410_gpio_setpin(S3C2410_GPF5, 0);
|
||||
printk("Touch Device => Power Off State\n");
|
||||
}
|
||||
|
||||
/*kit.add 2009/11/16*/
|
||||
touchPowerState = TOUCH_DEVICE_POWEROFF_STATE;
|
||||
/*kit.end*/
|
||||
break;
|
||||
|
||||
default:
|
||||
printk("Touch Device => No Such Power State...\n");
|
||||
}
|
||||
break;
|
||||
/*kit.end*/
|
||||
|
||||
default:
|
||||
printk("No such command\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* File operations struct for character device */
|
||||
static const struct file_operations s3c_ts_iic_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.ioctl = s3c_ts_iic_ioctl,
|
||||
.open = s3c_ts_iic_open,
|
||||
.release = NULL
|
||||
};
|
||||
|
||||
|
||||
|
||||
int s3c_ts_iic_port_init(struct i2c_client *c)
|
||||
{
|
||||
|
||||
int err = 0;
|
||||
|
||||
ts_iic->client = c;
|
||||
err = c->driver->command(c,QISDA_IIC_INIT,NULL);
|
||||
if(err<0){
|
||||
printk("Touch init faied\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
//touch_iic_init();
|
||||
//touch_iic_port_initialize();
|
||||
|
||||
//Qisda Tony 090415 [
|
||||
#ifdef POLLING_MODE
|
||||
|
||||
#ifdef KTHREAD_USE
|
||||
init_waitqueue_head(&wait);
|
||||
kmain_task = kthread_create(ts_iic_workqueue, NULL,"ts_iic_kthread");
|
||||
wake_up_process(kmain_task);
|
||||
#else
|
||||
init_timer(&touch_iic_timer_qisda);
|
||||
touch_iic_timer_qisda.function =touhc_iic_timer_handler_qisda;
|
||||
//touch_iic_timer_qisda.data;
|
||||
touch_iic_timer_qisda.expires = jiffies + 10*HZ;
|
||||
add_timer(&touch_iic_timer_qisda);
|
||||
#endif
|
||||
|
||||
#else //NOT POLLING_MODE
|
||||
|
||||
s3c2410_gpio_cfgpin(S3C2410_GPF2, S3C2410_GPF2_INP);
|
||||
s3c2410_gpio_pullup(S3C2410_GPF2, 2);
|
||||
|
||||
#ifndef KTHREAD_USE
|
||||
init_timer(&touch_iic_timer_qisda);
|
||||
touch_iic_timer_qisda.function =touhc_iic_timer_handler_qisda;
|
||||
#endif
|
||||
|
||||
set_irq_type(IRQ_EINT2, S3C2410_EXTINT_FALLEDGE);
|
||||
err = request_irq(IRQ_EINT2, touch_iic_isr, SA_INTERRUPT, DEVICE_NAME, (void *) ts_iic->pdev);
|
||||
|
||||
if (err) {
|
||||
printk("request_irq failed (IRQ_EINT2) !!!\n");
|
||||
free_irq(IRQ_EINT2, ts_iic->dev);
|
||||
//ts_iic_port_deinit();
|
||||
goto fail;
|
||||
}
|
||||
|
||||
//touch_iic_clear_interrupt_pending();
|
||||
//touch_iic_enable_interrupt();
|
||||
#endif //End of NOT POLLING_MODE
|
||||
|
||||
memset(keybuffer,0x0,sizeof(keybuffer));
|
||||
//Qisda Tony 090415 ]
|
||||
if (register_chrdev (TOUCH_MAJOR, "touch_cmd", &s3c_ts_iic_fops)) {
|
||||
printk("unable to get major %d\n", TOUCH_MAJOR);
|
||||
|
||||
}
|
||||
printk("s3c_ts_iic_port_init is initialized!!\n");
|
||||
|
||||
|
||||
s3c2410_gpio_pullup(S3C2410_GPF5, 2);
|
||||
s3c2410_gpio_cfgpin(S3C2410_GPF5, S3C2410_GPF5_OUTP);
|
||||
s3c2410_gpio_setpin(S3C2410_GPF5,1);
|
||||
|
||||
/*Qisda Qube for open smartcard pwr*/
|
||||
#if defined (CONFIG_QISDA_AS090B00_EVT1) || defined (CONFIG_QISDA_AS090B00_EVT1_1)
|
||||
s3c2410_gpio_cfgpin(S3C2410_GPC3, S3C2410_GPC3_OUTP);
|
||||
s3c2410_gpio_setpin(S3C2410_GPC3,1);
|
||||
#endif
|
||||
/*Qisda Qube for open smartcard pwr*/
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
platform_driver_unregister(&s3c_ts_iic_driver);
|
||||
input_free_device(ts_iic->dev);
|
||||
kfree(ts_iic);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static int __init s3c_ts_iic_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct input_dev *input_dev;
|
||||
int err;
|
||||
|
||||
//Tony test
|
||||
#ifdef QISDA_TEST_GPIO_PROBE
|
||||
g_pGPIOReg_GPFCON = ioremap(0x56000050, 0x00100000);
|
||||
writel(readl(g_pGPIOReg_GPFCON) | (0x1<<0), g_pGPIOReg_GPFCON);
|
||||
writel(readl(g_pGPIOReg_GPFCON+0x04) | (0x1<<0), g_pGPIOReg_GPFCON+0x04);
|
||||
writel(readl(g_pGPIOReg_GPFCON+0x08) | (0x2<<0), g_pGPIOReg_GPFCON+0x08);
|
||||
#endif
|
||||
|
||||
ts_iic = kzalloc(sizeof(struct s3c_ts_iic), GFP_KERNEL);
|
||||
input_dev = input_allocate_device();
|
||||
|
||||
if (!input_dev) {
|
||||
err = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
ts_iic->dev = input_dev;
|
||||
|
||||
ts_iic->dev->evbit[0] = ts_iic->dev->evbit[0] = BIT(EV_SYN) | BIT(EV_KEY) | BIT(EV_ABS);
|
||||
ts_iic->dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
|
||||
|
||||
input_set_abs_params(ts_iic->dev, ABS_X, 0, 0xFFF, 0, 0);
|
||||
input_set_abs_params(ts_iic->dev, ABS_Y, 0, 0xFFF, 0, 0);
|
||||
input_set_abs_params(ts_iic->dev, ABS_PRESSURE, 0, 1, 0, 0);
|
||||
|
||||
ts_iic->dev->private = ts_iic;
|
||||
ts_iic->dev->name = s3c_ts_iic_name;
|
||||
ts_iic->dev->id.bustype = BUS_I2C;
|
||||
ts_iic->dev->id.vendor = 0xDEAD;
|
||||
ts_iic->dev->id.product = 0xBEEF;
|
||||
ts_iic->dev->id.version = 0x0010;
|
||||
platform_set_drvdata(pdev, ts_iic);
|
||||
|
||||
/* All went ok, so register to the input system */
|
||||
err = input_register_device(ts_iic->dev);
|
||||
if(err) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ts_iic->pdev = pdev;
|
||||
ts_iic->polling_time = 20;
|
||||
INIT_WORK(&workq_readdata,ts_iic_workqueue);
|
||||
//However, stop interrupt first. This is because the signal is emitted from the panel repeatedly
|
||||
//Unless we set the interrupt mode of the the panel to stop emitting.
|
||||
//touch_iic_disable_interrupt();
|
||||
//printk("s3c_ts_iic_probe IRQ is initialized!!\n");
|
||||
|
||||
// touch panel enable in common-smdk.c
|
||||
//writel((readl(S3C2416_GPKCON) & ~(1<<5) | (1<<4)), S3C2416_GPKCON);
|
||||
//writel((readl(S3C2416_GPKDAT) | (1<<2)), S3C2416_GPKDAT);
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
input_free_device(input_dev);
|
||||
kfree(ts_iic);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static int s3c_ts_iic_remove(struct platform_device *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int s3c_ts_iic_suspend(struct platform_device *dev, pm_message_t state)
|
||||
{
|
||||
//disable_irq(IRQ_EINT2);
|
||||
|
||||
/*kit.add 2009/09/24*/
|
||||
//s3c2410_gpio_cfgpin(S3C2410_GPF5, S3C2410_GPF5_OUTP);
|
||||
//s3c2410_gpio_setpin(S3C2410_GPF5, 0);
|
||||
/*kit.end*/
|
||||
|
||||
/*kit.add 2009/11/04*/
|
||||
if(wkup_srce == 1)
|
||||
{
|
||||
printk("\nTouch Suspend: Add touch to wakeup source, do not turn off touch power...\n");
|
||||
}
|
||||
else if(wkup_srce == 0)
|
||||
{
|
||||
if(!isTouchPowerOff)
|
||||
{
|
||||
printk("\nTouch Suspend: Do not add touch to wakeup source, turn off touch power...\n");
|
||||
disable_irq(IRQ_EINT2);
|
||||
s3c2410_gpio_cfgpin(S3C2410_GPF5, S3C2410_GPF5_OUTP);
|
||||
s3c2410_gpio_setpin(S3C2410_GPF5, 0);
|
||||
}
|
||||
}
|
||||
/*kit.end*/
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int s3c_ts_iic_resume(struct platform_device *pdev)
|
||||
{
|
||||
/*need to check*/
|
||||
/*Qisda Qube for open smartcard pwr*/
|
||||
#if defined (CONFIG_QISDA_AS090B00_EVT1) || defined (CONFIG_QISDA_AS090B00_EVT1_1)
|
||||
/*s3c2410_gpio_pullup(S3C2410_GPF5, 2);
|
||||
s3c2410_gpio_cfgpin(S3C2410_GPF5, S3C2410_GPF5_OUTP);
|
||||
s3c2410_gpio_setpin(S3C2410_GPF5,1);
|
||||
msleep(200);*/
|
||||
|
||||
s3c2410_gpio_cfgpin(S3C2410_GPC3, S3C2410_GPC3_OUTP);
|
||||
s3c2410_gpio_setpin(S3C2410_GPC3, 1);
|
||||
msleep(200);
|
||||
#endif
|
||||
/*Qisda Qube for open smartcard pwr*/
|
||||
|
||||
ts_iic->client->driver->command(ts_iic->client, QISDA_IIC_INIT, NULL);
|
||||
|
||||
/*kit.add 2009/11/04*/
|
||||
if(wkup_srce == 1)
|
||||
{
|
||||
printk("\nTouch Resume: Touch is wakeup source\n");
|
||||
}
|
||||
else if(wkup_srce == 0)
|
||||
{
|
||||
printk("\nTouch Resume: Touch is not wakeup source, turn on touch power and enable IRQ...\n");
|
||||
|
||||
if(!isTouchPowerOff)
|
||||
{
|
||||
s3c2410_gpio_cfgpin(S3C2410_GPF5, S3C2410_GPF5_OUTP);
|
||||
s3c2410_gpio_setpin(S3C2410_GPF5, 1);
|
||||
msleep(200);
|
||||
ts_iic->client->driver->command(ts_iic->client, QISDA_IIC_INIT,NULL);
|
||||
enable_irq(IRQ_EINT2);
|
||||
|
||||
switch(touchPowerState)
|
||||
{
|
||||
case TOUCH_DEVICE_ACTIVE_STATE:
|
||||
ts_iic->client->driver->command(ts_iic->client, QISDA_IIC_ACTIVE_MODE, NULL);
|
||||
ts_iic->polling_time = TOUCH_ACTIVE_POLLING_TIME;
|
||||
touchPowerState = TOUCH_DEVICE_ACTIVE_STATE;
|
||||
break;
|
||||
|
||||
case TOUCH_DEVICE_SLEEP_STATE:
|
||||
ts_iic->client->driver->command(ts_iic->client, QISDA_IIC_SLEEP_MODE, NULL);
|
||||
ts_iic->polling_time = TOUCH_SLEEP_POLLING_TIME;
|
||||
touchPowerState = TOUCH_DEVICE_SLEEP_STATE;
|
||||
break;
|
||||
|
||||
case TOUCH_DEVICE_DEEPSLEEP_STATE:
|
||||
ts_iic->client->driver->command(ts_iic->client, QISDA_IIC_DEEPSLEEP_MODE, NULL);
|
||||
ts_iic->polling_time = TOUCH_DEEPSLEEP_POLLING_TIME;
|
||||
touchPowerState = TOUCH_DEVICE_DEEPSLEEP_STATE;
|
||||
break;
|
||||
|
||||
case TOUCH_DEVICE_POWEROFF_STATE:
|
||||
touchPowerState = TOUCH_DEVICE_DEEPSLEEP_STATE;
|
||||
break;
|
||||
|
||||
defaule:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
/*kit.end*/
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define s3c_ts_iic_suspend NULL
|
||||
#define s3c_ts_iic_resume NULL
|
||||
#endif
|
||||
|
||||
static struct platform_driver s3c_ts_iic_driver = {
|
||||
.probe = s3c_ts_iic_probe,
|
||||
.remove = s3c_ts_iic_remove,
|
||||
.suspend = s3c_ts_iic_suspend,
|
||||
.resume = s3c_ts_iic_resume,
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = DEVICE_NAME,
|
||||
},
|
||||
};
|
||||
|
||||
//static char banner[] __initdata = KERN_INFO "Touchscreen driver Of IIC type for Qisda\n";
|
||||
static int __init s3c_ts_iic_init(void) { return platform_driver_register(&s3c_ts_iic_driver); }
|
||||
static void __exit s3c_ts_iic_exit(void){ platform_driver_unregister(&s3c_ts_iic_driver); }
|
||||
module_init(s3c_ts_iic_init);
|
||||
module_exit(s3c_ts_iic_exit);
|
||||
MODULE_AUTHOR("Tony.YC.Huang@qisda.com");
|
||||
MODULE_DESCRIPTION("Touchscreen driver Of IIC type for Qisda");
|
||||
MODULE_LICENSE("GPL");
|
||||
//Qisda Tony 090406, add Auo touch i2c driver ]
|
||||
412
drivers/input/touchscreen/s3c_ts_iic.h
Normal file
412
drivers/input/touchscreen/s3c_ts_iic.h
Normal file
@@ -0,0 +1,412 @@
|
||||
/* drivers/media/video/s3c_camif.h
|
||||
*
|
||||
* Copyright (c) 2008 Samsung Electronics
|
||||
*
|
||||
* Samsung S3C Camera driver
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef __S3C_TS_IIC_H_
|
||||
#define __S3C_TS_IIC_H_
|
||||
|
||||
#ifdef __KERNEL__
|
||||
#include <asm/types.h>
|
||||
#include <linux/i2c.h>
|
||||
#endif /* __KERNEL__ */
|
||||
#if 0
|
||||
#if !defined(O_NONCAP)
|
||||
#define O_NONCAP O_TRUNC
|
||||
#endif
|
||||
|
||||
#if defined(CAMIF_DEBUG)
|
||||
#define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args)
|
||||
#else
|
||||
#define DPRINTK(fmt, args...)
|
||||
#endif
|
||||
|
||||
#if defined(CAMIF_DEBUG)
|
||||
#define assert(expr) \
|
||||
if(!(expr)) { \
|
||||
printk( "Assertion failed! %s,%s,%s,line=%d\n", \
|
||||
#expr,__FILE__,__FUNCTION__,__LINE__); \
|
||||
}
|
||||
#else
|
||||
#define assert(expr)
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_CPU_S3C6400) || defined(CONFIG_CPU_S3C6410)
|
||||
#define MEM_SIZE 0x08000000
|
||||
#define FIMC_VER "3.0"
|
||||
#elif defined(CONFIG_CPU_S3C2443) || defined(CONFIG_CPU_S3C2450) || defined(CONFIG_CPU_S3C2416)
|
||||
#define MEM_SIZE 0x04000000
|
||||
#define FIMC_VER "2.3"
|
||||
#else
|
||||
#define MEM_SIZE 0x04000000
|
||||
#define FIMC_VER "2.x"
|
||||
#endif
|
||||
|
||||
#undef FSM_ON_PREVIEW
|
||||
#define FSM_ON_CODEC
|
||||
|
||||
#undef USE_LAST_IRQ /* turn on if pp count is 1 */
|
||||
|
||||
#define CODEC_DEV_NAME "CAMIF_CODEC"
|
||||
#define PREVIEW_DEV_NAME "CAMIF_PREVIEW"
|
||||
|
||||
#define CAMIF_DEV_NUM 2
|
||||
#define FIMC_CODEC_INDEX 0
|
||||
#define FIMC_PREVIEW_INDEX 1
|
||||
|
||||
#define BURST_ERR 1
|
||||
#define RESERVED_MEM (15 * 1024 * 1024)
|
||||
#define YUV_MEM (10 * 1024 * 1024)
|
||||
#define RGB_MEM (RESERVED_MEM - YUV_MEM)
|
||||
|
||||
#define CODEC_DEFAULT_WIDTH 640
|
||||
#define CODEC_DEFAULT_HEIGHT 480
|
||||
#define PREVIEW_DEFAULT_WIDTH 640
|
||||
#define PREVIEW_DEFAULT_HEIGHT 480
|
||||
|
||||
#define CROP_DEFAULT_WIDTH 352
|
||||
#define CROP_DEFAULT_HEIGHT 272
|
||||
|
||||
#define CODEC_DEFAULT_PPNUM 4
|
||||
#define PREVIEW_DEFAULT_PPNUM 4
|
||||
|
||||
#define CODEC_MINOR 12
|
||||
#define PREVIEW_MINOR 13
|
||||
|
||||
#define CHECK_FREQ 5
|
||||
#define INSTANT_SKIP 0
|
||||
#define INSTANT_GO 1
|
||||
|
||||
#define VID_HARDWARE_SAMSUNG_FIMC3X 236
|
||||
|
||||
#define ZOOM_AT_A_TIME_IN_PIXELS 32
|
||||
#define ZOOM_IN_MAX 640
|
||||
|
||||
/* Codec or Preview Status */
|
||||
#define CAMIF_STARTED (1 << 1)
|
||||
#define CAMIF_STOPPED (1 << 2)
|
||||
#define CAMIF_INT_HAPPEN (1 << 3)
|
||||
|
||||
/* Codec or Preview : Interrupt FSM */
|
||||
#define CAMIF_1st_INT (1 << 7)
|
||||
#define CAMIF_Xth_INT (1 << 8)
|
||||
#define CAMIF_Yth_INT (1 << 9)
|
||||
#define CAMIF_Zth_INT (1 << 10)
|
||||
#define CAMIF_NORMAL_INT (1 << 11)
|
||||
#define CAMIF_DUMMY_INT (1 << 12)
|
||||
#define CAMIF_CONTINUOUS_INT (1 << 13)
|
||||
#define CAMIF_SET_LAST_INT (1 << 14)
|
||||
#define CAMIF_STOP_CAPTURE (1 << 15)
|
||||
#define CAMIF_LAST_IRQ (1 << 16)
|
||||
#define CAMIF_PENDING_INT 0
|
||||
|
||||
#define CAMIF_CAPTURE_SKIP_FRAMES 5
|
||||
|
||||
/* CAMIF RESET Definition */
|
||||
#define CAMIF_RESET (1 << 0)
|
||||
#define CAMIF_EX_RESET_AL (1 << 1) /* Active Low */
|
||||
#define CAMIF_EX_RESET_AH (1 << 2) /* Active High */
|
||||
|
||||
#define USER_EXIT (1 << 2)
|
||||
#define USER_ADD (1 << 1)
|
||||
#define SENSOR_INIT (1 << 0)
|
||||
|
||||
#define SENSOR_MAX 255
|
||||
#define SENSOR_QSVGA (1 << 12)
|
||||
#define SENSOR_UXGA (1 << 11)
|
||||
#define SENSOR_SVGA (1 << 10)
|
||||
#define SENSOR_SXGA (1 << 4)
|
||||
#define SENSOR_VGA (1 << 3)
|
||||
#define SENSOR_DEFAULT 0
|
||||
|
||||
#define SENSOR_WB (1 << 9)
|
||||
#define SENSOR_AF (1 << 8)
|
||||
#define SENSOR_MIRROR (1 << 7)
|
||||
#define SENSOR_ZOOMOUT (1 << 6)
|
||||
#define SENSOR_ZOOMIN (1 << 5)
|
||||
|
||||
/* Global Status Definition */
|
||||
#define PWANT2START (1 << 0)
|
||||
#define CWANT2START (1 << 1)
|
||||
#define BOTH_STARTED (PWANT2START | CWANT2START)
|
||||
#define P_NOT_WORKING (1 << 4)
|
||||
#define C_WORKING (1 << 5)
|
||||
#define P_WORKING (1 << 6)
|
||||
#define C_NOT_WORKING (1 << 7)
|
||||
|
||||
#define FORMAT_FLAGS_DITHER 0x01
|
||||
#define FORMAT_FLAGS_PACKED 0x02
|
||||
#define FORMAT_FLAGS_PLANAR 0x04
|
||||
#define FORMAT_FLAGS_RAW 0x08
|
||||
#define FORMAT_FLAGS_CrCb 0x10
|
||||
|
||||
enum camif_itu_fmt {
|
||||
CAMIF_ITU601 = (1 << 31),
|
||||
CAMIF_ITU656 = 0,
|
||||
};
|
||||
|
||||
/* It is possbie to use two device simultaneously */
|
||||
enum camif_dma_type {
|
||||
CAMIF_PREVIEW = (1 << 0),
|
||||
CAMIF_CODEC = (1 << 1),
|
||||
};
|
||||
|
||||
enum camif_order422 {
|
||||
CAMIF_YCBYCR = 0,
|
||||
CAMIF_YCRYCB = (1 << 14),
|
||||
CAMIF_CBYCRY = (1 << 15),
|
||||
CAMIF_CRYCBY = (1 << 15) | (1 << 14),
|
||||
};
|
||||
|
||||
enum flip_mode {
|
||||
CAMIF_FLIP = 0,
|
||||
CAMIF_ROTATE_90 = (1 << 13),
|
||||
CAMIF_FLIP_X = (1 << 14),
|
||||
CAMIF_FLIP_Y = (1 << 15),
|
||||
CAMIF_FLIP_MIRROR = (1 << 15) | (1 << 14),
|
||||
CAMIF_FLIP_ROTATE_270 = (1 << 15) | (1 << 14) | (1 << 13),
|
||||
};
|
||||
|
||||
enum camif_fmt {
|
||||
CAMIF_YCBCR420 = (1 << 0),
|
||||
CAMIF_YCBCR422 = (1 << 1),
|
||||
CAMIF_YCBCR422I = (1 << 2),
|
||||
CAMIF_RGB16 = (1 << 3),
|
||||
CAMIF_RGB24 = (1 << 4),
|
||||
CAMIF_RGB32 = (1 << 5),
|
||||
};
|
||||
|
||||
enum camif_capturing {
|
||||
CAMIF_BOTH_DMA_ON = (1 << 4),
|
||||
CAMIF_DMA_ON = (1 << 3),
|
||||
CAMIF_BOTH_DMA_OFF = (1 << 1),
|
||||
CAMIF_DMA_OFF = (1 << 0),
|
||||
CAMIF_DMA_OFF_L_IRQ = (1 << 5),
|
||||
};
|
||||
|
||||
enum image_effect {
|
||||
CAMIF_BYPASS,
|
||||
CAMIF_ARBITRARY_CB_CR,
|
||||
CAMIF_NEGATIVE,
|
||||
CAMIF_ART_FREEZE,
|
||||
CAMIF_EMBOSSING ,
|
||||
CAMIF_SILHOUETTE,
|
||||
};
|
||||
|
||||
enum input_channel{
|
||||
CAMERA_INPUT,
|
||||
MSDMA_FROM_CODEC,
|
||||
MSDMA_FROM_PREVIEW,
|
||||
};
|
||||
|
||||
enum output_channel{
|
||||
CAMIF_OUT_PP,
|
||||
CAMIF_OUT_FIFO,
|
||||
};
|
||||
|
||||
typedef struct camif_performance
|
||||
{
|
||||
int frames;
|
||||
int framesdropped;
|
||||
__u64 bytesin;
|
||||
__u64 bytesout;
|
||||
__u32 reserved[4];
|
||||
} camif_perf_t;
|
||||
|
||||
typedef struct {
|
||||
dma_addr_t phys_y;
|
||||
dma_addr_t phys_cb;
|
||||
dma_addr_t phys_cr;
|
||||
u8 *virt_y;
|
||||
u8 *virt_cb;
|
||||
u8 *virt_cr;
|
||||
dma_addr_t phys_rgb;
|
||||
u8 *virt_rgb;
|
||||
} img_buf_t;
|
||||
|
||||
/* this structure convers the CIWDOFFST, prescaler, mainscaler */
|
||||
typedef struct {
|
||||
u32 modified_src_x; /* After windows applyed to source_x */
|
||||
u32 modified_src_y;
|
||||
u32 hfactor;
|
||||
u32 vfactor;
|
||||
u32 shfactor; /* SHfactor = 10 - ( hfactor + vfactor ) */
|
||||
u32 prehratio;
|
||||
u32 prevratio;
|
||||
u32 predst_x;
|
||||
u32 predst_y;
|
||||
u32 scaleup_h;
|
||||
u32 scaleup_v;
|
||||
u32 mainhratio;
|
||||
u32 mainvratio;
|
||||
u32 scalerbypass; /* only codec */
|
||||
u32 zoom_in_cnt;
|
||||
} scaler_t;
|
||||
|
||||
enum v4l2_status {
|
||||
CAMIF_V4L2_INIT = (1 << 0),
|
||||
CAMIF_v4L2_DIRTY = (1 << 1),
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
struct mutex lock;
|
||||
enum camif_itu_fmt itu_fmt;
|
||||
enum camif_order422 order422;
|
||||
struct i2c_client *sensor;
|
||||
u32 win_hor_ofst;
|
||||
u32 win_ver_ofst;
|
||||
u32 win_hor_ofst2;
|
||||
u32 win_ver_ofst2;
|
||||
u32 camclk; /* External Image Sensor Camera Clock */
|
||||
u32 source_x;
|
||||
u32 source_y;
|
||||
u32 polarity_pclk;
|
||||
u32 polarity_vsync;
|
||||
u32 polarity_href;
|
||||
u32 user; /* MAX 2 (codec, preview) */
|
||||
u32 irq_old_priority; /* BUS PRIORITY register */
|
||||
u32 status;
|
||||
u32 init_sensor; /* initializing sensor */
|
||||
u32 reset_type; /* External Sensor Reset Type */
|
||||
u32 reset_udelay;
|
||||
u32 zoom_in_cnt;
|
||||
} camif_cis_t;
|
||||
|
||||
/* when App want to change v4l2 parameter,
|
||||
* we instantly store it into v4l2_t v2
|
||||
* and then reflect it to hardware
|
||||
*/
|
||||
typedef struct v4l2 {
|
||||
struct v4l2_fmtdesc *fmtdesc;
|
||||
struct v4l2_framebuffer frmbuf; /* current frame buffer */
|
||||
struct v4l2_input *input;
|
||||
struct v4l2_output *output;
|
||||
enum v4l2_status status;
|
||||
|
||||
/* crop */
|
||||
struct v4l2_rect crop_bounds;
|
||||
struct v4l2_rect crop_defrect;
|
||||
struct v4l2_rect crop_current;
|
||||
|
||||
} v4l2_t;
|
||||
|
||||
|
||||
typedef struct camif_c_t {
|
||||
struct video_device *v;
|
||||
|
||||
/* V4L2 param only for v4l2 driver */
|
||||
v4l2_t v2;
|
||||
camif_cis_t *cis; /* Common between Codec and Preview */
|
||||
|
||||
/* logical parameter */
|
||||
wait_queue_head_t waitq;
|
||||
u32 status; /* Start/Stop */
|
||||
u32 fsm; /* Start/Stop */
|
||||
u32 open_count; /* duplicated */
|
||||
int irq;
|
||||
char shortname[16];
|
||||
u32 target_x;
|
||||
u32 target_y;
|
||||
scaler_t sc;
|
||||
enum flip_mode flip;
|
||||
enum image_effect effect;
|
||||
enum camif_dma_type dma_type;
|
||||
|
||||
/* 4 pingpong Frame memory */
|
||||
u8 *pp_virt_buf;
|
||||
dma_addr_t pp_phys_buf;
|
||||
u32 pp_totalsize;
|
||||
u32 pp_num; /* used pingpong memory number */
|
||||
img_buf_t img_buf[4];
|
||||
enum camif_fmt src_fmt;
|
||||
enum camif_fmt dst_fmt;
|
||||
enum camif_capturing capture_enable;
|
||||
camif_perf_t perf;
|
||||
u32 cur_frame_num;
|
||||
u32 auto_restart; /* Only For Preview */
|
||||
int input_channel;
|
||||
int output_channel;
|
||||
int buffer_size;
|
||||
void *other; /* other camif_cfg_t */
|
||||
u32 msdma_status; /* 0 : stop, 1 : start */
|
||||
void __iomem *regs;
|
||||
} camif_cfg_t;
|
||||
|
||||
/* Test Application Usage */
|
||||
typedef struct {
|
||||
int src_x;
|
||||
int src_y;
|
||||
int dst_x;
|
||||
int dst_y;
|
||||
int src_fmt;
|
||||
int dst_fmt;
|
||||
int flip;
|
||||
int awb;
|
||||
int effect;
|
||||
int input_channel;
|
||||
int output_channel;
|
||||
unsigned int h_offset;
|
||||
unsigned int v_offset;
|
||||
unsigned int h_offset2;
|
||||
unsigned int v_offset2;
|
||||
} camif_param_t;
|
||||
|
||||
/* Externs */
|
||||
extern camif_cfg_t* s3c_camif_get_fimc_object(int);
|
||||
extern int s3c_camif_start_dma(camif_cfg_t *);
|
||||
extern int s3c_camif_stop_dma(camif_cfg_t *);
|
||||
extern int s3c_camif_get_frame_num(camif_cfg_t *);
|
||||
extern unsigned char* s3c_camif_get_frame(camif_cfg_t *);
|
||||
extern int s3c_camif_control_fimc(camif_cfg_t *);
|
||||
extern void s3c_camif_reset(int, int);
|
||||
extern void s3c_camif_init(void);
|
||||
extern int s3c_camif_get_fifo_status(camif_cfg_t *);
|
||||
extern void s3c_camif_enable_lastirq(camif_cfg_t *);
|
||||
extern void s3c_camif_disable_lastirq(camif_cfg_t *);
|
||||
extern void s3c_camif_change_flip(camif_cfg_t *);
|
||||
extern void s3c_camif_change_effect(camif_cfg_t *);
|
||||
extern int s3c_camif_start_codec_msdma(camif_cfg_t *);
|
||||
extern int s3c_camif_set_clock(unsigned int camclk);
|
||||
extern void s3c_camif_disable_clock(void);
|
||||
extern int s3c_camif_start_preview_msdma(camif_cfg_t *);
|
||||
extern camif_cis_t* get_initialized_cis(void);
|
||||
extern void s3c_camif_clear_irq(int);
|
||||
extern int s3c_camif_set_source_format(camif_cis_t *);
|
||||
extern void s3c_camif_register_sensor(struct i2c_client *);
|
||||
extern void s3c_camif_unregister_sensor(struct i2c_client*);
|
||||
extern int s3c_camif_setup_dma(camif_cfg_t *);
|
||||
extern void s3c_camif_init_sensor(camif_cfg_t *);
|
||||
extern int s3c_camif_set_offset(camif_cis_t *);
|
||||
extern void s3c_camif_set_priority(int);
|
||||
extern void s3c_camif_open_sensor(camif_cis_t *);
|
||||
extern void s3c_camif_set_polarity(camif_cfg_t *cfg);
|
||||
#endif
|
||||
#define QISDA_IIC_INIT (1<<0)
|
||||
#define QISDA_IIC_READ_POSITION (1<<1)
|
||||
#define QISDA_IIC_CALIBRATION (1<<2)
|
||||
|
||||
#define QISDA_IIC_ACTIVE_MODE (1<<3)
|
||||
#define QISDA_IIC_SLEEP_MODE (1<<4)
|
||||
#define QISDA_IIC_DEEPSLEEP_MODE (1<<5)
|
||||
|
||||
|
||||
|
||||
extern int s3c_ts_iic_port_init(struct i2c_client *c);
|
||||
#endif //__S3C_TS_IIC_H_
|
||||
|
||||
194
drivers/input/touchscreen/touchright.c
Normal file
194
drivers/input/touchscreen/touchright.c
Normal file
@@ -0,0 +1,194 @@
|
||||
/*
|
||||
* Touchright serial touchscreen driver
|
||||
*
|
||||
* Copyright (c) 2006 Rick Koch <n1gp@hotmail.com>
|
||||
*
|
||||
* Based on MicroTouch driver (drivers/input/touchscreen/mtouch.c)
|
||||
* Copyright (c) 2004 Vojtech Pavlik
|
||||
* and Dan Streetman <ddstreet@ieee.org>
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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/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>
|
||||
|
||||
#define DRIVER_DESC "Touchright serial touchscreen driver"
|
||||
|
||||
MODULE_AUTHOR("Rick Koch <n1gp@hotmail.com>");
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
/*
|
||||
* Definitions & global arrays.
|
||||
*/
|
||||
|
||||
#define TR_FORMAT_TOUCH_BIT 0x01
|
||||
#define TR_FORMAT_STATUS_BYTE 0x40
|
||||
#define TR_FORMAT_STATUS_MASK ~TR_FORMAT_TOUCH_BIT
|
||||
|
||||
#define TR_LENGTH 5
|
||||
|
||||
#define TR_MIN_XC 0
|
||||
#define TR_MAX_XC 0x1ff
|
||||
#define TR_MIN_YC 0
|
||||
#define TR_MAX_YC 0x1ff
|
||||
|
||||
/*
|
||||
* Per-touchscreen data.
|
||||
*/
|
||||
|
||||
struct tr {
|
||||
struct input_dev *dev;
|
||||
struct serio *serio;
|
||||
int idx;
|
||||
unsigned char data[TR_LENGTH];
|
||||
char phys[32];
|
||||
};
|
||||
|
||||
static irqreturn_t tr_interrupt(struct serio *serio,
|
||||
unsigned char data, unsigned int flags)
|
||||
{
|
||||
struct tr *tr = serio_get_drvdata(serio);
|
||||
struct input_dev *dev = tr->dev;
|
||||
|
||||
tr->data[tr->idx] = data;
|
||||
|
||||
if ((tr->data[0] & TR_FORMAT_STATUS_MASK) == TR_FORMAT_STATUS_BYTE) {
|
||||
if (++tr->idx == TR_LENGTH) {
|
||||
input_report_abs(dev, ABS_X,
|
||||
(tr->data[1] << 5) | (tr->data[2] >> 1));
|
||||
input_report_abs(dev, ABS_Y,
|
||||
(tr->data[3] << 5) | (tr->data[4] >> 1));
|
||||
input_report_key(dev, BTN_TOUCH,
|
||||
tr->data[0] & TR_FORMAT_TOUCH_BIT);
|
||||
input_sync(dev);
|
||||
tr->idx = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/*
|
||||
* tr_disconnect() is the opposite of tr_connect()
|
||||
*/
|
||||
|
||||
static void tr_disconnect(struct serio *serio)
|
||||
{
|
||||
struct tr *tr = serio_get_drvdata(serio);
|
||||
|
||||
input_get_device(tr->dev);
|
||||
input_unregister_device(tr->dev);
|
||||
serio_close(serio);
|
||||
serio_set_drvdata(serio, NULL);
|
||||
input_put_device(tr->dev);
|
||||
kfree(tr);
|
||||
}
|
||||
|
||||
/*
|
||||
* tr_connect() is the routine that is called when someone adds a
|
||||
* new serio device that supports the Touchright protocol and registers it as
|
||||
* an input device.
|
||||
*/
|
||||
|
||||
static int tr_connect(struct serio *serio, struct serio_driver *drv)
|
||||
{
|
||||
struct tr *tr;
|
||||
struct input_dev *input_dev;
|
||||
int err;
|
||||
|
||||
tr = kzalloc(sizeof(struct tr), GFP_KERNEL);
|
||||
input_dev = input_allocate_device();
|
||||
if (!tr || !input_dev) {
|
||||
err = -ENOMEM;
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
tr->serio = serio;
|
||||
tr->dev = input_dev;
|
||||
snprintf(tr->phys, sizeof(tr->phys), "%s/input0", serio->phys);
|
||||
|
||||
input_dev->private = tr;
|
||||
input_dev->name = "Touchright Serial TouchScreen";
|
||||
input_dev->phys = tr->phys;
|
||||
input_dev->id.bustype = BUS_RS232;
|
||||
input_dev->id.vendor = SERIO_TOUCHRIGHT;
|
||||
input_dev->id.product = 0;
|
||||
input_dev->id.version = 0x0100;
|
||||
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
|
||||
input_dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
|
||||
input_set_abs_params(tr->dev, ABS_X, TR_MIN_XC, TR_MAX_XC, 0, 0);
|
||||
input_set_abs_params(tr->dev, ABS_Y, TR_MIN_YC, TR_MAX_YC, 0, 0);
|
||||
|
||||
serio_set_drvdata(serio, tr);
|
||||
|
||||
err = serio_open(serio, drv);
|
||||
if (err)
|
||||
goto fail2;
|
||||
|
||||
err = input_register_device(tr->dev);
|
||||
if (err)
|
||||
goto fail3;
|
||||
|
||||
return 0;
|
||||
|
||||
fail3: serio_close(serio);
|
||||
fail2: serio_set_drvdata(serio, NULL);
|
||||
fail1: input_free_device(input_dev);
|
||||
kfree(tr);
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* The serio driver structure.
|
||||
*/
|
||||
|
||||
static struct serio_device_id tr_serio_ids[] = {
|
||||
{
|
||||
.type = SERIO_RS232,
|
||||
.proto = SERIO_TOUCHRIGHT,
|
||||
.id = SERIO_ANY,
|
||||
.extra = SERIO_ANY,
|
||||
},
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(serio, tr_serio_ids);
|
||||
|
||||
static struct serio_driver tr_drv = {
|
||||
.driver = {
|
||||
.name = "touchright",
|
||||
},
|
||||
.description = DRIVER_DESC,
|
||||
.id_table = tr_serio_ids,
|
||||
.interrupt = tr_interrupt,
|
||||
.connect = tr_connect,
|
||||
.disconnect = tr_disconnect,
|
||||
};
|
||||
|
||||
/*
|
||||
* The functions for inserting/removing us as a module.
|
||||
*/
|
||||
|
||||
static int __init tr_init(void)
|
||||
{
|
||||
return serio_register_driver(&tr_drv);
|
||||
}
|
||||
|
||||
static void __exit tr_exit(void)
|
||||
{
|
||||
serio_unregister_driver(&tr_drv);
|
||||
}
|
||||
|
||||
module_init(tr_init);
|
||||
module_exit(tr_exit);
|
||||
201
drivers/input/touchscreen/touchwin.c
Normal file
201
drivers/input/touchscreen/touchwin.c
Normal file
@@ -0,0 +1,201 @@
|
||||
/*
|
||||
* Touchwindow serial touchscreen driver
|
||||
*
|
||||
* Copyright (c) 2006 Rick Koch <n1gp@hotmail.com>
|
||||
*
|
||||
* Based on MicroTouch driver (drivers/input/touchscreen/mtouch.c)
|
||||
* Copyright (c) 2004 Vojtech Pavlik
|
||||
* and Dan Streetman <ddstreet@ieee.org>
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* 2005/02/19 Rick Koch:
|
||||
* The Touchwindow I used is made by Edmark Corp. and
|
||||
* constantly outputs a stream of 0's unless it is touched.
|
||||
* It then outputs 3 bytes: X, Y, and a copy of Y.
|
||||
*/
|
||||
|
||||
#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>
|
||||
|
||||
#define DRIVER_DESC "Touchwindow serial touchscreen driver"
|
||||
|
||||
MODULE_AUTHOR("Rick Koch <n1gp@hotmail.com>");
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
/*
|
||||
* Definitions & global arrays.
|
||||
*/
|
||||
|
||||
#define TW_LENGTH 3
|
||||
|
||||
#define TW_MIN_XC 0
|
||||
#define TW_MAX_XC 0xff
|
||||
#define TW_MIN_YC 0
|
||||
#define TW_MAX_YC 0xff
|
||||
|
||||
/*
|
||||
* Per-touchscreen data.
|
||||
*/
|
||||
|
||||
struct tw {
|
||||
struct input_dev *dev;
|
||||
struct serio *serio;
|
||||
int idx;
|
||||
int touched;
|
||||
unsigned char data[TW_LENGTH];
|
||||
char phys[32];
|
||||
};
|
||||
|
||||
static irqreturn_t tw_interrupt(struct serio *serio,
|
||||
unsigned char data, unsigned int flags)
|
||||
{
|
||||
struct tw *tw = serio_get_drvdata(serio);
|
||||
struct input_dev *dev = tw->dev;
|
||||
|
||||
if (data) { /* touch */
|
||||
tw->touched = 1;
|
||||
tw->data[tw->idx++] = data;
|
||||
/* verify length and that the two Y's are the same */
|
||||
if (tw->idx == TW_LENGTH && tw->data[1] == tw->data[2]) {
|
||||
input_report_abs(dev, ABS_X, tw->data[0]);
|
||||
input_report_abs(dev, ABS_Y, tw->data[1]);
|
||||
input_report_key(dev, BTN_TOUCH, 1);
|
||||
input_sync(dev);
|
||||
tw->idx = 0;
|
||||
}
|
||||
} else if (tw->touched) { /* untouch */
|
||||
input_report_key(dev, BTN_TOUCH, 0);
|
||||
input_sync(dev);
|
||||
tw->idx = 0;
|
||||
tw->touched = 0;
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/*
|
||||
* tw_disconnect() is the opposite of tw_connect()
|
||||
*/
|
||||
|
||||
static void tw_disconnect(struct serio *serio)
|
||||
{
|
||||
struct tw *tw = serio_get_drvdata(serio);
|
||||
|
||||
input_get_device(tw->dev);
|
||||
input_unregister_device(tw->dev);
|
||||
serio_close(serio);
|
||||
serio_set_drvdata(serio, NULL);
|
||||
input_put_device(tw->dev);
|
||||
kfree(tw);
|
||||
}
|
||||
|
||||
/*
|
||||
* tw_connect() is the routine that is called when someone adds a
|
||||
* new serio device that supports the Touchwin protocol and registers it as
|
||||
* an input device.
|
||||
*/
|
||||
|
||||
static int tw_connect(struct serio *serio, struct serio_driver *drv)
|
||||
{
|
||||
struct tw *tw;
|
||||
struct input_dev *input_dev;
|
||||
int err;
|
||||
|
||||
tw = kzalloc(sizeof(struct tw), GFP_KERNEL);
|
||||
input_dev = input_allocate_device();
|
||||
if (!tw || !input_dev) {
|
||||
err = -ENOMEM;
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
tw->serio = serio;
|
||||
tw->dev = input_dev;
|
||||
snprintf(tw->phys, sizeof(tw->phys), "%s/input0", serio->phys);
|
||||
|
||||
input_dev->private = tw;
|
||||
input_dev->name = "Touchwindow Serial TouchScreen";
|
||||
input_dev->phys = tw->phys;
|
||||
input_dev->id.bustype = BUS_RS232;
|
||||
input_dev->id.vendor = SERIO_TOUCHWIN;
|
||||
input_dev->id.product = 0;
|
||||
input_dev->id.version = 0x0100;
|
||||
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
|
||||
input_dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
|
||||
input_set_abs_params(tw->dev, ABS_X, TW_MIN_XC, TW_MAX_XC, 0, 0);
|
||||
input_set_abs_params(tw->dev, ABS_Y, TW_MIN_YC, TW_MAX_YC, 0, 0);
|
||||
|
||||
serio_set_drvdata(serio, tw);
|
||||
|
||||
err = serio_open(serio, drv);
|
||||
if (err)
|
||||
goto fail2;
|
||||
|
||||
err = input_register_device(tw->dev);
|
||||
if (err)
|
||||
goto fail3;
|
||||
|
||||
return 0;
|
||||
|
||||
fail3: serio_close(serio);
|
||||
fail2: serio_set_drvdata(serio, NULL);
|
||||
fail1: input_free_device(input_dev);
|
||||
kfree(tw);
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* The serio driver structure.
|
||||
*/
|
||||
|
||||
static struct serio_device_id tw_serio_ids[] = {
|
||||
{
|
||||
.type = SERIO_RS232,
|
||||
.proto = SERIO_TOUCHWIN,
|
||||
.id = SERIO_ANY,
|
||||
.extra = SERIO_ANY,
|
||||
},
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(serio, tw_serio_ids);
|
||||
|
||||
static struct serio_driver tw_drv = {
|
||||
.driver = {
|
||||
.name = "touchwin",
|
||||
},
|
||||
.description = DRIVER_DESC,
|
||||
.id_table = tw_serio_ids,
|
||||
.interrupt = tw_interrupt,
|
||||
.connect = tw_connect,
|
||||
.disconnect = tw_disconnect,
|
||||
};
|
||||
|
||||
/*
|
||||
* The functions for inserting/removing us as a module.
|
||||
*/
|
||||
|
||||
static int __init tw_init(void)
|
||||
{
|
||||
return serio_register_driver(&tw_drv);
|
||||
}
|
||||
|
||||
static void __exit tw_exit(void)
|
||||
{
|
||||
serio_unregister_driver(&tw_drv);
|
||||
}
|
||||
|
||||
module_init(tw_init);
|
||||
module_exit(tw_exit);
|
||||
580
drivers/input/touchscreen/ucb1400_ts.c
Normal file
580
drivers/input/touchscreen/ucb1400_ts.c
Normal file
@@ -0,0 +1,580 @@
|
||||
/*
|
||||
* Philips UCB1400 touchscreen driver
|
||||
*
|
||||
* Author: Nicolas Pitre
|
||||
* Created: September 25, 2006
|
||||
* Copyright: MontaVista Software, Inc.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This code is heavily based on ucb1x00-*.c copyrighted by Russell King
|
||||
* covering the UCB1100, UCB1200 and UCB1300.. Support for the UCB1400 has
|
||||
* been made separate from ucb1x00-core/ucb1x00-ts on Russell's request.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/suspend.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/freezer.h>
|
||||
|
||||
#include <sound/driver.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/ac97_codec.h>
|
||||
|
||||
|
||||
/*
|
||||
* Interesting UCB1400 AC-link registers
|
||||
*/
|
||||
|
||||
#define UCB_IE_RIS 0x5e
|
||||
#define UCB_IE_FAL 0x60
|
||||
#define UCB_IE_STATUS 0x62
|
||||
#define UCB_IE_CLEAR 0x62
|
||||
#define UCB_IE_ADC (1 << 11)
|
||||
#define UCB_IE_TSPX (1 << 12)
|
||||
|
||||
#define UCB_TS_CR 0x64
|
||||
#define UCB_TS_CR_TSMX_POW (1 << 0)
|
||||
#define UCB_TS_CR_TSPX_POW (1 << 1)
|
||||
#define UCB_TS_CR_TSMY_POW (1 << 2)
|
||||
#define UCB_TS_CR_TSPY_POW (1 << 3)
|
||||
#define UCB_TS_CR_TSMX_GND (1 << 4)
|
||||
#define UCB_TS_CR_TSPX_GND (1 << 5)
|
||||
#define UCB_TS_CR_TSMY_GND (1 << 6)
|
||||
#define UCB_TS_CR_TSPY_GND (1 << 7)
|
||||
#define UCB_TS_CR_MODE_INT (0 << 8)
|
||||
#define UCB_TS_CR_MODE_PRES (1 << 8)
|
||||
#define UCB_TS_CR_MODE_POS (2 << 8)
|
||||
#define UCB_TS_CR_BIAS_ENA (1 << 11)
|
||||
#define UCB_TS_CR_TSPX_LOW (1 << 12)
|
||||
#define UCB_TS_CR_TSMX_LOW (1 << 13)
|
||||
|
||||
#define UCB_ADC_CR 0x66
|
||||
#define UCB_ADC_SYNC_ENA (1 << 0)
|
||||
#define UCB_ADC_VREFBYP_CON (1 << 1)
|
||||
#define UCB_ADC_INP_TSPX (0 << 2)
|
||||
#define UCB_ADC_INP_TSMX (1 << 2)
|
||||
#define UCB_ADC_INP_TSPY (2 << 2)
|
||||
#define UCB_ADC_INP_TSMY (3 << 2)
|
||||
#define UCB_ADC_INP_AD0 (4 << 2)
|
||||
#define UCB_ADC_INP_AD1 (5 << 2)
|
||||
#define UCB_ADC_INP_AD2 (6 << 2)
|
||||
#define UCB_ADC_INP_AD3 (7 << 2)
|
||||
#define UCB_ADC_EXT_REF (1 << 5)
|
||||
#define UCB_ADC_START (1 << 7)
|
||||
#define UCB_ADC_ENA (1 << 15)
|
||||
|
||||
#define UCB_ADC_DATA 0x68
|
||||
#define UCB_ADC_DAT_VALID (1 << 15)
|
||||
#define UCB_ADC_DAT_VALUE(x) ((x) & 0x3ff)
|
||||
|
||||
#define UCB_ID 0x7e
|
||||
#define UCB_ID_1400 0x4304
|
||||
|
||||
|
||||
struct ucb1400 {
|
||||
struct snd_ac97 *ac97;
|
||||
struct input_dev *ts_idev;
|
||||
|
||||
int irq;
|
||||
|
||||
wait_queue_head_t ts_wait;
|
||||
struct task_struct *ts_task;
|
||||
|
||||
unsigned int irq_pending; /* not bit field shared */
|
||||
unsigned int ts_restart:1;
|
||||
unsigned int adcsync:1;
|
||||
};
|
||||
|
||||
static int adcsync;
|
||||
|
||||
static inline u16 ucb1400_reg_read(struct ucb1400 *ucb, u16 reg)
|
||||
{
|
||||
return ucb->ac97->bus->ops->read(ucb->ac97, reg);
|
||||
}
|
||||
|
||||
static inline void ucb1400_reg_write(struct ucb1400 *ucb, u16 reg, u16 val)
|
||||
{
|
||||
ucb->ac97->bus->ops->write(ucb->ac97, reg, val);
|
||||
}
|
||||
|
||||
static inline void ucb1400_adc_enable(struct ucb1400 *ucb)
|
||||
{
|
||||
ucb1400_reg_write(ucb, UCB_ADC_CR, UCB_ADC_ENA);
|
||||
}
|
||||
|
||||
static unsigned int ucb1400_adc_read(struct ucb1400 *ucb, u16 adc_channel)
|
||||
{
|
||||
unsigned int val;
|
||||
|
||||
if (ucb->adcsync)
|
||||
adc_channel |= UCB_ADC_SYNC_ENA;
|
||||
|
||||
ucb1400_reg_write(ucb, UCB_ADC_CR, UCB_ADC_ENA | adc_channel);
|
||||
ucb1400_reg_write(ucb, UCB_ADC_CR, UCB_ADC_ENA | adc_channel | UCB_ADC_START);
|
||||
|
||||
for (;;) {
|
||||
val = ucb1400_reg_read(ucb, UCB_ADC_DATA);
|
||||
if (val & UCB_ADC_DAT_VALID)
|
||||
break;
|
||||
/* yield to other processes */
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
schedule_timeout(1);
|
||||
}
|
||||
|
||||
return UCB_ADC_DAT_VALUE(val);
|
||||
}
|
||||
|
||||
static inline void ucb1400_adc_disable(struct ucb1400 *ucb)
|
||||
{
|
||||
ucb1400_reg_write(ucb, UCB_ADC_CR, 0);
|
||||
}
|
||||
|
||||
/* Switch to interrupt mode. */
|
||||
static inline void ucb1400_ts_mode_int(struct ucb1400 *ucb)
|
||||
{
|
||||
ucb1400_reg_write(ucb, 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);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 *ucb)
|
||||
{
|
||||
ucb1400_reg_write(ucb, 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);
|
||||
return ucb1400_adc_read(ucb, UCB_ADC_INP_TSPY);
|
||||
}
|
||||
|
||||
/*
|
||||
* Switch to X position mode and measure Y plate. We switch the plate
|
||||
* configuration in pressure mode, then switch to position mode. This
|
||||
* 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 *ucb)
|
||||
{
|
||||
ucb1400_reg_write(ucb, UCB_TS_CR,
|
||||
UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW |
|
||||
UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
|
||||
ucb1400_reg_write(ucb, UCB_TS_CR,
|
||||
UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW |
|
||||
UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
|
||||
ucb1400_reg_write(ucb, UCB_TS_CR,
|
||||
UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW |
|
||||
UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA);
|
||||
|
||||
udelay(55);
|
||||
|
||||
return ucb1400_adc_read(ucb, UCB_ADC_INP_TSPY);
|
||||
}
|
||||
|
||||
/*
|
||||
* Switch to Y position mode and measure X plate. We switch the plate
|
||||
* configuration in pressure mode, then switch to position mode. This
|
||||
* 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 *ucb)
|
||||
{
|
||||
ucb1400_reg_write(ucb, UCB_TS_CR,
|
||||
UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW |
|
||||
UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
|
||||
ucb1400_reg_write(ucb, UCB_TS_CR,
|
||||
UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW |
|
||||
UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
|
||||
ucb1400_reg_write(ucb, UCB_TS_CR,
|
||||
UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW |
|
||||
UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA);
|
||||
|
||||
udelay(55);
|
||||
|
||||
return ucb1400_adc_read(ucb, UCB_ADC_INP_TSPX);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 *ucb)
|
||||
{
|
||||
ucb1400_reg_write(ucb, UCB_TS_CR,
|
||||
UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW |
|
||||
UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
|
||||
return ucb1400_adc_read(ucb, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 *ucb)
|
||||
{
|
||||
ucb1400_reg_write(ucb, UCB_TS_CR,
|
||||
UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW |
|
||||
UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
|
||||
return ucb1400_adc_read(ucb, 0);
|
||||
}
|
||||
|
||||
static inline int ucb1400_ts_pen_down(struct ucb1400 *ucb)
|
||||
{
|
||||
unsigned short val = ucb1400_reg_read(ucb, UCB_TS_CR);
|
||||
return (val & (UCB_TS_CR_TSPX_LOW | UCB_TS_CR_TSMX_LOW));
|
||||
}
|
||||
|
||||
static inline void ucb1400_ts_irq_enable(struct ucb1400 *ucb)
|
||||
{
|
||||
ucb1400_reg_write(ucb, UCB_IE_CLEAR, UCB_IE_TSPX);
|
||||
ucb1400_reg_write(ucb, UCB_IE_CLEAR, 0);
|
||||
ucb1400_reg_write(ucb, UCB_IE_FAL, UCB_IE_TSPX);
|
||||
}
|
||||
|
||||
static inline void ucb1400_ts_irq_disable(struct ucb1400 *ucb)
|
||||
{
|
||||
ucb1400_reg_write(ucb, UCB_IE_FAL, 0);
|
||||
}
|
||||
|
||||
static void ucb1400_ts_evt_add(struct input_dev *idev, u16 pressure, u16 x, u16 y)
|
||||
{
|
||||
input_report_abs(idev, ABS_X, x);
|
||||
input_report_abs(idev, ABS_Y, y);
|
||||
input_report_abs(idev, ABS_PRESSURE, pressure);
|
||||
input_sync(idev);
|
||||
}
|
||||
|
||||
static void ucb1400_ts_event_release(struct input_dev *idev)
|
||||
{
|
||||
input_report_abs(idev, ABS_PRESSURE, 0);
|
||||
input_sync(idev);
|
||||
}
|
||||
|
||||
static void ucb1400_handle_pending_irq(struct ucb1400 *ucb)
|
||||
{
|
||||
unsigned int isr;
|
||||
|
||||
isr = ucb1400_reg_read(ucb, UCB_IE_STATUS);
|
||||
ucb1400_reg_write(ucb, UCB_IE_CLEAR, isr);
|
||||
ucb1400_reg_write(ucb, UCB_IE_CLEAR, 0);
|
||||
|
||||
if (isr & UCB_IE_TSPX)
|
||||
ucb1400_ts_irq_disable(ucb);
|
||||
else
|
||||
printk(KERN_ERR "ucb1400: unexpected IE_STATUS = %#x\n", isr);
|
||||
|
||||
enable_irq(ucb->irq);
|
||||
}
|
||||
|
||||
static int ucb1400_ts_thread(void *_ucb)
|
||||
{
|
||||
struct ucb1400 *ucb = _ucb;
|
||||
struct task_struct *tsk = current;
|
||||
int valid = 0;
|
||||
|
||||
tsk->policy = SCHED_FIFO;
|
||||
tsk->rt_priority = 1;
|
||||
|
||||
while (!kthread_should_stop()) {
|
||||
unsigned int x, y, p;
|
||||
long timeout;
|
||||
|
||||
ucb->ts_restart = 0;
|
||||
|
||||
if (ucb->irq_pending) {
|
||||
ucb->irq_pending = 0;
|
||||
ucb1400_handle_pending_irq(ucb);
|
||||
}
|
||||
|
||||
ucb1400_adc_enable(ucb);
|
||||
x = ucb1400_ts_read_xpos(ucb);
|
||||
y = ucb1400_ts_read_ypos(ucb);
|
||||
p = ucb1400_ts_read_pressure(ucb);
|
||||
ucb1400_adc_disable(ucb);
|
||||
|
||||
/* Switch back to interrupt mode. */
|
||||
ucb1400_ts_mode_int(ucb);
|
||||
|
||||
msleep(10);
|
||||
|
||||
if (ucb1400_ts_pen_down(ucb)) {
|
||||
ucb1400_ts_irq_enable(ucb);
|
||||
|
||||
/*
|
||||
* 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_interruptible_timeout(ucb->ts_wait,
|
||||
ucb->irq_pending || ucb->ts_restart || kthread_should_stop(),
|
||||
timeout);
|
||||
try_to_freeze();
|
||||
}
|
||||
|
||||
/* Send the "pen off" if we are stopping with the pen still active */
|
||||
if (valid)
|
||||
ucb1400_ts_event_release(ucb->ts_idev);
|
||||
|
||||
ucb->ts_task = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
struct ucb1400 *ucb = devid;
|
||||
|
||||
if (irqnr == ucb->irq) {
|
||||
disable_irq(ucb->irq);
|
||||
ucb->irq_pending = 1;
|
||||
wake_up(&ucb->ts_wait);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
static int ucb1400_ts_open(struct input_dev *idev)
|
||||
{
|
||||
struct ucb1400 *ucb = idev->private;
|
||||
int ret = 0;
|
||||
|
||||
BUG_ON(ucb->ts_task);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static void ucb1400_ts_close(struct input_dev *idev)
|
||||
{
|
||||
struct ucb1400 *ucb = idev->private;
|
||||
|
||||
if (ucb->ts_task)
|
||||
kthread_stop(ucb->ts_task);
|
||||
|
||||
ucb1400_ts_irq_disable(ucb);
|
||||
ucb1400_reg_write(ucb, UCB_TS_CR, 0);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int ucb1400_ts_resume(struct device *dev)
|
||||
{
|
||||
struct ucb1400 *ucb = dev_get_drvdata(dev);
|
||||
|
||||
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);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define ucb1400_ts_resume NULL
|
||||
#endif
|
||||
|
||||
#ifndef NO_IRQ
|
||||
#define NO_IRQ 0
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Try to probe our interrupt, rather than relying on lots of
|
||||
* hard-coded machine dependencies.
|
||||
*/
|
||||
static int ucb1400_detect_irq(struct ucb1400 *ucb)
|
||||
{
|
||||
unsigned long mask, timeout;
|
||||
|
||||
mask = probe_irq_on();
|
||||
if (!mask) {
|
||||
probe_irq_off(mask);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
/* Enable the ADC interrupt. */
|
||||
ucb1400_reg_write(ucb, UCB_IE_RIS, UCB_IE_ADC);
|
||||
ucb1400_reg_write(ucb, UCB_IE_FAL, UCB_IE_ADC);
|
||||
ucb1400_reg_write(ucb, UCB_IE_CLEAR, 0xffff);
|
||||
ucb1400_reg_write(ucb, UCB_IE_CLEAR, 0);
|
||||
|
||||
/* Cause an ADC interrupt. */
|
||||
ucb1400_reg_write(ucb, UCB_ADC_CR, UCB_ADC_ENA);
|
||||
ucb1400_reg_write(ucb, UCB_ADC_CR, UCB_ADC_ENA | UCB_ADC_START);
|
||||
|
||||
/* Wait for the conversion to complete. */
|
||||
timeout = jiffies + HZ/2;
|
||||
while (!(ucb1400_reg_read(ucb, UCB_ADC_DATA) & UCB_ADC_DAT_VALID)) {
|
||||
cpu_relax();
|
||||
if (time_after(jiffies, timeout)) {
|
||||
printk(KERN_ERR "ucb1400: timed out in IRQ probe\n");
|
||||
probe_irq_off(mask);
|
||||
return -ENODEV;
|
||||
}
|
||||
}
|
||||
ucb1400_reg_write(ucb, UCB_ADC_CR, 0);
|
||||
|
||||
/* Disable and clear interrupt. */
|
||||
ucb1400_reg_write(ucb, UCB_IE_RIS, 0);
|
||||
ucb1400_reg_write(ucb, UCB_IE_FAL, 0);
|
||||
ucb1400_reg_write(ucb, UCB_IE_CLEAR, 0xffff);
|
||||
ucb1400_reg_write(ucb, UCB_IE_CLEAR, 0);
|
||||
|
||||
/* Read triggered interrupt. */
|
||||
ucb->irq = probe_irq_off(mask);
|
||||
if (ucb->irq < 0 || ucb->irq == NO_IRQ)
|
||||
return -ENODEV;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ucb1400_ts_probe(struct device *dev)
|
||||
{
|
||||
struct ucb1400 *ucb;
|
||||
struct input_dev *idev;
|
||||
int error, id, x_res, y_res;
|
||||
|
||||
ucb = kzalloc(sizeof(struct ucb1400), GFP_KERNEL);
|
||||
idev = input_allocate_device();
|
||||
if (!ucb || !idev) {
|
||||
error = -ENOMEM;
|
||||
goto err_free_devs;
|
||||
}
|
||||
|
||||
ucb->ts_idev = idev;
|
||||
ucb->adcsync = adcsync;
|
||||
ucb->ac97 = to_ac97_t(dev);
|
||||
init_waitqueue_head(&ucb->ts_wait);
|
||||
|
||||
id = ucb1400_reg_read(ucb, UCB_ID);
|
||||
if (id != UCB_ID_1400) {
|
||||
error = -ENODEV;
|
||||
goto err_free_devs;
|
||||
}
|
||||
|
||||
error = ucb1400_detect_irq(ucb);
|
||||
if (error) {
|
||||
printk(KERN_ERR "UCB1400: IRQ probe failed\n");
|
||||
goto err_free_devs;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
idev->private = ucb;
|
||||
idev->cdev.dev = dev;
|
||||
idev->name = "UCB1400 touchscreen interface";
|
||||
idev->id.vendor = ucb1400_reg_read(ucb, AC97_VENDOR_ID1);
|
||||
idev->id.product = id;
|
||||
idev->open = ucb1400_ts_open;
|
||||
idev->close = ucb1400_ts_close;
|
||||
idev->evbit[0] = BIT(EV_ABS);
|
||||
|
||||
ucb1400_adc_enable(ucb);
|
||||
x_res = ucb1400_ts_read_xres(ucb);
|
||||
y_res = ucb1400_ts_read_yres(ucb);
|
||||
ucb1400_adc_disable(ucb);
|
||||
printk(KERN_DEBUG "UCB1400: x/y = %d/%d\n", x_res, y_res);
|
||||
|
||||
input_set_abs_params(idev, ABS_X, 0, x_res, 0, 0);
|
||||
input_set_abs_params(idev, ABS_Y, 0, y_res, 0, 0);
|
||||
input_set_abs_params(idev, ABS_PRESSURE, 0, 0, 0, 0);
|
||||
|
||||
error = input_register_device(idev);
|
||||
if (error)
|
||||
goto err_free_irq;
|
||||
|
||||
dev_set_drvdata(dev, ucb);
|
||||
return 0;
|
||||
|
||||
err_free_irq:
|
||||
free_irq(ucb->irq, ucb);
|
||||
err_free_devs:
|
||||
input_free_device(idev);
|
||||
kfree(ucb);
|
||||
return error;
|
||||
}
|
||||
|
||||
static int ucb1400_ts_remove(struct device *dev)
|
||||
{
|
||||
struct ucb1400 *ucb = dev_get_drvdata(dev);
|
||||
|
||||
free_irq(ucb->irq, ucb);
|
||||
input_unregister_device(ucb->ts_idev);
|
||||
dev_set_drvdata(dev, NULL);
|
||||
kfree(ucb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct device_driver ucb1400_ts_driver = {
|
||||
.name = "ucb1400_ts",
|
||||
.owner = THIS_MODULE,
|
||||
.bus = &ac97_bus_type,
|
||||
.probe = ucb1400_ts_probe,
|
||||
.remove = ucb1400_ts_remove,
|
||||
.resume = ucb1400_ts_resume,
|
||||
};
|
||||
|
||||
static int __init ucb1400_ts_init(void)
|
||||
{
|
||||
return driver_register(&ucb1400_ts_driver);
|
||||
}
|
||||
|
||||
static void __exit ucb1400_ts_exit(void)
|
||||
{
|
||||
driver_unregister(&ucb1400_ts_driver);
|
||||
}
|
||||
|
||||
module_param(adcsync, int, 0444);
|
||||
|
||||
module_init(ucb1400_ts_init);
|
||||
module_exit(ucb1400_ts_exit);
|
||||
|
||||
MODULE_DESCRIPTION("Philips UCB1400 touchscreen driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
Reference in New Issue
Block a user