guam: input add touch ilitek support

input add touch ilitek
support
NO_PROP_NEEDED:only for guam/guamp/guamna

Change-Id: I7cd9be63ad90f6c67d8df810799308b5a9fa7b26
Signed-off-by: dengwei1 <dengwei1@motorola.com>
Reviewed-on: https://gerrit.mot.com/1702165
SME-Granted: SME Approvals Granted
SLTApproved: Slta Waiver
Tested-by: Jira Key
Reviewed-by: Xiangpo Zhao <zhaoxp3@motorola.com>
Submit-Approved: Jira Key
This commit is contained in:
dengwei1 2020-07-22 18:32:31 +08:00 • committed by Wei Deng
commit 69aa47a468
16 changed files with 15825 additions and 0 deletions

View file

@ -0,0 +1,20 @@
DLKM_DIR := motorola/kernel/modules
LOCAL_PATH := $(call my-dir)
ifneq ($(BOARD_USES_DOUBLE_TAP),)
KERNEL_CFLAGS += CONFIG_INPUT_ILI_0FLASH_MMI_ENABLE_DOUBLE_TAP=y
endif
ifneq ($(BOARD_USES_PANEL_NOTIFICATIONS),)
KERNEL_CFLAGS += CONFIG_INPUT_ILI_PANEL_NOTIFICATIONS=y
endif
ifneq ($(DL_FW_BY_DISPLAY),)
KERNEL_CFLAGS += CONFIG_ILITEK_RESUME_BY_DDI=y
endif
include $(CLEAR_VARS)
LOCAL_MODULE := ili9882_mmi.ko
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT)
include $(DLKM_DIR)/AndroidKernelModule.mk

View file

@ -0,0 +1,28 @@
# 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/ili9882_mmi \
-I$(TOP)/motorola/kernel/modules/drivers/input/touchscreen/ili9882_mmi/firmware
ifeq ($(filter m y, $(CONFIG_TOUCHSCREEN_ILITEK_CHIPSET)),)
obj-m += ili9882_mmi.o
endif
ifneq ($(filter m y,$(CONFIG_INPUT_ILI_0FLASH_MMI_ENABLE_DOUBLE_TAP)),)
EXTRA_CFLAGS += -DILI_SENSOR_EN
endif
ifneq ($(filter m y,$(CONFIG_INPUT_ILI_PANEL_NOTIFICATIONS)),)
EXTRA_CFLAGS += -DILI_CONFIG_PANEL_NOTIFICATIONS
endif
ifneq ($(filter m y,$(CONFIG_ILITEK_RESUME_BY_DDI)),)
EXTRA_CFLAGS += -DILI_CONFIG_RESUME_BY_DDI
endif
ili9882_mmi-objs += ili9882.o
ili9882_mmi-objs += ili9882_spi.o
ili9882_mmi-objs += ili9882_qcom.o
ili9882_mmi-objs += ili9882_ic.o
ili9882_mmi-objs += ili9882_touch.o
ili9882_mmi-objs += ili9882_mp.o
ili9882_mmi-objs += ili9882_hostdl.o
ili9882_mmi-objs += ili9882_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,97 @@
/*
* 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,
};
#define HLT_INI_NAME_PATH "/sdcard/mp_hlt.ini"
#define HLT_FW_FILP_PATH "/sdcard/ILITEK_FW_HLT.hex"
#define HLT_INI_REQUEST_PATH "mp_hlt.ini"
#define HLT_FW_REQUEST_PATH "ILITEK_FW_HLT.hex"
static unsigned char CTPM_FW_HLT[] = {
};
#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,917 @@
/*
* 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 "ili9882.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 u8 *CTPM_FW;
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;
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");
/* Dma1 stop */
if (ili_ice_mode_write(0x072100, 0x02000000, 4) < 0)
ILI_ERR("Write dma1 stop failed\n");
/* crc on */
if (ili_ice_mode_write(0x041016, 0x01, 1) < 0)
ILI_ERR("Write crc on 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];
/* Get hex report info block*/
ipio_memcpy(&ilits->rib, ilits->fw_info, sizeof(ilits->rib), sizeof(ilits->rib));
ILI_INFO("report_info_block : nReportByPixel = %d, nIsHostDownload = %d, nIsSPIICE = %d, nIsSPISLAVE = %d\n",
ilits->rib.nReportByPixel, ilits->rib.nIsHostDownload, ilits->rib.nIsSPIICE, ilits->rib.nIsSPISLAVE);
ILI_INFO("report_info_block : nIsI2C = %d, nReserved00 = %d, nReserved01 = %x, nReserved02 = %x, nReserved03 = %x\n",
ilits->rib.nIsI2C, ilits->rib.nReserved00, ilits->rib.nReserved01, ilits->rib.nReserved02, ilits->rib.nReserved03);
/* 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_ili_convert(u8 *pfw)
{
int i, size, blk_num = 0, blk_map = 0, num;
int b0_addr = 0, b0_num = 0;
if (ERR_ALLOC_MEM(ilits->md_fw_ili))
return -ENOMEM;
CTPM_FW = ilits->md_fw_ili;
size = ilits->md_fw_ili_size;
if (size < ILI_FILE_HEADER || size > MAX_HEX_FILE_SIZE) {
ILI_ERR("size of ILI file is invalid\n");
return -EINVAL;
}
/* Check if it's old version of ILI format. */
if (CTPM_FW[22] == 0xFF && CTPM_FW[23] == 0xFF &&
CTPM_FW[24] == 0xFF && CTPM_FW[25] == 0xFF) {
ILI_ERR("Invaild ILI format, abort!\n");
return -EINVAL;
}
blk_num = CTPM_FW[131];
blk_map = (CTPM_FW[129] << 8) | CTPM_FW[130];
ILI_INFO("Parsing ILI file, block num = %d, block mapping = %x\n", blk_num, blk_map);
if (blk_num > (FW_BLOCK_INFO_NUM - 1) || !blk_num || !blk_map) {
ILI_ERR("Number of block or block mapping is invalid, abort!\n");
return -EINVAL;
}
memset(fbi, 0x0, sizeof(fbi));
tfd.start_addr = 0;
tfd.end_addr = 0;
tfd.hex_tag = BLOCK_TAG_AF;
/* Parsing block info */
for (i = 0; i < FW_BLOCK_INFO_NUM; i++) {
/* B0 tag */
b0_addr = (CTPM_FW[4 + i * 4] << 16) | (CTPM_FW[5 + i * 4] << 8) | (CTPM_FW[6 + i * 4]);
b0_num = CTPM_FW[7 + i * 4];
if ((b0_num != 0) && (b0_addr != 0x000000))
fbi[b0_num].fix_mem_start = b0_addr;
/* AF tag */
num = i + 1;
if (((blk_map >> i) & 0x01) == 0x01) {
fbi[num].start = (CTPM_FW[132 + i * 6] << 16) | (CTPM_FW[133 + i * 6] << 8) | CTPM_FW[134 + i * 6];
fbi[num].end = (CTPM_FW[135 + i * 6] << 16) | (CTPM_FW[136 + i * 6] << 8) | CTPM_FW[137 + i * 6];
if (fbi[num].fix_mem_start == 0)
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, fix_mem_start = 0x%x\n", num, fbi[num].start,
fbi[num].end, fbi[num].fix_mem_start);
if (num == GESTURE)
ilits->gesture_load_code = true;
}
}
memcpy(pfw, CTPM_FW + ILI_FILE_HEADER, size - ILI_FILE_HEADER);
if (ilitek_fw_calc_file_crc(pfw) < 0)
return -1;
tfd.block_number = blk_num;
tfd.end_addr = size - ILI_FILE_HEADER;
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 hex file fail, try upgrade from ILI file\n");
/*
* Users might not be aware of a broken hex file when recovering
* fw from ILI file. We should force them to check
* hex files if they attempt to update via device node.
*/
if (ilits->node_update) {
ILI_ERR("Ignore update from ILI file\n");
ipio_vfree((void **)&pfw);
return -EFW_CONVERT_FILE;
}
if (ilitek_tddi_fw_ili_convert(pfw) < 0) {
ILI_ERR("Convert ILI file error\n");
ret = -EFW_CONVERT_FILE;
goto out;
}
}
if (ilitek_tddi_fw_update_block_info(pfw) < 0) {
ret = -EFW_CONVERT_FILE;
goto out;
}
if (ilits->chip->core_ver >= CORE_VER_1470 && ilits->rib.nIsHostDownload == 0) {
ILI_ERR("hex file interface no match error\n");
return -EFW_INTERFACE;
}
}
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,470 @@
/*
* 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 "ili9882.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_pll_clk_wakeup(void)
{
int ret = 0;
u8 wakeup = 0xA3;
#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 = ilitek_i2c_write(&wakeup, sizeof(wakeup));
if (ret < 0)
ILI_ERR("i2c write dummy cmd error\n");
}
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 = ili_i2c_pll_clk_wakeup();
if (ret < 0) {
ILI_ERR("i2c-wrapper dummy cmd write error\n");
}
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

View file

@ -0,0 +1,459 @@
/*
* 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 "ili9882.h"
#include "tpd.h"
#define DTS_INT_GPIO "touch,irq-gpio"
#define DTS_RESET_GPIO "touch,reset-gpio"
#define DTS_OF_NAME "mediatek,cap_touch"
#define MTK_RST_GPIO GTP_RST_PORT
#define MTK_INT_GPIO GTP_INT_PORT
extern struct tpd_device *tpd;
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 */
tpd_gpio_output(ilits->tp_rst, 1);
mdelay(1);
tpd_gpio_output(ilits->tp_rst, 0);
mdelay(5);
tpd_gpio_output(ilits->tp_rst, 1);
mdelay(ilits->rst_edge_delay);
}
void ili_input_register(void)
{
int i;
ilits->input = tpd->dev;
if (tpd_dts_data.use_tpd_button) {
for (i = 0; i < tpd_dts_data.tpd_key_num; i++)
input_set_capability(ilits->input, EV_KEY, tpd_dts_data.tpd_key_local[i]);
}
/* 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);
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);
}
#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(tpd->tpd_dev, vdd_name);
if (ERR_ALLOC_MEM(ilits->vdd)) {
ILI_ERR("regulator_get VDD fail\n");
ilits->vdd = NULL;
}
tpd->reg = ilits->vdd;
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
static int ilitek_plat_gpio_register(void)
{
int ret = 0;
ilits->tp_int = MTK_INT_GPIO;
ilits->tp_rst = MTK_RST_GPIO;
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;
}
gpio_direction_input(ilits->tp_int);
return ret;
}
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;
struct device_node *node;
atomic_set(&ilits->irq_stat, DISABLE);
if (get_irq_pin == false) {
node = of_find_matching_node(NULL, touch_of_match);
if (node)
ilits->irq_num = irq_of_parse_and_map(node, 0);
ILI_INFO("ilits->irq_num = %d\n", ilits->irq_num);
get_irq_pin = true;
}
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;
}
static void tpd_resume(struct device *h)
{
if (ili_sleep_handler(TP_RESUME) < 0)
ILI_ERR("TP resume failed\n");
}
static void tpd_suspend(struct device *h)
{
if (ili_sleep_handler(TP_SUSPEND) < 0)
ILI_ERR("TP suspend failed\n");
}
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_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");
ili_irq_register(ilits->irq_tirgger_type);
if (ili_tddi_init() < 0) {
ILI_ERR("ILITEK Driver probe failed\n");
ili_irq_unregister();
return -ENODEV;
}
tpd_load_status = 1;
ilits->pm_suspend = false;
init_completion(&ilits->pm_completion);
ILI_INFO("ILITEK Driver loaded successfully!");
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_MTK,
.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 tpd_local_init(void)
{
ILI_INFO("TPD init device driver\n");
if (ili_dev_init(&hwif) < 0) {
ILI_ERR("Failed to register i2c/spi bus driver\n");
return -ENODEV;
}
if (tpd_load_status == 0) {
ILI_ERR("Add error touch panel driver\n");
return -1;
}
if (tpd_dts_data.use_tpd_button) {
tpd_button_setting(tpd_dts_data.tpd_key_num, tpd_dts_data.tpd_key_local,
tpd_dts_data.tpd_key_dim_local);
}
tpd_type_cap = 1;
return 0;
}
static struct tpd_driver_t tpd_device_driver = {
.tpd_device_name = TDDI_DEV_ID,
.tpd_local_init = tpd_local_init,
.suspend = tpd_suspend,
.resume = tpd_resume,
};
static int __init ilitek_plat_dev_init(void)
{
int ret = 0;
ILI_INFO("ILITEK TP driver init for MTK\n");
tpd_get_dts_info();
ret = tpd_driver_add(&tpd_device_driver);
if (ret < 0) {
ILI_ERR("ILITEK add TP driver failed\n");
tpd_driver_remove(&tpd_device_driver);
return -ENODEV;
}
return 0;
}
static void __exit ilitek_plat_dev_exit(void)
{
ILI_INFO("ilitek driver has been removed\n");
tpd_driver_remove(&tpd_device_driver);
}
module_init(ilitek_plat_dev_init);
module_exit(ilitek_plat_dev_exit);
MODULE_AUTHOR("ILITEK");
MODULE_LICENSE("GPL");

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,758 @@
/*
* 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 "ili9882.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
static int ilitek_plat_gpio_register(void)
{
int ret = 0;
u32 flag;
struct device_node *dev_node = ilits->dev->of_node;
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;
}
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 SPRD_SYSFS_SUSPEND_RESUME
static ssize_t ts_suspend_show(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf, "%s\n", ilits->tp_suspend ? "true" : "false");
}
static ssize_t ts_suspend_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
if ((buf[0] == '1') && !ilits->tp_suspend)
ili_sleep_handler(TP_DEEP_SLEEP);
else if ((buf[0] == '0') && ilits->tp_suspend)
ili_sleep_handler(TP_RESUME);
return count;
}
static DEVICE_ATTR_RW(ts_suspend);
static struct attribute *ilitek_dev_suspend_atts[] = {
&dev_attr_ts_suspend.attr,
NULL
};
static const struct attribute_group ilitek_dev_suspend_atts_group = {
.attrs = ilitek_dev_suspend_atts,
};
static const struct attribute_group *ilitek_dev_attr_groups[] = {
&ilitek_dev_suspend_atts_group,
NULL
};
int ili_sysfs_add_device(struct device *dev) {
int ret = 0, i;
for (i = 0; ilitek_dev_attr_groups[i]; i++) {
ret = sysfs_create_group(&dev->kobj, ilitek_dev_attr_groups[i]);
if (ret) {
while (--i >= 0) {
sysfs_remove_group(&dev->kobj, ilitek_dev_attr_groups[i]);
}
break;
}
}
return ret;
}
int ili_sysfs_remove_device(struct device *dev) {
int i;
sysfs_remove_link(NULL, "touchscreen");
for (i = 0; ilitek_dev_attr_groups[i]; i++) {
sysfs_remove_group(&dev->kobj, ilitek_dev_attr_groups[i]);
}
return 0;
}
#else
#ifdef CONFIG_DRM
static int ilitek_plat_notifier_fb(struct notifier_block *self, unsigned long event, void *data)
{
int *blank;
struct drm_panel_notifier *evdata = data;
ILI_INFO("Notifier's event = %ld\n", event);
if (!(evdata && evdata->data))
return 0;
blank = evdata->data;
switch (*blank) {
case DRM_PANEL_BLANK_POWERDOWN:
/*
if (TP_SUSPEND_PRIO) {
if (event != MSM_DRM_EARLY_EVENT_BLANK)
return NOTIFY_DONE;
} else {
if (event != MSM_DRM_EVENT_BLANK)
return NOTIFY_DONE;
}
*/
if (event == DRM_PANEL_EARLY_EVENT_BLANK)
if (ili_sleep_handler(TP_SUSPEND) < 0)
ILI_ERR("TP suspend failed\n");
break;
case DRM_PANEL_BLANK_UNBLANK:
if (event == DRM_PANEL_EVENT_BLANK)
{
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;
}
#else
#if defined(CONFIG_FB) || defined(CONFIG_DRM_MSM)
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;
}
#else
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
#endif
static void ilitek_plat_sleep_init(void)
{
ILI_INFO("Init notifier_fb struct\n");
ilits->notifier_fb.notifier_call = ilitek_plat_notifier_fb;
#if defined(CONFIG_DRM)
{
int ret = 0;
extern struct drm_panel *ili_active_panel;
if (ili_active_panel) {
ret = drm_panel_notifier_register(ili_active_panel, &ilits->notifier_fb);
if(ret) {
ILI_ERR("register drm_notifier failed. ret=%d\n", ret);
}
}
}
#else
#if defined(CONFIG_FB) || defined(CONFIG_DRM_MSM)
#if defined(CONFIG_DRM_MSM)
if (msm_drm_register_client(&ilits->notifier_fb)) {
ILI_ERR("msm_drm_register_client Unable to register fb_notifier\n");
}
#else
#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 /* CONFIG_DRM_MSM */
#else
ILI_INFO("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
#endif
}
#endif
#if CHARGER_NOTIFIER_CALLBACK
#if KERNEL_VERSION(4, 1, 0) <= LINUX_VERSION_CODE
/* add_for_charger_start */
static int ilitek_charger_notifier_callback(struct notifier_block *nb,
unsigned long val, void *v) {
int ret = 0;
struct power_supply *psy = NULL;
union power_supply_propval prop;
if(ilits->fw_update_stat != 100)
return 0;
psy= power_supply_get_by_name("usb");
if (!psy) {
ILI_ERR("Couldn't get usbpsy\n");
return -EINVAL;
}
if (!strcmp(psy->desc->name, "usb")) {
if (psy && val == POWER_SUPPLY_PROP_STATUS) {
ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_PRESENT,&prop);
if (ret < 0) {
ILI_ERR("Couldn't get POWER_SUPPLY_PROP_ONLINE rc=%d\n", ret);
return ret;
} else {
if(ilits->usb_plug_status == 2)
ilits->usb_plug_status = prop.intval;
if(ilits->usb_plug_status != prop.intval) {
ILI_INFO("usb prop.intval =%d\n", prop.intval);
ilits->usb_plug_status = prop.intval;
if(!ilits->tp_suspend && (ilits->charger_notify_wq != NULL))
queue_work(ilits->charger_notify_wq,&ilits->update_charger);
}
}
}
}
return 0;
}
static void ilitek_update_charger(struct work_struct *work)
{
int ret = 0;
mutex_lock(&ilits->touch_mutex);
ret = ili_ic_func_ctrl("plug", !ilits->usb_plug_status);// plug in
if(ret<0) {
ILI_ERR("Write plug in failed\n");
}
mutex_unlock(&ilits->touch_mutex);
}
void ilitek_plat_charger_init(void)
{
int ret = 0;
ilits->usb_plug_status = 2;
ilits->charger_notify_wq = create_singlethread_workqueue("ili_charger_wq");
if (!ilits->charger_notify_wq) {
ILI_ERR("allocate ili_charger_notify_wq failed\n");
return;
}
INIT_WORK(&ilits->update_charger, ilitek_update_charger);
ilits->notifier_charger.notifier_call = ilitek_charger_notifier_callback;
ret = power_supply_reg_notifier(&ilits->notifier_charger);
if (ret < 0)
ILI_ERR("power_supply_reg_notifier failed\n");
}
/* add_for_charger_end */
#endif
#endif
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");
ili_irq_register(ilits->irq_tirgger_type);
if (ili_tddi_init() < 0) {
ILI_ERR("ILITEK Driver probe failed\n");
ili_irq_unregister();
return -ENODEV;
}
#if SPRD_SYSFS_SUSPEND_RESUME
ili_sysfs_add_device(ilits->dev);
if (sysfs_create_link(NULL, &ilits->dev->kobj, "touchscreen") < 0)
ILI_INFO("Failed to create link!\n");
#else
ilitek_plat_sleep_init();
#endif
ilits->pm_suspend = false;
init_completion(&ilits->pm_completion);
#if CHARGER_NOTIFIER_CALLBACK
#if KERNEL_VERSION(4, 1, 0) <= LINUX_VERSION_CODE
/* add_for_charger_start */
ilitek_plat_charger_init();
/* add_for_charger_end */
#endif
#endif
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");
#if SPRD_SYSFS_SUSPEND_RESUME
ili_sysfs_remove_device(ilits->dev);
#endif
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);
late_initcall(ilitek_plat_dev_init);
module_exit(ilitek_plat_dev_exit);
MODULE_AUTHOR("ILITEK");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,655 @@
/*
* 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 "ili9882.h"
#ifdef CONFIG_DRM
struct drm_panel *ili_active_panel;
static int check_dt(struct device_node *np);
#endif
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 4096 // 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;// 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) {
ILI_ERR("Failed to write pre commands\n");
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) {
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->tp_suspend == true)) {
#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];
ilits->spi->chip_select = 0;
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;
}
#ifdef CONFIG_DRM
static int check_dt(struct device_node *np)
{
int i;
int count;
struct device_node *node;
struct drm_panel *panel;
count = of_count_phandle_with_args(np, "panel", NULL);
if (count <= 0)
return 0;
for (i = 0; i < count; i++) {
node = of_parse_phandle(np, "panel", i);
panel = of_drm_find_panel(node);
of_node_put(node);
if (!IS_ERR(panel)) {
ili_active_panel = panel;
return 0;
}
}
if (node)
pr_err("%s: %s not actived\n", __func__, node->name);
return -ENODEV;
}
#endif
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;
}
#ifdef CONFIG_DRM
{
struct device_node *dp = spi->dev.of_node;
if (check_dt(dp)) {
ILI_ERR("%s: %s not actived\n", __func__, dp->name);
return -ENODEV;
}
}
#endif
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