Rockchip平台TP驱动详解综述分析Word格式.docx
《Rockchip平台TP驱动详解综述分析Word格式.docx》由会员分享,可在线阅读,更多相关《Rockchip平台TP驱动详解综述分析Word格式.docx(25页珍藏版)》请在冰豆网上搜索。
ts@40{
//wake-gpio=<
/*...*/
∙9
∙10
∙11
∙12
∙13
∙14
∙15
∙16
∙17
∙18
∙19
∙20
∙21
∙22
表示i2c2总线上下挂在了多个i2c设备。
其中ts@40是表示此i2c设备的设备类型为触摸屏,设备地址为0x40(7位地址,注意:
在i2c的传输函数中,会将此地址左移一位,因此实际上gslx680的i2c设备地址为0x80)。
该节点下有多个属性:
1、compatible="
属性用于驱动和设备的绑定。
表示特定的设备名称,此处为gslX680;
2、reg=<
属性表示此设备的i2c地址为0x40,等同于@40;
3、wake-gpio=<
表示复位引脚使用的是GPIO0中的GPIO_D3这个引脚,低电平有效。
irp-gpio=<
表示中断引脚使用的是GPIO0中的GPIO_A2这个引脚,高电平触发。
很奇怪,为什么这里没有上电的信息,以及在整个驱动程序中都没有给ic上电的操作。
在前面的MTK平台上的tp驱动都有上电的动作,暂时还搞不懂在RK平台上为什么没有。
4、revert_x=<
标记x和y是否需要翻转。
在上述的信息中,可以通过of接口获取到属性对应的值。
在后面的probe()函数中就会使用到。
注:
关于dts的详细信息可以查看ARMLinux3.x的设备树(DeviceTree)和DeviceTreeUsage
2、修改Makefile、Kconfig、defconfig
(1)、修改Makefile添加gslx680驱动
drivers/input/touchscreen/Makefile中添加驱动:
obj-$(CONFIG_TOUCHSCREEN_GSLX680)+=gslx680/。
只要当配置了CONFIG_TOUCHSCREEN_GSLX680的选项才会去编译gslx680目录下的内容。
在配置内核的时候会通过makemenuconfig来配置对应的选项。
或者是直接在defconfig文件中强制设置该选项。
如果不想要这么复杂,可以将该语句写成obj-y+=gslx680/来强制编译该驱动。
(2)、修改Kconfig添加驱动配置描述
drivers/input/touchscreen/Kconfig中添加驱动配置描述:
configTOUCHSCREEN_GSLX680
tristate"
gslX680touchscreendriver"
help
gslX680touchscreendriver
(3)、配置defconfig设置编译驱动
一般在内核中会有配置好的默认的config文件供参考,可以直接修改defconfig来选择编译某个驱动。
此处在arch/arm/configs/rockchip_defconfig文件中添加CONFIG_TOUCHSCREEN_GSLX680=y并将该文件拷贝到kernel目录下命名为.config即可。
2、添加i2c驱动
#defineGSLX680_I2C_NAME"
#defineGSLX680_I2C_ADDR0x40
staticconststructi2c_device_idgsl_ts_id[]={
{GSLX680_I2C_NAME,0},
{}
};
MODULE_DEVICE_TABLE(i2c,gsl_ts_id);
staticstructi2c_drivergsl_ts_driver={
.driver={
.name=GSLX680_I2C_NAME,
.owner=THIS_MODULE,
},
#ifndefCONFIG_HAS_EARLYSUSPEND
//.suspend=gsl_ts_suspend,
//.resume=gsl_ts_resume,
#endif
.probe=gsl_ts_probe,
.remove=gsl_ts_remove,
.id_table=gsl_ts_id,
staticint__initgsl_ts_init(void)
{
intret;
printk("
==gsl_ts_init==\n"
);
ret=i2c_add_driver(&
gsl_ts_driver);
ret=%d\n"
ret);
returnret;
}
staticvoid__exitgsl_ts_exit(void)
==gsl_ts_exit==\n"
i2c_del_driver(&
return;
∙23
∙24
∙25
∙26
∙27
∙28
∙29
∙30
∙31
∙32
∙33
∙34
∙35
∙36
∙37
∙38
注册名字为GSLX680_I2C_NAME的i2c驱动,即gslx680,该驱动支持的设备名为字gsl_ts_id[]里的设备名称。
因为我们在dts中已注册了一个名字为gslx680的i2c设备。
因此,设备与驱动可以匹配成功并正确执行probe()函数。
至于设备与驱动是如何匹配的,可以参照Linuxi2c子系统
3、gsl_ts_probe()
staticintgsl_ts_probe(structi2c_client*client,conststructi2c_device_id*id)
structgsl_ts*ts;
intrc;
structdevice_node*np=client->
dev.of_node;
enumof_gpio_flagswake_flags;
unsignedlongirq_flags;
//检查i2c适配器的能力
GSLX680Enter%s\n"
__func__);
if(!
i2c_check_functionality(client->
adapter,I2C_FUNC_I2C))
{
dev_err(&
client->
dev,"
I2Cfunctionalitynotsupported\n"
return-ENODEV;
}
//为ts申请内核空间
ts=kzalloc(sizeof(*ts),GFP_KERNEL);
if(!
ts)
return-ENOMEM;
==kzallocsuccess=\n"
ts->
client=client;
i2c_set_clientdata(client,ts);
device_id=id->
driver_data;
//从设备节点np中获取到irq和wake的gpio的信息
irq_pin=of_get_named_gpio_flags(np,"
irp-gpio"
0,(enumof_gpio_flags*)&
irq_flags);
wake_pin=of_get_named_gpio_flags(np,"
wake-gpio"
0,&
wake_flags);
//为设备申请gpio,并设置默认电平
if(gpio_is_valid(ts->
wake_pin))
rc=devm_gpio_request_one(&
dev,ts->
wake_pin,(wake_flags&
OF_GPIO_ACTIVE_LOW)?
GPIOF_OUT_INIT_LOW:
GPIOF_OUT_INIT_HIGH,"
gslX680wakepin"
if(rc!
=0)
gslX680wakepinerror\n"
return-EIO;
g_wake_pin=ts->
wake_pin;
//msleep(100);
else
dev_info(&
wakepininvalid\n"
irq_pin))
irq_pin,(irq_flags&
gslX680irqpin"
if(rc!
gslX680irqpinerror\n"
irqpininvalid\n"
//创建工作队列,申请input设备
rc=gslX680_ts_init(client,ts);
if(rc<
0)
GSLX680initfailed\n"
gotoerror_mutex_destroy;
}
gsl_client=client;
//从设备节点中获取属性信息
of_property_read_u32(np,"
revert_x"
&
revert_x);
//sss
revert_y"
revert_y);
//初始化IC,包括复位,测试i2c以及加载ic配置信息
init_chip(ts->
client);
check_mem_data(ts->
//申请中断号
irq=gpio_to_irq(ts->
irq_pin);
//Ifnotdefinedinclient
if(ts->
irq)
//为client->
dev设备的中断号ts->
irq申请irq_flags触发的中断,中断服务子程序为gsl_ts_irq
rc=devm_request_threaded_irq(&
irq,NULL,gsl_ts_irq,irq_flags|IRQF_ONESHOT,client->
name,ts);
printk(KERN_ALERT"
CannotallocatetsINT!
ERRNO:
%d\n"
rc);
gotoerror_req_irq_fail;
//disable_irq(ts->
irq);
gslx680irqreqfail\n"
tp.tp_resume=gsl_ts_late_resume;
tp.tp_suspend=gsl_ts_early_suspend;
tp_register_fb(&
ts->
tp);
#ifdefCONFIG_HAS_EARLYSUSPEND
early_suspend.level=EARLY_SUSPEND_LEVEL_BLANK_SCREEN+1;
//ts->
early_suspend.level=EARLY_SUSPEND_LEVEL_DISABLE_FB+1;
early_suspend.suspend=gsl_ts_early_suspend;
early_suspend.resume=gsl_ts_late_resume;
register_early_suspend(&
early_suspend);
#ifdefGSL_MONITOR
printk("
gsl_ts_probe():
queuegsl_monitor_workqueue\n"
INIT_DELAYED_WORK(&
gsl_monitor_work,gsl_monitor_worker);
gsl_monitor_workqueue=create_singlethread_workqueue("
gsl_monitor_workqueue"
queue_delayed_work(gsl_monitor_workqueue,&
gsl_monitor_work,1000);
[GSLX680]End%s\n"
return0;
//exit_set_irq_mode:
error_req_irq_fail:
free_irq(ts->
irq,ts);
error_mutex_destroy:
input_free_device(ts->
input);
kfree(ts);
returnrc;
∙39
∙40
∙41
∙42
∙43
∙44
∙45
∙46
∙47
∙48
∙49
∙50
∙51
∙52
∙53
∙54
∙55
∙56
∙57
∙58
∙59
∙60
∙61
∙62
∙63
∙64
∙65
∙66
∙67
∙68
∙69
∙70
∙71
∙72
∙73
∙74
∙75
∙76
∙77
∙78
∙79
∙80
∙81
∙82
∙83
∙84
∙85
∙86
∙87
∙88
∙89
∙90
∙91
∙92
∙93
∙94
∙95
∙96
∙97
∙98
∙99
∙100
∙101
∙102
∙103
∙104
∙105
∙106
∙107
∙108
∙109
∙110
∙111
∙112
∙113
∙114
∙115
∙116
∙117
∙118
∙119
∙120
∙121
∙122
∙123
∙124
∙125
∙126
∙127
∙128
∙129
∙130
∙131
(1)、自定义的数据结构gsl_ts
一般在自己的驱动程序中,都会为该驱动程序封装一个数据结构,这里的gsl_ts就充当这种角色。
在该驱动程序中自定义了一个数据结构:
structgsl_ts{
structi2c_client*client;
structinput_dev*input;
structwork_structwork;
structworkqueue_struct*wq;
structgsl_ts_data*dd;
u8*touch_data;
u8device_id;
intirq;
intirq_pin;
intwake_pin;
structtp_devicetp;
#ifdefined(CONFIG_HAS_EARLYSUSPEND)
structearly_suspendearly_suspend;
client表示一个i2c的设备;
input表示一个输入设备;
work表示一个工作,用于处理中断到来之后获取坐标等信息;
wq表示一个工作队列,将上面的work加入到该工作队列中;
dd和touch_data用来存储坐标的相关信息;
device_id表示i2c设备的设备号;
irq申请的中断号;
irq_pin中断引脚;
wake_pin复位引脚;
这两个引脚信息可以通过dts获取到.
tp创建一个为tp_device的数据结构,里面有个成员notifier_block用来接收LCD背光灯的亮暗的通知进而调用suspend()和resume()。
主要的实现在tp_suspend.h中。
至于这里面的详细机制还不是很明白。
(2)、检查i2c适配器的能力
在很多i2c设备驱动程序中,一进入probe()就要检查i2c适配器的能力。
现在还不清楚这么做的目的是什么。
后面会仔细的学习一下linux的i2c子系统。
(3)、获取wake和irq的引脚信息
在前面配置dts说过:
dts中设备节点的属性的值可以通过of_接口获取到。
//从设备节点np中获取到irq和wake的gpio的信息
可以通过设备节点np获取到它irq-gpio这一个属性的值存放在ts->
irq_pin中,以及将其flag刚到变量irq_flags中。
因此我们可以知道:
irq_pin=GPIO_A2
irq_flag=IRQ_TYPE_LEVEL_HIGH
同理,wake_pin也是一样。
(4)、申请gpio并设置输出电平
将irq和wake引脚电平都设置输出低电平。
(5)、gslX680_ts_init
staticintgslX680_ts_init(structi2c_client*client,structgsl_ts*ts)
structinput_dev*input_device;
intrc=0;
[GSLX680]Enter%s\n"
//配置获取坐标信息
dd=&
devices[ts->
device_id];
if(ts->
device_id==0)
dd->
data_size=MAX_FINGERS*ts->
touch_bytes+ts->
touch_meta_data;
touch_index=0;
//申请空间存放坐标信息
touch_data=kzalloc(ts->
data_size,GFP_KERNEL);
touch_data)
pr_err("
%s:
Unabletoallocatememory\n"
//申请一个input_dev设备
input_device=input_allocate_device();
input_device){
rc=-ENOMEM;
gotoerror_alloc_dev;
//初始化input_device
input=input_device;
input_device->
name=GSLX680_I2C_NAME;
input_