MTK 平台TP 驱动

下面以汇顶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 静电等其他容易出现异常的情况下使用。