/* * ILITEK Touch IC driver * * Copyright (C) 2011 ILI Technology Corporation. * * Author: Dicky Chiang * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "ili9881x_fw.h" #include "ili9881x.h" /* Debug level */ bool debug_en = DEBUG_OUTPUT; EXPORT_SYMBOL(debug_en); bool ili_shutdown_is_on_going_tsp = false; static struct workqueue_struct *esd_wq; static struct workqueue_struct *bat_wq; static struct delayed_work esd_work; static struct delayed_work bat_work; #if RESUME_BY_DDI static struct workqueue_struct *resume_by_ddi_wq; static struct work_struct resume_by_ddi_work; static void ilitek_resume_by_ddi_work(struct work_struct *work) { mutex_lock(&ilits->touch_mutex); if (ilits->gesture) ili_irq_wake_disable(); /* Set tp as demo mode and reload code if it's iram. */ ilits->actual_tp_mode = P5_X_FW_AP_MODE; if (ilits->fw_upgrade_mode == UPGRADE_IRAM) ili_fw_upgrade_handler(NULL); else ili_reset_ctrl(ilits->reset); ili_irq_enable(); input_info(true, ilits->dev, "%s TP resume end by wq\n", __func__); ili_wq_ctrl(WQ_ESD, ENABLE); ili_wq_ctrl(WQ_BAT, ENABLE); ilits->tp_suspend = false; mutex_unlock(&ilits->touch_mutex); } void ili_resume_by_ddi(void) { if (!resume_by_ddi_wq) { input_info(true, ilits->dev, "%s resume_by_ddi_wq is null\n", __func__); return; } mutex_lock(&ilits->touch_mutex); input_info(true, ilits->dev, "%s TP resume start called by ddi\n", __func__); /* * To match the timing of sleep out, the first of mipi cmd must be sent within 10ms * after TP reset. We then create a wq doing host download before resume. */ atomic_set(&ilits->fw_stat, ENABLE); ili_reset_ctrl(ilits->reset); ili_ice_mode_ctrl(ENABLE, OFF); ilits->ddi_rest_done = true; usleep_range(5 * 1000, 5 * 1000); queue_work(resume_by_ddi_wq, &(resume_by_ddi_work)); mutex_unlock(&ilits->touch_mutex); } #endif void ilitek_pin_control(bool pin_set) { int retval = 0; if (IS_ERR(ilits->pinctrl)) return; ilits->pinctrl->state = NULL; if (pin_set) { if (!IS_ERR(ilits->pins_on_state)) { retval = pinctrl_select_state(ilits->pinctrl, ilits->pins_on_state); if (retval) input_err(true, ilits->dev, "can't set pins_on_state state : %d\n", retval); input_info(true, ilits->dev, "%s idle\n", __func__); } } else { if (!IS_ERR(ilits->pins_off_state)) { retval = pinctrl_select_state(ilits->pinctrl, ilits->pins_off_state); if (retval) input_err(true, ilits->dev, "can't set pins_off_state state : %d\n", retval); input_info(true, ilits->dev, "%s sleep\n", __func__); } } } int ili_mp_test_handler(char *apk, bool lcm_on) { int ret = 0; if (atomic_read(&ilits->fw_stat)) { input_err(true, ilits->dev, "%s fw upgrade processing, ignore\n", __func__); return -EMP_FW_PROC; } atomic_set(&ilits->mp_stat, ENABLE); if (ilits->actual_tp_mode != P5_X_FW_TEST_MODE) { ret = ili_switch_tp_mode(P5_X_FW_TEST_MODE); if (ret < 0) { input_err(true, ilits->dev, "%s Switch MP mode failed\n", __func__); ret = -EMP_MODE; goto out; } } ret = ili_mp_test_main(apk, lcm_on); out: /* * If there's running mp test with lcm off, we suspose that * users will soon call resume from suspend. TP mode will be changed * from MP to AP mode until resume finished. */ if (!lcm_on) { atomic_set(&ilits->mp_stat, DISABLE); return ret; } if (ilits->sec.cmd_all_factory_state != SEC_CMD_STATUS_RUNNING) { ilits->actual_tp_mode = P5_X_FW_AP_MODE; if (ilits->fw_upgrade_mode == UPGRADE_IRAM) { if (ili_fw_upgrade_handler(NULL) < 0) input_err(true, ilits->dev, "%s FW upgrade failed during mp test\n", __func__); } else { if (ili_reset_ctrl(ilits->reset) < 0) input_err(true, ilits->dev, "%s TP Reset failed during mp test\n", __func__); } atomic_set(&ilits->mp_stat, DISABLE); } return ret; } int ili_switch_tp_mode(u8 mode) { int ret = 0; bool ges_dbg = false; atomic_set(&ilits->tp_sw_mode, START); ilits->actual_tp_mode = mode; /* able to see cdc data in gesture mode */ if (ilits->tp_data_format == DATA_FORMAT_DEBUG && ilits->actual_tp_mode == P5_X_FW_GESTURE_MODE) ges_dbg = true; switch (ilits->actual_tp_mode) { case P5_X_FW_AP_MODE: input_info(true, ilits->dev, "%s Switch to AP mode\n", __func__); ilits->wait_int_timeout = AP_INT_TIMEOUT; if (ilits->fw_upgrade_mode == UPGRADE_IRAM) { if (ili_fw_upgrade_handler(NULL) < 0) input_err(true, ilits->dev, "%s FW upgrade failed\n", __func__); } else { ret = ili_reset_ctrl(ilits->reset); } if (ret < 0) input_err(true, ilits->dev, "%s TP Reset failed\n", __func__); break; case P5_X_FW_GESTURE_MODE: input_info(true, ilits->dev, "%s Switch to Gesture mode\n", __func__); ilits->wait_int_timeout = AP_INT_TIMEOUT; ret = ilits->gesture_move_code(ilits->gesture_mode); if (ret < 0) input_err(true, ilits->dev, "%s Move gesture code failed\n", __func__); if (ges_dbg) { input_info(true, ilits->dev, "%s Enable gesture debug func\n", __func__); ili_set_tp_data_len(DATA_FORMAT_GESTURE_DEBUG, false, NULL); } break; case P5_X_FW_TEST_MODE: input_info(true, ilits->dev, "%s Switch to Test mode\n", __func__); ilits->wait_int_timeout = MP_INT_TIMEOUT; ret = ilits->mp_move_code(); break; default: input_err(true, ilits->dev, "%s Unknown TP mode: %x\n", __func__, mode); ret = -1; break; } if (ret < 0) input_err(true, ilits->dev, "%s Switch TP mode (%d) failed\n", __func__, mode); ILI_DBG("%s Actual TP mode = %d\n", __func__, ilits->actual_tp_mode); atomic_set(&ilits->tp_sw_mode, END); return ret; } void ili_print_info(void) { if (!ilits) return; ilits->print_info_cnt_open++; if (ilits->print_info_cnt_open > 0xfff0) ilits->print_info_cnt_open = 0; if (ilits->touch_count == 0) ilits->print_info_cnt_release++; input_info(true, ilits->dev, "tc:%d ver:%02d%02d%02d%02d// #%d %d\n", ilits->touch_count, ilits->fw_cur_info[2],ilits->fw_cur_info[3],ilits->fw_cur_info[4], ilits->fw_cur_info[5], ilits->print_info_cnt_open, ilits->print_info_cnt_release); } static void ili_print_info_work(struct work_struct *work) { ili_print_info(); schedule_delayed_work(&ilits->work_print_info, msecs_to_jiffies(TOUCH_PRINT_INFO_DWORK_TIME)); } int ili_gesture_recovery(void) { int ret = 0; atomic_set(&ilits->esd_stat, START); input_info(true, ilits->dev, "%s Doing gesture recovery\n", __func__); ret = ilits->ges_recover(); atomic_set(&ilits->esd_stat, END); return ret; } void ili_spi_recovery(void) { atomic_set(&ilits->esd_stat, START); input_info(true, ilits->dev, "%s Doing spi recovery\n", __func__); if (ili_fw_upgrade_handler(NULL) < 0) input_err(true, ilits->dev, "%s FW upgrade failed\n", __func__); set_current_ic_mode(SET_MODE_NORMAL); atomic_set(&ilits->esd_stat, END); } int ili_wq_esd_spi_check(void) { int ret = 0; u8 tx = SPI_WRITE, rx = 0; ret = ilits->spi_write_then_read(ilits->spi, &tx, 1, &rx, 1); ILI_DBG("%s spi esd check = 0x%x\n", __func__, ret); if (ret == DO_SPI_RECOVER) { input_err(true, ilits->dev, "%s ret = 0x%x\n", ret, __func__); return -1; } return 0; } int ili_wq_esd_i2c_check(void) { ILI_DBG("%s", __func__); return 0; } static void ilitek_tddi_wq_esd_check(struct work_struct *work) { mutex_lock(&ilits->touch_mutex); if (ilits->esd_recover() < 0) { input_err(true, ilits->dev, "%s SPI ACK failed, doing spi recovery\n", __func__); ili_spi_recovery(); } mutex_unlock(&ilits->touch_mutex); complete_all(&ilits->esd_done); ili_wq_ctrl(WQ_ESD, ENABLE); } static int read_power_status(u8 *buf) { struct file *f = NULL; mm_segment_t old_fs; ssize_t byte = 0; old_fs = get_fs(); set_fs(get_ds()); f = filp_open(POWER_STATUS_PATH, O_RDONLY, 0); if (ERR_ALLOC_MEM(f)) { input_err(true, ilits->dev, "%s Failed to open %s\n", __func__, POWER_STATUS_PATH); return -1; } f->f_op->llseek(f, 0, SEEK_SET); byte = f->f_op->read(f, buf, 20, &f->f_pos); ILI_DBG("%s Read %d bytes\n", __func__, (int)byte); set_fs(old_fs); filp_close(f, NULL); return 0; } static void ilitek_tddi_wq_bat_check(struct work_struct *work) { u8 str[20] = {0}; static int charge_mode; if (read_power_status(str) < 0) input_err(true, ilits->dev, "%s Read power status failed\n", __func__); ILI_DBG("%s Batter Status: %s\n", __func__, str); if (strstr(str, "Charging") != NULL || strstr(str, "Full") != NULL || strstr(str, "Fully charged") != NULL) { if (charge_mode != 1) { ILI_DBG("%s Charging mode\n", __func__); if (ili_ic_func_ctrl("plug", DISABLE) < 0) // plug in input_err(true, ilits->dev, "%s Write plug in failed\n", __func__); charge_mode = 1; } } else { if (charge_mode != 2) { ILI_DBG("%s Not charging mode\n", __func__); if (ili_ic_func_ctrl("plug", ENABLE) < 0) // plug out input_err(true, ilits->dev, "%s Write plug out failed\n", __func__); charge_mode = 2; } } ili_wq_ctrl(WQ_BAT, ENABLE); } void ili_wq_ctrl(int type, int ctrl) { switch (type) { case WQ_ESD: if (ENABLE_WQ_ESD || ilits->wq_ctrl) { if (!esd_wq) { input_err(true, ilits->dev, "%s wq esd is null\n", __func__); break; } ilits->wq_esd_ctrl = ctrl; if (ctrl == ENABLE) { ILI_DBG("%s execute esd check\n", __func__); if (!queue_delayed_work(esd_wq, &esd_work, msecs_to_jiffies(WQ_ESD_DELAY))) ILI_DBG("%s esd check was already on queue\n", __func__); } else { cancel_delayed_work_sync(&esd_work); flush_workqueue(esd_wq); ILI_DBG("%s cancel esd wq\n", __func__); } } break; case WQ_BAT: if (ENABLE_WQ_BAT || ilits->wq_ctrl) { if (!bat_wq) { input_err(true, ilits->dev, "%s WQ BAT is null\n", __func__); break; } ilits->wq_bat_ctrl = ctrl; if (ctrl == ENABLE) { ILI_DBG("%s execute bat check\n", __func__); if (!queue_delayed_work(bat_wq, &bat_work, msecs_to_jiffies(WQ_BAT_DELAY))) ILI_DBG("%s bat check was already on queue\n", __func__); } else { cancel_delayed_work_sync(&bat_work); flush_workqueue(bat_wq); ILI_DBG("%s cancel bat wq\n", __func__); } } break; default: input_err(true, ilits->dev, "%s Unknown WQ type, %d\n", __func__, type); break; } } void set_current_ic_mode(int mode) { int ret = 0; input_info(true, ilits->dev, "%s,mode:%d\n", __func__, mode); if (ilits->power_status == POWER_OFF_STATUS) { input_info(true, ilits->dev, "%s power off satus\n", __func__); return; } switch (mode) { case SET_MODE_NORMAL: if (!ilits->dead_zone_enabled) { ret = ili_ic_func_ctrl("dead_zone_ctrl", DEAD_ZONE_DISABLE); if (ret < 0) input_err(true, ilits->dev, "%s DEAD_ZONE_ENABLE failed\n", __func__); } if (ilits->sip_mode_enabled) { ret = ili_ic_func_ctrl("sip_mode", SIP_MODE_ENABLE); if (ret < 0) input_err(true, ilits->dev, "%s SIP_MODE_ENABLE failed\n", __func__); } if (ilits->prox_face_mode) { ret = ili_ic_func_ctrl("proximity", ilits->prox_face_mode); if (ret < 0) input_err(true, ilits->dev, "%s ear detect enabled fail(%d)\n", __func__,ilits->prox_face_mode); } break; case SET_MODE_PROXIMTY_LCDOFF: if (ilits->prox_face_mode && ilits->tp_suspend) { if (ilits->prox_lp_scan_mode_enabled) { ret = ili_ic_func_ctrl("lpwg", 0x21);//proximity report start if (ret < 0) input_err(true, ilits->dev, "%s proximity report start failed\n", __func__); } else { if (ili_ic_func_ctrl("sleep", SLEEP_IN) < 0) input_err(true, ilits->dev, "%s Write sleep in cmd failed\n", __func__); } } break; case SET_MODE_GESTURE: ili_switch_tp_mode(P5_X_FW_GESTURE_MODE); break; default: input_err(true, ilits->dev, "%s Unknown mode\n", __func__); break; } } static void ilitek_tddi_wq_init(void) { esd_wq = alloc_workqueue("esd_check", WQ_MEM_RECLAIM, 0); bat_wq = alloc_workqueue("bat_check", WQ_MEM_RECLAIM, 0); WARN_ON(!esd_wq); WARN_ON(!bat_wq); INIT_DELAYED_WORK(&esd_work, ilitek_tddi_wq_esd_check); INIT_DELAYED_WORK(&bat_work, ilitek_tddi_wq_bat_check); #if RESUME_BY_DDI resume_by_ddi_wq = create_singlethread_workqueue("resume_by_ddi_wq"); WARN_ON(!resume_by_ddi_wq); INIT_WORK(&resume_by_ddi_work, ilitek_resume_by_ddi_work); #endif INIT_DELAYED_WORK(&ilits->work_print_info, ili_print_info_work); } int ili_sleep_handler(int mode) { int ret = 0; bool sense_stop = true; mutex_lock(&ilits->touch_mutex); atomic_set(&ilits->tp_sleep, START); if (atomic_read(&ilits->fw_stat) || atomic_read(&ilits->mp_stat)) { input_info(true, ilits->dev, "%s fw upgrade or mp still running, ignore sleep requst\n", __func__); atomic_set(&ilits->tp_sleep, END); mutex_unlock(&ilits->touch_mutex); return 0; } if (ilits->sleep_handler_mode == mode) { input_info(true, ilits->dev, "%s skip, already mode %d\n", __func__, mode); atomic_set(&ilits->tp_sleep, END); mutex_unlock(&ilits->touch_mutex); return 0; } ili_wq_ctrl(WQ_ESD, DISABLE); ili_wq_ctrl(WQ_BAT, DISABLE); ili_irq_disable(); if (ilits->ss_ctrl) sense_stop = true; else if (ilits->chip->core_ver >= CORE_VER_1430) sense_stop = false; else sense_stop = true; /* If you want to add other mode, must check sleep_handler_mode condition. */ switch (mode) { case TP_EARLY_SUSPEND: ilits->tp_suspend = true; if (sense_stop) { if (ili_ic_func_ctrl("sense", DISABLE) < 0) input_err(true, ilits->dev, "%s Write sense stop cmd failed\n", __func__); if (ili_ic_check_busy(50, 20) < 0) input_err(true, ilits->dev, "%s Check busy timeout during suspend\n", __func__); } input_info(true, ilits->dev, "%s prox_power_off:%d, prox_face_mode:%d, gesture:%d, ges_sym:0x%x, incell_power_state:%d\n", __func__, ilits->prox_power_off, ilits->prox_face_mode, ilits->gesture, ilits->ges_sym, ilits->incell_power_state); if (ilits->prox_face_mode || ilits->gesture) { ili_incell_power_control(ENABLE); ilits->power_status = LP_AOT_STATUS; ili_switch_tp_mode(P5_X_FW_GESTURE_MODE); ili_irq_wake_enable(); ili_irq_enable(); if (ilits->prox_face_mode) { ret = ili_ic_func_ctrl("sleep", SLEEP_IN); if (ret < 0) input_err(true, ilits->dev, "%s Write sleep in cmd failed\n", __func__); else ilits->power_status = LP_PROX_STATUS; } if (ilits->prox_lp_scan_mode_enabled) { set_current_ic_mode(SET_MODE_GESTURE); set_current_ic_mode(SET_MODE_PROXIMTY_LCDOFF); } } else if (ilits->incell_power_state) { ili_irq_wake_enable(); ili_irq_enable(); ilits->power_status = LP_FACTORY_STATUS; } else { ilitek_pin_control(false); ilits->power_status = POWER_OFF_STATUS; } ilits->screen_off_sate = TP_EARLY_SUSPEND; ilits->sleep_handler_mode = mode; break; case TP_EARLY_RESUME: input_info(true, ilits->dev, "%s ilits->power_status:%d, ges_sym:0x%x\n", __func__, ilits->power_status, ilits->ges_sym); if ((ilits->power_status != POWER_OFF_STATUS) && (ilits->power_status != POWER_ON_STATUS)) { ili_irq_wake_disable(); if (ilits->power_status == LP_PROX_STATUS) { ilits->actual_tp_mode = P5_X_FW_AP_MODE; ili_ic_func_ctrl("sleep", 0x01); ili_ic_func_ctrl("sense", 0x01); ilits->tp_data_format = DATA_FORMAT_DEMO; ilits->tp_data_len = P5_X_DEMO_MODE_PACKET_LEN; ilits->pll_clk_wakeup = false; ilits->prox_power_off = 0; ilits->prox_lp_scan_mode_enabled = false; } if (ilits->power_status != LP_FACTORY_STATUS) { ili_incell_power_control(DISABLE); ilitek_pin_control(false); ilits->power_status = POWER_OFF_STATUS; usleep_range(15000, 15000); } } ilits->screen_off_sate = TP_EARLY_RESUME; break; case TP_RESUME: #if !RESUME_BY_DDI ilits->power_status = POWER_ON_STATUS; /* Set tp as demo mode and reload code if it's iram. */ ilits->actual_tp_mode = P5_X_FW_AP_MODE; ilits->screen_off_sate = TP_RESUME; ilitek_pin_control(true); if (ilits->fw_upgrade_mode == UPGRADE_IRAM) { if (ili_fw_upgrade_handler(NULL) < 0) input_err(true, ilits->dev, "%sFW upgrade failed during resume\n", __func__); } else { if (ili_reset_ctrl(ilits->reset) < 0) input_err(true, ilits->dev, "%sTP Reset failed during resume\n", __func__); ili_ic_func_ctrl_reset(); } ilits->tp_suspend = false; #if PROXIMITY_BY_FW ilits->proxmity_face = false; #endif #endif ili_wq_ctrl(WQ_ESD, ENABLE); ili_wq_ctrl(WQ_BAT, ENABLE); msleep(ilits->rst_edge_delay);//resume, after 10ms enable irq , for INT noisy set_current_ic_mode(SET_MODE_NORMAL); ili_irq_enable(); ilits->sleep_handler_mode = mode; break; default: input_err(true, ilits->dev, "%sUnknown sleep mode, %d\n", __func__, mode); ret = -EINVAL; break; } if (mode == TP_RESUME) { cancel_delayed_work(&ilits->work_print_info); ilits->print_info_cnt_open = 0; ilits->print_info_cnt_release = 0; if (!ili_shutdown_is_on_going_tsp) schedule_work(&ilits->work_print_info.work); } else if (mode == TP_EARLY_SUSPEND || mode == TP_DEEP_SLEEP) { cancel_delayed_work(&ilits->work_print_info); ili_print_info(); } ili_touch_release_all_point(); atomic_set(&ilits->tp_sleep, END); mutex_unlock(&ilits->touch_mutex); input_info(true, ilits->dev, "%s end (mode %d)\n", __func__, ilits->sleep_handler_mode); return ret; } int ili_fw_upgrade_handler(void *data) { int ret = 0; atomic_set(&ilits->fw_stat, START); ilits->fw_update_stat = FW_STAT_INIT; ret = ili_fw_upgrade(ilits->fw_open); if (ret != 0) { input_info(true, ilits->dev, "%s FW upgrade fail\n", __func__); ilits->fw_update_stat = FW_UPDATE_FAIL; } else { input_info(true, ilits->dev, "%s FW upgrade pass\n", __func__); #if CHARGER_NOTIFIER_CALLBACK #if KERNEL_VERSION(4, 1, 0) <= LINUX_VERSION_CODE /* add_for_charger_start */ if ((ilits->usb_plug_status) && (ilits->actual_tp_mode != P5_X_FW_TEST_MODE)) { ret = ili_ic_func_ctrl("plug", !ilits->usb_plug_status);// plug in if (ret < 0) input_err(true, ilits->dev, "%sWrite plug in failed\n", __func__); } /* add_for_charger_end */ #endif #endif ilits->fw_update_stat = FW_UPDATE_PASS; } if (!ilits->boot) { ilits->boot = true; input_info(true, ilits->dev, "%s Registre touch to input subsystem\n", __func__); ili_input_register(); ili_wq_ctrl(WQ_ESD, ENABLE); ili_wq_ctrl(WQ_BAT, ENABLE); } atomic_set(&ilits->fw_stat, END); return ret; } int ili_set_tp_data_len(int format, bool send, u8 *data) { u8 cmd[10] = {0}, ctrl = 0, debug_ctrl = 0; u16 self_key = 2; int ret = 0, tp_mode = ilits->actual_tp_mode, len = 0; switch (format) { case DATA_FORMAT_DEMO: #if AXIS_PACKET len = P5_X_DEMO_MODE_PACKET_INFO_LEN + P5_X_DEMO_MODE_PACKET_LEN + P5_X_DEMO_MODE_AXIS_LEN + P5_X_DEMO_MODE_STATE_INFO; ctrl = DATA_FORMAT_DEMO_CMD; break; #endif case DATA_FORMAT_GESTURE_DEMO: len = P5_X_DEMO_MODE_PACKET_LEN; ctrl = DATA_FORMAT_DEMO_CMD; break; case DATA_FORMAT_DEBUG: #if AXIS_PACKET len = (2 * ilits->xch_num * ilits->ych_num) + (ilits->stx * 2) + (ilits->srx * 2); len += 2 * self_key + 1 + 35; len += P5_X_DEMO_MODE_AXIS_LEN + P5_X_DEMO_MODE_STATE_INFO; ctrl = DATA_FORMAT_DEBUG_CMD; break; #endif case DATA_FORMAT_GESTURE_DEBUG: len = (2 * ilits->xch_num * ilits->ych_num) + (ilits->stx * 2) + (ilits->srx * 2); len += 2 * self_key + (8 * 2) + 1 + 35; ctrl = DATA_FORMAT_DEBUG_CMD; break; case DATA_FORMAT_DEMO_DEBUG_INFO: /*only suport SPI interface now, so defult use size 1024 buffer*/ len = P5_X_DEMO_MODE_PACKET_LEN + P5_X_DEMO_DEBUG_INFO_ID0_LENGTH + P5_X_INFO_HEADER_LENGTH; ctrl = DATA_FORMAT_DEMO_DEBUG_INFO_CMD; break; case DATA_FORMAT_GESTURE_INFO: len = P5_X_GESTURE_INFO_LENGTH; ctrl = DATA_FORMAT_GESTURE_INFO_CMD; break; case DATA_FORMAT_GESTURE_NORMAL: len = P5_X_GESTURE_NORMAL_LENGTH; ctrl = DATA_FORMAT_GESTURE_NORMAL_CMD; break; case DATA_FORMAT_GESTURE_SPECIAL_DEMO: if (ilits->gesture_demo_ctrl == ENABLE) { if (ilits->gesture_mode == DATA_FORMAT_GESTURE_INFO) len = P5_X_GESTURE_INFO_LENGTH + P5_X_INFO_HEADER_LENGTH + P5_X_INFO_CHECKSUM_LENGTH; else len = P5_X_DEMO_MODE_PACKET_LEN + P5_X_INFO_HEADER_LENGTH + P5_X_INFO_CHECKSUM_LENGTH; } else { if (ilits->gesture_mode == DATA_FORMAT_GESTURE_INFO) len = P5_X_GESTURE_INFO_LENGTH; else len = P5_X_GESTURE_NORMAL_LENGTH; } input_info(true, ilits->dev, "%s Gesture demo mode control = %d\n", __func__, ilits->gesture_demo_ctrl); ili_ic_func_ctrl("gesture_demo_en", ilits->gesture_demo_ctrl); input_info(true, ilits->dev, "%s knock_en setting\n", __func__); ili_ic_func_ctrl("knock_en", 0x8); break; case DATA_FORMAT_DEBUG_LITE_ROI: debug_ctrl = DATA_FORMAT_DEBUG_LITE_ROI_CMD; ctrl = DATA_FORMAT_DEBUG_LITE_CMD; break; case DATA_FORMAT_DEBUG_LITE_WINDOW: debug_ctrl = DATA_FORMAT_DEBUG_LITE_WINDOW_CMD; ctrl = DATA_FORMAT_DEBUG_LITE_CMD; break; case DATA_FORMAT_DEBUG_LITE_AREA: /* * if(cmd == "") { * input_err(true, ilits->dev, "%sDATA_FORMAT_DEBUG_LITE_AREA error cmd\n", __func__); * return -1; * } */ debug_ctrl = DATA_FORMAT_DEBUG_LITE_AREA_CMD; ctrl = DATA_FORMAT_DEBUG_LITE_CMD; cmd[3] = data[0]; cmd[4] = data[1]; cmd[5] = data[2]; cmd[6] = data[3]; break; default: input_err(true, ilits->dev, "%sUnknow TP data format\n", __func__); return -1; } if (ctrl == DATA_FORMAT_DEBUG_LITE_CMD) { len = P5_X_DEBUG_LITE_LENGTH; cmd[0] = P5_X_MODE_CONTROL; cmd[1] = ctrl; cmd[2] = debug_ctrl; ret = ilits->wrapper(cmd, 10, NULL, 0, ON, OFF); if (ret < 0) { input_err(true, ilits->dev, "%sswitch to format %d failed\n", __func__, format); ili_switch_tp_mode(P5_X_FW_AP_MODE); } } else if (tp_mode == P5_X_FW_AP_MODE || format == DATA_FORMAT_GESTURE_DEMO || format == DATA_FORMAT_GESTURE_DEBUG) { cmd[0] = P5_X_MODE_CONTROL; cmd[1] = ctrl; ret = ilits->wrapper(cmd, 2, NULL, 0, ON, OFF); if (ret < 0) { input_err(true, ilits->dev, "%s switch to format %d failed\n", __func__, format); ili_switch_tp_mode(P5_X_FW_AP_MODE); } } else if (tp_mode == P5_X_FW_GESTURE_MODE) { /*set gesture symbol*/ ili_set_gesture_symbol(); if (send) { if (ilits->prox_face_mode) ret = ili_ic_func_ctrl("lpwg", 0x20); else ret = ili_ic_func_ctrl("lpwg", ctrl); if (ret < 0) input_err(true, ilits->dev, "%s write gesture mode failed\n", __func__); } } ilits->tp_data_format = format; ilits->tp_data_len = len; input_info(true, ilits->dev, "%s TP mode = %d, format = %d, len = %d\n", __func__, tp_mode, ilits->tp_data_format, ilits->tp_data_len); return ret; } int ili_report_handler(void) { int ret = 0, pid = 0; u8 checksum = 0, pack_checksum = 0; u8 *trdata = NULL; int rlen = 0; int tmp = debug_en; /* Just in case these stats couldn't be blocked in top half context */ if (!ilits->report || atomic_read(&ilits->tp_reset) || atomic_read(&ilits->fw_stat) || atomic_read(&ilits->tp_sw_mode) || atomic_read(&ilits->mp_stat) || atomic_read(&ilits->tp_sleep)) { input_info(true, ilits->dev, "%s ignore report request\n", __func__); return -EINVAL; } if (ilits->irq_after_recovery) { input_info(true, ilits->dev, "%s ignore int triggered by recovery\n", __func__); ilits->irq_after_recovery = false; return -EINVAL; } ili_wq_ctrl(WQ_ESD, DISABLE); ili_wq_ctrl(WQ_BAT, DISABLE); if (ilits->actual_tp_mode == P5_X_FW_GESTURE_MODE) { __pm_stay_awake(ilits->ws); if (ilits->pm_suspend) { /* Waiting for pm resume completed */ ret = wait_for_completion_timeout(&ilits->pm_completion, msecs_to_jiffies(700)); if (!ret) { input_err(true, ilits->dev, "%ssystem(spi) can't finished resuming procedure.", __func__); } } } rlen = ilits->tp_data_len; ILI_DBG("%s Packget length = %d\n", __func__, rlen); if (!rlen || rlen > TR_BUF_SIZE) { input_err(true, ilits->dev, "%sLength of packet is invaild\n", __func__); goto out; } memset(ilits->tr_buf, 0x0, TR_BUF_SIZE); ret = ilits->wrapper(NULL, 0, ilits->tr_buf, rlen, OFF, OFF); #if PROXIMITY_BY_FW if (ilits->tr_buf[0] == P5_X_DEMO_PROXUMITY_ID) { ili_report_proximity_mode(ilits->tr_buf[1], 1); goto out; } #endif if (ret < 0) { input_err(true, ilits->dev, "%s Read report packet failed, ret = %d\n", __func__, ret); if (ret == DO_SPI_RECOVER) { ili_ic_get_pc_counter(DO_SPI_RECOVER); input_info(true, ilits->dev, "%s prox_power_off:%d, ilits->tp_suspend:%d, ilits->actual_tp_mode:%d, prox_face_mode:%d, gesture:%d,\n", __func__, ilits->prox_power_off, ilits->tp_suspend, ilits->actual_tp_mode, ilits->prox_face_mode, ilits->gesture); if ((ilits->actual_tp_mode == P5_X_FW_GESTURE_MODE) && (ilits->gesture || ilits->prox_face_mode) && ilits->tp_suspend) { input_err(true, ilits->dev, "%s Gesture failed, doing gesture recovery\n", __func__); if (ili_gesture_recovery() < 0) input_err(true, ilits->dev, "%s Failed to recover gesture\n", __func__); ilits->irq_after_recovery = true; } else { input_err(true, ilits->dev, "%s SPI ACK failed, doing spi recovery\n", __func__); ili_spi_recovery(); ilits->irq_after_recovery = true; } } goto out; } ili_dump_data(ilits->tr_buf, 8, rlen, 0, "finger report"); checksum = ili_calc_packet_checksum(ilits->tr_buf, rlen - 1); pack_checksum = ilits->tr_buf[rlen-1]; trdata = ilits->tr_buf; pid = trdata[0]; ILI_DBG("%s Packet ID = %x\n", __func__, pid); if (checksum != pack_checksum && pid != P5_X_I2CUART_PACKET_ID) { input_err(true, ilits->dev, "%s Checksum Error (0x%X)! Pack = 0x%X, len = %d\n", __func__, checksum, pack_checksum, rlen); debug_en = DEBUG_ALL; ili_dump_data(trdata, 8, rlen, 0, "finger report with wrong"); debug_en = tmp; ret = -EINVAL; goto out; } if (pid == P5_X_INFO_HEADER_PACKET_ID) { trdata = ilits->tr_buf + P5_X_INFO_HEADER_LENGTH; pid = trdata[0]; } switch (pid) { case P5_X_DEMO_PACKET_ID: ili_report_ap_mode(trdata, rlen); break; case P5_X_DEBUG_PACKET_ID: ili_report_debug_mode(trdata, rlen); break; #if AXIS_PACKET case P5_X_DEMO_AXIS_PACKET_ID: ili_report_ap_mode(&trdata[P5_X_DEMO_MODE_PACKET_INFO_LEN], (rlen - P5_X_DEMO_MODE_PACKET_INFO_LEN - P5_X_DEMO_MODE_AXIS_LEN - P5_X_DEMO_MODE_STATE_INFO)); break; case P5_X_DEBUG_AXIS_PACKET_ID: ili_report_debug_mode(trdata, rlen); break; #endif case P5_X_DEBUG_LITE_PACKET_ID: ili_report_debug_lite_mode(trdata, rlen); break; case P5_X_I2CUART_PACKET_ID: ili_report_i2cuart_mode(trdata, rlen); break; case P5_X_GESTURE_PACKET_ID: ili_report_gesture_mode(trdata, rlen); break; case P5_X_GESTURE_FAIL_ID: input_info(true, ilits->dev, "%s gesture fail reason code = 0x%02x", __func__, trdata[1]); break; case P5_X_DEMO_DEBUG_INFO_PACKET_ID: ili_demo_debug_info_mode(trdata, rlen); break; default: input_err(true, ilits->dev, "%s Unknown packet id, %x\n", __func__, pid); #if (TDDI_INTERFACE == BUS_I2C) ili_ic_get_pc_counter(DO_I2C_RECOVER); if (ilits->fw_latch != 0) { msleep(100); ili_ic_func_ctrl_reset(); input_err(true, ilits->dev, "%s I2C func_ctrl_reset\n", __func__); } if ((ilits->actual_tp_mode == P5_X_FW_GESTURE_MODE) && ilits->fw_latch != 0) { msleep(100); ili_set_gesture_symbol(); input_err(true, ilits->dev, "%s I2C gesture_symbol\n", __func__); } #endif break; } out: if (ilits->actual_tp_mode != P5_X_FW_GESTURE_MODE) { ili_wq_ctrl(WQ_ESD, ENABLE); ili_wq_ctrl(WQ_BAT, ENABLE); } if (ilits->actual_tp_mode == P5_X_FW_GESTURE_MODE) __pm_relax(ilits->ws); return ret; } int ili_reset_ctrl(int mode) { int ret = 0; atomic_set(&ilits->tp_reset, START); switch (mode) { case TP_IC_CODE_RST: input_info(true, ilits->dev, "%s TP IC Code RST\n", __func__); ret = ili_ic_code_reset(); if (ret < 0) input_err(true, ilits->dev, "%s IC Code reset failed\n", __func__); break; case TP_IC_WHOLE_RST: input_info(true, ilits->dev, "%s TP IC whole RST\n", __func__); ret = ili_ic_whole_reset(); if (ret < 0) input_err(true, ilits->dev, "%s IC whole reset failed\n", __func__); break; case TP_HW_RST_ONLY: input_info(true, ilits->dev, "%s TP HW RST\n", __func__); ili_tp_reset(); break; default: input_err(true, ilits->dev, "%s Unknown reset mode, %d\n", __func__, mode); ret = -EINVAL; break; } /* * Since OTP must be folloing with reset, except for code rest, * the stat of ice mode should be set as 0. */ if (mode != TP_IC_CODE_RST) atomic_set(&ilits->ice_stat, DISABLE); ilits->tp_data_format = DATA_FORMAT_DEMO; ilits->tp_data_len = P5_X_DEMO_MODE_PACKET_LEN; #if AXIS_PACKET ilits->tp_data_len = P5_X_DEMO_MODE_PACKET_INFO_LEN + P5_X_DEMO_MODE_PACKET_LEN + P5_X_DEMO_MODE_AXIS_LEN + P5_X_DEMO_MODE_STATE_INFO; #endif ilits->pll_clk_wakeup = true; atomic_set(&ilits->tp_reset, END); return ret; } static int ilitek_get_tp_module(void) { /* * TODO: users should implement this function * if there are various tp modules been used in projects. */ return MODEL_DEF; } static void ili_update_tp_module_info(void) { int module; module = ilitek_get_tp_module(); switch (module) { case MODEL_CSOT: ilits->md_name = "CSOT"; ilits->md_fw_filp_path = CSOT_FW_FILP_PATH; ilits->md_fw_rq_path = CSOT_FW_REQUEST_PATH; ilits->md_ini_path = CSOT_INI_NAME_PATH; ilits->md_ini_rq_path = CSOT_INI_REQUEST_PATH; ilits->md_fw_ili = CTPM_FW_CSOT; ilits->md_fw_ili_size = sizeof(CTPM_FW_CSOT); break; case MODEL_AUO: ilits->md_name = "AUO"; ilits->md_fw_filp_path = AUO_FW_FILP_PATH; ilits->md_fw_rq_path = AUO_FW_REQUEST_PATH; ilits->md_ini_path = AUO_INI_NAME_PATH; ilits->md_ini_rq_path = AUO_INI_REQUEST_PATH; ilits->md_fw_ili = CTPM_FW_AUO; ilits->md_fw_ili_size = sizeof(CTPM_FW_AUO); break; case MODEL_BOE: ilits->md_name = "BOE"; ilits->md_fw_filp_path = BOE_FW_FILP_PATH; ilits->md_fw_rq_path = BOE_FW_REQUEST_PATH; ilits->md_ini_path = BOE_INI_NAME_PATH; ilits->md_ini_rq_path = BOE_INI_REQUEST_PATH; ilits->md_fw_ili = CTPM_FW_BOE; ilits->md_fw_ili_size = sizeof(CTPM_FW_BOE); break; case MODEL_INX: ilits->md_name = "INX"; ilits->md_fw_filp_path = INX_FW_FILP_PATH; ilits->md_fw_rq_path = INX_FW_REQUEST_PATH; ilits->md_ini_path = INX_INI_NAME_PATH; ilits->md_ini_rq_path = INX_INI_REQUEST_PATH; ilits->md_fw_ili = CTPM_FW_INX; ilits->md_fw_ili_size = sizeof(CTPM_FW_INX); break; case MODEL_DJ: ilits->md_name = "DJ"; ilits->md_fw_filp_path = DJ_FW_FILP_PATH; ilits->md_fw_rq_path = DJ_FW_REQUEST_PATH; ilits->md_ini_path = DJ_INI_NAME_PATH; ilits->md_ini_rq_path = DJ_INI_REQUEST_PATH; ilits->md_fw_ili = CTPM_FW_DJ; ilits->md_fw_ili_size = sizeof(CTPM_FW_DJ); break; case MODEL_TXD: ilits->md_name = "TXD"; ilits->md_fw_filp_path = TXD_FW_FILP_PATH; ilits->md_fw_rq_path = TXD_FW_REQUEST_PATH; ilits->md_ini_path = TXD_INI_NAME_PATH; ilits->md_ini_rq_path = TXD_FW_REQUEST_PATH; ilits->md_fw_ili = CTPM_FW_TXD; ilits->md_fw_ili_size = sizeof(CTPM_FW_TXD); break; case MODEL_TM: ilits->md_name = "TM"; ilits->md_fw_filp_path = TM_FW_REQUEST_PATH; ilits->md_fw_rq_path = TM_FW_REQUEST_PATH; ilits->md_ini_path = TM_INI_NAME_PATH; ilits->md_ini_rq_path = TM_INI_REQUEST_PATH; ilits->md_fw_ili = CTPM_FW_TM; ilits->md_fw_ili_size = sizeof(CTPM_FW_TM); break; default: break; } if (module == 0 || ilits->md_fw_ili_size < ILI_FILE_HEADER) { input_err(true, ilits->dev, "%s Couldn't find any tp modules, applying default settings\n", __func__); ilits->md_name = "DEF"; ilits->md_fw_filp_path = DEF_FW_FILP_PATH; ilits->md_fw_rq_path = (char *)ilits->fw_name; ilits->md_ini_path = DEF_INI_NAME_PATH; ilits->md_ini_rq_path = DEF_INI_REQUEST_PATH; /*Fw data loaded ilitek__get_fw_image function *ilits->md_fw_ili = CTPM_FW_DEF; *ilits->md_fw_ili_size = sizeof(CTPM_FW_DEF); */ } input_info(true, ilits->dev, "%s Found %s module: ini path = %s, fw path = (%s, %s, %d)\n", __func__, ilits->md_name, ilits->md_ini_path, ilits->md_fw_filp_path, ilits->md_fw_rq_path, ilits->md_fw_ili_size); ilits->tp_module = module; } int ili_tddi_init(void) { #if (BOOT_FW_UPDATE | HOST_DOWN_LOAD) struct task_struct *fw_boot_th; #endif int retval; input_info(true, ilits->dev, "%s driver version = %s\n", __func__, DRIVER_VERSION); mutex_init(&ilits->touch_mutex); mutex_init(&ilits->debug_mutex); mutex_init(&ilits->debug_read_mutex); init_waitqueue_head(&(ilits->inq)); spin_lock_init(&ilits->irq_spin); init_completion(&ilits->esd_done); atomic_set(&ilits->irq_stat, DISABLE); atomic_set(&ilits->irq_wake_stat, DISABLE); atomic_set(&ilits->ice_stat, DISABLE); atomic_set(&ilits->tp_reset, END); atomic_set(&ilits->fw_stat, END); atomic_set(&ilits->mp_stat, DISABLE); atomic_set(&ilits->tp_sleep, END); atomic_set(&ilits->cmd_int_check, DISABLE); atomic_set(&ilits->esd_stat, END); memset(ilits->fw_customer_info, 0x0, sizeof(ilits->fw_customer_info)); ili_ic_init(); ilitek_tddi_wq_init(); /* Must do hw reset once in first time for work normally if tp reset is avaliable */ #if !TDDI_RST_BIND if (ili_reset_ctrl(ilits->reset) < 0) input_err(true, ilits->dev, "%s TP Reset failed during init\n", __func__); #endif ilits->demo_debug_info[0] = ili_demo_debug_info_id0; ilits->tp_data_format = DATA_FORMAT_DEMO; ilits->boot = false; /* * This status of ice enable will be reset until process of fw upgrade runs. * it might cause unknown problems if we disable ice mode without any * codes inside touch ic. */ if (ili_ice_mode_ctrl(ENABLE, OFF) < 0) input_err(true, ilits->dev, "%s Failed to enable ice mode during ili_tddi_init\n", __func__); if (ili_ic_dummy_check() < 0) { input_err(true, ilits->dev, "%s Not found ilitek chip\n", __func__); return -ENODEV; } if (ili_ic_get_info() < 0) input_err(true, ilits->dev, "%s Chip info is incorrect\n", __func__); ili_update_tp_module_info(); ili_node_init(); retval = ili_sec_fn_init(); if (retval < 0) input_err(true, ilits->dev, "%s Failed to ili_sec_fn_init\n", __func__); ili_fw_read_flash_info(); #if (BOOT_FW_UPDATE | HOST_DOWN_LOAD) fw_boot_th = kthread_run(ili_fw_upgrade_handler, NULL, "ili_fw_boot"); if (fw_boot_th == (struct task_struct *)ERR_PTR) { fw_boot_th = NULL; WARN_ON(!fw_boot_th); input_err(true, ilits->dev, "%s Failed to create fw upgrade thread\n", __func__); } #else if (ili_ice_mode_ctrl(DISABLE, OFF) < 0) input_err(true, ilits->dev, "%s Failed to disable ice mode failed during init\n", __func__); #if (TDDI_INTERFACE == BUS_I2C) ilits->info_from_hex = DISABLE; #endif ili_ic_get_core_ver(); ili_ic_get_protocl_ver(); ili_ic_get_fw_ver(); ili_ic_get_tp_info(); ili_ic_get_panel_info(); #if (TDDI_INTERFACE == BUS_I2C) ilits->info_from_hex = ENABLE; #endif input_info(true, ilits->dev, "%s Registre touch to input subsystem\n", __func__); ili_input_register(); ili_wq_ctrl(WQ_ESD, ENABLE); ili_wq_ctrl(WQ_BAT, ENABLE); ilits->boot = true; #endif /* #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 9, 0) ilits->ws = wakeup_source_register(ilits->dev, "ili_wakelock"); //4.19 #else ilits->ws = wakeup_source_register("ili_wakelock"); //4.9 #endif */ ilits->ws = wakeup_source_register(ilits->dev, "ili_wakelock"); //4.19 if (!ilits->ws) input_err(true, ilits->dev, "%s wakeup source request failed\n", __func__); return 0; } void ili_dev_remove(void) { input_info(true, ilits->dev, "%s remove ilitek dev\n", __func__); if (!ilits) return; cancel_delayed_work_sync(&ilits->work_print_info); ili_shutdown_is_on_going_tsp = true; ilits->power_status = POWER_OFF_STATUS; ili_irq_wake_disable(); ili_irq_disable(); ilitek_pin_control(false); gpio_free(ilits->tp_int); gpio_free(ilits->tp_rst); if (esd_wq != NULL) { cancel_delayed_work_sync(&esd_work); flush_workqueue(esd_wq); destroy_workqueue(esd_wq); } if (bat_wq != NULL) { cancel_delayed_work_sync(&bat_work); flush_workqueue(bat_wq); destroy_workqueue(bat_wq); } ili_sec_fn_remove(); if (ilits->ws) wakeup_source_unregister(ilits->ws); if (ilits->lcd_bl_en) regulator_put(ilits->lcd_bl_en); if (ilits->lcd_vddi) regulator_put(ilits->lcd_vddi); if (ilits->lcd_rst) regulator_put(ilits->lcd_rst); kfree(ilits->tr_buf); kfree(ilits->gcoord); ili_interface_dev_exit(ilits); } int ili_dev_init(struct ilitek_hwif_info *hwif) { printk(KERN_ERR "[sec_input] %s TP Interface: %s\n", __func__, (hwif->bus_type == BUS_I2C) ? "I2C" : "SPI"); return ili_interface_dev_init(hwif); }