本博文将讲解基于Goodix触控芯片的tp驱动程序。如有不足之处,敬请指出。

初始化

static int __init tpd_driver_init(void)
{GTP_INFO("MediaTek gt91xx touch panel driver init\n");
#if defined(TPD_I2C_NUMBER) i2c_register_board_info(TPD_I2C_NUMBER, &i2c_tpd, 1);
#elsei2c_register_board_info(0, &i2c_tpd, 1);
#endifif (tpd_driver_add(&tpd_device_driver) < 0)GTP_INFO("add generic driver failed\n");return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

tpd_driver_init中主要做了两件事情:

1、注册一个i2c设备

i2c_register_board_info在 kernel\drivers\i2c\i2c-boardinfo.c 中定义。 
对应的函数说明如下:

i2c_register_board_info - statically declare I2C devices 
* @busnum: identifies the bus to which these devices belong 
* @info: vector of i2c device descriptors 
* @len: how many descriptors in the vector; may be zero to reserve 
* the specified bus number.

因此我们可知道: 
TPD_I2C_NUMBER表示的是当前注册的i2c设备是在哪个总线上面,需要查看硬件连接图; 
i2c_tpd表示的是该i2c设备的描述,包括设备名”gt9xx”和i2c的设备地址“(0xBA >> 1)”

static struct i2c_board_info __initdata i2c_tpd = { I2C_BOARD_INFO("gt9xx", (0xBA >> 1))};
  • 1

2、添加一个驱动到静态数组tpd_driver_list数组中

if (tpd_driver_add(&tpd_device_driver) < 0)GTP_INFO("add generic driver failed\n");
  • 1
  • 2

其中tpd_device_driver的定义为:

static struct tpd_driver_t tpd_device_driver =
{.tpd_device_name = "gt9xx",.tpd_local_init = tpd_local_init,.suspend = tpd_suspend,.resume = tpd_resume,
#ifdef TPD_HAVE_BUTTON.tpd_have_button = 1,
#else.tpd_have_button = 0,
#endif
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

这几个选项都是必须的。

其中的tpd_device_name不能为”generic”,那样的话就会识别为R-Touch(推测此处表示为电阻屏),被保存到tpd_driver_list[0]中。否则为C-Touch(推测此处表示为电容屏),保存到tpd_driver_list的非0位置处。在TP驱动中要自己实现这几个函数的所有内容。

tpd_local_init()

static int tpd_local_init(void)
{
#if GTP_ESD_PROTECTclk_tick_cnt = 2 * HZ;   // HZ: clock ticks in 1 second generated by systemGTP_DEBUG("Clock ticks for an esd cycle: %d", clk_tick_cnt);INIT_DELAYED_WORK(&gtp_esd_check_work, gtp_esd_check_func);gtp_esd_check_workqueue = create_workqueue("gtp_esd_check");spin_lock_init(&esd_lock);          // 2.6.39 & later// esd_lock = SPIN_LOCK_UNLOCKED;   // 2.6.39 & before
#endif#if GTP_SUPPORT_I2C_DMAtpd->dev->dev.coherent_dma_mask = DMA_BIT_MASK(32);gpDMABuf_va = (u8 *)dma_alloc_coherent(&tpd->dev->dev, 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);
#endifif (i2c_add_driver(&tpd_i2c_driver) != 0){GTP_INFO("unable to add i2c driver.\n");return -1;}if (tpd_load_status == 0) //if(tpd_load_status == 0) // disable auto load touch driver for linux3.0 porting{GTP_INFO("add error touch panel driver.\n");i2c_del_driver(&tpd_i2c_driver);return -1;}input_set_abs_params(tpd->dev, ABS_MT_TRACKING_ID, 0, (GTP_MAX_TOUCH-1), 0, 0);#ifdef TPD_HAVE_BUTTONif (FACTORY_BOOT == get_boot_mode()|| RECOVERY_BOOT == get_boot_mode())  // cty 2014-08-14{tpd_button_setting(TPD_KEY_COUNT, tpd_keys_local_factory, tpd_keys_dim_local);// initialize tpd button data}else{tpd_button_setting(TPD_KEY_COUNT, tpd_keys_local, tpd_keys_dim_local);// initialize tpd button data}
#endif#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 stringtpd->dev->id.vendor = 0x00;tpd->dev->id.product = tpd_info.pid;tpd->dev->id.version = tpd_info.vid;GTP_INFO("end %s, %d\n", __FUNCTION__, __LINE__);tpd_type_cap = 1;return 0;
}
  • 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

该函数主要做以下几项工作:

1、GTP_ESD_PROTECT

为ESD防护机制。

2、GTP_SUPPORT_I2C_DMA

主要是申请I2C DMA的空间,由gpDMABuf_va指向。 
tpd->dev->dev.coherent_dma_mask = DMA_BIT_MASK(32);表示这个设备能寻址的物理地址的范围为DMA_BIT_MASK(32),这个值相当于0xffffffffUL。关于这个成员的解释可以参见 linux下platform_device中的dma_mask与coherent_dma_mask。 
接下来的dma_alloc_coherent()申请DMA空间。返回值为gpDMABuf_va表示虚拟地址,以及返回gpDMABuf_pa表示DMA实际物理地址。至于这两个怎么用还不是很懂。详见:Dynamic DMA mapping using the generic device

3、注册i2c设备驱动

if (i2c_add_driver(&tpd_i2c_driver) != 0)
{GTP_INFO("unable to add i2c driver.\n");return -1;
}
if (tpd_load_status == 0) //if(tpd_load_status == 0) // disable auto load touch driver for linux3.0 porting
{GTP_INFO("add error touch panel driver.\n");i2c_del_driver(&tpd_i2c_driver);return -1;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

probe探测到总线上的设备并把设备和驱动建立连接以完成设备的初始化。这个中间会调用tpd_i2c_driver中的tpd_i2c_probe()来完成初始化工作。

static struct i2c_driver tpd_i2c_driver =
{.probe = tpd_i2c_probe,.remove = tpd_i2c_remove,.detect = tpd_i2c_detect,.driver.name = "b_gt9xx_hotknot",.id_table = tpd_i2c_id,.address_list = (const unsigned short *) forces,
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

这里面的内容都要自己去实现,特别是tpd_i2c_probe()。如果tpd_i2c_probe()中初始化设备全部完成,则要置位全局变量tpd_load_status为1标记成功初始化,否则的话匹配失败需要调用i2c_del_driver()删除已注册的驱动。

4、设置input设备tpd->dev支持的最大手指触摸个数

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

input设备tpd->dev的申请以及定义和初始化的内容都在mtk_tpd.c这个文件中的tpd_probe()函数实现了。推测之所以没有一起初始化的ABS_MT_TRACKING_ID的内容是因为每个触控IC的原厂软件实现最大的触摸个数都不太一样,所以这里需要根据情况自己设定。

5、按键的初始化

#ifdef TPD_HAVE_BUTTON
if (FACTORY_BOOT == get_boot_mode()|| RECOVERY_BOOT == get_boot_mode())  // cty 2014-08-14
{tpd_button_setting(TPD_KEY_COUNT, tpd_keys_local_factory, tpd_keys_dim_local);// initialize tpd button data
}
else
{tpd_button_setting(TPD_KEY_COUNT, tpd_keys_local, tpd_keys_dim_local);// initialize tpd button data
}
#endif
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

tpd_button_setting()函数的内容非常简单,就是将数组tpd_keys_local[TPD_KEY_COUNT]tpd_keys_dim_local[TPD_KEY_COUNT][4]拷贝给tpd_keystpd_keys_dim。这两个变量用于tpd_button_init()的初始化创建virtual keys。数组内容要根据TP的分辨率来确定按键的位置。需要根据不同分辨率来确定按键的个数、按键名称、按键的坐标范围。

#define TPD_KEY_COUNT   3
#define TPD_KEYS        { KEY_BACK, KEY_HOMEPAGE,KEY_MENU}
#define TPD_KEYS_FACTORY    {KEY_BACK , KEY_HOME,KEY_MENU}
#define TPD_KEYS_DIM            {{100,1500,50,30},{270,1500,50,30},{450,1500,50,30}}#ifdef TPD_HAVE_BUTTON
static int tpd_keys_local[TPD_KEY_COUNT] = TPD_KEYS;
static int tpd_keys_local_factory[TPD_KEY_COUNT] = TPD_KEYS_FACTORY;
static int tpd_keys_dim_local[TPD_KEY_COUNT][4] = TPD_KEYS_DIM;
#endif
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

tp button也是和其它触摸事件一样,以坐标形式的input_event进行上报。在初始化时会通过tpd_button_setting()函数根据定义在tpd_custom_XXX.h文件中的配置信息将虚拟按键的坐标信息写在/sys/board_properties/virtualkeys.mtk-tpd中。工作时,tp driver将按下的点的坐标进行上报,Android上层会读取sys中的按键配置信息,再判断上报的坐标是否属于某个按键的坐标范围,以此将坐标信息转化为具体的按键键值。 详见android 虚拟按键是通过哪种机制上报的?

MTK虚拟按键的实现在tpd_button.c中实现,具体内容详见Android tp的虚拟按键(virtual key)处理

6、设置input设备tpd->dev的信息

// set vendor string
tpd->dev->id.vendor = 0x00;
tpd->dev->id.product = tpd_info.pid;
tpd->dev->id.version = tpd_info.vid;
  • 1
  • 2
  • 3
  • 4

其中tpd_info的信息是在tpd_i2c_probe()中调用gtp_read_version()获取到的。

7、设置TP类型为电容屏

tpd_type_cap = 1;
  • 1

从变量字面上的意思来看是设置标记TP为电容屏,至于还有何作用还不得而知。

tpd_i2c_probe()

static s32 tpd_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id)
{s32 err = 0;s32 ret = 0;u16 version_info;#if GTP_HAVE_TOUCH_KEYs32 idx = 0;#endif#ifdef TPD_PROXIMITYstruct hwmsen_object obj_ps;#endifi2c_client_point = client;ret = tpd_power_on(client);if (ret < 0){GTP_ERROR("I2C communication ERROR!");goto out;}#ifdef VELOCITY_CUSTOMtpd_v_magnify_x = TPD_VELOCITY_CUSTOM_X;tpd_v_magnify_y = TPD_VELOCITY_CUSTOM_Y;#endifret = gtp_read_version(client, &version_info);if (ret < 0){GTP_ERROR("Read version failed.");goto out;}    ret = gtp_init_panel(client);if (ret < 0){GTP_ERROR("GTP init panel failed.");//goto out;}// Create proc file systemgt91xx_config_proc = proc_create(GT91XX_CONFIG_PROC_FILE, 0660, NULL, &gt_upgrade_proc_fops);if (gt91xx_config_proc == NULL){GTP_ERROR("create_proc_entry %s failed\n", GT91XX_CONFIG_PROC_FILE);goto out;}#if GTP_CREATE_WR_NODEinit_wr_node(client);#endifthread = kthread_run(touch_event_handler, 0, TPD_DEVICE);if (IS_ERR(thread)){err = PTR_ERR(thread);GTP_INFO(TPD_DEVICE " failed to create kernel thread: %d\n", err);goto out;}#if GTP_HAVE_TOUCH_KEYfor (idx = 0; idx < GTP_MAX_KEY_NUM; idx++){input_set_capability(tpd->dev, EV_KEY, touch_key_array[idx]);}#endif#if GTP_GESTURE_WAKEUP//input_set_capability(tpd->dev, EV_KEY, KEY_POWER);#endif#if GTP_WITH_PEN// pen support__set_bit(BTN_TOOL_PEN, tpd->dev->keybit);__set_bit(INPUT_PROP_DIRECT, tpd->dev->propbit);//__set_bit(INPUT_PROP_POINTER, tpd->dev->propbit); // 20130722#endif// set INT modemt_set_gpio_mode(GPIO_CTP_EINT_PIN, GPIO_CTP_EINT_PIN_M_EINT);mt_set_gpio_dir(GPIO_CTP_EINT_PIN, GPIO_DIR_IN);mt_set_gpio_pull_enable(GPIO_CTP_EINT_PIN, GPIO_PULL_DISABLE);msleep(50);mt_eint_set_hw_debounce(CUST_EINT_TOUCH_PANEL_NUM, 0);if (!int_type)  //EINTF_TRIGGER{mt_eint_registration(CUST_EINT_TOUCH_PANEL_NUM, EINTF_TRIGGER_RISING, tpd_eint_interrupt_handler, 1);}else{mt_eint_registration(CUST_EINT_TOUCH_PANEL_NUM, EINTF_TRIGGER_FALLING, tpd_eint_interrupt_handler, 1);}mt_eint_unmask(CUST_EINT_TOUCH_PANEL_NUM);#if GTP_AUTO_UPDATEret = gup_init_update_proc(client);if (ret < 0){GTP_ERROR("Create update thread error.");}#endif#ifdef TPD_PROXIMITY//obj_ps.self = cm3623_obj;obj_ps.polling = 0;         //0--interrupt mode;1--polling mode;obj_ps.sensor_operate = tpd_ps_operate;if ((err = hwmsen_attach(ID_PROXIMITY, &obj_ps))){GTP_ERROR("hwmsen attach fail, return:%d.", err);}#endif#if GTP_ESD_PROTECTgtp_esd_switch(client, SWITCH_ON);#endiftpd_load_status = 1;return 0;
out:return -1;
}
  • 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

一般来说,TP驱动probe()函数中一般会做以下几个工作:

1、TPD_PROXIMITY

在多数情况下,通话的时候,脸部靠近会自动息屏,脸部拿开会自动亮屏,这个功能主要由接近传感器Proximity Sensor(简称PS)实现。但是有时候在方案设计中,为了省下一颗光感,往往要求触控ic支持此功能。当TP感应上半部分有多数差值到达门限,就会认定脸部靠近,会置对应的标记为在某个寄存器中,驱动中读取该标记位判断是否亮屏或者暗屏。

所有与接近感应功能对应的code使用宏TPD_PROXIMITY进行控制: 
(1)、创建 hwsen 对象以及实现其内容

#ifdef TPD_PROXIMITYstruct hwmsen_object obj_ps;
#endif#ifdef TPD_PROXIMITY//obj_ps.self = cm3623_obj;obj_ps.polling = 0;         //0--interrupt mode;1--polling mode;obj_ps.sensor_operate = tpd_ps_operate;if ((err = hwmsen_attach(ID_PROXIMITY, &obj_ps))){GTP_ERROR("hwmsen attach fail, return:%d.", err);}#endif
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

MTK代码里使用了一个hwmsensor模块控制所有的sensor。 
代码路径:mediatek/kernel/drivers/hwmon/hwmsen/hwmsen_dev.c,编译成hwmsen_dev.o,系统起来后会生成/dev/hwmsensor设备。 使用sensor_operate()接口管理所有sensor驱动,向上提供hwmsen_unlocked_ioctl()接口,再往下就是具体的sensor驱动代码了,根据MTK的驱动结构完成sensor_operate()接口并调用hwmsen_dev.c里的hwmsen_attach()函数,把sensor_operate()接口加到hwmsen_dev的列表里,这样hwmsen_dev里就能调用所有sensor的sensor_operate()函数。 
详见Android Sensor学习这篇文章。

obj_ps.polling = 0;
  • 1

标记此sensor工作在中断模式下而不是轮询方式,通过中断上报数据。

obj_ps.sensor_operate = tpd_ps_operate; 
  • 1

主要实现获取通话时候的状态信息并根据此状态信息判断是否上报接近或者远离动作。

hwmsen_attach(ID_PROXIMITY, &obj_ps)
  • 1

这是将接近感应传感器设备驱动添加到 hwmsen device 中。 
(2)、实现tpd_ps_operate()内容 
创建两个全局变量:

#ifdef TPD_PROXIMITY
static s32 tpd_get_ps_value(void)
{return tpd_proximity_detect;
}// 判断是否打开接近感应功能
static s32 tpd_enable_ps(s32 enable)
{u8  state;s32 ret = -1;if (enable){state = 1;tpd_proximity_flag = 1;GTP_INFO("TPD proximity function to be on.");}else{state = 0;tpd_proximity_flag = 0;GTP_INFO("TPD proximity function to be off.");}ret = i2c_write_bytes(i2c_client_point, TPD_PROXIMITY_ENABLE_REG, &state, 1);if (ret < 0){GTP_ERROR("TPD %s proximity cmd failed.", state ? "enable" : "disable");return ret;}GTP_INFO("TPD proximity function %s success.", state ? "enable" : "disable");return 0;
}s32 tpd_ps_operate(void *self, u32 command, void *buff_in, s32 size_in,void *buff_out, s32 size_out, s32 *actualout)
{s32 err = 0;s32 value;hwm_sensor_data *sensor_data;switch (command){case SENSOR_DELAY:if ((buff_in == NULL) || (size_in < sizeof(int))){GTP_ERROR("Set delay parameter error!");err = -EINVAL;}// Do nothingbreak;case SENSOR_ENABLE:if ((buff_in == NULL) || (size_in < sizeof(int))){GTP_ERROR("Enable sensor parameter error!");err = -EINVAL;}else{value = *(int *)buff_in;err = tpd_enable_ps(value);  // 获取是否在通话的状态并赋值给tpd_proximity_flag }break;case SENSOR_GET_DATA:if ((buff_out == NULL) || (size_out < sizeof(hwm_sensor_data))){GTP_ERROR("Get sensor data parameter error!");err = -EINVAL;}else{sensor_data = (hwm_sensor_data *)buff_out;sensor_data->values[0] = tpd_get_ps_value();  // 将检测到的接近或者远离的状态tpd_proximity_detect发送出去sensor_data->value_divide = 1;sensor_data->status = SENSOR_STATUS_ACCURACY_MEDIUM;}break;default:GTP_ERROR("proxmy sensor operate function no this parameter %d!\n", command);err = -1;break;}return err;
}
#endif
  • 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

当通话的时候,会调用tpd_ps_operate()进入case SENSOR_ENABLE:将通话的标记作为参数传给tpd_enable_ps(value),这个函数很简单,就是赋值给变量tpd_proximity_flag,该变量标记此时是否需要打开接近感应功能。如果需要打开接近感应功能,需往ic对应的寄存器写入某个对应的值。当检测到接近全局变量tpd_proximity_detect会置为0;当检测到远离,tpd_proximity_detect会置为1。

case SENSOR_GET_DATA:中会上报检测到的接近或者远离的状态(tpd_proximity_detect为0或者为1)。不过在中断的内核进程中touch_event_handler()也有一段同样的code。不知道是走哪个流程的。

#ifdef TPD_PROXIMITYif (tpd_proximity_flag == 1){proximity_status = point_data[GTP_ADDR_LENGTH];GTP_DEBUG("REG INDEX[0x814E]:0x%02X\n", proximity_status);if (proximity_status & 0x60)  //proximity or large touch detect,enable hwm_sensor.{tpd_proximity_detect = 0;//sensor_data.values[0] = 0;}else{tpd_proximity_detect = 1;//sensor_data.values[0] = 1;}//get raw dataGTP_DEBUG(" ps change\n");GTP_DEBUG("PROXIMITY STATUS:0x%02X\n", tpd_proximity_detect);//map and store data to hwm_sensor_datasensor_data.values[0] = tpd_get_ps_value();sensor_data.value_divide = 1;sensor_data.status = SENSOR_STATUS_ACCURACY_MEDIUM;//report to the up-layerret = hwmsen_get_interrupt_data(ID_PROXIMITY, &sensor_data); // 通过中断的方式将接近或者远离的状态上报给 PROXIMITY sensorif (ret){GTP_ERROR("Call hwmsen_get_interrupt_data fail = %d\n", err);}}
#endif
  • 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

注意,如果是检测到在通话中且脸部接近的动作的时候,此时应该要屏蔽上报坐标的功能。

(3)、亮屏、暗屏 
暗屏和亮屏的动作也会调用tpd_suspend()tpd_resume()。一般suspend的时候ic要进入休眠模式且关闭中断刷新工作队列等工作。在resume的时候要唤醒ic,使能中断以及其他工作。如果是由于通话中接近或者远离导致suspend或者resume都不必做这些工作,只需要直接返回。因为此时要求ic还要能够正常工作。

2、tpd_power_on()

在这个函数里面,主要做了三件事:

1、给ic上电

#ifdef MT6573// power on CTPmt_set_gpio_mode(GPIO_CTP_EN_PIN, GPIO_CTP_EN_PIN_M_GPIO);mt_set_gpio_dir(GPIO_CTP_EN_PIN, GPIO_DIR_OUT);mt_set_gpio_out(GPIO_CTP_EN_PIN, GPIO_OUT_ONE);#else   // ( defined(MT6575) || defined(MT6577) || defined(MT6589) )#ifdef TPD_POWER_SOURCE_CUSTOMhwPowerOn(TPD_POWER_SOURCE_CUSTOM, VOL_2800, "TP");#elsehwPowerOn(MT65XX_POWER_LDO_VGP2, VOL_2800, "TP");#endif#ifdef TPD_POWER_SOURCE_1800hwPowerOn(TPD_POWER_SOURCE_1800, VOL_1800, "TP");#endif#endif
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

关于上电的动作可参考MTK Android Driver:PMIC

2、复位ic

void gtp_reset_guitar(struct i2c_client *client, s32 ms)
{GTP_INFO("GTP RESET!\n");GTP_GPIO_OUTPUT(GTP_RST_PORT, 0);msleep(ms);GTP_GPIO_OUTPUT(GTP_INT_PORT, client->addr == 0x14);msleep(2);GTP_GPIO_OUTPUT(GTP_RST_PORT, 1);msleep(6);                      //must >= 6ms#if GTP_COMPATIBLE_MODEif (CHIP_TYPE_GT9F == gtp_chip_type){return;}
#endifgtp_int_sync(100);  // for dbl-system
#if GTP_ESD_PROTECTgtp_init_ext_watchdog(i2c_client_point);
#endif
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

复位ic就是讲ic上的RST脚设为输出拉低再拉高。MTK的tp驱动程序需要涉及对ic三个引脚的操作:EN(使能或VDD脚)、RST(复位脚)、EINT(外部中断脚)。这三个引脚对应的宏固定为:

GPIO_CTP_EN_PIN
GPIO_CTP_RST_PIN
GPIO_CTP_EINT_PIN
  • 1
  • 2
  • 3

这几个宏在以下路径中的文件进行配置对应的GPIO口 
alps\mediatek\dct\DrvGen.exe 
alps\mediatek\custom\project\kernel\dct\dct\codegen.dws 配置

3、测试I2C是否通

static s8 gtp_i2c_test(struct i2c_client *client);
  • 1

测试i2c是否通信成功,需要用到i2c的读写接口,至于接口函数怎么写,可以参照这篇文章待定

3、初始化ic

接下来就是通过i2c读写接口初始化ic了。

s32 gtp_read_version(struct i2c_client *client, u16 *version);
static s32 gtp_init_panel(struct i2c_client *client);
  • 1
  • 2

至于初始化的内容会放到一个全局的数组之中,这项工作一般都要FAE来完成。

4、创建内核线程

thread = kthread_run(touch_event_handler, 0, TPD_DEVICE);if (IS_ERR(thread))
{err = PTR_ERR(thread);GTP_INFO(TPD_DEVICE " failed to create kernel thread: %d\n", err);goto out;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

创建名为TPD_DEVICE线程thread是为了处理中断来临之后读取坐标、上报坐标、手势识别、按键等等信息。判断thread是否有效需要用IS_ERR()来判断,而不是简单的使用(thread == NULL)判断,详见Linux内核多线程(一)

其主要内容如下:

static int touch_event_handler(void *unused)
{struct sched_param param = { .sched_priority = RTPM_PRIO_TPD };sched_setscheduler(current, SCHED_RR, &param); // 调度策略和调度参数分别设置为param指向的sched_param结构中指定的policy和参数do{set_current_state(TASK_INTERRUPTIBLE); // 设置当前线程可被打断if(tpd_eint_mode)  // 标记采用轮询方式还是中断方式,此处为中断方式{wait_event_interruptible(waiter, tpd_flag != 0); // 此处等待waiter被唤醒,只有当tpd_flag不为0且wake_up waite才会去执行。可知这两个条件都在中断服务子程序去置位了tpd_flag = 0; // 清除标记位}else{msleep(tpd_polling_time);}       set_current_state(TASK_RUNNING);// 标记当前线程正执行mutex_lock(&i2c_access);// 此处添加你的工作mutex_unlock(&i2c_access);} while (!kthread_should_stop());return 0;
}
  • 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

对于wait_event_interruptible()可以参照wait_event_interruptible 使用方法 
写这个内容好着急啊,很多知识点都没有很深入的了解,只能参照网上说的来进行解释。

5、申请中断服务子程序

// set INT mode
mt_set_gpio_mode(GPIO_CTP_EINT_PIN,GPIO_CTP_EINT_PIN_M_EINT);
mt_set_gpio_dir(GPIO_CTP_EINT_PIN, GPIO_DIR_IN);
mt_set_gpio_pull_enable(GPIO_CTP_EINT_PIN,GPIO_PULL_DISABLE);msleep(50);mt_eint_set_hw_debounce(CUST_EINT_TOUCH_PANEL_NUM, 0);if (!int_type)  //EINTF_TRIGGER
{mt_eint_registration(CUST_EINT_TOUCH_PANEL_NUM, EINTF_TRIGGER_RISING, tpd_eint_interrupt_handler, 1);
}
else
{mt_eint_registration(CUST_EINT_TOUCH_PANEL_NUM, EINTF_TRIGGER_FALLING, tpd_eint_interrupt_handler, 1);
}mt_eint_unmask(CUST_EINT_TOUCH_PANEL_NUM); // 关闭中断使能,probe完毕之后会打开中断使能
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

CUST_EINT_TOUCH_PANEL_NUM为分配给触摸ic的中断号,在alps\mediatek\custom\project\kernel\dct\dct\cust_eint.h 中定义; 
设置中断的触发方式 为上升沿或者下降沿(需要根据ic的功能设置);GPIO的相关功能可参照MTK6577+Android之GPIO驱动简介 
设置中断服务子程序static void tpd_eint_interrupt_handler(void) 
这个函数的内容很简单,就是置位tpd_flag变量和wake_up waiter.

static void tpd_eint_interrupt_handler(void)
{TPD_DEBUG_PRINT_INT;tpd_flag = 1;wake_up_interruptible(&waiter);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

至此,tpd_i2c_probe()的所有内容都已实现,接下来就是等待ic给中断然后读取坐标等信息了。

tpd_down()和tpd_up()

这两个函数通过input子系统上报坐标以及上报手指抬起的动作。关于input子系统,我根据自己的理解写了待定。

tpd_suspend()和tpd_resume()

关于休眠和唤醒的内容根据ic的特性设置。如休眠的时候需要关闭中断、配置进入休眠模式。唤醒的时候唤醒ic,使能中断等。

MTK平台TP驱动详解相关推荐

  1. MTK 驱动(64)---Mtk touch panel驱动/TP驱动详解

    Mtk touch panel驱动/TP驱动详解 TP还算是比LCM好理解的多. 在启动过程中,先注册/mediatek/custom/command/kernel/touchpanel目录下的具体驱 ...

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

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

  3. 深入学习Linux摄像头(四)三星平台fimc驱动详解

    深入学习Linux摄像头系列 深入学习Linux摄像头(一)v4l2应用编程 深入学习Linux摄像头(二)v4l2驱动框架 深入学习Linux摄像头(三)虚拟摄像头驱动分析 深入学习Linux摄像头 ...

  4. 三星平台fimc驱动详解

    一.硬件接口 摄像头 摄像头传感器由摄像头接口和控制接口(一般为i2c)组成 摄像头接口用于传输传感器采集到的数据 控制接口用于控制摄像头传感器(例如设置图像格式-) 芯片 芯片上由多个摄像头控制器, ...

  5. MTK 平台TP调试遇坑

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

  6. MTK 智能机平台系统文件夹文件详解

    MTK6573智能机平台系统文件夹文件详解3 - App目录 欢迎转载收藏,转载请保留如下信息: Mr.ROM出品 Email: iammrrom@gmail.com Blog: http://blo ...

  7. Pixhawk(PX4)之驱动详解篇(0)_前期准备(招贤令)

    Pixhawk(PX4)之驱动详解篇(0)_前期准备(招贤令) 原创 2017年03月01日 22:58:39 标签: 开发人员 / UAV / 软件 / 硬件 一.开篇 开源精神常在!!! 谁说软件 ...

  8. imx6ul 驱动详解

    链表的知识: struct list_head {struct list_head *next, *prev; }; API函数 函数 功能 LIST_HEAD 声明并初始化双向链表. INIT_LI ...

  9. linux usb gadget驱动详解(一)

    由于PC的推广,USB(通用串行总线)是我们最熟知的通信总线规范之一,其他的还有诸如以太网.PCIE总线和RS232串口等.这里我们主要讨论USB. USB是一个主从通信架构,但只能一主多从.其中us ...

  10. 博通wifi驱动详解

    1        WLAN技术 WLAN是英文WirelessLAN的缩写,就是无线局域网的意思.无线以太网技术是一种基于无线传输的局域网技术,与有线网络技术相比,具有灵活.建网迅速.个人化等特点.将 ...

最新文章

  1. 改写了一个shell写的cdn节点测试代码
  2. 一小段代码:父类和子类
  3. 搜集来的命令集合,挺好的
  4. JS中的正则表达式小小的技巧---TEST方法的使用
  5. 深度学习目标检测之 YOLO v3
  6. 平面设计背景素材|打造超酷的炫彩光束光效海报!
  7. 电脑计算器_哪几种计算器可以携带入考场!注会考试忘带计算器了怎么办?
  8. php根据出生日期计算年龄函数
  9. android 静默暗转_Android 7.0 静默安装
  10. 实现微信小程序授权获取手机号登录(c#后端代码附上 少爷接收)
  11. 计算机报名登录老显示验证码错误是怎么回事,为何登录博客时输入了正确的验证码却总提示验证码错误?...
  12. CodeReview流程梳理
  13. 移动应用程序设计基础——期末考核——登录界面与简单日记本的综合实践
  14. C语言编程统计八皇后问题的解的个数,八皇后问题C语言解法
  15. 【专精特新周报】邦德股份北交所上市,首日收涨27.86%;12家创新层公司被降层;2022年国家新增支持五百家左右专精特新小巨人...
  16. 虚拟网卡 TUN/TAP 驱动程序设计原理
  17. 跑步,找到自己的节奏
  18. h5逻辑_H5+app 混合开发
  19. PLSQL启动报错:Initialization error
  20. 最好的3个安卓epub阅读器

热门文章

  1. mysql获取记录的插入时间_mysql 自动记录数据插入及最后修改时间
  2. Network Switching Software Platform Guide学习笔记
  3. 【NodeJS】Codecademy学习笔记
  4. 首次参加齐鲁软件设计大赛经验(及总结出的划水要点)
  5. 大学计算机课是绩点课吗,选修课是什么意思 选修课成绩算入绩点吗
  6. 关于ram的结构和读写过程
  7. mysql用户定义的完整性_Mysql(数据库完整性)
  8. leetcode 73 矩阵归零
  9. VC++从入门到精通视频教程
  10. 知识分享:数据分析的6大基本步骤