1、Rockchip平台TP驱动详解综述共26页本文描述在RK3126平台上添加一个新的TP驱动(gslx680驱动)以及详细(xingx)的驱动代码信息。如有不足之处,敬请指出。1、修改dts,添加(tin ji)新的i2c设备。在arch/arm/boot/dts/rk312x-sdk-v2.2.dtsi中添加(tin ji)i2c设备的相关信息:ts40 compatible = gslX680; reg = ; wake-gpio = ; irp-gpio = ; revert_x = ; revert_y = ; ;12345678&i2c2 status = okay; /* ts55
2、 compatible = goodix,gt8xx; reg = ; touch-gpio = ; reset-gpio = ; /power-gpio = ; max-x = ; max-y = ; ;*/ ts40 compatible = gslX680; reg = ; /wake-gpio = ; irp-gpio = ; revert_x = ; revert_y = ; ; /* . */12345678910111213141516171819202122表示i2c2总线上下(shngxi)挂在了多个i2c设备。其中(qzhng)ts40是表示此i2c设备(shbi)的设备类
3、型为触摸屏,设备地址为0x40(7位地址(dzh),注意:在i2c的传输函数中,会将此地址左移一位,因此实际上gslx680的i2c设备地址为0x80)。该节点下有多个属性:1、compatible = gslX680;属性用于驱动和设备的绑定。表示特定的设备名称,此处为gslX680;2、reg = ;属性表示此设备的i2c地址为0x40,等同于40;3、wake-gpio = ;表示复位引脚使用的是GPIO0 中的GPIO_D3这个引脚,低电平有效。irp-gpio = ;表示中断引脚使用的是GPIO0中的GPIO_A2这个引脚,高电平触发。很奇怪,为什么这里没有上电的信息,以及在整个驱动
4、程序中都没有给ic上电的操作。在前面的MTK平台上的tp驱动都有上电的动作,暂时还搞不懂在RK平台上为什么没有。4、revert_x = ; revert_y = ;标记x和y是否需要翻转。在上述的信息中,可以通过of接口获取到属性对应的值。在后面的probe()函数中就会使用到。注:关于(guny)dts的详细信息可以查看ARM Linux 3.x的设备(shbi)树(Device Tree)和Device Tree Usage2、修改(xigi)Makefile、Kconfig、defconfig(1)、修改Makefile添加(tin ji)gslx680驱动在drivers/input
5、/touchscreen/Makefile中添加(tin ji)驱动:obj-$(CONFIG_TOUCHSCREEN_GSLX680) += gslx680/。只要(zhyo)当配置了CONFIG_TOUCHSCREEN_GSLX680的选项才会去编译(biny)gslx680目录(ml)下的内容。在配置内核的时候会通过make menuconfig来配置对应的选项。或者是直接在defconfig文件中强制设置该选项。注:如果不想要这么复杂,可以将该语句写成obj-y += gslx680/来强制编译该驱动。(2)、修改Kconfig添加驱动配置描述在drivers/input/touchs
6、creen/Kconfig中添加驱动配置描述:config TOUCHSCREEN_GSLX680 tristate gslX680 touchscreen driver help gslX680 touchscreen driver1234(3)、配置defconfig设置编译驱动一般在内核中会有配置好的默认的config文件供参考,可以直接修改defconfig来选择编译某个驱动。此处在arch/arm/configs/rockchip_defconfig文件中添加CONFIG_TOUCHSCREEN_GSLX680=y并将该文件拷贝到kernel目录下命名为.config即可。2、添加i
7、2c驱动#define GSLX680_I2C_NAME gslX680#define GSLX680_I2C_ADDR 0x40static const struct i2c_device_id gsl_ts_id = GSLX680_I2C_NAME, 0, ;MODULE_DEVICE_TABLE(i2c, gsl_ts_id);static struct i2c_driver gsl_ts_driver = .driver = .name = GSLX680_I2C_NAME, .owner = THIS_MODULE, ,#ifndef CONFIG_HAS_EARLYSUSPEND
8、/ .suspend = gsl_ts_suspend,/ .resume = gsl_ts_resume,#endif .probe = gsl_ts_probe, .remove = gsl_ts_remove, .id_table = gsl_ts_id,;static int _init gsl_ts_init(void) int ret; printk(=gsl_ts_init=n); ret = i2c_add_driver(&gsl_ts_driver); printk(ret=%dn,ret); return ret;static void _exit gsl_ts_exit(
9、void) printk(=gsl_ts_exit=n); i2c_del_driver(&gsl_ts_driver); return;1234567891011121314151617181920212223242526272829303132333435363738注册(zhc)名字为GSLX680_I2C_NAME的i2c驱动(q dn),即gslx680,该驱动支持(zhch)的设备名为字gsl_ts_id里的设备名称。因为(yn wi)我们在dts中已注册了一个名字为gslx680的i2c设备。因此,设备与驱动可以匹配成功并正确执行probe()函数。至于设备与驱动是如何匹配的,可
10、以参照Linux i2c子系统3、gsl_ts_probe()static int gsl_ts_probe(struct i2c_client *client,const struct i2c_device_id *id) struct gsl_ts *ts; int rc; struct device_node *np = client-dev.of_node; enum of_gpio_flags wake_flags; unsigned long irq_flags; / 检查i2c适配器的能力 printk(GSLX680 Enter %sn, _func_); if (!i2c_c
11、heck_functionality(client-adapter, I2C_FUNC_I2C) dev_err(&client-dev, I2C functionality not supportedn); return -ENODEV; / 为ts申请内核(ni h)空间 ts = kzalloc(sizeof(*ts), GFP_KERNEL); if(!ts) return -ENOMEM; printk(=kzalloc success=n); ts-client = client; i2c_set_clientdata(client, ts); ts-device_id = id-
12、driver_data; / 从设备(shbi)节点np中获取(huq)到irq和wake 的gpio的信息(xnx) ts-irq_pin=of_get_named_gpio_flags(np, irp-gpio, 0, (enum of_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-
13、dev, ts-wake_pin, (wake_flags & OF_GPIO_ACTIVE_LOW) ? GPIOF_OUT_INIT_LOW : GPIOF_OUT_INIT_HIGH, gslX680 wake pin); if(rc != 0) dev_err(&client-dev, gslX680 wake pin errorn); return -EIO; g_wake_pin = ts-wake_pin; /msleep(100); else dev_info(&client-dev, wake pin invalidn); if(gpio_is_valid(ts-irq_pi
14、n) rc = devm_gpio_request_one(&client-dev, ts-irq_pin, (irq_flags & OF_GPIO_ACTIVE_LOW) ? GPIOF_OUT_INIT_LOW : GPIOF_OUT_INIT_HIGH, gslX680 irq pin); if (rc != 0) dev_err(&client-dev, gslX680 irq pin errorn); return -EIO; else dev_info(&client-dev, irq pin invalidn); / 创建(chungjin)工作队列,申请input设备(shb
15、i) rc = gslX680_ts_init(client, ts); if(rc dev, GSLX680 init failedn); goto error_mutex_destroy; gsl_client = client; / 从设备节点中获取属性(shxng)信息 of_property_read_u32(np,revert_x,&revert_x);/sss of_property_read_u32(np,revert_y,&revert_y);/sss / 初始化IC,包括(boku)复位,测试i2c以及(yj)加载ic配置(pizh)信息 init_chip(ts-clie
16、nt); check_mem_data(ts-client); / 申请中断号 ts-irq=gpio_to_irq(ts-irq_pin); /If not defined in client 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) pri
17、ntk(KERN_ALERT Cannot allocate ts INT!ERRNO:%dn, rc); goto error_req_irq_fail; /disable_irq(ts-irq); else printk(gsl x680 irq req failn); goto error_req_irq_fail; ts-tp.tp_resume = gsl_ts_late_resume; ts-tp.tp_suspend = gsl_ts_early_suspend; tp_register_fb(&ts-tp);#ifdef CONFIG_HAS_EARLYSUSPEND ts-e
18、arly_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#ifdef GSL_MONITOR printk( gsl_ts_pr
19、obe () : queue gsl_monitor_workqueuen); 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 %sn, _func_); return 0;/exit_se
20、t_irq_mode: error_req_irq_fail: free_irq(ts-irq, ts); error_mutex_destroy: input_free_device(ts-input); kfree(ts); return rc;1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
21、93949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131(1)、自定义的数据结构(sh j ji u)gsl_ts一般在自己(zj)的驱动程序中,都会为该驱动程序封装一个数据结构,这里的gsl_ts就充当(chngdng)这种角色。在该驱动程序中自定义了一个(y )数据结构:struct gsl_ts struct i2c_client *client; struct input_dev *input; struct work_st
22、ruct work; struct workqueue_struct *wq; struct gsl_ts_data *dd; u8 *touch_data; u8 device_id; int irq; int irq_pin; int wake_pin; struct tp_device tp;#if defined(CONFIG_HAS_EARLYSUSPEND) struct early_suspend early_suspend;#endif;12345678910111213141516client表示一个(y )i2c的设备;input表示(biosh)一个输入设备;work表示
23、(biosh)一个工作,用于处理中断到来之后获取坐标等信息;wq表示一个工作队列(duli),将上面的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)、检查(jinch)i2c适配
24、器的能力在很多i2c设备(shbi)驱动程序中,一进入probe()就要检查i2c适配器的能力。现在还不清楚这么做的目的是什么(shn me)。后面会仔细的学习一下linux 的i2c子系统。(3)、获取(huq)wake和irq的引脚信息在前面配置dts说过:dts中设备节点的属性的值可以通过of_接口获取到。/ 从设备节点np中获取到irq和wake 的gpio的信息 ts-irq_pin=of_get_named_gpio_flags(np, irp-gpio, 0, (enum of_gpio_flags *)&irq_flags); ts-wake_pin=of_get_named_
25、gpio_flags(np, wake-gpio, 0, &wake_flags);123可以通过设备节点np获取到它irq-gpio这一个属性的值存放在ts-irq_pin中,以及将其flag刚到变量irq_flags中。因此我们可以知道:ts-irq_pin = GPIO_A2irq_flag = IRQ_TYPE_LEVEL_HIGH12同理,wake_pin也是一样。(4)、申请gpio并设置输出电平将irq和wake 引脚电平都设置输出低电平。(5)、gslX680_ts_initstatic int gslX680_ts_init(struct i2c_client *client
26、, struct gsl_ts *ts) struct input_dev *input_device; int rc = 0; printk(GSLX680 Enter %sn, _func_); / 配置(pizh)获取坐标信息 ts-dd = &devicests-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; / 申请空间存放(cnfng)坐标信息 ts-touch_data = kzalloc(ts-dd-data_size, GFP_KERNEL); if(!ts-touch_data) pr_err(%s: Unable to allocate memoryn, _func_); return -ENOMEM; / 申请(shnqng)一个input_dev 设备(shbi) input_device = input_allocate_device();
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1