Android touch详解(2) touch bringup
前言
了解需求
电路图
驱动移植
1. 添加touch driver
2. 修改Makefile
3. kconfig
4. .ini文件
5. DTS
6. 修改相关权限
准备code
1. 申请电源
2. 修改分辨率
3. 确认I2C从机地址
4. 申请GPIO
5. 睡眠和唤醒
6. 检查函数返回值
7. debug level
ADSP
1. gpio配置的xml
2. i2c gpio权限配置
更新FW
调试
软件调试
硬件调试
前言
  本篇文章是根据本人实际项目的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

准备code
1. 申请电源
  申请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)

  

ADSP
1. 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>

更新FW
  拿到 touch panel 的第一时间确认FW(固件)的情况:touch panel中是否已经刷好FW,如果刷好确认FW版本;如果没刷就去更新firmware

调试
  上述工作完成并不代表 touch 就可以正常工作,那么当download 代码后发现触摸屏幕没反应,我们该怎么做呢?

软件调试
  首先 getevent 查看touch是否有事件上报,如果有待显示屏亮了后测试touch是否有效;
  如果没有事件上报,检查是否有调用中断handler;如果有调用锁定数据在哪里被拦截再去解决,如果没有调用可按照下方次序进行软件debug:
  1) 查看log看初始化是否成功;
  2) 检查touch需要的电源是否都正常上电;(sys/kernel/debug/regultor)
  3) 检查中断、复位、I2C信号电压、空闲状态是否正常;(cat sys/kernel/debug/gpio)
  4) I2C gpio func是否正常;
  5) 检查时序是否满足要求;
  6) 检查touch FW版本是否正确;

硬件调试
  如果软件调试都没有问题,但 touch 依旧没有反应,进入硬件debug:
  1) 示波器测中断引脚,确认电平是否正确,当触摸 panel 时是否有中断,如果有但是soc没收到,那就去看信号在哪里被拦截了
  2)若触摸屏幕没有中断,则去检查touch的供电电源、确认上电时序;
  3)硬件确认level shift两边的中断、复位、I2C信号电压、空闲状态是否正常;
  4)硬件确认touch需要的电源是否都正常上电;
  5)考虑是否是单体问题,更换touch panel试试
————————————————
版权声明:本文为CSDN博主「永暮十三」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_44904296/article/details/112826283

Android touch bringup相关推荐

  1. Android touch详解(2) touch bringup

    Android touch详解(2) touch bringup 前言 了解需求 电路图 驱动移植 1. 添加touch driver 2. 修改Makefile 3. kconfig 4. .ini ...

  2. Android Touch事件传递机制 二:单纯的(伪生命周期) 这个清楚一点

    转载于:http://blog.csdn.net/yuanzeyao/article/details/38025165 在前一篇文章中,我主要讲解了Android源码中的Touch事件的传递过程,现在 ...

  3. android touch screen keyboard input移植记录

    android touch screen keyboard input移植记录  仅仅是作为记录: Andorid 的 touchscreen 事件必须要有  BTN_TOUCH 才可以. 所以初始化 ...

  4. Android Touch事件原理加实例分析

    Android中有各种各样的事件,以响应用户的操作.这些事件可以分为按键事件和触屏事件.而Touch事件是触屏事件的基础事件,在进行Android开发时经常会用到,所以非常有必要深入理解它的原理机制. ...

  5. Android Touch事件传递机制解析 (推荐)

    最近新闻列表里的下拉 down up  move 等等让我十分头疼 ,无意间看到了一篇非常不错的帖子,转载如下: 开篇语:最近程序在做一个小效果,要用到touch,结果整得云里面雾里的,干脆就好好把a ...

  6. Android Touch事件传递机制 二:单纯的(伪生命周期)

    转载于:http://blog.csdn.net/yuanzeyao/article/details/38025165 在前一篇文章中,我主要讲解了Android源码中的Touch事件的传递过程,现在 ...

  7. 详解Android Touch事件的传递机制

    1.基础知识 (1) 所有Touch事件都被封装成了MotionEvent对象,包括Touch的位置.时间.历史记录以及第几个手指(多指触摸)等. (2) 事件类型分为ACTION_DOWN, ACT ...

  8. android touch事件无反应,触摸屏 无响应

    (609条消息) android touch事件无反应,android的touch事件分发响应机制_蒙娜lisa的博客-CSDN博客 (609条消息) android touch事件无反应,移动端to ...

  9. Android Touch事件分发—拦截—处理

    Android Touch事件分发(dispatchTouchEvent)-拦截(onInterceptTouchEvent)-处理(onTouchEvent) 转自:http://www.cnblo ...

最新文章

  1. Spring|AOP
  2. windows通过putty连接虚拟机Fedora
  3. 【TensorFlow2.0】数据读取与使用方式
  4. CanalAdapter启动过程分析(源码详解)
  5. 微软:明明修复了Bug,你们还把我骂上热搜?
  6. .NET或将引入类型类和扩展
  7. 【CCF】201712-2游戏
  8. Linux SSH使用公钥私钥实现免登陆 以及 登陆失败、公钥失效的处理转
  9. 聚焦2017博鳌亚洲论坛:数据安全仍是互联网金融的重中之重
  10. php环境配置详细教程,图文教程:php环境全部配置
  11. H5+CSS3面试题
  12. 【转】ACM各种WA的说明及可能的原因
  13. 转贴自圣骑士wind:Google Maps Android API V2的使用及问题解决
  14. Python下探究随机数的产生原理和算法
  15. RBAC权限框架_MVC权限框架
  16. pwc(普华永道)招聘.net
  17. 机器学习中文资源合集
  18. 图片压缩 in Android
  19. 智课雅思词汇---二十四、形容词后缀-al-ial-ar-ary-ic-id-ish-ile-ine-oid-ory
  20. python jupyter notebook 多个excel文档合并

热门文章

  1. Flask的简单介绍及使用方法简介
  2. 什么样的学计算机能找到好工作
  3. 电脑重装系统变成一个C盘,其他D,E,F盘数据消失都没有了-艾奇恢复软件
  4. css 实现带弧度的三角
  5. Crack:Inobitec DICOM Viewer 2.9.0 中文版
  6. 阿里云SSL证书到期怎么办
  7. HTML——我们学习过的标签有哪些?
  8. linux启动nfs守护进程,linux下搭建nfs共享并实现开机自动挂载的具体操作
  9. 量子计算机可不可以穿越时空,美俄专家新发现:量子世界时间可以停止!未来穿越时空有希望?...
  10. iOS Scrollview 滚动结束