//#defineCONFIG_TOUCHSCREEN_S3C2410_DEBUG
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#defineTRUE1//CoAsiaadded
#defineFALSE0//CoAsiaadded
#defineFILTER_LIMIT25//CoAsiaadded
/*Forts.dev.id.version*/
#defineS3C2410TSVERSION0x0101
#defineTSC_SLEEP(S3C2410_ADCTSC_PULL_UP_DISABLE|S3C2410_ADCTSC_XY_PST(0))
#defineWAIT4INT(x)(((x)<<8)|\
S3C2410_ADCTSC_YM_SEN|S3C2410_ADCTSC_YP_SEN|S3C2410_ADCTSC_XP_SEN|\
S3C2410_ADCTSC_XY_PST(3))
#defineAUTOPST(S3C2410_ADCTSC_YM_SEN|S3C2410_ADCTSC_YP_SEN|S3C2410_ADCTSC_XP_SEN|\
S3C2410_ADCTSC_AUTO_PST|S3C2410_ADCTSC_XY_PST(0))
#defineDEBUG_LVL"<3>"//KERN_DEBUG
staticchar*s3c2440ts_name="s3c2440TouchScreen";
/*
*Per-touchscreendata.
*/
//定义s3c2440触摸屏使用的数据结构体
structs3c2440ts{
structinput_dev*dev;
longxp;
longyp;
intcount;
intshift;
};
staticstructs3c2440tsts;
staticstructclk*adc_clock;
//__iomem声明地址空间是设备地址映射空间
staticvoid__iomem*base_addr;
//函数声明
staticvoidtouch_timer_fire(unsignedlongdata);
staticirqreturn_ttc_irq(intirq,void*dev_id);
staticirqreturn_tadc_irq(intirq,void*dev_id);
staticint__inits3c2440ts_probe(structplatform_device*pdev);
staticints3c2440ts_remove(structplatform_device*pdev);
staticints3c2440ts_resume(structplatform_device*pdev);
//定义定时器
staticstructtimer_listtouch_timer=
TIMER_INITIALIZER(touch_timer_fire,0,0);
//IRQ_TC中断处理函数
staticirqreturn_ttc_irq(intirq,void*dev_id)
{
//data0,data1用于存放读取的ADCDAT数据寄存器的值
unsignedlongdata0;
unsignedlongdata1;
intupdown;//用于存放光标的按下或提起的状态
//读取ADCDAT0、ADCDAT1数据寄存器的值
data0=readl(base_addr+S3C2410_ADCDAT0);
data1=readl(base_addr+S3C2410_ADCDAT1);
//查看数据寄存器的第15位的值
updown=(!(data0&S3C2410_ADCDAT0_UPDOWN))&&(!(data1&S3C2410_ADCDAT0_UPDOWN));
/*TODOweshouldnevergetaninterruptwithupdownsetwhile
*thetimerisrunning,butmaybeweoughttoverifythatthe
*timerisntrunninganyways.*/
//如果data0和data1的第15位都是0,则updown为1,则通过函数touch_timer_fire()函数来启动ADC转换
if(updown)
touch_timer_fire(0);
returnIRQ_HANDLED;
}
staticvoidtouch_timer_fire(unsignedlongdata)
{
//用于存储数据寄存器ADCDAT0、ADCDAT1的值
unsignedlongdata0;
unsignedlongdata1;
//用于存放光标是否被按下
intupdown;
data0=readl(base_addr+S3C2410_ADCDAT0);
data1=readl(base_addr+S3C2410_ADCDAT1);
updown=(!(data0&S3C2410_ADCDAT0_UPDOWN))&&(!(data1&S3C2410_ADCDAT0_UPDOWN));
//printk("Thenumberofupdownis%d\n",updown);
//如果光标被按下,执行
if(updown)
{
//ts.count!=0表示ADC已经转换过,下面就报告事件和光标位置数据
if(ts.count!=0)
{
ts.xp>>=ts.shift;//这里shift为2,这里实际上是求均值,四次的和/4,这样定位更加准确
ts.yp>>=ts.shift;
#ifdefCONFIG_TOUCHSCREEN_S3C2410_DEBUG
{
structtimevaltv;
do_gettimeofday(&tv);
printk(DEBUG_LVL"T:%06d,X:%03ld,Y:%03ld\n",(int)tv.tv_usec,ts.xp,ts.yp);
}
#endif
/*
下面的函数位于/include/linux/input.h,作用是报告事件
staticinlinevoidinput_report_abs(structinput_dev*dev,unsignedintcode,intvalue)
{
input_event(dev,EV_ABS,code,value);
}
*/
//报告X,Y的绝对坐标
input_report_abs(ts.dev,ABS_X,ts.xp);
input_report_abs(ts.dev,ABS_Y,ts.yp);
//报告事件,1代表光标被按下
input_report_key(ts.dev,BTN_TOUCH,1);
//报告触摸屏状态,1代表触摸屏被按下
input_report_abs(ts.dev,ABS_PRESSURE,1);
//等待接收方的确认,用于事件的同步
input_sync(ts.dev);
}
//现在光标被按下,并且ADC转换没有启动
ts.xp=0;
ts.yp=0;
ts.count=0;
//设置触摸屏控制寄存器的值为0xdcB:11011100,设置控制寄存器上拉无效,自动转换X,Y坐标
//printk("S3C2410_ADCTSC:0x%x\n",S3C2410_ADCTSC_PULL_UP_DISABLE|AUTOPST);
writel(S3C2410_ADCTSC_PULL_UP_DISABLE|AUTOPST,base_addr+S3C2410_ADCTSC);
//启动ADC转换
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);
}
}
staticirqreturn_tadc_irq(intirq,void*dev_id)
{
//用于存放数据寄存器的数据
unsignedlongdata0;
unsignedlongdata1;
//读取数据,这次主要读取的是位置数据
data0=readl(base_addr+S3C2410_ADCDAT0);
data1=readl(base_addr+S3C2410_ADCDAT1);
ts.xp+=data0&S3C2410_ADCDAT0_XPDATA_MASK;//累加四次准换结果的X坐标和
ts.yp+=data1&S3C2410_ADCDAT1_YPDATA_MASK;//累加四次准换结果的Y坐标和
ts.count++;//转换次数加一
//如果转换次数小于4
if(ts.count
{
//再次设置触摸屏控制寄存器上拉不使能、自动X、Y转换模式
writel(S3C2410_ADCTSC_PULL_UP_DISABLE|AUTOPST,base_addr+S3C2410_ADCTSC);
//再次启动ADC转换
writel(readl(base_addr+S3C2410_ADCCON)|S3C2410_ADCCON_ENABLE_START,base_addr+S3C2410_ADCCON);
}
else//这时,ADC转换四次完成,延迟一个系统滴答,执行touch_timer_fire()函数
{
mod_timer(&touch_timer,jiffies+1);
writel(WAIT4INT(1),base_addr+S3C2410_ADCTSC);
}
returnIRQ_HANDLED;
}
/*
*Thefunctionsforinserting/removingusasamodule.
*/
/*
该结构体定义在/include/linux/platform_device.h
structplatform_device{
constchar*name;
intid;
structdevicedev;
u32num_resources;
structresource*resource;
};
*/
staticint__inits3c2440ts_probe(structplatform_device*pdev)
{
intrc;
/*
下面结构体定义在/include/mach/s3c2410_ts.h
structs3c2410_ts_mach_info{
intdelay;
intpresc;
intoversampling_shift;
};
*/
structs3c2410_ts_mach_info*info;
structinput_dev*input_dev;
/*
void*platform_data;//Platformspecificdata,devicecoredoesnttouchit
*/
info=(structs3c2440_ts_mach_info*)pdev->dev.platform_data;
if(!info)
{
printk(KERN_ERR"Hm...toobad:noplatformdataforts\n");
return-EINVAL;
}
#ifdefCONFIG_TOUCHSCREEN_S3C2410_DEBUG
printk(DEBUG_LVL"Enterings3c2440ts_init\n");
#endif
//由于ADC转换需要时钟,这里获取时钟
adc_clock=clk_get(NULL,"adc");
if(!adc_clock){
printk(KERN_ERR"failedtogetadcclocksource\n");
return-ENOENT;
}
clk_enable(adc_clock);//使能时钟
#ifdefCONFIG_TOUCHSCREEN_S3C2410_DEBUG
printk(DEBUG_LVL"gotandenabledclock\n");
#endif
//通过ioremap实现物理地址到虚拟地址的转换
base_addr=ioremap(S3C2410_PA_ADC,0x20);
if(base_addr==NULL){
printk(KERN_ERR"Failedtoremapregisterblock\n");
return-ENOMEM;
}
//设置ADCCON控制寄存器为0x4c40,设置预分频有效,预分频值为B:110001D:49
//printk("ADCCONis0x%x\n",S3C2410_ADCCON_PRSCEN|S3C2410_ADCCON_PRSCVL(info->presc&0xFF));
if((info->presc&0xff)>0)
writel(S3C2410_ADCCON_PRSCEN|S3C2410_ADCCON_PRSCVL(info->presc&0xFF),\
base_addr+S3C2410_ADCCON);
else
writel(0,base_addr+S3C2410_ADCCON);
/*Initialiseregisters*/
/*
设置ADC开始延时寄存器ADCDLY:0x4e20
*/
//printk("ADCDLY:0x%x\n",info->delay&0xffff);
if((info->delay&0xffff)>0)
writel(info->delay&0xffff,base_addr+S3C2410_ADCDLY);
/*
设置ADC触摸屏控制寄存器ADC_TSC:0xd3B:11010011
[8]检测光标按下中断信号
[7]YM输出驱动有效(GND)
[6]YP输出驱动无效(AIN5)
[5]XM输出驱动无效(Hi-z)
[4]XP输出驱动无效(AIN7)
[3]XP上拉有效
[2]普通ADC转换
[0:1]等待中断模式测量X和Y的坐标
*/
//printk("ADC_TSC:0x%x\n",WAIT4INT(0));
writel(WAIT4INT(0),base_addr+S3C2410_ADCTSC);
/*Initialiseinputstuff*/
memset(&ts,0,sizeof(structs3c2440ts));
/*
下面的函数
为新的输入设备分配内存。
使用free_device()释放没有被注册的函数,使用input_unregister_device()解除已经注册的设备
定义在/drivers/input/input.c
structinput_dev*input_allocate_device(void)
{
structinput_dev*dev;
dev=kzalloc(sizeof(structinput_dev),GFP_KERNEL);
if(dev){
dev->dev.type=&input_dev_type;
dev->dev.class=&input_class;
device_initialize(&dev->dev);
mutex_init(&dev->mutex);
spin_lock_init(&dev->event_lock);
INIT_LIST_HEAD(&dev->h_list);
INIT_LIST_HEAD(&dev->node);
__module_get(THIS_MODULE);
}
returndev;
}
*/
input_dev=input_allocate_device();
if(!input_dev){
printk(KERN_ERR"Unabletoallocatetheinputdevice!!\n");
return-ENOMEM;
}
//下面初始化输入设备信息
ts.dev=input_dev;
ts.dev->evbit[0]=BIT_MASK(EV_SYN)|BIT_MASK(EV_KEY)|
BIT_MASK(EV_ABS);
ts.dev->keybit[BIT_WORD(BTN_TOUCH)]=BIT_MASK(BTN_TOUCH);
input_set_abs_params(ts.dev,ABS_X,0,0x3FF,0,0);
input_set_abs_params(ts.dev,ABS_Y,0,0x3FF,0,0);
input_set_abs_params(ts.dev,ABS_PRESSURE,0,1,0,0);
//ts.dev->private=&ts;
ts.dev->name=s3c2440ts_name;
ts.dev->id.bustype=BUS_RS232;
ts.dev->id.vendor=0xDEAD;
ts.dev->id.product=0xBEEF;
ts.dev->id.version=S3C2410TSVERSION;
ts.shift=info->oversampling_shift;
//printk("shift:%d\n",ts.shift);
/*Getirqs*/
//申请ADC中断,注意,中断类型为IRQF_SAMPLE_RANDOM|IRQF_SHARED,这样在使用触摸屏的时候
//可以调试自己的ADC转换驱动,中断处理函数为adc_irq
if(request_irq(IRQ_ADC,adc_irq,IRQF_SAMPLE_RANDOM|IRQF_SHARED,
"s3c2440_action",ts.dev)){
printk(KERN_ERR"s3c2440_ts.c:CouldnotallocatetsIRQ_ADC!\n");
iounmap(base_addr);
return-EIO;
}
//申请TC中断,中断处理函数为tc_irq
if(request_irq(IRQ_TC,tc_irq,IRQF_SAMPLE_RANDOM,
"s3c2440_action",ts.dev)){
printk(KERN_ERR"s3c2440_ts.c:CouldnotallocatetsIRQ_TC!\n");
free_irq(IRQ_ADC,ts.dev);
iounmap(base_addr);
return-EIO;
}
printk(KERN_INFO"%ssuccessfullyloaded\n",s3c2440ts_name);
/*Allwentok,soregistertotheinputsystem*/
/*这里注册设备
函数功能:
*Thisfunctionregistersdevicewithinputcore.Thedevicemustbe
*allocatedwithinput_allocate_device()andallitscapabilities
*setupbeforeregistering.
*Iffunctionfailsthedevicemustbefreedwithinput_free_device().
*Oncedevicehasbeensuccessfullyregistereditcanbeunregistered
*withinput_unregister_device();input_free_device()shouldnotbe
*calledinthiscase.
函数原型如下:
intinput_register_device(structinput_dev*dev)
{
staticatomic_tinput_no=ATOMIC_INIT(0);
structinput_handler*handler;
constchar*path;
interror;
__set_bit(EV_SYN,dev->evbit);
init_timer(&dev->timer);
if(!dev->rep[REP_DELAY]&&!dev->rep[REP_PERIOD]){
dev->timer.data=(long)dev;
dev->timer.function=input_repeat_key;
dev->rep[REP_DELAY]=250;
dev->rep[REP_PERIOD]=33;
}
if(!dev->getkeycode)
dev->getkeycode=input_default_getkeycode;
if(!dev->setkeycode)
dev->setkeycode=input_default_setkeycode;
snprintf(dev->dev.bus_id,sizeof(dev->dev.bus_id),
"input%ld",(unsignedlong)atomic_inc_return(&input_no)-1);
error=device_add(&dev->dev);
if(error)
returnerror;
path=kobject_get_path(&dev->dev.kobj,GFP_KERNEL);
printk(KERN_INFO"input:%sas%s\n",
dev->name?dev->name:"Unspecifieddevice",path?path:"N/A");
kfree(path);
error=mutex_lock_interruptible(&input_mutex);
if(error){
device_del(&dev->dev);
returnerror;
}
list_add_tail(&dev->node,&input_dev_list);
list_for_each_entry(handler,&input_handler_list,node)
input_attach_handler(dev,handler);
input_wakeup_procfs_readers();
mutex_unlock(&input_mutex);
return0;
}
*/
rc=input_register_device(ts.dev);
if(rc){
free_irq(IRQ_TC,ts.dev);
free_irq(IRQ_ADC,ts.dev);
clk_disable(adc_clock);
iounmap(base_addr);
return-EIO;
}
return0;
}
staticints3c2440ts_remove(structplatform_device*pdev)
{
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);
return0;
}
#ifdefCONFIG_PM
staticints3c2440ts_suspend(structplatform_device*pdev,pm_message_tstate)
{
writel(TSC_SLEEP,base_addr+S3C2410_ADCTSC);
writel(readl(base_addr+S3C2410_ADCCON)|S3C2410_ADCCON_STDBM,
base_addr+S3C2410_ADCCON);
disable_irq(IRQ_ADC);
disable_irq(IRQ_TC);
clk_disable(adc_clock);
return0;
}
staticints3c2440ts_resume(structplatform_device*pdev)
{
structs3c2440_ts_mach_info*info=
(structs3c2440_ts_mach_info*)pdev->dev.platform_data;
clk_enable(adc_clock);
msleep(1);
enable_irq(IRQ_ADC);
enable_irq(IRQ_TC);
if((info->presc&0xff)>0)
writel(S3C2410_ADCCON_PRSCEN|S3C2410_ADCCON_PRSCVL(info->presc&0xFF),\
base_addr+S3C2410_ADCCON);
else
writel(0,base_addr+S3C2410_ADCCON);
/*Initialiseregisters*/
if((info->delay&0xffff)>0)
writel(info->delay&0xffff,base_addr+S3C2410_ADCDLY);
writel(WAIT4INT(0),base_addr+S3C2410_ADCTSC);
return0;
}
#else
#defines3c2440ts_suspendNULL
#defines3c2440ts_resumeNULL
#endif
/*
下面是/linux/platform_device.h定义的platform_driver结构体
structplatform_driver{
int(*probe)(structplatform_device*);//设备的检测,所以需要先前的设备注册
int(*remove)(structplatform_device*);//删除该设备
void(*shutdown)(structplatform_device*);//关闭该设备
int(*suspend)(structplatform_device*,pm_message_tstate);
int(*suspend_late)(structplatform_device*,pm_message_tstate);
int(*resume_early)(structplatform_device*);
int(*resume)(structplatform_device*);
structpm_ext_ops*pm;
structdevice_driverdriver;//设备驱动,定义在include/linux/device.h中
};
内核提供的platform_driver结构体的注册函数为platform_driver_register(),该函数定义在driver/base/platform.c中
*/
staticstructplatform_drivers3c2440ts_driver={
.driver={
.name="s3c2440-ts",
.owner=THIS_MODULE,
},
.probe=s3c2440ts_probe,
.remove=s3c2440ts_remove,
.suspend=s3c2440ts_suspend,
.resume=s3c2440ts_resume,
};
staticint__inits3c2440ts_init(void)
{
intrc;
rc=platform_driver_register(&s3c2440ts_driver);
if(rc<0)
printk(KERN_ERR"platform_driver_registererror!\n");
returnrc;
}
staticvoid__exits3c2440ts_exit(void)
{
platform_driver_unregister(&s3c2440ts_driver);
}
module_init(s3c2440ts_init);
module_exit(s3c2440ts_exit);
MODULE_AUTHOR("YANMING");
MODULE_DESCRIPTION("Mys3c2440touchscreendriver");
MODULE_LICENSE("GPL");