Add support for T152 KoD knob events [1]. The KoD touch controller family supports up to two knobs attached to the glass. Each knob can be turned in either direction and the touch controller processes the event and reports the knob position for each knob. Each knob is also pressure sensitive, the pressure is reported as well. Each knob also supports center press and additional buttons, which are reported as BTN_0/BTN_1 for the center press for each knob, and BTN_A/BTN_B for the additional buttons on the knob. The knob is similar to Dell Canvas 27 knob already supported by hid-multitouch, except it is non-removable and there can be up to two such knobs . This implementation is extracted and heavily reworked from Atmel downstream patchset work by Michael <mksgong@xxxxxxxxx> from [2] branch master as of commit 9c77fbf32982 ("Merge pull request #35 from atmel-maxtouch/20240103_HA_protocol_fixes"). [1] https://www.microchip.com/en-us/products/touch-and-gesture/maxtouch-touchscreen-controllers/kod-family [2] https://github.com/atmel-maxtouch/maXTouch_linux Signed-off-by: Marek Vasut <marex@xxxxxxx> --- Note: I am not sure whether reporting the second wheel as GAS/REL_HWHEEL is the right thing to do, I don't think it is. Maybe there is some better way to handle multiple knobs ? --- Cc: Dmitry Torokhov <dmitry.torokhov@xxxxxxxxx> Cc: Michael <mksgong@xxxxxxxxx> Cc: Nick Dyer <nick@xxxxxxxxxxxxx> Cc: linux-input@xxxxxxxxxxxxxxx --- drivers/input/touchscreen/atmel_mxt_ts.c | 111 +++++++++++++++++++++++ 1 file changed, 111 insertions(+) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 6cd1c87eb1877..04b979e1501d2 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -75,6 +75,7 @@ #define MXT_SPT_CTECONFIG_T46 46 #define MXT_SPT_DYNAMICCONFIGURATIONCONTAINER_T71 71 #define MXT_TOUCH_MULTITOUCHSCREEN_T100 100 +#define MXT_SPT_WIDGET_T152 152 /* MXT_GEN_MESSAGE_T5 object */ #define MXT_RPTID_NOMSG 0xff @@ -186,6 +187,22 @@ enum t100_type { MXT_T100_TYPE_LARGE_TOUCH = 6, }; +/* T152 Message Types */ +#define MXT_BUTTON_KNOB 0x02 +#define MXT_POSITION_KNOB 0x03 + +/* T152 Widget Object */ +#define MXT_T152_MAXDETENT 0x0a + +/* T152 Knob masks */ +#define MXT_KNOB_TYPE_MASK 0x07 +#define MXT_KNOB_EVENT_MASK 0x07 + +/* T152 Position Events */ +#define MXT_POS_PUSH_MOVE 0x03 +#define MXT_POS_PUSH 0x04 +#define MXT_POS_UNPUSH 0x06 + #define MXT_DISTANCE_ACTIVE_TOUCH 0 #define MXT_DISTANCE_HOVERING 1 @@ -336,6 +353,10 @@ struct mxt_data { u8 T97_reportid_max; u8 T100_reportid_min; u8 T100_reportid_max; + u16 T152_address; + u8 T152_reportid_min; + u8 T152_reportid_max; + u8 T152_obj_size; /* for fw update in bootloader */ struct completion bl_completion; @@ -1026,6 +1047,41 @@ static void mxt_proc_t100_message(struct mxt_data *data, u8 *message) data->update_input = true; } +static void mxt_proc_t152_messages(struct mxt_data *data, u8 *message) +{ + struct input_dev *input_dev = data->input_dev; + int id = message[0] - data->T152_reportid_min; + u8 msg_type = message[1] & MXT_KNOB_TYPE_MASK; + u16 state; + + if (msg_type == MXT_BUTTON_KNOB) { + state = get_unaligned_le16(&message[5]); + input_report_key(data->input_dev, BTN_0, !!(state & BIT(0))); + input_report_key(data->input_dev, BTN_1, !!(state & BIT(1))); + input_sync(data->input_dev); + } else if (msg_type == MXT_POSITION_KNOB) { + input_mt_report_slot_state(input_dev, MT_TOOL_DIAL, 1); + + input_report_abs(input_dev, id ? ABS_GAS : ABS_WHEEL, + get_unaligned_le16(&message[5])); + input_report_abs(input_dev, ABS_PRESSURE, message[8]); + + /* Knob turn CCW is 0xffff or -1 , CW is 1 */ + input_report_rel(input_dev, id ? REL_HWHEEL : REL_WHEEL, + (s16)get_unaligned_le16(&message[3])); + + state = message[2] & MXT_KNOB_EVENT_MASK; + if (state == MXT_POS_PUSH || state == MXT_POS_PUSH_MOVE) + input_report_key(input_dev, id ? BTN_B : BTN_A, 1); + else if (state == MXT_POS_UNPUSH) + input_report_key(input_dev, id ? BTN_B : BTN_A, 0); + + input_mt_report_slot_inactive(input_dev); + + input_sync(input_dev); + } +} + static int mxt_proc_message(struct mxt_data *data, u8 *message) { u8 report_id = message[0]; @@ -1053,6 +1109,9 @@ static int mxt_proc_message(struct mxt_data *data, u8 *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->T152_reportid_min && + report_id <= data->T152_reportid_max) { + mxt_proc_t152_messages(data, message); } else if (report_id == data->T19_reportid) { mxt_input_button(data, message); data->update_input = true; @@ -1747,6 +1806,8 @@ static void mxt_free_object_table(struct mxt_data *data) data->T97_reportid_max = 0; data->T100_reportid_min = 0; data->T100_reportid_max = 0; + data->T152_reportid_min = 0; + data->T152_reportid_max = 0; data->max_reportid = 0; } @@ -1841,6 +1902,12 @@ static int mxt_parse_object_table(struct mxt_data *data, /* first two report IDs reserved */ data->num_touchids = object->num_report_ids - 2; break; + case MXT_SPT_WIDGET_T152: + data->T152_address = object->start_address; + data->T152_reportid_min = min_id; + data->T152_reportid_max = max_id; + data->T152_obj_size = mxt_obj_size(object); + break; } end_address = object->start_address @@ -2235,6 +2302,50 @@ static int mxt_initialize_input_device(struct mxt_data *data) EV_KEY, data->t15_keymap[i]); } + /* For T152 knobs */ + if (data->T152_address) { + u8 detent_ofs = data->T152_address + MXT_T152_MAXDETENT; + u8 bufp, bufs; + + error = __mxt_read_reg(data->client, detent_ofs, 1, &bufp); + if (error) + goto err_free_mem; + + error = __mxt_read_reg(data->client, detent_ofs + data->T152_obj_size, + 1, &bufs); + if (error) + goto err_free_mem; + + if (bufp || bufs) { + input_set_abs_params(input_dev, ABS_MT_TOOL_TYPE, + 0, MT_TOOL_MAX, 0, 0); + + /* Pressure reporting on both knobs */ + input_set_capability(input_dev, EV_ABS, ABS_PRESSURE); + input_set_abs_params(input_dev, ABS_PRESSURE, 0, 255, 0, 0); + } + + /* First knob */ + if (bufp) { + input_set_capability(input_dev, EV_KEY, BTN_0); /* Center */ + input_set_capability(input_dev, EV_KEY, BTN_A); /* Push */ + input_set_capability(input_dev, EV_REL, REL_WHEEL); + input_set_capability(input_dev, EV_ABS, ABS_WHEEL); + input_set_abs_params(input_dev, ABS_WHEEL, 0, bufp - 1, 0, 0); + input_dev->relbit[0] |= BIT_MASK(REL_WHEEL); + } + + /* Second knob */ + if (bufs) { + input_set_capability(input_dev, EV_KEY, BTN_1); /* Center */ + input_set_capability(input_dev, EV_KEY, BTN_B); /* Push */ + input_set_capability(input_dev, EV_REL, REL_HWHEEL); + input_set_capability(input_dev, EV_ABS, ABS_GAS); + input_set_abs_params(input_dev, ABS_GAS, 0, bufs - 1, 0, 0); + input_dev->relbit[0] |= BIT_MASK(REL_HWHEEL); + } + } + input_set_drvdata(input_dev, data); error = input_register_device(input_dev); -- 2.45.2