6db4831e98
Android 14
455 lines
10 KiB
C
455 lines
10 KiB
C
/* SPDX-License-Identifier: GPL-2.0 */
|
|
/*
|
|
* Copyright (C) 2019 MediaTek Inc.
|
|
*/
|
|
|
|
#include "ilitek_v3.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;
|
|
ilits->tp_data_mode = AP_MODE;
|
|
|
|
if (TDDI_RST_BIND)
|
|
ilits->reset = TP_IC_WHOLE_RST;
|
|
else
|
|
ilits->reset = TP_HW_RST_ONLY;
|
|
|
|
ilits->rst_edge_delay = 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);
|
|
}
|