// SPDX-License-Identifier: GPL-2.0 /* * Google Touch Interface for Pixel devices. * * Copyright 2022 Google LLC. */ #include #include #include #include #include "goog_touch_interface.h" #include "touch_bus_negotiator.h" #include "../../../gs-google/drivers/soc/google/vh/kernel/systrace.h" static struct class *gti_class; static u8 gti_dev_num; static ssize_t offload_enable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size); static ssize_t offload_enable_show(struct device *dev, struct device_attribute *attr, char *buf); static ssize_t v4l2_enable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size); static ssize_t v4l2_enable_show(struct device *dev, struct device_attribute *attr, char *buf); static DEVICE_ATTR_RW(offload_enable); static DEVICE_ATTR_RW(v4l2_enable); static struct attribute *goog_attributes[] = { &dev_attr_offload_enable.attr, &dev_attr_v4l2_enable.attr, NULL, }; static struct attribute_group goog_attr_group = { .attrs = goog_attributes, }; static ssize_t offload_enable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct goog_touch_interface *gti = dev_get_drvdata(dev); if (kstrtobool(buf, >i->offload_enable)) GOOG_ERR("invalid input!\n"); else GOOG_LOG("offload_enable= %d.\n", gti->offload_enable); return size; } static ssize_t offload_enable_show(struct device *dev, struct device_attribute *attr, char *buf) { size_t size = 0; struct goog_touch_interface *gti = dev_get_drvdata(dev); size += scnprintf(buf, PAGE_SIZE, "offload_enable= %d.\n", gti->offload_enable); GOOG_LOG("%s", buf); return size; } static ssize_t v4l2_enable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct goog_touch_interface *gti = dev_get_drvdata(dev); if (kstrtobool(buf, >i->v4l2_enable)) GOOG_ERR("invalid input!\n"); else GOOG_LOG("v4l2_enable= %d.\n", gti->v4l2_enable); return size; } static ssize_t v4l2_enable_show(struct device *dev, struct device_attribute *attr, char *buf) { size_t size = 0; struct goog_touch_interface *gti = dev_get_drvdata(dev); size += scnprintf(buf, PAGE_SIZE, "v4l2_enable= %d.\n", gti->v4l2_enable); GOOG_LOG("%s", buf); return size; } static void panel_bridge_enable(struct drm_bridge *bridge) { int ret = 0; struct goog_touch_interface *gti = container_of(bridge, struct goog_touch_interface, panel_bridge); if (gti->panel_is_lp_mode) { GOOG_LOG("skip screen-on because of panel_is_lp_mode enabled!\n"); } else { if (gti->tbn_register_mask) tbn_request_bus(gti->tbn_register_mask); GOOG_LOG("screen-on.\n"); gti->cmd.display_state_cmd.setting = GTI_DISPLAY_STATE_ON; ret = goog_process_vendor_cmd(gti, GTI_CMD_NOTIFY_DISPLAY_STATE); if (ret) GOOG_WARN("unexpected return(%d)!", ret); } } static void panel_bridge_disable(struct drm_bridge *bridge) { int ret = 0; struct goog_touch_interface *gti = container_of(bridge, struct goog_touch_interface, panel_bridge); if (bridge->encoder && bridge->encoder->crtc) { const struct drm_crtc_state *crtc_state = bridge->encoder->crtc->state; if (drm_atomic_crtc_effectively_active(crtc_state)) return; } GOOG_LOG("screen-off.\n"); gti->cmd.display_state_cmd.setting = GTI_DISPLAY_STATE_OFF; ret = goog_process_vendor_cmd(gti, GTI_CMD_NOTIFY_DISPLAY_STATE); if (ret) GOOG_WARN("unexpected return(%d)!", ret); if (gti->tbn_register_mask) tbn_release_bus(gti->tbn_register_mask); } struct drm_connector *get_bridge_connector(struct drm_bridge *bridge) { struct drm_connector *connector; struct drm_connector_list_iter conn_iter; drm_connector_list_iter_begin(bridge->dev, &conn_iter); drm_for_each_connector_iter(connector, &conn_iter) { if (connector->encoder == bridge->encoder) break; } drm_connector_list_iter_end(&conn_iter); return connector; } static bool panel_bridge_is_lp_mode(struct drm_connector *connector) { if (connector && connector->state) { struct exynos_drm_connector_state *s = to_exynos_connector_state(connector->state); return s->exynos_mode.is_lp_mode; } return false; } static void panel_bridge_mode_set(struct drm_bridge *bridge, const struct drm_display_mode *mode, const struct drm_display_mode *adjusted_mode) { int ret = 0; bool panel_is_lp_mode; struct goog_touch_interface *gti = container_of(bridge, struct goog_touch_interface, panel_bridge); if (!gti->connector || !gti->connector->state) gti->connector = get_bridge_connector(bridge); panel_is_lp_mode = panel_bridge_is_lp_mode(gti->connector); if (gti->panel_is_lp_mode != panel_is_lp_mode) { GOOG_LOG("panel_is_lp_mode changed from %d to %d.\n", gti->panel_is_lp_mode, panel_is_lp_mode); if (panel_is_lp_mode) gti->cmd.display_state_cmd.setting = GTI_DISPLAY_STATE_OFF; else gti->cmd.display_state_cmd.setting = GTI_DISPLAY_STATE_ON; ret = goog_process_vendor_cmd(gti, GTI_CMD_NOTIFY_DISPLAY_STATE); if (ret) GOOG_WARN("unexpected return(%d)!", ret); } gti->panel_is_lp_mode = panel_is_lp_mode; if (mode) { int vrefresh = drm_mode_vrefresh(mode); if (gti->display_vrefresh != vrefresh) { GOOG_DBG("display_vrefresh(Hz) changed to %d from %d.\n", vrefresh, gti->display_vrefresh); gti->display_vrefresh = vrefresh; gti->cmd.display_vrefresh_cmd.setting = vrefresh; ret = goog_process_vendor_cmd(gti, GTI_CMD_NOTIFY_DISPLAY_VREFRESH); if (ret) GOOG_WARN("unexpected return(%d)!", ret); } } } static const struct drm_bridge_funcs panel_bridge_funcs = { .enable = panel_bridge_enable, .disable = panel_bridge_disable, .mode_set = panel_bridge_mode_set, }; static int register_panel_bridge(struct goog_touch_interface *gti) { GOOG_LOG("\n"); #ifdef CONFIG_OF gti->panel_bridge.of_node = gti->vendor_dev->of_node; #endif gti->panel_bridge.funcs = &panel_bridge_funcs; drm_bridge_add(>i->panel_bridge); return 0; } static void unregister_panel_bridge(struct drm_bridge *bridge) { struct drm_bridge *node; GOOG_LOG("\n"); drm_bridge_remove(bridge); if (!bridge->dev) /* not attached */ return; drm_modeset_lock(&bridge->dev->mode_config.connection_mutex, NULL); list_for_each_entry(node, &bridge->encoder->bridge_chain, chain_node) { if (node == bridge) { if (bridge->funcs->detach) bridge->funcs->detach(bridge); list_del(&bridge->chain_node); break; } } drm_modeset_unlock(&bridge->dev->mode_config.connection_mutex); bridge->dev = NULL; } int goog_process_vendor_cmd(struct goog_touch_interface *gti, enum gti_cmd_type cmd_type) { void *private_data = gti->vendor_private_data; int ret = -ESRCH; /* Use optional vendor operation if available. */ switch (cmd_type) { case GTI_CMD_GET_SENSOR_DATA: if (gti->cmd.sensor_data_cmd.type & TOUCH_SCAN_TYPE_MUTUAL) { ret = gti->options.get_mutual_sensor_data( private_data, >i->cmd.sensor_data_cmd); } else if (gti->cmd.sensor_data_cmd.type & TOUCH_SCAN_TYPE_SELF) { ret = gti->options.get_self_sensor_data( private_data, >i->cmd.sensor_data_cmd); } break; case GTI_CMD_SET_GRIP: ret = gti->options.set_grip(private_data, >i->cmd.grip_cmd); break; case GTI_CMD_SET_PALM: ret = gti->options.set_palm(private_data, >i->cmd.palm_cmd); break; case GTI_CMD_SET_CONTINUOUS_REPORT: ret = gti->options.set_continuous_report(private_data, >i->cmd.continuous_report_cmd); break; case GTI_CMD_NOTIFY_DISPLAY_STATE: ret = gti->options.notify_display_state(private_data, >i->cmd.display_state_cmd); break; case GTI_CMD_NOTIFY_DISPLAY_VREFRESH: ret = gti->options.notify_display_vrefresh(private_data, >i->cmd.display_vrefresh_cmd); break; default: break; } /* Back to vendor default handler if no optional operation available. */ if (ret == -ESRCH) ret = gti->vendor_default_handler(private_data, cmd_type, >i->cmd); return ret; } void goog_update_motion_filter(struct goog_touch_interface *gti, unsigned long slot_bit) { int ret = 0; const u32 mf_timeout_ms = 500; unsigned long touches = hweight_long(slot_bit); u32 next_state = gti->mf_state; switch (gti->mf_mode) { case GTI_MF_MODE_AUTO_REPORT: case GTI_MF_MODE_UNFILTER: next_state = GTI_MF_STATE_UNFILTERED; break; case GTI_MF_MODE_FILTER: next_state = GTI_MF_STATE_FILTERED; break; case GTI_MF_MODE_DYNAMIC: default: /* * Determine the next filter state. The motion filter is enabled by * default and it is disabled while a single finger is touching the * screen. If another finger is touched down or if a timeout expires, * the motion filter is reenabled and remains enabled until all fingers * are lifted. */ switch (next_state) { case GTI_MF_STATE_FILTERED: if (touches == 1) { next_state = GTI_MF_STATE_UNFILTERED; gti->mf_downtime = ktime_get(); } break; case GTI_MF_STATE_UNFILTERED: if (touches == 0) { next_state = GTI_MF_STATE_FILTERED; } else if (touches > 1 || ktime_after(ktime_get(), ktime_add_ms(gti->mf_downtime, mf_timeout_ms))) { next_state = GTI_MF_STATE_FILTERED_LOCKED; } break; case GTI_MF_STATE_FILTERED_LOCKED: if (touches == 0) next_state = GTI_MF_STATE_FILTERED; break; } break; } /* Send command to setup continuous report. */ if ((next_state == GTI_MF_STATE_UNFILTERED) != (gti->mf_state == GTI_MF_STATE_UNFILTERED)) { gti->cmd.continuous_report_cmd.setting = GTI_CONTINUOUS_REPORT_DISABLE; if (next_state == GTI_MF_STATE_UNFILTERED) gti->cmd.continuous_report_cmd.setting = GTI_CONTINUOUS_REPORT_ENABLE; ret = goog_process_vendor_cmd(gti, GTI_CMD_SET_CONTINUOUS_REPORT); if (ret) GOOG_WARN("unexpected return(%d)!", ret); } gti->mf_state = next_state; } bool goog_v4l2_read_frame_cb(struct v4l2_heatmap *v4l2) { struct goog_touch_interface *gti = container_of(v4l2, struct goog_touch_interface, v4l2); bool ret = false; u32 v4l2_size = gti->v4l2.width * gti->v4l2.height * 2; if (gti->heatmap_buf && v4l2_size == gti->heatmap_buf_size) { memcpy(v4l2->frame, gti->heatmap_buf, v4l2_size); ret = true; } else { GOOG_ERR("wrong pointer(%p) or size (W: %lu, H: %lu) vs %u\n", gti->heatmap_buf, gti->v4l2.width, gti->v4l2.height, gti->heatmap_buf_size); } return ret; } void goog_v4l2_read(struct goog_touch_interface *gti, ktime_t timestamp) { if (gti->v4l2_enable) heatmap_read(>i->v4l2, ktime_to_ns(timestamp)); } void goog_offload_populate_coordinate_channel(struct goog_touch_interface *gti, struct touch_offload_frame *frame, int channel) { int i; struct TouchOffloadDataCoord *dc = (struct TouchOffloadDataCoord *)frame->channel_data[channel]; memset(dc, 0, frame->channel_data_size[channel]); dc->header.channel_type = TOUCH_DATA_TYPE_COORD; dc->header.channel_size = TOUCH_OFFLOAD_FRAME_SIZE_COORD; for (i = 0; i < MAX_COORDS; i++) { dc->coords[i].x = gti->offload.coords[i].x; dc->coords[i].y = gti->offload.coords[i].y; dc->coords[i].major = gti->offload.coords[i].major; dc->coords[i].minor = gti->offload.coords[i].minor; dc->coords[i].pressure = gti->offload.coords[i].pressure; dc->coords[i].status = gti->offload.coords[i].status; } } void goog_offload_populate_mutual_channel(struct goog_touch_interface *gti, struct touch_offload_frame *frame, int channel, u8 *buffer, u32 size) { struct TouchOffloadData2d *mutual = (struct TouchOffloadData2d *)frame->channel_data[channel]; mutual->tx_size = gti->offload.caps.tx_size; mutual->rx_size = gti->offload.caps.rx_size; mutual->header.channel_type = frame->channel_type[channel]; mutual->header.channel_size = TOUCH_OFFLOAD_FRAME_SIZE_2D(mutual->rx_size, mutual->tx_size); memcpy(mutual->data, buffer, size); } void goog_offload_populate_self_channel(struct goog_touch_interface *gti, struct touch_offload_frame *frame, int channel, u8 *buffer, u32 size) { struct TouchOffloadData1d *self = (struct TouchOffloadData1d *)frame->channel_data[channel]; self->tx_size = gti->offload.caps.tx_size; self->rx_size = gti->offload.caps.rx_size; self->header.channel_type = frame->channel_type[channel]; self->header.channel_size = TOUCH_OFFLOAD_FRAME_SIZE_1D(self->rx_size, self->tx_size); memcpy(self->data, buffer, size); } void goog_offload_populate_frame(struct goog_touch_interface *gti, struct touch_offload_frame *frame) { static u64 index; u32 channel_type; int i; int ret; u16 tx = gti->offload.caps.tx_size; u16 rx = gti->offload.caps.rx_size; struct gti_sensor_data_cmd *cmd = >i->cmd.sensor_data_cmd; frame->header.index = index++; frame->header.timestamp = gti->input_timestamp; ATRACE_BEGIN(__func__); /* * TODO(b/201610482): * Porting for other channels, like driver status, stylus status * and others. */ /* Populate all channels */ for (i = 0; i < frame->num_channels; i++) { channel_type = frame->channel_type[i]; GOOG_DBG("#%d: get data(type %#x) from vendor driver", i, channel_type); ret = 0; cmd->type = channel_type; cmd->buffer = NULL; cmd->size = 0; if (channel_type == TOUCH_DATA_TYPE_COORD) { ATRACE_BEGIN("populate coord"); goog_offload_populate_coordinate_channel(gti, frame, i); ATRACE_END(); } else if (channel_type & TOUCH_SCAN_TYPE_MUTUAL) { ATRACE_BEGIN("populate mutual data"); ret = goog_process_vendor_cmd(gti, GTI_CMD_GET_SENSOR_DATA); if (ret == 0 && cmd->buffer && cmd->size == TOUCH_OFFLOAD_DATA_SIZE_2D(rx, tx)) { goog_offload_populate_mutual_channel(gti, frame, i, cmd->buffer, cmd->size); /* Backup strength data for v4l2. */ if (channel_type & TOUCH_DATA_TYPE_STRENGTH) memcpy(gti->heatmap_buf, cmd->buffer, cmd->size); } ATRACE_END(); } else if (channel_type & TOUCH_SCAN_TYPE_SELF) { ATRACE_BEGIN("populate self data"); ret = goog_process_vendor_cmd(gti, GTI_CMD_GET_SENSOR_DATA); if (ret == 0 && cmd->buffer && cmd->size == TOUCH_OFFLOAD_DATA_SIZE_1D(rx, tx)) { goog_offload_populate_self_channel(gti, frame, i, cmd->buffer, cmd->size); } ATRACE_END(); } if (ret) { GOOG_DBG("skip to populate data(type %#x, ret %d)!\n", channel_type, ret); } } ATRACE_END(); } void goog_offload_set_running(struct goog_touch_interface *gti, bool running) { int ret = 0; if (gti->offload.offload_running != running) { gti->offload.offload_running = running; if (running && gti->offload.config.filter_grip) gti->cmd.grip_cmd.setting = GTI_GRIP_DISABLE; else gti->cmd.grip_cmd.setting = GTI_GRIP_DRIVER_DEFAULT; ret = goog_process_vendor_cmd(gti, GTI_CMD_SET_GRIP); if (ret) GOOG_WARN("unexpected return(%d)!", ret); gti->grip_setting = gti->cmd.grip_cmd.setting; if (running && gti->offload.config.filter_palm) gti->cmd.palm_cmd.setting = GTI_PALM_DISABLE; else gti->cmd.palm_cmd.setting = GTI_PALM_DRIVER_DEFAULT; ret = goog_process_vendor_cmd(gti, GTI_CMD_SET_PALM); if (ret) GOOG_WARN("unexpected return(%d)!", ret); gti->palm_setting = gti->cmd.palm_cmd.setting; } } void goog_offload_input_report(void *handle, struct TouchOffloadIocReport *report) { struct goog_touch_interface *gti = (struct goog_touch_interface *)handle; bool touch_down = 0; unsigned int tool_type = MT_TOOL_FINGER; int i; unsigned long active_slot_bit = 0; ATRACE_BEGIN(__func__); goog_input_lock(gti); input_set_timestamp(gti->vendor_input_dev, report->timestamp); for (i = 0; i < MAX_COORDS; i++) { if (report->coords[i].status != COORD_STATUS_INACTIVE) { switch (report->coords[i].status) { case COORD_STATUS_EDGE: case COORD_STATUS_PALM: case COORD_STATUS_CANCEL: tool_type = MT_TOOL_PALM; break; case COORD_STATUS_FINGER: case COORD_STATUS_PEN: default: tool_type = MT_TOOL_FINGER; break; } __set_bit(i, &active_slot_bit); input_mt_slot(gti->vendor_input_dev, i); touch_down = 1; input_report_key(gti->vendor_input_dev, BTN_TOUCH, touch_down); input_mt_report_slot_state(gti->vendor_input_dev, tool_type, 1); input_report_abs(gti->vendor_input_dev, ABS_MT_POSITION_X, report->coords[i].x); input_report_abs(gti->vendor_input_dev, ABS_MT_POSITION_Y, report->coords[i].y); input_report_abs(gti->vendor_input_dev, ABS_MT_TOUCH_MAJOR, report->coords[i].major); input_report_abs(gti->vendor_input_dev, ABS_MT_TOUCH_MINOR, report->coords[i].minor); input_report_abs(gti->vendor_input_dev, ABS_MT_PRESSURE, report->coords[i].pressure); } else { __clear_bit(i, &active_slot_bit); input_mt_slot(gti->vendor_input_dev, i); input_report_abs(gti->vendor_input_dev, ABS_MT_PRESSURE, 0); input_mt_report_slot_state(gti->vendor_input_dev, MT_TOOL_FINGER, 0); input_report_abs(gti->vendor_input_dev, ABS_MT_TRACKING_ID, -1); } } input_report_key(gti->vendor_input_dev, BTN_TOUCH, touch_down); input_sync(gti->vendor_input_dev); goog_input_unlock(gti); if (touch_down) goog_v4l2_read(gti, report->timestamp); goog_update_motion_filter(gti, active_slot_bit); ATRACE_END(); } int goog_offload_probe(struct goog_touch_interface *gti) { int ret; u16 values[2]; struct device_node *np = gti->vendor_dev->of_node; if (of_property_read_u8_array(np, "goog,touch_offload_id", gti->offload_id_byte, 4)) { GOOG_LOG("set default offload id: GOOG!\n"); gti->offload_id_byte[0] = 'G'; gti->offload_id_byte[1] = 'O'; gti->offload_id_byte[2] = 'O'; gti->offload_id_byte[3] = 'G'; } gti->offload.caps.touch_offload_major_version = TOUCH_OFFLOAD_INTERFACE_MAJOR_VERSION; gti->offload.caps.touch_offload_minor_version = TOUCH_OFFLOAD_INTERFACE_MINOR_VERSION; gti->offload.caps.device_id = gti->offload_id; if (of_property_read_u16_array(np, "goog,display-resolution", values, 2) == 0) { gti->offload.caps.display_width = values[0]; gti->offload.caps.display_height = values[1]; } else { GOOG_ERR("Plesae set \"goog,display-resolution\" in dts!"); } if (of_property_read_u16_array(np, "goog,channel-num", values, 2) == 0) { gti->offload.caps.tx_size = values[0]; gti->offload.caps.rx_size = values[1]; } else { GOOG_ERR("Plesae set \"goog,channel-num\" in dts!"); ret = -EINVAL; goto err_offload_probe; } /* * TODO(b/201610482): Set more offload caps from parameters or from dtsi? */ gti->offload.caps.heatmap_size = HEATMAP_SIZE_FULL; gti->offload.caps.bus_type = BUS_TYPE_SPI; if (of_property_read_u32(np, "spi-max-frequency", >i->offload.caps.bus_speed_hz)) gti->offload.caps.bus_speed_hz = 0; gti->offload.caps.touch_data_types = TOUCH_DATA_TYPE_COORD | TOUCH_DATA_TYPE_STRENGTH | TOUCH_DATA_TYPE_RAW | TOUCH_DATA_TYPE_BASELINE; gti->offload.caps.touch_scan_types = TOUCH_SCAN_TYPE_MUTUAL; gti->offload.caps.continuous_reporting = true; gti->offload.caps.noise_reporting = false; gti->offload.caps.cancel_reporting = false; gti->offload.caps.size_reporting = true; gti->offload.caps.filter_grip = true; gti->offload.caps.filter_palm = true; gti->offload.caps.num_sensitivity_settings = 1; gti->offload.hcallback = (void *)gti; gti->offload.report_cb = goog_offload_input_report; ret = touch_offload_init(>i->offload); if (ret) { GOOG_ERR("offload init failed, ret %d!\n", ret); goto err_offload_probe; } gti->offload_enable = of_property_read_bool(np, "goog,offload-enable"); GOOG_LOG("offload configucation: %d * %d (%d * %d)\n", gti->offload.caps.display_width, gti->offload.caps.display_height, gti->offload.caps.tx_size, gti->offload.caps.rx_size); GOOG_LOG("offload ID: \"%c%c%c%c\" / 0x%08X, offload_enable=%d.\n", gti->offload_id_byte[0], gti->offload_id_byte[1], gti->offload_id_byte[2], gti->offload_id_byte[3], gti->offload_id, gti->offload_enable); gti->heatmap_buf_size = gti->offload.caps.tx_size * gti->offload.caps.rx_size * sizeof(u16); gti->heatmap_buf = devm_kzalloc(gti->vendor_dev, gti->heatmap_buf_size, GFP_KERNEL); if (!gti->heatmap_buf) { GOOG_ERR("heamap alloc failed!\n"); ret = -ENOMEM; goto err_offload_probe; } /* * Heatmap_probe must be called before irq routine is registered, * because heatmap_read is called from the irq context. * If the ISR runs before heatmap_probe is finished, it will invoke * heatmap_read and cause NPE, since read_frame would not yet be set. */ gti->v4l2.parent_dev = gti->vendor_dev; gti->v4l2.input_dev = gti->vendor_input_dev; gti->v4l2.read_frame = goog_v4l2_read_frame_cb; gti->v4l2.width = gti->offload.caps.tx_size; gti->v4l2.height = gti->offload.caps.rx_size; /* 120 Hz operation */ gti->v4l2.timeperframe.numerator = 1; if (of_property_read_u32(np, "goog,report-rate", >i->v4l2.timeperframe.denominator)) gti->v4l2.timeperframe.denominator = 120; ret = heatmap_probe(>i->v4l2); if (ret) { GOOG_ERR("v4l2 init failed, ret %d!\n", ret); goto err_offload_probe; } gti->v4l2_enable = of_property_read_bool(np, "goog,v4l2-enable"); GOOG_LOG("v4l2 W/H=(%lu, %lu), v4l2_enable=%d.\n", gti->v4l2.width, gti->v4l2.height, gti->v4l2_enable); err_offload_probe: return ret; } void goog_offload_remove(struct goog_touch_interface *gti) { touch_offload_cleanup(>i->offload); } bool goog_input_legacy_report(struct goog_touch_interface *gti) { if (!gti->offload.offload_running || gti->force_legacy_report) return true; return false; } int goog_input_process(struct goog_touch_interface *gti) { int ret = 0; struct touch_offload_frame **frame = >i->offload_frame; if (!gti->coord_changed) return -EPERM; if (gti->offload_enable) { ret = touch_offload_reserve_frame(>i->offload, frame); if (ret != 0 || frame == NULL) { GOOG_ERR("could not reserve a frame(ret %d)!\n", ret); /* Stop offload when there are no buffers available. */ goog_offload_set_running(gti, false); /* * TODO(b/193467748): * How to handle current coord if offload running * terminating in the halfway(not beginning case)? */ ret = -EBUSY; } else { goog_offload_set_running(gti, true); goog_offload_populate_frame(gti, *frame); ret = touch_offload_queue_frame(>i->offload, *frame); if (ret) GOOG_ERR("failed to queue reserved frame(ret %d)!\n", ret); else gti->offload_frame = NULL; } } /* * If offload is NOT running, read heatmap directly by callback. * Otherwise, heatmap will be handled for both offload and v4l2 * during goog_offload_populate_frame(). */ if (!gti->offload.offload_running && gti->v4l2_enable) { int ret; struct gti_sensor_data_cmd *cmd = >i->cmd.sensor_data_cmd; cmd->buffer = NULL; cmd->size = 0; cmd->type = GTI_SENSOR_DATA_TYPE_MS; ret = goog_process_vendor_cmd(gti, GTI_CMD_GET_SENSOR_DATA); if (ret == 0 && cmd->buffer && cmd->size) memcpy(gti->heatmap_buf, cmd->buffer, cmd->size); goog_v4l2_read(gti, gti->input_timestamp); goog_update_motion_filter(gti, gti->active_slot_bit); } gti->input_timestamp_changed = false; gti->coord_changed = false; return ret; } EXPORT_SYMBOL(goog_input_process); void goog_input_lock(struct goog_touch_interface *gti) { mutex_lock(>i->input_lock); } EXPORT_SYMBOL(goog_input_lock); void goog_input_unlock(struct goog_touch_interface *gti) { mutex_unlock(>i->input_lock); } EXPORT_SYMBOL(goog_input_unlock); void goog_input_set_timestamp( struct goog_touch_interface *gti, struct input_dev *dev, ktime_t timestamp) { int ret = 0; /* Specific case to handle all fingers release. */ if (!ktime_compare(timestamp, KTIME_RELEASE_ALL)) { GOOG_DBG("Enable force_legacy_report for all fingers release.\n"); /* Enable FW palm and grip for low power sensing during suspend. */ if (gti->offload.offload_running) { gti->cmd.grip_cmd.setting = GTI_GRIP_ENABLE; ret = goog_process_vendor_cmd(gti, GTI_CMD_SET_GRIP); if (ret) GOOG_WARN("unexpected return(%d)!", ret); gti->cmd.palm_cmd.setting = GTI_PALM_ENABLE; ret = goog_process_vendor_cmd(gti, GTI_CMD_SET_PALM); if (ret) GOOG_WARN("unexpected return(%d)!", ret); } timestamp = ktime_get(); gti->force_legacy_report = true; } else { /* Once device is from suspend to resume, recover last grip/palm state. */ if (gti->offload.offload_running && gti->force_legacy_report) { gti->cmd.grip_cmd.setting = gti->grip_setting; ret = goog_process_vendor_cmd(gti, GTI_CMD_SET_GRIP); if (ret) GOOG_WARN("unexpected return(%d)!", ret); gti->cmd.palm_cmd.setting = gti->palm_setting; ret = goog_process_vendor_cmd(gti, GTI_CMD_SET_PALM); if (ret) GOOG_WARN("unexpected return(%d)!", ret); } GOOG_DBG("Disable force_legacy_report as usual state.\n"); gti->force_legacy_report = false; } if (goog_input_legacy_report(gti)) input_set_timestamp(dev, timestamp); gti->input_timestamp = timestamp; gti->input_timestamp_changed = true; } EXPORT_SYMBOL(goog_input_set_timestamp); void goog_input_mt_slot( struct goog_touch_interface *gti, struct input_dev *dev, int slot) { if (goog_input_legacy_report(gti)) input_mt_slot(dev, slot); if (slot < MAX_COORDS) { gti->slot = slot; /* * Make sure the input timestamp should be set before updating 1st mt_slot. * This is for input report switch between offload and legacy. */ if (!gti->coord_changed && !gti->input_timestamp_changed) GOOG_ERR("please exec goog_input_set_timestamp before %s!\n", __func__); gti->coord_changed = true; } } EXPORT_SYMBOL(goog_input_mt_slot); void goog_input_mt_report_slot_state( struct goog_touch_interface *gti, struct input_dev *dev, unsigned int tool_type, bool active) { if (goog_input_legacy_report(gti)) input_mt_report_slot_state(dev, tool_type, active); if (tool_type == MT_TOOL_FINGER) { if (active) { gti->offload.coords[gti->slot].status = COORD_STATUS_FINGER; __set_bit(gti->slot, >i->active_slot_bit); } else { gti->offload.coords[gti->slot].status = COORD_STATUS_INACTIVE; __clear_bit(gti->slot, >i->active_slot_bit); } } } EXPORT_SYMBOL(goog_input_mt_report_slot_state); void goog_input_report_abs( struct goog_touch_interface *gti, struct input_dev *dev, unsigned int code, int value) { if (goog_input_legacy_report(gti)) input_report_abs(dev, code, value); if (gti->slot < MAX_COORDS) { switch (code) { case ABS_MT_POSITION_X: gti->offload.coords[gti->slot].x = value; break; case ABS_MT_POSITION_Y: gti->offload.coords[gti->slot].y = value; break; case ABS_MT_TOUCH_MAJOR: gti->offload.coords[gti->slot].major = value; break; case ABS_MT_TOUCH_MINOR: gti->offload.coords[gti->slot].minor = value; break; case ABS_MT_PRESSURE: gti->offload.coords[gti->slot].pressure = value; break; default: break; } } } EXPORT_SYMBOL(goog_input_report_abs); void goog_input_report_key( struct goog_touch_interface *gti, struct input_dev *dev, unsigned int code, int value) { if (goog_input_legacy_report(gti)) input_report_key(dev, code, value); } EXPORT_SYMBOL(goog_input_report_key); void goog_input_sync(struct goog_touch_interface *gti, struct input_dev *dev) { if (goog_input_legacy_report(gti)) input_sync(dev); } EXPORT_SYMBOL(goog_input_sync); void goog_register_tbn(struct goog_touch_interface *gti) { struct device_node *np = gti->vendor_dev->of_node; gti->tbn_enable = of_property_read_bool(np, "goog,tbn-enable"); if (gti->tbn_enable) { if (register_tbn(>i->tbn_register_mask)) { GOOG_ERR("failed to register tbn context!\n"); gti->tbn_enable = false; } else { GOOG_LOG("tbn_register_mask = %#x.\n", gti->tbn_register_mask); } } } static int goog_get_mutual_sensor_data_nop( void *private_data, struct gti_sensor_data_cmd *cmd) { return -ESRCH; } static int goog_get_self_sensor_data_nop( void *private_data, struct gti_sensor_data_cmd *cmd) { return -ESRCH; } static int goog_set_grip_nop( void *private_data, struct gti_grip_cmd *cmd) { return -ESRCH; } static int goog_set_palm_nop( void *private_data, struct gti_palm_cmd *cmd) { return -ESRCH; } static int goog_set_continuous_report_nop( void *private_data, struct gti_continuous_report_cmd *cmd) { return -ESRCH; } static int goog_notify_display_state_nop( void *private_data, struct gti_display_state_cmd *cmd) { return -ESRCH; } static int goog_notify_display_vrefresh_nop( void *private_data, struct gti_display_vrefresh_cmd *cmd) { return -ESRCH; } void goog_init_options(struct goog_touch_interface *gti, struct gti_optional_configuration *options) { /* Initialize default functions. */ gti->options.get_mutual_sensor_data = goog_get_mutual_sensor_data_nop; gti->options.get_self_sensor_data = goog_get_self_sensor_data_nop; gti->options.set_grip = goog_set_grip_nop; gti->options.set_palm = goog_set_palm_nop; gti->options.set_continuous_report = goog_set_continuous_report_nop; gti->options.notify_display_state = goog_notify_display_state_nop; gti->options.notify_display_vrefresh = goog_notify_display_vrefresh_nop; /* Set optional operation if available. */ if (options) { if (options->get_mutual_sensor_data) gti->options.get_mutual_sensor_data = options->get_mutual_sensor_data; if (options->get_self_sensor_data) gti->options.get_self_sensor_data = options->get_self_sensor_data; if (options->set_grip) gti->options.set_grip = options->set_grip; if (options->set_palm) gti->options.set_palm = options->set_palm; if (options->set_continuous_report) gti->options.set_continuous_report = options->set_continuous_report; if (options->notify_display_state) gti->options.notify_display_state = options->notify_display_state; if (options->notify_display_vrefresh) { gti->options.notify_display_vrefresh = options->notify_display_vrefresh; } } } struct goog_touch_interface *goog_touch_interface_probe( void *private_data, struct device *dev, struct input_dev *input_dev, int (*default_handler)(void *private_data, u32 cmd_type, struct gti_union_cmd_data *cmd), struct gti_optional_configuration *options) { struct goog_touch_interface *gti; if (!dev || !input_dev || !default_handler) { GOOG_ERR("invalid dev/input_dev or default_handler!\n"); return NULL; } gti = devm_kzalloc(dev, sizeof(struct goog_touch_interface), GFP_KERNEL); if (gti) { gti->vendor_private_data = private_data; gti->vendor_dev = dev; gti->vendor_input_dev = input_dev; gti->vendor_default_handler = default_handler; gti->mf_mode = GTI_MF_MODE_DEFAULT; mutex_init(>i->input_lock); goog_offload_probe(gti); register_panel_bridge(gti); goog_register_tbn(gti); goog_init_options(gti, options); } if (!gti_class) gti_class = class_create(THIS_MODULE, "goog_touch_interface"); if (gti && gti_class) { char *name = kasprintf(GFP_KERNEL, "gti.%d", gti_dev_num); if (name && !alloc_chrdev_region(>i->dev_id, 0, 1, name)) { gti->dev = device_create(gti_class, NULL, gti->dev_id, gti, name); if (gti->dev) { gti_dev_num++; GOOG_LOG("device create \"%s\".\n", name); if (gti->vendor_dev) { sysfs_create_link(>i->dev->kobj, >i->vendor_dev->kobj, "vendor"); } if (gti->vendor_input_dev) { sysfs_create_link(>i->dev->kobj, >i->vendor_input_dev->dev.kobj, "vendor_input"); } } } kfree(name); } if (gti && gti->dev) { int ret; ret = sysfs_create_group(>i->dev->kobj, &goog_attr_group); if (ret) GOOG_ERR("sysfs_create_group() failed, ret= %d!\n", ret); } return gti; } EXPORT_SYMBOL(goog_touch_interface_probe); int goog_touch_interface_remove(struct goog_touch_interface *gti) { if (!gti) return -ENODEV; if (gti->tbn_enable && gti->tbn_register_mask) unregister_tbn(>i->tbn_register_mask); unregister_panel_bridge(>i->panel_bridge); if (gti->vendor_dev) sysfs_remove_link(>i->dev->kobj, "vendor"); if (gti->vendor_input_dev) sysfs_remove_link(>i->dev->kobj, "vendor_input"); if (gti_class) { unregister_chrdev_region(gti->dev_id, 1); device_destroy(gti_class, gti->dev_id); gti_dev_num--; } gti->offload_enable = false; gti->v4l2_enable = false; goog_offload_remove(gti); heatmap_remove(>i->v4l2); devm_kfree(gti->vendor_dev, gti->heatmap_buf); devm_kfree(gti->vendor_dev, gti); if (gti_class && !gti_dev_num) class_destroy(gti_class); return 0; } EXPORT_SYMBOL(goog_touch_interface_remove); MODULE_DESCRIPTION("Google Touch Interface"); MODULE_AUTHOR("Super Liu"); MODULE_LICENSE("GPL v2");