Rockchip平台TP驱动详解.docx

上传人:b****6 文档编号:3716196 上传时间:2022-11-24 格式:DOCX 页数:25 大小:23.57KB
下载 相关 举报
Rockchip平台TP驱动详解.docx_第1页
第1页 / 共25页
Rockchip平台TP驱动详解.docx_第2页
第2页 / 共25页
Rockchip平台TP驱动详解.docx_第3页
第3页 / 共25页
Rockchip平台TP驱动详解.docx_第4页
第4页 / 共25页
Rockchip平台TP驱动详解.docx_第5页
第5页 / 共25页
点击查看更多>>
下载资源
资源描述

Rockchip平台TP驱动详解.docx

《Rockchip平台TP驱动详解.docx》由会员分享,可在线阅读,更多相关《Rockchip平台TP驱动详解.docx(25页珍藏版)》请在冰豆网上搜索。

Rockchip平台TP驱动详解.docx

Rockchip平台TP驱动详解

本文描述在RK3126平台上添加一个新的TP驱动(gslx680驱动)以及详细的驱动代码信息。

如有不足之处,敬请指出。

1、修改dts,添加新的i2c设备。

在 arch/arm/boot/dts/rk312x-sdk-v2.2.dtsi中添加i2c设备的相关信息:

ts@40{

compatible="gslX680";

reg=<0x40>;

wake-gpio=<&gpio0GPIO_D3GPIO_ACTIVE_LOW>;

irp-gpio=<&gpio0GPIO_A2IRQ_TYPE_LEVEL_HIGH>;

revert_x=<0>;

revert_y=<0>;

};

∙1

∙2

∙3

∙4

∙5

∙6

∙7

∙8

&i2c2{

status="okay";

/*

ts@55{

compatible="goodix,gt8xx";

reg=<0x55>;

touch-gpio=<&gpio1GPIO_B0IRQ_TYPE_LEVEL_LOW>;

reset-gpio=<&gpio2GPIO_C1GPIO_ACTIVE_LOW>;

//power-gpio=<&gpio0GPIO_C5GPIO_ACTIVE_LOW>;

max-x=<1280>;

max-y=<800>;

};*/

ts@40{

compatible="gslX680";

reg=<0x40>;

//wake-gpio=<&gpio0GPIO_D3GPIO_ACTIVE_LOW>;

irp-gpio=<&gpio0GPIO_A2IRQ_TYPE_LEVEL_HIGH>;

revert_x=<0>;

revert_y=<0>;

};

/*...*/

∙1

∙2

∙3

∙4

∙5

∙6

∙7

∙8

∙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";属性用于驱动和设备的绑定。

表示特定的设备名称,此处为gslX680; 

2、reg=<0x40>;属性表示此设备的i2c地址为0x40,等同于@40; 

3、wake-gpio=<&gpio0GPIO_D3GPIO_ACTIVE_LOW>;表示复位引脚使用的是GPIO0中的GPIO_D3这个引脚,低电平有效。

 

irp-gpio=<&gpio0GPIO_A2IRQ_TYPE_LEVEL_HIGH>;表示中断引脚使用的是GPIO0中的GPIO_A2这个引脚,高电平触发。

 

很奇怪,为什么这里没有上电的信息,以及在整个驱动程序中都没有给ic上电的操作。

在前面的MTK平台上的tp驱动都有上电的动作,暂时还搞不懂在RK平台上为什么没有。

 

4、revert_x=<0>;revert_y=<0>;标记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

∙1

∙2

∙3

∙4

(3)、配置defconfig设置编译驱动

一般在内核中会有配置好的默认的config文件供参考,可以直接修改defconfig来选择编译某个驱动。

此处在arch/arm/configs/rockchip_defconfig文件中添加CONFIG_TOUCHSCREEN_GSLX680=y并将该文件拷贝到kernel目录下命名为.config即可。

2、添加i2c驱动

#defineGSLX680_I2C_NAME"gslX680"

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

printk("ret=%d\n",ret);

returnret;

}

staticvoid__exitgsl_ts_exit(void)

{

printk("==gsl_ts_exit==\n");

i2c_del_driver(&gsl_ts_driver);

return;

}

∙1

∙2

∙3

∙4

∙5

∙6

∙7

∙8

∙9

∙10

∙11

∙12

∙13

∙14

∙15

∙16

∙17

∙18

∙19

∙20

∙21

∙22

∙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适配器的能力

printk("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;

printk("==kzallocsuccess=\n");

ts->client=client;

i2c_set_clientdata(client,ts);

ts->device_id=id->driver_data;

//从设备节点np中获取到irq和wake的gpio的信息

ts->irq_pin=of_get_named_gpio_flags(np,"irp-gpio",0,(enumof_gpio_flags*)&irq_flags);

ts->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(&client->dev,ts->wake_pin,(wake_flags&OF_GPIO_ACTIVE_LOW)?

GPIOF_OUT_INIT_LOW:

GPIOF_OUT_INIT_HIGH,"gslX680wakepin");

if(rc!

=0)

{

dev_err(&client->dev,"gslX680wakepinerror\n");

return-EIO;

}

g_wake_pin=ts->wake_pin;

//msleep(100);

}

else

{

dev_info(&client->dev,"wakepininvalid\n");

}

if(gpio_is_valid(ts->irq_pin))

{

rc=devm_gpio_request_one(&client->dev,ts->irq_pin,(irq_flags&OF_GPIO_ACTIVE_LOW)?

GPIOF_OUT_INIT_LOW:

GPIOF_OUT_INIT_HIGH,"gslX680irqpin");

if(rc!

=0)

{

dev_err(&client->dev,"gslX680irqpinerror\n");

return-EIO;

}

}

else

{

dev_info(&client->dev,"irqpininvalid\n");

}

//创建工作队列,申请input设备

rc=gslX680_ts_init(client,ts);

if(rc<0)

{

dev_err(&client->dev,"GSLX680initfailed\n");

gotoerror_mutex_destroy;

}

gsl_client=client;

//从设备节点中获取属性信息

of_property_read_u32(np,"revert_x",&revert_x);//sss

of_property_read_u32(np,"revert_y",&revert_y);//sss

//初始化IC,包括复位,测试i2c以及加载ic配置信息

init_chip(ts->client);

check_mem_data(ts->client);

//申请中断号

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(&client->dev,ts->irq,NULL,gsl_ts_irq,irq_flags|IRQF_ONESHOT,client->name,ts);

if(rc!

=0)

{

printk(KERN_ALERT"CannotallocatetsINT!

ERRNO:

%d\n",rc);

gotoerror_req_irq_fail;

}

//disable_irq(ts->irq);

}

else

{

printk("gslx680irqreqfail\n");

gotoerror_req_irq_fail;

}

ts->tp.tp_resume=gsl_ts_late_resume;

ts->tp.tp_suspend=gsl_ts_early_suspend;

tp_register_fb(&ts->tp);

#ifdefCONFIG_HAS_EARLYSUSPEND

ts->early_suspend.level=EARLY_SUSPEND_LEVEL_BLANK_SCREEN+1;

//ts->early_suspend.level=EARLY_SUSPEND_LEVEL_DISABLE_FB+1;

ts->early_suspend.suspend=gsl_ts_early_suspend;

ts->early_suspend.resume=gsl_ts_late_resume;

register_early_suspend(&ts->early_suspend);

#endif

 

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

#endif

printk("[GSLX680]End%s\n",__func__);

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;

}

∙1

∙2

∙3

∙4

∙5

∙6

∙7

∙8

∙9

∙10

∙11

∙12

∙13

∙14

∙15

∙16

∙17

∙18

∙19

∙20

∙21

∙22

∙23

∙24

∙25

∙26

∙27

∙28

∙29

∙30

∙31

∙32

∙33

∙34

∙35

∙36

∙37

∙38

∙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;

#endif

};

∙1

∙2

∙3

∙4

∙5

∙6

∙7

∙8

∙9

∙10

∙11

∙12

∙13

∙14

∙15

∙16

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的信息

ts->irq_pin=of_get_named_gpio_flags(np,"irp-gpio",0,(enumof_gpio_flags*)&irq_flags);

ts->wake_pin=of_get_named_gpio_flags(np,"wake-gpio",0,&wake_flags);

∙1

∙2

∙3

可以通过设备节点np获取到它irq-gpio这一个属性的值存放在ts->irq_pin中,以及将其flag刚到变量irq_flags中。

因此我们可以知道:

ts->irq_pin=GPIO_A2

irq_flag=IRQ_TYPE_LEVEL_HIGH

∙1

∙2

同理,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;

printk("[GSLX680]Enter%s\n",__func__);

//配置获取坐标信息

ts->dd=&devices[ts->device_id];

if(ts->device_id==0)

{

ts->dd->data_size=MAX_FINGERS*ts->dd->touch_bytes+ts->dd->touch_meta_data;

ts->dd->touch_index=0;

}

//申请空间存放坐标信息

ts->touch_data=kzalloc(ts->dd->data_size,GFP_KERNEL);

if(!

ts->touch_data)

{

pr_err("%s:

Unabletoallocatememory\n",__func__);

return-ENOMEM;

}

//申请一个input_dev设备

input_device=input_allocate_device();

if(!

input_device){

rc=-ENOMEM;

gotoerror_alloc_dev;

}

//初始化input_device

ts->input=input_device;

input_device->name=GSLX680_I2C_NAME;

input_devi

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 经管营销 > 人力资源管理

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1