#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
// YM_SEN输出驱动器使能,XP_SEN,YP_SEN输出驱动器禁止
// S3C2410_ADCTSC_XY_PST(3) -- 手动测量X、Y方向,等待中断模式
// x=0, 将ADCTSC[8]设置为0,即检测笔尖落下中断信号
#define WAIT4INT(x) (((x)<<8) | \
S3C2410_ADCTSC_YM_SEN | S3C2410_ADCTSC_YP_SEN | S3C2410_ADCTSC_XP_SEN | \
S3C2410_ADCTSC_XY_PST(3))
/* ADCTSC Register Bits */
#define S3C2410_ADCTSC_YM_SEN (1<<7)
#define S3C2410_ADCTSC_YP_SEN (1<<6)
#define S3C2410_ADCTSC_XM_SEN (1<<5)
#define S3C2410_ADCTSC_XP_SEN (1<<4)
#define S3C2410_ADCTSC_PULL_UP_DISABLE (1<<3)
#define S3C2410_ADCTSC_AUTO_PST (1<<2)
#define S3C2410_ADCTSC_XY_PST(x) (((x)&0x3)<<0)
/* ADCCON Register Bits */
#define S3C2410_ADCCON_ECFLG (1<<15)
#define S3C2410_ADCCON_PRSCEN (1<<14)
#define S3C2410_ADCCON_PRSCVL(x) (((x)&0xFF)<<6)
#define S3C2410_ADCCON_PRSCVLMASK (0xFF<<6)
#define S3C2410_ADCCON_SELMUX(x) (((x)&0x7)<<3)
#define S3C2410_ADCCON_MUXMASK (0x7<<3)
#define S3C2410_ADCCON_STDBM (1<<2)
#define S3C2410_ADCCON_READ_START (1<<1)
#define S3C2410_ADCCON_ENABLE_START (1<<0)
#define S3C2410_ADCCON_STARTMASK (0x3<<0)
#ifdef S3C2410_ADCCON
#undef S3C2410_ADCCON
#endif
#ifdef S3C2410_ADCTSC
#undef S3C2410_ADCTSC
#endif
#ifdef S3C2410_ADCDLY
#undef S3C2410_ADCDLY
#endif
#ifdef S3C2410_ADCDAT0
#undef S3C2410_ADCDAT0
#endif
#ifdef S3C2410_ADCDAT1
#undef S3C2410_ADCDAT1
#endif
#ifdef S3C2410_PA_ADC
#undef S3C2410_PA_ADC
#endif
#define S3C2410_PA_ADC (0x58000000)
staticvoid__iomem *base_addr;
#define S3C2410_ADCCON (base_addr+(0x00))
#define S3C2410_ADCTSC (base_addr+(0x04))
#define S3C2410_ADCDLY (base_addr+(0x08))
#define S3C2410_ADCDAT0 (base_addr+(0x0c))
#define S3C2410_ADCDAT1 (base_addr+(0x10))
#ifdef CONFIG_PM
#include
#endif
// 支持拖拽
// 若不支持拖拽,则可以获取到笔触坐标,但是LCD上的图标没有响应。
// 可以看到read接口被调用到,不知为何图标没有响应。
#define HOOK_FOR_DRAG
//#define DEBUG
#ifdef DEBUG
#define DPRINTK printk
#else
#define DPRINTK
#endif
typedefstruct{
unsignedshortpressure;
unsignedshortx;
unsignedshorty;
unsignedshortpad;
} TS_RET;
typedefstruct{
intxscale;
intxtrans;
intyscale;
intytrans;
intxyswap;
} TS_CAL;
#define PEN_UP 0
#define PEN_DOWN 1
#define PEN_FLEETING 2
#define MAX_TS_BUF 16 /* how many do we want to buffer */
#undef USE_ASYNC
#define DEVICE_NAME "s3c2410-ts"
#define TSRAW_MINOR 1
typedefstruct{
unsignedintpenStatus;/* PEN_UP, PEN_DOWN, PEN_SAMPLE */
TS_RET buf[MAX_TS_BUF];/* protect against overrun */
unsignedinthead, tail;/* head and tail for queued events */
wait_queue_head_t wq;
spinlock_t lock;
#ifdef USE_ASYNC
structfasync_struct *aq;
#endif
#ifdef CONFIG_PM
structpm_dev *pm_dev;
#endif
} TS_DEV;
staticTS_DEV tsdev;
#define BUF_HEAD (tsdev.buf[tsdev.head])
#define BUF_TAIL (tsdev.buf[tsdev.tail])
#define INCBUF(x,mod) ((++(x)) & ((mod) - 1))
staticinttsMajor = 0;
staticvoid(*tsEvent)(void);
#ifdef HOOK_FOR_DRAG
#define TS_TIMER_DELAY (HZ/100) /* 10 ms */
staticstructtimer_list ts_timer;
#endif
// pick-up regs val from 2.4.18&2440
// YM_SEN输出驱动器使能,XP上拉使能,正常ADC转换,检测笔尖落下中断信号,等待中断模式
#define wait_down_int() __raw_writel(0x000000d3,S3C2410_ADCTSC)
// YM_SEN输出驱动器使能,XP上拉使能,正常ADC转换,检测笔尖抬起中断信号,等待中断模式
#define wait_up_int() __raw_writel(0x000001d3, S3C2410_ADCTSC)
// XM_SEN、XP_SEN输出驱动器使能,XP上拉禁止,正常ADC转换,X方向测量模式
#define mode_x_axis() __raw_writel(0x00000069, S3C2410_ADCTSC)
// 相当于__raw_writel(0x00000068, S3C2410_ADCTSC),即
// XM_SEN、YP_SEN使能,XP上拉禁止,正常ADC转换,无操作模式
#define mode_x_axis_n() __raw_writel(XP_EXTVLT | XM_GND | YP_AIN | YM_HIZ | \
XP_PULL_UP_DIS | XP_PST(NOP_MODE), S3C2410_ADCTSC)
// YM_SEN、YP_SEN使能,XP上拉禁止,正常ADC转换,Y方向测量模式
#define mode_y_axis() __raw_writel(0x0000009a, S3C2410_ADCTSC)
// __raw_writel(0x00007ffa, S3C2410_ADCCON); -- A/D转换器预分频器使能,预分频值0xff,模拟输入通道SEL_MUX为XP,正常工作模式,使能读启动操作
// __raw_readl(S3C2410_ADCDAT0); -- 读取ADCDAT)的值
#define start_adc_x() do {__raw_writel(0x00007ffa, S3C2410_ADCCON); \
__raw_readl(S3C2410_ADCDAT0); }while(0)
// 怎么跟start_adc_x()配置一样?应该为0x00007fea?
#define start_adc_y() do {__raw_writel(0x00007ffa, S3C2410_ADCCON); \
__raw_readl(S3C2410_ADCDAT1); }while(0)
// 禁止读启动操作,即READ_START位置0
#define disable_ts_adc() __raw_writel(__raw_readl(S3C2410_ADCCON)&0xfffffffd, S3C2410_ADCCON)
staticintadc_state = 0;
staticintx, y;/* touch screen coorinates */
#ifdef HOOK_FOR_DRAG
#define RT_BT_EMU_TM ((HZ>>1)+(HZ>>2)) //0.75S
//static u16 ts_r_x[5];
//static u16 ts_r_y[5];
//static u16 ts_r_idx;
//static u16 ts_r_beg;
staticu32 dn_start;
#endif
staticvoidtsEvent_raw(void)
{
DPRINTK("@@@@@@@@ tvEvent_raw() @@@@@@@@@\n");
if(tsdev.penStatus == PEN_DOWN)
{
//u16 i, j;
#ifdef HOOK_FOR_DRAG
ts_timer.expires = jiffies + TS_TIMER_DELAY;
add_timer(&ts_timer);
#endif
BUF_HEAD.x = x;
BUF_HEAD.y = y;
#ifdef HOOK_FOR_DRAG
BUF_HEAD.pressure = ((jiffies - dn_start) >= RT_BT_EMU_TM) ? PEN_FLEETING : PEN_DOWN;
#else
BUF_HEAD.pressure = PEN_DOWN;
#endif
}/* if (tsdev.penStatus == PEN_DOWN) */
else
{
#ifdef HOOK_FOR_DRAG
del_timer(&ts_timer);
#endif
BUF_HEAD.x = x;
BUF_HEAD.y = y;
BUF_HEAD.pressure = PEN_UP;
}
tsdev.head = INCBUF(tsdev.head, MAX_TS_BUF);
DPRINTK("@@@@@@@@ wake_up_interruptible() in tvEvent_raw() @@@@@@@@@\n");
wake_up_interruptible(&(tsdev.wq));
#ifdef USE_ASYNC
if(tsdev.aq)
kill_fasync(&(tsdev.aq), SIGIO, POLL_IN);
#endif
#ifdef CONFIG_PM
pm_access(tsdev.pm_dev);
#endif
}
staticinttsRead(TS_RET * ts_ret)
{
spin_lock_irq(&(tsdev.lock));
ts_ret->x = BUF_TAIL.x;
ts_ret->y = BUF_TAIL.y;
ts_ret->pressure = BUF_TAIL.pressure;
tsdev.tail = INCBUF(tsdev.tail, MAX_TS_BUF);
spin_unlock_irq(&(tsdev.lock));
returnsizeof(TS_RET);
}
staticssize_t s3c2410_ts_read(structfile *filp,char*buffer,size_tcount, loff_t *ppos)
{
TS_RET ts_ret;
retry:
if(tsdev.head != tsdev.tail)
{
intcount;
count = tsRead(&ts_ret);
if(count)
{
printk("@@@@@ ts_ret.x: %hd, ts_ret.y: %hd @@@@@@@\n", ts_ret.x, ts_ret.y);
copy_to_user(buffer, (char*)&ts_ret, count);
}
returncount;
}
else
{
if(filp->f_flags & O_NONBLOCK)
return-EAGAIN;
interruptible_sleep_on(&(tsdev.wq));
if(signal_pending(current))
return-ERESTARTSYS;
gotoretry;
}
returnsizeof(TS_RET);
}
#ifdef USE_ASYNC
staticints3c2410_ts_fasync(intfd,structfile *filp,intmode)
{
returnfasync_helper(fd, filp, mode, &(tsdev.aq));
}
#endif
staticunsignedints3c2410_ts_poll(structfile *filp,structpoll_table_struct *wait)
{
poll_wait(filp, &(tsdev.wq), wait);
return(tsdev.head == tsdev.tail) ? 0 : (POLLIN | POLLRDNORM);
}
staticinlinevoidstart_ts_adc(void)
{
adc_state = 0;
mode_x_axis();
start_adc_x();
}
staticinlinevoids3c2410_get_XY(void)
{
if(adc_state == 0)
{
adc_state = 1;
disable_ts_adc();
y = __raw_readl(S3C2410_ADCDAT1) & 0x3ff;//x:f04 y:f0e dh by pht.
mode_y_axis();
start_adc_y();
}
elseif(adc_state == 1)
{
adc_state = 0;
disable_ts_adc();
x = __raw_readl(S3C2410_ADCDAT0) & 0x3ff;//y:f04 x:f0e dh by pht.
printk("@@@@@@ PEN DOWN: x: %d, y: %d @@@@@@\n", x, y);
wait_up_int();
tsdev.penStatus = PEN_DOWN;
tsEvent();
}
}
staticirqreturn_t s3c2410_isr_adc(intirq,void*dev_id,structpt_regs *reg)
{
DPRINTK("@@@@@@@@ s3c2410_isr_adc() @@@@@@@@\n");
spin_lock_irq(&(tsdev.lock));
if(tsdev.penStatus == PEN_UP)
{
DPRINTK("@@@@@@ s3c2410_isr_adc 1 @@@@@@\n");
s3c2410_get_XY();
}
#ifdef HOOK_FOR_DRAG
else
{
DPRINTK("@@@@@@ s3c2410_isr_adc 2 @@@@@@\n");
s3c2410_get_XY();
}
#endif
spin_unlock_irq(&(tsdev.lock));
returnIRQ_HANDLED;
}
staticirqreturn_t s3c2410_isr_tc(intirq,void*dev_id,structpt_regs *reg)
{
DPRINTK("@@@@@@@@ s3c2410_isr_tc() @@@@@@@@\n");
spin_lock_irq(&(tsdev.lock));
if(tsdev.penStatus == PEN_UP)
{
DPRINTK("@@@@@@@@ s3c2410_isr_tc 1 @@@@@@@@\n");
#ifdef HOOK_FOR_DRAG
dn_start = jiffies;// add by gzliu
#endif
start_ts_adc();
}
else
{
tsdev.penStatus = PEN_UP;
DPRINTK("@@@@@@@@ s3c2410_isr_tc 2 @@@@@@@@\n");
printk("@@@@@@@ PEN UP: x: %d, y: %d @@@@@@@\n", x, y);
wait_down_int();
tsEvent();
}
spin_unlock_irq(&(tsdev.lock));
returnIRQ_HANDLED;
}
#ifdef HOOK_FOR_DRAG
staticvoidts_timer_handler(unsignedlongdata)
{
spin_lock_irq(&(tsdev.lock));
if(tsdev.penStatus == PEN_DOWN)
{
start_ts_adc();
}
spin_unlock_irq(&(tsdev.lock));
}
#endif
staticints3c2410_ts_open(structinode *inode,structfile *filp)
{
tsdev.head = tsdev.tail = 0;
tsdev.penStatus = PEN_UP;
#ifdef HOOK_FOR_DRAG
init_timer(&ts_timer);
ts_timer.function = ts_timer_handler;
#endif
tsEvent = tsEvent_raw;
init_waitqueue_head(&(tsdev.wq));
//MOD_INC_USE_COUNT;
return0;
}
staticints3c2410_ts_release(structinode *inode,structfile *filp)
{
#ifdef HOOK_FOR_DRAG
del_timer(&ts_timer);
#endif
//MOD_DEC_USE_COUNT;
return0;
}
staticstructfile_operations s3c2410_fops = {
owner: THIS_MODULE,
open: s3c2410_ts_open,
read: s3c2410_ts_read,
release: s3c2410_ts_release,
#ifdef USE_ASYNC
fasync: s3c2410_ts_fasync,
#endif
poll: s3c2410_ts_poll,
};
voidtsEvent_dummy(void) {}
#ifdef CONFIG_PM
staticints3c2410_ts_pm_callback(structpm_dev *pm_dev, pm_request_t req,
void*data)
{
switch(req) {
casePM_SUSPEND:
tsEvent = tsEvent_dummy;
break;
casePM_RESUME:
tsEvent = tsEvent_raw;
wait_down_int();
break;
}
return0;
}
#endif
staticstructclk *adc_clock;
staticint__init s3c2410ts_probe(structdevice *dev)
{
intret;
tsEvent = tsEvent_dummy;
DPRINTK("@@@@@@@@@@ s3c2410ts_probe @@@@@@@@@@\n");
ret = register_chrdev(0, DEVICE_NAME, &s3c2410_fops);
if(ret
printk(DEVICE_NAME" can't get major number\n");
returnret;
}
tsMajor = ret;
adc_clock = clk_get(NULL,"adc");
if(!adc_clock) {
printk(KERN_ERR"failed to get adc clock source\n");
return-ENOENT;
}
clk_use(adc_clock);
clk_enable(adc_clock);
base_addr = ioremap(S3C2410_PA_ADC, 0x20);
__raw_writel(WAIT4INT(0), S3C2410_ADCTSC);
__raw_writel(30000, S3C2410_ADCDLY);// 30000--20000
ret = request_irq(IRQ_ADC, s3c2410_isr_adc, SA_INTERRUPT, DEVICE_NAME, s3c2410_isr_adc);
if(ret)
gotoadc_failed;
ret = request_irq(IRQ_TC, s3c2410_isr_tc, SA_INTERRUPT, DEVICE_NAME, s3c2410_isr_tc);
if(ret)
gototc_failed;
/* Wait for touch screen interrupts */
wait_down_int();
#ifdef CONFIG_DEVFS_FS
devfs_mk_dir("touchscreen");
devfs_mk_cdev(MKDEV(tsMajor, TSRAW_MINOR), S_IFCHR|S_IRUGO|S_IWUSR,"touchscreen/%s","0raw");
#endif
#ifdef CONFIG_PM
tsdev.pm_dev = pm_register(PM_DEBUG_DEV, PM_USER_INPUT, s3c2410_ts_pm_callback);
#endif
return0;
tc_failed:
{
printk(KERN_ERR"tc failed!!!!!!!!!!!!!\n");
free_irq(IRQ_ADC, s3c2410_isr_adc);
}
adc_failed:
{
printk(KERN_ERR"adc failed!!!!!!!!!!!!!\n");
returnret;
}
}
staticstructdevice_driver s3c2410ts_driver = {
.name = DEVICE_NAME,
.bus = &platform_bus_type,
.probe = s3c2410ts_probe,
#ifdef CONFIG_PM
.suspend = s3c2410ts_suspend,
.resume = s3c2410ts_resume,
#endif
};
staticint__init s3c2410ts_init(void)
{
intret;
printk("@@@@@@@@ s3c2410ts init() @@@@@@@\n");
ret = driver_register(&s3c2410ts_driver);
if(ret)
{
printk("register %s driver failed, return code is %d\n", DEVICE_NAME, ret);
}
else
{
printk("@@@@@@@ register %s driver success, return code is %d @@@@@@@@@@\n", DEVICE_NAME, ret);
}
returnret;
}
staticvoid__exit s3c2410ts_exit(void)
{
#ifdef CONFIG_DEVFS_FS
devfs_remove("touchscreen/%d", 0);
devfs_remove("touchscreen");
#endif
unregister_chrdev(tsMajor, DEVICE_NAME);
#ifdef CONFIG_PM
pm_unregister(tsdev.pm_dev);
#endif
free_irq(IRQ_ADC, s3c2410_isr_adc);
free_irq(IRQ_TC, s3c2410_isr_tc);
driver_unregister(&s3c2410ts_driver);
}
module_init(s3c2410ts_init);
module_exit(s3c2410ts_exit);