/* * * FocalTech TouchScreen driver. * * Copyright (c) 2012-2020, FocalTech Systems, Ltd., all rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and * may be copied, distributed, and modified under those terms. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * */ /***************************************************************************** * * File Name: focaltech_core.c * * Author: Focaltech Driver Team * * Created: 2016-08-08 * * Abstract: entrance for focaltech ts driver * * Version: V1.0 * *****************************************************************************/ /***************************************************************************** * Included header files *****************************************************************************/ #include #include #include #include #include #include #if defined(CONFIG_FB) #include #include #elif defined(CONFIG_DRM) #if defined(CONFIG_DRM_PANEL) #include #elif defined(CONFIG_ARCH_MSM) #include #endif #elif defined(CONFIG_HAS_EARLYSUSPEND) #include #define FTS_SUSPEND_LEVEL 1 /* Early-suspend level */ #endif #include "focaltech_core.h" /***************************************************************************** * Private constant and macro definitions using #define *****************************************************************************/ #if GOOGLE_REPORT_MODE const short int hopping_freq[4] = {277, 237, 112, 0}; #endif #define FTS_DRIVER_NAME "fts_ts" #define FTS_DRIVER_PEN_NAME "fts_ts,pen" #define INTERVAL_READ_REG 200 /* unit:ms */ #define TIMEOUT_READ_REG 1000 /* unit:ms */ /***************************************************************************** * Global variable or extern global variabls/functions *****************************************************************************/ struct fts_ts_data *fts_data; /***************************************************************************** * Static function prototypes *****************************************************************************/ #if IS_ENABLED(CONFIG_TOUCHSCREEN_PANEL_BRIDGE) static int register_panel_bridge(struct fts_ts_data *ts); static void unregister_panel_bridge(struct drm_bridge *bridge); #endif #if IS_ENABLED(CONFIG_TOUCHSCREEN_OFFLOAD) static void fts_offload_set_running(struct fts_ts_data *ts_data, bool running); static void fts_populate_frame(struct fts_ts_data *ts_data); #endif #if IS_ENABLED(CONFIG_TOUCHSCREEN_OFFLOAD) || \ IS_ENABLED(CONFIG_TOUCHSCREEN_HEATMAP) static int fts_get_heatmap(struct fts_ts_data *ts_data); #endif #if GOOGLE_REPORT_MODE static u8 current_host_status; #endif static int fts_ts_suspend(struct device *dev); static int fts_ts_resume(struct device *dev); static void fts_update_motion_filter(struct fts_ts_data *ts, u8 touches); int fts_check_cid(struct fts_ts_data *ts_data, u8 id_h) { int i = 0; struct ft_chip_id_t *cid = &ts_data->ic_info.cid; u8 cid_h = 0x0; if (cid->type == 0) return -ENODATA; for (i = 0; i < FTS_MAX_CHIP_IDS; i++) { cid_h = ((cid->chip_ids[i] >> 8) & 0x00FF); if (cid_h && (id_h == cid_h)) { return 0; } } return -ENODATA; } /***************************************************************************** * Name: fts_wait_tp_to_valid * Brief: Read chip id until TP FW become valid(Timeout: TIMEOUT_READ_REG), * need call when reset/power on/resume... * Input: * Output: * Return: return 0 if tp valid, otherwise return error code *****************************************************************************/ int fts_wait_tp_to_valid(void) { int ret = 0; int cnt = 0; u8 idh = 0; struct fts_ts_data *ts_data = fts_data; u8 chip_idh = ts_data->ic_info.ids.chip_idh; do { ret = fts_read_reg(FTS_REG_CHIP_ID, &idh); if (ret == -EIO) { FTS_ERROR("Wait tp with unexpected I/O error, ret: %d", ret); return ret; } if (ret == 0 && ((idh == chip_idh) || (fts_check_cid(ts_data, idh) == 0))) { FTS_INFO("TP Ready,Device ID:0x%02x", idh); return 0; } FTS_DEBUG("TP Not Ready,ReadData:0x%02x,ret:%d", idh, ret); cnt++; msleep(INTERVAL_READ_REG); } while ((cnt * INTERVAL_READ_REG) < TIMEOUT_READ_REG); FTS_ERROR("Wait tp timeout"); return -ETIMEDOUT; } /***************************************************************************** * Name: fts_tp_state_recovery * Brief: Need execute this function when reset * Input: * Output: * Return: *****************************************************************************/ void fts_tp_state_recovery(struct fts_ts_data *ts_data) { FTS_FUNC_ENTER(); /* wait tp stable */ fts_wait_tp_to_valid(); /* recover TP charger state 0x8B */ /* recover TP glove state 0xC0 */ /* recover TP cover state 0xC1 */ fts_ex_mode_recovery(ts_data); /* recover TP gesture state 0xD0 */ fts_gesture_recovery(ts_data); FTS_FUNC_EXIT(); } int fts_reset_proc(int hdelayms) { FTS_DEBUG("tp reset"); gpio_direction_output(fts_data->pdata->reset_gpio, 0); /* The minimum reset duration is 1 ms. */ msleep(1); gpio_direction_output(fts_data->pdata->reset_gpio, 1); if (hdelayms) { msleep(hdelayms); } return 0; } void fts_irq_disable(void) { unsigned long irqflags; FTS_FUNC_ENTER(); spin_lock_irqsave(&fts_data->irq_lock, irqflags); if (!fts_data->irq_disabled) { disable_irq_nosync(fts_data->irq); fts_data->irq_disabled = true; } spin_unlock_irqrestore(&fts_data->irq_lock, irqflags); FTS_FUNC_EXIT(); } void fts_irq_enable(void) { unsigned long irqflags = 0; FTS_FUNC_ENTER(); spin_lock_irqsave(&fts_data->irq_lock, irqflags); if (fts_data->irq_disabled) { enable_irq(fts_data->irq); fts_data->irq_disabled = false; } spin_unlock_irqrestore(&fts_data->irq_lock, irqflags); FTS_FUNC_EXIT(); } void fts_hid2std(void) { int ret = 0; u8 buf[3] = {0xEB, 0xAA, 0x09}; if (fts_data->bus_type != FTS_BUS_TYPE_I2C) return; ret = fts_write(buf, 3); if (ret < 0) { FTS_ERROR("hid2std cmd write fail"); } else { msleep(10); buf[0] = buf[1] = buf[2] = 0; ret = fts_read(NULL, 0, buf, 3); if (ret < 0) { FTS_ERROR("hid2std cmd read fail"); } else if ((0xEB == buf[0]) && (0xAA == buf[1]) && (0x08 == buf[2])) { FTS_DEBUG("hidi2c change to stdi2c successful"); } else { FTS_DEBUG("hidi2c change to stdi2c not support or fail"); } } } static int fts_match_cid(struct fts_ts_data *ts_data, u16 type, u8 id_h, u8 id_l, bool force) { #ifdef FTS_CHIP_ID_MAPPING u32 i = 0; u32 j = 0; struct ft_chip_id_t chip_id_list[] = FTS_CHIP_ID_MAPPING; u32 cid_entries = sizeof(chip_id_list) / sizeof(struct ft_chip_id_t); u16 id = (id_h << 8) + id_l; memset(&ts_data->ic_info.cid, 0, sizeof(struct ft_chip_id_t)); for (i = 0; i < cid_entries; i++) { if (!force && (type == chip_id_list[i].type)) { break; } else if (force && (type == chip_id_list[i].type)) { FTS_INFO("match cid,type:0x%x", (int)chip_id_list[i].type); ts_data->ic_info.cid = chip_id_list[i]; return 0; } } if (i >= cid_entries) { return -ENODATA; } for (j = 0; j < FTS_MAX_CHIP_IDS; j++) { if (id == chip_id_list[i].chip_ids[j]) { FTS_DEBUG("cid:%x==%x", id, chip_id_list[i].chip_ids[j]); FTS_INFO("match cid,type:0x%x", (int)chip_id_list[i].type); ts_data->ic_info.cid = chip_id_list[i]; return 0; } } return -ENODATA; #else return -EINVAL; #endif } static int fts_get_chip_types( struct fts_ts_data *ts_data, u8 id_h, u8 id_l, bool fw_valid) { u32 i = 0; struct ft_chip_t ctype[] = FTS_CHIP_TYPE_MAPPING; u32 ctype_entries = sizeof(ctype) / sizeof(struct ft_chip_t); if ((0x0 == id_h) || (0x0 == id_l)) { FTS_ERROR("id_h/id_l is 0"); return -EINVAL; } FTS_DEBUG("verify id:0x%02x%02x", id_h, id_l); for (i = 0; i < ctype_entries; i++) { if (VALID == fw_valid) { if (((id_h == ctype[i].chip_idh) && (id_l == ctype[i].chip_idl)) || (!fts_match_cid(ts_data, ctype[i].type, id_h, id_l, 0))) break; } else { if (((id_h == ctype[i].rom_idh) && (id_l == ctype[i].rom_idl)) || ((id_h == ctype[i].pb_idh) && (id_l == ctype[i].pb_idl)) || ((id_h == ctype[i].bl_idh) && (id_l == ctype[i].bl_idl))) { break; } } } if (i >= ctype_entries) { return -ENODATA; } fts_match_cid(ts_data, ctype[i].type, id_h, id_l, 1); ts_data->ic_info.ids = ctype[i]; return 0; } static int fts_read_bootid(struct fts_ts_data *ts_data, u8 *id) { int ret = 0; u8 chip_id[2] = { 0 }; u8 id_cmd[4] = { 0 }; u32 id_cmd_len = 0; id_cmd[0] = FTS_CMD_START1; id_cmd[1] = FTS_CMD_START2; ret = fts_write(id_cmd, 2); if (ret < 0) { FTS_ERROR("start cmd write fail"); return ret; } msleep(FTS_CMD_START_DELAY); id_cmd[0] = FTS_CMD_READ_ID; id_cmd[1] = id_cmd[2] = id_cmd[3] = 0x00; if (ts_data->ic_info.is_incell) id_cmd_len = FTS_CMD_READ_ID_LEN_INCELL; else id_cmd_len = FTS_CMD_READ_ID_LEN; ret = fts_read(id_cmd, id_cmd_len, chip_id, 2); if ((ret < 0) || (0x0 == chip_id[0]) || (0x0 == chip_id[1])) { FTS_ERROR("read boot id fail,read:0x%02x%02x", chip_id[0], chip_id[1]); return -EIO; } id[0] = chip_id[0]; id[1] = chip_id[1]; return 0; } /***************************************************************************** * Name: fts_get_ic_information * Brief: read chip id to get ic information, after run the function, driver w- * ill know which IC is it. * If cant get the ic information, maybe not focaltech's touch IC, need * unregister the driver * Input: * Output: * Return: return 0 if get correct ic information, otherwise return error code *****************************************************************************/ static int fts_get_ic_information(struct fts_ts_data *ts_data) { int ret = 0; int cnt = 0; u8 chip_id[2] = { 0 }; ts_data->ic_info.is_incell = FTS_CHIP_IDC; ts_data->ic_info.hid_supported = FTS_HID_SUPPORTTED; do { ret = fts_read_reg(FTS_REG_CHIP_ID, &chip_id[0]); ret = fts_read_reg(FTS_REG_CHIP_ID2, &chip_id[1]); if ((ret < 0) || (0x0 == chip_id[0]) || (0x0 == chip_id[1])) { FTS_DEBUG("chip id read invalid, read:0x%02x%02x", chip_id[0], chip_id[1]); } else { ret = fts_get_chip_types(ts_data, chip_id[0], chip_id[1], VALID); if (!ret) break; else FTS_DEBUG("TP not ready, read:0x%02x%02x", chip_id[0], chip_id[1]); } cnt++; msleep(INTERVAL_READ_REG); } while ((cnt * INTERVAL_READ_REG) < TIMEOUT_READ_REG); if ((cnt * INTERVAL_READ_REG) >= TIMEOUT_READ_REG) { FTS_INFO("fw is invalid, need read boot id"); if (ts_data->ic_info.hid_supported) { fts_hid2std(); } ret = fts_read_bootid(ts_data, &chip_id[0]); if (ret < 0) { FTS_ERROR("read boot id fail"); return ret; } ret = fts_get_chip_types(ts_data, chip_id[0], chip_id[1], INVALID); if (ret < 0) { FTS_ERROR("can't get ic informaton"); return ret; } } FTS_INFO("get ic information, chip id = 0x%02x%02x(cid type=0x%x)", ts_data->ic_info.ids.chip_idh, ts_data->ic_info.ids.chip_idl, ts_data->ic_info.cid.type); return 0; } /***************************************************************************** * Reprot related *****************************************************************************/ static void fts_show_touch_buffer(u8 *data, int datalen) { int i = 0; int count = 0; char *tmpbuf = NULL; tmpbuf = kzalloc(1024, GFP_KERNEL); if (!tmpbuf) { FTS_ERROR("tmpbuf zalloc fail"); return; } for (i = 0; i < datalen; i++) { count += snprintf(tmpbuf + count, 1024 - count, "%02X,", data[i]); if (count >= 1024) break; } FTS_DEBUG("point buffer:%s", tmpbuf); if (tmpbuf) { kfree(tmpbuf); tmpbuf = NULL; } } void fts_release_all_finger(void) { struct fts_ts_data *ts_data = fts_data; struct input_dev *input_dev = ts_data->input_dev; #if FTS_MT_PROTOCOL_B_EN u32 finger_count = 0; u32 max_touches = ts_data->pdata->max_touch_number; #endif mutex_lock(&ts_data->report_mutex); #if FTS_MT_PROTOCOL_B_EN for (finger_count = 0; finger_count < max_touches; finger_count++) { input_mt_slot(input_dev, finger_count); input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, false); #if IS_ENABLED(CONFIG_TOUCHSCREEN_OFFLOAD) ts_data->offload.coords[finger_count].status = COORD_STATUS_INACTIVE; ts_data->offload.coords[finger_count].major = 0; ts_data->offload.coords[finger_count].minor = 0; ts_data->offload.coords[finger_count].pressure = 0; ts_data->offload.coords[finger_count].rotation = 0; #endif } #else input_mt_sync(input_dev); #endif input_report_key(input_dev, BTN_TOUCH, 0); input_sync(input_dev); #if FTS_PEN_EN input_report_key(ts_data->pen_dev, BTN_TOOL_PEN, 0); input_report_key(ts_data->pen_dev, BTN_TOUCH, 0); input_sync(ts_data->pen_dev); #endif ts_data->touchs = 0; ts_data->key_state = 0; mutex_unlock(&ts_data->report_mutex); } /***************************************************************************** * Name: fts_input_report_key * Brief: process key events,need report key-event if key enable. * if point's coordinate is in (x_dim-50,y_dim-50) ~ (x_dim+50,y_dim+50), * need report it to key event. * x_dim: parse from dts, means key x_coordinate, dimension:+-50 * y_dim: parse from dts, means key y_coordinate, dimension:+-50 * Input: * Output: * Return: return 0 if it's key event, otherwise return error code *****************************************************************************/ static int fts_input_report_key(struct fts_ts_data *data, int index) { int i = 0; int x = data->events[index].x; int y = data->events[index].y; int *x_dim = &data->pdata->key_x_coords[0]; int *y_dim = &data->pdata->key_y_coords[0]; if (!data->pdata->have_key) { return -EINVAL; } for (i = 0; i < data->pdata->key_number; i++) { if ((x >= x_dim[i] - FTS_KEY_DIM) && (x <= x_dim[i] + FTS_KEY_DIM) && (y >= y_dim[i] - FTS_KEY_DIM) && (y <= y_dim[i] + FTS_KEY_DIM)) { if (EVENT_DOWN(data->events[index].flag) && !(data->key_state & (1 << i))) { input_report_key(data->input_dev, data->pdata->keys[i], 1); data->key_state |= (1 << i); FTS_DEBUG("Key%d(%d,%d) DOWN!", i, x, y); } else if (EVENT_UP(data->events[index].flag) && (data->key_state & (1 << i))) { input_report_key(data->input_dev, data->pdata->keys[i], 0); data->key_state &= ~(1 << i); FTS_DEBUG("Key%d(%d,%d) Up!", i, x, y); } return 0; } } return -EINVAL; } #if FTS_MT_PROTOCOL_B_EN static int fts_input_report_b(struct fts_ts_data *data) { int i = 0; int touchs = 0; bool va_reported = false; u32 max_touch_num = data->pdata->max_touch_number; struct ts_event *events = data->events; for (i = 0; i < data->touch_point; i++) { if (fts_input_report_key(data, i) == 0) { continue; } va_reported = true; if (EVENT_DOWN(events[i].flag)) { #if IS_ENABLED(CONFIG_TOUCHSCREEN_OFFLOAD) data->offload.coords[events[i].id].status = COORD_STATUS_FINGER; data->offload.coords[events[i].id].x = events[i].x; data->offload.coords[events[i].id].y = events[i].y; data->offload.coords[events[i].id].pressure = events[i].p; data->offload.coords[events[i].id].major = events[i].major; data->offload.coords[events[i].id].minor = events[i].minor; if (!data->offload.offload_running) { #endif input_mt_slot(data->input_dev, events[i].id); input_mt_report_slot_state(data->input_dev, MT_TOOL_FINGER, true); #if FTS_REPORT_PRESSURE_EN if (events[i].p <= 0) { events[i].p = 0x00; } input_report_abs(data->input_dev, ABS_MT_PRESSURE, events[i].p); #endif if (events[i].area <= 0) { events[i].area = 0x00; } input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR, events[i].major); input_report_abs(data->input_dev, ABS_MT_TOUCH_MINOR, events[i].minor); input_report_abs(data->input_dev, ABS_MT_POSITION_X, events[i].x); input_report_abs(data->input_dev, ABS_MT_POSITION_Y, events[i].y); touchs |= BIT(events[i].id); data->touchs |= BIT(events[i].id); #if IS_ENABLED(CONFIG_TOUCHSCREEN_OFFLOAD) } #endif if ((data->log_level >= 2) || ((1 == data->log_level) && (FTS_TOUCH_DOWN == events[i].flag))) { FTS_DEBUG("[B]P%d(%d, %d)[ma:%d,mi:%d,p:%d] DOWN!", events[i].id, events[i].x, events[i].y, events[i].major, events[i].minor, events[i].p); } } else { //EVENT_UP #if IS_ENABLED(CONFIG_TOUCHSCREEN_OFFLOAD) data->offload.coords[events[i].id].status = COORD_STATUS_INACTIVE; if (!data->offload.offload_running) { #endif input_mt_slot(data->input_dev, events[i].id); input_mt_report_slot_state(data->input_dev, MT_TOOL_FINGER, false); data->touchs &= ~BIT(events[i].id); #if IS_ENABLED(CONFIG_TOUCHSCREEN_OFFLOAD) } #endif if (data->log_level >= 1) { FTS_DEBUG("[B1]P%d UP!", events[i].id); } } } #if IS_ENABLED(CONFIG_TOUCHSCREEN_OFFLOAD) if (!data->offload.offload_running) { #endif if (unlikely(data->touchs ^ touchs)) { for (i = 0; i < max_touch_num; i++) { if (BIT(i) & (data->touchs ^ touchs)) { if (data->log_level >= 1) { FTS_DEBUG("[B2]P%d UP!", i); } va_reported = true; input_mt_slot(data->input_dev, i); input_mt_report_slot_state(data->input_dev, MT_TOOL_FINGER, false); } } } data->touchs = touchs; if (va_reported) { /* touchs==0, there's no point but key */ if (EVENT_NO_DOWN(data) || (!touchs)) { if (data->log_level >= 1) { FTS_DEBUG("[B]Points All Up!"); } input_report_key(data->input_dev, BTN_TOUCH, 0); } else { input_report_key(data->input_dev, BTN_TOUCH, 1); } } input_set_timestamp(data->input_dev, data->coords_timestamp); input_sync(data->input_dev); fts_update_motion_filter(data, data->point_num); #if IS_ENABLED(CONFIG_TOUCHSCREEN_OFFLOAD) } #endif return 0; } #else static int fts_input_report_a(struct fts_ts_data *data) { int i = 0; int touchs = 0; bool va_reported = false; struct ts_event *events = data->events; for (i = 0; i < data->touch_point; i++) { if (fts_input_report_key(data, i) == 0) { continue; } va_reported = true; if (EVENT_DOWN(events[i].flag)) { input_report_abs(data->input_dev, ABS_MT_TRACKING_ID, events[i].id); #if FTS_REPORT_PRESSURE_EN if (events[i].p <= 0) { events[i].p = 0x00; } input_report_abs(data->input_dev, ABS_MT_PRESSURE, events[i].p); #endif if (events[i].area <= 0) { events[i].area = 0x00; } input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR, events[i].major); input_report_abs(data->input_dev, ABS_MT_TOUCH_MINOR, events[i].minor); input_report_abs(data->input_dev, ABS_MT_POSITION_X, events[i].x); input_report_abs(data->input_dev, ABS_MT_POSITION_Y, events[i].y); input_mt_sync(data->input_dev); if ((data->log_level >= 2) || ((1 == data->log_level) && (FTS_TOUCH_DOWN == events[i].flag))) { FTS_DEBUG("[A]P%d(%d, %d)[ma:%d,mi:%d,p:%d] DOWN!", events[i].id, events[i].x, events[i].y, events[i].major, events[i].minor, events[i].p); } touchs++; } } /* last point down, current no point but key */ if (data->touchs && !touchs) { va_reported = true; } data->touchs = touchs; if (va_reported) { if (EVENT_NO_DOWN(data)) { if (data->log_level >= 1) { FTS_DEBUG("[A]Points All Up!"); } input_report_key(data->input_dev, BTN_TOUCH, 0); input_mt_sync(data->input_dev); } else { input_report_key(data->input_dev, BTN_TOUCH, 1); } } input_set_timestamp(data->input_dev, data->timestamp); input_sync(data->input_dev); return 0; } #endif #if FTS_PEN_EN static int fts_input_pen_report(struct fts_ts_data *data) { struct input_dev *pen_dev = data->pen_dev; struct pen_event *pevt = &data->pevent; u8 *buf = data->point_buf; if (buf[3] & 0x08) input_report_key(pen_dev, BTN_STYLUS, 1); else input_report_key(pen_dev, BTN_STYLUS, 0); if (buf[3] & 0x02) input_report_key(pen_dev, BTN_STYLUS2, 1); else input_report_key(pen_dev, BTN_STYLUS2, 0); pevt->inrange = (buf[3] & 0x20) ? 1 : 0; pevt->tip = (buf[3] & 0x01) ? 1 : 0; pevt->x = ((buf[4] & 0x0F) << 8) + buf[5]; pevt->y = ((buf[6] & 0x0F) << 8) + buf[7]; pevt->p = ((buf[8] & 0x0F) << 8) + buf[9]; pevt->id = buf[6] >> 4; pevt->flag = buf[4] >> 6; pevt->tilt_x = (buf[10] << 8) + buf[11]; pevt->tilt_y = (buf[12] << 8) + buf[13]; pevt->tool_type = BTN_TOOL_PEN; if (data->log_level >= 2 || ((1 == data->log_level) && (FTS_TOUCH_DOWN == pevt->flag))) { FTS_DEBUG("[PEN]x:%d,y:%d,p:%d,inrange:%d,tip:%d,flag:%d DOWN!", pevt->x, pevt->y, pevt->p, pevt->inrange, pevt->tip, pevt->flag); } if ( (data->log_level >= 1) && (!pevt->inrange)) { FTS_DEBUG("[PEN]UP!"); } input_report_abs(pen_dev, ABS_X, pevt->x); input_report_abs(pen_dev, ABS_Y, pevt->y); input_report_abs(pen_dev, ABS_PRESSURE, pevt->p); /* check if the pen support tilt event */ if ((pevt->tilt_x != 0) || (pevt->tilt_y != 0)) { input_report_abs(pen_dev, ABS_TILT_X, pevt->tilt_x); input_report_abs(pen_dev, ABS_TILT_Y, pevt->tilt_y); } input_report_key(pen_dev, BTN_TOUCH, pevt->tip); input_report_key(pen_dev, BTN_TOOL_PEN, pevt->inrange); input_sync(pen_dev); return 0; } #endif static int fts_read_touchdata(struct fts_ts_data *data) { u8 *buf = data->point_buf; u8 cmd[1] = { 0 }; #if IS_ENABLED(GOOGLE_REPORT_MODE) u8 get_regB2_status = 0; u8 check_regB2_status = 0; u8 current_hopping = 0; u8 new_hopping = 0; int i; #endif cmd[0] = FTS_CMD_READ_TOUCH_DATA; #if IS_ENABLED(CONFIG_TOUCHSCREEN_OFFLOAD) || \ IS_ENABLED(CONFIG_TOUCHSCREEN_HEATMAP) fts_get_heatmap(data); memcpy(buf + 1,data->heatmap_raw, data->pnt_buf_size - 1); #else int ret = fts_read(cmd, 1, buf + 1, data->pnt_buf_size - 1); if (ret < 0) { FTS_ERROR("touch data(%x) abnormal,ret:%d", buf[1], ret); return -EIO; } if (data->gesture_mode) { ret = fts_gesture_readdata(data, buf + FTS_TOUCH_DATA_LEN); if (0 == ret) { FTS_INFO("succuss to get gesture data in irq handler"); return 1; } } #endif #if IS_ENABLED(GOOGLE_REPORT_MODE) if (data->work_mode == FTS_REG_WORKMODE_WORK_VALUE) { fts_read_reg(FTS_REG_CUSTOMER_STATUS, &get_regB2_status); check_regB2_status = get_regB2_status ^ current_host_status ; if (check_regB2_status) { // current_status is different with previous_status for(i = STATUS_HOPPING; i < STATUS_CNT_END; i++) { switch (i) { case STATUS_HOPPING : // Hopping result current_hopping = current_host_status & 0x03; new_hopping = get_regB2_status & 0x03; if (current_hopping != new_hopping && current_hopping < 3 && new_hopping < 3) { FTS_INFO("-------Hopping from %dKhz to %dKhz\n", hopping_freq[current_hopping], hopping_freq[new_hopping]); } break; case STATUS_PALM : // Palm result if (check_regB2_status & (1 << i)) { FTS_INFO("-------PALM mode = %s\n", (get_regB2_status & (1 << i)) ? "Enable" : "Disable" ); } break; case STATUS_WATER : // Water result if (check_regB2_status & (1 << i)) { FTS_INFO("-------WATER mode = %s\n", (get_regB2_status & (1 << i)) ? "Enable" : "Disable" ); } break; case STATUS_GRIP : // Grip result if( check_regB2_status & (1 << i)) { FTS_INFO("-------GRIP mode = %s\n", (get_regB2_status & (1 << i)) ? "Enable" : "Disable" ); } break; case STATUS_GLOVE : // Glove result if( check_regB2_status & (1 << i)) { FTS_INFO("-------GlOVE mode = %s\n", (get_regB2_status & (1 << i)) ? "Enable" : "Disable" ); } break; case STATUS_EDGE_PALM : // Edge palm result if( check_regB2_status & (1 << i)) { FTS_INFO("-------Edge palm = %s\n", (get_regB2_status & (1 << i)) ? "Enable" : "Disable" ); } break; case STATUS_LPTW : // LPTW result if( check_regB2_status & (1 << i)) { FTS_INFO("-------LPTW = %s\n", (get_regB2_status & (1 << i)) ? "Enable" : "Disable" ); } break; default: break; } } current_host_status = get_regB2_status; } } #endif if (data->log_level >= 3) { fts_show_touch_buffer(buf, data->pnt_buf_size); } return 0; } static int fts_read_parse_touchdata(struct fts_ts_data *data) { int ret = 0; int i = 0; u8 pointid = 0; int base = 0; struct ts_event *events = data->events; int max_touch_num = data->pdata->max_touch_number; u8 *buf = data->point_buf; ret = fts_read_touchdata(data); if (ret) { return ret; } #if FTS_PEN_EN if ((buf[2] & 0xF0) == 0xB0) { fts_input_pen_report(data); return 2; } #endif data->point_num = buf[FTS_TOUCH_POINT_NUM] & 0x0F; data->touch_point = 0; if (data->ic_info.is_incell) { if ((data->point_num == 0x0F) && (buf[2] == 0xFF) && (buf[3] == 0xFF) && (buf[4] == 0xFF) && (buf[5] == 0xFF) && (buf[6] == 0xFF)) { FTS_DEBUG("touch buff is 0xff, need recovery state"); fts_release_all_finger(); fts_tp_state_recovery(data); data->point_num = 0; return -EIO; } } if (data->point_num > max_touch_num) { FTS_DEBUG("invalid point_num(%d)", data->point_num); data->point_num = 0; return -EIO; } for (i = 0; i < max_touch_num; i++) { base = FTS_ONE_TCH_LEN * i; pointid = (buf[FTS_TOUCH_ID_POS + base]) >> 4; if (pointid >= FTS_MAX_ID) break; else if (pointid >= max_touch_num) { FTS_ERROR("ID(%d) beyond max_touch_number", pointid); return -EINVAL; } data->touch_point++; events[i].x = ((buf[FTS_TOUCH_X_H_POS + base] & 0x0F) << 8) + (buf[FTS_TOUCH_X_L_POS + base] & 0xFF); events[i].y = ((buf[FTS_TOUCH_Y_H_POS + base] & 0x0F) << 8) + (buf[FTS_TOUCH_Y_L_POS + base] & 0xFF); events[i].flag = buf[FTS_TOUCH_EVENT_POS + base] >> 6; events[i].id = buf[FTS_TOUCH_ID_POS + base] >> 4; events[i].p = (((buf[FTS_TOUCH_AREA_POS + base] << 1) & 0x02) + (buf[FTS_TOUCH_PRE_POS + base] & 0x01)) * FTS_PRESSURE_SCALE; events[i].minor = ((buf[FTS_TOUCH_PRE_POS + base] >> 1) & 0x7F) * data->pdata->mm2px; events[i].major = ((buf[FTS_TOUCH_AREA_POS + base] >> 1) & 0x7F) * data->pdata->mm2px; if (EVENT_DOWN(events[i].flag) && (data->point_num == 0)) { FTS_INFO("abnormal touch data from fw"); return -EIO; } } if (data->touch_point == 0) { FTS_INFO("no touch point information(%02x)", buf[2]); return -EIO; } return 0; } static void fts_irq_read_report(void) { int ret = 0; struct fts_ts_data *ts_data = fts_data; #if FTS_ESDCHECK_EN fts_esdcheck_set_intr(1); #endif #if FTS_POINT_REPORT_CHECK_EN fts_prc_queue_work(ts_data); #endif ret = fts_read_parse_touchdata(ts_data); if (ret == 0) { mutex_lock(&ts_data->report_mutex); #if FTS_MT_PROTOCOL_B_EN fts_input_report_b(ts_data); #else fts_input_report_a(ts_data); #endif mutex_unlock(&ts_data->report_mutex); } #if IS_ENABLED(CONFIG_TOUCHSCREEN_OFFLOAD) ret = touch_offload_reserve_frame(&ts_data->offload, &ts_data->reserved_frame); if (ret != 0) { PR_LOGD("Could not reserve a frame: error=%d.\n", ret); /* Stop offload when there are no buffers available. */ fts_offload_set_running(ts_data, false); } else { fts_offload_set_running(ts_data, true); PR_LOGD("reserve a frame ok"); fts_populate_frame(ts_data); ret = touch_offload_queue_frame(&ts_data->offload, ts_data->reserved_frame); if (ret != 0) { FTS_ERROR("Failed to queue reserved frame: error=%d.\n", ret); } } #endif #if IS_ENABLED(CONFIG_TOUCHSCREEN_HEATMAP) heatmap_read(&ts_data->v4l2, ktime_to_ns(ts_data->coords_timestamp)); #endif #if FTS_ESDCHECK_EN fts_esdcheck_set_intr(0); #endif } #if IS_ENABLED(CONFIG_TOUCHSCREEN_PANEL_BRIDGE) static void fts_ts_aggregate_bus_state(struct fts_ts_data *ts) { /* Complete or cancel any outstanding transitions */ cancel_work_sync(&ts->suspend_work); cancel_work_sync(&ts->resume_work); if ((ts->bus_refmask == 0 && ts->power_status == FTS_TS_STATE_SUSPEND) || (ts->bus_refmask != 0 && ts->power_status != FTS_TS_STATE_SUSPEND)) return; if (ts->bus_refmask == 0) queue_work(ts->ts_workqueue, &ts->suspend_work); else queue_work(ts->ts_workqueue, &ts->resume_work); } int fts_ts_set_bus_ref(struct fts_ts_data *ts, u16 ref, bool enable) { int result = 0; mutex_lock(&ts->bus_mutex); if ((enable && (ts->bus_refmask & ref)) || (!enable && !(ts->bus_refmask & ref))) { mutex_unlock(&ts->bus_mutex); return -EINVAL; } if (enable) { /* IRQs can only keep the bus active. IRQs received while the * bus is transferred to AOC should be ignored. */ if (ref == FTS_TS_BUS_REF_IRQ && ts->bus_refmask == 0) result = -EAGAIN; else ts->bus_refmask |= ref; } else ts->bus_refmask &= ~ref; fts_ts_aggregate_bus_state(ts); mutex_unlock(&ts->bus_mutex); /* When triggering a wake, wait up to one second to resume. SCREEN_ON * and IRQ references do not need to wait. */ if (enable && ref != FTS_TS_BUS_REF_SCREEN_ON && ref != FTS_TS_BUS_REF_IRQ) { wait_for_completion_timeout(&ts->bus_resumed, HZ); if (ts->power_status != FTS_TS_STATE_POWER_ON) { FTS_ERROR("Failed to wake the touch bus.\n"); result = -ETIMEDOUT; } } return result; } 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 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_enable(struct drm_bridge *bridge) { struct fts_ts_data *ts = container_of(bridge, struct fts_ts_data, panel_bridge); if (!ts->is_panel_lp_mode) fts_ts_set_bus_ref(ts, FTS_TS_BUS_REF_SCREEN_ON, true); } static void panel_bridge_disable(struct drm_bridge *bridge) { struct fts_ts_data *ts = container_of(bridge, struct fts_ts_data, 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; } fts_ts_set_bus_ref(ts, FTS_TS_BUS_REF_SCREEN_ON, false); } static void panel_bridge_mode_set(struct drm_bridge *bridge, const struct drm_display_mode *mode, const struct drm_display_mode *adjusted_mode) { struct fts_ts_data *ts = container_of(bridge, struct fts_ts_data, panel_bridge); if (!ts->connector || !ts->connector->state) ts->connector = get_bridge_connector(bridge); ts->is_panel_lp_mode = bridge_is_lp_mode(ts->connector); fts_ts_set_bus_ref(ts, FTS_TS_BUS_REF_SCREEN_ON, !ts->is_panel_lp_mode); if (adjusted_mode) { int vrefresh = drm_mode_vrefresh(adjusted_mode); if (ts->display_refresh_rate != vrefresh) { FTS_INFO("refresh rate(Hz) changed to %d from %d\n", vrefresh, ts->display_refresh_rate); ts->display_refresh_rate = vrefresh; } } } 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 fts_ts_data *ts) { FTS_FUNC_ENTER(); #ifdef CONFIG_OF ts->panel_bridge.of_node = ts->spi->dev.of_node; #endif ts->panel_bridge.funcs = &panel_bridge_funcs; drm_bridge_add(&ts->panel_bridge); FTS_FUNC_EXIT(); return 0; } static void unregister_panel_bridge(struct drm_bridge *bridge) { struct drm_bridge *node; FTS_FUNC_ENTER(); 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; FTS_FUNC_EXIT(); } #endif /* Update a state machine used to toggle control of the touch IC's motion * filter. */ static void fts_update_motion_filter(struct fts_ts_data *ts, u8 touches) { /* Motion filter timeout, in milliseconds */ const u32 mf_timeout_ms = 500; u8 next_state; next_state = ts->mf_state; if (ts->mf_mode == MF_OFF) { next_state = MF_UNFILTERED; } else if (ts->mf_mode == MF_DYNAMIC) { /* 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. */ next_state = ts->mf_state; switch (ts->mf_state) { case MF_FILTERED: if (touches == 1) { next_state = MF_UNFILTERED; ts->mf_downtime = ktime_get(); } break; case MF_UNFILTERED: if (touches == 0) { next_state = MF_FILTERED; } else if (touches > 1 || ktime_after(ktime_get(), ktime_add_ms(ts->mf_downtime, mf_timeout_ms))) { next_state = MF_FILTERED_LOCKED; } break; case MF_FILTERED_LOCKED: if (touches == 0) next_state = MF_FILTERED; break; } } else if (ts->mf_mode == MF_ON) { next_state = MF_FILTERED; } else { /* Set MF_DYNAMIC as default when an invalid value is found. */ ts->mf_mode = MF_DYNAMIC; return; } /* Send command to update firmware continuous report */ if ((next_state == MF_UNFILTERED) != (ts->mf_state == MF_UNFILTERED)) { bool en = (next_state == MF_UNFILTERED) ? true : false; fts_set_continuous_mode(en); } ts->mf_state = next_state; } #if IS_ENABLED(CONFIG_TOUCHSCREEN_OFFLOAD) || \ IS_ENABLED(CONFIG_TOUCHSCREEN_HEATMAP) #if IS_ENABLED(GOOGLE_HEATMAP_DEBUG) static void fts_show_heatmap_data(struct fts_ts_data *ts_data) { int i; int idx_buff; u8 tx = ts_data->pdata->tx_ch_num; u8 rx = ts_data->pdata->rx_ch_num; FTS_DEBUG("Show mutual data:\n"); idx_buff = 0; for (i = 0; i < rx; i++) { FTS_DEBUG("RX(%d):%5d %5d %5d %5d %5d %5d %5d %5d %5d %5d %5d %5d %5d %5d %5d %5d", idx_buff, (s16)ts_data->heatmap_buff[idx_buff], (s16)ts_data->heatmap_buff[idx_buff + 1], (s16)ts_data->heatmap_buff[idx_buff + 2], (s16)ts_data->heatmap_buff[idx_buff + 3], (s16)ts_data->heatmap_buff[idx_buff + 4], (s16)ts_data->heatmap_buff[idx_buff + 5], (s16)ts_data->heatmap_buff[idx_buff + 6], (s16)ts_data->heatmap_buff[idx_buff + 7], (s16)ts_data->heatmap_buff[idx_buff + 8], (s16)ts_data->heatmap_buff[idx_buff + 9], (s16)ts_data->heatmap_buff[idx_buff + 10], (s16)ts_data->heatmap_buff[idx_buff + 11], (s16)ts_data->heatmap_buff[idx_buff + 12], (s16)ts_data->heatmap_buff[idx_buff + 13], (s16)ts_data->heatmap_buff[idx_buff + 14], (s16)ts_data->heatmap_buff[idx_buff + 15]); idx_buff += tx; } FTS_DEBUG("Show Tx self data:\n"); FTS_DEBUG("Tx(idx_buff=%d):%5d %5d %5d %5d %5d %5d %5d %5d %5d %5d %5d %5d %5d %5d %5d %5d", idx_buff, (s16)ts_data->heatmap_buff[idx_buff], (s16)ts_data->heatmap_buff[idx_buff + 1], (s16)ts_data->heatmap_buff[idx_buff + 2], (s16)ts_data->heatmap_buff[idx_buff + 3], (s16)ts_data->heatmap_buff[idx_buff + 4], (s16)ts_data->heatmap_buff[idx_buff + 5], (s16)ts_data->heatmap_buff[idx_buff + 6], (s16)ts_data->heatmap_buff[idx_buff + 7], (s16)ts_data->heatmap_buff[idx_buff + 8], (s16)ts_data->heatmap_buff[idx_buff + 9], (s16)ts_data->heatmap_buff[idx_buff + 10], (s16)ts_data->heatmap_buff[idx_buff + 11], (s16)ts_data->heatmap_buff[idx_buff + 12], (s16)ts_data->heatmap_buff[idx_buff + 13], (s16)ts_data->heatmap_buff[idx_buff + 14], (s16)ts_data->heatmap_buff[idx_buff + 15]); idx_buff += 16; FTS_DEBUG("Show Rx self data:\n"); FTS_DEBUG("Rx(idx_buff=%d)%5d %5d %5d %5d %5d %5d %5d %5d %5d %5d %5d %5d %5d %5d %5d %5d", idx_buff, (short)ts_data->heatmap_buff[idx_buff], (short)ts_data->heatmap_buff[idx_buff + 1], (short)ts_data->heatmap_buff[idx_buff + 2], (short)ts_data->heatmap_buff[idx_buff + 3], (short)ts_data->heatmap_buff[idx_buff + 4], (short)ts_data->heatmap_buff[idx_buff + 5], (short)ts_data->heatmap_buff[idx_buff + 6], (short)ts_data->heatmap_buff[idx_buff + 7], (short)ts_data->heatmap_buff[idx_buff + 8], (short)ts_data->heatmap_buff[idx_buff + 9], (short)ts_data->heatmap_buff[idx_buff + 10], (short)ts_data->heatmap_buff[idx_buff + 11], (short)ts_data->heatmap_buff[idx_buff + 12], (short)ts_data->heatmap_buff[idx_buff + 13], (short)ts_data->heatmap_buff[idx_buff + 14], (short)ts_data->heatmap_buff[idx_buff + 15]); idx_buff += 16; FTS_DEBUG("Rx(idx_buff=%d)%5d %5d %5d %5d %5d %5d %5d %5d %5d %5d %5d %5d %5d %5d %5d %5d", idx_buff, (short)ts_data->heatmap_buff[idx_buff], (short)ts_data->heatmap_buff[idx_buff + 1], (short)ts_data->heatmap_buff[idx_buff + 2], (short)ts_data->heatmap_buff[idx_buff + 3], (short)ts_data->heatmap_buff[idx_buff + 4], (short)ts_data->heatmap_buff[idx_buff + 5], (short)ts_data->heatmap_buff[idx_buff + 6], (short)ts_data->heatmap_buff[idx_buff + 7], (short)ts_data->heatmap_buff[idx_buff + 8], (short)ts_data->heatmap_buff[idx_buff + 9], (short)ts_data->heatmap_buff[idx_buff + 10], (short)ts_data->heatmap_buff[idx_buff + 11], (short)ts_data->heatmap_buff[idx_buff + 12], (short)ts_data->heatmap_buff[idx_buff + 13], (short)ts_data->heatmap_buff[idx_buff + 14], (short)ts_data->heatmap_buff[idx_buff + 15]); idx_buff += 16; FTS_DEBUG("Rx(idx_buff=%d)%5d %5d", idx_buff, (short)ts_data->heatmap_buff[idx_buff], (short)ts_data->heatmap_buff[idx_buff + 1]); idx_buff += 2; FTS_DEBUG("Print done, idx_buff=%d", idx_buff); } #endif /* GOOGLE_HEATMAP_DEBUG */ extern void transpose_raw(u8 *src, u8 *dist, int tx, int rx); static int fts_get_heatmap(struct fts_ts_data *ts_data) { int ret = 0; int i; int idx_buff = 0; int node_num = 0; int self_node = 0; int self_data_size = 0; int total_heatmap_data_size = 0; u8 cmd[1] = {0}; u8 tx = ts_data->pdata->tx_ch_num; u8 rx = ts_data->pdata->rx_ch_num; int idx_ms_raw = FTS_CAP_DATA_OFFSET; int idx_ss_tx_raw = 0; int idx_ss_rx_raw = 0; #if IS_ENABLED(GOOGLE_HEATMAP_DEBUG) FTS_FUNC_ENTER(); #endif node_num = tx * rx; self_node = tx + rx; idx_ss_tx_raw = FTS_CAP_DATA_OFFSET + (node_num + rx) * sizeof(u16); idx_ss_rx_raw = FTS_CAP_DATA_OFFSET + node_num * sizeof(u16); self_data_size = FTS_SELF_DATA_LEN * sizeof(u16); total_heatmap_data_size = FTS_CAP_DATA_OFFSET + node_num * sizeof(u16) + self_data_size; /* The format of heatmap from touch chip * * |- cap header (91) -|- mutual data(tx*rx*2) -|- cap(on) data(68*2) -| * |- -|- 16*34*2 -|- (34+16)*2 -| * * Only needs mutual data and cap(on) data. */ if (!ts_data->heatmap_buff) { FTS_ERROR("The heatmap_buff is not allocated!!"); ret = -ENOMEM; goto exit; } /* Get total heatmap data (cap header(91) + ms + ss). */ cmd[0] = FTS_CMD_READ_TOUCH_DATA; ret = fts_read(cmd, 1, ts_data->heatmap_raw, total_heatmap_data_size); if (ret < 0) { FTS_ERROR("Failed to get heatmap raw data, ret=%d.", ret); goto exit; } #if IS_ENABLED(GOOGLE_HEATMAP_DEBUG) FTS_DEBUG("start to copy matual data,idx_buff=%d,idx_ms_raw=%d.", idx_buff, idx_ms_raw); #endif /* MS: Transform the order from RX->TX. */ transpose_raw(ts_data->heatmap_raw + idx_ms_raw, ts_data->trans_raw, tx, rx); /* Copy mutual data. */ for (i = 0; i < node_num; i++) { ((u16*)ts_data->heatmap_buff)[idx_buff++] = (u16)(ts_data->trans_raw[(i * 2)] << 8) + ts_data->trans_raw[(i * 2) + 1]; } /* Copy tx self data first */ #if IS_ENABLED(GOOGLE_HEATMAP_DEBUG) FTS_DEBUG("Start to copy the tx self data,idx_buff=%d,idx_ss_tx_raw=%d.", idx_buff, idx_ss_tx_raw); #endif for (i = 0 ; i < tx; i++) { ((u16*)ts_data->heatmap_buff)[idx_buff++] = (u16)(ts_data->heatmap_raw[idx_ss_tx_raw + (i * 2)] << 8) + ts_data->heatmap_raw[idx_ss_tx_raw +(i * 2) + 1]; } /* Then copy rx self data */ #if IS_ENABLED(GOOGLE_HEATMAP_DEBUG) FTS_DEBUG("Start to copy the rx self data,idx_buff=%d,idx_ss_rx_raw=%d.", idx_buff, idx_ss_rx_raw); #endif for (i = 0 ; i < rx; i++) { ((u16*)ts_data->heatmap_buff)[idx_buff++] = (u16)(ts_data->heatmap_raw[idx_ss_rx_raw + (i * 2)] << 8) + ts_data->heatmap_raw[idx_ss_rx_raw + (i * 2) + 1]; } #if IS_ENABLED(GOOGLE_HEATMAP_DEBUG) /* Show the heatmap data for debugging. */ fts_show_heatmap_data(ts_data); #endif #if IS_ENABLED(GOOGLE_HEATMAP_DEBUG) FTS_FUNC_EXIT(); #endif exit: return ret; } #endif /* CONFIG_TOUCHSCREEN_OFFLOAD || CONFIG_TOUCHSCREEN_HEATMAP */ #if IS_ENABLED(CONFIG_TOUCHSCREEN_OFFLOAD) static void fts_offload_set_running(struct fts_ts_data *ts_data, bool running) { ts_data->offload.offload_running = running; /* * Disable firmware grip_suppression/palm_rejection when offload is running * and upper layer grip_suppression/palm_rejection is enabled. */ if (running) { if (ts_data->enable_fw_grip < FW_GRIP_FORCE_DISABLE) { int new_fw_grip = ts_data->offload.config.filter_grip ? FW_GRIP_DISABLE : FW_GRIP_ENABLE; if (ts_data->enable_fw_grip != new_fw_grip) { ts_data->enable_fw_grip = new_fw_grip; fts_set_grip_mode(ts_data->enable_fw_grip % 2); } } if (ts_data->enable_fw_palm < FW_PALM_FORCE_DISABLE) { int new_fw_palm = ts_data->offload.config.filter_palm ? FW_PALM_DISABLE : FW_PALM_ENABLE; if (ts_data->enable_fw_palm != new_fw_palm) { ts_data->enable_fw_palm = new_fw_palm; fts_set_palm_mode(ts_data->enable_fw_palm % 2); } } } else { if (ts_data->enable_fw_grip < FW_GRIP_FORCE_DISABLE && ts_data->enable_fw_grip != FTS_DEFAULT_FW_GRIP) { ts_data->enable_fw_grip = FTS_DEFAULT_FW_GRIP; fts_set_grip_mode(ts_data->enable_fw_grip % 2); } if (ts_data->enable_fw_palm < FW_PALM_FORCE_DISABLE && ts_data->enable_fw_palm != FTS_DEFAULT_FW_PALM) { ts_data->enable_fw_palm = FTS_DEFAULT_FW_PALM; fts_set_palm_mode(ts_data->enable_fw_palm % 2); } } } static void fts_offload_report(void *handle, struct TouchOffloadIocReport *report) { struct fts_ts_data *ts_data = (struct fts_ts_data *)handle; bool touch_down = 0; int i; int touch_count = 0; int tool_type; mutex_lock(&ts_data->report_mutex); input_set_timestamp(ts_data->input_dev, report->timestamp); for (i = 0; i < MAX_COORDS; i++) { if (report->coords[i].status != COORD_STATUS_INACTIVE) { input_mt_slot(ts_data->input_dev, i); touch_count++; touch_down = 1; input_report_key(ts_data->input_dev, BTN_TOUCH, touch_down); input_report_key(ts_data->input_dev, BTN_TOOL_FINGER, touch_down); 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: default: tool_type = MT_TOOL_FINGER; break; } input_mt_report_slot_state(ts_data->input_dev, tool_type, 1); input_report_abs(ts_data->input_dev, ABS_MT_POSITION_X, report->coords[i].x); input_report_abs(ts_data->input_dev, ABS_MT_POSITION_Y, report->coords[i].y); input_report_abs(ts_data->input_dev, ABS_MT_TOUCH_MAJOR, report->coords[i].major); input_report_abs(ts_data->input_dev, ABS_MT_TOUCH_MINOR, report->coords[i].minor); input_report_abs(ts_data->input_dev, ABS_MT_PRESSURE, report->coords[i].pressure); } else { input_mt_slot(ts_data->input_dev, i); input_report_abs(ts_data->input_dev, ABS_MT_PRESSURE, 0); input_mt_report_slot_state(ts_data->input_dev, MT_TOOL_FINGER, 0); input_report_abs(ts_data->input_dev, ABS_MT_TRACKING_ID, -1); } } input_report_key(ts_data->input_dev, BTN_TOUCH, touch_down); input_report_key(ts_data->input_dev, BTN_TOOL_FINGER, touch_down); input_sync(ts_data->input_dev); fts_update_motion_filter(ts_data, touch_count); mutex_unlock(&ts_data->report_mutex); } static void fts_populate_coordinate_channel(struct fts_ts_data *ts_data, 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 = ts_data->offload.coords[i].x; dc->coords[i].y = ts_data->offload.coords[i].y; dc->coords[i].major = ts_data->offload.coords[i].major; dc->coords[i].minor = ts_data->offload.coords[i].minor; dc->coords[i].pressure = ts_data->offload.coords[i].pressure; dc->coords[i].status = ts_data->offload.coords[i].status; } } static void fts_populate_mutual_channel(struct fts_ts_data *ts_data, struct touch_offload_frame *frame, int channel) { struct TouchOffloadData2d *mutual_strength = (struct TouchOffloadData2d *)frame->channel_data[channel]; mutual_strength->tx_size = ts_data->pdata->tx_ch_num; mutual_strength->rx_size = ts_data->pdata->rx_ch_num; mutual_strength->header.channel_type = frame->channel_type[channel]; mutual_strength->header.channel_size = TOUCH_OFFLOAD_FRAME_SIZE_2D(mutual_strength->rx_size, mutual_strength->tx_size); memcpy(mutual_strength->data, ts_data->heatmap_buff, mutual_strength->tx_size * mutual_strength->rx_size * sizeof(u16)); } static void fts_populate_self_channel(struct fts_ts_data *ts_data, struct touch_offload_frame *frame, int channel) { int i; int idx = 0; struct TouchOffloadData1d *self_strength = (struct TouchOffloadData1d *)frame->channel_data[channel]; self_strength->tx_size = ts_data->pdata->tx_ch_num; self_strength->rx_size = ts_data->pdata->rx_ch_num; self_strength->header.channel_type = frame->channel_type[channel]; self_strength->header.channel_size = TOUCH_OFFLOAD_FRAME_SIZE_1D(self_strength->rx_size, self_strength->tx_size); idx = ts_data->pdata->tx_ch_num * ts_data->pdata->rx_ch_num; for (i = 0; i < ts_data->pdata->tx_ch_num + ts_data->pdata->rx_ch_num; i++) { ((u16 *) self_strength->data)[i] = ((u16 *) ts_data->heatmap_buff)[idx + i]; } } static void fts_populate_frame(struct fts_ts_data *ts_data) { static u64 index; int i; struct touch_offload_frame *frame = ts_data->reserved_frame; frame->header.index = index++; frame->header.timestamp = ts_data->coords_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(ts_data, frame, i); } else if ((frame->channel_type[i] & TOUCH_SCAN_TYPE_MUTUAL) != 0) { fts_populate_mutual_channel(ts_data, frame, i); } else if ((frame->channel_type[i] & TOUCH_SCAN_TYPE_SELF) != 0) { fts_populate_self_channel(ts_data, frame, i); } } } #endif /* CONFIG_TOUCHSCREEN_OFFLOAD */ #if IS_ENABLED(CONFIG_TOUCHSCREEN_HEATMAP) static bool v4l2_read_frame(struct v4l2_heatmap *v4l2) { bool ret = true; struct fts_ts_data *ts_data = container_of(v4l2, struct fts_ts_data, v4l2); #if IS_ENABLED(GOOGLE_HEATMAP_DEBUG) FTS_FUNC_ENTER(); #endif if (ts_data->v4l2.width == ts_data->pdata->tx_ch_num && ts_data->v4l2.height == ts_data->pdata->rx_ch_num) { #if IS_ENABLED(GOOGLE_HEATMAP_DEBUG) FTS_DEBUG("v4l2 mutual strength data is ready."); #endif memcpy(v4l2->frame, ts_data->heatmap_buff, ts_data->v4l2.width * ts_data->v4l2.height * sizeof(u16)); } else { FTS_ERROR("size mismatched, (%lu, %lu) vs (%u, %u)!\n", ts_data->v4l2.width, ts_data->v4l2.height, ts_data->pdata->tx_ch_num, ts_data->pdata->rx_ch_num); ret = false; } return ret; } #endif /* CONFIG_TOUCHSCREEN_HEATMAP */ static irqreturn_t fts_irq_ts(int irq, void *data) { struct fts_ts_data *ts_data = data; ts_data->isr_timestamp = ktime_get(); return IRQ_WAKE_THREAD; } extern int int_test_has_interrupt; static irqreturn_t fts_irq_handler(int irq, void *data) { #if IS_ENABLED(CONFIG_TOUCHSCREEN_PANEL_BRIDGE) struct fts_ts_data *ts_data = fts_data; if (fts_ts_set_bus_ref(ts_data, FTS_TS_BUS_REF_IRQ, true) < 0) { if (!ts_data->gesture_mode) { /* Interrupt during bus suspend */ FTS_INFO("Skipping stray interrupt since bus is suspended(power_status: %d)\n", ts_data->power_status); return IRQ_HANDLED; } } #endif #if defined(CONFIG_PM) && FTS_PATCH_COMERR_PM int ret = 0; struct fts_ts_data *ts_data = fts_data; if ((ts_data->suspended) && (ts_data->pm_suspend)) { ret = wait_for_completion_timeout( &ts_data->pm_completion, msecs_to_jiffies(FTS_TIMEOUT_COMERR_PM)); if (!ret) { FTS_ERROR("Bus don't resume from pm(deep),timeout,skip irq"); return IRQ_HANDLED; } } #endif int_test_has_interrupt++; fts_data->coords_timestamp = fts_data->isr_timestamp; cpu_latency_qos_update_request(&ts_data->pm_qos_req, 100 /* usec */); fts_irq_read_report(); cpu_latency_qos_update_request(&ts_data->pm_qos_req, PM_QOS_DEFAULT_VALUE); #if IS_ENABLED(CONFIG_TOUCHSCREEN_PANEL_BRIDGE) fts_ts_set_bus_ref(ts_data, FTS_TS_BUS_REF_IRQ, false); #endif return IRQ_HANDLED; } static int fts_irq_registration(struct fts_ts_data *ts_data) { int ret = 0; struct fts_ts_platform_data *pdata = ts_data->pdata; ts_data->irq = gpio_to_irq(pdata->irq_gpio); pdata->irq_gpio_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT; FTS_INFO("irq:%d, flag:%x", ts_data->irq, pdata->irq_gpio_flags); ret = request_threaded_irq(ts_data->irq, fts_irq_ts, fts_irq_handler, pdata->irq_gpio_flags, FTS_DRIVER_NAME, ts_data); return ret; } #if FTS_PEN_EN static int fts_input_pen_init(struct fts_ts_data *ts_data) { int ret = 0; struct input_dev *pen_dev; struct fts_ts_platform_data *pdata = ts_data->pdata; FTS_FUNC_ENTER(); pen_dev = input_allocate_device(); if (!pen_dev) { FTS_ERROR("Failed to allocate memory for input_pen device"); return -ENOMEM; } pen_dev->dev.parent = ts_data->dev; pen_dev->name = FTS_DRIVER_PEN_NAME; pen_dev->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); __set_bit(ABS_X, pen_dev->absbit); __set_bit(ABS_Y, pen_dev->absbit); __set_bit(BTN_STYLUS, pen_dev->keybit); __set_bit(BTN_STYLUS2, pen_dev->keybit); __set_bit(BTN_TOUCH, pen_dev->keybit); __set_bit(BTN_TOOL_PEN, pen_dev->keybit); __set_bit(INPUT_PROP_DIRECT, pen_dev->propbit); input_set_abs_params(pen_dev, ABS_X, pdata->x_min, pdata->x_max, 0, 0); input_set_abs_params(pen_dev, ABS_Y, pdata->y_min, pdata->y_max, 0, 0); input_set_abs_params(pen_dev, ABS_PRESSURE, 0, 4096, 0, 0); ret = input_register_device(pen_dev); if (ret) { FTS_ERROR("Input device registration failed"); input_free_device(pen_dev); pen_dev = NULL; return ret; } ts_data->pen_dev = pen_dev; FTS_FUNC_EXIT(); return 0; } #endif static int fts_input_init(struct fts_ts_data *ts_data) { int ret = 0; int key_num = 0; struct fts_ts_platform_data *pdata = ts_data->pdata; struct input_dev *input_dev; FTS_FUNC_ENTER(); input_dev = input_allocate_device(); if (!input_dev) { FTS_ERROR("Failed to allocate memory for input device"); return -ENOMEM; } /* Init and register Input device */ input_dev->name = FTS_DRIVER_NAME; if (ts_data->bus_type == FTS_BUS_TYPE_I2C) input_dev->id.bustype = BUS_I2C; else input_dev->id.bustype = BUS_SPI; input_dev->dev.parent = ts_data->dev; input_set_drvdata(input_dev, ts_data); __set_bit(EV_SYN, input_dev->evbit); __set_bit(EV_ABS, input_dev->evbit); __set_bit(EV_KEY, input_dev->evbit); __set_bit(BTN_TOUCH, input_dev->keybit); __set_bit(INPUT_PROP_DIRECT, input_dev->propbit); if (pdata->have_key) { FTS_INFO("set key capabilities"); for (key_num = 0; key_num < pdata->key_number; key_num++) input_set_capability(input_dev, EV_KEY, pdata->keys[key_num]); } #if FTS_MT_PROTOCOL_B_EN input_mt_init_slots(input_dev, pdata->max_touch_number, INPUT_MT_DIRECT); #else input_set_abs_params(input_dev, ABS_MT_TRACKING_ID, 0, 0x0F, 0, 0); #endif input_set_abs_params(input_dev, ABS_MT_POSITION_X, pdata->x_min, pdata->x_max, 0, 0); input_set_abs_params(input_dev, ABS_MT_POSITION_Y, pdata->y_min, pdata->y_max, 0, 0); input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, 0x3F, 0, 0); input_set_abs_params(input_dev, ABS_MT_TOUCH_MINOR, 0, 0x3F, 0, 0); #if FTS_REPORT_PRESSURE_EN input_set_abs_params(input_dev, ABS_MT_PRESSURE, 0, 0xFF, 0, 0); #endif ret = input_register_device(input_dev); if (ret) { FTS_ERROR("Input device registration failed"); input_set_drvdata(input_dev, NULL); input_free_device(input_dev); input_dev = NULL; return ret; } #if FTS_PEN_EN ret = fts_input_pen_init(ts_data); if (ret) { FTS_ERROR("Input-pen device registration failed"); input_set_drvdata(input_dev, NULL); input_free_device(input_dev); input_dev = NULL; return ret; } #endif ts_data->input_dev = input_dev; FTS_FUNC_EXIT(); return 0; } static int fts_report_buffer_init(struct fts_ts_data *ts_data) { int point_num = 0; int events_num = 0; point_num = FTS_MAX_POINTS_SUPPORT; ts_data->pnt_buf_size = FTS_TOUCH_DATA_LEN + FTS_GESTURE_DATA_LEN; ts_data->point_buf = (u8 *)kzalloc(ts_data->pnt_buf_size + 1, GFP_KERNEL); if (!ts_data->point_buf) { FTS_ERROR("failed to alloc memory for point buf"); return -ENOMEM; } events_num = point_num * sizeof(struct ts_event); ts_data->events = (struct ts_event *)kzalloc(events_num, GFP_KERNEL); if (!ts_data->events) { FTS_ERROR("failed to alloc memory for point events"); kfree_safe(ts_data->point_buf); return -ENOMEM; } return 0; } #if FTS_POWER_SOURCE_CUST_EN /***************************************************************************** * Power Control *****************************************************************************/ #if FTS_PINCTRL_EN static int fts_pinctrl_init(struct fts_ts_data *ts) { int ret = 0; ts->pinctrl = devm_pinctrl_get(ts->dev); if (IS_ERR_OR_NULL(ts->pinctrl)) { FTS_ERROR("Failed to get pinctrl, please check dts"); ret = PTR_ERR(ts->pinctrl); goto err_pinctrl_get; } ts->pins_active = pinctrl_lookup_state(ts->pinctrl, "ts_active"); if (IS_ERR_OR_NULL(ts->pins_active)) { FTS_ERROR("Pin state[active] not found"); ret = PTR_ERR(ts->pins_active); goto err_pinctrl_lookup; } ts->pins_suspend = pinctrl_lookup_state(ts->pinctrl, "ts_suspend"); if (IS_ERR_OR_NULL(ts->pins_suspend)) { FTS_ERROR("Pin state[suspend] not found"); ret = PTR_ERR(ts->pins_suspend); goto err_pinctrl_lookup; } return 0; err_pinctrl_lookup: if (ts->pinctrl) { devm_pinctrl_put(ts->pinctrl); } err_pinctrl_get: ts->pinctrl = NULL; ts->pins_suspend = NULL; ts->pins_active = NULL; return ret; } static int fts_pinctrl_select_normal(struct fts_ts_data *ts) { int ret = 0; FTS_DEBUG("Pins control select normal"); if (ts->pinctrl && ts->pins_active) { ret = pinctrl_select_state(ts->pinctrl, ts->pins_active); if (ret < 0) { FTS_ERROR("Set normal pin state error:%d", ret); } } return ret; } static int fts_pinctrl_select_suspend(struct fts_ts_data *ts) { int ret = 0; FTS_DEBUG("Pins control select suspend"); if (ts->pinctrl && ts->pins_suspend) { ret = pinctrl_select_state(ts->pinctrl, ts->pins_suspend); if (ret < 0) { FTS_ERROR("Set suspend pin state error:%d", ret); } } return ret; } #endif /* FTS_PINCTRL_EN */ static int fts_power_source_ctrl(struct fts_ts_data *ts_data, int enable) { int ret = 0; if (IS_ERR_OR_NULL(ts_data->avdd)) { FTS_ERROR("avdd is invalid"); return -EINVAL; } FTS_FUNC_ENTER(); if (enable) { if (ts_data->power_disabled) { FTS_DEBUG("regulator enable !"); ret = regulator_enable(ts_data->avdd); if (ret) { FTS_ERROR("enable avdd regulator failed,ret=%d", ret); } if (!IS_ERR_OR_NULL(ts_data->dvdd)) { ret = regulator_enable(ts_data->dvdd); if (ret) { FTS_ERROR("enable dvdd regulator failed,ret=%d", ret); } } /* sleep 1 ms to power on avdd/dvdd to match spec. */ msleep(1); gpio_direction_output(ts_data->pdata->reset_gpio, 1); ts_data->power_disabled = false; } } else { if (!ts_data->power_disabled) { FTS_DEBUG("regulator disable !"); gpio_direction_output(ts_data->pdata->reset_gpio, 0); /* sleep 1 ms to power off avdd/dvdd to match spec. */ msleep(1); ret = regulator_disable(ts_data->avdd); if (ret) { FTS_ERROR("disable avdd regulator failed,ret=%d", ret); } if (!IS_ERR_OR_NULL(ts_data->dvdd)) { ret = regulator_disable(ts_data->dvdd); if (ret) { FTS_ERROR("disable dvdd regulator failed,ret=%d", ret); } } ts_data->power_disabled = true; } } FTS_FUNC_EXIT(); return ret; } /***************************************************************************** * Name: fts_power_source_init * Brief: Init regulator power:avdd/dvdd(if have), generally, no dvdd * avdd---->avdd-supply in dts, kernel will auto add "-supply" to parse * Must be call after fts_gpio_configure() execute,because this function * will operate reset-gpio which request gpio in fts_gpio_configure() * Input: * Output: * Return: return 0 if init power successfully, otherwise return error code *****************************************************************************/ static int fts_power_source_init(struct fts_ts_data *ts_data) { int ret = 0; FTS_FUNC_ENTER(); #if FTS_PINCTRL_EN fts_pinctrl_init(ts_data); fts_pinctrl_select_normal(ts_data); #endif if (of_property_read_bool(ts_data->dev->of_node, "avdd-supply")) { ts_data->avdd = regulator_get(ts_data->dev, "avdd"); if (IS_ERR_OR_NULL(ts_data->avdd)) { ret = PTR_ERR(ts_data->avdd); ts_data->avdd = NULL; FTS_ERROR("get avdd regulator failed,ret=%d", ret); return ret; } } else { FTS_ERROR("avdd-supply not found!"); } if (of_property_read_bool(ts_data->dev->of_node, "vdd-supply")) { ts_data->dvdd = regulator_get(ts_data->dev, "vdd"); if (IS_ERR_OR_NULL(ts_data->dvdd)) { ret = PTR_ERR(ts_data->dvdd); ts_data->dvdd = NULL; FTS_ERROR("get dvdd regulator failed,ret=%d", ret); } } else { FTS_ERROR("vdd-supply not found!"); } ts_data->power_disabled = true; ret = fts_power_source_ctrl(ts_data, ENABLE); if (ret) { FTS_ERROR("fail to enable power(regulator)"); } FTS_FUNC_EXIT(); return ret; } static int fts_power_source_exit(struct fts_ts_data *ts_data) { fts_power_source_ctrl(ts_data, DISABLE); #if FTS_PINCTRL_EN fts_pinctrl_select_suspend(ts_data); #endif if (!IS_ERR_OR_NULL(ts_data->avdd)) { regulator_put(ts_data->avdd); ts_data->avdd = NULL; } if (!IS_ERR_OR_NULL(ts_data->dvdd)) { regulator_put(ts_data->dvdd); ts_data->dvdd = NULL; } return 0; } static int fts_power_source_suspend(struct fts_ts_data *ts_data) { int ret = 0; #if !defined(FTS_AOC_GESTURE_EN) ret = fts_power_source_ctrl(ts_data, DISABLE); if (ret < 0) { FTS_ERROR("power off fail, ret=%d", ret); } #endif #if FTS_PINCTRL_EN fts_pinctrl_select_suspend(ts_data); #endif return ret; } static int fts_power_source_resume(struct fts_ts_data *ts_data) { int ret = 0; #if FTS_PINCTRL_EN fts_pinctrl_select_normal(ts_data); #endif #if !defined(FTS_AOC_GESTURE_EN) ret = fts_power_source_ctrl(ts_data, ENABLE); if (ret < 0) { FTS_ERROR("power on fail, ret=%d", ret); } #endif return ret; } #endif /* FTS_POWER_SOURCE_CUST_EN */ static int fts_gpio_configure(struct fts_ts_data *data) { int ret = 0; FTS_FUNC_ENTER(); /* request irq gpio */ if (gpio_is_valid(data->pdata->irq_gpio)) { ret = gpio_request(data->pdata->irq_gpio, "fts_irq_gpio"); if (ret) { FTS_ERROR("[GPIO]irq gpio request failed"); goto err_irq_gpio_req; } ret = gpio_direction_input(data->pdata->irq_gpio); if (ret) { FTS_ERROR("[GPIO]set_direction for irq gpio failed"); goto err_irq_gpio_dir; } } /* request reset gpio */ if (gpio_is_valid(data->pdata->reset_gpio)) { ret = gpio_request(data->pdata->reset_gpio, "fts_reset_gpio"); if (ret) { FTS_ERROR("[GPIO]reset gpio request failed"); goto err_irq_gpio_dir; } ret = gpio_direction_output(data->pdata->reset_gpio, 0); if (ret) { FTS_ERROR("[GPIO]set_direction for reset gpio failed"); goto err_reset_gpio_dir; } } FTS_FUNC_EXIT(); return 0; err_reset_gpio_dir: if (gpio_is_valid(data->pdata->reset_gpio)) gpio_free(data->pdata->reset_gpio); err_irq_gpio_dir: if (gpio_is_valid(data->pdata->irq_gpio)) gpio_free(data->pdata->irq_gpio); err_irq_gpio_req: FTS_FUNC_EXIT(); return ret; } static int fts_get_dt_coords(struct device *dev, char *name, struct fts_ts_platform_data *pdata) { int ret = 0; u32 coords[FTS_COORDS_ARR_SIZE] = { 0 }; struct property *prop; struct device_node *np = dev->of_node; int coords_size; prop = of_find_property(np, name, NULL); if (!prop) return -EINVAL; if (!prop->value) return -ENODATA; coords_size = prop->length / sizeof(u32); if (coords_size != FTS_COORDS_ARR_SIZE) { FTS_ERROR("invalid:%s, size:%d", name, coords_size); return -EINVAL; } ret = of_property_read_u32_array(np, name, coords, coords_size); if (ret < 0) { FTS_ERROR("Unable to read %s, please check dts", name); pdata->x_min = FTS_X_MIN_DISPLAY_DEFAULT; pdata->y_min = FTS_Y_MIN_DISPLAY_DEFAULT; pdata->x_max = FTS_X_MAX_DISPLAY_DEFAULT; pdata->y_max = FTS_Y_MAX_DISPLAY_DEFAULT; return -ENODATA; } else { pdata->x_min = coords[0]; pdata->y_min = coords[1]; pdata->x_max = coords[2]; pdata->y_max = coords[3]; } FTS_INFO("display x(%d %d) y(%d %d)", pdata->x_min, pdata->x_max, pdata->y_min, pdata->y_max); return 0; } static int fts_check_panel_map(struct device_node *np, struct fts_ts_platform_data *pdata) { int ret = 0; int index; struct of_phandle_args panelmap; struct drm_panel *panel = NULL; if (of_property_read_bool(np, "focaltech,panel_map")) { for (index = 0 ;; index++) { ret = of_parse_phandle_with_fixed_args(np, "focaltech,panel_map", 1, index, &panelmap); if (ret) { FTS_ERROR("Can't find display panel!\n"); return -EPROBE_DEFER; } panel = of_drm_find_panel(panelmap.np); of_node_put(panelmap.np); if (!IS_ERR_OR_NULL(panel)) { pdata->panel = panel; pdata->initial_panel_index = panelmap.args[0]; break; } } } return 0; } static int fts_parse_dt(struct device *dev, struct fts_ts_platform_data *pdata) { int ret = 0; struct device_node *np = dev->of_node; u32 temp_val = 0; #if IS_ENABLED(CONFIG_TOUCHSCREEN_OFFLOAD) u8 offload_id[4]; #endif FTS_FUNC_ENTER(); /* Check if panel(s) exist or not. */ ret = fts_check_panel_map(np, pdata); if (ret) return ret; ret = fts_get_dt_coords(dev, "focaltech,display-coords", pdata); if (ret < 0) FTS_ERROR("Unable to get display-coords"); /* key */ pdata->have_key = of_property_read_bool(np, "focaltech,have-key"); if (pdata->have_key) { ret = of_property_read_u32(np, "focaltech,key-number", &pdata->key_number); if (ret < 0) FTS_ERROR("Key number undefined!"); ret = of_property_read_u32_array(np, "focaltech,keys", pdata->keys, pdata->key_number); if (ret < 0) FTS_ERROR("Keys undefined!"); else if (pdata->key_number > FTS_MAX_KEYS) pdata->key_number = FTS_MAX_KEYS; ret = of_property_read_u32_array(np, "focaltech,key-x-coords", pdata->key_x_coords, pdata->key_number); if (ret < 0) FTS_ERROR("Key Y Coords undefined!"); ret = of_property_read_u32_array(np, "focaltech,key-y-coords", pdata->key_y_coords, pdata->key_number); if (ret < 0) FTS_ERROR("Key X Coords undefined!"); FTS_INFO("VK Number:%d, key:(%d,%d,%d), " "coords:(%d,%d),(%d,%d),(%d,%d)", pdata->key_number, pdata->keys[0], pdata->keys[1], pdata->keys[2], pdata->key_x_coords[0], pdata->key_y_coords[0], pdata->key_x_coords[1], pdata->key_y_coords[1], pdata->key_x_coords[2], pdata->key_y_coords[2]); } /* reset, irq gpio info */ pdata->reset_gpio = of_get_named_gpio_flags(np, "focaltech,reset-gpio", 0, &pdata->reset_gpio_flags); if (pdata->reset_gpio < 0) FTS_ERROR("Unable to get reset_gpio"); #if IS_ENABLED(CONFIG_TOUCHSCREEN_OFFLOAD) pdata->offload_id = 0; ret = of_property_read_u8_array(np, "focaltech,touch_offload_id", offload_id, 4); if (ret == -EINVAL) { FTS_ERROR("Failed to read focaltech,touch_offload_id with error = %d\n", ret); } else { pdata->offload_id = *(u32 *)offload_id; FTS_DEBUG("Offload device ID = \"%c%c%c%c\" / 0x%08X\n", offload_id[0], offload_id[1], offload_id[2], offload_id[3], pdata->offload_id); } #endif ret = of_property_read_u32(np, "focaltech,tx_ch_num", &temp_val); if (ret < 0) { FTS_ERROR("Unable to get tx_ch_num, please check dts"); } else { pdata->tx_ch_num = temp_val; FTS_DEBUG("tx_ch_num = %d", pdata->tx_ch_num); } ret = of_property_read_u32(np, "focaltech,rx_ch_num", &temp_val); if (ret < 0) { FTS_ERROR("Unable to get rx_ch_num, please check dts"); } else { pdata->rx_ch_num = temp_val; FTS_DEBUG("rx_ch_num = %d", pdata->rx_ch_num); } ret = of_property_read_u8(np, "focaltech,mm2px", &pdata->mm2px); if (ret < 0) { FTS_ERROR("Unable to get mm2px, please check dts"); pdata->mm2px = 1; } else { FTS_DEBUG("mm2px = %d", pdata->mm2px); } pdata->irq_gpio = of_get_named_gpio_flags(np, "focaltech,irq-gpio", 0, &pdata->irq_gpio_flags); if (pdata->irq_gpio < 0) FTS_ERROR("Unable to get irq_gpio"); ret = of_property_read_u32(np, "focaltech,max-touch-number", &temp_val); if (ret < 0) { FTS_ERROR("Unable to get max-touch-number, please check dts"); pdata->max_touch_number = FTS_MAX_POINTS_SUPPORT; } else { if (temp_val < 2) pdata->max_touch_number = 2; /* max_touch_number must >= 2 */ else if (temp_val > FTS_MAX_POINTS_SUPPORT) pdata->max_touch_number = FTS_MAX_POINTS_SUPPORT; else pdata->max_touch_number = temp_val; } FTS_INFO("max touch number:%d, irq gpio:%d, reset gpio:%d", pdata->max_touch_number, pdata->irq_gpio, pdata->reset_gpio); FTS_FUNC_EXIT(); return 0; } static void fts_suspend_work(struct work_struct *work) { struct fts_ts_data *ts_data = container_of(work, struct fts_ts_data, suspend_work); FTS_DEBUG("Entry"); mutex_lock(&ts_data->device_mutex); reinit_completion(&ts_data->bus_resumed); #if IS_ENABLED(CONFIG_TOUCHSCREEN_PANEL_BRIDGE) if (ts_data->power_status == FTS_TS_STATE_SUSPEND) { FTS_ERROR("Already suspended.\n"); mutex_unlock(&ts_data->device_mutex); return; } #endif fts_ts_suspend(ts_data->dev); #if IS_ENABLED(CONFIG_TOUCHSCREEN_PANEL_BRIDGE) ts_data->power_status = FTS_TS_STATE_SUSPEND; #endif #if IS_ENABLED(CONFIG_TOUCHSCREEN_TBN) if (ts_data->tbn_register_mask) tbn_release_bus(ts_data->tbn_register_mask); #endif mutex_unlock(&ts_data->device_mutex); } static void fts_resume_work(struct work_struct *work) { struct fts_ts_data *ts_data = container_of(work, struct fts_ts_data, resume_work); FTS_DEBUG("Entry"); mutex_lock(&ts_data->device_mutex); #if IS_ENABLED(CONFIG_TOUCHSCREEN_TBN) if (ts_data->tbn_register_mask) tbn_request_bus(ts_data->tbn_register_mask); #endif #if IS_ENABLED(CONFIG_TOUCHSCREEN_PANEL_BRIDGE) if (ts_data->power_status == FTS_TS_STATE_POWER_ON) { FTS_ERROR("Already resumed.\n"); mutex_unlock(&ts_data->device_mutex); return; } #endif fts_ts_resume(ts_data->dev); #if IS_ENABLED(CONFIG_TOUCHSCREEN_PANEL_BRIDGE) ts_data->power_status = FTS_TS_STATE_POWER_ON; #endif complete_all(&ts_data->bus_resumed); mutex_unlock(&ts_data->device_mutex); } #if defined(CONFIG_FB) static int fb_notifier_callback(struct notifier_block *self, unsigned long event, void *data) { struct fb_event *evdata = data; int *blank = NULL; struct fts_ts_data *ts_data = container_of(self, struct fts_ts_data, fb_notif); if (!evdata) { FTS_ERROR("evdata is null"); return 0; } if (!(event == FB_EARLY_EVENT_BLANK || event == FB_EVENT_BLANK)) { FTS_INFO("event(%lu) do not need process\n", event); return 0; } blank = evdata->data; FTS_INFO("FB event:%lu,blank:%d", event, *blank); switch (*blank) { case FB_BLANK_UNBLANK: if (FB_EARLY_EVENT_BLANK == event) { FTS_INFO("resume: event = %lu, not care\n", event); } else if (FB_EVENT_BLANK == event) { queue_work(fts_data->ts_workqueue, &fts_data->resume_work); } break; case FB_BLANK_POWERDOWN: if (FB_EARLY_EVENT_BLANK == event) { cancel_work_sync(&fts_data->resume_work); fts_ts_suspend(ts_data->dev); } else if (FB_EVENT_BLANK == event) { FTS_INFO("suspend: event = %lu, not care\n", event); } break; default: FTS_INFO("FB BLANK(%d) do not need process\n", *blank); break; } return 0; } #elif defined(CONFIG_DRM) #if defined(CONFIG_DRM_PANEL) static struct drm_panel *active_panel; static int drm_check_dt(struct device_node *np) { int i = 0; int count = 0; struct device_node *node = NULL; struct drm_panel *panel = NULL; count = of_count_phandle_with_args(np, "panel", NULL); if (count <= 0) { FTS_ERROR("find drm_panel count(%d) fail", count); return -ENODEV; } for (i = 0; i < count; i++) { node = of_parse_phandle(np, "panel", i); panel = of_drm_find_panel(node); of_node_put(node); if (!IS_ERR(panel)) { FTS_INFO("find drm_panel successfully"); active_panel = panel; return 0; } } FTS_ERROR("no find drm_panel"); return -ENODEV; } static int drm_notifier_callback(struct notifier_block *self, unsigned long event, void *data) { struct msm_drm_notifier *evdata = data; int *blank = NULL; struct fts_ts_data *ts_data = container_of(self, struct fts_ts_data, fb_notif); if (!evdata) { FTS_ERROR("evdata is null"); return 0; } if (!((event == DRM_PANEL_EARLY_EVENT_BLANK ) || (event == DRM_PANEL_EVENT_BLANK))) { FTS_INFO("event(%lu) do not need process\n", event); return 0; } blank = evdata->data; FTS_INFO("DRM event:%lu,blank:%d", event, *blank); switch (*blank) { case DRM_PANEL_BLANK_UNBLANK: if (DRM_PANEL_EARLY_EVENT_BLANK == event) { FTS_INFO("resume: event = %lu, not care\n", event); } else if (DRM_PANEL_EVENT_BLANK == event) { queue_work(fts_data->ts_workqueue, &fts_data->resume_work); } break; case DRM_PANEL_BLANK_POWERDOWN: if (DRM_PANEL_EARLY_EVENT_BLANK == event) { cancel_work_sync(&fts_data->resume_work); fts_ts_suspend(ts_data->dev); } else if (DRM_PANEL_EVENT_BLANK == event) { FTS_INFO("suspend: event = %lu, not care\n", event); } break; default: FTS_INFO("DRM BLANK(%d) do not need process\n", *blank); break; } return 0; } #elif defined(CONFIG_ARCH_MSM) static int drm_notifier_callback(struct notifier_block *self, unsigned long event, void *data) { struct msm_drm_notifier *evdata = data; int *blank = NULL; struct fts_ts_data *ts_data = container_of(self, struct fts_ts_data, fb_notif); if (!evdata) { FTS_ERROR("evdata is null"); return 0; } if (!((event == MSM_DRM_EARLY_EVENT_BLANK ) || (event == MSM_DRM_EVENT_BLANK))) { FTS_INFO("event(%lu) do not need process\n", event); return 0; } blank = evdata->data; FTS_INFO("DRM event:%lu,blank:%d", event, *blank); switch (*blank) { case MSM_DRM_BLANK_UNBLANK: if (MSM_DRM_EARLY_EVENT_BLANK == event) { FTS_INFO("resume: event = %lu, not care\n", event); } else if (MSM_DRM_EVENT_BLANK == event) { queue_work(fts_data->ts_workqueue, &fts_data->resume_work); } break; case MSM_DRM_BLANK_POWERDOWN: if (MSM_DRM_EARLY_EVENT_BLANK == event) { cancel_work_sync(&fts_data->resume_work); fts_ts_suspend(ts_data->dev); } else if (MSM_DRM_EVENT_BLANK == event) { FTS_INFO("suspend: event = %lu, not care\n", event); } break; default: FTS_INFO("DRM BLANK(%d) do not need process\n", *blank); break; } return 0; } #endif #elif defined(CONFIG_HAS_EARLYSUSPEND) static void fts_ts_early_suspend(struct early_suspend *handler) { struct fts_ts_data *ts_data = container_of(handler, struct fts_ts_data, early_suspend); cancel_work_sync(&fts_data->resume_work); fts_ts_suspend(ts_data->dev); } static void fts_ts_late_resume(struct early_suspend *handler) { struct fts_ts_data *ts_data = container_of(handler, struct fts_ts_data, early_suspend); queue_work(fts_data->ts_workqueue, &fts_data->resume_work); } #endif static int fts_ts_probe_entry(struct fts_ts_data *ts_data) { int ret = 0; int pdata_size = sizeof(struct fts_ts_platform_data); FTS_FUNC_ENTER(); FTS_INFO("%s", FTS_DRIVER_VERSION); ts_data->pdata = kzalloc(pdata_size, GFP_KERNEL); if (!ts_data->pdata) { FTS_ERROR("allocate memory for platform_data fail"); return -ENOMEM; } if (ts_data->dev->of_node) { ret = fts_parse_dt(ts_data->dev, ts_data->pdata); if (ret) FTS_ERROR("device-tree parse fail"); #if defined(CONFIG_DRM) #if defined(CONFIG_DRM_PANEL) ret = drm_check_dt(ts_data->dev->of_node); if (ret) { FTS_ERROR("parse drm-panel fail"); } #endif #endif } else { if (ts_data->dev->platform_data) { memcpy(ts_data->pdata, ts_data->dev->platform_data, pdata_size); } else { FTS_ERROR("platform_data is null"); return -ENODEV; } } ts_data->ts_workqueue = create_singlethread_workqueue("fts_wq"); if (!ts_data->ts_workqueue) { FTS_ERROR("create fts workqueue fail"); } spin_lock_init(&ts_data->irq_lock); mutex_init(&ts_data->report_mutex); mutex_init(&ts_data->bus_lock); #if IS_ENABLED(CONFIG_TOUCHSCREEN_PANEL_BRIDGE) ts_data->power_status = FTS_TS_STATE_POWER_ON; ts_data->bus_refmask = FTS_TS_BUS_REF_SCREEN_ON; mutex_init(&ts_data->bus_mutex); #endif mutex_init(&ts_data->device_mutex); init_completion(&ts_data->bus_resumed); complete_all(&ts_data->bus_resumed); #if IS_ENABLED(CONFIG_TOUCHSCREEN_TBN) if (register_tbn(&ts_data->tbn_register_mask)) { ret = -ENODEV; FTS_ERROR("Failed to register tbn context.\n"); goto err_init_tbn; } FTS_INFO("tbn_register_mask = %#x.\n", ts_data->tbn_register_mask); #endif /* Init communication interface */ ret = fts_bus_init(ts_data); if (ret) { FTS_ERROR("bus initialize fail"); goto err_bus_init; } ret = fts_input_init(ts_data); if (ret) { FTS_ERROR("input initialize fail"); goto err_input_init; } ret = fts_report_buffer_init(ts_data); if (ret) { FTS_ERROR("report buffer init fail"); goto err_report_buffer; } ret = fts_gpio_configure(ts_data); if (ret) { FTS_ERROR("configure the gpios fail"); goto err_gpio_config; } #if FTS_POWER_SOURCE_CUST_EN ret = fts_power_source_init(ts_data); if (ret) { FTS_ERROR("fail to get power(regulator)"); goto err_power_init; } #endif #if (!FTS_CHIP_IDC) //fts_reset_proc(200); #endif ret = fts_get_ic_information(ts_data); if (ret) { FTS_ERROR("not focal IC, unregister driver"); goto err_power_init; } ret = fts_create_apk_debug_channel(ts_data); if (ret) { FTS_ERROR("create apk debug node fail"); } #if IS_ENABLED(CONFIG_TOUCHSCREEN_HEATMAP) ts_data->enable_fw_heatmap = false; #endif ts_data->enable_fw_grip = FTS_DEFAULT_FW_GRIP; ts_data->enable_fw_palm = FTS_DEFAULT_FW_PALM; ts_data->set_continuously_report = ENABLE; ts_data->glove_mode = DISABLE; fts_update_feature_setting(ts_data); #if IS_ENABLED(CONFIG_TOUCHSCREEN_OFFLOAD) ts_data->offload.caps.touch_offload_major_version = TOUCH_OFFLOAD_INTERFACE_MAJOR_VERSION; ts_data->offload.caps.touch_offload_minor_version = TOUCH_OFFLOAD_INTERFACE_MINOR_VERSION; ts_data->offload.caps.device_id = ts_data->pdata->offload_id; ts_data->offload.caps.display_width = ts_data->pdata->x_max; ts_data->offload.caps.display_height = ts_data->pdata->y_max; ts_data->offload.caps.tx_size = ts_data->pdata->tx_ch_num; ts_data->offload.caps.rx_size = ts_data->pdata->rx_ch_num; ts_data->offload.caps.bus_type = BUS_TYPE_SPI; ts_data->offload.caps.bus_speed_hz = 10000000; ts_data->offload.caps.heatmap_size = HEATMAP_SIZE_FULL; ts_data->offload.caps.touch_data_types = TOUCH_DATA_TYPE_COORD | TOUCH_DATA_TYPE_STRENGTH | TOUCH_DATA_TYPE_RAW; ts_data->offload.caps.touch_scan_types = TOUCH_SCAN_TYPE_MUTUAL | TOUCH_SCAN_TYPE_SELF; ts_data->offload.caps.continuous_reporting = true; ts_data->offload.caps.noise_reporting = false; ts_data->offload.caps.cancel_reporting = false; ts_data->offload.caps.size_reporting = true; ts_data->offload.caps.filter_grip = true; ts_data->offload.caps.filter_palm = true; ts_data->offload.caps.num_sensitivity_settings = 1; ts_data->offload.hcallback = (void *)ts_data; ts_data->offload.report_cb = fts_offload_report; touch_offload_init(&ts_data->offload); #endif #if IS_ENABLED(CONFIG_TOUCHSCREEN_OFFLOAD) || \ IS_ENABLED(CONFIG_TOUCHSCREEN_HEATMAP) if (!ts_data->heatmap_buff) { int heatmap_buff_size = sizeof(u16) * ((ts_data->pdata->tx_ch_num * ts_data->pdata->rx_ch_num) + ts_data->pdata->tx_ch_num + ts_data->pdata->rx_ch_num); FTS_DEBUG("Allocate heatmap_buff size=%d\n", heatmap_buff_size); ts_data->heatmap_buff = kmalloc(heatmap_buff_size, GFP_KERNEL); if (!ts_data->heatmap_buff) { FTS_ERROR("allocate heatmap_buff failed\n"); goto err_heatmap_buff; } } if (!ts_data->heatmap_raw) { int node_num = ts_data->pdata->tx_ch_num * ts_data->pdata->rx_ch_num; int heatmap_raw_size = FTS_CAP_DATA_OFFSET + ((node_num + FTS_SELF_DATA_LEN) * sizeof(u16)); FTS_DEBUG("Allocate heatmap_raw size=%d\n", heatmap_raw_size); ts_data->heatmap_raw = kmalloc(heatmap_raw_size, GFP_KERNEL); if (!ts_data->heatmap_raw) { FTS_ERROR("allocate heatmap_raw failed\n"); goto err_heatmap_raw; } } if (!ts_data->trans_raw) { int node_num = ts_data->pdata->tx_ch_num * ts_data->pdata->rx_ch_num; int trans_raw_size = sizeof(u16) * node_num; FTS_DEBUG("Allocate trans_raw size=%d\n", trans_raw_size); ts_data->trans_raw = kmalloc(trans_raw_size, GFP_KERNEL); if (!ts_data->trans_raw) { FTS_ERROR("allocate trans_raw failed\n"); goto err_trans_raw; } } #endif #if IS_ENABLED(CONFIG_TOUCHSCREEN_HEATMAP) /* * Heatmap_probe must be called before irq routine is registered, * because heatmap_read is called from interrupt context. */ ts_data->v4l2.parent_dev = ts_data->dev; ts_data->v4l2.input_dev = ts_data->input_dev; ts_data->v4l2.read_frame = v4l2_read_frame; ts_data->v4l2.width = ts_data->pdata->tx_ch_num; ts_data->v4l2.height = ts_data->pdata->rx_ch_num; /* 180 Hz operation */ ts_data->v4l2.timeperframe.numerator = 1; ts_data->v4l2.timeperframe.denominator = 180; ret = heatmap_probe(&ts_data->v4l2); if (ret < 0) { FTS_ERROR("heatmap probe unsuccessfully!"); goto err_heatmap_probe; } else { FTS_DEBUG("heatmap probe successfully!"); } #endif /* init motion filter mode and state. */ ts_data->mf_mode = MF_DYNAMIC; ts_data->mf_state = MF_UNFILTERED; ret = fts_create_sysfs(ts_data); if (ret) { FTS_ERROR("create sysfs node fail"); } #if FTS_POINT_REPORT_CHECK_EN ret = fts_point_report_check_init(ts_data); if (ret) { FTS_ERROR("init point report check fail"); } #endif ret = fts_ex_mode_init(ts_data); if (ret) { FTS_ERROR("init glove/cover/charger fail"); } ret = fts_gesture_init(ts_data); if (ret) { FTS_ERROR("init gesture fail"); } #if FTS_TEST_EN ret = fts_test_init(ts_data); if (ret) { FTS_ERROR("init production test fail"); } #endif #if FTS_ESDCHECK_EN ret = fts_esdcheck_init(ts_data); if (ret) { FTS_ERROR("init esd check fail"); } #endif /* init pm_qos before interrupt registered. */ cpu_latency_qos_add_request(&ts_data->pm_qos_req, PM_QOS_DEFAULT_VALUE); ret = fts_irq_registration(ts_data); if (ret) { FTS_ERROR("request irq failed"); goto err_irq_req; } #if IS_ENABLED(CONFIG_TOUCHSCREEN_PANEL_BRIDGE) ret = register_panel_bridge(ts_data); if (ret < 0) { FTS_ERROR("register_panel_bridge failed. ret = 0x%08X\n", ret); } #endif if (ts_data->ts_workqueue) { INIT_WORK(&ts_data->resume_work, fts_resume_work); INIT_WORK(&ts_data->suspend_work, fts_suspend_work); } ret = fts_fwupg_init(ts_data); if (ret) { FTS_ERROR("init fw upgrade fail"); } #if defined(CONFIG_PM) && FTS_PATCH_COMERR_PM init_completion(&ts_data->pm_completion); ts_data->pm_suspend = false; #endif #if defined(CONFIG_FB) ts_data->fb_notif.notifier_call = fb_notifier_callback; ret = fb_register_client(&ts_data->fb_notif); if (ret) { FTS_ERROR("[FB]Unable to register fb_notifier: %d", ret); } #elif defined(CONFIG_DRM_PANEL) || defined(CONFIG_ARCH_MSM) ts_data->fb_notif.notifier_call = drm_notifier_callback; #if defined(CONFIG_DRM_PANEL) if (active_panel) { ret = drm_panel_notifier_register(active_panel, &ts_data->fb_notif); if (ret) FTS_ERROR("[DRM]drm_panel_notifier_register fail: %d\n", ret); } #elif defined(CONFIG_ARCH_MSM) ret = msm_drm_register_client(&ts_data->fb_notif); if (ret) { FTS_ERROR("[DRM]Unable to register fb_notifier: %d\n", ret); } #endif #elif defined(CONFIG_HAS_EARLYSUSPEND) ts_data->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + FTS_SUSPEND_LEVEL; ts_data->early_suspend.suspend = fts_ts_early_suspend; ts_data->early_suspend.resume = fts_ts_late_resume; register_early_suspend(&ts_data->early_suspend); #endif ts_data->work_mode = FTS_REG_WORKMODE_WORK_VALUE; #if GOOGLE_REPORT_MODE fts_read_reg(FTS_REG_CUSTOMER_STATUS, ¤t_host_status); if ((current_host_status & 0x03) < 3) { FTS_DEBUG("-------Hopping %dKhz\n", hopping_freq[(current_host_status & 0x03)]); } FTS_DEBUG("-------PALM mode = %s\n", (current_host_status & (1 << STATUS_PALM)) ? "Enable" : "Disable"); FTS_DEBUG("-------WATER mode = %s\n", (current_host_status & (1 << STATUS_WATER)) ? "Enable" : "Disable"); FTS_DEBUG("-------GRIP mode = %s\n", (current_host_status & (1 << STATUS_GRIP)) ? "Enable" : "Disable"); FTS_DEBUG("-------GlOVE mode = %s\n", (current_host_status & (1 << STATUS_GLOVE)) ? "Enable" : "Disable"); FTS_DEBUG("-------Edge palm %s\n", (current_host_status & (1 << STATUS_EDGE_PALM)) ? "Enable" : "Disable"); FTS_DEBUG("-------LPTW = %s\n", (current_host_status & (1 << STATUS_LPTW)) ? "Enable" : "Disable"); #endif FTS_FUNC_EXIT(); return 0; err_irq_req: cpu_latency_qos_remove_request(&ts_data->pm_qos_req); #if IS_ENABLED(CONFIG_TOUCHSCREEN_HEATMAP) heatmap_remove(&ts_data->v4l2); err_heatmap_probe: #endif #if IS_ENABLED(CONFIG_TOUCHSCREEN_OFFLOAD) || \ IS_ENABLED(CONFIG_TOUCHSCREEN_HEATMAP) if (ts_data->trans_raw) { kfree_safe(ts_data->trans_raw); ts_data->trans_raw = NULL; } err_trans_raw: if (ts_data->heatmap_raw) { kfree_safe(ts_data->heatmap_raw); ts_data->heatmap_raw = NULL; } err_heatmap_raw: if (ts_data->heatmap_buff) { kfree_safe(ts_data->heatmap_buff); ts_data->heatmap_buff = NULL; } err_heatmap_buff: #endif #if IS_ENABLED(CONFIG_TOUCHSCREEN_OFFLOAD) touch_offload_cleanup(&ts_data->offload); #endif #if FTS_POWER_SOURCE_CUST_EN err_power_init: fts_power_source_exit(ts_data); #endif if (gpio_is_valid(ts_data->pdata->reset_gpio)) gpio_free(ts_data->pdata->reset_gpio); if (gpio_is_valid(ts_data->pdata->irq_gpio)) gpio_free(ts_data->pdata->irq_gpio); err_gpio_config: kfree_safe(ts_data->point_buf); kfree_safe(ts_data->events); err_report_buffer: input_unregister_device(ts_data->input_dev); #if FTS_PEN_EN input_unregister_device(ts_data->pen_dev); #endif err_input_init: if (ts_data->ts_workqueue) destroy_workqueue(ts_data->ts_workqueue); err_bus_init: #if IS_ENABLED(CONFIG_TOUCHSCREEN_TBN) if (ts_data->tbn_register_mask) unregister_tbn(&ts_data->tbn_register_mask); err_init_tbn: #endif kfree_safe(ts_data->bus_tx_buf); kfree_safe(ts_data->bus_rx_buf); kfree_safe(ts_data->pdata); FTS_FUNC_EXIT(); return ret; } static int fts_ts_remove_entry(struct fts_ts_data *ts_data) { FTS_FUNC_ENTER(); #if FTS_POINT_REPORT_CHECK_EN fts_point_report_check_exit(ts_data); #endif fts_release_apk_debug_channel(ts_data); fts_remove_sysfs(ts_data); #if IS_ENABLED(CONFIG_TOUCHSCREEN_OFFLOAD) touch_offload_cleanup(&ts_data->offload); #endif #if IS_ENABLED(CONFIG_TOUCHSCREEN_OFFLOAD) || \ IS_ENABLED(CONFIG_TOUCHSCREEN_HEATMAP) if (ts_data->heatmap_buff) { kfree_safe(ts_data->heatmap_buff); ts_data->heatmap_buff = NULL; } if (ts_data->heatmap_raw) { kfree_safe(ts_data->heatmap_raw); ts_data->heatmap_raw = NULL; } if (ts_data->trans_raw) { kfree_safe(ts_data->trans_raw); ts_data->trans_raw = NULL; } #endif #if IS_ENABLED(CONFIG_TOUCHSCREEN_HEATMAP) heatmap_remove(&ts_data->v4l2); #endif fts_ex_mode_exit(ts_data); fts_fwupg_exit(ts_data); #if FTS_TEST_EN fts_test_exit(ts_data); #endif #if FTS_ESDCHECK_EN fts_esdcheck_exit(ts_data); #endif fts_gesture_exit(ts_data); fts_bus_exit(ts_data); free_irq(ts_data->irq, ts_data); input_unregister_device(ts_data->input_dev); #if FTS_PEN_EN input_unregister_device(ts_data->pen_dev); #endif cancel_work_sync(&ts_data->suspend_work); cancel_work_sync(&ts_data->resume_work); #if IS_ENABLED(CONFIG_TOUCHSCREEN_TBN) if (ts_data->tbn_register_mask) unregister_tbn(&ts_data->tbn_register_mask); #endif #if IS_ENABLED(CONFIG_TOUCHSCREEN_PANEL_BRIDGE) unregister_panel_bridge(&ts_data->panel_bridge); #endif if (ts_data->ts_workqueue) destroy_workqueue(ts_data->ts_workqueue); cpu_latency_qos_remove_request(&ts_data->pm_qos_req); #if defined(CONFIG_FB) if (fb_unregister_client(&ts_data->fb_notif)) FTS_ERROR("[FB]Error occurred while unregistering fb_notifier."); #elif defined(CONFIG_DRM) #if defined(CONFIG_DRM_PANEL) if (active_panel) drm_panel_notifier_unregister(active_panel, &ts_data->fb_notif); #elif defined(CONFIG_ARCH_MSM) if (msm_drm_unregister_client(&ts_data->fb_notif)) FTS_ERROR("[DRM]Error occurred while unregistering fb_notifier.\n"); #endif #elif defined(CONFIG_HAS_EARLYSUSPEND) unregister_early_suspend(&ts_data->early_suspend); #endif if (gpio_is_valid(ts_data->pdata->reset_gpio)) gpio_free(ts_data->pdata->reset_gpio); if (gpio_is_valid(ts_data->pdata->irq_gpio)) gpio_free(ts_data->pdata->irq_gpio); #if FTS_POWER_SOURCE_CUST_EN fts_power_source_exit(ts_data); #endif kfree_safe(ts_data->point_buf); kfree_safe(ts_data->events); kfree_safe(ts_data->pdata); kfree_safe(ts_data); FTS_FUNC_EXIT(); return 0; } void fts_set_heatmap_mode(bool en) { struct fts_ts_data *ts_data = fts_data; if (en == 1){ fts_write_reg(FTS_REG_HEATMAP_1E, 0x01); fts_write_reg(FTS_REG_HEATMAP_ED, 0x00); fts_write_reg(FTS_REG_HEATMAP_9E, 0x01); } else { fts_write_reg(FTS_REG_HEATMAP_9E, 0x00); fts_write_reg(FTS_REG_HEATMAP_1E, 0x00); } ts_data->enable_fw_heatmap = en; FTS_DEBUG("%s heatmap.\n", en ? "Enable" : "Disable"); } int fts_set_grip_mode(bool en) { int ret = 0; #if FTS_DEBUG_EN struct fts_ts_data *ts_data = fts_data; #endif ret = fts_write_reg(FTS_REG_EDGE_MODE_EN, en ? 0x00 : 0xAA); if (ret < 0) { FTS_ERROR("write reg0x8C fails"); return ret; } FTS_DEBUG("%s fw_grip(%d).\n", en ? "Enable" : "Disable", ts_data->enable_fw_grip); return ret; } int fts_set_palm_mode(bool en) { int ret = 0; #if FTS_DEBUG_EN struct fts_ts_data *ts_data = fts_data; #endif ret = fts_write_reg(FTS_REG_PALM_EN, en ? ENABLE : DISABLE); if (ret < 0) { FTS_ERROR("write reg0xC5 fails"); return ret; } FTS_DEBUG("%s fw_palm(%d).\n", en ? "Enable" : "Disable", ts_data->enable_fw_palm); return ret; } int fts_set_continuous_mode(bool en) { int ret = 0; struct fts_ts_data *ts_data = fts_data; u8 value = en ? ENABLE : DISABLE; ret = fts_write_reg(FTS_REG_CONTINUOUS_EN, value); if (ret < 0) { FTS_ERROR("write reg0xE7 fails"); return ret; } ts_data->set_continuously_report = value; PR_LOGD("%s firmware continuous report.\n", en ? "Enable" : "Disable"); return ret; } int fts_set_glove_mode(bool en) { int ret = 0; struct fts_ts_data *ts_data = fts_data; u8 value = en ? ENABLE : DISABLE; ret = fts_write_reg(FTS_REG_GLOVE_MODE_EN, value); if (ret < 0) { FTS_ERROR("write reg0xC0 fails"); return ret; } ts_data->glove_mode = value; FTS_DEBUG("%s firmware glove mode.\n", en ? "Enable" : "Disable"); return ret; } /** * fts_update_feature_setting() * * Restore the feature settings after the device resume. * * @param * [ in] ts_data: touch driver handle * */ void fts_update_feature_setting(struct fts_ts_data *ts_data) { #if IS_ENABLED(CONFIG_TOUCHSCREEN_HEATMAP) fts_set_heatmap_mode(true); #endif fts_set_grip_mode(ts_data->enable_fw_grip % 2); fts_set_palm_mode(ts_data->enable_fw_palm % 2); fts_set_continuous_mode(ts_data->set_continuously_report); fts_set_glove_mode(ts_data->glove_mode); } static int fts_ts_suspend(struct device *dev) { int ret = 0; struct fts_ts_data *ts_data = fts_data; FTS_FUNC_ENTER(); if (ts_data->bus_refmask) FTS_DEBUG("bus_refmask 0x%X\n", ts_data->bus_refmask); if (ts_data->suspended) { FTS_INFO("Already in suspend state"); return 0; } if (ts_data->fw_loading) { FTS_INFO("fw upgrade in process, can't suspend"); return 0; } #if FTS_ESDCHECK_EN fts_esdcheck_suspend(); #endif #if IS_ENABLED(CONFIG_TOUCHSCREEN_HEATMAP) fts_set_heatmap_mode(false); #endif if (ts_data->gesture_mode) { fts_gesture_suspend(ts_data); } else { if (ts_data->bus_refmask == FTS_TS_BUS_REF_BUGREPORT && ktime_ms_delta(ktime_get(), ts_data->bugreport_ktime_start) > 30 * MSEC_PER_SEC) { fts_ts_set_bus_ref(ts_data, FTS_TS_BUS_REF_BUGREPORT, false); pm_relax(ts_data->dev); ts_data->bugreport_ktime_start = 0; FTS_DEBUG("Force release FTS_TS_BUS_REF_BUGREPORT reference bit."); return -EBUSY; } /* Disable irq */ fts_irq_disable(); FTS_DEBUG("make TP enter into sleep mode"); ret = fts_write_reg(FTS_REG_POWER_MODE, FTS_REG_POWER_MODE_SLEEP); if (ret < 0) FTS_ERROR("set TP to sleep mode fail, ret=%d", ret); if (!ts_data->ic_info.is_incell) { #if FTS_POWER_SOURCE_CUST_EN ret = fts_power_source_suspend(ts_data); if (ret < 0) { FTS_ERROR("power enter suspend fail"); } #endif } } fts_release_all_finger(); ts_data->suspended = true; FTS_FUNC_EXIT(); return 0; } static int fts_ts_resume(struct device *dev) { struct fts_ts_data *ts_data = fts_data; int ret = 0; FTS_FUNC_ENTER(); if (!ts_data->suspended) { FTS_DEBUG("Already in awake state"); return 0; } fts_release_all_finger(); if (!ts_data->ic_info.is_incell) { #if FTS_POWER_SOURCE_CUST_EN fts_power_source_resume(ts_data); #endif fts_reset_proc(200); } ret = fts_wait_tp_to_valid(); if (ret != 0) { FTS_ERROR("Resume has been cancelled by wake up timeout"); #if FTS_POWER_SOURCE_CUST_EN fts_power_source_suspend(ts_data); #endif return ret; } fts_ex_mode_recovery(ts_data); #if FTS_ESDCHECK_EN fts_esdcheck_resume(); #endif if (ts_data->gesture_mode) { fts_gesture_resume(ts_data); } else { fts_irq_enable(); } /* update feature setting. */ fts_update_feature_setting(ts_data); ts_data->suspended = false; FTS_FUNC_EXIT(); return 0; } #if defined(CONFIG_PM) && FTS_PATCH_COMERR_PM static int fts_pm_suspend(struct device *dev) { struct fts_ts_data *ts_data = dev_get_drvdata(dev); FTS_INFO("system enters into pm_suspend"); ts_data->pm_suspend = true; reinit_completion(&ts_data->pm_completion); return 0; } static int fts_pm_resume(struct device *dev) { struct fts_ts_data *ts_data = dev_get_drvdata(dev); FTS_INFO("system resumes from pm_suspend"); ts_data->pm_suspend = false; complete(&ts_data->pm_completion); return 0; } static const struct dev_pm_ops fts_dev_pm_ops = { .suspend = fts_pm_suspend, .resume = fts_pm_resume, }; #endif /***************************************************************************** * TP Driver *****************************************************************************/ static int fts_ts_probe(struct spi_device *spi) { int ret = 0; struct fts_ts_data *ts_data = NULL; FTS_INFO("Touch Screen(SPI BUS) driver proboe..."); spi->mode = SPI_MODE_0; spi->bits_per_word = 8; spi->rt = true; ret = spi_setup(spi); if (ret) { FTS_ERROR("spi setup fail"); return ret; } /* malloc memory for global struct variable */ ts_data = (struct fts_ts_data *)kzalloc(sizeof(*ts_data), GFP_KERNEL); if (!ts_data) { FTS_ERROR("allocate memory for fts_data fail"); return -ENOMEM; } fts_data = ts_data; ts_data->spi = spi; ts_data->dev = &spi->dev; ts_data->log_level = FTS_KEY_LOG_LEVEL; ts_data->bus_type = FTS_BUS_TYPE_SPI_V2; spi_set_drvdata(spi, ts_data); ret = fts_ts_probe_entry(ts_data); if (ret) { FTS_ERROR("Touch Screen(SPI BUS) driver probe fail"); kfree_safe(ts_data); return ret; } FTS_INFO("Touch Screen(SPI BUS) driver probe successfully"); return 0; } static int fts_ts_remove(struct spi_device *spi) { return fts_ts_remove_entry(spi_get_drvdata(spi)); } static const struct spi_device_id fts_ts_id[] = { {FTS_DRIVER_NAME, 0}, {}, }; static const struct of_device_id fts_dt_match[] = { {.compatible = "focaltech,ts", }, {}, }; MODULE_DEVICE_TABLE(of, fts_dt_match); static struct spi_driver fts_ts_driver = { .probe = fts_ts_probe, .remove = fts_ts_remove, .driver = { .name = FTS_DRIVER_NAME, .owner = THIS_MODULE, #if defined(CONFIG_PM) && FTS_PATCH_COMERR_PM .pm = &fts_dev_pm_ops, #endif .of_match_table = of_match_ptr(fts_dt_match), }, .id_table = fts_ts_id, }; static int __init fts_ts_init(void) { int ret = 0; FTS_FUNC_ENTER(); ret = spi_register_driver(&fts_ts_driver); if ( ret != 0 ) { FTS_ERROR("Focaltech touch screen driver init failed!"); } FTS_FUNC_EXIT(); return ret; } static void __exit fts_ts_exit(void) { spi_unregister_driver(&fts_ts_driver); } module_init(fts_ts_init); module_exit(fts_ts_exit); MODULE_AUTHOR("FocalTech Driver Team"); MODULE_DESCRIPTION("FocalTech Touchscreen Driver"); MODULE_LICENSE("GPL v2");