MTK平台TP驱动详解

本博文将讲解基于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);
#else
    i2c_register_board_info(0, &i2c_tpd, 1);
#endif
    if (tpd_driver_add(&tpd_device_driver) < 0)
        GTP_INFO("add generic driver failed\n");

    return 0;
}

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))};

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

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

其中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
};

这几个选项都是必须的。

其中的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_PROTECT
    clk_tick_cnt = 2 * HZ;   // HZ: clock ticks in 1 second generated by system
    GTP_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_DMA
    tpd->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);
#endif
    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;
    }
    input_set_abs_params(tpd->dev, ABS_MT_TRACKING_ID, 0, (GTP_MAX_TOUCH-1), 0, 0);

#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

#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\n", __FUNCTION__, __LINE__);
    tpd_type_cap = 1;

    return 0;
}

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

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;
}

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,
};

这里面的内容都要自己去实现,特别是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);

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

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

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;

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

7、设置TP类型为电容屏

tpd_type_cap = 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_KEY
    s32 idx = 0;
    #endif

    #ifdef TPD_PROXIMITY
    struct hwmsen_object obj_ps;
    #endif

    i2c_client_point = client;
    ret = tpd_power_on(client);

    if (ret < 0)
    {
        GTP_ERROR("I2C communication ERROR!");
        goto out;
    }

    #ifdef VELOCITY_CUSTOM
    tpd_v_magnify_x = TPD_VELOCITY_CUSTOM_X;
    tpd_v_magnify_y = TPD_VELOCITY_CUSTOM_Y;
    #endif

    ret = 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 system
    gt91xx_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_NODE
    init_wr_node(client);
    #endif

    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;
    }


    #if 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

    #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 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);


    #if GTP_AUTO_UPDATE
    ret = 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_PROTECT
    gtp_esd_switch(client, SWITCH_ON);
    #endif

    tpd_load_status = 1;

    return 0;
out:
    return -1;
}

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

1、TPD_PROXIMITY

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

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

#ifdef TPD_PROXIMITY
   struct 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

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;

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

obj_ps.sensor_operate = tpd_ps_operate; 

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

hwmsen_attach(ID_PROXIMITY, &obj_ps)

这是将接近感应传感器设备驱动添加到 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 nothing
            break;

        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

当通话的时候,会调用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_PROXIMITY
    if (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 data
        GTP_DEBUG(" ps change\n");
        GTP_DEBUG("PROXIMITY STATUS:0x%02X\n", tpd_proximity_detect);
        //map and store data to hwm_sensor_data
        sensor_data.values[0] = tpd_get_ps_value();
        sensor_data.value_divide = 1;
        sensor_data.status = SENSOR_STATUS_ACCURACY_MEDIUM;
        //report to the up-layer
        ret = hwmsen_get_interrupt_data(ID_PROXIMITY, &sensor_data); // 通过中断的方式将接近或者远离的状态上报给 PROXIMITY sensor

        if (ret)
        {
            GTP_ERROR("Call hwmsen_get_interrupt_data fail = %d\n", err);
        }
    }
#endif

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

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

2、tpd_power_on()

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

1、给ic上电

#ifdef MT6573
    // power on CTP
    mt_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_CUSTOM
        hwPowerOn(TPD_POWER_SOURCE_CUSTOM, VOL_2800, "TP");
    #else
        hwPowerOn(MT65XX_POWER_LDO_VGP2, VOL_2800, "TP");
    #endif
    #ifdef TPD_POWER_SOURCE_1800
        hwPowerOn(TPD_POWER_SOURCE_1800, VOL_1800, "TP");
    #endif

#endif

关于上电的动作可参考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_MODE
    if (CHIP_TYPE_GT9F == gtp_chip_type)
    {
        return;
    }
#endif

    gtp_int_sync(100);  // for dbl-system
#if GTP_ESD_PROTECT
    gtp_init_ext_watchdog(i2c_client_point);
#endif
}

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

GPIO_CTP_EN_PIN
GPIO_CTP_RST_PIN
GPIO_CTP_EINT_PIN

这几个宏在以下路径中的文件进行配置对应的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);

测试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);

至于初始化的内容会放到一个全局的数组之中,这项工作一般都要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;
}

创建名为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;
}

对于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完毕之后会打开中断使能

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);
}

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

tpd_down()和tpd_up()

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

tpd_suspend()和tpd_resume()

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

  • 11
    点赞
  • 91
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
一、 手机CAMERA的物理结构: ........................................................................................ - 4 - 二、 CAMERA的成像原理: ................................................................................................. - 4 - 三、 CAMERA常见的数据输出格式:.................................................................................. - 5 - 四、 阅读CAMERA的规格书(以TRULY模组OV5647_RAW为例): ........................... - 6 - 五、 CAMERA的硬件原理图及引脚 ..................................................................................... - 7 - 1、 电源部分:.................................................................................................................... - 7 - 2、 SENSOR INPUT部分: ................................................................................................... - 7 - 3、 SENSOR OUTPUT部分: ............................................................................................... - 7 - 4、 I2C部分:SCL,I2C时钟信号线和SDA,I2C数据信号线。 .................................. - 7 - 六、 MTK平台CAMERA驱动架构: .................................................................................. - 8 - 七、 MTK平台CAMERA相关代码文件(以下代码均为MTK6575平台): .................... - 9 - 1、 CAMERASENSOR驱动相关文件 .................................................................................... - 9 - 2、 SENSOR ID 和一些枚举类型的定义 ............................................................................. - 9 - 3、 SENSOR供电 .................................................................................................................. - 9 - 4、 KERNEL SPACE的SENSORLIST,IMGSENSOR模块注册 ............................................... - 9 - 5、 USER SPACE的SENSORLIST,向用户空间提供支持的SENSORLIST ......................... - 10 - 6、 SENSOR 效果调整的接口 ............................................................................................ - 10 - 八、 CAMERA模块驱动、设备与总线结构: ..................................................................... - 11 - A) 驱动的注册: .................................................................................................................. - 11 - B) 设备的注册: .................................................................................................................. - 11 - C) 总线的匹配: .................................................................................................................. - 12 - 九、 CAMERA驱动工作流程: ............................................................................................- 13 - 十、 CAMERA驱动添加、调试流程:.......
mtk平台lcd驱动框架详解可以从Mtkfb.c文件开始解释。在该文件中,我们可以看到定义了一个名为mtkfb_driver的结构体变量,该变量是一个platform_driver结构体类型,其中包含了一些成员函数的指针,如probe、remove、suspend、resume和shutdown等。这些函数是用来处理与MTK平台上的LCD显示相关的操作。例如,当系统探测到MTK平台上的LCD设备时,probe函数会被调用来初始化LCD驱动;当系统要移除LCD设备时,remove函数会被调用来卸载LCD驱动。 除了上述的成员函数指针外,mtkfb_driver结构体变量还包含了一些其他成员,如name、pm和bus等。其中,name成员指定了驱动的名称,pm成员用于指定与电源管理相关的操作函数,而bus成员则指定了驱动所属的总线类型。 需要注意的是,以上只是mtk平台上lcd驱动的框架概述,具体实现细节可能因不同的平台而有所不同。此外,本文可能存在错误或不够深入,请参考相关资料以获取更详细的信息。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [MTK平台LCD驱动框架详解(一)](https://blog.csdn.net/xuan_h/article/details/38519975)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值