Atmel touch screen Driver移植

Atmel toch MXT641T_AT && MXT641T_CCU

MX641T_AT 和 MXT641T_CCU属于同一系列IC,AT 是车规级,CCU属于消费级。驱动主体框架部分没有大的区别细节部分有些差异。

本驱动主要结合frescale中的driver对atmel_touch进行讲解
首先给出驱动在dts中的配置

//电源配置

atmel_vdd: vdd {
            compatible = "regulator-fixed";
            regulator-name = "vdd";
            regulator-min-microvolt = <3300000>;
            regulator-max-microvolt = <3300000>;
            regulator-always-on;
        };

        atmel_avdd: avdd {
            compatible = "regulator-fixed";
            regulator-name = "avdd";
            regulator-min-microvolt = <3300000>;
            regulator-max-microvolt = <3300000>;
            regulator-always-on;
        };

atmel_mxt_ts@4a{
        compatible = "atmel,mxt641t";
        reg = <0x4a>;                         //touch IC挂在I2C总线下地址0x4a
        interrupt-parent = <&gpio7>;          //中断配置
        interrupts = <11 2>;
        wakeup-gpios = <&gpio7 11 0>;
        atmel,reset-gpio = <&pca9555_b 5 0>;
        //触摸按键设置,由于AT 和CCU采用不同的按键模式因此在相应同一个事件时按键映射有差异
        atmel,key-buttons_at = <158 102 63 305 115 139 114 306 106 113>;
        atmel,key-buttons_ccu = <106 114 306 113 0 0 0 0 158 102 0 63 305 0 115 139>;
        atmel,suspend-mode = <2>;
        atmel,input_name = "atmel_touchscreen";
        atmel,cfg_name_at = "atmel_mxt641t_at.raw";
        atmel,cfg_name_ccu = "atmel_mxt641t_ccu.raw";  //升级firmware所需的配置文件名称
}

驱动代码部分从probe函数开始解析。

static int mxt_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
    struct mxt_data *data;
    const struct mxt_platform_data *pdata;
    int error;

    //probe_1: 解析dts文件,配置gpio, regulator, enable.注册等常规驱动基本操作
    pdata = dev_get_platdata(&client->dev);
    if (!pdata) {
        pdata = mxt_parse_dt(client);
        if (IS_ERR(pdata))
            return PTR_ERR(pdata);
    }

    data = kzalloc(sizeof(struct mxt_data), GFP_KERNEL);
    if (!data)
        return -ENOMEM;

    snprintf(data->phys, sizeof(data->phys), "i2c-%u-%04x/input0",
         client->adapter->nr, client->addr);

    data->client = client;
    data->pdata = pdata;
    i2c_set_clientdata(client, data);

    if (data->pdata->cfg_name)
        mxt_update_file_name(&data->client->dev,
                     &data->cfg_name,
                     data->pdata->cfg_name,
                     strlen(data->pdata->cfg_name));

    if (data->pdata->cfg_name_1)
        mxt_update_file_name(&data->client->dev,
                     &data->cfg_name_1,
                     data->pdata->cfg_name_1,
                     strlen(data->pdata->cfg_name_1));

    init_completion(&data->reset_completion);
    init_completion(&data->crc_completion);
    init_completion(&data->chg_completion);
    mutex_init(&data->debug_msg_lock);

    if(padata->suspend_mode == MXT_SUSPEND_REGULATOR){
        error = mxt_acquire_irq(data);
        if(error)
            goto err_free_mem;
        error = mxt_probe_regulators(data);
        if(error)
            goto err_free(data);
        disable_irq(data->irq); 
    }


    error = sysfs_create_group(&client->dev.kobj, &mxt_attr_group);    //创建sys文件节点
    if (error) {
        dev_err(&client->dev, "Failure %d creating sysfs group\n",
            error);
        goto err_free_irq;
    }

    sysfs_bin_attr_init(&data->mem_access_attr);
    data->mem_access_attr.attr.name = "mem_access";
    data->mem_access_attr.attr.mode = S_IRUGO | S_IWUSR;
    data->mem_access_attr.read = mxt_mem_access_read;
    data->mem_access_attr.write = mxt_mem_access_write;
    data->mem_access_attr.size = data->mem_size;

    if (sysfs_create_bin_file(&client->dev.kobj,
                  &data->mem_access_attr) < 0) {
        dev_err(&client->dev, "Failed to create %s\n",
            data->mem_access_attr.attr.name);
        goto err_remove_sysfs_group;
    }

    //probe_2: touch IC初始化核心操作
    error = mxt_initialize(data);
    if(error)
        goto err_remove_mem_access;

    return 0;

err_remove_mem_accsess:
    sysfs_remove_bin_file(&client->data.kobj, &data->mem_access_attr);
    data->mem_access_attr.attr.name = NULL;

err_remove_sysfs_group:
    sysfs_remove_group(&client->dev.kobj, &mxt_attr_group);
err_free_irq:
    if (data->irq)
        free_irq(data->irq, data);
err_free_mem:
    kfree(data);
    return error;
}

probe_2: touch IC初始化核心操作
mxt_initialize主要完成一下几个方面的工作:
1.读取touch IC的基本信息---这部分信息比较难理解,需要结合datasheet去理解,这些地方不是关键点
2.检测bootloader基本状态
3.申请中断
4.检测是否需要更新固件

static int mxt_initialize(struct mxt_data *data)
{
    struct i2c_client *client = data->client;
    int recovery_attempts = 0;
    int error;

    while (1) {
        error = mxt_read_info_block(data);
        if (!error)
            break;

        /* Check bootloader state */
        error = mxt_probe_bootloader(data, false);
        if (error) {
            dev_info(&client->dev, "Trying alternate bootloader address\n");
            error = mxt_probe_bootloader(data, true);
            if (error) {
                /* Chip is not in appmode or bootloader mode */
                return error;
            }
        }

        /* OK, we are in bootloader, see if we can recover */
        if (++recovery_attempts > 1) {
            dev_err(&client->dev, "Could not recover from bootloader mode\n");
            /* * We can reflash from this state, so do not * abort initialization. */
            data->in_bootloader = true;
            return 0;
        }

        /* Attempt to exit bootloader into app mode */
        mxt_send_bootloader_cmd(data, false);
        msleep(MXT_FW_RESET_TIME);
    }

    error = mxt_check_retrigen(data);
    if (error)
        goto err_free_object_table;

    error = mxt_acquire_irq(data);
    if (error)
        goto err_free_object_table;

    error = mxt_debug_msg_init(data);
    if (error)
        goto err_free_object_table;

    if(data->info->variant_id == MXT641CCU_VARIANT_ID)
        data->cfg_name = data->cfg_name_1;
    printk("AJ:update config file %s\n", data->cfg_name);

    if (data->cfg_name) {
        //请求firmware更新mxt_config_cb中若发现需要更新配置文件,则会更新IC中的配置文件,否则直接使用IC中配置文件
        //同时mxt_config_cb-->mxt_configure_objects(data, cfg)--->update_cfg
        error = request_firmware_nowait(THIS_MODULE, true,
                    data->cfg_name, &data->client->dev,
                    GFP_KERNEL, data, mxt_config_cb);
        if (error) {
            dev_err(&client->dev, "Failed to invoke firmware loader: %d\n",
                error);
            goto err_free_object_table;
        }
    } else {
        error = mxt_configure_objects(data, NULL);
        if (error)
            goto err_free_object_table;
    }

    return 0;

err_free_object_table:
    mxt_free_object_table(data);
    return error;
}

申请中断--中断处理函数mxt_interrupt,该函数是触摸事件处理中断函数

static int mxt_acquire_irq(struct mxt_data *data)
{
    int error;

    if (!data->irq) {
        error = request_threaded_irq(data->client->irq, NULL,
                mxt_interrupt,
                data->pdata->irqflags | IRQF_ONESHOT,
                data->client->name, data);
        if (error) {
            dev_err(&data->client->dev, "Error requesting irq\n");
            return error;
        }

        /* Presence of data->irq means IRQ initialised */
        data->irq = data->client->irq;
    } else {
        enable_irq(data->irq);
    }

    if (data->object_table && data->use_retrigen_workaround) {
        error = mxt_process_messages_until_invalid(data);
        if (error)
            return error;
    }

    return 0;
}

中断事件处理函数-mxt_process_messages_t44(data)--最终调用到--->mxt_process_messages

static irqreturn_t mxt_interrupt(int irq, void *dev_id)
{
    struct mxt_data *data = dev_id;

    complete(&data->chg_completion);

    if (data->in_bootloader) {
        if (data->flash && &data->flash->work)
            cancel_delayed_work_sync(&data->flash->work);

        return IRQ_RETVAL(mxt_check_bootloader(data));
    }

    if (!data->object_table)
        return IRQ_HANDLED;

    if (data->T44_address) {
        return mxt_process_messages_t44(data);
    } else {
        return mxt_process_messages(data);
    }
}

主要处理触摸消息手势以及软key事件-–mxt_proc_message--触摸事件处理

static irqreturn_t mxt_process_messages_t44(struct mxt_data *data)
{
    struct device *dev = &data->client->dev;
    int ret;
    u8 count, num_left;

    /* Read T44 and T5 together */
    ret = __mxt_read_reg(data->client, data->T44_address,
        data->T5_msg_size + 1, data->msg_buf);
    if (ret) {
        dev_err(dev, "Failed to read T44 and T5 (%d)\n", ret);
        return IRQ_NONE;
    }

    count = data->msg_buf[0];    //记录触摸次数: 如滑动触摸为多次触摸组成需要进行多次处理

    if (count == 0) {
        /*
         * This condition is caused by the CHG line being configured
         * in Mode 0. It results in unnecessary I2C operations but it
         * is benign.
         */
        dev_dbg(dev, "Interrupt triggered but zero messages\n");
        return IRQ_NONE;
    } else if (count > data->max_reportid) { dev_err(dev, "T44 count %d exceeded max report id\n", count); count = data->max_reportid; }

    /* Process first message */
    ret = mxt_proc_message(data, data->msg_buf + 1);
    if (ret < 0) {
        dev_warn(dev, "Unexpected invalid message\n");
        return IRQ_NONE;
    }

    num_left = count - 1;

    /* Process remaining messages if necessary */
    if (num_left) {
        ret = mxt_read_and_process_messages(data, num_left);
        if (ret < 0)
            goto end;
        else if (ret != num_left)
            dev_warn(dev, "Unexpected invalid message\n");
    }

end:
    if (data->update_input) { mxt_input_sync(data); data->update_input = false; }

    return IRQ_HANDLED;
}

//mxt_proc_message根据不同的report_id对消息进行不同的响应,触摸消息为T100,按键为T15

static int mxt_proc_message(struct mxt_data *data, u8 *message)
{
    u8 report_id = message[0];
    bool dump = data->debug_enabled;

    if (report_id == MXT_RPTID_NOMSG)
        return 0;

    if (report_id == data->T6_reportid) { mxt_proc_t6_messages(data, message); } else if (report_id >= data->T42_reportid_min && report_id <= data->T42_reportid_max) { mxt_proc_t42_messages(data, message); } else if (report_id == data->T48_reportid) { mxt_proc_t48_messages(data, message); } else if (!data->input_dev || data->suspended) { /* * Do not report events if input device is not * yet registered or returning from suspend */ mxt_dump_message(data, message); } else if (report_id >= data->T9_reportid_min && report_id <= data->T9_reportid_max) { mxt_proc_t9_message(data, message); } else if (report_id >= data->T100_reportid_min && report_id <= data->T100_reportid_max) { mxt_proc_t100_message(data, message); } else if (report_id == data->T19_reportid) { mxt_input_button(data, message); data->update_input = true; } else if (report_id >= data->T63_reportid_min && report_id <= data->T63_reportid_max) { mxt_proc_t63_messages(data, message); } else if (report_id >= data->T15_reportid_min && report_id <= data->T15_reportid_max) { mxt_proc_t15_messages(data, message); } else { dump = true; }

    if (dump)
        mxt_dump_message(data, message);

    if (data->debug_v2_enabled)
        mxt_debug_msg_add(data, message);

    return 1;
}

首先看下按键响应消息:msg 的第0个byte为report_id, msg[1] objcet, msg[2], msg[3]为按键bit
t15又可以根据report_id复用msg[2],msg[3]因此组合成更多的key事件。mgs[2],msg[3]每个bit位对应
一个key事件。

static void mxt_proc_t15_messages(struct mxt_data *data, u8 *msg)
{
    struct input_dev *input_dev = data->input_dev;
    struct device *dev = &data->client->dev;
    int key;
    bool curr_state, new_state;
    bool sync = false;
    u16 t15_2_msg = (msg[2] << 8) |msg[3];
    unsigned long keystates ;
    u8 report_id = msg[0];

    printk("AJ: T15 report_id %d\n", report_id);
    if(data->info->variant_id == MXT641AT_VARIANT_ID){
        keystates = le32_to_cpu(msg[2]);    //大小端转换
        for (key = 0; key < data->pdata->t15_num_keys; key++) {
            curr_state = test_bit(key, &data->t15_keystatus);    //检测置1bit位 
            new_state = test_bit(key, &keystates);    

            /* for T15 key */
            if (!curr_state && new_state) {
                dev_dbg(dev, "T15 key press: %u\n", key);
                __set_bit(key, &data->t15_keystatus);
                if(report_id == 5 )
                    key += 8;
                input_event(input_dev, EV_KEY,
                        data->pdata->t15_keymap[key], 1);    //向应用层上报key事件
                sync = true;
            } else if (curr_state && !new_state) {
                dev_dbg(dev, "T15 key release: %u\n", key);

                __clear_bit(key, &data->t15_keystatus);
                if( report_id == 5 )
                    key += 8;
                input_event(input_dev, EV_KEY,
                        data->pdata->t15_keymap[key], 0);
                sync = true;
            }
        }
    }else if(data->info->variant_id == MXT641CCU_VARIANT_ID){
        keystates = le32_to_cpu(t15_2_msg);
        for (key = 0; key < 16; key++) {
            curr_state = test_bit(key, &data->t15_keystatus);
            new_state = test_bit(key, &keystates);
            /* for T15 key */
            if (!curr_state && new_state) {
                dev_dbg(dev, "T15 key press: %u\n", key);
                __set_bit(key, &data->t15_keystatus);
                input_event(input_dev, EV_KEY,
                        data->pdata->t15_keymap_cu[key], 1);
                printk("AJ_down1: report_id:%d,Key:%d,num:%d\n", report_id, data->pdata->t15_keymap_cu[key], key);
                sync = true;
            } else if (curr_state && !new_state) {
                dev_dbg(dev, "T15 key release: %u\n", key);

                __clear_bit(key, &data->t15_keystatus);
                if( report_id == 5 )
                    key += 8;
                input_event(input_dev, EV_KEY,
                        data->pdata->t15_keymap_cu[key], 0);
                printk("AJ_up1: report_id:%d,Key:%d,num:%d\n", report_id, data->pdata->t15_keymap_cu[key], key);
                sync = true;
            }
        }
        }

    if (sync)
        input_sync(input_dev);
}

触摸事件处理函数多点触摸处理函数

static void mxt_proc_t100_message(struct mxt_data *data, u8 *message)
{
    struct device *dev = &data->client->dev;
    struct input_dev *input_dev = data->input_dev;
    int id;
    u8 status;
    u8 type;
    int x;
    int y;
    int tool;
    u8 major = 0;
    u8 pressure = 0;
    u8 orientation = 0;
    bool active = false;
    bool hover = false;
    bool eraser = false;
    bool barrel = false;

    id = message[0] - data->T100_reportid_min - 2;

    /* ignore SCRSTATUS events */
    if (id < 0)
        return;

    status = message[1];
    x = (message[3] << 8) | message[2];
    y = (message[5] << 8) | message[4];

    if (status & MXT_T100_DETECT) {
        type = (status & MXT_T100_TYPE_MASK) >> 4;

        switch (type) {
        case MXT_T100_TYPE_HOVERING_FINGER:
            hover = true;
            /* fall through */
        case MXT_T100_TYPE_FINGER:
        case MXT_T100_TYPE_GLOVE:
            active = true;
            tool = MT_TOOL_FINGER;

            if (data->t100_aux_area)
                major = message[data->t100_aux_area];
            if (data->t100_aux_ampl)
                pressure = message[data->t100_aux_ampl];
            if (data->t100_aux_vect)
                orientation = message[data->t100_aux_vect];

            break;

        case MXT_T100_TYPE_PASSIVE_STYLUS:
            active = true;
            tool = MT_TOOL_PEN;

            /* Passive stylus is reported with size zero so * hardcode */
            major = MXT_TOUCH_MAJOR_DEFAULT;

            if (data->t100_aux_ampl)
                pressure = message[data->t100_aux_ampl];

            break;

        case MXT_T100_TYPE_ACTIVE_STYLUS:
            /* stylus in range, but position unavailable */
            if (!(message[6] & MXT_T107_STYLUS_HOVER))
                break;

            active = true;
            tool = MT_TOOL_PEN;
            major = MXT_TOUCH_MAJOR_DEFAULT;
            eraser = message[6] & MXT_T107_STYLUS_BUTTON0;
            barrel = message[6] & MXT_T107_STYLUS_BUTTON1;

            if (!(message[6] & MXT_T107_STYLUS_TIPSWITCH))
                hover = true;
            else if (data->stylus_aux_pressure)
                pressure = message[data->stylus_aux_pressure];

            break;

        case MXT_T100_TYPE_LARGE_TOUCH:
            /* Ignore suppressed touch */
            break;

        default:
            dev_dbg(dev, "Unexpected T100 type\n");
            return;
        }
    }

    if (hover) {
        pressure = 0;
        major = 0;
    } else if (active) {
        /* * Values reported should be non-zero if tool is touching the * device */
        if (pressure == 0)
            pressure = MXT_PRESSURE_DEFAULT;

        if (major == 0)
            major = MXT_TOUCH_MAJOR_DEFAULT;
    }

    input_mt_slot(input_dev, id);

    if (active) {
        dev_dbg(dev, "[%u] type:%u x:%u y:%u a:%02X p:%02X v:%02X\n",
            id, type, x, y, major, pressure, orientation);

        input_mt_report_slot_state(input_dev, tool, 1);
        input_report_abs(input_dev, ABS_MT_POSITION_X, x);
        input_report_abs(input_dev, ABS_MT_POSITION_Y, y);
        input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, major);
        input_report_abs(input_dev, ABS_MT_PRESSURE, pressure);
        input_report_abs(input_dev, ABS_MT_ORIENTATION, orientation);

        input_report_key(input_dev, BTN_STYLUS, eraser);
        input_report_key(input_dev, BTN_STYLUS2, barrel);
    } else {
        dev_dbg(dev, "[%u] release\n", id);

        /* close out slot */
        input_mt_report_slot_state(input_dev, 0, 0);
    }

    data->update_input = true;
}

你可能感兴趣的:(kernel)