ilitek_0flash_mmi: add ili9881x initial driver

ilitek_0flash_mmi:
Add ili9881x initial driver.

Change-Id: I2f9373cbcdbfb4eb9f7cc0c521977da77634e919
Signed-off-by: wangyq13 <wangyq13@lenovo.com>
Reviewed-on: https://gerrit.mot.com/1584344
SME-Granted: SME Approvals Granted
SLTApproved: Slta Waiver
Tested-by: Jira Key
Reviewed-by: Huosheng Liao <liaohs@motorola.com>
Submit-Approved: Jira Key
This commit is contained in:
wangyq13 2020-05-28 15:01:24 +08:00 • committed by Yeqing Wang
commit 1f94cddbdd
15 changed files with 14528 additions and 0 deletions

View file

@ -0,0 +1,8 @@
DLKM_DIR := motorola/kernel/modules
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := ilitek_0flash_mmi.ko
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT)
include $(DLKM_DIR)/AndroidKernelModule.mk

View file

@ -0,0 +1,17 @@
# add -Wall to try to catch everything we can.
EXTRA_CFLAGS += -Wall
EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include \
-I$(TOP)/motorola/kernel/modules/drivers/input/touchscreen/ilitek_0flash_mmi
ifeq ($(filter m y, $(CONFIG_TOUCHSCREEN_ILITEK_CHIPSET)),)
obj-m += ilitek_0flash_mmi.o
endif
ilitek_0flash_mmi-objs += ilitek.o
ilitek_0flash_mmi-objs += ilitek_spi.o
ilitek_0flash_mmi-objs += ilitek_qcom.o
ilitek_0flash_mmi-objs += ilitek_ic.o
ilitek_0flash_mmi-objs += ilitek_touch.o
ilitek_0flash_mmi-objs += ilitek_mp.o
ilitek_0flash_mmi-objs += ilitek_hostdl.o
ilitek_0flash_mmi-objs += ilitek_node.o

View file

@ -0,0 +1,10 @@
KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build
all:
$(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS)
modules_install:
$(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install
clean:
$(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean

View file

@ -0,0 +1,90 @@
/*
* ILITEK Touch IC driver
*
* Copyright (C) 2011 ILI Technology Corporation.
*
* Author: Dicky Chiang <dicky_chiang@ilitek.com>
*
* 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
*/
#ifndef __ILI9881X_FW_H
#define __ILI9881X_FW_H
/* define names and paths for the variety of tp modules */
#define DEF_INI_NAME_PATH "/sdcard/mp.ini"
#define DEF_FW_FILP_PATH "/sdcard/ILITEK_FW"
#define DEF_INI_REQUEST_PATH "mp.ini"
#define DEF_FW_REQUEST_PATH "ilitek_fw.bin"
static unsigned char CTPM_FW_DEF[] = {
};
#define CSOT_INI_NAME_PATH "/sdcard/mp_csot.ini"
#define CSOT_FW_FILP_PATH "/sdcard/ILITEK_FW_CSOT"
#define CSOT_INI_REQUEST_PATH "mp_csot.ini"
#define CSOT_FW_REQUEST_PATH "ILITEK_FW_CSOT"
static unsigned char CTPM_FW_CSOT[] = {
0xFF,
};
#define AUO_INI_NAME_PATH "/sdcard/mp_auo.ini"
#define AUO_FW_FILP_PATH "/sdcard/ILITEK_FW_AUO"
#define AUO_INI_REQUEST_PATH "mp_auo.ini"
#define AUO_FW_REQUEST_PATH "ILITEK_FW_AUO"
static unsigned char CTPM_FW_AUO[] = {
0xFF,
};
#define BOE_INI_NAME_PATH "/sdcard/mp_boe.ini"
#define BOE_FW_FILP_PATH "/sdcard/ILITEK_FW_BOE"
#define BOE_INI_REQUEST_PATH "mp_boe.ini"
#define BOE_FW_REQUEST_PATH "ILITEK_FW_BOE"
static unsigned char CTPM_FW_BOE[] = {
0xFF,
};
#define INX_INI_NAME_PATH "/sdcard/mp_inx.ini"
#define INX_FW_FILP_PATH "/sdcard/ILITEK_FW_INX"
#define INX_INI_REQUEST_PATH "mp_inx.ini"
#define INX_FW_REQUEST_PATH "ILITEK_FW_INX"
static unsigned char CTPM_FW_INX[] = {
0xFF,
};
#define DJ_INI_NAME_PATH "/sdcard/mp_dj.ini"
#define DJ_FW_FILP_PATH "/sdcard/ILITEK_FW_DJ"
#define DJ_INI_REQUEST_PATH "mp_dj.ini"
#define DJ_FW_REQUEST_PATH "ILITEK_FW_DJ"
static unsigned char CTPM_FW_DJ[] = {
0xFF,
};
#define TXD_INI_NAME_PATH "/sdcard/mp_txd.ini"
#define TXD_FW_FILP_PATH "/sdcard/ILITEK_FW_TXD"
#define TXD_INI_REQUEST_PATH "mp_txd.ini"
#define TXD_FW_REQUEST_PATH "ILITEK_FW_TXD"
static unsigned char CTPM_FW_TXD[] = {
0xFF,
};
#define TM_INI_NAME_PATH "/sdcard/mp_tm.ini"
#define TM_FW_FILP_PATH "/sdcard/ILITEK_FW_TM"
#define TM_INI_REQUEST_PATH "mp_tm.ini"
#define TM_FW_REQUEST_PATH "ILITEK_FW_TM"
static unsigned char CTPM_FW_TM[] = {
0xFF,
};
#endif

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,815 @@
/*
* ILITEK Touch IC driver
*
* Copyright (C) 2011 ILI Technology Corporation.
*
* Author: Dicky Chiang <dicky_chiang@ilitek.com>
*
* 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 "ilitek.h"
#define UPDATE_PASS 0
#define UPDATE_FAIL -1
#define TIMEOUT_SECTOR 500
#define TIMEOUT_PAGE 3500
#define TIMEOUT_PROGRAM 10
static struct touch_fw_data {
u8 block_number;
u32 start_addr;
u32 end_addr;
u32 new_fw_cb;
int delay_after_upgrade;
bool isCRC;
bool isboot;
bool is80k;
int hex_tag;
} tfd;
static struct flash_block_info {
char *name;
u32 start;
u32 end;
u32 len;
u32 mem_start;
u32 fix_mem_start;
u8 mode;
} fbi[FW_BLOCK_INFO_NUM];
static u8 *pfw;
static u32 HexToDec(char *phex, s32 len)
{
u32 ret = 0, temp = 0, i;
s32 shift = (len - 1) * 4;
for (i = 0; i < len; shift -= 4, i++) {
if ((phex[i] >= '0') && (phex[i] <= '9'))
temp = phex[i] - '0';
else if ((phex[i] >= 'a') && (phex[i] <= 'f'))
temp = (phex[i] - 'a') + 10;
else if ((phex[i] >= 'A') && (phex[i] <= 'F'))
temp = (phex[i] - 'A') + 10;
else
return -1;
ret |= (temp << shift);
}
return ret;
}
static int CalculateCRC32(u32 start_addr, u32 len, u8 *pfw)
{
int i = 0, j = 0;
int crc_poly = 0x04C11DB7;
int tmp_crc = 0xFFFFFFFF;
for (i = start_addr; i < start_addr + len; i++) {
tmp_crc ^= (pfw[i] << 24);
for (j = 0; j < 8; j++) {
if ((tmp_crc & 0x80000000) != 0)
tmp_crc = tmp_crc << 1 ^ crc_poly;
else
tmp_crc = tmp_crc << 1;
}
}
return tmp_crc;
}
static int calc_hw_dma_crc(u32 start_addr, u32 block_size)
{
int count = 50;
u32 busy = 0;
/* dma1 src1 address */
if (ili_ice_mode_write(0x072104, start_addr, 4) < 0)
ILI_ERR("Write dma1 src1 address failed\n");
/* dma1 src1 format */
if (ili_ice_mode_write(0x072108, 0x80000001, 4) < 0)
ILI_ERR("Write dma1 src1 format failed\n");
/* dma1 dest address */
if (ili_ice_mode_write(0x072114, 0x0002725C, 4) < 0)
ILI_ERR("Write dma1 src1 format failed\n");
/* dma1 dest format */
if (ili_ice_mode_write(0x072118, 0x80000000, 4) < 0)
ILI_ERR("Write dma1 dest format failed\n");
/* Block size*/
if (ili_ice_mode_write(0x07211C, block_size, 4) < 0)
ILI_ERR("Write block size (%d) failed\n", block_size);
/* crc off */
if (ili_ice_mode_write(0x041016, 0x00, 1) < 0)
ILI_INFO("Write crc of failed\n");
/* dma crc */
if (ili_ice_mode_write(0x041017, 0x03, 1) < 0)
ILI_ERR("Write dma 1 crc failed\n");
/* crc on */
if (ili_ice_mode_write(0x041016, 0x01, 1) < 0)
ILI_ERR("Write crc on failed\n");
/* Dma1 stop */
if (ili_ice_mode_write(0x072100, 0x00000000, 4) < 0)
ILI_ERR("Write dma1 stop failed\n");
/* clr int */
if (ili_ice_mode_write(0x048006, 0x2, 1) < 0)
ILI_ERR("Write clr int failed\n");
/* Dma1 start */
if (ili_ice_mode_write(0x072100, 0x01000000, 4) < 0)
ILI_ERR("Write dma1 start failed\n");
/* Polling BIT0 */
while (count > 0) {
mdelay(1);
if (ili_ice_mode_read(0x048006, &busy, sizeof(u8)) < 0)
ILI_ERR("Read busy error\n");
ILI_DBG("busy = %x\n", busy);
if ((busy & 0x02) == 2)
break;
count--;
}
if (count <= 0) {
ILI_ERR("BIT0 is busy\n");
return -1;
}
if (ili_ice_mode_read(0x04101C, &busy, sizeof(u32)) < 0) {
ILI_ERR("Read dma crc error\n");
return -1;
}
return busy;
}
static int ilitek_tddi_fw_iram_read(u8 *buf, u32 start, int len)
{
int limit = SPI_RX_BUF_SIZE;
int addr = 0, loop = 0, tmp_len = len, cnt = 0;
u8 cmd[4] = {0};
if (!buf) {
ILI_ERR("buf is null\n");
return -ENOMEM;
}
if (len % limit)
loop = (len / limit) + 1;
else
loop = len / limit;
for (cnt = 0, addr = start; cnt < loop; cnt++, addr += limit) {
if (tmp_len > limit)
tmp_len = limit;
cmd[0] = 0x25;
cmd[3] = (char)((addr & 0x00FF0000) >> 16);
cmd[2] = (char)((addr & 0x0000FF00) >> 8);
cmd[1] = (char)((addr & 0x000000FF));
if (ilits->wrapper(cmd, 4, NULL, 0, OFF, OFF) < 0) {
ILI_ERR("Failed to write iram data\n");
return -ENODEV;
}
if (ilits->wrapper(NULL, 0, buf + cnt * limit, tmp_len, OFF, OFF) < 0) {
ILI_ERR("Failed to Read iram data\n");
return -ENODEV;
}
tmp_len = len - cnt * limit;
ilits->fw_update_stat = ((len - tmp_len) * 100) / len;
ILI_INFO("Reading iram data .... %d%c", ilits->fw_update_stat, '%');
}
return 0;
}
int ili_fw_dump_iram_data(u32 start, u32 end, bool save)
{
struct file *f = NULL;
mm_segment_t old_fs;
loff_t pos = 0;
int i, ret = 0;
int len, tmp = debug_en;
bool ice = atomic_read(&ilits->ice_stat);
if (!ice) {
ret = ili_ice_mode_ctrl(ENABLE, OFF);
if (ret < 0) {
ILI_ERR("Enable ice mode failed\n");
return ret;
}
}
len = end - start + 1;
if (len > MAX_HEX_FILE_SIZE) {
ILI_ERR("len is larger than buffer, abort\n");
ret = -EINVAL;
goto out;
}
for (i = 0; i < MAX_HEX_FILE_SIZE; i++)
ilits->update_buf[i] = 0xFF;
ret = ilitek_tddi_fw_iram_read(ilits->update_buf, start, len);
if (ret < 0) {
ILI_ERR("Read IRAM data failed\n");
goto out;
}
if (save) {
f = filp_open(DUMP_IRAM_PATH, O_WRONLY | O_CREAT | O_TRUNC, 644);
if (ERR_ALLOC_MEM(f)) {
ILI_ERR("Failed to open the file at %ld.\n", PTR_ERR(f));
ret = -ENOMEM;
goto out;
}
old_fs = get_fs();
set_fs(get_ds());
set_fs(KERNEL_DS);
pos = 0;
vfs_write(f, ilits->update_buf, len, &pos);
set_fs(old_fs);
filp_close(f, NULL);
ILI_INFO("Save iram data to %s\n", DUMP_IRAM_PATH);
} else {
debug_en = DEBUG_ALL;
ili_dump_data(ilits->update_buf, 8, len, 0, "IRAM");
debug_en = tmp;
}
out:
if (!ice) {
if (ili_ice_mode_ctrl(DISABLE, OFF) < 0)
ILI_ERR("Enable ice mode failed after code reset\n");
}
ILI_INFO("Dump IRAM %s\n", (ret < 0) ? "FAIL" : "SUCCESS");
return ret;
}
static int ilitek_tddi_fw_iram_program(u32 start, u8 *w_buf, u32 w_len, u32 split_len)
{
int i = 0, j = 0, addr = 0;
u32 end = start + w_len;
bool fix_4_alignment = false;
if (split_len % 4 > 0)
ILI_ERR("Since split_len must be four-aligned, it must be a multiple of four");
if (split_len != 0) {
for (addr = start, i = 0; addr < end; addr += split_len, i += split_len) {
if ((addr + split_len) > end) {
split_len = end - addr;
if (split_len % 4 != 0)
fix_4_alignment = true;
}
ilits->update_buf[0] = SPI_WRITE;
ilits->update_buf[1] = 0x25;
ilits->update_buf[2] = (char)((addr & 0x000000FF));
ilits->update_buf[3] = (char)((addr & 0x0000FF00) >> 8);
ilits->update_buf[4] = (char)((addr & 0x00FF0000) >> 16);
for (j = 0; j < split_len; j++)
ilits->update_buf[5 + j] = w_buf[i + j];
if (fix_4_alignment) {
ILI_INFO("org split_len = 0x%X\n", split_len);
ILI_INFO("idev->update_buf[5 + 0x%X] = 0x%X\n", split_len - 4, ilits->update_buf[5 + split_len - 4]);
ILI_INFO("idev->update_buf[5 + 0x%X] = 0x%X\n", split_len - 3, ilits->update_buf[5 + split_len - 3]);
ILI_INFO("idev->update_buf[5 + 0x%X] = 0x%X\n", split_len - 2, ilits->update_buf[5 + split_len - 2]);
ILI_INFO("idev->update_buf[5 + 0x%X] = 0x%X\n", split_len - 1, ilits->update_buf[5 + split_len - 1]);
for (j = 0; j < (4 - (split_len % 4)); j++) {
ilits->update_buf[5 + j + split_len] = 0xFF;
ILI_INFO("idev->update_buf[5 + 0x%X] = 0x%X\n",j + split_len, ilits->update_buf[5 + j + split_len]);
}
ILI_INFO("split_len %% 4 = %d\n", split_len % 4);
split_len = split_len + (4 - (split_len % 4));
ILI_INFO("fix split_len = 0x%X\n", split_len);
}
if (ilits->spi_write_then_read(ilits->spi, ilits->update_buf, split_len + 5, NULL, 0)) {
ILI_ERR("Failed to write data via SPI in host download (%x)\n", split_len + 5);
return -EIO;
}
ilits->fw_update_stat = (i * 100) / w_len;
}
} else {
for (i = 0; i < MAX_HEX_FILE_SIZE; i++)
ilits->update_buf[i] = 0xFF;
ilits->update_buf[0] = SPI_WRITE;
ilits->update_buf[1] = 0x25;
ilits->update_buf[2] = (char)((start & 0x000000FF));
ilits->update_buf[3] = (char)((start & 0x0000FF00) >> 8);
ilits->update_buf[4] = (char)((start & 0x00FF0000) >> 16);
memcpy(&ilits->update_buf[5], w_buf, w_len);
if (w_len % 4 != 0) {
ILI_INFO("org w_len = %d\n", w_len);
w_len = w_len + (4 - (w_len % 4));
ILI_INFO("w_len = %d w_len %% 4 = %d\n", w_len, w_len % 4);
}
/* It must be supported by platforms that have the ability to transfer all data at once. */
if (ilits->spi_write_then_read(ilits->spi, ilits->update_buf, w_len + 5, NULL, 0) < 0) {
ILI_ERR("Failed to write data via SPI in host download (%x)\n", w_len + 5);
return -EIO;
}
}
return 0;
}
static int ilitek_tddi_fw_iram_upgrade(u8 *pfw)
{
int i, ret = UPDATE_PASS;
u32 mode, crc, dma, iram_crc = 0;
u8 *fw_ptr = NULL, crc_temp[4], crc_len = 4;
bool iram_crc_err = false;
if (!ilits->ddi_rest_done) {
if (ilits->actual_tp_mode != P5_X_FW_GESTURE_MODE)
ili_reset_ctrl(ilits->reset);
ret = ili_ice_mode_ctrl(ENABLE, OFF);
if (ret < 0)
return -EFW_ICE_MODE;
} else {
/* Restore it if the wq of load_fw_ddi has been called. */
ilits->ddi_rest_done = false;
}
if (ilits->chip->dma_reset) {
ILI_INFO("operate dma reset in reg after tp reset\n");
if (ili_ice_mode_write(0x40040, 0x00800000, 4) < 0)
ILI_INFO("Failed to open DMA reset\n");
if (ili_ice_mode_write(0x40040, 0x00000000, 4) < 0)
ILI_INFO("Failed to close DMA reset\n");
}
/* Point to pfw with different addresses for getting its block data. */
fw_ptr = pfw;
if (ilits->actual_tp_mode == P5_X_FW_TEST_MODE) {
mode = MP;
} else if (ilits->actual_tp_mode == P5_X_FW_GESTURE_MODE) {
mode = GESTURE;
crc_len = 0;
} else {
mode = AP;
}
/* Program data to iram acorrding to each block */
for (i = 0; i < FW_BLOCK_INFO_NUM; i++) {
if (fbi[i].mode == mode && fbi[i].len != 0) {
ILI_DBG("Download %s code from hex 0x%x to IRAM 0x%x, len = 0x%x\n",
fbi[i].name, fbi[i].start, fbi[i].mem_start, fbi[i].len);
#if SPI_DMA_TRANSFER_SPLIT
if (ilitek_tddi_fw_iram_program(fbi[i].mem_start, (fw_ptr + fbi[i].start), fbi[i].len, SPI_UPGRADE_LEN) < 0)
ILI_ERR("IRAM program failed\n");
#else
if (ilitek_tddi_fw_iram_program(fbi[i].mem_start, (fw_ptr + fbi[i].start), fbi[i].len, 0) < 0)
ILI_ERR("IRAM program failed\n");
#endif
crc = CalculateCRC32(fbi[i].start, fbi[i].len - crc_len, fw_ptr);
dma = calc_hw_dma_crc(fbi[i].mem_start, fbi[i].len - crc_len);
if (mode != GESTURE) {
ilitek_tddi_fw_iram_read(crc_temp, (fbi[i].mem_start + fbi[i].len - crc_len), sizeof(crc_temp));
iram_crc = crc_temp[0] << 24 | crc_temp[1] << 16 |crc_temp[2] << 8 | crc_temp[3];
if (iram_crc != dma)
iram_crc_err = true;
}
ILI_INFO("%s CRC is %s hex(%x) : dma(%x) : iram(%x), calculation len is 0x%x\n",
fbi[i].name,((crc != dma)||(iram_crc_err)) ? "Invalid !" : "Correct !", crc, dma, iram_crc, fbi[i].len - crc_len);
if ((crc != dma)|| iram_crc_err) {
ILI_ERR("CRC Error! print iram data with first 16 bytes\n");
ili_fw_dump_iram_data(0x0, 0xF, false);
return -EFW_CRC;
}
}
}
if (ilits->actual_tp_mode != P5_X_FW_GESTURE_MODE) {
if (ili_reset_ctrl(TP_IC_CODE_RST) < 0) {
ILI_ERR("TP Code reset failed during iram programming\n");
ret = -EFW_REST;
return ret;
}
}
if (ili_ice_mode_ctrl(DISABLE, OFF) < 0) {
ILI_ERR("Disable ice mode failed after code reset\n");
ret = -EFW_ICE_MODE;
}
/* Waiting for fw ready sending first cmd */
if (!ilits->info_from_hex || (ilits->chip->core_ver < CORE_VER_1410))
mdelay(100);
return ret;
}
static int ilitek_fw_calc_file_crc(u8 *pfw)
{
int i;
u32 ex_addr, data_crc, file_crc;
for (i = 0; i < ARRAY_SIZE(fbi); i++) {
if (fbi[i].end == 0)
continue;
ex_addr = fbi[i].end;
data_crc = CalculateCRC32(fbi[i].start, fbi[i].len - 4, pfw);
file_crc = pfw[ex_addr - 3] << 24 | pfw[ex_addr - 2] << 16 | pfw[ex_addr - 1] << 8 | pfw[ex_addr];
ILI_DBG("data crc = %x, file crc = %x\n", data_crc, file_crc);
if (data_crc != file_crc) {
ILI_ERR("Content of fw file is broken. (%d, %x, %x)\n",
i, data_crc, file_crc);
return -1;
}
}
ILI_INFO("Content of fw file is correct\n");
return 0;
}
static int ilitek_tddi_fw_update_block_info(u8 *pfw)
{
u32 ges_area_section = 0, ges_info_addr = 0, ges_fw_start = 0, ges_fw_end = 0;
u32 ap_end = 0, ap_len = 0;
u32 fw_info_addr = 0, fw_mp_ver_addr = 0;
if (tfd.hex_tag != BLOCK_TAG_AF) {
ILI_ERR("HEX TAG is invalid (0x%X)\n", tfd.hex_tag);
return -EINVAL;
}
fbi[AP].mem_start = (fbi[AP].fix_mem_start != INT_MAX) ? fbi[AP].fix_mem_start : 0;
fbi[DATA].mem_start = (fbi[DATA].fix_mem_start != INT_MAX) ? fbi[DATA].fix_mem_start : DLM_START_ADDRESS;
fbi[TUNING].mem_start = (fbi[TUNING].fix_mem_start != INT_MAX) ? fbi[TUNING].fix_mem_start : fbi[DATA].mem_start + fbi[DATA].len;
fbi[MP].mem_start = (fbi[MP].fix_mem_start != INT_MAX) ? fbi[MP].fix_mem_start : 0;
fbi[GESTURE].mem_start = (fbi[GESTURE].fix_mem_start != INT_MAX) ? fbi[GESTURE].fix_mem_start : 0;
fbi[TAG].mem_start = (fbi[TAG].fix_mem_start != INT_MAX) ? fbi[TAG].fix_mem_start : 0;
fbi[PARA_BACKUP].mem_start = (fbi[PARA_BACKUP].fix_mem_start != INT_MAX) ? fbi[PARA_BACKUP].fix_mem_start : 0;
fbi[DDI].mem_start = (fbi[DDI].fix_mem_start != INT_MAX) ? fbi[DDI].fix_mem_start : 0;
/* Parsing gesture info form AP code */
ges_info_addr = (fbi[AP].end + 1 - 60);
ges_area_section = (pfw[ges_info_addr + 3] << 24) + (pfw[ges_info_addr + 2] << 16) + (pfw[ges_info_addr + 1] << 8) + pfw[ges_info_addr];
fbi[GESTURE].mem_start = (pfw[ges_info_addr + 7] << 24) + (pfw[ges_info_addr + 6] << 16) + (pfw[ges_info_addr + 5] << 8) + pfw[ges_info_addr + 4];
ap_end = (pfw[ges_info_addr + 11] << 24) + (pfw[ges_info_addr + 10] << 16) + (pfw[ges_info_addr + 9] << 8) + pfw[ges_info_addr + 8];
if (ap_end != fbi[GESTURE].mem_start)
ap_len = ap_end - fbi[GESTURE].mem_start + 1;
ges_fw_start = (pfw[ges_info_addr + 15] << 24) + (pfw[ges_info_addr + 14] << 16) + (pfw[ges_info_addr + 13] << 8) + pfw[ges_info_addr + 12];
ges_fw_end = (pfw[ges_info_addr + 19] << 24) + (pfw[ges_info_addr + 18] << 16) + (pfw[ges_info_addr + 17] << 8) + pfw[ges_info_addr + 16];
if (ges_fw_end != ges_fw_start)
fbi[GESTURE].len = ges_fw_end - ges_fw_start;
/* update gesture address */
fbi[GESTURE].start = ges_fw_start;
ILI_INFO("==== Gesture loader info ====\n");
ILI_INFO("gesture move to ap addr => start = 0x%x, ap_end = 0x%x, ap_len = 0x%x\n", fbi[GESTURE].mem_start, ap_end, ap_len);
ILI_INFO("gesture hex addr => start = 0x%x, gesture_end = 0x%x, gesture_len = 0x%x\n", ges_fw_start, ges_fw_end, fbi[GESTURE].len);
ILI_INFO("=============================\n");
fbi[AP].name = "AP";
fbi[DATA].name = "DATA";
fbi[TUNING].name = "TUNING";
fbi[MP].name = "MP";
fbi[GESTURE].name = "GESTURE";
fbi[TAG].name = "TAG";
fbi[PARA_BACKUP].name = "PARA_BACKUP";
fbi[DDI].name = "DDI";
/* upgrade mode define */
fbi[DATA].mode = fbi[AP].mode = fbi[TUNING].mode = AP;
fbi[MP].mode = MP;
fbi[GESTURE].mode = GESTURE;
if (fbi[AP].end > (64*K))
tfd.is80k = true;
/* Copy fw info */
fw_info_addr = fbi[AP].end - INFO_HEX_ST_ADDR;
ILI_INFO("Parsing hex info start addr = 0x%x\n", fw_info_addr);
ipio_memcpy(ilits->fw_info, (pfw + fw_info_addr), sizeof(ilits->fw_info), sizeof(ilits->fw_info));
/* copy fw mp ver */
fw_mp_ver_addr = fbi[MP].end - INFO_MP_HEX_ADDR;
ILI_INFO("Parsing hex mp ver addr = 0x%x\n", fw_mp_ver_addr);
ipio_memcpy(ilits->fw_mp_ver, pfw + fw_mp_ver_addr, sizeof(ilits->fw_mp_ver), sizeof(ilits->fw_mp_ver));
/* copy fw core ver */
ilits->chip->core_ver = (ilits->fw_info[68] << 24) | (ilits->fw_info[69] << 16) |
(ilits->fw_info[70] << 8) | ilits->fw_info[71];
ILI_INFO("New FW Core version = %x\n", ilits->chip->core_ver);
/* Get hex fw vers */
tfd.new_fw_cb = (ilits->fw_info[48] << 24) | (ilits->fw_info[49] << 16) |
(ilits->fw_info[50] << 8) | ilits->fw_info[51];
/* Calculate update address */
ILI_INFO("New FW ver = 0x%x\n", tfd.new_fw_cb);
ILI_INFO("star_addr = 0x%06X, end_addr = 0x%06X, Block Num = %d\n", tfd.start_addr, tfd.end_addr, tfd.block_number);
return 0;
}
static int ilitek_tddi_fw_hex_convert(u8 *phex, int size, u8 *pfw)
{
int block = 0;
u32 i = 0, j = 0, k = 0, num = 0;
u32 len = 0, addr = 0, type = 0;
u32 start_addr = 0x0, end_addr = 0x0, ex_addr = 0;
u32 offset;
memset(fbi, 0x0, sizeof(fbi));
/* Parsing HEX file */
for (; i < size;) {
len = HexToDec(&phex[i + 1], 2);
addr = HexToDec(&phex[i + 3], 4);
type = HexToDec(&phex[i + 7], 2);
if (type == 0x04) {
ex_addr = HexToDec(&phex[i + 9], 4);
} else if (type == 0x02) {
ex_addr = HexToDec(&phex[i + 9], 4);
ex_addr = ex_addr >> 12;
} else if (type == BLOCK_TAG_AF) {
/* insert block info extracted from hex */
tfd.hex_tag = type;
if (tfd.hex_tag == BLOCK_TAG_AF)
num = HexToDec(&phex[i + 9 + 6 + 6], 2);
else
num = 0xFF;
if (num > (FW_BLOCK_INFO_NUM - 1)) {
ILI_ERR("ERROR! block num is larger than its define (%d, %d)\n",
num, FW_BLOCK_INFO_NUM - 1);
return -EINVAL;
}
fbi[num].start = HexToDec(&phex[i + 9], 6);
fbi[num].end = HexToDec(&phex[i + 9 + 6], 6);
fbi[num].fix_mem_start = INT_MAX;
fbi[num].len = fbi[num].end - fbi[num].start + 1;
ILI_DBG("Block[%d]: start_addr = %x, end = %x", num, fbi[num].start, fbi[num].end);
if (num == GESTURE)
ilits->gesture_load_code = true;
block++;
} else if (type == BLOCK_TAG_B0 && tfd.hex_tag == BLOCK_TAG_AF) {
num = HexToDec(&phex[i + 9 + 6], 2);
if (num > (FW_BLOCK_INFO_NUM - 1)) {
ILI_ERR("ERROR! block num is larger than its define (%d, %d)\n",
num, FW_BLOCK_INFO_NUM - 1);
return -EINVAL;
}
fbi[num].fix_mem_start = HexToDec(&phex[i + 9], 6);
ILI_DBG("Tag 0xB0: change Block[%d] to addr = 0x%x\n", num, fbi[num].fix_mem_start);
}
addr = addr + (ex_addr << 16);
if (phex[i + 1 + 2 + 4 + 2 + (len * 2) + 2] == 0x0D)
offset = 2;
else
offset = 1;
if (addr > MAX_HEX_FILE_SIZE) {
ILI_ERR("Invalid hex format %d\n", addr);
return -1;
}
if (type == 0x00) {
end_addr = addr + len;
if (addr < start_addr)
start_addr = addr;
/* fill data */
for (j = 0, k = 0; j < (len * 2); j += 2, k++)
pfw[addr + k] = HexToDec(&phex[i + 9 + j], 2);
}
i += 1 + 2 + 4 + 2 + (len * 2) + 2 + offset;
}
if (ilitek_fw_calc_file_crc(pfw) < 0)
return -1;
tfd.start_addr = start_addr;
tfd.end_addr = end_addr;
tfd.block_number = block;
return 0;
}
static int ilitek_tdd_fw_hex_open(u8 op, u8 *pfw)
{
int ret =0, fsize = 0;
const struct firmware *fw = NULL;
struct file *f = NULL;
mm_segment_t old_fs;
loff_t pos = 0;
ILI_INFO("Open file method = %s, path = %s\n",
op ? "FILP_OPEN" : "REQUEST_FIRMWARE",
op ? ilits->md_fw_filp_path : ilits->md_fw_rq_path);
switch (op) {
case REQUEST_FIRMWARE:
if (request_firmware(&fw, ilits->md_fw_rq_path, ilits->dev) < 0) {
ILI_ERR("Request firmware failed, try again\n");
if (request_firmware(&fw, ilits->md_fw_rq_path, ilits->dev) < 0) {
ILI_ERR("Request firmware failed after retry\n");
ret = -1;
goto out;
}
}
fsize = fw->size;
ILI_INFO("fsize = %d\n", fsize);
if (fsize <= 0) {
ILI_ERR("The size of file is invaild\n");
release_firmware(fw);
ret = -1;
goto out;
}
ilits->tp_fw.size = 0;
ilits->tp_fw.data = vmalloc(fsize);
if (ERR_ALLOC_MEM(ilits->tp_fw.data)) {
ILI_ERR("Failed to allocate tp_fw by vmalloc, try again\n");
ilits->tp_fw.data = vmalloc(fsize);
if (ERR_ALLOC_MEM(ilits->tp_fw.data)) {
ILI_ERR("Failed to allocate tp_fw after retry\n");
release_firmware(fw);
ret = -ENOMEM;
goto out;
}
}
/* Copy fw data got from request_firmware to global */
ipio_memcpy((u8 *)ilits->tp_fw.data, fw->data, fsize * sizeof(*fw->data), fsize);
ilits->tp_fw.size = fsize;
release_firmware(fw);
break;
case FILP_OPEN:
f = filp_open(ilits->md_fw_filp_path, O_RDONLY, 0644);
if (ERR_ALLOC_MEM(f)) {
ILI_ERR("Failed to open the file, %ld\n", PTR_ERR(f));
ret = -1;
goto out;
}
fsize = f->f_inode->i_size;
ILI_INFO("fsize = %d\n", fsize);
if (fsize <= 0) {
ILI_ERR("The size of file is invaild\n");
filp_close(f, NULL);
ret = -1;
goto out;
}
ilits->tp_fw.size = 0;
ilits->tp_fw.data = vmalloc(fsize);
if (ERR_ALLOC_MEM(ilits->tp_fw.data)) {
ILI_ERR("Failed to allocate tp_fw by vmalloc, try again\n");
ilits->tp_fw.data = vmalloc(fsize);
if (ERR_ALLOC_MEM(ilits->tp_fw.data)) {
ILI_ERR("Failed to allocate tp_fw after retry\n");
filp_close(f, NULL);
ret = -ENOMEM;
goto out;
}
}
/* ready to map user's memory to obtain data by reading files */
old_fs = get_fs();
set_fs(get_ds());
set_fs(KERNEL_DS);
pos = 0;
vfs_read(f, (u8 *)ilits->tp_fw.data, fsize, &pos);
set_fs(old_fs);
filp_close(f, NULL);
ilits->tp_fw.size = fsize;
break;
default:
ILI_ERR("Unknown open file method, %d\n", op);
break;
}
if (ERR_ALLOC_MEM(ilits->tp_fw.data) || ilits->tp_fw.size <= 0) {
ILI_ERR("fw data/size is invaild\n");
ret = -1;
goto out;
}
/* Convert hex and copy data from tp_fw.data to pfw */
if (ilitek_tddi_fw_hex_convert((u8 *)ilits->tp_fw.data, ilits->tp_fw.size, pfw) < 0) {
ILI_ERR("Convert hex file failed\n");
ret = -1;
}
out:
ipio_vfree((void **)&(ilits->tp_fw.data));
return ret;
}
int ili_fw_upgrade(int op)
{
int i, ret = 0, retry = 3;
if (!ilits->boot || ilits->force_fw_update || ERR_ALLOC_MEM(pfw)) {
ilits->gesture_load_code = false;
if (ERR_ALLOC_MEM(pfw)) {
ipio_vfree((void **)&pfw);
pfw = vmalloc(MAX_HEX_FILE_SIZE * sizeof(u8));
if (ERR_ALLOC_MEM(pfw)) {
ILI_ERR("Failed to allocate pfw memory, %ld\n", PTR_ERR(pfw));
ipio_vfree((void **)&pfw);
ret = -ENOMEM;
goto out;
}
}
for (i = 0; i < MAX_HEX_FILE_SIZE; i++)
pfw[i] = 0xFF;
if (ilitek_tdd_fw_hex_open(op, pfw) < 0) {
ILI_ERR("Open firmware file fail \n");
}
if (ilitek_tddi_fw_update_block_info(pfw) < 0) {
ret = -EFW_CONVERT_FILE;
goto out;
}
}
do {
ret = ilitek_tddi_fw_iram_upgrade(pfw);
if (ret == UPDATE_PASS)
break;
ILI_ERR("Upgrade failed, do retry!\n");
} while (--retry > 0);
if (ret != UPDATE_PASS) {
ILI_ERR("Failed to upgrade fw %d times, erasing iram\n", retry);
if (ili_reset_ctrl(ilits->reset) < 0)
ILI_ERR("TP reset failed while erasing data\n");
ilits->xch_num = 0;
ilits->ych_num = 0;
return ret;
}
out:
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();
ili_ic_func_ctrl_reset();
return ret;
}
void ili_fw_read_flash_info(void)
{
return;
}
void ili_flash_clear_dma(void)
{
return;
}
void ili_flash_dma_write(u32 start, u32 end, u32 len)
{
return;
}
int ili_fw_dump_flash_data(u32 start, u32 end, bool user)
{
return 0;
}

View file

@ -0,0 +1,448 @@
/*
* ILITEK Touch IC driver
*
* Copyright (C) 2011 ILI Technology Corporation.
*
* Author: Dicky Chiang <dicky_chiang@ilitek.com>
*
* 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 "ilitek.h"
struct touch_bus_info {
struct i2c_driver bus_driver;
struct ilitek_hwif_info *hwif;
};
struct ilitek_ts_data *ilits;
#define RW_SYNC 0
#define R_ONLY 1
#define W_ONLY 2
#if I2C_DMA_TRANSFER
static unsigned char *ilitek_dma_va;
static dma_addr_t ilitek_dma_pa;
#define DMA_VA_BUFFER 4096
static int dma_i2c_alloc(struct i2c_client *client)
{
if (client != NULL) {
client->dev.coherent_dma_mask = DMA_BIT_MASK(32);
ilitek_dma_va = (u8 *)dmam_alloc_coherent(client->dev, DMA_VA_BUFFER, &ilitek_dma_pa, GFP_KERNEL);
if (ERR_ALLOC_MEM(ilitek_dma_va)) {
ILI_ERR("Allocate DMA I2C Buffer failed\n");
return -ENOMEM;
}
memset(ilitek_dma_va, 0, DMA_VA_BUFFER);
client->ext_flag |= I2C_DMA_FLAG;
return 0;
}
ILI_ERR("client is NULL, allocated dma i2c failed\n");
return -ENODEV;
}
#endif
static int core_i2c_write(void *buf, int len)
{
int ret = 0;
u8 *txbuf = (u8 *)buf;
u8 check_sum = 0;
u8 *mpbuf = NULL;
struct i2c_msg msgs[] = {
{
.addr = ilits->i2c->addr,
.flags = 0, /* write flag. */
.len = len,
.buf = txbuf,
},
};
#if I2C_DMA_TRANSFER
if (len > 8) {
msgs[0].addr = (ilits->client->addr & I2C_MASK_FLAG);
msgs[0].ext_flag = (ilits->client->ext_flag | I2C_ENEXT_FLAG | I2C_DMA_FLAG);
memcpy(ilitek_dma_va, txbuf, len);
msgs[0].buf = (u8 *)ilitek_dma_pa;
}
#endif
/*
* 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) && txbuf[0] == P5_X_SET_CDC_INIT) {
check_sum = ili_calc_packet_checksum(txbuf, len);
mpbuf = kcalloc(len + 1, sizeof(u8), GFP_KERNEL);
if (ERR_ALLOC_MEM(mpbuf)) {
ILI_ERR("Failed to allocate mpbuf mem\n");
return -ENOMEM;
}
ipio_memcpy(mpbuf, txbuf, len, msgs[0].len);
mpbuf[len] = check_sum;
msgs[0].buf = mpbuf;
ili_dump_data(mpbuf, 8, len+1, 0, "mp cdc cmd");
msgs[0].len = len + 1;
}
if (i2c_transfer(ilits->i2c->adapter, msgs, 1) != 1)
ret = -1;
ipio_kfree((void **)&mpbuf);
return ret;
}
static int core_i2c_read(void *buf, int len)
{
int ret;
u8 *rxbuf = (u8 *)buf;
struct i2c_msg msgs[] = {
{
.addr = ilits->i2c->addr,
.flags = I2C_M_RD, /* read flag. */
.len = len,
.buf = rxbuf,
},
};
#if I2C_DMA_TRANSFER
if (len > 8) {
msgs[0].addr = (ilits->client->addr & I2C_MASK_FLAG);
msgs[0].ext_flag = (ilits->client->ext_flag | I2C_ENEXT_FLAG | I2C_DMA_FLAG);
msgs[0].buf = (u8 *)ilitek_dma_pa;
} else {
msgs[0].buf = rxbuf;
}
#endif
ret = i2c_transfer(ilits->i2c->adapter, msgs, 1);
#if I2C_DMA_TRANSFER
if (len > 8)
memcpy(rxbuf, ilitek_dma_va, len);
#endif
/*
* If i2c_transfer is ok (must return 1 because only sends one msg),
* return #bytes transferred, else error code.
*/
return (ret == 1) ? len : ret;
}
static int ilitek_i2c_write(void *buf, int len)
{
int ret = 0;
if (len == 0) {
ILI_ERR("i2c write len is invalid\n");
return -EINVAL;
}
ret = core_i2c_write(buf, len);
if (ret < 0) {
if (atomic_read(&ilits->tp_reset) == START) {
ret = 0;
goto out;
}
ILI_ERR("i2c write error, ret = %d\n", ret);
}
out:
return ret;
}
static int ilitek_i2c_read(void *buf, int len)
{
int ret = 0;
if (len == 0) {
ILI_ERR("i2c read len is invalid\n");
return -EINVAL;
}
ret = core_i2c_read(buf, len);
if (ret < 0) {
if (atomic_read(&ilits->tp_reset) == START) {
ret = 0;
goto out;
}
ILI_ERR("i2c read error, ret = %d\n", ret);
}
out:
return ret;
}
static int ili_i2c_wrapper(u8 *txbuf, u32 wlen, u8 *rxbuf, u32 rlen, bool spi_irq, bool i2c_irq)
{
int ret = 0, operate = -1;
if (wlen > 0) {
if (ERR_ALLOC_MEM(txbuf)) {
ILI_ERR("txbuf is null\n");
return -ENOMEM;
}
}
if (rlen > 0) {
if (ERR_ALLOC_MEM(rxbuf)) {
ILI_ERR("rxbuf is null\n");
return -ENOMEM;
}
}
if (wlen > 0 && rlen > 0)
operate = RW_SYNC;
else if (wlen > 0 && !rlen)
operate = W_ONLY;
else
operate = R_ONLY;
if (ilits->int_pulse)
ilits->detect_int_stat = ili_ic_check_int_pulse;
else
ilits->detect_int_stat = ili_ic_check_int_level;
if (i2c_irq)
atomic_set(&ilits->cmd_int_check, ENABLE);
switch (operate) {
case RW_SYNC:
ret = ilitek_i2c_write(txbuf, wlen);
if (ret < 0) {
ILI_ERR("i2c-wrapper write error\n");
break;
}
if (i2c_irq) {
if (ilits->detect_int_stat(false) < 0) {
ILI_ERR("ERROR! Check INT timeout\n");
ret = -ETIME;
break;
}
} else {
msleep(1);
}
ret = ilitek_i2c_read(rxbuf, rlen);
if (ret < 0) {
ILI_ERR("i2c-wrapper read error\n");
break;
}
break;
case W_ONLY:
ret = ilitek_i2c_write(txbuf, wlen);
if (ret < 0) {
ILI_ERR("i2c-wrapper write error\n");
break;
}
break;
case R_ONLY:
if (i2c_irq) {
if (ilits->detect_int_stat(false) < 0) {
ILI_ERR("ERROR! Check INT timeout\n");
ret = -ETIME;
break;
}
}
ret = ilitek_i2c_read(rxbuf, rlen);
if (ret < 0) {
ILI_ERR("i2c-wrapper read error\n");
break;
}
break;
default:
ILI_ERR("Unknown ts-i2c operation\n");
ret = -EINVAL;
break;
}
if (i2c_irq)
atomic_set(&ilits->cmd_int_check, DISABLE);
return ret;
}
int ili_core_spi_setup(int num)
{
ILI_ERR("Not support this interface\n");
return 0;
}
static int ilitek_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
{
struct touch_bus_info *info =
container_of(to_i2c_driver(i2c->dev.driver),
struct touch_bus_info, bus_driver);
ILI_INFO("ilitek i2c probe\n");
if (!i2c) {
ILI_ERR("i2c client is NULL\n");
return -ENODEV;
}
if (i2c->addr != TDDI_I2C_ADDR) {
i2c->addr = TDDI_I2C_ADDR;
ILI_INFO("i2c addr doesn't be set up, use default : 0x%x\n", i2c->addr);
}
if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C)) {
ILI_ERR("i2c functions are not supported!\n");
return -ENODEV;
}
ilits = devm_kzalloc(&i2c->dev, sizeof(struct ilitek_ts_data), GFP_KERNEL);
if (ERR_ALLOC_MEM(ilits)) {
ILI_ERR("Failed to allocate ts memory, %ld\n", PTR_ERR(ilits));
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)) {
ILI_ERR("failed to allocate touch report buffer\n");
return -ENOMEM;
}
ilits->gcoord = kzalloc(sizeof(struct gesture_coordinate), GFP_KERNEL);
if (ERR_ALLOC_MEM(ilits->gcoord)) {
ILI_ERR("Failed to allocate gresture coordinate buffer\n");
return -ENOMEM;
}
ilits->i2c = i2c;
ilits->spi = NULL;
ilits->dev = &i2c->dev;
ilits->hwif = info->hwif;
ilits->phys = "I2C";
ilits->wrapper = ili_i2c_wrapper;
ilits->detect_int_stat = ili_ic_check_int_pulse;
ilits->int_pulse = true;
ilits->mp_retry = false;
#if I2C_DMA_TRANSFER
if (dma_i2c_alloc(ilits->i2c) < 0)
ILI_ERR("Failed to alllocate DMA mem %ld\n", PTR_ERR(ilits->i2c));
#endif
ilits->actual_tp_mode = P5_X_FW_AP_MODE;
if (TDDI_RST_BIND)
ilits->reset = TP_IC_WHOLE_RST;
else
ilits->reset = TP_HW_RST_ONLY;
ilits->rst_edge_delay = 100;
ilits->fw_open = FILP_OPEN;
ilits->fw_upgrade_mode = UPGRADE_FLASH;
ilits->mp_move_code = ili_move_mp_code_flash;
ilits->gesture_move_code = ili_move_gesture_code_flash;
ilits->esd_recover = ili_wq_esd_i2c_check;
ilits->ges_recover = ili_touch_esd_gesture_flash;
ilits->gesture_mode = DATA_FORMAT_GESTURE_INFO;
ilits->gesture_demo_ctrl = DISABLE;
ilits->wtd_ctrl = OFF;
ilits->report = ENABLE;
ilits->netlink = DISABLE;
ilits->dnp = DISABLE;
ilits->irq_tirgger_type = IRQF_TRIGGER_FALLING;
ilits->info_from_hex = DISABLE;
ilits->wait_int_timeout = AP_INT_TIMEOUT;
#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
return info->hwif->plat_probe();
}
static int ilitek_i2c_remove(struct i2c_client *i2c)
{
ILI_INFO();
return 0;
}
static const struct i2c_device_id tp_i2c_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) {
ILI_ERR("faied to allocate i2c_driver\n");
return -ENOMEM;
}
if (hwif->bus_type != BUS_I2C) {
ILI_ERR("Not I2C dev\n");
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_i2c_probe;
info->bus_driver.remove = ilitek_i2c_remove;
info->bus_driver.id_table = tp_i2c_id;
info->hwif = hwif;
return i2c_add_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;
ILI_INFO("remove i2c dev\n");
i2c_del_driver(&info->bus_driver);
ipio_kfree((void **)&info);
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,745 @@
/*
* ILITEK Touch IC driver
*
* Copyright (C) 2011 ILI Technology Corporation.
*
* Author: Dicky Chiang <dicky_chiang@ilitek.com>
*
* 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 "ilitek.h"
#define DTS_INT_GPIO "touch,irq-gpio"
#define DTS_RESET_GPIO "touch,reset-gpio"
#define DTS_OF_NAME "tchip,ilitek"
void ili_tp_reset(void)
{
ILI_INFO("edge delay = %d\n", ilits->rst_edge_delay);
/* Need accurate power sequence, do not change it to msleep */
gpio_direction_output(ilits->tp_rst, 1);
mdelay(1);
gpio_set_value(ilits->tp_rst, 0);
mdelay(5);
gpio_set_value(ilits->tp_rst, 1);
mdelay(ilits->rst_edge_delay);
}
void ili_input_register(void)
{
ILI_INFO();
ilits->input = input_allocate_device();
if (ERR_ALLOC_MEM(ilits->input)) {
ILI_ERR("Failed to allocate touch input device\n");
input_free_device(ilits->input);
return;
}
ilits->input->name = ilits->hwif->name;
ilits->input->phys = ilits->phys;
ilits->input->dev.parent = ilits->dev;
ilits->input->id.bustype = ilits->hwif->bus_type;
/* set the supported event type for input device */
set_bit(EV_ABS, ilits->input->evbit);
set_bit(EV_SYN, ilits->input->evbit);
set_bit(EV_KEY, ilits->input->evbit);
set_bit(BTN_TOUCH, ilits->input->keybit);
set_bit(BTN_TOOL_FINGER, ilits->input->keybit);
set_bit(INPUT_PROP_DIRECT, ilits->input->propbit);
input_set_abs_params(ilits->input, ABS_MT_POSITION_X, TOUCH_SCREEN_X_MIN, ilits->panel_wid - 1, 0, 0);
input_set_abs_params(ilits->input, ABS_MT_POSITION_Y, TOUCH_SCREEN_Y_MIN, ilits->panel_hei - 1, 0, 0);
input_set_abs_params(ilits->input, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
input_set_abs_params(ilits->input, ABS_MT_WIDTH_MAJOR, 0, 255, 0, 0);
if (MT_PRESSURE)
input_set_abs_params(ilits->input, ABS_MT_PRESSURE, 0, 255, 0, 0);
if (MT_B_TYPE) {
#if KERNEL_VERSION(3, 7, 0) <= LINUX_VERSION_CODE
input_mt_init_slots(ilits->input, MAX_TOUCH_NUM, INPUT_MT_DIRECT);
#else
input_mt_init_slots(ilits->input, MAX_TOUCH_NUM);
#endif /* LINUX_VERSION_CODE */
} else {
input_set_abs_params(ilits->input, ABS_MT_TRACKING_ID, 0, MAX_TOUCH_NUM, 0, 0);
}
/* Gesture keys register */
input_set_capability(ilits->input, EV_KEY, KEY_POWER);
input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_UP);
input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_DOWN);
input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_LEFT);
input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_RIGHT);
input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_O);
input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_E);
input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_M);
input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_W);
input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_S);
input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_V);
input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_Z);
input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_C);
input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_F);
__set_bit(KEY_GESTURE_POWER, ilits->input->keybit);
__set_bit(KEY_GESTURE_UP, ilits->input->keybit);
__set_bit(KEY_GESTURE_DOWN, ilits->input->keybit);
__set_bit(KEY_GESTURE_LEFT, ilits->input->keybit);
__set_bit(KEY_GESTURE_RIGHT, ilits->input->keybit);
__set_bit(KEY_GESTURE_O, ilits->input->keybit);
__set_bit(KEY_GESTURE_E, ilits->input->keybit);
__set_bit(KEY_GESTURE_M, ilits->input->keybit);
__set_bit(KEY_GESTURE_W, ilits->input->keybit);
__set_bit(KEY_GESTURE_S, ilits->input->keybit);
__set_bit(KEY_GESTURE_V, ilits->input->keybit);
__set_bit(KEY_GESTURE_Z, ilits->input->keybit);
__set_bit(KEY_GESTURE_C, ilits->input->keybit);
__set_bit(KEY_GESTURE_F, ilits->input->keybit);
/* register the input device to input sub-system */
if (input_register_device(ilits->input) < 0) {
ILI_ERR("Failed to register touch input device\n");
input_unregister_device(ilits->input);
input_free_device(ilits->input);
}
}
#if REGULATOR_POWER
void ili_plat_regulator_power_on(bool status)
{
ILI_INFO("%s\n", status ? "POWER ON" : "POWER OFF");
if (status) {
if (ilits->vdd) {
if (regulator_enable(ilits->vdd) < 0)
ILI_ERR("regulator_enable VDD fail\n");
}
if (ilits->vcc) {
if (regulator_enable(ilits->vcc) < 0)
ILI_ERR("regulator_enable VCC fail\n");
}
} else {
if (ilits->vdd) {
if (regulator_disable(ilits->vdd) < 0)
ILI_ERR("regulator_enable VDD fail\n");
}
if (ilits->vcc) {
if (regulator_disable(ilits->vcc) < 0)
ILI_ERR("regulator_enable VCC fail\n");
}
}
atomic_set(&ilits->ice_stat, DISABLE);
mdelay(5);
}
static void ilitek_plat_regulator_power_init(void)
{
const char *vdd_name = "vdd";
const char *vcc_name = "vcc";
ilits->vdd = regulator_get(ilits->dev, vdd_name);
if (ERR_ALLOC_MEM(ilits->vdd)) {
ILI_ERR("regulator_get VDD fail\n");
ilits->vdd = NULL;
}
if (regulator_set_voltage(ilits->vdd, VDD_VOLTAGE, VDD_VOLTAGE) < 0)
ILI_ERR("Failed to set VDD %d\n", VDD_VOLTAGE);
ilits->vcc = regulator_get(ilits->dev, vcc_name);
if (ERR_ALLOC_MEM(ilits->vcc)) {
ILI_ERR("regulator_get VCC fail.\n");
ilits->vcc = NULL;
}
if (regulator_set_voltage(ilits->vcc, VCC_VOLTAGE, VCC_VOLTAGE) < 0)
ILI_ERR("Failed to set VCC %d\n", VCC_VOLTAGE);
ili_plat_regulator_power_on(true);
}
#endif
void ili_irq_disable(void)
{
unsigned long flag;
spin_lock_irqsave(&ilits->irq_spin, flag);
if (atomic_read(&ilits->irq_stat) == DISABLE)
goto out;
if (!ilits->irq_num) {
ILI_ERR("gpio_to_irq (%d) is incorrect\n", ilits->irq_num);
goto out;
}
disable_irq_nosync(ilits->irq_num);
atomic_set(&ilits->irq_stat, DISABLE);
ILI_DBG("Disable irq success\n");
out:
spin_unlock_irqrestore(&ilits->irq_spin, flag);
}
void ili_irq_enable(void)
{
unsigned long flag;
spin_lock_irqsave(&ilits->irq_spin, flag);
if (atomic_read(&ilits->irq_stat) == ENABLE)
goto out;
if (!ilits->irq_num) {
ILI_ERR("gpio_to_irq (%d) is incorrect\n", ilits->irq_num);
goto out;
}
enable_irq(ilits->irq_num);
atomic_set(&ilits->irq_stat, ENABLE);
ILI_DBG("Enable irq success\n");
out:
spin_unlock_irqrestore(&ilits->irq_spin, flag);
}
static irqreturn_t ilitek_plat_isr_top_half(int irq, void *dev_id)
{
if (irq != ilits->irq_num) {
ILI_ERR("Incorrect irq number (%d)\n", irq);
return IRQ_NONE;
}
if (atomic_read(&ilits->cmd_int_check) == ENABLE) {
atomic_set(&ilits->cmd_int_check, DISABLE);
ILI_DBG("CMD INT detected, ignore\n");
wake_up(&(ilits->inq));
return IRQ_HANDLED;
}
if (ilits->prox_near) {
ILI_INFO("Proximity event, ignore interrupt!\n");
return IRQ_HANDLED;
}
ILI_DBG("report: %d, rst: %d, fw: %d, switch: %d, mp: %d, sleep: %d, esd: %d\n",
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),
atomic_read(&ilits->esd_stat));
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) ||
atomic_read(&ilits->esd_stat)) {
ILI_DBG("ignore interrupt !\n");
return IRQ_HANDLED;
}
return IRQ_WAKE_THREAD;
}
static irqreturn_t ilitek_plat_isr_bottom_half(int irq, void *dev_id)
{
if (mutex_is_locked(&ilits->touch_mutex)) {
ILI_DBG("touch is locked, ignore\n");
return IRQ_HANDLED;
}
mutex_lock(&ilits->touch_mutex);
ili_report_handler();
mutex_unlock(&ilits->touch_mutex);
return IRQ_HANDLED;
}
void ili_irq_unregister(void)
{
devm_free_irq(ilits->dev, ilits->irq_num, NULL);
}
int ili_irq_register(int type)
{
int ret = 0;
static bool get_irq_pin;
atomic_set(&ilits->irq_stat, DISABLE);
if (get_irq_pin == false) {
ilits->irq_num = gpio_to_irq(ilits->tp_int);
get_irq_pin = true;
}
ILI_INFO("ilits->irq_num = %d\n", ilits->irq_num);
ret = devm_request_threaded_irq(ilits->dev, ilits->irq_num,
ilitek_plat_isr_top_half,
ilitek_plat_isr_bottom_half,
type | IRQF_ONESHOT, "ilitek", NULL);
if (type == IRQF_TRIGGER_FALLING)
ILI_INFO("IRQ TYPE = IRQF_TRIGGER_FALLING\n");
if (type == IRQF_TRIGGER_RISING)
ILI_INFO("IRQ TYPE = IRQF_TRIGGER_RISING\n");
if (ret != 0)
ILI_ERR("Failed to register irq handler, irq = %d, ret = %d\n", ilits->irq_num, ret);
atomic_set(&ilits->irq_stat, ENABLE);
return ret;
}
#if defined(CONFIG_FB)
static int ilitek_plat_notifier_fb(struct notifier_block *self, unsigned long event, void *data)
{
int *blank;
struct fb_event *evdata = data;
ILI_INFO("Notifier's event = %ld\n", event);
/*
* FB_EVENT_BLANK(0x09): A hardware display blank change occurred.
* FB_EARLY_EVENT_BLANK(0x10): A hardware display blank early change occurred.
*/
if (evdata && evdata->data) {
blank = evdata->data;
switch (*blank) {
#ifdef CONFIG_DRM_MSM
case MSM_DRM_BLANK_POWERDOWN:
#else
case FB_BLANK_POWERDOWN:
#endif
#if CONFIG_PLAT_SPRD
case DRM_MODE_DPMS_OFF:
#endif /* CONFIG_PLAT_SPRD */
if (TP_SUSPEND_PRIO) {
#ifdef CONFIG_DRM_MSM
if (event != MSM_DRM_EARLY_EVENT_BLANK)
#else
if (event != FB_EARLY_EVENT_BLANK)
#endif
return NOTIFY_DONE;
} else {
#ifdef CONFIG_DRM_MSM
if (event != MSM_DRM_EVENT_BLANK)
#else
if (event != FB_EVENT_BLANK)
#endif
return NOTIFY_DONE;
}
if (ili_sleep_handler(TP_SUSPEND) < 0)
ILI_ERR("TP suspend failed\n");
break;
#ifdef CONFIG_DRM_MSM
case MSM_DRM_BLANK_UNBLANK:
case MSM_DRM_BLANK_NORMAL:
#else
case FB_BLANK_UNBLANK:
case FB_BLANK_NORMAL:
#endif
#if CONFIG_PLAT_SPRD
case DRM_MODE_DPMS_ON:
#endif /* CONFIG_PLAT_SPRD */
#ifdef CONFIG_DRM_MSM
if (event == MSM_DRM_EVENT_BLANK)
#else
if (event == FB_EVENT_BLANK)
#endif
{
if (ili_sleep_handler(TP_RESUME) < 0)
ILI_ERR("TP resume failed\n");
}
break;
default:
ILI_ERR("Unknown event, blank = %d\n", *blank);
break;
}
}
return NOTIFY_OK;
}
#endif
#if defined(CONFIG_DRM)
#if defined(CONFIG_DRM_PANEL)
static struct drm_panel *active_panel;
int ili_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) {
ILI_ERR("find drm_panel count(%d) fail", count);
return 0;
}
ILI_INFO("find drm_panel count(%d) ", count);
for (i = 0; i < count; i++) {
node = of_parse_phandle(np, "panel", i);
ILI_INFO("node%p", node);
panel = of_drm_find_panel(node);
ILI_INFO("panel%p ", panel);
of_node_put(node);
if (!IS_ERR(panel)) {
ILI_INFO("find drm_panel successfully");
active_panel = panel;
return 0;
}
}
ILI_ERR("no find drm_panel");
return -ENODEV;
}
int ili_check_default_tp(struct device_node *dt, const char *prop)
{
const char *active_tp;
const char *compatible;
char *start;
int ret;
ret = of_property_read_string(dt->parent, prop, &active_tp);
if (ret) {
ILI_ERR(" %s:fail to read %s %d\n", __func__, prop, ret);
return -ENODEV;
}
ILI_INFO("active_tp %s", active_tp);
ret = of_property_read_string(dt, "compatible", &compatible);
if (ret < 0) {
ILI_ERR(" %s:fail to read %s %d\n", __func__, "compatible", ret);
return -ENODEV;
}
ILI_INFO("compatible %s", compatible);
start = strnstr(active_tp, compatible, strlen(active_tp));
if (start == NULL) {
ILI_ERR(" %s:no match compatible, %s, %s\n",
__func__, compatible, active_tp);
ret = -ENODEV;
}
return ret;
}
static int drm_notifier_callback(struct notifier_block *self,
unsigned long event, void *data)
{
struct drm_panel_notifier *evdata = data;
int *blank = NULL;
if (!evdata) {
ILI_ERR("evdata is null");
return 0;
}
if (!((event == DRM_PANEL_EARLY_EVENT_BLANK )
|| (event == DRM_PANEL_EVENT_BLANK))) {
ILI_INFO("event(%lu) do not need process\n", event);
return 0;
}
blank = evdata->data;
ILI_INFO("DRM event:%lu,blank:%d", event, *blank);
switch (*blank) {
case DRM_PANEL_BLANK_UNBLANK:
if (DRM_PANEL_EARLY_EVENT_BLANK == event) {
ILI_INFO("resume: event = %lu, not care\n", event);
} else if (DRM_PANEL_EVENT_BLANK == event) {
ILI_INFO("suspend: event = %lu, TP_RESUME\n", event);
if (ili_sleep_handler(TP_RESUME) < 0)
ILI_ERR("TP resume failed\n");
}
break;
case DRM_PANEL_BLANK_POWERDOWN:
if (DRM_PANEL_EARLY_EVENT_BLANK == event) {
ILI_INFO("suspend: event = %lu, TP_SUSPEND\n", event);
if (ili_sleep_handler(TP_SUSPEND) < 0)
ILI_ERR("TP suspend failed\n");
} else if (DRM_PANEL_EVENT_BLANK == event) {
ILI_INFO("suspend: event = %lu, not care\n", event);
}
break;
default:
ILI_INFO("DRM BLANK(%d) do not need process\n", *blank);
break;
}
return 0;
}
#endif
#if defined(CONFIG_DRM_MSM)
static int drm_notifier_callback(struct notifier_block *self,
unsigned long event, void *data)
{
struct msm_drm_notifier *evdata = data;
int *blank = NULL;
if (!evdata) {
ILI_ERR("evdata is null");
return 0;
}
if (!((event == MSM_DRM_EARLY_EVENT_BLANK )
|| (event == MSM_DRM_EVENT_BLANK))) {
ILI_INFO("event(%lu) do not need process\n", event);
return 0;
}
blank = evdata->data;
ILI_INFO("DRM event:%lu,blank:%d", event, *blank);
switch (*blank) {
case MSM_DRM_BLANK_UNBLANK:
if (MSM_DRM_EARLY_EVENT_BLANK == event) {
ILI_INFO("resume: event = %lu, not care\n", event);
} else if (MSM_DRM_EVENT_BLANK == event) {
if (ili_sleep_handler(TP_RESUME) < 0)
ILI_ERR("TP resume failed\n");
}
break;
case MSM_DRM_BLANK_POWERDOWN:
if (MSM_DRM_EARLY_EVENT_BLANK == event) {
if (ili_sleep_handler(TP_SUSPEND) < 0)
ILI_ERR("TP suspend failed\n");
} else if (MSM_DRM_EVENT_BLANK == event) {
ILI_INFO("suspend: event = %lu, not care\n", event);
}
break;
default:
ILI_INFO("DRM BLANK(%d) do not need process\n", *blank);
break;
}
return 0;
}
#endif
#endif
#if defined(CONFIG_HAS_EARLYSUSPEND)
static void ilitek_plat_early_suspend(struct early_suspend *h)
{
if (ili_sleep_handler(TP_SUSPEND) < 0)
ILI_ERR("TP suspend failed\n");
}
static void ilitek_plat_late_resume(struct early_suspend *h)
{
if (ili_sleep_handler(TP_RESUME) < 0)
ILI_ERR("TP resume failed\n");
}
#endif
static void ilitek_plat_sleep_init(void)
{
int ret;
#if defined(CONFIG_FB)
ILI_INFO("Init notifier_fb struct\n");
ilits->notifier_fb.notifier_call = ilitek_plat_notifier_fb;
#if CONFIG_PLAT_SPRD
if (adf_register_client(&ilits->notifier_fb))
ILI_ERR("Unable to register notifier_fb\n");
#else
if (fb_register_client(&ilits->notifier_fb))
ILI_ERR("Unable to register notifier_fb\n");
#endif /* CONFIG_PLAT_SPRD */
#endif
#if defined(CONFIG_DRM)
ilits->notifier_fb.notifier_call = drm_notifier_callback;
#if defined(CONFIG_DRM_PANEL)
if (active_panel) {
ret = drm_panel_notifier_register(active_panel,&ilits->notifier_fb);
if (ret)
ILI_ERR("[DRM]drm_panel_notifier_register fail: %d\n", ret);
}
#endif
#if defined(CONFIG_DRM_MSM)
ret = msm_drm_register_client(&ilits->notifier_fb);
if (ret) {
ILI_ERR("[DRM]Unable to register fb_notifier: %d\n", ret);
}
#endif
#endif/* CONFIG_DRM */
#if defined(CONFIG_HAS_EARLYSUSPEND)
ILI_ERR("Init eqarly_suspend struct\n");
ilits->early_suspend.suspend = ilitek_plat_early_suspend;
ilits->early_suspend.resume = ilitek_plat_late_resume;
ilits->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
register_early_suspend(&ilits->early_suspend);
#endif
}
static int ilitek_plat_gpio_register(void)
{
int ret = 0;
u32 flag;
struct device_node *dev_node = ilits->dev->of_node;
#if defined(CONFIG_DRM)
#if defined(CONFIG_DRM_PANEL)
int err;
ret = ili_drm_check_dt(dev_node);
if (ret) {
ILI_ERR("parse drm-panel fail");
if(!ili_check_default_tp(dev_node,"tchip,ilitek"))
{
ILI_ERR("EPROBE_DEFER");
err=-EPROBE_DEFER;
}else{
ILI_ERR("ENODEV");
err= -ENODEV;
}
return err;
}
#endif
#endif
ilits->tp_int = of_get_named_gpio_flags(dev_node, DTS_INT_GPIO, 0, &flag);
ilits->tp_rst = of_get_named_gpio_flags(dev_node, DTS_RESET_GPIO, 0, &flag);
ILI_INFO("TP INT: %d\n", ilits->tp_int);
ILI_INFO("TP RESET: %d\n", ilits->tp_rst);
if (!gpio_is_valid(ilits->tp_int)) {
ILI_ERR("Invalid INT gpio: %d\n", ilits->tp_int);
return -EBADR;
}
if (!gpio_is_valid(ilits->tp_rst)) {
ILI_ERR("Invalid RESET gpio: %d\n", ilits->tp_rst);
return -EBADR;
}
ret = gpio_request(ilits->tp_int, "TP_INT");
if (ret < 0) {
ILI_ERR("Request IRQ GPIO failed, ret = %d\n", ret);
gpio_free(ilits->tp_int);
ret = gpio_request(ilits->tp_int, "TP_INT");
if (ret < 0) {
ILI_ERR("Retrying request INT GPIO still failed , ret = %d\n", ret);
goto out;
}
}
ret = gpio_request(ilits->tp_rst, "TP_RESET");
if (ret < 0) {
ILI_ERR("Request RESET GPIO failed, ret = %d\n", ret);
gpio_free(ilits->tp_rst);
ret = gpio_request(ilits->tp_rst, "TP_RESET");
if (ret < 0) {
ILI_ERR("Retrying request RESET GPIO still failed , ret = %d\n", ret);
goto out;
}
}
out:
gpio_direction_input(ilits->tp_int);
return ret;
}
static int ilitek_plat_probe(void)
{
ILI_INFO("platform probe\n");
#if REGULATOR_POWER
ilitek_plat_regulator_power_init();
#endif
if (ilitek_plat_gpio_register() < 0)
ILI_ERR("Register gpio failed\n");
if (ili_tddi_init() < 0) {
ILI_ERR("ILITEK Driver probe failed\n");
if(gpio_is_valid(ilits->tp_int))
gpio_free(ilits->tp_int);
if(gpio_is_valid(ilits->tp_rst))
gpio_free(ilits->tp_rst);
return -ENODEV;
}
ili_irq_register(ilits->irq_tirgger_type);
ilitek_plat_sleep_init();
ilits->pm_suspend = false;
init_completion(&ilits->pm_completion);
ILI_INFO("ILITEK Driver loaded successfully!");
return 0;
}
static int ilitek_tp_pm_suspend(struct device *dev)
{
ILI_INFO("CALL BACK TP PM SUSPEND");
ilits->pm_suspend = true;
reinit_completion(&ilits->pm_completion);
return 0;
}
static int ilitek_tp_pm_resume(struct device *dev)
{
ILI_INFO("CALL BACK TP PM RESUME");
ilits->pm_suspend = false;
complete(&ilits->pm_completion);
return 0;
}
static int ilitek_plat_remove(void)
{
ILI_INFO("remove plat dev\n");
ili_dev_remove();
return 0;
}
static const struct dev_pm_ops tp_pm_ops = {
.suspend = ilitek_tp_pm_suspend,
.resume = ilitek_tp_pm_resume,
};
static const struct of_device_id tp_match_table[] = {
{.compatible = DTS_OF_NAME},
{},
};
static struct ilitek_hwif_info hwif = {
.bus_type = TDDI_INTERFACE,
.plat_type = TP_PLAT_QCOM,
.owner = THIS_MODULE,
.name = TDDI_DEV_ID,
.of_match_table = of_match_ptr(tp_match_table),
.plat_probe = ilitek_plat_probe,
.plat_remove = ilitek_plat_remove,
.pm = &tp_pm_ops,
};
static int __init ilitek_plat_dev_init(void)
{
ILI_INFO("ILITEK TP driver init for QCOM\n");
if (ili_dev_init(&hwif) < 0) {
ILI_ERR("Failed to register i2c/spi bus driver\n");
return -ENODEV;
}
return 0;
}
static void __exit ilitek_plat_dev_exit(void)
{
ILI_INFO("remove plat dev\n");
ili_dev_remove();
}
module_init(ilitek_plat_dev_init);
module_exit(ilitek_plat_dev_exit);
MODULE_AUTHOR("ILITEK");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,615 @@
/*
* ILITEK Touch IC driver
*
* Copyright (C) 2011 ILI Technology Corporation.
*
* Author: Dicky Chiang <dicky_chiang@ilitek.com>
*
* 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 "ilitek.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;
xfer = kzalloc(DMA_TRANSFER_MAX_CHUNK * sizeof(struct spi_transfer), GFP_KERNEL);
if (n_rx > SPI_RX_BUF_SIZE) {
ILI_ERR("Rx length is greater than spi local buf, abort\n");
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;
}
status = spi_sync(spi, &message);
break;
case SPI_READ:
if (n_tx > DMA_TRANSFER_MAX_LEN) {
ILI_ERR("Tx length must be lower than dma length (%d).\n", 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;
}
status = spi_sync(spi, &message);
if (status == 0) {
if (ilits->spi_rx[1] != SPI_ACK && !atomic_read(&ilits->ice_stat)) {
status = DO_SPI_RECOVER;
ILI_ERR("Do spi recovery: rxbuf[1] = 0x%x, ice = %d\n", ilits->spi_rx[1], atomic_read(&ilits->ice_stat));
break;
}
memcpy((u8 *)rxbuf, ilits->spi_rx + offset + 1, n_rx);
} else {
ILI_ERR("spi read fail, status = %d\n", status);
}
break;
default:
ILI_INFO("Unknown command 0x%x\n", 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 (n_rx > SPI_RX_BUF_SIZE) {
ILI_ERR("Rx length is greater than spi local buf, abort\n");
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);
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)) {
ILI_ERR("duplex_len is over than dma buf, abort\n");
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);
status = spi_sync(spi, &message);
if (status == 0) {
if (ilits->spi_rx[1] != SPI_ACK && !atomic_read(&ilits->ice_stat)) {
status = DO_SPI_RECOVER;
ILI_ERR("Do spi recovery: rxbuf[1] = 0x%x, ice = %d\n", ilits->spi_rx[1], atomic_read(&ilits->ice_stat));
break;
}
memcpy((u8 *)rxbuf, ilits->spi_rx + offset + 1, n_rx);
} else {
ILI_ERR("spi read fail, status = %d\n", status);
}
break;
default:
ILI_INFO("Unknown command 0x%x\n", cmd);
break;
}
out:
return status;
}
#endif
static int ili_spi_mp_pre_cmd(u8 cdc)
{
u8 pre[5] = {0};
if (!atomic_read(&ilits->mp_stat) || cdc != P5_X_SET_CDC_INIT ||
ilits->chip->core_ver >= CORE_VER_1430)
return 0;
ILI_DBG("mp test with pre commands\n");
pre[0] = SPI_WRITE;
pre[1] = 0x0;// len low byte
pre[2] = 0x2;// len high 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) {
ILI_ERR("Failed to write pre commands\n");
return -1;
}
pre[0] = SPI_WRITE;
pre[1] = 0x0;// len low byte
pre[2] = 0x1;// len high byte
pre[3] = P5_X_GET_CDC_DATA;
if (ilits->spi_write_then_read(ilits->spi, pre, 4, NULL, 0) < 0) {
ILI_ERR("Failed to write pre commands\n");
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);
wdata[0] = SPI_WRITE;
wdata[1] = wlen >> 8;
wdata[2] = wlen & 0xff;
index = 3;
wlen += index;
ipio_memcpy(&wdata[index], wakeup, wlen, wlen);
ILI_INFO("Write dummy to wake up spi pll clk\n");
if (ilits->spi_write_then_read(ilits->spi, wdata, wlen, NULL, 0) < 0) {
ILI_INFO("spi slave write error\n");
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 (wlen > 0) {
if (!txbuf) {
ILI_ERR("txbuf is null\n");
return -ENOMEM;
}
/* 3 bytes data consist of length and header */
if ((wlen + 3) > sizeof(wdata)) {
ILI_ERR("WARNING! wlen(%d) > wdata(%d), using wdata length to transfer\n", wlen, (int)sizeof(wdata));
wlen = sizeof(wdata);
}
}
if (rlen > 0) {
if (!rxbuf) {
ILI_ERR("rxbuf is null\n");
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->actual_tp_mode == P5_X_FW_GESTURE_MODE))
#endif
{
ret = ili_spi_pll_clk_wakeup();
if (ret < 0) {
ILI_ERR("Wakeup pll clk error\n");
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) {
ILI_INFO("spi-wrapper write error\n");
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) {
ILI_ERR("ERROR! Check INT timeout\n");
ret = -ETIME;
break;
}
}
ret = ili_spi_mp_pre_cmd(wdata[3]);
if (ret < 0)
ILI_ERR("spi-wrapper mp pre cmd error\n");
wdata[0] = SPI_READ;
ret = ilits->spi_write_then_read(ilits->spi, wdata, 1, rxbuf, rlen);
if (ret < 0)
ILI_ERR("spi-wrapper read error\n");
break;
default:
ILI_ERR("Unknown spi mode (%d)\n", 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)) {
ILI_ERR("Invaild clk freq, set default clk freq\n");
num = 7;
}
ILI_INFO("spi clock = %d\n", freq[num]);
ilits->spi->mode = SPI_MODE_0;
ilits->spi->bits_per_word = 8;
ilits->spi->max_speed_hz = freq[num];
if (spi_setup(ilits->spi) < 0) {
ILI_ERR("Failed to setup spi device\n");
return -ENODEV;
}
ILI_INFO("name = %s, bus_num = %d,cs = %d, mode = %d, speed = %d\n",
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 =
container_of(to_spi_driver(spi->dev.driver),
struct touch_bus_info, bus_driver);
ILI_INFO("ilitek spi probe\n");
if (!spi) {
ILI_ERR("spi device is NULL\n");
return -ENODEV;
}
ilits = devm_kzalloc(&spi->dev, sizeof(struct ilitek_ts_data), GFP_KERNEL);
if (ERR_ALLOC_MEM(ilits)) {
ILI_ERR("Failed to allocate ts memory, %ld\n", PTR_ERR(ilits));
return -ENOMEM;
}
if (spi->master->flags & SPI_MASTER_HALF_DUPLEX) {
ILI_ERR("Full duplex not supported by master\n");
return -EIO;
}
ilits->update_buf = kzalloc(MAX_HEX_FILE_SIZE, GFP_KERNEL | GFP_DMA);
if (ERR_ALLOC_MEM(ilits->update_buf)) {
ILI_ERR("fw kzalloc error\n");
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)) {
ILI_ERR("failed to allocate touch report buffer\n");
return -ENOMEM;
}
ilits->spi_tx = kzalloc(SPI_TX_BUF_SIZE, GFP_KERNEL | GFP_DMA);
if (ERR_ALLOC_MEM(ilits->spi_tx)) {
ILI_ERR("Failed to allocate spi tx buffer\n");
return -ENOMEM;
}
ilits->spi_rx = kzalloc(SPI_RX_BUF_SIZE, GFP_KERNEL | GFP_DMA);
if (ERR_ALLOC_MEM(ilits->spi_rx)) {
ILI_ERR("Failed to allocate spi rx buffer\n");
return -ENOMEM;
}
ilits->gcoord = kzalloc(sizeof(struct gesture_coordinate), GFP_KERNEL);
if (ERR_ALLOC_MEM(ilits->gcoord)) {
ILI_ERR("Failed to allocate gresture coordinate buffer\n");
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;
#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 (TDDI_RST_BIND)
ilits->reset = TP_IC_WHOLE_RST;
else
ilits->reset = TP_HW_RST_ONLY;
ilits->rst_edge_delay = 10;
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->netlink = DISABLE;
ilits->dnp = DISABLE;
ilits->irq_tirgger_type = IRQF_TRIGGER_FALLING;
ilits->info_from_hex = ENABLE;
ilits->wait_int_timeout = AP_INT_TIMEOUT;
#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 int ilitek_spi_remove(struct spi_device *spi)
{
ILI_INFO();
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) {
ILI_ERR("faied to allocate spi_driver\n");
return -ENOMEM;
}
if (hwif->bus_type != BUS_SPI) {
ILI_ERR("Not SPI dev\n");
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.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;
ILI_INFO("remove spi dev\n");
kfree(ilits->update_buf);
kfree(ilits->spi_tx);
kfree(ilits->spi_rx);
spi_unregister_driver(&info->bus_driver);
ipio_kfree((void **)&info);
}

File diff suppressed because it is too large Load diff