diff mbox series

Input: atmel_mxt_ts - support KoD knob

Message ID 20241223190416.52871-1-marex@denx.de
State New
Headers show
Series Input: atmel_mxt_ts - support KoD knob | expand

Commit Message

Marek Vasut Dec. 23, 2024, 7:03 p.m. UTC
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@gmail.com> 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@denx.de>
---
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@gmail.com>
Cc: Michael <mksgong@gmail.com>
Cc: Nick Dyer <nick@shmanahar.org>
Cc: linux-input@vger.kernel.org
---
 drivers/input/touchscreen/atmel_mxt_ts.c | 111 +++++++++++++++++++++++
 1 file changed, 111 insertions(+)
diff mbox series

Patch

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