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