kernel_samsung_a34x-permissive/drivers/input/touchscreen/ILITEK9882/ilitek_v3_i2c.c
2024-04-28 15:51:13 +02:00

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);
}