下面以汇顶gt9xx_driver.c 驱动为例讲解TP 驱动的整个关键点,本篇只讲TP 驱动本身的代码,

在驱动代码涉及的方法技术,因为每一个都牵涉linux内核的设计和知识,后面会逐个展开深入讲解。

首先,我们来总体看下TP 驱动代码初始化流程:


MTK kernel-4.14 TP 驱动初始化和部分工作流程main.cmain.cgt9xx_driver.cgt9xx_driver.cmtk_tpd.cmtk_tpd.ci2c.hi2c.hkthread.hkthread.hwait.hwait.hgt9xx_update.cgt9xx_update.cdo_initcallsmodule_inittpd_driver_inittpd_get_dts_infotpd_driver_addtpd_local_initi2c_add_drivertpd_i2c_probetpd_power_ongtp_init_panelkthread_runtouch_event_handlerwait_event_interruptibletpd_irq_registrationtpd_interrupt_handlerwake_up_interruptiblegup_init_update_proc

首先看TP 驱动模块初始化:

1

2

3

4

5

6

7

8

9

static int __init tpd_driver_init(void)

{

        GTP_INFO("GT9 series touch panel driver init");

        tpd_get_dts_info();

        if (tpd_driver_add(&tpd_device_driver) < 0)

                GTP_INFO("add generic driver failed");

                return 0;

}

请注意这个tpd_driver_init 是一个__init 修饰的函数,说明这个函数在编译时会被放到跟其他__init 修饰的函数放到一起,
在系统初始化,一旦内核启动后,就释放这些东西。一般用__init修饰的变量或者函数会编译到专门的一个段里面去,
这个段的数据和函数只有在kernel初始化的时候会被调用,以后一定不会被使用,kernel可能会在以后的某个时候释放掉这个段所占用的内存,
给别的地方使用,是不是设计很巧妙? 感兴趣的同学可以继续深入分析看看。

那么,我们看到这个函数主要就做了两件事情:
(1)是获取这个TP 所有的DTS 信息,我们看看这个代码的实现:

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

void tpd_get_dts_info(void)

{

        struct device_node *node1 = NULL;

        int key_dim_local[16], i, ret;

        node1 = of_find_matching_node(node1, touch_of_match);

        if (node1) {

                ret = of_property_read_u32(node1, "tpd-max-touch-num", &tpd_dts_data.touch_max_num);

                if (ret != 0)

                        TPD_DEBUG("tpd-max-touch-num not found\n");

                ret = of_property_read_u32(node1, "use-tpd-button", &tpd_dts_data.use_tpd_button);

                if (ret != 0)

                        TPD_DEBUG("use-tpd-button not found\n");

                else

                        TPD_DEBUG("[tpd]use-tpd-button = %d\n", tpd_dts_data.use_tpd_button);

                ret = of_property_read_u32_array(node1, "tpd-resolution",

                        tpd_dts_data.tpd_resolution, ARRAY_SIZE(tpd_dts_data.tpd_resolution));

                if (ret != 0)

                        TPD_DEBUG("tpd-resolution not found\n");

                if (tpd_dts_data.use_tpd_button) {

                        ret = of_property_read_u32(node1, "tpd-key-num", &tpd_dts_data.tpd_key_num);

                        if (ret != 0)

                                TPD_DEBUG("tpd-key-num not found\n");

                        ret = of_property_read_u32_array(node1, "tpd-key-local",

                                tpd_dts_data.tpd_key_local, ARRAY_SIZE(tpd_dts_data.tpd_key_local));

                        if (ret != 0)

                                TPD_DEBUG("tpd-key-local not found\n");

                        ret = of_property_read_u32_array(node1, "tpd-key-dim-local",

                                key_dim_local, ARRAY_SIZE(key_dim_local));

                        if (ret != 0)

                                TPD_DEBUG("tpd-key-dim-local not found\n");

                        memcpy(tpd_dts_data.tpd_key_dim_local, key_dim_local, sizeof(key_dim_local));

                        for (i = 0; i < 4; i++) {

                                pr_debug("[tpd]key[%d].key_x = %d\n", i, tpd_dts_data.tpd_key_dim_local[i].key_x);

                                pr_debug("[tpd]key[%d].key_y = %d\n", i, tpd_dts_data.tpd_key_dim_local[i].key_y);

                                pr_debug("[tpd]key[%d].key_W = %d\n", i, tpd_dts_data.tpd_key_dim_local[i].key_width);

                                pr_debug("[tpd]key[%d].key_H = %d\n", i, tpd_dts_data.tpd_key_dim_local[i].key_height);

                        }

                }

                ret = of_property_read_u32(node1, "tpd-filter-enable", &tpd_dts_data.touch_filter.enable);

                if (ret != 0)

                        TPD_DEBUG("tpd-filter-enable not found\n");

                if (tpd_dts_data.touch_filter.enable) {

                        ret = of_property_read_u32(node1, "tpd-filter-pixel-density",

                                                 &tpd_dts_data.touch_filter.pixel_density);

                        if (ret != 0)

                                TPD_DEBUG("tpd-filter-pixel-density not found\n");

                        ret = of_property_read_u32_array(node1, "tpd-filter-custom-prameters",

                                (u32 *)tpd_dts_data.touch_filter.W_W, ARRAY_SIZE(tpd_dts_data.touch_filter.W_W));

                        if (ret != 0)

                                TPD_DEBUG("tpd-filter-custom-prameters not found\n");

                        ret = of_property_read_u32_array(node1, "tpd-filter-custom-speed",

                                tpd_dts_data.touch_filter.VECLOCITY_THRESHOLD,

                                ARRAY_SIZE(tpd_dts_data.touch_filter.VECLOCITY_THRESHOLD));

                        if (ret != 0)

                                TPD_DEBUG("tpd-filter-custom-speed not found\n");

                }

                memcpy(&tpd_filter, &tpd_dts_data.touch_filter, sizeof(tpd_filter));

                TPD_DEBUG("[tpd]tpd-filter-enable = %d, pixel_density = %d\n",

                                        tpd_filter.enable, tpd_filter.pixel_density);

                tpd_dts_data.tpd_use_ext_gpio = of_property_read_bool(node1, "tpd-use-ext-gpio");

                ret = of_property_read_u32(node1, "tpd-rst-ext-gpio-num", &tpd_dts_data.rst_ext_gpio_num);

                if (ret != 0)

                        TPD_DEBUG("tpd-rst-ext-gpio-num not found\n");

        else {

                pr_err("[tpd]%s can't find touch compatible custom node\n", __func__);

        }

}

这个函数看似很长,其实就是在读取我们定义在dts文件里面跟这个TP相关的数据,包括最大手指数:tpd-max-touch-num,对应的数据上报分辨率:tpd_resolution

复位GPIO 配置:tpd-rst-ext-gpio-num等信息。

我们来看一个相关dts配置信息:

arch/arm64/boot/dts/mediatek/tb8788p1_64_bsp.dts

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

&touch {

        //tpd-resolution = <800 1280>;

        tpd-resolution = <1920 1200>;

        use-tpd-button = <0>;

        tpd-key-num = <3>;

        tpd-key-local= <139 172 158 0>;

        tpd-key-dim-local = <90 883 100 40 230 883 100 40 370 883 100 40 0 0 0 0>;

        tpd-max-touch-num = <5>;

        tpd-filter-enable = <1>;

        tpd-filter-pixel-density = <146>;

        tpd-filter-custom-prameters = <0 0 0 0 0 0 0 0 0 0 0 0>;

        tpd-filter-custom-speed = <0 0 0>;

        pinctrl-names = "default""state_eint_as_int""state_eint_output0""state_eint_output1",

                "state_rst_output0""state_rst_output1";

        pinctrl-0 = <&ctp_pins_default>;

        pinctrl-1 = <&ctp_pins_eint_as_int>;

        pinctrl-2 = <&ctp_pins_eint_output0>;

        pinctrl-3 = <&ctp_pins_eint_output1>;

        pinctrl-4 = <&ctp_pins_rst_output0>;

        pinctrl-5 = <&ctp_pins_rst_output1>;

        status = "okay";

(2)第二件事情,将这个tpd 的驱动加入到内核里面来:

tpd_driver_add(&tpd_device_driver)

1

2

3

4

5

6

static struct tpd_driver_t tpd_device_driver = {

                .tpd_device_name = "gt9xx",

                .tpd_local_init = tpd_local_init,

                .suspend = tpd_suspend,

                .resume = tpd_resume,

};

这个driver 结构体里面定义了这个触摸屏的真正初始化函数,以及他的睡眠和唤醒函数,

另外tpd_device_name 名字为gt9xx,这个为以后识别设备,然后加载驱动提供的名字匹配。

同时,如果看了tpd_driver_add 代码实现,就能发下这个driver会被加入到tpd_driver_list 中。

接下来我们分析TP的真正初始化:tpd_local_init

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

static int tpd_local_init(void)

{

        int retval;

        gtp_workqueue = create_workqueue("gtp-workqueue");

        clk_tick_cnt = 2 * HZ;   /* HZ: clock ticks in 1 second generated by system */

#ifdef CONFIG_GTP_ESD_PROTECT

        GTP_DEBUG("Clock ticks for an esd cycle: %d", clk_tick_cnt);

        INIT_DELAYED_WORK(>p_esd_check_work, gtp_esd_check_func);

        spin_lock_init(&esd_lock);      /* 2.6.39 & later */

#endif

#ifdef CONFIG_GTP_CHARGER_DETECT

        if( (KERNEL_POWER_OFF_CHARGING_BOOT != get_boot_mode() ) && ( LOW_POWER_OFF_CHARGING_BOOT != get_boot_mode() )  )

 {

printk("###########cwj1*********");

        gtp_charger_workqueue = create_workqueue("gtp-charger-workqueue");

        clk_tick_cnt1 = 2 * HZ;  /* HZ: clock ticks in 1 second generated by system */

        INIT_DELAYED_WORK(>p_charger_check_work, gtp_charger_check_func);

        queue_delayed_work(gtp_charger_workqueue, >p_charger_check_work, clk_tick_cnt1);

 }

#endif

#ifdef CONFIG_GTP_SUPPORT_I2C_DMA

        gpDMABuf_va = (u8 *)dma_alloc_coherent(NULL, GTP_DMA_MAX_TRANSACTION_LENGTH,

                        &gpDMABuf_pa, GFP_KERNEL);

        if (!gpDMABuf_va)

                GTP_INFO("[Error] Allocate DMA I2C Buffer failed!\n");

        memset(gpDMABuf_va, 0, GTP_DMA_MAX_TRANSACTION_LENGTH);

#endif

#if 1

    TPD_DMESG("set reg voltage!\n");

        tpd->reg = regulator_get(tpd->tpd_dev, "vtouch");

        retval = regulator_set_voltage(tpd->reg, 28000002800000);

        if (retval != 0) {

                TPD_DMESG("Failed to set reg-vgp6 voltage: %d\n", retval);

                return -1;

        }

#endif

        if (i2c_add_driver(&tpd_i2c_driver) != 0) {

                GTP_INFO("unable to add i2c driver.");

                return -1;

        }

        if (tpd_load_status == 0)       {

                GTP_INFO("add error touch panel driver.");

                i2c_del_driver(&tpd_i2c_driver);

                return -1;

        }

        input_set_abs_params(tpd->dev, ABS_MT_TRACKING_ID, 0, (GTP_MAX_TOUCH-1), 00);

        if (tpd_dts_data.use_tpd_button) {

                /*initialize tpd button data*/

                tpd_button_setting(tpd_dts_data.tpd_key_num, tpd_dts_data.tpd_key_local,

                tpd_dts_data.tpd_key_dim_local);

        }

#if (defined(TPD_WARP_START) && defined(TPD_WARP_END))

        TPD_DO_WARP = 1;

        memcpy(tpd_wb_start, tpd_wb_start_local, TPD_WARP_CNT * 4);

        memcpy(tpd_wb_end, tpd_wb_start_local, TPD_WARP_CNT * 4);

#endif

#if (defined(TPD_HAVE_CALIBRATION) && !defined(TPD_CUSTOM_CALIBRATION))

        memcpy(tpd_calmat, tpd_def_calmat_local, 8 4);

        memcpy(tpd_def_calmat, tpd_def_calmat_local, 8 4);

#endif

        /* set vendor string */

        tpd->dev->id.vendor = 0x00;

        tpd->dev->id.product = tpd_info.pid;

        tpd->dev->id.version = tpd_info.vid;

        GTP_INFO("end %s, %d", __func__, __LINE__);

        tpd_type_cap = 1;

        return 0;

}

我们看到这个代码里面写了很多个宏,

GTP_ESD_PROTECT 是用来打开 TP 静电保护功能的,这个功能的主要作用是在TP 遇到高电压的静电时能够恢复工作,而不会影响使用。

如果对这个静电机制感兴趣,可以再深入研究,后面我再开一篇文章讲解。

第二个宏CONFIG_GTP_CHARGER_DETECT,这个宏看起来是用来充电情况下保证TP工作的,这应该是跟具体TP有关,因为有的TP在

工作时会受充电器影响,所以这个驱动中加了这个充电检测机制。

第三个宏GTP_SUPPORT_I2C_DMA ,这个主要是申请I2C DMA的空间,用户I2C 传输数据,这个指向的是直接虚拟地址,有利于提高传输数据效率。

接下来我们看到 regulator_set_voltage(tpd->reg, 2800000, 2800000);

这个是设置TP 的工作电压,这个代码看起来是后面加的,正常是把这个配置放DTS文件就好了,不会直接放到驱动初始化。

再来看驱动真正的注册I2C 驱动,因为大部分TP 设备都是挂着I2C 总线上面,所以驱动里面都是注册I2C驱动

i2c_add_driver(&tpd_i2c_driver)

i2c_add_driver 是一个宏定义在i2c.h中,其真正的实现在根据平台需要自己的实现中,有兴趣的可以再继续深入研究。

我们来关注tpd_i2c_driver 本身这个结构体的情况:

1

2

3

4

5

6

7

8

9

10

11

12

13

static struct i2c_driver tpd_i2c_driver = {

        .driver = {

                .name = "gt9xx",

#ifdef CONFIG_OF

                .of_match_table = of_match_ptr(gt9xx_dt_match),

#endif

                },

        .probe = tpd_i2c_probe,

        .remove = tpd_i2c_remove,

        .detect = tpd_i2c_detect,

        .id_table = tpd_i2c_id,

        .address_list = (const unsigned short *) forces,

};

注意,这个是i2c driver,跟上面讲解的tpd devce driver不是同一个!

这个结构体里面name同样是用来匹配设备的,另外还有一个of_match_table是用来匹配dts数据信息的

tpd_i2c_probe 是这个tpd i2c 真正的初始化函数,tpd_i2c_detect主要写入tpd驱动的类型。

static const struct i2c_device_id tpd_i2c_id[] = {{"gt9xx", 0}, {} };

id_table 是用来列出支持这个驱动的设备列表,与name的意义相似。

接下来我们讲讲这个i2c 驱动的初始化:tpd_i2c_probe

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

static s32 tpd_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id)

{

                u16 version_info;

#ifdef CONFIG_GTP_HAVE_TOUCH_KEY

                s32 idx = 0;

#endif

                s32 err = 0;

                s32 ret = 0;

#ifdef CONFIG_GTP_PROXIMITY

                struct hwmsen_object obj_ps;

#endif

        if( (KERNEL_POWER_OFF_CHARGING_BOOT == get_boot_mode() ) || ( LOW_POWER_OFF_CHARGING_BOOT == get_boot_mode() )  )

        {

            goto out;

        }

        i2c_client_point = client;

        ret = tpd_power_on(client);

        if (ret < 0) {

                GTP_ERROR("I2C communication ERROR!");

                goto out;

        }

        ret = gtp_read_version(client, &version_info);

        if (ret < 0) {

                GTP_ERROR("Read version failed.");

        }

        ret = gtp_init_panel(client);

        if (ret < 0) {

                GTP_ERROR("GTP init panel failed.");

                goto out;

        }

        GTP_DEBUG("gtp_init_panel success");

        /* Create proc file system */

        gt91xx_config_proc = proc_create(GT91XX_CONFIG_PROC_FILE, 0660, NULL, >_upgrade_proc_fops);

        if (gt91xx_config_proc == NULL) {

                GTP_ERROR("create_proc_entry %s failed", GT91XX_CONFIG_PROC_FILE);

                goto out;

        }

#ifdef CONFIG_GTP_CREATE_WR_NODE

        init_wr_node(client);

#endif

        thread = kthread_run(touch_event_handler, 0, TPD_DEVICE);

        if (IS_ERR(thread)) {

                err = PTR_ERR(thread);

                GTP_ERROR(TPD_DEVICE " failed to create kernel thread: %d", err);

                goto out;

        }

#ifdef CONFIG_GTP_HAVE_TOUCH_KEY

        for (idx = 0; idx < GTP_MAX_KEY_NUM; idx++)

                input_set_capability(tpd->dev, EV_KEY, touch_key_array[idx]);

#endif

#ifdef CONFIG_GTP_WITH_HOVER

    gtp_pen_init();

#endif

#ifdef CONFIG_GTP_GESTURE_WAKEUP

        gtp_extents_init();

        input_set_capability(tpd->dev, EV_KEY, KEY_F2);

        input_set_capability(tpd->dev, EV_KEY, KEY_F3);

#endif

#ifdef CONFIG_GTP_WITH_PEN

        /* pen support */

        __set_bit(BTN_TOOL_PEN, tpd->dev->keybit);

        __set_bit(INPUT_PROP_DIRECT, tpd->dev->propbit);

#endif

        msleep(50);

        tpd_irq_registration();

        /*gtp_irq_enable();*/

        //enable_irq(touch_irq);

    msleep(50);

#ifdef CONFIG_GTP_ESD_PROTECT

        gtp_esd_switch(client, SWITCH_ON);

#endif

#ifdef CONFIG_GTP_AUTO_UPDATE

        ret = gup_init_update_proc(client);

        if (ret < 0)

        {

                GTP_ERROR("Create update thread error.");

        }

#endif

#ifdef CONFIG_GTP_PROXIMITY

        /* obj_ps.self = cm3623_obj; */

        obj_ps.polling = 0;                              /* 0--interrupt mode;1--polling mode; */

        obj_ps.sensor_operate = tpd_ps_operate;

        err = hwmsen_attach(ID_PROXIMITY, &obj_ps);

        if (err)

                GTP_ERROR("hwmsen attach fail, return:%d.", err);

#endif

        tpd_load_status = 1;

        GTP_INFO("%s, success run Done", __func__);

        return 0;

out:

        //gtp_free_gpio_res();

        return -1;

}

首先我们看到 宏CONFIG_GTP_PROXIMITY ,这个宏意思就是这个TP是否支持距离传感功能,如果支持这个功能,就会在后面通过hwmsen_attach(ID_PROXIMITY, &obj_ps)来注册一个

ID 为ID_PROXIMITY 的sensor到MTK 设备驱动中,但大部分情况下,我们是用的额外的psensor,这里暂时不做这个psensor研究。

接下来我们看到tpd_power_on 给tp IC上电的流程:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

static int tpd_power_on(struct i2c_client *client)

{

                int ret = 0;

                int reset_count = 0;

reset_proc:

        gtp_gpio_output(GTP_IRQ_GPIO, 0);

        gtp_gpio_output(GTP_RST_GPIO, 0);

        msleep(20);

        /* power on, need confirm with SA */

        GTP_INFO("turn on power reg-vgp\n");

    #if 1

        ret = regulator_enable(tpd->reg);

        if (ret != 0)

                TPD_DMESG("Failed to enable reg-vgp6: %d\n", ret);

    #endif

    ... ...  //省略后续部分

这个函数就是对一些GPIO进行操作,比如复位GPIO,使能IRQ的GPIO,同时使能一些regulator来打开电流。

gtp_init_panel 这个 真正的初始化TP IC 的,这个函数的主要就是对TP的一些寄存器进行设置操作,这部分主要由TP FAE来提供,在没有规格书的情况下是无法进行修改的。

创建一个内核线程:

thread = kthread_run(touch_event_handler, 0, TPD_DEVICE);

这名为TPD_DEVICE线程thread是为了处理中断传来的数据,

需要注意的是,判断thread是否有效需要用IS_ERR()来判断,而不是简单的使用(thread == NULL)判断。

我们看看这个线程的真正实现:

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

132

133

134

135

136

static int touch_event_handler(void *unused)

{

                struct sched_param param = { .sched_priority = 4 };

                u8      end_cmd[3] = {GTP_READ_COOR_ADDR >> 8, GTP_READ_COOR_ADDR & 0xFF0};

                u8      point_data[2 1 8 * GTP_MAX_TOUCH + 1] = {

                        GTP_READ_COOR_ADDR >> 8, GTP_READ_COOR_ADDR & 0xFF};

                u8      touch_num = 0, finger = 0, key_value = 0, *coor_data = NULL;

                static u8 pre_touch, pre_key;

#ifdef CONFIG_GTP_WITH_PEN

                static u8 pre_pen;

#endif

                s32 input_x = 0, input_y = 0, input_w = 0;

                s32 id = 0, i = 0, ret = -1;

#ifdef CONFIG_GTP_WITH_HOVER

                u8 pen_active = 0;

                static u8 pre_pen = 0;

#endif

              u8 pre_finger = 0;

                u8 dev_active = 0;

#ifdef CONFIG_HOTKNOT_BLOCK_RW

                u8 hn_state_buf[10] = {(u8)(GTP_REG_HN_STATE >> 8),

                                (u8)(GTP_REG_HN_STATE & 0xFF), 0};

                u8 hn_pxy_state = 0, hn_pxy_state_bak = 0;

                u8 hn_paired_cnt = 0;

#endif

        sched_setscheduler(current, SCHED_RR, ¶m);

        do {

                set_current_state(TASK_INTERRUPTIBLE);

                if (tpd_eint_mode) {

                        wait_event_interruptible(waiter, tpd_flag != 0);

                        tpd_flag = 0;

                        msleep(1);  //Neostra huangjialong 170823

                else {

                        msleep(tpd_polling_time);

                }

                set_current_state(TASK_RUNNING);

                mutex_lock(&i2c_access);

        //      gtp_irq_disable();

#ifdef CONFIG_GTP_GESTURE_WAKEUP

                if (gesture_data.enabled) {

                        ret = gesture_event_handler(tpd->dev);

                        if (ret > 0) { /* event handled */                                mutex_unlock(&i2c_access);

                                continue;

                        }

                }

#endif

                if (tpd_halt || gtp_resetting || gtp_loading_fw) {

                        GTP_DEBUG("Interrupt exit,halt:%d,reset:%d,ld_fw:%d",

                                tpd_halt, gtp_resetting, gtp_loading_fw);

                        goto exit_unlock;

                }

                ret = gtp_i2c_read(i2c_client_point, point_data, 12);

                if (ret < 0) {

                                GTP_ERROR("I2C transfer error. errno:%d\n ", ret);

                                goto exit_unlock;

                }

                finger = point_data[GTP_ADDR_LENGTH];

#ifdef CONFIG_GTP_COMPATIBLE_MODE

                if ((finger == 0x00) && (CHIP_TYPE_GT9F == gtp_chip_type)) {

                        u8 rqst_data[3] = {(u8)(GTP_REG_RQST >> 8),

                                                        (u8)(GTP_REG_RQST & 0xFF), 0};

                        ret = gtp_i2c_read(i2c_client_point, rqst_data, 3);

                        if (ret < 0) {

                                GTP_ERROR("I2C transfer error. errno:%d\n ", ret);

                                goto exit_unlock;

                        }

                        switch (rqst_data[2]&0x0F) {

                        case GTP_RQST_BAK_REF:

                                GTP_INFO("Request Ref.");

                                ret = gtp_bak_ref_proc(i2c_client_point, GTP_BAK_REF_SEND);

                                if (SUCCESS == ret) {

                                        GTP_INFO("Send ref success.");

                                        rqst_data[2] = GTP_RQST_RESPONDED;

                                        gtp_i2c_write(i2c_client_point, rqst_data, 3);

                                }

                                goto exit_work_func;

                        case GTP_RQST_CONFIG:

                                GTP_INFO("Request Config.");

                                ret = gtp_send_cfg(i2c_client_point);

                                if (ret < 0) {

                                        GTP_ERROR("Send config error.");

                                else {

                                       GTP_INFO("Send config success.");

                                        rqst_data[2] = GTP_RQST_RESPONDED;

                                        gtp_i2c_write(i2c_client_point, rqst_data, 3);

                                }

                                goto exit_work_func;

                        case GTP_RQST_MAIN_CLOCK:

                                GTP_INFO("Request main clock.");

                                rqst_processing = 1;

                                ret = gtp_main_clk_proc(i2c_client_point);

                                if (SUCCESS == ret) {

                                        GTP_INFO("Send main clk success.");

                                        rqst_data[2] = GTP_RQST_RESPONDED;

                                        gtp_i2c_write(i2c_client_point, rqst_data, 3);

                                        rqst_processing = 0;

                                }

                                goto exit_work_func;

                        case GTP_RQST_RESET:

                                GTP_INFO("Request Reset.");

                                mutex_unlock(&i2c_access);

                                gtp_recovery_reset(i2c_client_point);

                                goto exit_work_func;

                        default:

                                break;

                        }

                }

#endif

#ifdef CONFIG_GTP_HOTKNOT

                if (finger == 0x00 && gtp_hotknot_enabled) {

                        u8 rqst_data[3] = {(u8)(GTP_REG_RQST >> 8),

                                                                (u8)(GTP_REG_RQST & 0xFF), 0};

                        ret = gtp_i2c_read(i2c_client_point, rqst_data, 3);

                        if (ret < 0) {

                                GTP_ERROR("I2C transfer error. errno:%d\n ", ret);

                                goto exit_unlock;

                        }

                        if ((rqst_data[2] & 0x0F) == GTP_RQST_HOTKNOT_CODE) {

                                GTP_INFO("Request HotKnot Code.");

                                gup_load_hotknot_fw();

                                goto exit_unlock;

                        }

                }

#endif

... ...

这个函数非常长,它的主要功能就是当中断发生时,读取坐标、上报坐标、手势识别、按键等等信息。

再关注probe函数里面的 tpd_irq_registration 中断注册函数

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

static int tpd_irq_registration(void)

{

        struct device_node *node = NULL;

        unsigned long irqf_val = 0;

        int ret = 0;

        tpd_gpio_as_int(GTP_INT_PORT);

        msleep(50);

        //node = of_find_compatible_node(NULL, NULL, "mediatek,cap_touch");

        node = of_find_matching_node(node, touch_of_match);

        if (node) {

                /*touch_irq = gpio_to_irq(tpd_int_gpio);*/

                touch_irq = irq_of_parse_and_map(node, 0);

                irqf_val = !int_type ? IRQF_TRIGGER_RISING : IRQF_TRIGGER_FALLING;

                ret = request_irq(touch_irq, tpd_interrupt_handler,

                                IRQF_TRIGGER_FALLING, "TOUCH_PANEL-eint", NULL);

                if (ret < 0)

                        GTP_ERROR("tpd request_irq IRQ LINE NOT AVAILABLE!.");

        else {

                GTP_ERROR("[%s] tpd request_irq can not find touch eint device node!.", __func__);

        }

        return ret;

}

这中断的就是TP 工作时,中断的处理,它的处理函数为:

tpd_interrupt_handler

1

2

3

4

5

6

7

static irqreturn_t tpd_interrupt_handler(int irq, void *dev_id)

{

        TPD_DEBUG_PRINT_INT;

        tpd_flag = 1;

        wake_up_interruptible(&waiter);

        return IRQ_HANDLED;

}

这个中断处理函数是通过一个 waiter 来处理的,它是一个wait 工作队列

static DECLARE_WAIT_QUEUE_HEAD(waiter);

TP 固件自动升级函数:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

u8 gup_init_update_proc(struct i2c_client *client)

{

                struct task_struct *thread = NULL;

                GTP_INFO("Ready to run auto update thread");

#ifdef CONFIG_GTP_COMPATIBLE_MODE

                if (CHIP_TYPE_GT9F == gtp_chip_type)

                        thread = kthread_run(gup_update_proc, "update""fl_auto_update");

                else

#endif

                        thread = kthread_run(gup_update_proc, (void *)NULL, "guitar_update");

                if (IS_ERR(thread)) {

                        GTP_ERROR("Failed to create update thread.\n");

                        return -1;

                }

                return 0;

也是通过一个thread来进行固件升级,真正的升级流程放在了gup_update_proc函数中。

到这里,所有的TP 主要功能函数都讲解了,具体每个细节后续再通过其他文档补充说明。

最后,我们再来关注一些其他细节情况:

1、tpd_down()和tpd_up() 这两个函数通过input子系统上报坐标以及上报手指抬起的动;

2、tpd_suspend()和tpd_resume() 关于休眠和唤醒的内容根据ic的特性设置。如休眠的时候需要关闭中断、配置进入休眠模式。另外唤醒的复位设置也需要关注;

3、汇顶还实现了gtp_i2c_test 来测试TP设备与I2C 通信是否正常;

4、GTP_MAX_TOUCH 可以限定TP 最大支持touch数量;

5、gtp_reset_guitar 可用来对TP的复位,一般在待机唤醒和ESD 静电等其他容易出现异常的情况下使用。

MTK 平台TP 驱动相关推荐

  1. MTK平台TP驱动框架解析

    一,TP驱动代码的组成 MTK平台TP驱动主要包括两处文件: 1,kernel-3.18\drivers\input\touchscreen\mediatek\mtk_tpd.c 平台设备驱动,主要为 ...

  2. MTK 平台TP调试遇坑

    #前言 最近在调试我们项目上的TP驱动,奈何一直不能使用,而且这个项目的硬件确定是没有问题的「这个是前提」,我们在软件上提升了SDK基线,在之前的基线版本上软件是没有问题的. 然后我就赶紧检查了两个方 ...

  3. MTK 驱动(69)---MTK平台 电池驱动相关

    MTK平台 电池驱动相关 MTK平台 电池驱动 一.相关代码路径(mt6755/Android6.0) alps/kernel-3.18/drivers/misc/mediatek/include/m ...

  4. MTK平台闪光灯驱动分析

    MTK平台闪光灯驱动分析   以前没写过博客,总想写着来着,把之前学到的做过的东西都记录下来,但是一直没有时间也没那么大的决心.这次趁着刚换工作,正在学习熟悉平台不是太忙的机会,把自己总结的文档写下来 ...

  5. mtk平台lcd驱动移植

    mtk平台lcd驱动分为两个部分:lk和kernel.两部分基本流程相同,除了GPIO操作及头文件. 1. lk:需要修改的目录 vendor/mediatek/proprietary/bootabl ...

  6. MTK 驱动---MTK平台 电池驱动相关

    MTK平台 电池驱动 一.相关代码路径(mt6755/Android6.0) alps/kernel-3.18/drivers/misc/mediatek/include/mt-plat/mt6755 ...

  7. 基于MTK 的 TP 驱动分析

    1. 克隆服务器工程源码并切换分支 git clone git@192.168.20.2: mt658292_kk v9 git checkout -b submit_v9_dongxf_tp_mod ...

  8. mtk android平台学习,MTK平台的驱动学习——(阶段1规划篇)

    受老罗的影响,由于本人还是菜鸟,不能像老罗一样重头开始研究整个系统,决定从就近的工作开始,从android MTK 的驱动-->中间层-->应用层,一步一步研究. 一边看书,一边搜集网上的 ...

  9. MTK平台LCD驱动框架详解(一)

    许多学习嵌入式的进入MTK开发平台,很多东西都会感到很陌生.在MTK平台上你可以简简单单几分钟就点亮一块屏.加上MTK快速开发的节奏,也很少有时间自己整理学习.如果不思进取,不加班加点学习.很容易就慢 ...

  10. 高通平台tp驱动(一)

    在网上对于gsl680驱动的剖析文章很多,本人自己也负责过tp驱动的移植,但是对于内部tp驱动的构造从未了解过,觉得有必要对tp驱动进行一个梳理.以下仅为本人观点,如有不对欢迎各路大佬指点,目的主要是 ...

最新文章

  1. java 外部类似_[求指点] 如何用java 实现类似linux中管道调用外部程序的功能
  2. 特征工程(feature engineering)是什么?特征工程(feature engineering)包含哪些方面?
  3. 导入Scrapy 项目报错:no module named scrapy
  4. 百斗度输入法linux,斗字输入法安卓版-斗字输入法app下载-最火软件站
  5. vue自定义指令(详细)
  6. 什么是ZooKeeper
  7. 超252万市民预约报名 北京数字人民币红包中签结果公布啦
  8. Microsoft Project 2010
  9. 产品需求文档写作方法(一)写前准备+梳理需求
  10. 快递鸟接口国内常用快递公司编码表
  11. 禅道怎样添加开发人员
  12. 从电商到软件市场,阿里双十一战火蔓延
  13. vue实现词云图(echarts/Highcharts)
  14. html实现展开余下全文多个,DIV+css内容太长,怎么实现点击展开余下全文?
  15. 【Pytorch安装】Failed building wheel for XXX踩坑
  16. Python爬虫——动漫zj(manhua站)
  17. CSS实现图片居中显示的文字环绕功能
  18. 牛逼了!Python代码补全利器,提高效率告别996!
  19. 计算机出现多个组或用户名,一台计算机怎么同时出现在两个不同的工作组里?...
  20. 【软件工程理论与实践】(笔记)

热门文章

  1. 怎样将CAD里面画好的图纸转换到WORD文档里面去
  2. 小工具--浏览器主页被挟持,svchost.exe占用网速,treeSizeFree,桌面日历,WIN自带哈希校验
  3. 社群运营普遍存在的五个问题
  4. 计算机设计大赛软件应用开发作品,2019年(第12届)中国大学生计算机设计大赛软件应用与开发类总决赛报道...
  5. HTML5期末大作业:动漫A网站设计——动画漫展学习资料电影模板(6页) 网页设计作业 _ 动漫网页设计作业,网页设计作业 _ 动漫网页设计成品,网页设计作业 _ 动漫网页设计成品模板下载
  6. 获取微信小程序二维码后返回一堆乱码
  7. 05.前端面经汇总javaScript篇
  8. html网页框架分割三部分,Dreamweaver用框架建立网站把浏览器的显示空间分割为几个部分...
  9. 程序人生:程序员如何和老板谈升职加薪
  10. CROSSFORMER: A VERSATILE VISION TRANSFORMER BASED ON CROSS-SCALE ATTENTION