/* * 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.h" struct touch_bus_info { struct spi_driver bus_driver; struct ilitek_hwif_info *hwif; }; struct ilitek_ts_data *ilits; #if SPI_DMA_TRANSFER_SPLIT #define DMA_TRANSFER_MAX_CHUNK 64 // number of chunks to be transferred. #define DMA_TRANSFER_MAX_LEN 1024 // length of a chunk. int ili_spi_write_then_read_split(struct spi_device *spi, const void *txbuf, unsigned n_tx, void *rxbuf, unsigned n_rx) { int status = -1, duplex_len = 0; int xfercnt = 0, xferlen = 0, xferloop = 0; int offset = 0; u8 cmd = 0; struct spi_message message; struct spi_transfer *xfer; if (ilits->power_status == POWER_OFF_STATUS) { input_err(true, ilits->dev, "%s failed(power off state).\n", __func__); return -1; } xfer = kzalloc(DMA_TRANSFER_MAX_CHUNK * sizeof(struct spi_transfer), GFP_KERNEL); if (n_rx > SPI_RX_BUF_SIZE) { input_err(true, ilits->dev, "%s Rx length is greater than spi local buf, abort\n", __func__); status = -ENOMEM; goto out; } spi_message_init(&message); memset(ilits->spi_tx, 0x0, SPI_TX_BUF_SIZE); memset(ilits->spi_rx, 0x0, SPI_RX_BUF_SIZE); if ((n_tx > 0) && (n_rx > 0)) cmd = SPI_READ; else cmd = SPI_WRITE; switch (cmd) { case SPI_WRITE: if (n_tx % DMA_TRANSFER_MAX_LEN) xferloop = (n_tx / DMA_TRANSFER_MAX_LEN) + 1; else xferloop = n_tx / DMA_TRANSFER_MAX_LEN; xferlen = n_tx; memcpy(ilits->spi_tx, (u8 *)txbuf, xferlen); for (xfercnt = 0; xfercnt < xferloop; xfercnt++) { if (xferlen > DMA_TRANSFER_MAX_LEN) xferlen = DMA_TRANSFER_MAX_LEN; xfer[xfercnt].len = xferlen; xfer[xfercnt].tx_buf = ilits->spi_tx + xfercnt * DMA_TRANSFER_MAX_LEN; spi_message_add_tail(&xfer[xfercnt], &message); xferlen = n_tx - (xfercnt+1) * DMA_TRANSFER_MAX_LEN; } if (ilits->cs_gpio > 0) { gpio_direction_output(ilits->cs_gpio, 0); status = spi_sync(spi, &message); gpio_direction_output(ilits->cs_gpio, 1); } else { status = spi_sync(spi, &message); } break; case SPI_READ: if (n_tx > DMA_TRANSFER_MAX_LEN) { input_err(true, ilits->dev, "%s Tx length must be lower than dma length (%d).\n", __func__, DMA_TRANSFER_MAX_LEN); status = -EINVAL; break; } if (!atomic_read(&ilits->ice_stat)) offset = 2; memcpy(ilits->spi_tx, txbuf, n_tx); duplex_len = n_tx + n_rx + offset; if (duplex_len % DMA_TRANSFER_MAX_LEN) xferloop = (duplex_len / DMA_TRANSFER_MAX_LEN) + 1; else xferloop = duplex_len / DMA_TRANSFER_MAX_LEN; xferlen = duplex_len; for (xfercnt = 0; xfercnt < xferloop; xfercnt++) { if (xferlen > DMA_TRANSFER_MAX_LEN) xferlen = DMA_TRANSFER_MAX_LEN; xfer[xfercnt].len = xferlen; xfer[xfercnt].tx_buf = ilits->spi_tx; xfer[xfercnt].rx_buf = ilits->spi_rx + xfercnt * DMA_TRANSFER_MAX_LEN; spi_message_add_tail(&xfer[xfercnt], &message); xferlen = duplex_len - (xfercnt + 1) * DMA_TRANSFER_MAX_LEN; } if (ilits->cs_gpio > 0) { gpio_direction_output(ilits->cs_gpio, 0); status = spi_sync(spi, &message); gpio_direction_output(ilits->cs_gpio, 1); } else { status = spi_sync(spi, &message); } if (status == 0) { if (ilits->spi_rx[1] != SPI_ACK && !atomic_read(&ilits->ice_stat)) { status = DO_SPI_RECOVER; input_err(true, ilits->dev, "%s Do spi recovery: rxbuf[1] = 0x%x, ice = %d\n", __func__, ilits->spi_rx[1], atomic_read(&ilits->ice_stat)); break; } memcpy((u8 *)rxbuf, ilits->spi_rx + offset + 1, n_rx); } else { input_err(true, ilits->dev, "%s spi read fail, status = %d\n", __func__, status); } break; default: input_info(true, ilits->dev, "%s Unknown command 0x%x\n", __func__, cmd); break; } out: ipio_kfree((void **)&xfer); return status; } #else int ili_spi_write_then_read_direct(struct spi_device *spi, const void *txbuf, unsigned n_tx, void *rxbuf, unsigned n_rx) { int status = -1, duplex_len = 0; int offset = 0; u8 cmd; struct spi_message message; struct spi_transfer xfer; if (ilits->power_status == POWER_OFF_STATUS) { input_err(true, ilits->dev, "%s failed(power off state).\n", __func__); return -1; } if (n_rx > SPI_RX_BUF_SIZE) { input_err(true, ilits->dev, "%s Rx length is greater than spi local buf, abort\n", __func__); status = -ENOMEM; goto out; } spi_message_init(&message); memset(&xfer, 0, sizeof(xfer)); if ((n_tx > 0) && (n_rx > 0)) cmd = SPI_READ; else cmd = SPI_WRITE; switch (cmd) { case SPI_WRITE: xfer.len = n_tx; xfer.tx_buf = txbuf; spi_message_add_tail(&xfer, &message); if (ilits->cs_gpio > 0) { gpio_direction_output(ilits->cs_gpio, 0); status = spi_sync(spi, &message); gpio_direction_output(ilits->cs_gpio, 1); } else { status = spi_sync(spi, &message); } break; case SPI_READ: if (!atomic_read(&ilits->ice_stat)) offset = 2; duplex_len = n_tx + n_rx + offset; if ((duplex_len > SPI_TX_BUF_SIZE) || (duplex_len > SPI_RX_BUF_SIZE)) { input_err(true, ilits->dev, "%s duplex_len is over than dma buf, abort\n", __func__); status = -ENOMEM; break; } memset(ilits->spi_tx, 0x0, SPI_TX_BUF_SIZE); memset(ilits->spi_rx, 0x0, SPI_RX_BUF_SIZE); xfer.len = duplex_len; memcpy(ilits->spi_tx, txbuf, n_tx); xfer.tx_buf = ilits->spi_tx; xfer.rx_buf = ilits->spi_rx; spi_message_add_tail(&xfer, &message); if (ilits->cs_gpio > 0) { gpio_direction_output(ilits->cs_gpio, 0); status = spi_sync(spi, &message); gpio_direction_output(ilits->cs_gpio, 1); } else { status = spi_sync(spi, &message); } if (status == 0) { if (ilits->spi_rx[1] != SPI_ACK && !atomic_read(&ilits->ice_stat)) { status = DO_SPI_RECOVER; input_err(true, ilits->dev, "%s Do spi recovery: rxbuf[1] = 0x%x, ice = %d\n", __func__, ilits->spi_rx[1], atomic_read(&ilits->ice_stat)); break; } memcpy((u8 *)rxbuf, ilits->spi_rx + offset + 1, n_rx); } else { input_err(true, ilits->dev, "%s spi read fail, status = %d\n", __func__, status); } break; default: input_info(true, ilits->dev, "%s Unknown command 0x%x\n", __func__, cmd); break; } out: return status; } #endif static int ili_spi_mp_pre_cmd(u8 cdc) { u8 pre[5] = {0}; if (ilits->power_status == POWER_OFF_STATUS) { input_err(true, ilits->dev, "%s failed(power off state).\n", __func__); return -1; } if (!atomic_read(&ilits->mp_stat) || cdc != P5_X_SET_CDC_INIT || ilits->chip->core_ver >= CORE_VER_1430) return 0; ILI_DBG("%s mp test with pre commands\n", __func__); pre[0] = SPI_WRITE; pre[1] = 0x0;// dummy byte pre[2] = 0x2;// Write len byte pre[3] = P5_X_READ_DATA_CTRL; pre[4] = P5_X_GET_CDC_DATA; if (ilits->spi_write_then_read(ilits->spi, pre, 5, NULL, 0) < 0) { input_err(true, ilits->dev, "%s Failed to write pre commands\n", __func__); return -1; } pre[0] = SPI_WRITE; pre[1] = 0x0;// dummy byte pre[2] = 0x1;// Write len byte pre[3] = P5_X_GET_CDC_DATA; if (ilits->spi_write_then_read(ilits->spi, pre, 4, NULL, 0) < 0) { input_err(true, ilits->dev, "%s Failed to write pre commands\n", __func__); return -1; } return 0; } static int ili_spi_pll_clk_wakeup(void) { int index = 0; u8 wdata[32] = {0}; u8 wakeup[9] = {0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3}; u32 wlen = sizeof(wakeup); if (ilits->power_status == POWER_OFF_STATUS) { input_err(true, ilits->dev, "%s failed(power off state).\n", __func__); return -1; } wdata[0] = SPI_WRITE; wdata[1] = wlen >> 8; wdata[2] = wlen & 0xff; index = 3; wlen += index; ipio_memcpy(&wdata[index], wakeup, wlen, wlen); input_info(true, ilits->dev, "%s Write dummy to wake up spi pll clk\n", __func__); if (ilits->spi_write_then_read(ilits->spi, wdata, wlen, NULL, 0) < 0) { input_info(true, ilits->dev, "%s spi slave write error\n", __func__); return -1; } return 0; } static int ili_spi_wrapper(u8 *txbuf, u32 wlen, u8 *rxbuf, u32 rlen, bool spi_irq, bool i2c_irq) { int ret = 0; int mode = 0, index = 0; u8 wdata[32] = {0}; u8 checksum = 0; bool ice = atomic_read(&ilits->ice_stat); if (ilits->power_status == POWER_OFF_STATUS) { input_err(true, ilits->dev, "%s failed(power off state).\n", __func__); return -1; } if (wlen > 0) { if (!txbuf) { input_err(true, ilits->dev, "%s txbuf is null\n", __func__); return -ENOMEM; } /* 3 bytes data consist of length and header */ if ((wlen + 4) > sizeof(wdata)) { input_err(true, ilits->dev, "%s WARNING! wlen(%d) > wdata(%d), using wdata length to transfer\n", __func__, wlen, (int)sizeof(wdata)); return -ENOMEM; } } if (rlen > 0) { if (!rxbuf) { input_err(true, ilits->dev, "%s rxbuf is null\n", __func__); return -ENOMEM; } } if (rlen > 0 && !wlen) mode = SPI_READ; else mode = SPI_WRITE; if (ilits->int_pulse) ilits->detect_int_stat = ili_ic_check_int_pulse; else ilits->detect_int_stat = ili_ic_check_int_level; if (spi_irq) atomic_set(&ilits->cmd_int_check, ENABLE); switch (mode) { case SPI_WRITE: #if (PLL_CLK_WAKEUP_TP_RESUME == ENABLE) if (ilits->pll_clk_wakeup == true) { #else if ((ilits->pll_clk_wakeup == true) && (ilits->tp_suspend == true)) { #endif ret = ili_spi_pll_clk_wakeup(); if (ret < 0) { input_err(true, ilits->dev, "%s Wakeup pll clk error\n", __func__); break; } } if (ice) { wdata[0] = SPI_WRITE; index = 1; } else { wdata[0] = SPI_WRITE; wdata[1] = wlen >> 8; wdata[2] = wlen & 0xff; index = 3; } wlen += index; ipio_memcpy(&wdata[index], txbuf, wlen, wlen); /* * NOTE: If TP driver is doing MP test and commanding 0xF1 to FW, we add a checksum * to the last index and plus 1 with size. */ if (atomic_read(&ilits->mp_stat) && wdata[index] == P5_X_SET_CDC_INIT) { checksum = ili_calc_packet_checksum(&wdata[index], wlen - index); wdata[wlen] = checksum; wlen++; wdata[1] = (wlen - index) >> 8; wdata[2] = (wlen - index) & 0xff; ili_dump_data(wdata, 8, wlen, 0, "mp cdc cmd with checksum"); } ret = ilits->spi_write_then_read(ilits->spi, wdata, wlen, txbuf, 0); if (ret < 0) { input_info(true, ilits->dev, "%s spi-wrapper write error\n", __func__); break; } /* Won't break if it needs to read data following with writing. */ if (!rlen) break; case SPI_READ: if (!ice && spi_irq) { /* Check INT triggered by FW when sending cmds. */ if (ilits->detect_int_stat(false) < 0) { input_err(true, ilits->dev, "%s ERROR! Check INT timeout\n", __func__); ret = -ETIME; break; } } ret = ili_spi_mp_pre_cmd(wdata[3]); if (ret < 0) input_err(true, ilits->dev, "%s spi-wrapper mp pre cmd error\n", __func__); wdata[0] = SPI_READ; ret = ilits->spi_write_then_read(ilits->spi, wdata, 1, rxbuf, rlen); if (ret < 0) input_err(true, ilits->dev, "%s spi-wrapper read error\n", __func__); break; /* * DEADCODE : prevent major issue. * default: * input_err(true, ilits->dev, "%s Unknown spi mode (%d)\n", __func__, mode); * ret = -EINVAL; * break; */ } if (spi_irq) atomic_set(&ilits->cmd_int_check, DISABLE); return ret; } int ili_core_spi_setup(int num) { u32 freq[] = { TP_SPI_CLK_1M, TP_SPI_CLK_2M, TP_SPI_CLK_3M, TP_SPI_CLK_4M, TP_SPI_CLK_5M, TP_SPI_CLK_6M, TP_SPI_CLK_7M, TP_SPI_CLK_8M, TP_SPI_CLK_9M, TP_SPI_CLK_10M, TP_SPI_CLK_11M, TP_SPI_CLK_12M, TP_SPI_CLK_13M, TP_SPI_CLK_14M, TP_SPI_CLK_15M }; if (num >= (sizeof(freq) / sizeof(freq[0]))) { input_err(true, ilits->dev, "%s Invaild clk freq, set default clk freq\n", __func__); num = 7; } input_info(true, ilits->dev, "%s spi clock = %d\n", __func__, freq[num]); ilits->spi->mode = ilits->spi_mode; ilits->spi->bits_per_word = 8; ilits->spi->max_speed_hz = freq[num]; if (spi_setup(ilits->spi) < 0) { input_err(true, ilits->dev, "%s Failed to setup spi device\n", __func__); return -ENODEV; } input_info(true, ilits->dev, "%s name = %s, bus_num = %d,cs = %d, mode = %d, speed = %d\n", __func__, ilits->spi->modalias, ilits->spi->master->bus_num, ilits->spi->chip_select, ilits->spi->mode, ilits->spi->max_speed_hz); return 0; } static int ilitek_spi_probe(struct spi_device *spi) { struct touch_bus_info *info; input_info(true, &spi->dev, "%s ilitek spi probe\n", __func__); if (!spi) { input_err(true, &spi->dev, "%s spi device is NULL\n", __func__); return -ENODEV; } info = container_of(to_spi_driver(spi->dev.driver), struct touch_bus_info, bus_driver); ilits = devm_kzalloc(&spi->dev, sizeof(struct ilitek_ts_data), GFP_KERNEL); if (ERR_ALLOC_MEM(ilits)) { input_err(true, &spi->dev, "%s Failed to allocate ts memory, %ld\n", __func__, PTR_ERR(ilits)); return -ENOMEM; } if (spi->master->flags & SPI_MASTER_HALF_DUPLEX) { input_err(true, &spi->dev, "%s Full duplex not supported by master\n", __func__); return -EIO; } ilits->update_buf = kzalloc(MAX_HEX_FILE_SIZE, GFP_KERNEL | GFP_DMA); if (ERR_ALLOC_MEM(ilits->update_buf)) { input_err(true, &spi->dev, "%s fw kzalloc error\n", __func__); return -ENOMEM; } /* Used for receiving touch data only, do not mix up with others. */ ilits->tr_buf = kzalloc(TR_BUF_SIZE, GFP_ATOMIC); if (ERR_ALLOC_MEM(ilits->tr_buf)) { input_err(true, &spi->dev, "%s failed to allocate touch report buffer\n", __func__); return -ENOMEM; } ilits->spi_tx = kzalloc(SPI_TX_BUF_SIZE, GFP_KERNEL | GFP_DMA); if (ERR_ALLOC_MEM(ilits->spi_tx)) { input_err(true, &spi->dev, "%s Failed to allocate spi tx buffer\n", __func__); return -ENOMEM; } ilits->spi_rx = kzalloc(SPI_RX_BUF_SIZE, GFP_KERNEL | GFP_DMA); if (ERR_ALLOC_MEM(ilits->spi_rx)) { input_err(true, &spi->dev, "%s Failed to allocate spi rx buffer\n", __func__); return -ENOMEM; } ilits->gcoord = kzalloc(sizeof(struct gesture_coordinate), GFP_KERNEL); if (ERR_ALLOC_MEM(ilits->gcoord)) { input_err(true, &spi->dev, "%s Failed to allocate gresture coordinate buffer\n", __func__); return -ENOMEM; } ilits->i2c = NULL; ilits->spi = spi; ilits->dev = &spi->dev; ilits->hwif = info->hwif; ilits->phys = "SPI"; ilits->wrapper = ili_spi_wrapper; ilits->detect_int_stat = ili_ic_check_int_pulse; ilits->int_pulse = true; ilits->mp_retry = false; ilits->tp_suspend = false; ilits->power_status = POWER_ON_STATUS; ilits->screen_off_sate = TP_RESUME; #if SPI_DMA_TRANSFER_SPLIT ilits->spi_write_then_read = ili_spi_write_then_read_split; #else ilits->spi_write_then_read = ili_spi_write_then_read_direct; #endif ilits->actual_tp_mode = P5_X_FW_AP_MODE; 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->tp_data_mode = AP_MODE; if (TDDI_RST_BIND) ilits->reset = TP_IC_WHOLE_RST; else ilits->reset = TP_HW_RST_ONLY; ilits->rst_edge_delay = 11; ilits->fw_open = REQUEST_FIRMWARE; ilits->fw_upgrade_mode = UPGRADE_IRAM; ilits->mp_move_code = ili_move_mp_code_iram; ilits->gesture_move_code = ili_move_gesture_code_iram; ilits->esd_recover = ili_wq_esd_spi_check; ilits->ges_recover = ili_touch_esd_gesture_iram; ilits->gesture_mode = DATA_FORMAT_GESTURE_INFO; ilits->gesture_demo_ctrl = DISABLE; ilits->wtd_ctrl = OFF; ilits->report = ENABLE; ilits->dnp = DISABLE; ilits->irq_tirgger_type = IRQF_TRIGGER_FALLING; ilits->info_from_hex = ENABLE; ilits->wait_int_timeout = AP_INT_TIMEOUT; ilits->prox_lp_scan_mode = false; ilits->started_prox_intensity = false; ilits->incell_power_state = false; ilits->prox_face_mode = false; ilits->dead_zone_enabled = true; //default true at fw ilits->sip_mode_enabled = false; ilits->prox_lp_scan_mode_enabled = false; ilits->sleep_handler_mode = TP_RESUME; #if ENABLE_GESTURE ilits->gesture = DISABLE; ilits->ges_sym.double_tap = DOUBLE_TAP; ilits->ges_sym.alphabet_line_2_top = ALPHABET_LINE_2_TOP; ilits->ges_sym.alphabet_line_2_bottom = ALPHABET_LINE_2_BOTTOM; ilits->ges_sym.alphabet_line_2_left = ALPHABET_LINE_2_LEFT; ilits->ges_sym.alphabet_line_2_right = ALPHABET_LINE_2_RIGHT; ilits->ges_sym.alphabet_m = ALPHABET_M; ilits->ges_sym.alphabet_w = ALPHABET_W; ilits->ges_sym.alphabet_c = ALPHABET_C; ilits->ges_sym.alphabet_E = ALPHABET_E; ilits->ges_sym.alphabet_V = ALPHABET_V; ilits->ges_sym.alphabet_O = ALPHABET_O; ilits->ges_sym.alphabet_S = ALPHABET_S; ilits->ges_sym.alphabet_Z = ALPHABET_Z; ilits->ges_sym.alphabet_V_down = ALPHABET_V_DOWN; ilits->ges_sym.alphabet_V_left = ALPHABET_V_LEFT; ilits->ges_sym.alphabet_V_right = ALPHABET_V_RIGHT; ilits->ges_sym.alphabet_two_line_2_bottom = ALPHABET_TWO_LINE_2_BOTTOM; ilits->ges_sym.alphabet_F = ALPHABET_F; ilits->ges_sym.alphabet_AT = ALPHABET_AT; #endif if (ili_core_spi_setup(SPI_CLK) < 0) return -EINVAL; return info->hwif->plat_probe(); } static void ilitek_spi_shutdown(struct spi_device *spi) { input_info(true, ilits->dev, "%s\n", __func__); ilits->hwif->plat_shutdown(); return; } static int ilitek_spi_remove(struct spi_device *spi) { input_info(true, ilits->dev, "%s\n", __func__); return 0; } static struct spi_device_id tp_spi_id[] = { {TDDI_DEV_ID, 0}, {}, }; int ili_interface_dev_init(struct ilitek_hwif_info *hwif) { struct touch_bus_info *info; info = kzalloc(sizeof(*info), GFP_KERNEL); if (!info) { printk(KERN_ERR "[sec_input] %s faied to allocate spi_driver\n", __func__); return -ENOMEM; } if (hwif->bus_type != BUS_SPI) { printk(KERN_ERR "[sec_input] %s Not SPI dev\n", __func__); ipio_kfree((void **)&info); return -EINVAL; } hwif->info = info; info->bus_driver.driver.name = hwif->name; info->bus_driver.driver.owner = hwif->owner; info->bus_driver.driver.of_match_table = hwif->of_match_table; info->bus_driver.driver.pm = hwif->pm; info->bus_driver.probe = ilitek_spi_probe; info->bus_driver.shutdown = ilitek_spi_shutdown; info->bus_driver.remove = ilitek_spi_remove; info->bus_driver.id_table = tp_spi_id; info->hwif = hwif; return spi_register_driver(&info->bus_driver); } void ili_interface_dev_exit(struct ilitek_ts_data *ts) { struct touch_bus_info *info = (struct touch_bus_info *)ilits->hwif->info; input_info(true, ilits->dev, "%s remove spi dev\n", __func__); kfree(ilits->update_buf); kfree(ilits->spi_tx); kfree(ilits->spi_rx); // spi_unregister_driver(&info->bus_driver); ipio_kfree((void **)&info); }