Creation of Cybook 2416 (actually Gen4) repository

This commit is contained in:
mlt
2009-12-18 17:10:00 +00:00
committed by godzil
commit 76f20f4d40
13791 changed files with 6812321 additions and 0 deletions

View 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

View 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 ]

File diff suppressed because it is too large Load Diff

View 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");

View 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);

View 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);

View 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);

View 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");

View 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);

View 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);

View 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);

View 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");

View 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 ]

View 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_

View 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);

View 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);

View 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");