Android touch详解(2) porting drvier

Android touch详解(2) porting drvier,第1张

概述Androidtouch详解(2)portingdrvier前言了解需求电路图驱动移植1.添加touchdriver2.修改Makefile3.kconfig4..ini文件5.DTS6.修改相关权限准备code1.申请电源2.修改分辨率3.确认I2C从机地址4.申请GPIO5.睡眠和唤醒6.检查函数返回值7.debuglevelADSP1.gpi

AndroID touch详解(2)porting drvIEr前言了解需求电路图驱动移植1. 添加touch driver2. 修改Makefile3. kconfig4. .ini文件5. DTS6. 修改相关权限准备code1. 申请电源2. 修改分辨率3. 确认I2C从机地址4. 申请GPIO5. 睡眠和唤醒6. 检查函数返回值7. debug levelADSP1. gpio配置的xml2. i2c gpio权限配置

前言

  本篇文章是根据本人实际项目的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相关电源都是畅通的;

驱动移植1. 添加touch driver

  将驱动代码移植到目录" \drivers\input\touchscreen\sis_i2c_95xx " 下,“sis_i2c_95xx” 是touch驱动代码所在的文件夹,一般命名方式为:厂商缩写_(i2c/usb_)芯片型号

2. 修改Makefile
\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就不会编译连接

3. kconfig
\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配置选项被选中时,当前配置选项的提示信息才会出现,才能设置当前配置选项

4. .ini文件
\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那边的分辨率信息去进行坐标转换。
  

3. 确认I2C从机地址

  再次确认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. 检查函数返回值

  记得检查函数返回值和错误处理;示例代码为了简洁把检查返回值和错误处理都删掉了
  

7. deBUG level

  添加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所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/1046640.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-24
下一篇 2022-05-24

发表评论

登录后才能评论

评论列表(0条)

保存