本篇文章是根据本人实际项目的touch开发流程编写的,如有遗漏欢迎补充
了解需求在一个项目的起始阶段,首先要了解该项目要用的touch 模组或touch IC;阅读touch模组和touch Ic spec;整理touch相关的需求,包括时序要求、电压要求、I2C从机地址、wakeup的实现等。确认有无特殊的功能要求。
电路图 revIEw硬件电路图,确认touch使用的gpio只有touch使用、检查soc的spec确认中断gpio是否具有wakeup能力;I2C是否有上拉,有没有对应测试点。
确认touch需要enable的电源,一般包括,I2C上拉电源、touch IC电源、level shift电源;确保touch相关电源都是畅通的;
将驱动代码移植到目录" \drivers\input\touchscreen\sis_i2c_95xx " 下,“sis_i2c_95xx” 是touch驱动代码所在的文件夹,一般命名方式为:厂商缩写_(i2c/usb_)芯片型号
\drivers\input\touchscreen\sis_i2c_95xx\Makefile
## # Makefile for the sis95xx touchscreen drivers.#obj-$(CONfig_touchSCREEN_SIS_I2C_95XX) += sis_i2c.o
修改上级目录Makefile
obj-$(CONfig_touchSCREEN_SIS_I2C_95XX) += sis_i2c_95xx/
obj-$(CONfig_XXX) += sis_i2c.o,表示在这个目录中有一个名为sis_i2c.o的目标文件。sis_i2c.o将从sis_i2c.c 或sis_i2c.S文件编译得到
其中 $(CONfig_XXX)代表引用了CONfig_XXX变量,CONfig_XXX一般定义在.ini文件中,可以为y(编译进内核)或m(编译成模块),如果CONfig_XXX的不是y或m就不会编译连接
\drivers\input\touchscreen\sis_i2c_95xx\Kconfig
## STMicroelectronics touchscreen driver configuration#config touchSCREEN_SIS_I2C_95XX tristate "SiS95xx serIEs I2C touchscreen driver" depends on I2C help Say Y here to enable support for I2C connected ilitek touch panels. If unsure, say N. To compile this driver as a module, choose M here: the module will be called sis_i2c.
修改上级目录Kconfig
source "drivers/input/touchscreen/sis_i2c_95xx/Kconfig"
config是关键字,表示一个配置选项的开始;紧跟着的touchSCREEN_SIS_I2C_95XX是配置选项的名称。编译过程中会以arch\arm64\Kconfig为起点逐层解析Kconfig,在touchSCREEN_SIS_I2C_95XX前面加入前缀"CONfig_"
tristate表示变量类型,即"CONfig_touchSCREEN_SIS_I2C_95XX"的类型,有5种类型:bool、tristate、string、hex和int,其中 tristate和string是基本的类型
depends on:表示依赖于XXX,“depends on I2C”表示只有当I2C配置选项被选中时,当前配置选项的提示信息才会出现,才能设置当前配置选项
\mpos_config\hw_config\hw_common.ini
CONfig_touchSCREEN_SIS_I2C_95XX=y
设置CONfig_touchSCREEN_SIS_I2C_95XX的值,y为编译进内核、m为编译成模块。在编译过程中会通过Makefile、Kconfig和.ini中的定义一起结合生成.config文件。总结来说,Kconfig中去定义"CONfig_XXX"变量,Makefile中引用变量看是否编译,.ini定义变量的值。
\androID_out\ …\target\product\ …\obj\KERNEL_OBJ\ .config
CONfig_touchSCREEN_SIS_I2C_95XX=y
若没有定义变量的值,则"CONfig_XXX"的值为n(不编译)
5. DTS修改对应dts文件,通过I2C SCL和SDA的gpio确定touch挂在哪个I2C下面,设置compatible匹配的driver、addr,设置irq、reset、电源的gpio,设置pinctrl的名字和状态。
\arch\arm64\boot\dts\qcom\
&tlmm { sis_ts_default { sis_ts_reset_default: sis_ts_reset_default { mux { pins = "gpio66"; function = "gpio"; }; config { pins = "gpio66"; drive-strength = <2>; bias_disable; }; }; sis_ts_int_default: sis_ts_int_default { mux { pins = "gpio107"; function = "gpio"; }; config { pins = "gpio107"; drive-strength = <2>; bias_pull_down; }; }; sis_ts_through_default: sis_ts_through_default { mux { pins = "gpio113"; function = "gpio"; }; config { pins = "gpio113"; drive-strength = <2>; bias_disable; }; }; };};&i2c_4 { status = "okay"; /* sis touch configuration */ sis_touchscreen@5c { compatible = "sis,sis_touch"; //和驱动中保持一致 reg = <0x5c>;// I2C地址 interrupt-parent = <&tlmm>; interrupts = <107 0x0>; sis,irq-gpio = <&tlmm 107 0x00>; sis,reset-gpio = <&tlmm 66 0x00>; sis,through-gpio = <&tlmm 113 0x00>; // 电源 p5v_usb-supply = <&p5v_usb>; vregl13-supply = <&pm660_l13>; vregl11-supply = <&pm660_l11>; p3v3-supply = <&p3v3>; pinctrl-names = "sis_ts_default"; pinctrl-0 = <&sis_ts_reset_default &sis_ts_int_default &sis_ts_through_default>; status = "Disabled"; };};
注意:compatible要与kernel driver中的of_match_table中的compatible一致
static struct of_device_ID sis_ts_dt_IDs[] = { { .compatible = "sis,sis_touch" }, { }};static struct i2c_driver sis_ts_driver = { .probe = sis_ts_probe, .remove = sis_ts_remove, .ID_table = sis_ts_ID, .shutdown = sis_ts_shutdown, .driver = { .name = SIS_I2C_name, .owner = THIS_MODulE, .of_match_table = of_match_ptr(sis_ts_dt_IDs), },};
上面status = “Disabled”;表示默认Disabled,在对应项目的dts中enable。这样做一方面是可以将touch相关的dts都放在同一个dtsi中方便修改和管理;另一方面由于有时不同设备用的同一个touch驱动,虽然是同一个驱动但每个设备要求的配置可能不一样,那么我们会在dts中去区分,比如不同设备有不同的dts文件。
&i2c_4 { sis_touchscreen@5c { //定义分辨率 sis,max-x = <1919>; sis,max-y = <1079>; status = "okay"; };};
6. 修改相关权限\system\core\rootdir\ueventd.rc\system\core\rootdir\init.rc
根据实际需求定义权限
#sis/dev/sis_hydra_touch_device 0666 root root
准备code1. 申请电源申请touch用到的所有电源。找到每个regulator对应的dts name,为保证一致性,在dts中去定义电源,.c去读取dts的字串,读取后再进行电源的enable和disable;touch电源如果有专用的控制IO,则根据电路图进行配置电平。
enum sis_regulator { /*sync order to sis_regu[]*/ P3V3, USB5V, VREGL13, VREGL11,};static struct sis_regulator_data { const int index; const char *dts_name; const int voltage; struct regulator *regu_name;} sis_regu[] = { {P3V3, "p3v3", 3300000, NulL}, {USB5V, "p5v_usb", 5000000, NulL}, {VREGL13, "vregl13", 1800000, NulL}, {VREGL11, "vregl11", 1800000, NulL}, {-1, NulL, NulL, NulL},};static int sis_regulator_power_on(struct sis_ts_data *ts, bool flag){ int i, ret = 0; if(true == flag) { for(i = 0; sis_regu[i].regu_name; i++) { ret = regulator_enable(sis_regu[i].regu_name); if (ret) { dev_err(&ts->clIEnt->dev, "Failed to enable regulator: %d\n", ret); return ret; } } } else { for(i = 0; sis_regu[i].regu_name; i++) { ret = regulator_disable(sis_regu[i].regu_name); if (ret) { dev_err(&ts->clIEnt->dev, "Failed to disable regulator: %d\n", ret); return ret; } } } return ret;}static int sis_regulator_power_init(struct sis_ts_data *ts){ int i, ret = 0; for(i = 0; sis_regu[i].dts_name; i++) { sis_regu[i].regu_name = regulator_get(&ts->clIEnt->dev, sis_regu[i].dts_name); ret = ERR_ALLOC_MEM(sis_regu[i].regu_name); if (ret) { dev_err(&ts->clIEnt->dev, "regulator_get %s fail\n", sis_regu[i].dts_name); sis_regu[i].regu_name = NulL; return ret; } ret = regulator_set_voltage(sis_regu[i].regu_name, sis_regu[i].voltage, sis_regu[i].voltage); if (ret < 0) { dev_err(&ts->clIEnt->dev, "Failed to set %d\n", sis_regu[i].voltage); return ret; } } ret = sis_regulator_power_on(ts, true); if (ret) { dev_err(&ts->clIEnt->dev, "Failed to sis_regulator_power_on: %d\n", ret); return ret; } return ret;}static int sis_regulator_power_exit(struct sis_ts_data *ts){ int i; sis_regulator_power_on(ts, false); for(i = 0; sis_regu[i].dts_name; i++) { if (!IS_ERR_OR_NulL(sis_regu[i].regu_name)) { if (regulator_count_voltages(sis_regu[i].regu_name) > 0) regulator_set_voltage(sis_regu[i].regu_name, 0, sis_regu[i].voltage); regulator_put(sis_regu[i].regu_name); } } return 0;}
2. 修改分辨率static int sis_ts_probe( struct i2c_clIEnt *clIEnt, const struct i2c_device_ID *ID){ ... // 从dts中读取分辨率 if (of_property_read_u32(clIEnt->dev.of_node, "sis,max-x", &ts->max_x)) { pr_err("%s: sis touch max_x not specifIEd\n", __func__); ret = ENXIO; goto err_fb_notif_Failed; } if (of_property_read_u32(clIEnt->dev.of_node, "sis,max-y", &ts->max_y)) { pr_err("%s: sis touch max_y not specifIEd\n", __func__); ret = ENXIO; goto err_fb_notif_Failed; } ... input_set_abs_params(ts->input_dev, ABS_MT_position_X, 0, SIS_MAX_X, 0, 0); input_set_abs_params(ts->input_dev, ABS_MT_position_Y, 0, SIS_MAX_Y, 0, 0); ...}
touch这边不修改分辨率也没问题的,输入系统会根据display那边的分辨率信息去进行坐标转换。
再次确认dts和驱动中的I2C地址是否和spec中一致
4. 申请GPIO申请irq、reset 等GPIO。包括从dts读gpio名字、合法性检查、gpio request、设置输入输出、gpio free;中断gpio申请为IRQ,添加中断线程;
static int sis_ts_gpio_register(struct sis_ts_data *ts){ ts->irq_gpio = of_get_named_gpio(ts->clIEnt->dev.of_node, "sis,irq-gpio",0); if (gpio_is_valID(ts->irq_gpio)) {// 合法性检查 gpio_request(ts->irq_gpio, // 申请gpio "sis_touch_gpio_irq"); ... gpio_direction_input(ts->irq_gpio);// 设置方向为输入 } ... return ret;}
5. 睡眠和唤醒添加callback和睡眠唤醒,如果需要wakeup睡眠时不要disable中断,不要关闭影响中断的的电源,保证从硬件产生中断到soc接受中断一路畅通;
static int sis_ts_suspend(struct i2c_clIEnt *clIEnt){ int ret = 0; struct sis_ts_data *ts = i2c_get_clIEntdata(clIEnt); if (socinfo_get_tp_wakeup()) { regulator_disable(sis_regu[VREGL11].regu_name); } else { if (ts->use_irq) {#if ( liNUX_VERSION_CODE < KERNEL_VERSION (4, 2, 0) ) if ((ts->desc->irq_data.state_use_accessors & IRQD_IRQ_Disabled) == IRQ_STATUS_ENABLED)#else if ((ts->desc->irq_common_data.state_use_accessors & IRQD_IRQ_Disabled) == IRQ_STATUS_ENABLED)#endif disable_irq(ts->clIEnt->irq); } else hrtimer_cancel(&ts->timer); flush_workqueue(sis_wq); /* only flush sis_wq */ sis_regulator_power_on(ts, false); return 0;}static int sis_ts_resume(struct i2c_clIEnt *clIEnt){ int ret = 0; struct sis_ts_data *ts = i2c_get_clIEntdata(clIEnt); if (socinfo_get_tp_wakeup()) { regulator_enable(sis_regu[VREGL11].regu_name); } else { sis_regulator_power_on(ts, true); if (ts->use_irq) {#if ( liNUX_VERSION_CODE < KERNEL_VERSION (4, 2, 0) ) if ((ts->desc->irq_data.state_use_accessors & IRQD_IRQ_Disabled) == IRQ_STATUS_Disabled)#else if ((ts->desc->irq_common_data.state_use_accessors & IRQD_IRQ_Disabled) == IRQ_STATUS_Disabled)#endif enable_irq(ts->clIEnt->irq); } else hrtimer_start(&ts->timer, ktime_set(1, 0), HRTIMER_MODE_REL); } return 0;}static int fb_notifIEr_callback(struct notifIEr_block *self, unsigned long event, voID *data){ int blank; struct fb_event *evdata = data; struct sis_ts_data *ts_data = container_of(self, struct sis_ts_data, fb_notif); if (evdata && evdata->data && ts_data && event == FB_EVENT_BLANK) { blank = *(int *)(evdata->data); if (blank == FB_BLANK_POWERDOWN) sis_ts_suspend(ts_data->clIEnt); else if (blank == FB_BLANK_UNBLANK) sis_ts_resume(ts_data->clIEnt); } return 0;}
6. 检查函数返回值 记得检查函数返回值和错误处理;示例代码为了简洁把检查返回值和错误处理都删掉了
添加deBUG level,用于打印一下调试信息
/* 1: Default, 0: No log. The bigger value, the more detailed log is output. */#define CONfig_SIS_DEBUG_LEVEL (0)#define SIS_LOG_LEVEL_HIGH 2#define DEBUG_LEVEL(level, fmt, arg...) do {\ if (level <= tp_deBUG_level)\ pr_info(fmt, ##arg);\ } while (0)#define SIS_DBG(fmt, arg...) DEBUG_LEVEL(1, fmt, ##arg)
ADSP1. gpio配置的xml
该文件中对gpio配置在BootLoader时期生效,进入kernel后如果有重新配置跟随新的配置,如果没有则一直有效。
\non_hlos\boot_images\QcomPkg\Sdm660Pkg\Settings\TLMM\loader\TLMMChipset_Handheld.xml
<var_seq name="DALTLMMBSP_LowPowerCfg" type=DALPROP_DATA_TYPE_UINT32_SEQ> /* GPIO DIR, PulL, Outval Program*/ /* 0 */ DALTLMM_input | DALTLMM_PulL_DOWN | DALTLMM_OUT_LOW | DALTLMM_PRG_NO, ...
第一个参数代表gpio是输入还是输出;第二个参数代表拉高、拉低还是no pull;第三个参数当gpio设为输出时,输出高电平还是低电平;第四个参数代表对这个gpio的配置是否生效。
2. i2c gpio权限配置\non_hlos\trustzone_images\core\buses\qup_accesscontrol\honeybadger\config\QUPAC_660_Access.xml
I2C gpio权限应该为AC_HLOS
<device ID=BLSP_QUP4_DEV_ACCESS> <props name="PERIPH ID" type=DALPROP_ATTR_TYPE_UINT32> BLSP_QUP4 </props> <props name="GPIO range" type=DALPROP_ATTR_TYPE_BYTE_SEQ> 14, 15, end </props> <props name="IS_GPIO_PROTECTED" type=DALPROP_ATTR_TYPE_UINT32> 1 </props> <props name="RW_ACCESS_List" type=DALPROP_ATTR_TYPE_BYTE_SEQ> AC_HLOS, end </props> <props name="IS_PERSISTENT" type=DALPROP_ATTR_TYPE_UINT32> 0 </props> </device>
总结 以上是内存溢出为你收集整理的Android touch详解(2) porting drvier全部内容,希望文章能够帮你解决Android touch详解(2) porting drvier所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)