文章目錄
- 一、TP的硬體介面
- 二、代碼路徑
- 三、TP代碼分析
- 1、硬體引數設定
- 2、TP設備驅動
- 3、TP模組驅動
一、TP的硬體介面
| 引腳 | 名稱及作用 |
|---|---|
| VDD | TP供電 |
| RESET | 復位引腳 |
| EINT | 中斷引腳 |
| SCL、SDA | I2C介面 |
? TP的作業方式比較簡單:
- 上電后通過RESET腳控制TP芯片復位;
- 通過I2C介面給TP設定引數或讀取TP資料;
- TP有觸摸操作時通過EINT腳通知主控;
二、代碼路徑
| 描述 | 路徑 | 檔案 |
|---|---|---|
| 系統設定 | device\top\top6737t_36_a_m0kernel-3.18\arch\arm\configs | ProjectConfig.mkuser版本和userdebug版本對應: top6737t_36_a_m0_defconfigdebug版本對應: top6737t_36_a_m0_debug_defconfig |
| 介面設定 | kernel-3.18\arch\arm\boot\dts | top6737t_36_a_m0.dts cust_i2c.dtsi |
| Kernel代碼 | kernel-3.18\drivers\input\touchscreen\mediatek | mtk_tpd.c 等 |
三、TP代碼分析
1、硬體引數設定
? 在dts檔案中,對照硬體介面,修改對應GPIO設定,還有TP的虛擬鍵坐標、解析度等引數資訊:
// top6737t_36_a_m0.dts
......
&accdet {
pinctrl-names = "default","state_eint_as_int";
pinctrl-0 = <&ACCDET_pins_default>;
pinctrl-1 = <&ACCDET_pins_eint_int>;
status = "okay";
};
&pio {
ACCDET_pins_default: eint6default {
};
ACCDET_pins_eint_int: eint@6 {
pins_cmd_dat {
pins = <PINMUX_GPIO6__FUNC_GPIO6>;
slew-rate = <0>;
bias-disable;
};
};
};
&touch {
tpd-resolution = <240 240>;
use-tpd-button = <0>;
tpd-key-num = <0>;
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 = <1>;
tpd-filter-enable = <1>;
tpd-filter-pixel-density = <124>;
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";
};
......
還有對應的I2C設定:
/* cust_i2c.dtsi */
&i2c1 {
cap_touch@5d {
compatible = "mediatek,cap_touch";
reg = <0x5d>;
};
};
2、TP設備驅動
? 檔案mtk_tpd.c是TP設備驅動的入口,可以通過分析本檔案簡單捋一下代碼結構,
? 首先,是通過platform_driver_register注冊一個設備驅動:
//mtk_tpd.c
static int __init tpd_device_init(void)
{
TPD_DEBUG("MediaTek touch panel driver init\n");
if (platform_driver_register(&tpd_driver) != 0) {
TPD_DMESG("unable to register touch panel driver.\n");
return -1;
}
return 0;
}
//設備驅動結構體
static struct platform_driver tpd_driver = {
.remove = tpd_remove,
.shutdown = NULL,
.probe = tpd_probe,
.driver = {
.name = TPD_DEVICE,
.pm = &tpd_pm_ops,
.owner = THIS_MODULE,
.of_match_table = touch_of_match,
},
};
我們看一下這個設備驅動的probe函式:
//mtk_tpd.c
static int tpd_probe(struct platform_device *pdev)
{
......
//首先,注冊了一個雜項設備
if (misc_register(&tpd_misc_device))
{
pr_err("mtk_tpd: tpd_misc_device register failed\n");
}
//讀取dts中相關gpio等設定
tpd_get_gpio_info(pdev);
tpd = kmalloc(sizeof(struct tpd_device), GFP_KERNEL);
if (tpd == NULL)
return -ENOMEM;
memset(tpd, 0, sizeof(struct tpd_device));
//在記憶體中為輸入設備結構體分配一個空間,并對結構體成員進行初始化
tpd->dev = input_allocate_device();
if (tpd->dev == NULL) {
kfree(tpd);
return -ENOMEM;
}
#ifdef CONFIG_MTK_LCM_PHYSICAL_ROTATION
//看下是否存在橫屏豎用,TP也要跟著LCD進行橫豎轉換
if (0 == strncmp(CONFIG_MTK_LCM_PHYSICAL_ROTATION, "90", 2) || 0 == strncmp(CONFIG_MTK_LCM_PHYSICAL_ROTATION, "270", 3))
{
#ifdef CONFIG_MTK_FB /*Fix build errors,as some projects cannot support these apis while bring up*/
TPD_RES_Y = DISP_GetScreenWidth();
TPD_RES_X = DISP_GetScreenHeight();
#endif
}
else
#endif
{
#ifdef CONFIG_CUSTOM_LCM_X
#ifndef CONFIG_MTK_FPGA
#ifdef CONFIG_MTK_FB /*Fix build errors,as some projects cannot support these apis while bring up*/
TPD_RES_X = DISP_GetScreenWidth();
TPD_RES_Y = DISP_GetScreenHeight();
#endif
#endif
#else
#ifdef CONFIG_LCM_WIDTH
ret = kstrtoul(CONFIG_LCM_WIDTH, 0, &tpd_res_x);
if (ret < 0) {
pr_err("Touch down get lcm_x failed");
return ret;
}
TPD_RES_X = tpd_res_x;
ret = kstrtoul(CONFIG_LCM_HEIGHT, 0, &tpd_res_x);*/
if (ret < 0) {
pr_err("Touch down get lcm_y failed");
return ret;
}
TPD_RES_Y = tpd_res_y;
#endif
#endif
}
if (2560 == TPD_RES_X)
TPD_RES_X = 2048;
if (1600 == TPD_RES_Y)
TPD_RES_Y = 1536;
pr_info("mtk_tpd: TPD_RES_X = %lu, TPD_RES_Y = %lu\n", TPD_RES_X, TPD_RES_Y);
//設定輸入設備引數,包括時間型別等;
tpd_mode = TPD_MODE_NORMAL;
tpd_mode_axis = 0;
tpd_mode_min = TPD_RES_Y / 2;
tpd_mode_max = TPD_RES_Y;
tpd_mode_keypad_tolerance = TPD_RES_X * TPD_RES_X / 1600;
//初始化輸入設備結構
tpd->dev->name = TPD_DEVICE;
set_bit(EV_ABS, tpd->dev->evbit);
set_bit(EV_KEY, tpd->dev->evbit);
set_bit(ABS_X, tpd->dev->absbit);
set_bit(ABS_Y, tpd->dev->absbit);
set_bit(ABS_PRESSURE, tpd->dev->absbit);
#if !defined(CONFIG_MTK_S3320) && !defined(CONFIG_MTK_S3320_47)\
&& !defined(CONFIG_MTK_S3320_50) && !defined(CONFIG_MTK_MIT200) \
&& !defined(CONFIG_TOUCHSCREEN_SYNAPTICS_S3528) && !defined(CONFIG_MTK_S7020)
set_bit(BTN_TOUCH, tpd->dev->keybit);
#endif /* CONFIG_MTK_S3320 */
set_bit(INPUT_PROP_DIRECT, tpd->dev->propbit);
//將注冊到的platform_device保存到創建的輸入設備中;
tpd->tpd_dev = &pdev->dev;
//遍歷tpd_driver_list,找到當前在用TP;
for (i = 1; i < TP_DRV_MAX_COUNT; i++)
{
if (tpd_driver_list[i].tpd_device_name != NULL)
{
tpd_driver_list[i].tpd_local_init();
if (tpd_load_status == 1)
{
TPD_DMESG("[mtk-tpd]tpd_probe, tpd_driver_name=%s\n",tpd_driver_list[i].tpd_device_name);
g_tpd_drv = &tpd_driver_list[i];
break;
}
}
}
//如果tpd_driver_list中所有TP都初始化失敗,強制使用index=0的TP;
if (g_tpd_drv == NULL)
{
if (tpd_driver_list[0].tpd_device_name != NULL)
{
g_tpd_drv = &tpd_driver_list[0];
/* touch_type:0: r-touch, 1: C-touch */
touch_type = 0;
g_tpd_drv->tpd_local_init();
TPD_DMESG("[mtk-tpd]Generic touch panel driver\n");
} else {
TPD_DMESG("[mtk-tpd]cap touch and Generic touch both are not loaded!!\n");
return 0;
}
}
//為TP作業創建一個作業佇列;
touch_resume_workqueue = create_singlethread_workqueue("touch_resume");
INIT_WORK(&touch_resume_work, touch_resume_workqueue_callback);
//設定背光通知的回呼函式,以便在亮屏時喚醒TP,或者息屏后關閉TP;
tpd_fb_notifier.notifier_call = tpd_fb_notifier_callback;
if (fb_register_client(&tpd_fb_notifier))
TPD_DMESG("register fb_notifier fail!\n");
//設定TP相關引數;
if (touch_type == 1)
{
set_bit(ABS_MT_TRACKING_ID, tpd->dev->absbit);
set_bit(ABS_MT_TOUCH_MAJOR, tpd->dev->absbit);
set_bit(ABS_MT_TOUCH_MINOR, tpd->dev->absbit);
set_bit(ABS_MT_POSITION_X, tpd->dev->absbit);
set_bit(ABS_MT_POSITION_Y, tpd->dev->absbit);
input_set_abs_params(tpd->dev, ABS_MT_POSITION_X, 0, TPD_RES_X, 0, 0);
input_set_abs_params(tpd->dev, ABS_MT_POSITION_Y, 0, TPD_RES_Y, 0, 0);
#if defined(CONFIG_MTK_S3320) || defined(CONFIG_MTK_S3320_47) \
|| defined(CONFIG_MTK_S3320_50) || defined(CONFIG_MTK_MIT200) \
|| defined(CONFIG_TOUCHSCREEN_SYNAPTICS_S3528) || defined(CONFIG_MTK_S7020)
input_set_abs_params(tpd->dev, ABS_MT_PRESSURE, 0, 255, 0, 0);
input_set_abs_params(tpd->dev, ABS_MT_WIDTH_MAJOR, 0, 15, 0, 0);
input_set_abs_params(tpd->dev, ABS_MT_WIDTH_MINOR, 0, 15, 0, 0);
input_mt_init_slots(tpd->dev, 10, 0);
#else
input_set_abs_params(tpd->dev, ABS_MT_TOUCH_MAJOR, 0, 100, 0, 0);
input_set_abs_params(tpd->dev, ABS_MT_TOUCH_MINOR, 0, 100, 0, 0);
#endif /* CONFIG_MTK_S3320 */
TPD_DMESG("Cap touch panel driver\n");
}
input_set_abs_params(tpd->dev, ABS_X, 0, TPD_RES_X, 0, 0);
input_set_abs_params(tpd->dev, ABS_Y, 0, TPD_RES_Y, 0, 0);
input_abs_set_res(tpd->dev, ABS_X, TPD_RES_X);
input_abs_set_res(tpd->dev, ABS_Y, TPD_RES_Y);
input_set_abs_params(tpd->dev, ABS_PRESSURE, 0, 255, 0, 0);
input_set_abs_params(tpd->dev, ABS_MT_TRACKING_ID, 0, 10, 0, 0);
//注冊輸入設備
if (input_register_device(tpd->dev))
TPD_DMESG("input_register_device failed.(tpd)\n");
else
tpd_register_flag = 1;
//如果TP支持虛擬按鍵,還要進行虛擬按鍵的初始化;
if (g_tpd_drv->tpd_have_button)
tpd_button_init();
//創建用于除錯用的節點檔案
if (g_tpd_drv->attrs.num)
tpd_create_attributes(&pdev->dev, &g_tpd_drv->attrs);
return 0;
}
執行完probe函式,TP的設備節點就創建好了,
? 在probe中,查找當前使用TP是通過遍歷一個支持串列—tpd_driver_list實作的,這個串列的元素是TP操作的具體函陣列成的結構體,定義在:
//tpd.h
struct tpd_driver_t {
char *tpd_device_name; //TP名字
int (*tpd_local_init)(void); //TP初始化
void (*suspend)(struct device *h);//TP休眠
void (*resume)(struct device *h);//TP喚醒
int tpd_have_button;//是否有虛擬按鍵
struct tpd_attrs attrs;//創建節點檔案
};
不同的TP都是要實作這個結構體中的內容,完成TP應有的作業,
? tpd_driver_list這個串列時通過呼叫函式tpd_driver_add來添加元素的:
//mtk_tpd.c
int tpd_driver_add(struct tpd_driver_t *tpd_drv)
{
......
tpd_drv->tpd_have_button = tpd_dts_data.use_tpd_button;
//如果是電阻屏,強制放到串列index=0位置;
if (strcmp(tpd_drv->tpd_device_name, "generic") == 0) {
tpd_driver_list[0].tpd_device_name = tpd_drv->tpd_device_name;
tpd_driver_list[0].tpd_local_init = tpd_drv->tpd_local_init;
tpd_driver_list[0].suspend = tpd_drv->suspend;
tpd_driver_list[0].resume = tpd_drv->resume;
tpd_driver_list[0].tpd_have_button = tpd_drv->tpd_have_button;
return 0;
}
//如果是電容屏,放到串列尾;
for (i = 1; i < TP_DRV_MAX_COUNT; i++) {
if (tpd_driver_list[i].tpd_device_name == NULL) {
tpd_driver_list[i].tpd_device_name = tpd_drv->tpd_device_name;
tpd_driver_list[i].tpd_local_init = tpd_drv->tpd_local_init;
tpd_driver_list[i].suspend = tpd_drv->suspend;
tpd_driver_list[i].resume = tpd_drv->resume;
tpd_driver_list[i].tpd_have_button = tpd_drv->tpd_have_button;
tpd_driver_list[i].attrs = tpd_drv->attrs;
break;
}
//若串列中已經有了這個TP,不再重復添加;
if (strcmp(tpd_driver_list[i].tpd_device_name, tpd_drv->tpd_device_name) == 0)
return 1;
}
return 0;
}
? 另外,probe函式中還注冊了一個背光通知回呼函式,用于控制TP隨背光變化調整作業方式,回呼函式如下:
//mtk_tpd.c
static int tpd_fb_notifier_callback(struct notifier_block *self, unsigned long event, void *data)
{
......
switch (blank) {
//背光被點亮通知
case FB_BLANK_UNBLANK:
TPD_DMESG("LCD ON Notify\n");
if (g_tpd_drv && tpd_suspend_flag) {
//通過作業佇列方式,呼叫TP的喚醒函式
err = queue_work(touch_resume_workqueue, &touch_resume_work);
if (!err) {
TPD_DMESG("start touch_resume_workqueue failed\n");
return err;
}
}
break;
//背光被熄滅通知
case FB_BLANK_POWERDOWN:
TPD_DMESG("LCD OFF Notify\n");
if (g_tpd_drv)
{
//先取消作業佇列,避免邏輯混亂
err = cancel_work_sync(&touch_resume_work);
if (!err)
TPD_DMESG("cancel touch_resume_workqueue err = %d\n", err);
//直接呼叫TP的休眠函式
g_tpd_drv->suspend(NULL);
tpd_suspend_flag = 1;
}
break;
default:
break;
}
return 0;
}
? TP的設備驅動基本結構就是這些,
3、TP模組驅動
? TP設備驅動搭好了TP驅動的架構,不同的TP模組按照這個架構,主要是實作結構體 tpd_driver_t 中內容,然后通過函式 tpd_driver_add 加入到驅動串列中,下面就以一個實體—ST16XX,看一下TP模組的驅動代碼,
? 首先,通過 module_init 和 module_exit 實作模組的初始化和退出:
//ST16XX_driver.c
static int __init tpd_driver_init(void)
{
//獲取模組在dts中的引數資訊,包括解析度、虛擬按鍵等
tpd_get_dts_info();
TPD_DMESG("Sitronix st16xx touch panel driver init\n");
//添加到 串列tpd_driver_t
if(tpd_driver_add(&tpd_device_driver) < 0)
TPD_DMESG("add generic driver failed\n");
return 0;
}
static void __exit tpd_driver_exit(void)
{
TPD_DMESG("MediaTek ST16XX touch panel driver exit\n");
//從串列tpd_driver_t 移除
tpd_driver_remove(&tpd_device_driver);
}
module_init(tpd_driver_init);
module_exit(tpd_driver_exit);
結構體 tpd_driver_t 的具體實作為:
//ST16XX_driver.c
static struct tpd_driver_t tpd_device_driver = {
.tpd_device_name =CTP_NAME, // TPD_DEVICE,
.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
};
其中,tpd_local_init 原始碼如下:
//ST16XX_driver.c
static int tpd_local_init(void)
{
//添加一個I2C驅動,用于和TP通信
if(i2c_add_driver(&tpd_i2c_driver)!=0) {
TPD_DMESG("unable to add i2c driver.\n");
return -1;
}
//若添加I2C驅動時,并未成功初始化,則洗掉I2C驅動
if (tpd_load_status == 0) {
TPD_DMESG("add error touch panel driver.");
i2c_del_driver(&tpd_i2c_driver);
return -1;
}
......
//電容屏
tpd_type_cap = 1;
return 0;
}
? 可以看出,添加I2C驅動的操作,是 tpd_local_init 的重點,tpd_i2c_driver 實作如下:
//ST16XX_driver.c
static struct i2c_client *i2c_client = NULL;
static const struct i2c_device_id tpd_i2c_id[] ={{CTP_NAME,0},{}};
static const struct of_device_id tpd_of_match[] = {
{.compatible = "mediatek,cap_touch"},
{},
};
static struct i2c_driver tpd_i2c_driver = {
.driver = {
.name = CTP_NAME,
#ifdef CONFIG_OF
.of_match_table = tpd_of_match,
#endif
.owner = THIS_MODULE,
},
.probe = tpd_i2c_probe,
.remove = tpd_i2c_remove,
.detect = tpd_i2c_detect,
.driver.name = CTP_NAME,
.id_table = tpd_i2c_id,
};
還是要讀一下probe函式:
//ST16XX_driver.c
static int tpd_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
......
//賦值I2C地址
client->addr = 0x55;
i2c_client = client;
TPD_DMESG("Sitronix st16xx touch panel i2c probe:%s\n", id->name);
//給TP上電
tpd->reg = regulator_get(tpd->tpd_dev, "vtouch");
if (IS_ERR(tpd->reg))
{
TPD_DMESG("regulator_get() failed!\n");
}
err = regulator_set_voltage(tpd->reg, 2800000, 2800000);
if (err)
{
TPD_DMESG("regulator_set_voltage() failed!\n");
}
err = regulator_enable(tpd->reg);
if (err != 0)
{
TPD_DMESG("Failed to enable reg-vtouch: %d\n", err);
}
//控制RESET腳,使TP復位一下
tpd_gpio_output(GTP_RST_PORT, 1);
msleep(10);
tpd_gpio_output(GTP_RST_PORT, 0);
msleep(10);
tpd_gpio_output(GTP_RST_PORT, 1);
msleep(300);
//讀取TP的ID
retval = tpd_get_st16xx_id();
if (retval < 0)
{
tpd_load_status = 0;
TPD_DMESG("tpd_get_st16xx_id error, Maybe not ST16XX\n");
return -1;
}
#ifdef I2C_SUPPORT_RS_DMA
//如果支持DMA的話,申請DMA記憶體;
I2CDMABuf_va = (u8 *)dma_alloc_coherent(NULL, 4096, &I2CDMABuf_pa, GFP_KERNEL);
if(!I2CDMABuf_va)
{
TPD_DMESG("st16xx Allocate Touch DMA I2C Buffer failed!\n");
return -1;
}
#endif
//注冊EINT腳中斷
tpd_irq_registration();
msleep(100);
//讀取TP狀態
status = st16xx_get_status();
if(status != 6)
{
st16xx_print_version(); //讀取TP韌體版本
tpd_halt = 1;
// upgrade panel here
tpd_halt = 0;
}
//TP 韌體自動升級操作
#ifdef ST_UPGRADE_FIRMWARE
#ifdef ST_FIREWARE_FILE
kthread_run(st_upgrade_fw, "Sitronix", "sitronix_update");
#else
st_upgrade_fw();
#endif
#endif
//創建一個執行緒,用于執行EINT中斷下半部
thread = kthread_run(touch_event_handler, 0, CTP_NAME);
if (IS_ERR(thread)) {
err = PTR_ERR(thread);
TPD_DMESG(CTP_NAME " st16xx failed to create kernel thread: %d\n", err);
}
//初始化完成,TP加載完畢
tpd_load_status = 1;
return 0;
}
? 然后,我們看一下EINT中斷相關的代碼:
//ST16XX_driver.c
static int tpd_irq_registration(void)
{
......
//查找指定節點,獲取相關引數
node = of_find_matching_node(node, touch_of_match);
if (node)
{
//防抖設定
of_property_read_u32_array(node, "debounce", ints, ARRAY_SIZE(ints));
gpio_set_debounce(ints[0], ints[1]);
//設定中斷,包括中斷號、中斷回呼、中斷觸發方式等;
touch_irq = irq_of_parse_and_map(node, 0);
ret = request_irq(touch_irq, tpd_eint_interrupt_handler, IRQF_TRIGGER_FALLING,
"TOUCH_PANEL-eint", NULL); //IRQF_TRIGGER_FALLING IRQF_TRIGGER_RISING
if (ret > 0) {
ret = -1;
TPD_DEBUG("tpd request_irq IRQ LINE NOT AVAILABLE!.\n");
}
}
......
return ret;
}
看下中斷回呼函式:
//ST16XX_driver.c
static irqreturn_t tpd_eint_interrupt_handler(int irq, void *dev_id)
{
tpd_flag = 1;
wake_up_interruptible(&waiter);//喚醒等待佇列
return IRQ_HANDLED;
}
可以看到,中斷回呼只是EINT中斷的上半部,上半部只是喚醒了一個等待佇列,probe函式中創建了一個執行緒,ENIT中斷的下半部放在這個執行緒中執行:
//ST16XX_driver.c
static DECLARE_WAIT_QUEUE_HEAD(waiter); //宣告并創建一個等待佇列
static int touch_event_handler( void *unused )
{
struct sched_param param = { .sched_priority = RTPM_PRIO_TPD };
unsigned char buf[ST16XX_MAX_TOUCHES*4];
int ret = 0,i=0;
int x,y;
u8 touchCount=0;
//設定執行緒調度策略為:時間片輪轉實時調度;
sched_setscheduler(current, SCHED_RR, ¶m);
do
{
set_current_state(TASK_INTERRUPTIBLE);
while (tpd_halt) {tpd_flag = 0; msleep(20);}
//等待等待佇列被喚醒;
wait_event_interruptible(waiter, tpd_flag != 0);
tpd_flag = 0;
TPD_DEBUG_SET_TIME;
set_current_state(TASK_RUNNING);
touchCount=0;
//通過I2C讀取TP資料
ret = tpd_i2c_read(i2c_client, buf, ST16XX_MAX_TOUCHES*4, 0x12);
//決議TP資料
for(i=0;i<ST16XX_MAX_TOUCHES;i++)
{
if(buf[4*i] & 0x80)
{
x = (int)(buf[i*4] & 0x70) << 4 | buf[i * 4 + 1];
y = (int)(buf[i*4] & 0x07) << 8 | buf[i * 4 + 2];
touchCount++;
tpd_down(0,0, x, y, i);
}
}
if(touchCount == 0)
{
tpd_up(0,0, 0,0,0);
}
//上報TP資料
input_sync(tpd->dev);
} while (!kthread_should_stop());
return 0;
}
看到這里,TP通過中斷上報資料的流程就清晰了,
? 最后,我們再看看TP的休眠和喚醒都做了什么:
//ST16XX_driver.c
//休眠函式
static void tpd_suspend(struct device *h)
{
unsigned char buf[2];
int status;
int ret;
TPD_DEBUG("ST16xx call suspend\n");
//通過I2C,控制TP進入休眠模式
buf[0] = 0x02;
buf[1] = 0x02;
ret = tpd_i2c_write(i2c_client, buf, 2);
msleep(100);
//讀取TP狀態,看操作是否成功;
status = st16xx_get_status();
if(status == 5)
TPD_DEBUG("ST16xx go power down mode success\n");
else
TPD_DEBUG("ST16xx go power down mode fail\n");
//禁止EINT中斷;
disable_irq(touch_irq);
tpd_halt = 1;
}
//喚醒函式
static void tpd_resume(struct device *h)
{
TPD_DEBUG("ST16XX call resume\n");
//控制RESET腳復位TP
tpd_gpio_output(GTP_RST_PORT, 1);
msleep(10);
tpd_gpio_output(GTP_RST_PORT, 0);
msleep(10);
tpd_gpio_output(GTP_RST_PORT, 1);
msleep(300);
//使能EINT中斷
enable_irq(touch_irq);
tpd_halt = 0;
}
? 至此,TP驅動的核心代碼就很清晰了,
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/264186.html
標籤:其他
