1920 lines
56 KiB
C
1920 lines
56 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
#define LOG_TAG "Plat"
|
|
|
|
#include "cts_config.h"
|
|
#include "cts_platform.h"
|
|
#include "cts_core.h"
|
|
#include "cts_firmware.h"
|
|
#include "cts_sysfs.h"
|
|
#include "cts_tcs.h"
|
|
|
|
#ifdef CFG_CTS_PLATFORM_MTK_TPD_SUPPORTED
|
|
int tpd_rst_gpio_index = 0;
|
|
int tpd_int_gpio_index = 1;
|
|
static int tpd_history_x, tpd_history_y;
|
|
#endif
|
|
|
|
#ifdef CFG_CTS_HAS_RESET_PIN
|
|
int cts_plat_set_reset(struct cts_platform_data *pdata, int val)
|
|
{
|
|
cts_info("Set Reset to %s", val ? "HIGH" : "LOW");
|
|
#ifdef CFG_CTS_PLATFORM_MTK_TPD_SUPPORTED
|
|
if (val)
|
|
tpd_gpio_output(tpd_rst_gpio_index, 1);
|
|
else
|
|
tpd_gpio_output(tpd_rst_gpio_index, 0);
|
|
#else
|
|
if (val)
|
|
gpio_set_value(pdata->rst_gpio, 1);
|
|
else
|
|
gpio_set_value(pdata->rst_gpio, 0);
|
|
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
int cts_plat_reset_device(struct cts_platform_data *pdata)
|
|
{
|
|
/* !!!can not be modified */
|
|
/* !!!can not be modified */
|
|
/* !!!can not be modified */
|
|
cts_plat_set_reset(pdata, 1);
|
|
mdelay(1);
|
|
cts_plat_set_reset(pdata, 0);
|
|
mdelay(5);/* 1ms */
|
|
cts_plat_set_reset(pdata, 1);
|
|
/* under normal mode, delay over 50ms */
|
|
#ifdef CONFIG_CTS_I2C_HOST
|
|
mdelay(120);
|
|
#else
|
|
mdelay(70);
|
|
#endif
|
|
return 0;
|
|
}
|
|
#endif /* CFG_CTS_HAS_RESET_PIN */
|
|
|
|
#ifdef CFG_CTS_MANUAL_CS
|
|
int cts_plat_set_cs(struct cts_platform_data *pdata, int val)
|
|
{
|
|
#ifdef CFG_CTS_PLATFORM_MTK_TPD_SUPPORTED
|
|
if (val)
|
|
pinctrl_select_state(pdata->pinctrl1, pdata->spi_cs_high);
|
|
else
|
|
pinctrl_select_state(pdata->pinctrl1, pdata->spi_cs_low);
|
|
#else
|
|
if (val)
|
|
gpio_set_value(pdata->cs_gpio, 1);
|
|
else
|
|
gpio_set_value(pdata->cs_gpio, 0);
|
|
#endif
|
|
return 0;
|
|
}
|
|
#endif /* CFG_CTS_MANUAL_CS */
|
|
|
|
|
|
#ifdef CONFIG_CTS_I2C_HOST
|
|
size_t cts_plat_get_max_i2c_xfer_size(struct cts_platform_data *pdata)
|
|
{
|
|
#ifdef TPD_SUPPORT_I2C_DMA
|
|
if (pdata->i2c_dma_available) {
|
|
return CFG_CTS_MAX_I2C_XFER_SIZE;
|
|
} else {
|
|
return CFG_CTS_MAX_I2C_FIFO_XFER_SIZE;
|
|
}
|
|
#else
|
|
return CFG_CTS_MAX_I2C_XFER_SIZE;
|
|
#endif
|
|
}
|
|
|
|
u8 *cts_plat_get_i2c_xfer_buf(struct cts_platform_data *pdata, size_t xfer_size)
|
|
{
|
|
#ifdef TPD_SUPPORT_I2C_DMA
|
|
if (pdata->i2c_dma_available && xfer_size > CFG_CTS_MAX_I2C_FIFO_XFER_SIZE) {
|
|
return pdata->i2c_dma_buff_va;
|
|
} else
|
|
#endif /* TPD_SUPPORT_I2C_DMA */
|
|
return pdata->i2c_fifo_buf;
|
|
}
|
|
|
|
int cts_plat_i2c_write(struct cts_platform_data *pdata, u8 i2c_addr,
|
|
const void *src, size_t len, int retry, int delay)
|
|
{
|
|
int ret = 0, retries = 0;
|
|
|
|
#ifdef TPD_SUPPORT_I2C_DMA
|
|
struct i2c_msg msg = {
|
|
.addr = i2c_addr,
|
|
.flags = !I2C_M_RD,
|
|
.len = len,
|
|
.timing = 300,
|
|
};
|
|
|
|
if (pdata->i2c_dma_available && len > CFG_CTS_MAX_I2C_FIFO_XFER_SIZE) {
|
|
msg.ext_flag = (pdata->i2c_client->ext_flag | I2C_ENEXT_FLAG | I2C_DMA_FLAG);
|
|
msg.buf = (u8 *)pdata->i2c_dma_buff_pa;
|
|
memcpy(pdata->i2c_dma_buff_va, src, len);
|
|
} else {
|
|
msg.buf = (u8 *)src;
|
|
}
|
|
msg.len = len;
|
|
#else /* TPD_SUPPORT_I2C_DMA */
|
|
struct i2c_msg msg = {
|
|
.flags = !I2C_M_RD,
|
|
.addr = i2c_addr,
|
|
.buf = (u8 *) src,
|
|
.len = len,
|
|
};
|
|
#endif /* TPD_SUPPORT_I2C_DMA */
|
|
|
|
do {
|
|
ret = i2c_transfer(pdata->i2c_client->adapter, &msg, 1);
|
|
if (ret != 1) {
|
|
if (ret >= 0) {
|
|
ret = -EIO;
|
|
}
|
|
|
|
if (delay) {
|
|
mdelay(delay);
|
|
}
|
|
continue;
|
|
} else {
|
|
return 0;
|
|
}
|
|
} while (++retries < retry);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int cts_plat_i2c_read(struct cts_platform_data *pdata, u8 i2c_addr,
|
|
const u8 *wbuf, size_t wlen, void *rbuf, size_t rlen,
|
|
int retry, int delay)
|
|
{
|
|
int num_msg, ret = 0, retries = 0;
|
|
|
|
#ifdef TPD_SUPPORT_I2C_DMA
|
|
struct i2c_msg msgs[2] = {
|
|
{
|
|
.addr = i2c_addr,
|
|
.flags = !I2C_M_RD,
|
|
.len = wlen,
|
|
.buf = (u8 *)wbuf,
|
|
.timing = 300,
|
|
},
|
|
{
|
|
.addr = i2c_addr,
|
|
.flags = I2C_M_RD,
|
|
.len = rlen,
|
|
.timing = 300,
|
|
},
|
|
};
|
|
|
|
if (pdata->i2c_dma_available && rlen > CFG_CTS_MAX_I2C_FIFO_XFER_SIZE) {
|
|
msgs[1].ext_flag = (pdata->i2c_client->ext_flag | I2C_ENEXT_FLAG | I2C_DMA_FLAG);
|
|
msgs[1].buf = (u8 *)pdata->i2c_dma_buff_pa;
|
|
} else {
|
|
msgs[1].buf = (u8 *)rbuf;
|
|
}
|
|
#else /* TPD_SUPPORT_I2C_DMA */
|
|
struct i2c_msg msgs[2] = {
|
|
{
|
|
.addr = i2c_addr,
|
|
.flags = !I2C_M_RD,
|
|
.buf = (u8 *) wbuf,
|
|
.len = wlen },
|
|
{
|
|
.addr = i2c_addr,
|
|
.flags = I2C_M_RD,
|
|
.buf = (u8 *) rbuf,
|
|
.len = rlen }
|
|
};
|
|
#endif /* TPD_SUPPORT_I2C_DMA */
|
|
|
|
if (wbuf == NULL || wlen == 0)
|
|
num_msg = 1;
|
|
else
|
|
num_msg = 2;
|
|
|
|
do {
|
|
ret = i2c_transfer(pdata->i2c_client->adapter,
|
|
msgs + ARRAY_SIZE(msgs) - num_msg, num_msg);
|
|
|
|
if (ret != num_msg) {
|
|
if (ret >= 0)
|
|
ret = -EIO;
|
|
|
|
if (delay)
|
|
mdelay(delay);
|
|
continue;
|
|
} else {
|
|
#ifdef TPD_SUPPORT_I2C_DMA
|
|
if (pdata->i2c_dma_available && rlen > CFG_CTS_MAX_I2C_FIFO_XFER_SIZE) {
|
|
memcpy(rbuf, pdata->i2c_dma_buff_va, rlen);
|
|
}
|
|
#endif /* TPD_SUPPORT_I2C_DMA */
|
|
|
|
return 0;
|
|
}
|
|
} while (++retries < retry);
|
|
|
|
return ret;
|
|
}
|
|
|
|
#else /*CONFIG_CTS_I2C_HOST*/
|
|
|
|
#ifdef CFG_MTK_LEGEND_PLATFORM
|
|
struct mt_chip_conf cts_spi_conf_mt65xx = {
|
|
.setuptime = 15,
|
|
.holdtime = 15,
|
|
.high_time = 21, //for mt6582, 104000khz/(4+4) = 130000khz
|
|
.low_time = 21,
|
|
.cs_idletime = 20,
|
|
.ulthgh_thrsh = 0,
|
|
|
|
.cpol = 0,
|
|
.cpha = 0,
|
|
|
|
.rx_mlsb = 1,
|
|
.tx_mlsb = 1,
|
|
|
|
.tx_endian = 0,
|
|
.rx_endian = 0,
|
|
|
|
.com_mod = FIFO_TRANSFER,
|
|
.pause = 1,
|
|
.finish_intr = 1,
|
|
.deassert = 0,
|
|
.ulthigh = 0,
|
|
.tckdly = 0,
|
|
};
|
|
|
|
typedef enum {
|
|
SPEED_500KHZ = 500,
|
|
SPEED_1MHZ = 1000,
|
|
SPEED_2MHZ = 2000,
|
|
SPEED_3MHZ = 3000,
|
|
SPEED_4MHZ = 4000,
|
|
SPEED_6MHZ = 6000,
|
|
SPEED_8MHZ = 8000,
|
|
SPEED_KEEP,
|
|
SPEED_UNSUPPORTED
|
|
} SPI_SPEED;
|
|
|
|
static int cts_plat_spi_set_mode(struct spi_device *spi, SPI_SPEED speed, int flag)
|
|
{
|
|
struct mt_chip_conf *mcc = &cts_spi_conf_mt65xx;
|
|
int ret;
|
|
|
|
if (flag == 0) {
|
|
mcc->com_mod = FIFO_TRANSFER;
|
|
} else {
|
|
mcc->com_mod = DMA_TRANSFER;
|
|
}
|
|
|
|
switch (speed) {
|
|
case SPEED_500KHZ:
|
|
mcc->high_time = 120;
|
|
mcc->low_time = 120;
|
|
break;
|
|
case SPEED_1MHZ:
|
|
mcc->high_time = 60;
|
|
mcc->low_time = 60;
|
|
break;
|
|
case SPEED_2MHZ:
|
|
mcc->high_time = 30;
|
|
mcc->low_time = 30;
|
|
break;
|
|
case SPEED_3MHZ:
|
|
mcc->high_time = 20;
|
|
mcc->low_time = 20;
|
|
break;
|
|
case SPEED_4MHZ:
|
|
mcc->high_time = 15;
|
|
mcc->low_time = 15;
|
|
break;
|
|
case SPEED_6MHZ:
|
|
mcc->high_time = 10;
|
|
mcc->low_time = 10;
|
|
break;
|
|
case SPEED_8MHZ:
|
|
mcc->high_time = 8;
|
|
mcc->low_time = 8;
|
|
break;
|
|
case SPEED_KEEP:
|
|
case SPEED_UNSUPPORTED:
|
|
break;
|
|
}
|
|
|
|
ret = spi_setup(spi);
|
|
if (ret) {
|
|
cts_err("Spi setup failed %d(%s)", ret, cts_strerror(ret));
|
|
}
|
|
return ret;
|
|
}
|
|
#endif /* CFG_MTK_LEGEND_PLATFORM */
|
|
|
|
int cts_plat_spi_setup(struct cts_platform_data *pdata)
|
|
{
|
|
int ret;
|
|
|
|
pdata->spi_client->chip_select = 0;
|
|
pdata->spi_client->mode = SPI_MODE_0;
|
|
pdata->spi_client->bits_per_word = 8;
|
|
|
|
cts_info(" - chip_select :%d", pdata->spi_client->chip_select);
|
|
cts_info(" - spi_mode :%d", pdata->spi_client->mode);
|
|
cts_info(" - bits_per_word:%d", pdata->spi_client->bits_per_word);
|
|
|
|
#ifdef CFG_MTK_LEGEND_PLATFORM
|
|
pdata->spi_client->controller_data = (void *)&cts_spi_conf_mt65xx;
|
|
ret = spi_setup(pdata->spi_client);
|
|
cts_plat_spi_set_mode(pdata->spi_client, pdata->spi_speed, 0);
|
|
#else /* CFG_MTK_LEGEND_PLATFORM */
|
|
ret = spi_setup(pdata->spi_client);
|
|
#endif /* CFG_MTK_LEGEND_PLATFORM */
|
|
if (ret) {
|
|
cts_err("spi_setup err!");
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int cts_spi_send_recv(struct cts_platform_data *pdata, size_t len,
|
|
u8 *tx_buffer, u8 *rx_buffer)
|
|
{
|
|
struct chipone_ts_data *cts_data;
|
|
struct spi_message msg;
|
|
struct spi_transfer cmd = {
|
|
.delay_usecs = 0,
|
|
.speed_hz = pdata->spi_speed * 1000u,
|
|
.tx_buf = tx_buffer,
|
|
.rx_buf = rx_buffer,
|
|
.len = len,
|
|
/* .tx_dma = 0, */
|
|
/* .rx_dma = 0, */
|
|
};
|
|
int ret = 0;
|
|
|
|
cts_data = container_of(pdata->cts_dev, struct chipone_ts_data, cts_dev);
|
|
|
|
#ifdef CFG_CTS_MANUAL_CS
|
|
cts_plat_set_cs(pdata, 0);
|
|
#endif
|
|
spi_message_init(&msg);
|
|
spi_message_add_tail(&cmd, &msg);
|
|
ret = spi_sync(cts_data->spi_client, &msg);
|
|
if (ret)
|
|
cts_err("spi sync failed %d", ret);
|
|
|
|
#ifdef CFG_CTS_MANUAL_CS
|
|
udelay(100);
|
|
cts_plat_set_cs(pdata, 1);
|
|
#endif
|
|
return ret;
|
|
}
|
|
|
|
size_t cts_plat_get_max_spi_xfer_size(struct cts_platform_data *pdata)
|
|
{
|
|
return CFG_CTS_MAX_SPI_XFER_SIZE;
|
|
}
|
|
|
|
u8 *cts_plat_get_spi_xfer_buf(struct cts_platform_data *pdata, size_t xfer_size)
|
|
{
|
|
return pdata->spi_cache_buf;
|
|
}
|
|
|
|
int cts_plat_spi_write(struct cts_platform_data *pdata, u8 dev_addr,
|
|
const void *src, size_t len, int retry, int delay)
|
|
{
|
|
int ret = 0, retries = 0;
|
|
u16 crc16_calc;
|
|
size_t data_len;
|
|
|
|
if (len > CFG_CTS_MAX_SPI_XFER_SIZE) {
|
|
cts_err("write too much data:wlen=%zu", len);
|
|
return -EIO;
|
|
}
|
|
|
|
if (pdata->cts_dev->rtdata.program_mode) {
|
|
pdata->spi_tx_buf[0] = dev_addr;
|
|
memcpy(&pdata->spi_tx_buf[1], src, len);
|
|
do {
|
|
ret = cts_spi_send_recv(pdata, len + 1, pdata->spi_tx_buf,
|
|
pdata->spi_rx_buf);
|
|
if (ret) {
|
|
cts_err("SPI write failed %d", ret);
|
|
if (delay)
|
|
mdelay(delay);
|
|
} else
|
|
return 0;
|
|
} while (++retries < retry);
|
|
} else {
|
|
data_len = len - 2;
|
|
pdata->spi_tx_buf[0] = dev_addr;
|
|
pdata->spi_tx_buf[1] = *((u8 *) src + 1);
|
|
pdata->spi_tx_buf[2] = *((u8 *) src);
|
|
put_unaligned_le16(data_len, &pdata->spi_tx_buf[3]);
|
|
crc16_calc = (u16) cts_crc32(pdata->spi_tx_buf, 5);
|
|
put_unaligned_le16(crc16_calc, &pdata->spi_tx_buf[5]);
|
|
memcpy(&pdata->spi_tx_buf[7], (char *)src + 2, data_len);
|
|
crc16_calc = (u16) cts_crc32((char *)src + 2, data_len);
|
|
put_unaligned_le16(crc16_calc, &pdata->spi_tx_buf[7 + data_len]);
|
|
do {
|
|
ret = cts_spi_send_recv(pdata, len + 7, pdata->spi_tx_buf,
|
|
pdata->spi_rx_buf);
|
|
udelay(10 * data_len);
|
|
if (ret) {
|
|
cts_err("SPI write failed %d", ret);
|
|
if (delay)
|
|
mdelay(delay);
|
|
} else
|
|
return 0;
|
|
} while (++retries < retry);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int cts_plat_spi_read(struct cts_platform_data *pdata, u8 dev_addr,
|
|
const u8 *wbuf, size_t wlen, void *rbuf, size_t rlen,
|
|
int retry, int delay)
|
|
{
|
|
int ret = 0, retries = 0;
|
|
u16 crc16_calc, crc16_recv;
|
|
|
|
if (wlen > CFG_CTS_MAX_SPI_XFER_SIZE
|
|
|| rlen > CFG_CTS_MAX_SPI_XFER_SIZE) {
|
|
cts_err("write/read too much data:wlen=%zd, rlen=%zd", wlen, rlen);
|
|
return -EIO;
|
|
}
|
|
|
|
if (pdata->cts_dev->rtdata.program_mode) {
|
|
pdata->spi_tx_buf[0] = dev_addr | 0x01;
|
|
memcpy(&pdata->spi_tx_buf[1], wbuf, wlen);
|
|
do {
|
|
ret = cts_spi_send_recv(pdata, rlen + 5, pdata->spi_tx_buf,
|
|
pdata->spi_rx_buf);
|
|
if (ret) {
|
|
cts_err("SPI read failed %d", ret);
|
|
if (delay)
|
|
mdelay(delay);
|
|
continue;
|
|
}
|
|
memcpy(rbuf, pdata->spi_rx_buf + 5, rlen);
|
|
return 0;
|
|
} while (++retries < retry);
|
|
} else {
|
|
do {
|
|
if (wlen != 0) {
|
|
pdata->spi_tx_buf[0] = dev_addr | 0x01;
|
|
pdata->spi_tx_buf[1] = wbuf[1];
|
|
pdata->spi_tx_buf[2] = wbuf[0];
|
|
put_unaligned_le16(rlen, &pdata->spi_tx_buf[3]);
|
|
crc16_calc = (u16) cts_crc32(pdata->spi_tx_buf, 5);
|
|
put_unaligned_le16(crc16_calc, &pdata->spi_tx_buf[5]);
|
|
ret = cts_spi_send_recv(pdata, 7, pdata->spi_tx_buf,
|
|
pdata->spi_rx_buf);
|
|
if (ret) {
|
|
cts_err("SPI read failed %d", ret);
|
|
if (delay)
|
|
mdelay(delay);
|
|
continue;
|
|
}
|
|
}
|
|
memset(pdata->spi_tx_buf, 0, 7);
|
|
pdata->spi_tx_buf[0] = dev_addr | 0x01;
|
|
udelay(100);
|
|
ret = cts_spi_send_recv(pdata, rlen + 2,
|
|
pdata->spi_tx_buf, pdata->spi_rx_buf);
|
|
if (ret) {
|
|
cts_err("SPI read failed %d", ret);
|
|
if (delay)
|
|
mdelay(delay);
|
|
continue;
|
|
}
|
|
memcpy(rbuf, pdata->spi_rx_buf, rlen);
|
|
crc16_calc = (u16) cts_crc32(pdata->spi_rx_buf, rlen);
|
|
crc16_recv = get_unaligned_le16(&pdata->spi_rx_buf[rlen]);
|
|
if (crc16_recv != crc16_calc) {
|
|
cts_err("SPI RX CRC error: rx_crc %04x != %04x",
|
|
crc16_recv, crc16_calc);
|
|
continue;
|
|
}
|
|
return 0;
|
|
} while (++retries < retry);
|
|
}
|
|
if (retries >= retry)
|
|
cts_err("SPI read too much retry");
|
|
|
|
return -EIO;
|
|
}
|
|
|
|
int cts_plat_spi_read_delay_idle(struct cts_platform_data *pdata, u8 dev_addr,
|
|
const u8 *wbuf, size_t wlen, void *rbuf,
|
|
size_t rlen, int retry, int delay, int idle)
|
|
{
|
|
int ret = 0, retries = 0;
|
|
u16 crc;
|
|
|
|
if (wlen > CFG_CTS_MAX_SPI_XFER_SIZE
|
|
|| rlen > CFG_CTS_MAX_SPI_XFER_SIZE) {
|
|
cts_err("write/read too much data:wlen=%zu, rlen=%zu", wlen, rlen);
|
|
return -E2BIG;
|
|
}
|
|
|
|
if (pdata->cts_dev->rtdata.program_mode) {
|
|
pdata->spi_tx_buf[0] = dev_addr | 0x01;
|
|
memcpy(&pdata->spi_tx_buf[1], wbuf, wlen);
|
|
do {
|
|
ret = cts_spi_send_recv(pdata, rlen + 5, pdata->spi_tx_buf,
|
|
pdata->spi_rx_buf);
|
|
if (ret) {
|
|
cts_err("SPI read failed %d", ret);
|
|
if (delay)
|
|
mdelay(delay);
|
|
continue;
|
|
}
|
|
memcpy(rbuf, pdata->spi_rx_buf + 5, rlen);
|
|
return 0;
|
|
} while (++retries < retry);
|
|
} else {
|
|
do {
|
|
if (wlen != 0) {
|
|
pdata->spi_tx_buf[0] = dev_addr | 0x01;
|
|
pdata->spi_tx_buf[1] = wbuf[1];
|
|
pdata->spi_tx_buf[2] = wbuf[0];
|
|
put_unaligned_le16(rlen, &pdata->spi_tx_buf[3]);
|
|
crc = (u16) cts_crc32(pdata->spi_tx_buf, 5);
|
|
put_unaligned_le16(crc, &pdata->spi_tx_buf[5]);
|
|
ret = cts_spi_send_recv(pdata, 7, pdata->spi_tx_buf,
|
|
pdata->spi_rx_buf);
|
|
if (ret) {
|
|
cts_err("SPI read failed %d", ret);
|
|
if (delay)
|
|
mdelay(delay);
|
|
continue;
|
|
}
|
|
}
|
|
memset(pdata->spi_tx_buf, 0, 7);
|
|
pdata->spi_tx_buf[0] = dev_addr | 0x01;
|
|
udelay(idle);
|
|
ret = cts_spi_send_recv(pdata, rlen + 2,
|
|
pdata->spi_tx_buf, pdata->spi_rx_buf);
|
|
if (ret) {
|
|
if (delay)
|
|
mdelay(delay);
|
|
continue;
|
|
}
|
|
memcpy(rbuf, pdata->spi_rx_buf, rlen);
|
|
crc = (u16) cts_crc32(pdata->spi_rx_buf, rlen);
|
|
if (get_unaligned_le16(&pdata->spi_rx_buf[rlen]) != crc)
|
|
continue;
|
|
return 0;
|
|
} while (++retries < retry);
|
|
}
|
|
if (retries >= retry)
|
|
cts_err("cts_plat_spi_read error");
|
|
|
|
return -EIO;
|
|
}
|
|
#endif /*CONFIG_CTS_I2C_HOST*/
|
|
|
|
int cts_plat_is_normal_mode(struct cts_platform_data *pdata)
|
|
{
|
|
struct chipone_ts_data *cts_data;
|
|
u16 fwid;
|
|
int ret;
|
|
|
|
cts_set_normal_addr(pdata->cts_dev);
|
|
cts_data = container_of(pdata->cts_dev, struct chipone_ts_data, cts_dev);
|
|
ret = cts_tcs_get_fw_id(pdata->cts_dev, &fwid);
|
|
if (ret || !cts_is_fwid_valid(fwid))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
static void cts_plat_handle_irq(struct cts_platform_data *pdata)
|
|
{
|
|
int ret;
|
|
|
|
cts_dbg("Handle IRQ");
|
|
|
|
cts_lock_device(pdata->cts_dev);
|
|
ret = cts_irq_handler(pdata->cts_dev);
|
|
if (ret)
|
|
cts_err("Device handle IRQ failed %d", ret);
|
|
cts_unlock_device(pdata->cts_dev);
|
|
}
|
|
|
|
static irqreturn_t cts_plat_irq_handler(int irq, void *dev_id)
|
|
{
|
|
struct cts_platform_data *pdata;
|
|
#ifndef CONFIG_GENERIC_HARDIRQS
|
|
struct chipone_ts_data *cts_data;
|
|
#endif
|
|
|
|
cts_dbg("IRQ handler");
|
|
|
|
pdata = (struct cts_platform_data *)dev_id;
|
|
if (pdata == NULL) {
|
|
cts_err("IRQ handler with NULL dev_id");
|
|
return IRQ_NONE;
|
|
}
|
|
#ifdef CONFIG_GENERIC_HARDIRQS
|
|
cts_plat_handle_irq(pdata);
|
|
#else
|
|
cts_data = container_of(pdata->cts_dev, struct chipone_ts_data, cts_dev);
|
|
|
|
if (queue_work(cts_data->workqueue, &pdata->ts_irq_work)) {
|
|
cts_dbg("IRQ queue work");
|
|
cts_plat_disable_irq(pdata);
|
|
} else
|
|
cts_warn("IRQ handler queue work failed as already on the queue");
|
|
#endif /* CONFIG_GENERIC_HARDIRQS */
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
#ifndef CONFIG_GENERIC_HARDIRQS
|
|
static void cts_plat_touch_dev_irq_work(struct work_struct *work)
|
|
{
|
|
struct cts_platform_data *pdata =
|
|
container_of(work, struct cts_platform_data, ts_irq_work);
|
|
|
|
cts_dbg("IRQ work");
|
|
|
|
cts_plat_handle_irq(pdata);
|
|
|
|
cts_plat_enable_irq(pdata);
|
|
}
|
|
#endif /* CONFIG_GENERIC_HARDIRQS */
|
|
|
|
|
|
#ifdef CFG_CTS_FORCE_UP
|
|
static void cts_plat_touch_timeout_work(struct work_struct *work)
|
|
{
|
|
struct cts_platform_data *pdata = container_of(work,
|
|
struct cts_platform_data, touch_timeout.work);
|
|
|
|
cts_warn("Touch event timeout work");
|
|
|
|
cts_plat_release_all_touch(pdata);
|
|
}
|
|
#endif
|
|
|
|
|
|
#ifndef CFG_CTS_PLATFORM_MTK_TPD_SUPPORTED
|
|
#ifdef CONFIG_CTS_OF
|
|
static int cts_plat_parse_dt(struct cts_platform_data *pdata,
|
|
struct device_node *dev_node)
|
|
{
|
|
int ret = 0;
|
|
|
|
cts_info("Parse device tree");
|
|
|
|
pdata->int_gpio = of_get_named_gpio(dev_node, CFG_CTS_OF_INT_GPIO_NAME, 0);
|
|
if (!gpio_is_valid(pdata->int_gpio)) {
|
|
cts_err("Parse INT GPIO from dt failed %d", pdata->int_gpio);
|
|
pdata->int_gpio = -1;
|
|
}
|
|
cts_info(" %-12s: %d", "int gpio", pdata->int_gpio);
|
|
|
|
pdata->irq = gpio_to_irq(pdata->int_gpio);
|
|
if (pdata->irq < 0) {
|
|
cts_err("Parse irq failed %d", ret);
|
|
return pdata->irq;
|
|
}
|
|
cts_info(" %-12s: %d", "irq num", pdata->irq);
|
|
|
|
#ifdef CFG_CTS_HAS_RESET_PIN
|
|
pdata->rst_gpio = of_get_named_gpio(dev_node, CFG_CTS_OF_RST_GPIO_NAME, 0);
|
|
if (!gpio_is_valid(pdata->rst_gpio)) {
|
|
cts_err("Parse RST GPIO from dt failed %d", pdata->rst_gpio);
|
|
pdata->rst_gpio = -1;
|
|
}
|
|
cts_info(" %-12s: %d", "rst gpio", pdata->rst_gpio);
|
|
#endif /* CFG_CTS_HAS_RESET_PIN */
|
|
|
|
#ifdef CFG_CTS_MANUAL_CS
|
|
pdata->cs_gpio = of_get_named_gpio(dev_node, CFG_CTS_OF_CS_GPIO_NAME, 0);
|
|
if (!gpio_is_valid(pdata->cs_gpio)) {
|
|
cts_err("Parse CS GPIO from dt failed %d", pdata->cs_gpio);
|
|
pdata->cs_gpio = -1;
|
|
}
|
|
cts_info(" %-12s: %d", "cs gpio", pdata->cs_gpio);
|
|
#endif
|
|
|
|
ret = of_property_read_u32(dev_node, CFG_CTS_OF_X_RESOLUTION_NAME,
|
|
&pdata->res_x);
|
|
if (ret)
|
|
cts_warn("Parse X resolution from dt failed %d", ret);
|
|
|
|
cts_info(" %-12s: %d", "X resolution", pdata->res_x);
|
|
|
|
ret = of_property_read_u32(dev_node, CFG_CTS_OF_Y_RESOLUTION_NAME,
|
|
&pdata->res_y);
|
|
if (ret)
|
|
cts_warn("Parse Y resolution from dt failed %d", ret);
|
|
|
|
cts_info(" %-12s: %d", "Y resolution", pdata->res_y);
|
|
|
|
if (of_property_read_u32(dev_node, "chipone,def-build-id", &pdata->build_id)) {
|
|
pdata->build_id = 0;
|
|
cts_info("chipone,build_id undefined.");
|
|
} else
|
|
cts_info("chipone,build_id=0x%04X", pdata->build_id);
|
|
|
|
if (of_property_read_u32(dev_node, "chipone,def-config-id", &pdata->config_id)) {
|
|
pdata->config_id = 0;
|
|
cts_info("chipone,config_id undefined.");
|
|
} else
|
|
cts_info("chipone,config_id=0x%04X", pdata->config_id);
|
|
|
|
#ifdef CFG_CTS_FW_UPDATE_SYS
|
|
ret = of_property_read_string(dev_node, CFG_CTS_OF_PANEL_SUPPLIER,
|
|
&pdata->panel_supplier);
|
|
if (ret) {
|
|
pdata->panel_supplier = NULL;
|
|
cts_warn("read panel supplier failed, ret=%d", ret);
|
|
} else
|
|
cts_info("panel supplier=%s", (char *)pdata->panel_supplier);
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
#endif /* CONFIG_CTS_OF */
|
|
|
|
#ifdef CONFIG_CTS_I2C_HOST
|
|
int cts_init_platform_data(struct cts_platform_data *pdata,
|
|
struct i2c_client *i2c_client)
|
|
#else
|
|
int cts_init_platform_data(struct cts_platform_data *pdata,
|
|
struct spi_device *spi)
|
|
#endif
|
|
{
|
|
struct input_dev *input_dev;
|
|
struct input_dev *pen_dev;
|
|
int ret = 0;
|
|
|
|
cts_info("cts_init_platform_data Init");
|
|
|
|
#ifdef CONFIG_CTS_OF
|
|
{
|
|
struct device *dev;
|
|
|
|
#ifdef CONFIG_CTS_I2C_HOST
|
|
dev = &i2c_client->dev;
|
|
#else
|
|
dev = &spi->dev;
|
|
#endif /* CONFIG_CTS_I2C_HOST */
|
|
ret = cts_plat_parse_dt(pdata, dev->of_node);
|
|
if (ret) {
|
|
cts_err("Parse dt failed %d", ret);
|
|
return ret;
|
|
}
|
|
}
|
|
#endif /* CONFIG_CTS_OF */
|
|
|
|
#ifdef CONFIG_CTS_I2C_HOST
|
|
pdata->i2c_client = i2c_client;
|
|
pdata->i2c_client->irq = pdata->irq;
|
|
#else
|
|
pdata->spi_client = spi;
|
|
pdata->spi_client->irq = pdata->irq;
|
|
#endif /* CONFIG_CTS_I2C_HOST */
|
|
|
|
mutex_init(&pdata->dev_lock);
|
|
spin_lock_init(&pdata->irq_lock);
|
|
|
|
input_dev = input_allocate_device();
|
|
if (input_dev == NULL) {
|
|
cts_err("Failed to allocate input device.");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
/** - Init input device */
|
|
input_dev->name = CFG_CTS_DEVICE_NAME;
|
|
input_dev->name = CFG_CTS_DEVICE_NAME;
|
|
#ifdef CONFIG_CTS_I2C_HOST
|
|
input_dev->id.bustype = BUS_I2C;
|
|
input_dev->dev.parent = &pdata->i2c_client->dev;
|
|
#else
|
|
input_dev->id.bustype = BUS_SPI;
|
|
input_dev->dev.parent = &pdata->spi_client->dev;
|
|
#endif /* CONFIG_CTS_I2C_HOST */
|
|
input_dev->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
|
|
#ifdef CFG_CTS_PALM_DETECT
|
|
set_bit(CFG_CTS_PALM_EVENT, input_dev->keybit);
|
|
#endif
|
|
|
|
#ifdef CFG_CTS_SWAP_XY
|
|
input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, pdata->res_y, 0, 0);
|
|
input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, pdata->res_x, 0, 0);
|
|
#else /* CFG_CTS_SWAP_XY */
|
|
input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, pdata->res_x, 0, 0);
|
|
input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, pdata->res_y, 0, 0);
|
|
#endif /* CFG_CTS_SWAP_XY */
|
|
|
|
input_set_abs_params(input_dev, ABS_MT_PRESSURE, 0, 255, 0, 0);
|
|
input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
|
|
input_set_abs_params(input_dev, ABS_MT_TRACKING_ID, 0,
|
|
CFG_CTS_MAX_TOUCH_NUM * 2, 0, 0);
|
|
|
|
input_set_capability(input_dev, EV_KEY, BTN_TOUCH);
|
|
|
|
#ifdef CONFIG_CTS_SLOTPROTOCOL
|
|
input_mt_init_slots(input_dev, CFG_CTS_MAX_TOUCH_NUM, 0);
|
|
#endif /* CONFIG_CTS_SLOTPROTOCOL */
|
|
__set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
|
|
__set_bit(EV_ABS, input_dev->evbit);
|
|
input_set_drvdata(input_dev, pdata);
|
|
ret = input_register_device(input_dev);
|
|
if (ret) {
|
|
cts_err("Failed to register input device");
|
|
return ret;
|
|
}
|
|
pdata->ts_input_dev = input_dev;
|
|
|
|
|
|
pen_dev = input_allocate_device();
|
|
if (pen_dev == NULL) {
|
|
cts_err("Failed to allocate pen device.");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
#ifdef CONFIG_CTS_I2C_HOST
|
|
pen_dev->id.bustype = BUS_I2C;
|
|
pen_dev->dev.parent = &pdata->i2c_client->dev;
|
|
#else
|
|
pen_dev->id.bustype = BUS_SPI;
|
|
pen_dev->dev.parent = &pdata->spi_client->dev;
|
|
#endif /* CONFIG_CTS_I2C_HOST */
|
|
pen_dev->name = "chipone-tddi,pen";
|
|
pen_dev->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
|
|
__set_bit(ABS_X, pen_dev->absbit);
|
|
__set_bit(ABS_Y, pen_dev->absbit);
|
|
__set_bit(BTN_STYLUS, pen_dev->keybit);
|
|
__set_bit(BTN_STYLUS2, pen_dev->keybit);
|
|
__set_bit(BTN_TOUCH, pen_dev->keybit);
|
|
__set_bit(BTN_TOOL_PEN, pen_dev->keybit);
|
|
__set_bit(INPUT_PROP_DIRECT, pen_dev->propbit);
|
|
input_set_abs_params(pen_dev, ABS_X, 0, pdata->res_x, 0, 0);
|
|
input_set_abs_params(pen_dev, ABS_Y, 0, pdata->res_y, 0, 0);
|
|
input_set_abs_params(pen_dev, ABS_PRESSURE, 0, 4096, 0, 0);
|
|
input_set_abs_params(pen_dev, ABS_TILT_X, -9000, 9000, 0, 0);
|
|
input_set_abs_params(pen_dev, ABS_TILT_Y, -9000, 9000, 0, 0);
|
|
input_set_abs_params(pen_dev, ABS_Z, 0, 36000, 0, 0);
|
|
|
|
ret = input_register_device(pen_dev);
|
|
if (ret) {
|
|
cts_err("Input pen device registration failed");
|
|
input_free_device(pen_dev);
|
|
pen_dev = NULL;
|
|
return ret;
|
|
}
|
|
pdata->pen_input_dev = pen_dev;
|
|
|
|
#if !defined(CONFIG_GENERIC_HARDIRQS)
|
|
INIT_WORK(&pdata->ts_irq_work, cts_plat_touch_dev_irq_work);
|
|
#endif /* CONFIG_GENERIC_HARDIRQS */
|
|
|
|
#ifdef CONFIG_CTS_VIRTUALKEY
|
|
{
|
|
u8 vkey_keymap[CFG_CTS_NUM_VKEY] = CFG_CTS_VKEY_KEYCODES;
|
|
|
|
memcpy(pdata->vkey_keycodes, vkey_keymap, sizeof(vkey_keymap));
|
|
pdata->vkey_num = CFG_CTS_NUM_VKEY;
|
|
}
|
|
#endif /* CONFIG_CTS_VIRTUALKEY */
|
|
|
|
#ifdef CFG_CTS_GESTURE
|
|
{
|
|
u8 gesture_keymap[CFG_CTS_NUM_GESTURE][2] = CFG_CTS_GESTURE_KEYMAP;
|
|
|
|
memcpy(pdata->gesture_keymap, gesture_keymap, sizeof(gesture_keymap));
|
|
pdata->gesture_num = CFG_CTS_NUM_GESTURE;
|
|
}
|
|
#endif /* CFG_CTS_GESTURE */
|
|
|
|
#ifdef CFG_CTS_FORCE_UP
|
|
INIT_DELAYED_WORK(&pdata->touch_timeout, cts_plat_touch_timeout_work);
|
|
#endif
|
|
|
|
#ifndef CONFIG_CTS_I2C_HOST
|
|
pdata->spi_speed = CFG_CTS_SPI_SPEED_KHZ;
|
|
cts_plat_spi_setup(pdata);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
int cts_deinit_platform_data(struct cts_platform_data *pdata)
|
|
{
|
|
cts_info("De-Init platform_data");
|
|
input_unregister_device(pdata->ts_input_dev);
|
|
return 0;
|
|
}
|
|
|
|
int cts_plat_request_resource(struct cts_platform_data *pdata)
|
|
{
|
|
int ret;
|
|
|
|
cts_info("Request resource");
|
|
|
|
ret = gpio_request_one(pdata->int_gpio, GPIOF_IN,
|
|
CFG_CTS_DEVICE_NAME "-int");
|
|
if (ret) {
|
|
cts_err("Request INT gpio (%d) failed %d", pdata->int_gpio, ret);
|
|
goto err_out;
|
|
}
|
|
#ifdef CFG_CTS_HAS_RESET_PIN
|
|
ret = gpio_request_one(pdata->rst_gpio, GPIOF_OUT_INIT_HIGH,
|
|
CFG_CTS_DEVICE_NAME "-rst");
|
|
if (ret) {
|
|
cts_err("Request RST gpio (%d) failed %d", pdata->rst_gpio, ret);
|
|
goto err_free_int;
|
|
}
|
|
#endif /* CFG_CTS_HAS_RESET_PIN */
|
|
|
|
#ifdef CFG_CTS_MANUAL_CS
|
|
ret = gpio_request_one(pdata->cs_gpio, GPIOF_OUT_INIT_HIGH,
|
|
CFG_CTS_DEVICE_NAME "-cs");
|
|
if (ret) {
|
|
cts_err("Request CS gpio (%d) failed %d", pdata->cs_gpio, ret);
|
|
goto err_request_cs_gpio;
|
|
}
|
|
#endif
|
|
|
|
return 0;
|
|
|
|
#ifdef CFG_CTS_MANUAL_CS
|
|
err_request_cs_gpio:
|
|
#endif
|
|
|
|
#ifdef CONFIG_CTS_REGULATOR
|
|
err_free_rst:
|
|
#endif /* CONFIG_CTS_REGULATOR */
|
|
#ifdef CFG_CTS_HAS_RESET_PIN
|
|
gpio_free(pdata->rst_gpio);
|
|
err_free_int:
|
|
#endif /* CFG_CTS_HAS_RESET_PIN */
|
|
gpio_free(pdata->int_gpio);
|
|
err_out:
|
|
return ret;
|
|
}
|
|
|
|
void cts_plat_free_resource(struct cts_platform_data *pdata)
|
|
{
|
|
cts_info("Free resource");
|
|
|
|
if (gpio_is_valid(pdata->int_gpio))
|
|
gpio_free(pdata->int_gpio);
|
|
|
|
#ifdef CFG_CTS_HAS_RESET_PIN
|
|
if (gpio_is_valid(pdata->rst_gpio))
|
|
gpio_free(pdata->rst_gpio);
|
|
|
|
#endif /* CFG_CTS_HAS_RESET_PIN */
|
|
#ifdef CFG_CTS_MANUAL_CS
|
|
if (gpio_is_valid(pdata->cs_gpio))
|
|
gpio_free(pdata->cs_gpio);
|
|
|
|
#endif
|
|
}
|
|
|
|
#else /*CFG_CTS_PLATFORM_MTK_TPD_SUPPORTED*/
|
|
|
|
#ifndef CONFIG_CTS_I2C_HOST
|
|
static int cts_plat_init_dts(struct cts_platform_data *pdata, struct device *device)
|
|
{
|
|
#ifdef CFG_CTS_MANUAL_CS
|
|
struct device_node *node;
|
|
|
|
pdata->pinctrl1 = devm_pinctrl_get(device);
|
|
node = device->of_node;
|
|
if (node) {
|
|
pdata->spi_cs_low = pinctrl_lookup_state(pdata->pinctrl1, "spi_cs_low");
|
|
if (IS_ERR(pdata->spi_cs_low)) {
|
|
cts_err("Cannot find pinctrl spi cs high!\n");
|
|
return -ENOENT;
|
|
}
|
|
pdata->spi_cs_high = pinctrl_lookup_state(pdata->pinctrl1, "spi_cs_high");
|
|
if (IS_ERR(pdata->spi_cs_high)) {
|
|
return -ENOENT;
|
|
}
|
|
return 0;
|
|
}
|
|
return -ENOENT;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
#endif /* CONFIG_CTS_I2C_HOST */
|
|
|
|
#ifdef CONFIG_CTS_I2C_HOST
|
|
int cts_init_platform_data(struct cts_platform_data *pdata,
|
|
struct i2c_client *i2c_client)
|
|
#else
|
|
int cts_init_platform_data(struct cts_platform_data *pdata,
|
|
struct spi_device *spi)
|
|
#endif
|
|
{
|
|
struct device_node *node = NULL;
|
|
u32 ints[2] = { 0, 0 };
|
|
|
|
cts_info("cts_init_platform_data Init");
|
|
|
|
#ifdef CONFIG_CTS_OF
|
|
{
|
|
struct device *dev;
|
|
|
|
#ifdef CONFIG_CTS_I2C_HOST
|
|
dev = &i2c_client->dev;
|
|
#else
|
|
dev = &spi->dev;
|
|
#endif /* CONFIG_CTS_I2C_HOST */
|
|
}
|
|
#endif /* CONFIG_CTS_OF */
|
|
|
|
#ifdef CONFIG_CTS_I2C_HOST
|
|
pdata->i2c_client = i2c_client;
|
|
#else
|
|
pdata->spi_client = spi;
|
|
#endif /* CONFIG_CTS_I2C_HOST */
|
|
|
|
pdata->ts_input_dev = tpd->dev;
|
|
|
|
spin_lock_init(&pdata->irq_lock);
|
|
mutex_init(&pdata->dev_lock);
|
|
|
|
#if !defined(CONFIG_GENERIC_HARDIRQS)
|
|
INIT_WORK(&pdata->ts_irq_work, cts_plat_touch_dev_irq_work);
|
|
#endif /* CONFIG_GENERIC_HARDIRQS */
|
|
|
|
if ((node = of_find_matching_node(node, touch_of_match)) == NULL) {
|
|
cts_err("Find touch eint node failed");
|
|
return -ENODATA;
|
|
}
|
|
if (of_property_read_u32_array(node, "debounce", ints, ARRAY_SIZE(ints)) == 0) {
|
|
gpio_set_debounce(ints[0], ints[1]);
|
|
} else {
|
|
cts_info("Debounce time not found");
|
|
}
|
|
pdata->irq = irq_of_parse_and_map(node, 0);
|
|
if (pdata->irq == 0) {
|
|
cts_err("Parse irq in dts failed");
|
|
return -ENODEV;
|
|
}
|
|
|
|
#ifdef CONFIG_CTS_VIRTUALKEY
|
|
pdata->vkey_num = tpd_dts_data.tpd_keycnt;
|
|
#endif /* CONFIG_CTS_VIRTUALKEY */
|
|
|
|
#ifdef CFG_CTS_GESTURE
|
|
{
|
|
u8 gesture_keymap[CFG_CTS_NUM_GESTURE][2] = CFG_CTS_GESTURE_KEYMAP;
|
|
memcpy(pdata->gesture_keymap, gesture_keymap, sizeof(gesture_keymap));
|
|
pdata->gesture_num = CFG_CTS_NUM_GESTURE;
|
|
}
|
|
#endif /* CFG_CTS_GESTURE */
|
|
|
|
#ifdef TPD_SUPPORT_I2C_DMA
|
|
tpd->dev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
|
|
pdata->i2c_dma_buff_va = (u8 *)dma_alloc_coherent(&tpd->dev->dev,
|
|
CFG_CTS_MAX_I2C_XFER_SIZE, &pdata->i2c_dma_buff_pa, GFP_KERNEL);
|
|
if (pdata->i2c_dma_buff_va == NULL) {
|
|
cts_err("Allocate I2C DMA Buffer failed!");
|
|
//return -ENOMEM;
|
|
} else {
|
|
pdata->i2c_dma_available = true;
|
|
}
|
|
#endif /* TPD_SUPPORT_I2C_DMA */
|
|
|
|
#ifdef CFG_CTS_FORCE_UP
|
|
INIT_DELAYED_WORK(&pdata->touch_timeout, cts_plat_touch_timeout_work);
|
|
#endif
|
|
|
|
#ifndef CONFIG_CTS_I2C_HOST
|
|
cts_plat_init_dts(pdata, &spi->dev);
|
|
pdata->spi_speed = CFG_CTS_SPI_SPEED_KHZ;
|
|
cts_plat_spi_setup(pdata);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
int cts_plat_request_resource(struct cts_platform_data *pdata)
|
|
{
|
|
cts_info("Request resource");
|
|
|
|
tpd_gpio_as_int(tpd_int_gpio_index);
|
|
tpd_gpio_output(tpd_rst_gpio_index, 1);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void cts_plat_free_resource(struct cts_platform_data *pdata)
|
|
{
|
|
cts_info("Free resource");
|
|
|
|
/**
|
|
* Note:
|
|
* If resource request without managed, should free all resource
|
|
* requested in cts_plat_request_resource().
|
|
*/
|
|
#ifdef TPD_SUPPORT_I2C_DMA
|
|
if (pdata->i2c_dma_buff_va) {
|
|
dma_free_coherent(&tpd->dev->dev, CFG_CTS_MAX_I2C_XFER_SIZE,
|
|
pdata->i2c_dma_buff_va, pdata->i2c_dma_buff_pa);
|
|
pdata->i2c_dma_buff_va = NULL;
|
|
pdata->i2c_dma_buff_pa = 0;
|
|
}
|
|
#endif /* TPD_SUPPORT_I2C_DMA */
|
|
}
|
|
#endif /*CFG_CTS_PLATFORM_MTK_TPD_SUPPORTED*/
|
|
|
|
|
|
int cts_plat_request_irq(struct cts_platform_data *pdata)
|
|
{
|
|
int ret;
|
|
|
|
cts_info("Request IRQ");
|
|
|
|
#ifdef CONFIG_GENERIC_HARDIRQS
|
|
ret = request_threaded_irq(pdata->irq, NULL, cts_plat_irq_handler,
|
|
IRQF_TRIGGER_FALLING | IRQF_ONESHOT, CFG_CTS_DRIVER_NAME, pdata);
|
|
#else /* CONFIG_GENERIC_HARDIRQS */
|
|
ret = request_irq(pdata->irq, cts_plat_irq_handler,
|
|
IRQF_TRIGGER_FALLING | IRQF_ONESHOT, CFG_CTS_DRIVER_NAME, pdata);
|
|
#endif /* CONFIG_GENERIC_HARDIRQS */
|
|
if (ret) {
|
|
cts_err("Request IRQ failed %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
cts_plat_disable_irq(pdata);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void cts_plat_free_irq(struct cts_platform_data *pdata)
|
|
{
|
|
free_irq(pdata->irq, pdata);
|
|
}
|
|
|
|
int cts_plat_enable_irq(struct cts_platform_data *pdata)
|
|
{
|
|
unsigned long irqflags;
|
|
|
|
cts_dbg("Enable IRQ");
|
|
|
|
if (pdata->irq > 0) {
|
|
spin_lock_irqsave(&pdata->irq_lock, irqflags);
|
|
if (pdata->irq_is_disable) {/* && !cts_is_device_suspended(pdata->chip)) */
|
|
cts_dbg("Real enable IRQ");
|
|
enable_irq(pdata->irq);
|
|
pdata->irq_is_disable = false;
|
|
}
|
|
spin_unlock_irqrestore(&pdata->irq_lock, irqflags);
|
|
|
|
return 0;
|
|
}
|
|
|
|
return -ENODEV;
|
|
}
|
|
|
|
int cts_plat_disable_irq(struct cts_platform_data *pdata)
|
|
{
|
|
unsigned long irqflags;
|
|
|
|
cts_dbg("Disable IRQ");
|
|
|
|
if (pdata->irq > 0) {
|
|
spin_lock_irqsave(&pdata->irq_lock, irqflags);
|
|
if (!pdata->irq_is_disable) {
|
|
cts_dbg("Real disable IRQ");
|
|
disable_irq_nosync(pdata->irq);
|
|
pdata->irq_is_disable = true;
|
|
}
|
|
spin_unlock_irqrestore(&pdata->irq_lock, irqflags);
|
|
|
|
return 0;
|
|
}
|
|
|
|
return -ENODEV;
|
|
}
|
|
|
|
int cts_plat_get_int_pin(struct cts_platform_data *pdata)
|
|
{
|
|
#ifndef CFG_CTS_PLATFORM_MTK_TPD_SUPPORTED
|
|
return gpio_get_value(pdata->int_gpio);
|
|
#else
|
|
cts_err("MTK platform can not get INT pin value");
|
|
return -ENOTSUPP;
|
|
#endif
|
|
}
|
|
|
|
int cts_plat_init_touch_device(struct cts_platform_data *pdata)
|
|
{
|
|
cts_info("Init touch device");
|
|
|
|
#ifdef CFG_CTS_PLATFORM_MTK_TPD_SUPPORTED
|
|
return input_mt_init_slots(pdata->ts_input_dev,
|
|
tpd_dts_data.touch_max_num, INPUT_MT_DIRECT);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
void cts_plat_deinit_touch_device(struct cts_platform_data *pdata)
|
|
{
|
|
cts_info("De-init touch device");
|
|
|
|
#ifndef CONFIG_GENERIC_HARDIRQS
|
|
if (work_pending(&pdata->ts_irq_work)) {
|
|
cancel_work_sync(&pdata->ts_irq_work);
|
|
}
|
|
#endif /* CONFIG_GENERIC_HARDIRQS */
|
|
}
|
|
|
|
#ifdef CFG_CTS_PALM_DETECT
|
|
void cts_report_palm_event(struct cts_platform_data *pdata)
|
|
{
|
|
input_report_key(pdata->ts_input_dev, CFG_CTS_PALM_EVENT, 1);
|
|
input_sync(pdata->ts_input_dev);
|
|
msleep(100);
|
|
input_report_key(pdata->ts_input_dev, CFG_CTS_PALM_EVENT, 0);
|
|
input_sync(pdata->ts_input_dev);
|
|
}
|
|
#endif
|
|
|
|
int cts_plat_process_touch_msg(struct cts_platform_data *pdata,
|
|
struct cts_device_touch_msg *msgs, int num)
|
|
{
|
|
struct chipone_ts_data *cts_data;
|
|
struct input_dev *input_dev = pdata->ts_input_dev;
|
|
int i;
|
|
int contact = 0;
|
|
#ifdef CONFIG_CTS_SLOTPROTOCOL
|
|
static unsigned char finger_last[CFG_CTS_MAX_TOUCH_NUM] = { 0 };
|
|
unsigned char finger_current[CFG_CTS_MAX_TOUCH_NUM] = { 0 };
|
|
#endif
|
|
|
|
cts_dbg("Process touch %d msgs", num);
|
|
|
|
cts_data = container_of(pdata->cts_dev, struct chipone_ts_data, cts_dev);
|
|
|
|
if (num == 0 || num > CFG_CTS_MAX_TOUCH_NUM)
|
|
return 0;
|
|
|
|
for (i = 0; i < num; i++) {
|
|
u16 x, y;
|
|
|
|
x = (msgs[i].xl) | (msgs[i].xh << 8);
|
|
y = (msgs[i].yl) | (msgs[i].yh << 8);
|
|
|
|
cts_dbg(" Process touch msg[%d]: id[%u] ev=%u x=%u y=%u p=%u",
|
|
i, msgs[i].id, msgs[i].event, x, y, msgs[i].pressure);
|
|
if (msgs[i].event == CTS_DEVICE_TOUCH_EVENT_DOWN
|
|
|| msgs[i].event == CTS_DEVICE_TOUCH_EVENT_MOVE
|
|
|| msgs[i].event == CTS_DEVICE_TOUCH_EVENT_STAY) {
|
|
if (msgs[i].id < CFG_CTS_MAX_TOUCH_NUM)
|
|
finger_current[msgs[i].id] = 1;
|
|
}
|
|
#ifdef CONFIG_CTS_SLOTPROTOCOL
|
|
/* input_mt_slot(input_dev, msgs[i].id); */
|
|
switch (msgs[i].event) {
|
|
case CTS_DEVICE_TOUCH_EVENT_DOWN:
|
|
#ifdef CFG_CTS_PLATFORM_MTK_TPD_SUPPORTED
|
|
TPD_DEBUG_SET_TIME;
|
|
TPD_EM_PRINT(x, y, x, y, msgs[i].id, 1);
|
|
tpd_history_x = x;
|
|
tpd_history_y = y;
|
|
#ifdef CONFIG_MTK_BOOT
|
|
if (tpd_dts_data.use_tpd_button) {
|
|
if (FACTORY_BOOT == get_boot_mode() ||
|
|
RECOVERY_BOOT == get_boot_mode())
|
|
tpd_button(x, y, 1);
|
|
}
|
|
#endif /* CONFIG_MTK_BOOT */
|
|
#endif /* CFG_CTS_PLATFORM_MTK_TPD_SUPPORTED */
|
|
case CTS_DEVICE_TOUCH_EVENT_MOVE:
|
|
case CTS_DEVICE_TOUCH_EVENT_STAY:
|
|
contact++;
|
|
input_mt_slot(input_dev, msgs[i].id);
|
|
input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, true);
|
|
input_report_abs(input_dev, ABS_MT_POSITION_X,x);
|
|
input_report_abs(input_dev, ABS_MT_POSITION_Y,y);
|
|
input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, msgs[i].pressure);
|
|
input_report_abs(input_dev, ABS_MT_PRESSURE, msgs[i].pressure);
|
|
break;
|
|
|
|
case CTS_DEVICE_TOUCH_EVENT_UP:
|
|
#ifdef CFG_CTS_PLATFORM_MTK_TPD_SUPPORTED
|
|
TPD_DEBUG_SET_TIME;
|
|
TPD_EM_PRINT(tpd_history_x, tpd_history_y, tpd_history_x, tpd_history_y, msgs[i].id, 0);
|
|
tpd_history_x = 0;
|
|
tpd_history_y = 0;
|
|
#ifdef CONFIG_MTK_BOOT
|
|
if (tpd_dts_data.use_tpd_button) {
|
|
if (FACTORY_BOOT == get_boot_mode() ||
|
|
RECOVERY_BOOT == get_boot_mode())
|
|
tpd_button(0, 0, 0);
|
|
}
|
|
#endif /* CONFIG_MTK_BOOT */
|
|
//input_report_key(input_dev, BTN_TOUCH, 0);
|
|
//input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, false);
|
|
#endif /* CFG_CTS_PLATFORM_MTK_TPD_SUPPORTED */
|
|
break;
|
|
|
|
default:
|
|
cts_warn("Process touch msg with unknwon event %u id %u",
|
|
msgs[i].event, msgs[i].id);
|
|
break;
|
|
}
|
|
#else /* CONFIG_CTS_SLOTPROTOCOL */
|
|
/**
|
|
* If the driver reports one of BTN_TOUCH or ABS_PRESSURE
|
|
* in addition to the ABS_MT events, the last SYN_MT_REPORT event
|
|
* may be omitted. Otherwise, the last SYN_REPORT will be dropped
|
|
* by the input core, resulting in no zero-contact event
|
|
* reaching userland.
|
|
*/
|
|
switch (msgs[i].event) {
|
|
case CTS_DEVICE_TOUCH_EVENT_DOWN:
|
|
case CTS_DEVICE_TOUCH_EVENT_MOVE:
|
|
case CTS_DEVICE_TOUCH_EVENT_STAY:
|
|
contact++;
|
|
input_report_abs(input_dev, ABS_MT_PRESSURE, msgs[i].pressure);
|
|
input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, msgs[i].pressure);
|
|
input_report_key(input_dev, BTN_TOUCH, 1);
|
|
input_report_abs(input_dev, ABS_MT_POSITION_X, x);
|
|
input_report_abs(input_dev, ABS_MT_POSITION_Y, y);
|
|
input_mt_sync(input_dev);
|
|
break;
|
|
|
|
case CTS_DEVICE_TOUCH_EVENT_UP:
|
|
break;
|
|
default:
|
|
cts_warn("Process touch msg with unknwon event %u id %u",
|
|
msgs[i].event, msgs[i].id);
|
|
break;
|
|
}
|
|
#endif /* CONFIG_CTS_SLOTPROTOCOL */
|
|
}
|
|
|
|
#ifdef CONFIG_CTS_SLOTPROTOCOL
|
|
for (i = 0; i < CFG_CTS_MAX_TOUCH_NUM; i++) {
|
|
if (finger_last[i] != 0 && finger_current[i] == 0) {
|
|
input_mt_slot(input_dev, i);
|
|
input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, false);
|
|
}
|
|
finger_last[i] = finger_current[i];
|
|
}
|
|
input_report_key(input_dev, BTN_TOUCH, contact > 0);
|
|
#else /* CONFIG_CTS_SLOTPROTOCOL */
|
|
if (contact == 0) {
|
|
input_report_key(input_dev, BTN_TOUCH, 0);
|
|
input_mt_sync(input_dev);
|
|
}
|
|
#endif /* CONFIG_CTS_SLOTPROTOCOL */
|
|
input_sync(input_dev);
|
|
|
|
#ifdef CFG_CTS_FORCE_UP
|
|
if (contact) {
|
|
if (delayed_work_pending(&pdata->touch_timeout)) {
|
|
mod_delayed_work(cts_data->workqueue,
|
|
&pdata->touch_timeout, msecs_to_jiffies(100));
|
|
} else {
|
|
queue_delayed_work(cts_data->workqueue,
|
|
&pdata->touch_timeout, msecs_to_jiffies(100));
|
|
}
|
|
} else {
|
|
cancel_delayed_work_sync(&pdata->touch_timeout);
|
|
}
|
|
#endif
|
|
|
|
#ifdef CFG_CTS_HEARTBEAT_MECHANISM
|
|
if (contact) {
|
|
if (delayed_work_pending(&cts_data->heart_work)) {
|
|
mod_delayed_work(cts_data->heart_workqueue,
|
|
&cts_data->heart_work, msecs_to_jiffies(2000));
|
|
} else {
|
|
queue_delayed_work(cts_data->heart_workqueue,
|
|
&cts_data->heart_work, msecs_to_jiffies(2000));
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
int cts_plat_process_stylus_msg(struct cts_platform_data *pdata,
|
|
struct cts_device_stylus_msg *msgs, int num)
|
|
{
|
|
struct chipone_ts_data *cts_data;
|
|
struct input_dev *pen_dev = pdata->pen_input_dev;
|
|
int i;
|
|
int contact = 0;
|
|
|
|
cts_dbg("Process stylus %d msgs", num);
|
|
|
|
cts_data = container_of(pdata->cts_dev, struct chipone_ts_data, cts_dev);
|
|
|
|
if (num == 0 || num > CFG_CTS_MAX_STYLUS_NUM)
|
|
return 0;
|
|
|
|
for (i = 0; i < num; i++) {
|
|
u16 x, y, p;
|
|
|
|
x = (msgs[i].tip_xl) | (msgs[i].tip_xh << 8);
|
|
y = (msgs[i].tip_yl) | (msgs[i].tip_yh << 8);
|
|
p = msgs[i].pressure_l | (msgs[i].pressure_h << 8);
|
|
|
|
cts_dbg(" Process stylus msg[%d]: id[%u] ev=%u x=%u y=%u p=%u"
|
|
" tx=%d ty=%d btn0=%d btn1=%d btn2=%d",
|
|
i, msgs[i].id, msgs[i].event, x, y, p, msgs[i].tiltx, msgs[i].tilty,
|
|
msgs[i].btn0, msgs[i].btn1, msgs[i].btn2);
|
|
|
|
/* Report stylus button */
|
|
input_report_key(pen_dev, BTN_STYLUS, msgs[i].btn0);
|
|
input_report_key(pen_dev, BTN_STYLUS2, msgs[i].btn1);
|
|
|
|
switch (msgs[i].event) {
|
|
case CTS_DEVICE_TOUCH_EVENT_DOWN:
|
|
case CTS_DEVICE_TOUCH_EVENT_MOVE:
|
|
case CTS_DEVICE_TOUCH_EVENT_STAY:
|
|
contact++;
|
|
input_report_abs(pen_dev, ABS_X, x);
|
|
input_report_abs(pen_dev, ABS_Y, y);
|
|
input_report_abs(pen_dev, ABS_PRESSURE, p);
|
|
input_report_abs(pen_dev, ABS_TILT_X, msgs[i].tiltx);
|
|
input_report_abs(pen_dev, ABS_TILT_Y, msgs[i].tilty);
|
|
break;
|
|
|
|
case CTS_DEVICE_TOUCH_EVENT_UP:
|
|
break;
|
|
default:
|
|
cts_warn("Process stylus msg with unknwon event %u id %u",
|
|
msgs[i].event, msgs[i].id);
|
|
break;
|
|
}
|
|
}
|
|
|
|
input_report_key(pen_dev, BTN_TOUCH, contact > 0);
|
|
input_report_key(pen_dev, BTN_TOOL_PEN, contact > 0);
|
|
input_sync(pen_dev);
|
|
|
|
#ifdef CFG_CTS_FORCE_UP
|
|
if (contact) {
|
|
if (delayed_work_pending(&pdata->touch_timeout)) {
|
|
mod_delayed_work(cts_data->workqueue,
|
|
&pdata->touch_timeout, msecs_to_jiffies(100));
|
|
} else {
|
|
queue_delayed_work(cts_data->workqueue,
|
|
&pdata->touch_timeout, msecs_to_jiffies(100));
|
|
}
|
|
} else {
|
|
cancel_delayed_work_sync(&pdata->touch_timeout);
|
|
}
|
|
#endif
|
|
|
|
#ifdef CFG_CTS_HEARTBEAT_MECHANISM
|
|
if (contact) {
|
|
if (delayed_work_pending(&cts_data->heart_work)) {
|
|
mod_delayed_work(cts_data->heart_workqueue,
|
|
&cts_data->heart_work, msecs_to_jiffies(2000));
|
|
} else {
|
|
queue_delayed_work(cts_data->heart_workqueue,
|
|
&cts_data->heart_work, msecs_to_jiffies(2000));
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef CFG_CTS_FINGER_STYLUS_SUPPORTED
|
|
int cts_plat_process_touch_stylus(struct cts_platform_data *pdata,
|
|
struct cts_device_touch_info *touch_info)
|
|
{
|
|
struct chipone_ts_data *cts_data;
|
|
struct input_dev *input_dev = pdata->ts_input_dev;
|
|
struct input_dev *pen_dev = pdata->pen_input_dev;
|
|
struct cts_device_touch_msg *msgs = touch_info->msgs;
|
|
int touch_num = touch_info->num_msg;
|
|
struct cts_device_stylus_msg *smsgs = touch_info->smsgs;
|
|
int stylus_num = touch_info->stylus_num;
|
|
int i;
|
|
int contact = 0;
|
|
#ifdef CONFIG_CTS_SLOTPROTOCOL
|
|
static unsigned char finger_last[CFG_CTS_MAX_TOUCH_NUM] = { 0 };
|
|
unsigned char finger_current[CFG_CTS_MAX_TOUCH_NUM] = { 0 };
|
|
#endif
|
|
|
|
cts_dbg("Process touch %d msgs, stylus %d msgs", touch_num, stylus_num);
|
|
|
|
cts_data = container_of(pdata->cts_dev, struct chipone_ts_data, cts_dev);
|
|
|
|
if (touch_num == 0 || touch_num > CFG_CTS_MAX_TOUCH_NUM) {
|
|
goto process_stylus;
|
|
}
|
|
|
|
for (i = 0; i < touch_num; i++) {
|
|
u16 x, y;
|
|
|
|
x = (msgs[i].xl) | (msgs[i].xh << 8);
|
|
y = (msgs[i].yl) | (msgs[i].yh << 8);
|
|
|
|
cts_dbg(" Process touch msg[%d]: id[%u] ev=%u x=%u y=%u p=%u",
|
|
i, msgs[i].id, msgs[i].event, x, y, msgs[i].pressure);
|
|
if (msgs[i].event == CTS_DEVICE_TOUCH_EVENT_DOWN
|
|
|| msgs[i].event == CTS_DEVICE_TOUCH_EVENT_MOVE
|
|
|| msgs[i].event == CTS_DEVICE_TOUCH_EVENT_STAY) {
|
|
if (msgs[i].id < CFG_CTS_MAX_TOUCH_NUM)
|
|
finger_current[msgs[i].id] = 1;
|
|
}
|
|
#ifdef CONFIG_CTS_SLOTPROTOCOL
|
|
/* input_mt_slot(input_dev, msgs[i].id); */
|
|
switch (msgs[i].event) {
|
|
case CTS_DEVICE_TOUCH_EVENT_DOWN:
|
|
#ifdef CFG_CTS_PLATFORM_MTK_TPD_SUPPORTED
|
|
TPD_DEBUG_SET_TIME;
|
|
TPD_EM_PRINT(x, y, x, y, msgs[i].id, 1);
|
|
tpd_history_x = x;
|
|
tpd_history_y = y;
|
|
#ifdef CONFIG_MTK_BOOT
|
|
if (tpd_dts_data.use_tpd_button) {
|
|
if (FACTORY_BOOT == get_boot_mode() ||
|
|
RECOVERY_BOOT == get_boot_mode())
|
|
tpd_button(x, y, 1);
|
|
}
|
|
#endif /* CONFIG_MTK_BOOT */
|
|
#endif /* CFG_CTS_PLATFORM_MTK_TPD_SUPPORTED */
|
|
case CTS_DEVICE_TOUCH_EVENT_MOVE:
|
|
case CTS_DEVICE_TOUCH_EVENT_STAY:
|
|
contact++;
|
|
input_mt_slot(input_dev, msgs[i].id);
|
|
input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, true);
|
|
input_report_abs(input_dev, ABS_MT_POSITION_X, x);
|
|
input_report_abs(input_dev, ABS_MT_POSITION_Y, y);
|
|
input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, msgs[i].pressure);
|
|
input_report_abs(input_dev, ABS_MT_PRESSURE, msgs[i].pressure);
|
|
break;
|
|
|
|
case CTS_DEVICE_TOUCH_EVENT_UP:
|
|
#ifdef CFG_CTS_PLATFORM_MTK_TPD_SUPPORTED
|
|
TPD_DEBUG_SET_TIME;
|
|
TPD_EM_PRINT(tpd_history_x, tpd_history_y, tpd_history_x, tpd_history_y, msgs[i].id, 0);
|
|
tpd_history_x = 0;
|
|
tpd_history_y = 0;
|
|
#ifdef CONFIG_MTK_BOOT
|
|
if (tpd_dts_data.use_tpd_button) {
|
|
if (FACTORY_BOOT == get_boot_mode() ||
|
|
RECOVERY_BOOT == get_boot_mode())
|
|
tpd_button(0, 0, 0);
|
|
}
|
|
#endif /* CONFIG_MTK_BOOT */
|
|
#endif /* CFG_CTS_PLATFORM_MTK_TPD_SUPPORTED */
|
|
break;
|
|
default:
|
|
cts_warn("Process touch msg with unknwon event %u id %u",
|
|
msgs[i].event, msgs[i].id);
|
|
break;
|
|
}
|
|
#else /* CONFIG_CTS_SLOTPROTOCOL */
|
|
/**
|
|
* If the driver reports one of BTN_TOUCH or ABS_PRESSURE
|
|
* in addition to the ABS_MT events, the last SYN_MT_REPORT event
|
|
* may be omitted. Otherwise, the last SYN_REPORT will be dropped
|
|
* by the input core, resulting in no zero-contact event
|
|
* reaching userland.
|
|
*/
|
|
switch (msgs[i].event) {
|
|
case CTS_DEVICE_TOUCH_EVENT_DOWN:
|
|
case CTS_DEVICE_TOUCH_EVENT_MOVE:
|
|
case CTS_DEVICE_TOUCH_EVENT_STAY:
|
|
contact++;
|
|
input_report_abs(input_dev, ABS_MT_PRESSURE, msgs[i].pressure);
|
|
input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, msgs[i].pressure);
|
|
input_report_key(input_dev, BTN_TOUCH, 1);
|
|
input_report_abs(input_dev, ABS_MT_POSITION_X, x);
|
|
input_report_abs(input_dev, ABS_MT_POSITION_Y, y);
|
|
input_mt_sync(input_dev);
|
|
break;
|
|
|
|
case CTS_DEVICE_TOUCH_EVENT_UP:
|
|
break;
|
|
default:
|
|
cts_warn("Process touch msg with unknwon event %u id %u",
|
|
msgs[i].event, msgs[i].id);
|
|
break;
|
|
}
|
|
#endif /* CONFIG_CTS_SLOTPROTOCOL */
|
|
}
|
|
|
|
#ifdef CONFIG_CTS_SLOTPROTOCOL
|
|
for (i = 0; i < CFG_CTS_MAX_TOUCH_NUM; i++) {
|
|
if (finger_last[i] != 0 && finger_current[i] == 0) {
|
|
input_mt_slot(input_dev, i);
|
|
input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, false);
|
|
}
|
|
finger_last[i] = finger_current[i];
|
|
}
|
|
input_report_key(input_dev, BTN_TOUCH, contact > 0);
|
|
#else
|
|
if (contact == 0) {
|
|
input_report_key(input_dev, BTN_TOUCH, 0);
|
|
input_mt_sync(input_dev);
|
|
}
|
|
#endif
|
|
input_sync(input_dev);
|
|
|
|
process_stylus:
|
|
if (stylus_num == 0 || stylus_num > CFG_CTS_MAX_STYLUS_NUM) {
|
|
return 0;
|
|
}
|
|
|
|
for (i = 0; i < stylus_num; i++) {
|
|
u16 x, y, p;
|
|
|
|
x = (smsgs[i].tip_xl) | (smsgs[i].tip_xh << 8);
|
|
y = (smsgs[i].tip_yl) | (smsgs[i].tip_yh << 8);
|
|
p = smsgs[i].pressure_l | (smsgs[i].pressure_h << 8);
|
|
|
|
cts_dbg(" Process stylus msg[%d]: id[%u] ev=%u x=%u y=%u p=%u"
|
|
" tx=%d ty=%d btn0=%d btn1=%d btn2=%d",
|
|
i, smsgs[i].id, smsgs[i].event, x, y, p, smsgs[i].tiltx, smsgs[i].tilty,
|
|
smsgs[i].btn0, smsgs[i].btn1, smsgs[i].btn2);
|
|
|
|
/* Report stylus button */
|
|
input_report_key(pen_dev, BTN_STYLUS, smsgs[i].btn0);
|
|
input_report_key(pen_dev, BTN_STYLUS2, smsgs[i].btn1);
|
|
|
|
switch (smsgs[i].event) {
|
|
case CTS_DEVICE_TOUCH_EVENT_DOWN:
|
|
case CTS_DEVICE_TOUCH_EVENT_MOVE:
|
|
case CTS_DEVICE_TOUCH_EVENT_STAY:
|
|
contact++;
|
|
input_report_abs(pen_dev, ABS_X, x);
|
|
input_report_abs(pen_dev, ABS_Y, y);
|
|
input_report_abs(pen_dev, ABS_PRESSURE, p);
|
|
input_report_abs(pen_dev, ABS_TILT_X, smsgs[i].tiltx);
|
|
input_report_abs(pen_dev, ABS_TILT_Y, smsgs[i].tilty);
|
|
break;
|
|
|
|
case CTS_DEVICE_TOUCH_EVENT_UP:
|
|
break;
|
|
default:
|
|
cts_warn("Process stylus msg with unknwon event %u id %u",
|
|
smsgs[i].event, smsgs[i].id);
|
|
break;
|
|
}
|
|
}
|
|
|
|
input_report_key(pen_dev, BTN_TOUCH, contact > 0);
|
|
input_report_key(pen_dev, BTN_TOOL_PEN, contact > 0);
|
|
input_sync(pen_dev);
|
|
|
|
|
|
#ifdef CFG_CTS_FORCE_UP
|
|
if (contact) {
|
|
if (delayed_work_pending(&pdata->touch_timeout)) {
|
|
mod_delayed_work(cts_data->workqueue,
|
|
&pdata->touch_timeout, msecs_to_jiffies(100));
|
|
} else {
|
|
queue_delayed_work(cts_data->workqueue,
|
|
&pdata->touch_timeout, msecs_to_jiffies(100));
|
|
}
|
|
} else {
|
|
cancel_delayed_work_sync(&pdata->touch_timeout);
|
|
}
|
|
#endif
|
|
|
|
#ifdef CFG_CTS_HEARTBEAT_MECHANISM
|
|
if (contact) {
|
|
if (delayed_work_pending(&cts_data->heart_work)) {
|
|
mod_delayed_work(cts_data->heart_workqueue,
|
|
&cts_data->heart_work, msecs_to_jiffies(2000));
|
|
} else {
|
|
queue_delayed_work(cts_data->heart_workqueue,
|
|
&cts_data->heart_work, msecs_to_jiffies(2000));
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
int cts_plat_release_all_touch(struct cts_platform_data *pdata)
|
|
{
|
|
struct input_dev *input_dev = pdata->ts_input_dev;
|
|
struct input_dev *pen_dev = pdata->pen_input_dev;
|
|
|
|
#if defined(CONFIG_CTS_SLOTPROTOCOL)
|
|
int id;
|
|
#endif /* CONFIG_CTS_SLOTPROTOCOL */
|
|
|
|
cts_info("Release all touch");
|
|
|
|
#ifdef CONFIG_CTS_SLOTPROTOCOL
|
|
for (id = 0; id < CFG_CTS_MAX_TOUCH_NUM; id++) {
|
|
input_mt_slot(input_dev, id);
|
|
input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, false);
|
|
}
|
|
input_report_key(input_dev, BTN_TOUCH, 0);
|
|
#else
|
|
input_report_key(input_dev, BTN_TOUCH, 0);
|
|
input_mt_sync(input_dev);
|
|
#endif /* CONFIG_CTS_SLOTPROTOCOL */
|
|
input_sync(input_dev);
|
|
|
|
input_report_key(pen_dev, BTN_TOUCH, 0);
|
|
input_report_key(pen_dev, BTN_TOOL_PEN, 0);
|
|
input_sync(pen_dev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef CONFIG_CTS_VIRTUALKEY
|
|
int cts_plat_init_vkey_device(struct cts_platform_data *pdata)
|
|
{
|
|
int i;
|
|
|
|
cts_info("Init VKey");
|
|
pdata->vkey_state = 0;
|
|
|
|
#ifndef CFG_CTS_PLATFORM_MTK_TPD_SUPPORTED
|
|
for (i = 0; i < pdata->vkey_num; i++) {
|
|
input_set_capability(pdata->ts_input_dev,
|
|
EV_KEY, pdata->vkey_keycodes[i]);
|
|
}
|
|
#else
|
|
if (tpd_dts_data.use_tpd_button) {
|
|
cts_info("Init vkey");
|
|
|
|
pdata->vkey_state = 0;
|
|
tpd_button_setting(tpd_dts_data.tpd_key_num, tpd_dts_data.tpd_key_local,
|
|
tpd_dts_data.tpd_key_dim_local);
|
|
}
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
void cts_plat_deinit_vkey_device(struct cts_platform_data *pdata)
|
|
{
|
|
cts_info("De-init VKey");
|
|
|
|
pdata->vkey_state = 0;
|
|
}
|
|
|
|
int cts_plat_process_vkey(struct cts_platform_data *pdata, u8 vkey_state)
|
|
{
|
|
u8 event;
|
|
int i;
|
|
|
|
event = pdata->vkey_state ^ vkey_state;
|
|
|
|
cts_dbg("Process vkey state=0x%02x, event=0x%02x", vkey_state, event);
|
|
|
|
#ifndef CFG_CTS_PLATFORM_MTK_TPD_SUPPORTED
|
|
for (i = 0; i < pdata->vkey_num; i++) {
|
|
input_report_key(pdata->ts_input_dev, pdata->vkey_keycodes[i],
|
|
vkey_state & BIT(i) ? 1 : 0);
|
|
}
|
|
#else
|
|
for (i = 0; i < pdata->vkey_num; i++) {
|
|
if (event & BIT(i)) {
|
|
tpd_button(x, y, vkey_state & BIT(i));
|
|
|
|
/* MTK fobidon more than one key pressed in the same time */
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
pdata->vkey_state = vkey_state;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int cts_plat_release_all_vkey(struct cts_platform_data *pdata)
|
|
{
|
|
int i;
|
|
|
|
cts_info("Release all vkeys");
|
|
|
|
#ifndef CFG_CTS_PLATFORM_MTK_TPD_SUPPORTED
|
|
for (i = 0; i < pdata->vkey_num; i++) {
|
|
if (pdata->vkey_state & BIT(i)) {
|
|
input_report_key(pdata->ts_input_dev, pdata->vkey_keycodes[i], 0);
|
|
}
|
|
}
|
|
#else
|
|
for (i = 0; i < pdata->vkey_num; i++) {
|
|
if (pdata->vkey_state & BIT(i)) {
|
|
tpd_button(x, y, 0);
|
|
}
|
|
}
|
|
#endif
|
|
pdata->vkey_state = 0;
|
|
|
|
return 0;
|
|
}
|
|
#endif /* CONFIG_CTS_VIRTUALKEY */
|
|
|
|
#ifdef CFG_CTS_GESTURE
|
|
int cts_plat_enable_irq_wake(struct cts_platform_data *pdata)
|
|
{
|
|
int ret;
|
|
|
|
cts_info("Enable IRQ wake");
|
|
|
|
if (pdata->irq > 0) {
|
|
ret = enable_irq_wake(pdata->irq);
|
|
if (ret < 0) {
|
|
cts_err("Enable irq wake failed");
|
|
return -EINVAL;
|
|
}
|
|
pdata->irq_wake_enabled = true;
|
|
return 0;
|
|
}
|
|
|
|
cts_warn("Enable irq wake while irq invalid %d", pdata->irq);
|
|
return -ENODEV;
|
|
}
|
|
|
|
int cts_plat_disable_irq_wake(struct cts_platform_data *pdata)
|
|
{
|
|
int ret;
|
|
|
|
cts_info("Disable IRQ wake");
|
|
|
|
if (pdata->irq > 0) {
|
|
ret = disable_irq_wake(pdata->irq);
|
|
if (ret < 0) {
|
|
cts_warn("Disable irq wake while already disabled");
|
|
return -EINVAL;
|
|
}
|
|
pdata->irq_wake_enabled = false;
|
|
return 0;
|
|
}
|
|
|
|
cts_warn("Disable irq wake while irq invalid %d", pdata->irq);
|
|
return -ENODEV;
|
|
}
|
|
|
|
int cts_plat_init_gesture(struct cts_platform_data *pdata)
|
|
{
|
|
int i;
|
|
|
|
cts_info("Init gesture");
|
|
|
|
/* TODO: If system will issure enable/disable command, comment following line. */
|
|
/* cts_enable_gesture_wakeup(pdata->cts_dev); */
|
|
|
|
for (i = 0; i < pdata->gesture_num; i++) {
|
|
input_set_capability(pdata->ts_input_dev, EV_KEY,
|
|
pdata->gesture_keymap[i][1]);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void cts_plat_deinit_gesture(struct cts_platform_data *pdata)
|
|
{
|
|
cts_info("De-init gesture");
|
|
}
|
|
|
|
int cts_plat_process_gesture_info(struct cts_platform_data *pdata,
|
|
struct cts_device_gesture_info *gesture_info)
|
|
{
|
|
int i;
|
|
|
|
cts_info("Process gesture, id=0x%02x", gesture_info->gesture_id);
|
|
|
|
#if defined(CFG_CTS_GESTURE_REPORT_KEY)
|
|
for (i = 0; i < CFG_CTS_NUM_GESTURE; i++) {
|
|
if (gesture_info->gesture_id == pdata->gesture_keymap[i][0]) {
|
|
if (gesture_info->gesture_id == GESTURE_D_TAP) {
|
|
if (!pdata->cts_dev->rtdata.gesture_d_tap_enabled) {
|
|
cts_info("Ingore double tap");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
cts_info("Report key[%u]", pdata->gesture_keymap[i][1]);
|
|
input_report_key(pdata->ts_input_dev, pdata->gesture_keymap[i][1], 1);
|
|
input_sync(pdata->ts_input_dev);
|
|
|
|
input_report_key(pdata->ts_input_dev, pdata->gesture_keymap[i][1], 0);
|
|
input_sync(pdata->ts_input_dev);
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
#endif /* CFG_CTS_GESTURE_REPORT_KEY */
|
|
|
|
cts_warn("Process unrecognized gesture id=%u", gesture_info->gesture_id);
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
#endif /* CFG_CTS_GESTURE */
|