input: touchscreen: stm: export to the touch_offload driver

The touch_offload driver makes it possible for the fts driver to export
heatmap and coordinate data to a user space consumer. The fts driver is
responsible for initializing the touch_offload driver with information
about its supported capabilities. Then, as touch events occur, the fts
driver must capture required heatmap/coordinate frames, load it into
buffers provided by the touch_offload driver, and submit the buffers
back to the touch_offload driver. Finally, the fts driver must report
touch input events through a callback provided to touch_offload.

Bug: 143723547
Bug: 139955910
Signed-off-by: Steve Pfetsch <spfetsch@google.com>
Change-Id: Ifb154ffa89a71b52d2426caf1486cf63404a4211
diff --git a/fts.c b/fts.c
index 69dc037..0d8205e 100644
--- a/fts.c
+++ b/fts.c
@@ -61,6 +61,8 @@
 #include <linux/input/mt.h>
 #endif
 
+#include <linux/input/touch_offload.h>
+
 #include <drm/drm_panel.h>
 #include <video/display_timing.h>
 
@@ -2870,7 +2872,6 @@
 	if (y > info->board->y_axis_max)
 		y = info->board->y_axis_max;
 
-	input_mt_slot(info->input_dev, touchId);
 	switch (touchType) {
 #ifdef STYLUS_MODE
 	case TOUCH_TYPE_STYLUS:
@@ -2911,12 +2912,19 @@
 		goto no_report;
 	}
 
+
+
+#ifdef CONFIG_TOUCHSCREEN_OFFLOAD
+	info->offload.coords[touchId].x = x;
+	info->offload.coords[touchId].y = y;
+	info->offload.coords[touchId].status = COORD_STATUS_FINGER;
+
+	if (!info->offload.offload_running) {
+#endif
+
+	input_mt_slot(info->input_dev, touchId);
 	input_report_key(info->input_dev, BTN_TOUCH, touch_condition);
 	input_mt_report_slot_state(info->input_dev, tool, 1);
-
-	/* pr_info("%s : TouchID = %d,Touchcount = %d\n", __func__,
-	  *	touchId,touchcount); */
-
 	input_report_abs(info->input_dev, ABS_MT_POSITION_X, x);
 	input_report_abs(info->input_dev, ABS_MT_POSITION_Y, y);
 	input_report_abs(info->input_dev, ABS_MT_TOUCH_MAJOR, major);
@@ -2928,6 +2936,10 @@
 #ifndef SKIP_DISTANCE
 	input_report_abs(info->input_dev, ABS_MT_DISTANCE, distance);
 #endif
+
+#ifdef CONFIG_TOUCHSCREEN_OFFLOAD
+	}
+#endif
 	/* pr_info("%s :  Event 0x%02x - ID[%d], (x, y) = (%3d, %3d)
 	 * Size = %d\n",
 	  *	__func__, *event, touchId, x, y, touchType); */
@@ -2951,9 +2963,6 @@
 	touchType = event[1] & 0x0F;
 	touchId = (event[1] & 0xF0) >> 4;
 
-	input_mt_slot(info->input_dev, touchId);
-
-	input_report_abs(info->input_dev, ABS_MT_PRESSURE, 0);
 	switch (touchType) {
 #ifdef STYLUS_MODE
 	case TOUCH_TYPE_STYLUS:
@@ -2984,15 +2993,20 @@
 		return false;
 	}
 
+#ifdef CONFIG_TOUCHSCREEN_OFFLOAD
+	info->offload.coords[touchId].status = COORD_STATUS_INACTIVE;
+	if (!info->offload.offload_running) {
+#endif
+
+	input_mt_slot(info->input_dev, touchId);
+	input_report_abs(info->input_dev, ABS_MT_PRESSURE, 0);
 	input_mt_report_slot_state(info->input_dev, tool, 0);
-
-	/* pr_info("%s : TouchID = %d, Touchcount = %d\n", __func__,
-	 *	touchId,touchcount); */
-
-
 	input_report_abs(info->input_dev, ABS_MT_TRACKING_ID, -1);
-	/* pr_info("%s : Event 0x%02x - release ID[%d]\n", __func__,
-	 *	event[0], touchId); */
+
+#ifdef CONFIG_TOUCHSCREEN_OFFLOAD
+	}
+#endif
+
 	return true;
 }
 
@@ -3835,6 +3849,150 @@
 	return 0;
 }
 
+#ifdef CONFIG_TOUCHSCREEN_OFFLOAD
+
+static void fts_populate_coordinate_channel(struct fts_ts_info *info,
+					struct touch_offload_frame *frame,
+					int channel)
+{
+	int j;
+
+	struct TouchOffloadDataCoord *dc =
+		(struct TouchOffloadDataCoord *)frame->channel_data[channel];
+	memset(dc, 0, frame->channel_data_size[channel]);
+	dc->size_bytes = TOUCH_OFFLOAD_FRAME_SIZE_COORD;
+
+	for (j = 0; j < MAX_COORDS; j++) {
+		dc->coords[j].x = info->offload.coords[j].x;
+		dc->coords[j].y = info->offload.coords[j].y;
+		dc->coords[j].status = info->offload.coords[j].status;
+	}
+}
+
+static void fts_populate_mutual_channel(struct fts_ts_info *info,
+					struct touch_offload_frame *frame,
+					int channel)
+{
+	MutualSenseFrame ms_frame = { 0 };
+	uint32_t frame_index = 0, x, y;
+	uint16_t heatmap_value;
+	int result;
+	uint32_t data_type = 0;
+	struct TouchOffloadData2d *mutual_strength =
+		(struct TouchOffloadData2d *)frame->channel_data[channel];
+
+	switch (frame->channel_type[channel] & ~TOUCH_SCAN_TYPE_MUTUAL) {
+	case TOUCH_DATA_TYPE_RAW:
+		data_type = MS_RAW;
+		break;
+	case TOUCH_DATA_TYPE_FILTERED:
+		data_type = MS_FILTER;
+		break;
+	case TOUCH_DATA_TYPE_STRENGTH:
+		data_type = MS_STRENGTH;
+		break;
+	case TOUCH_DATA_TYPE_BASELINE:
+		data_type = MS_BASELINE;
+		break;
+	}
+	mutual_strength->tx_size = getForceLen();
+	mutual_strength->rx_size = getSenseLen();
+	mutual_strength->size_bytes =
+		TOUCH_OFFLOAD_FRAME_SIZE_2D(mutual_strength->rx_size,
+					    mutual_strength->tx_size);
+
+	result = getMSFrame3(data_type, &ms_frame);
+	if (result <= 0) {
+		pr_err("getMSFrame3 failed with result=0x%08X.\n", result);
+	} else {
+		uint32_t max_x = mutual_strength->tx_size;
+		uint32_t max_y = mutual_strength->rx_size;
+
+		for (y = 0; y < mutual_strength->rx_size; y++) {
+			for (x = 0; x < mutual_strength->tx_size; x++) {
+				/* Rotate frame counter-clockwise and invert
+				 * if necessary.
+				 */
+				if (!info->board->sensor_inverted) {
+					heatmap_value =
+					    ms_frame.node_data[
+						((max_x-1) - x) * max_y + y];
+				} else {
+					heatmap_value =
+					    ms_frame.node_data[
+						((max_x-1) - x) * max_y +
+						((max_y-1) - y)];
+				}
+				((uint16_t *)
+				 mutual_strength->data)[frame_index++] =
+				    heatmap_value;
+			}
+		}
+	}
+}
+
+static void fts_populate_self_channel(struct fts_ts_info *info,
+					struct touch_offload_frame *frame,
+					int channel)
+{
+	SelfSenseFrame ss_frame = { 0 };
+	int result;
+	uint32_t data_type = 0;
+	struct TouchOffloadData1d *self_strength =
+		(struct TouchOffloadData1d *)frame->channel_data[channel];
+	self_strength->tx_size = getForceLen();
+	self_strength->rx_size = getSenseLen();
+	self_strength->size_bytes =
+		TOUCH_OFFLOAD_FRAME_SIZE_1D(self_strength->rx_size,
+					    self_strength->tx_size);
+
+	switch (frame->channel_type[channel] & ~TOUCH_SCAN_TYPE_SELF) {
+	case TOUCH_DATA_TYPE_RAW:
+		data_type = SS_RAW;
+		break;
+	case TOUCH_DATA_TYPE_FILTERED:
+		data_type = SS_FILTER;
+		break;
+	case TOUCH_DATA_TYPE_STRENGTH:
+		data_type = SS_STRENGTH;
+		break;
+	case TOUCH_DATA_TYPE_BASELINE:
+		data_type = SS_BASELINE;
+		break;
+	}
+	result = getSSFrame3(data_type, &ss_frame);
+	if (result <= 0) {
+		pr_err("getSSFrame3 failed with result=0x%08X.\n", result);
+	} else {
+		memcpy(self_strength->data, ss_frame.force_data,
+		       2 * self_strength->tx_size);
+		memcpy(&self_strength->data[2 * self_strength->tx_size],
+		       ss_frame.sense_data, 2 * self_strength->rx_size);
+	}
+}
+
+static void fts_populate_frame(struct fts_ts_info *info,
+				struct touch_offload_frame *frame)
+{
+	static u64 index;
+	int i;
+
+	frame->header.index = index++;
+	frame->header.timestamp = info->timestamp;
+
+	/* Populate all channels */
+	for (i = 0; i < frame->num_channels; i++) {
+		if (frame->channel_type[i] == TOUCH_DATA_TYPE_COORD)
+			fts_populate_coordinate_channel(info, frame, i);
+		else if ((frame->channel_type[i] & TOUCH_SCAN_TYPE_MUTUAL) != 0)
+			fts_populate_mutual_channel(info, frame, i);
+		else if ((frame->channel_type[i] & TOUCH_SCAN_TYPE_SELF) != 0)
+			fts_populate_self_channel(info, frame, i);
+	}
+}
+
+#endif /* CONFIG_TOUCHSCREEN_OFFLOAD */
+
 /**
   * Bottom Half Interrupt Handler function
   * This handler is called each time there is at least one new event in the FIFO
@@ -3855,6 +4013,10 @@
 	unsigned char *evt_data;
 	bool processed_pointer_event = false;
 
+#ifdef CONFIG_TOUCHSCREEN_OFFLOAD
+	struct touch_offload_frame *frame = NULL;
+#endif
+
 	/* It is possible that interrupts were disabled while the handler is
 	 * executing, before acquiring the mutex. If so, simply return.
 	 */
@@ -3901,11 +4063,41 @@
 		}
 	}
 
+#ifdef CONFIG_TOUCHSCREEN_OFFLOAD
+	if (!info->offload.offload_running) {
+#endif
+
 	if (info->touch_id == 0)
 		input_report_key(info->input_dev, BTN_TOUCH, 0);
 
 	input_sync(info->input_dev);
 
+#ifdef CONFIG_TOUCHSCREEN_OFFLOAD
+	}
+
+	error = touch_offload_reserve_frame(&info->offload, &frame);
+	if (error != 0) {
+		pr_debug("%s: Could not reserve a frame: error=%d.\n",
+			 __func__, error);
+
+		/* Consider a lack of buffers to be the feature stopping */
+		info->offload.offload_running = false;
+	} else {
+		info->offload.offload_running = true;
+
+		fts_populate_frame(info, frame);
+
+		error = touch_offload_queue_frame(&info->offload, frame);
+		if (error != 0) {
+			pr_err("%s: Failed to queue reserved frame: error=%d.\n",
+			       __func__, error);
+		}
+	}
+#endif
+
+	/* TODO(spfetsch): if the mutual strength heatmap was already read into
+	 * the touch offload interface, use it here instead of reading again.
+	 */
 	if (processed_pointer_event)
 		heatmap_read(&info->v4l2, ktime_to_ns(info->timestamp));
 
@@ -3918,6 +4110,47 @@
 }
 /** @}*/
 
+#ifdef CONFIG_TOUCHSCREEN_OFFLOAD
+static void fts_offload_report(void *handle,
+			       struct TouchOffloadIocReport *report)
+{
+	struct fts_ts_info *info = (struct fts_ts_info *)handle;
+	bool touch_down = 0;
+	int i;
+
+	for (i = 0; i < MAX_COORDS; i++) {
+		if (report->coords[i].status == COORD_STATUS_FINGER) {
+			input_mt_slot(info->input_dev, i);
+			touch_down = 1;
+			input_report_key(info->input_dev, BTN_TOUCH,
+					 touch_down);
+			input_mt_report_slot_state(info->input_dev,
+						   MT_TOOL_FINGER, 1);
+			input_report_abs(info->input_dev, ABS_MT_POSITION_X,
+					 report->coords[i].x);
+			input_report_abs(info->input_dev, ABS_MT_POSITION_Y,
+					 report->coords[i].y);
+
+#ifndef SKIP_PRESSURE
+			input_report_abs(info->input_dev, ABS_MT_PRESSURE,
+					 0x30);
+#endif
+		} else {
+			input_mt_slot(info->input_dev, i);
+			input_report_abs(info->input_dev, ABS_MT_PRESSURE, 0);
+			input_mt_report_slot_state(info->input_dev,
+						   MT_TOOL_FINGER, 0);
+			input_report_abs(info->input_dev, ABS_MT_TRACKING_ID,
+					 -1);
+		}
+	}
+
+	input_report_key(info->input_dev, BTN_TOUCH, touch_down);
+
+	input_sync(info->input_dev);
+}
+#endif /* CONFIG_TOUCHSCREEN_OFFLOAD */
+
 /*
  * Read the display panel's extinfo from the display driver.
  *
@@ -5595,6 +5828,32 @@
 	if (error < OK)
 		goto ProbeErrorExit_6;
 
+#ifdef CONFIG_TOUCHSCREEN_OFFLOAD
+	/* Hard-coded C2 caps */
+	info->offload.caps.tx_size = 17;
+	info->offload.caps.rx_size = 36;
+	info->offload.caps.bus_type = BUS_TYPE_SPI;
+	info->offload.caps.bus_speed_hz = 10000000;
+	info->offload.caps.heatmap_size = HEATMAP_SIZE_FULL;
+	info->offload.caps.touch_data_types =
+	    TOUCH_DATA_TYPE_COORD | TOUCH_DATA_TYPE_RAW |
+	    TOUCH_DATA_TYPE_STRENGTH | TOUCH_DATA_TYPE_FILTERED |
+	    TOUCH_DATA_TYPE_BASELINE;
+	info->offload.caps.touch_scan_types =
+	    TOUCH_SCAN_TYPE_MUTUAL | TOUCH_SCAN_TYPE_SELF;
+	info->offload.caps.continuous_reporting = true;
+	info->offload.caps.noise_reporting = false;
+	info->offload.caps.cancel_reporting = false;
+	info->offload.caps.size_reporting = true;
+	info->offload.caps.filter_grip = true;
+	info->offload.caps.filter_palm = true;
+	info->offload.caps.num_sensitivity_settings = 1;
+
+	info->offload.hcallback = (void *)info;
+	info->offload.report_cb = fts_offload_report;
+	touch_offload_init(&info->offload);
+#endif
+
 #if defined(FW_UPDATE_ON_PROBE) && defined(FW_H_FILE)
 	pr_info("FW Update and Sensing Initialization:\n");
 	error = fts_fw_update(info);
@@ -5655,6 +5914,10 @@
 
 	msm_drm_unregister_client(&info->notifier);
 
+#ifdef CONFIG_TOUCHSCREEN_OFFLOAD
+	touch_offload_cleanup(&info->offload);
+#endif
+
 	heatmap_remove(&info->v4l2);
 
 ProbeErrorExit_6:
@@ -5719,6 +5982,10 @@
 	/* remove interrupt and event handlers */
 	fts_interrupt_uninstall(info);
 
+#ifdef CONFIG_TOUCHSCREEN_OFFLOAD
+	touch_offload_cleanup(&info->offload);
+#endif
+
 	heatmap_remove(&info->v4l2);
 
 	pm_qos_remove_request(&info->pm_qos_req);
diff --git a/fts.h b/fts.h
index 178bf07..01bdfba 100644
--- a/fts.h
+++ b/fts.h
@@ -35,6 +35,7 @@
 #include <linux/device.h>
 #include <linux/input/heatmap.h>
 #include <linux/pm_qos.h>
+#include <linux/input/touch_offload.h>
 #include <drm/drm_panel.h>
 #include "fts_lib/ftsSoftware.h"
 #include "fts_lib/ftsHardware.h"
@@ -43,7 +44,6 @@
 #include <linux/input/touch_bus_negotiator.h>
 #endif
 
-
 /****************** CONFIGURATION SECTION ******************/
 /** @defgroup conf_section	 Driver Configuration Section
   * Settings of the driver code in order to suit the HW set up and the
@@ -399,6 +399,10 @@
 
 	struct v4l2_heatmap v4l2;
 
+#ifdef CONFIG_TOUCHSCREEN_OFFLOAD
+	struct touch_offload_context offload;
+#endif
+
 	struct delayed_work fwu_work;	/* Work for fw update */
 	struct workqueue_struct *fwu_workqueue;	/* Fw update work queue */
 	event_dispatch_handler_t *event_dispatch_table;	/* Dispatch table */
diff --git a/fts_lib/ftsFrame.c b/fts_lib/ftsFrame.c
index b79f498..7ea3778 100644
--- a/fts_lib/ftsFrame.c
+++ b/fts_lib/ftsFrame.c
@@ -288,7 +288,7 @@
 	}
 
 
-	pr_info("%s: Starting to get frame %02X\n", __func__, type);
+	pr_debug("%s: Starting to get frame %02X\n", __func__, type);
 	switch (type) {
 	case SS_RAW:
 		offset_force = systemInfo.u16_ssTchTxRawAddr;
@@ -396,7 +396,7 @@
 
 	frame->header.type = type;
 
-	pr_info("%s: Force_len = %d Sense_len = %d Offset_force = %04X Offset_sense = %04X\n",
+	pr_debug("%s: Force_len = %d Sense_len = %d Offset_force = %04X Offset_sense = %04X\n",
 		__func__, frame->header.force_node,
 		frame->header.sense_node,
 		offset_force, offset_sense);
@@ -445,7 +445,7 @@
 	/* if you want to access one node i,j,
 	  * the offset like: offset = i*columns + j = > frame[i, j] */
 
-	pr_info("Frame acquired!\n");
+	pr_debug("Frame acquired!\n");
 	return frame->header.force_node + frame->header.sense_node;
 	/* return the number of data put inside frame */
 }