c05564c4d8
Android 13
3143 lines
84 KiB
C
Executable file
3143 lines
84 KiB
C
Executable file
/*
|
|
* Copyright (C) 2010 - 2018 Novatek, Inc.
|
|
*
|
|
* $Revision: 61426 $
|
|
* $Date: 2020-04-29 15:04:36 +0800 $
|
|
*
|
|
* 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.
|
|
*
|
|
*/
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/irq.h>
|
|
#include <linux/gpio.h>
|
|
#include <linux/proc_fs.h>
|
|
#include <linux/input/mt.h>
|
|
#include <linux/of_gpio.h>
|
|
#include <linux/of_irq.h>
|
|
|
|
#if defined(CONFIG_FB)
|
|
#ifdef CONFIG_DRM_MSM
|
|
#include <linux/msm_drm_notify.h>
|
|
#endif
|
|
#include <linux/notifier.h>
|
|
#include <linux/fb.h>
|
|
#elif defined(CONFIG_HAS_EARLYSUSPEND)
|
|
#include <linux/earlysuspend.h>
|
|
#endif
|
|
|
|
#include "nt36xxx.h"
|
|
#if NVT_TOUCH_ESD_PROTECT
|
|
#include <linux/jiffies.h>
|
|
#endif /* #if NVT_TOUCH_ESD_PROTECT */
|
|
|
|
int nvt_ts_sec_fn_init(struct nvt_ts_data *ts);
|
|
void nvt_ts_sec_fn_remove(struct nvt_ts_data *ts);
|
|
|
|
#if NVT_TOUCH_ESD_PROTECT
|
|
static struct delayed_work nvt_esd_check_work;
|
|
static struct workqueue_struct *nvt_esd_check_wq;
|
|
static unsigned long irq_timer = 0;
|
|
uint8_t esd_check = false;
|
|
uint8_t esd_retry = 0;
|
|
#endif /* #if NVT_TOUCH_ESD_PROTECT */
|
|
|
|
#if NVT_TOUCH_EXT_PROC
|
|
extern int32_t nvt_extra_proc_init(void);
|
|
extern void nvt_extra_proc_deinit(void);
|
|
#endif
|
|
|
|
#if NVT_TOUCH_MP
|
|
extern int32_t nvt_mp_proc_init(void);
|
|
extern void nvt_mp_proc_deinit(void);
|
|
#endif
|
|
|
|
struct nvt_ts_data *ts;
|
|
|
|
#if BOOT_UPDATE_FIRMWARE
|
|
static struct workqueue_struct *nvt_fwu_wq;
|
|
extern void Boot_Update_Firmware(struct work_struct *work);
|
|
#endif
|
|
|
|
#if defined(CONFIG_FB)
|
|
#ifdef _MSM_DRM_NOTIFY_H_
|
|
static int nvt_drm_notifier_callback(struct notifier_block *self, unsigned long event, void *data);
|
|
#else
|
|
static int nvt_fb_notifier_callback(struct notifier_block *self, unsigned long event, void *data);
|
|
#endif
|
|
#elif defined(CONFIG_HAS_EARLYSUSPEND)
|
|
static void nvt_ts_early_suspend(struct early_suspend *h);
|
|
static void nvt_ts_late_resume(struct early_suspend *h);
|
|
#endif
|
|
|
|
uint32_t ENG_RST_ADDR = 0x7FFF80;
|
|
uint32_t SWRST_N8_ADDR = 0; //read from dtsi
|
|
uint32_t SPI_RD_FAST_ADDR = 0; //read from dtsi
|
|
|
|
#if TOUCH_KEY_NUM > 0
|
|
const uint16_t touch_key_array[TOUCH_KEY_NUM] = {
|
|
KEY_BACK,
|
|
KEY_HOME,
|
|
KEY_MENU
|
|
};
|
|
#endif
|
|
|
|
#if WAKEUP_GESTURE
|
|
const uint16_t gesture_key_array[] = {
|
|
KEY_WAKEUP, //GESTURE_WORD_C
|
|
KEY_WAKEUP, //GESTURE_WORD_W
|
|
KEY_WAKEUP, //GESTURE_WORD_V
|
|
KEY_WAKEUP, //GESTURE_DOUBLE_CLICK
|
|
KEY_WAKEUP, //GESTURE_WORD_Z
|
|
KEY_WAKEUP, //GESTURE_WORD_M
|
|
KEY_WAKEUP, //GESTURE_WORD_O
|
|
KEY_WAKEUP, //GESTURE_WORD_e
|
|
KEY_WAKEUP, //GESTURE_WORD_S
|
|
KEY_WAKEUP, //GESTURE_SLIDE_UP
|
|
KEY_WAKEUP, //GESTURE_SLIDE_DOWN
|
|
KEY_WAKEUP, //GESTURE_SLIDE_LEFT
|
|
KEY_WAKEUP, //GESTURE_SLIDE_RIGHT
|
|
};
|
|
|
|
#define GESTURE_WORD_C 12
|
|
#define GESTURE_WORD_W 13
|
|
#define GESTURE_WORD_V 14
|
|
#define GESTURE_DOUBLE_CLICK 15
|
|
#define GESTURE_WORD_Z 16
|
|
#define GESTURE_WORD_M 17
|
|
#define GESTURE_WORD_O 18
|
|
#define GESTURE_WORD_e 19
|
|
#define GESTURE_WORD_S 20
|
|
#define GESTURE_SLIDE_UP 21
|
|
#define GESTURE_SLIDE_DOWN 22
|
|
#define GESTURE_SLIDE_LEFT 23
|
|
#define GESTURE_SLIDE_RIGHT 24
|
|
#endif
|
|
|
|
#ifdef CONFIG_MTK_SPI
|
|
const struct mt_chip_conf spi_ctrdata = {
|
|
.setuptime = 25,
|
|
.holdtime = 25,
|
|
.high_time = 5, /* 10MHz (SPI_SPEED=100M / (high_time+low_time(10ns)))*/
|
|
.low_time = 5,
|
|
.cs_idletime = 2,
|
|
.ulthgh_thrsh = 0,
|
|
.cpol = 0,
|
|
.cpha = 0,
|
|
.rx_mlsb = 1,
|
|
.tx_mlsb = 1,
|
|
.tx_endian = 0,
|
|
.rx_endian = 0,
|
|
.com_mod = DMA_TRANSFER,
|
|
.pause = 0,
|
|
.finish_intr = 1,
|
|
.deassert = 0,
|
|
.ulthigh = 0,
|
|
.tckdly = 0,
|
|
};
|
|
#endif
|
|
|
|
#if defined(CONFIG_SPI_MT65XX)
|
|
const struct mtk_chip_config spi_ctrdata = {
|
|
// .rx_mlsb = 1,
|
|
// .tx_mlsb = 1,
|
|
// .cs_pol = 0,
|
|
.sample_sel = 0,
|
|
|
|
.cs_setuptime = 25,
|
|
.cs_holdtime = 0,
|
|
.cs_idletime = 0,
|
|
.deassert_mode = 0,
|
|
};
|
|
#endif
|
|
|
|
/*******************************************************
|
|
Description:
|
|
Novatek touchscreen irq enable/disable function.
|
|
|
|
return:
|
|
n.a.
|
|
*******************************************************/
|
|
static void nvt_irq_enable(bool enable)
|
|
{
|
|
struct irq_desc *desc;
|
|
|
|
if (enable) {
|
|
if (!ts->irq_enabled) {
|
|
enable_irq(ts->client->irq);
|
|
ts->irq_enabled = true;
|
|
}
|
|
} else {
|
|
if (ts->irq_enabled) {
|
|
disable_irq(ts->client->irq);
|
|
ts->irq_enabled = false;
|
|
}
|
|
}
|
|
|
|
desc = irq_to_desc(ts->client->irq);
|
|
input_info(true, &ts->client->dev,"enable=%d, desc->depth=%d\n", enable, desc->depth);
|
|
}
|
|
|
|
/*******************************************************
|
|
Description:
|
|
Novatek touchscreen spi read/write core function.
|
|
|
|
return:
|
|
Executive outcomes. 0---succeed.
|
|
*******************************************************/
|
|
static inline int32_t spi_read_write(struct spi_device *client, uint8_t *buf, size_t len , NVT_SPI_RW rw)
|
|
{
|
|
struct spi_message m;
|
|
struct spi_transfer t = {
|
|
.len = len,
|
|
};
|
|
int ret;
|
|
|
|
if (ts->power_status == POWER_OFF_STATUS) {
|
|
input_err(true, &ts->client->dev, "%s: POWER_STATUS : OFF!\n", __func__);
|
|
return -EIO;
|
|
}
|
|
|
|
memset(ts->xbuf, 0, len + DUMMY_BYTES);
|
|
memcpy(ts->xbuf, buf, len);
|
|
|
|
switch (rw) {
|
|
case NVTREAD:
|
|
t.tx_buf = ts->xbuf;
|
|
t.rx_buf = ts->rbuf;
|
|
t.len = (len + DUMMY_BYTES);
|
|
break;
|
|
|
|
case NVTWRITE:
|
|
t.tx_buf = ts->xbuf;
|
|
break;
|
|
}
|
|
|
|
spi_message_init(&m);
|
|
spi_message_add_tail(&t, &m);
|
|
|
|
if (ts->platdata->cs_gpio > 0)
|
|
gpio_direction_output(ts->platdata->cs_gpio, 0);
|
|
|
|
ret = spi_sync(client, &m);
|
|
|
|
if (ts->platdata->cs_gpio > 0)
|
|
gpio_direction_output(ts->platdata->cs_gpio, 1);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*******************************************************
|
|
Description:
|
|
Novatek touchscreen spi read function.
|
|
|
|
return:
|
|
Executive outcomes. 2---succeed. -5---I/O error
|
|
*******************************************************/
|
|
int32_t CTP_SPI_READ(struct spi_device *client, uint8_t *buf, uint16_t len)
|
|
{
|
|
int32_t ret = -1;
|
|
int32_t retries = 0;
|
|
|
|
if (ts->power_status == POWER_OFF_STATUS) {
|
|
input_err(true, &client->dev, "%s: POWER_STATUS : OFF!\n", __func__);
|
|
return -EIO;
|
|
}
|
|
|
|
mutex_lock(&ts->xbuf_lock);
|
|
|
|
buf[0] = SPI_READ_MASK(buf[0]);
|
|
|
|
while (retries < 5) {
|
|
ret = spi_read_write(client, buf, len, NVTREAD);
|
|
if (ret == 0) break;
|
|
retries++;
|
|
|
|
if (ts->power_status == POWER_OFF_STATUS) {
|
|
input_err(true, &client->dev, "%s: POWER_STATUS : OFF!\n", __func__);
|
|
mutex_unlock(&ts->xbuf_lock);
|
|
return -EIO;
|
|
}
|
|
}
|
|
|
|
if (unlikely(retries == 5)) {
|
|
input_err(true, &client->dev,"read error, ret=%d\n", ret);
|
|
ret = -EIO;
|
|
} else {
|
|
memcpy((buf+1), (ts->rbuf+2), (len-1));
|
|
}
|
|
|
|
mutex_unlock(&ts->xbuf_lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*******************************************************
|
|
Description:
|
|
Novatek touchscreen spi write function.
|
|
|
|
return:
|
|
Executive outcomes. 1---succeed. -5---I/O error
|
|
*******************************************************/
|
|
int32_t CTP_SPI_WRITE(struct spi_device *client, uint8_t *buf, uint16_t len)
|
|
{
|
|
int32_t ret = -1;
|
|
int32_t retries = 0;
|
|
|
|
if (ts->power_status == POWER_OFF_STATUS) {
|
|
input_err(true, &client->dev, "%s: POWER_STATUS : OFF!\n", __func__);
|
|
return -EIO;
|
|
}
|
|
|
|
mutex_lock(&ts->xbuf_lock);
|
|
|
|
buf[0] = SPI_WRITE_MASK(buf[0]);
|
|
|
|
while (retries < 5) {
|
|
ret = spi_read_write(client, buf, len, NVTWRITE);
|
|
if (ret == 0) break;
|
|
retries++;
|
|
|
|
if (ts->power_status == POWER_OFF_STATUS) {
|
|
input_err(true, &client->dev, "%s: POWER_STATUS : OFF!\n", __func__);
|
|
mutex_unlock(&ts->xbuf_lock);
|
|
return -EIO;
|
|
}
|
|
}
|
|
|
|
if (unlikely(retries == 5)) {
|
|
input_err(true, &client->dev,"error, ret=%d\n", ret);
|
|
ret = -EIO;
|
|
}
|
|
|
|
mutex_unlock(&ts->xbuf_lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*******************************************************
|
|
Description:
|
|
Novatek touchscreen set index/page/addr address.
|
|
|
|
return:
|
|
Executive outcomes. 0---succeed. -5---access fail.
|
|
*******************************************************/
|
|
int32_t nvt_set_page(uint32_t addr)
|
|
{
|
|
uint8_t buf[4] = {0};
|
|
|
|
buf[0] = 0xFF; //set index/page/addr command
|
|
buf[1] = (addr >> 15) & 0xFF;
|
|
buf[2] = (addr >> 7) & 0xFF;
|
|
|
|
return CTP_SPI_WRITE(ts->client, buf, 3);
|
|
}
|
|
|
|
/*******************************************************
|
|
Description:
|
|
Novatek touchscreen write data to specify address.
|
|
|
|
return:
|
|
Executive outcomes. 0---succeed. -5---access fail.
|
|
*******************************************************/
|
|
int32_t nvt_write_addr(uint32_t addr, uint8_t data)
|
|
{
|
|
int32_t ret = 0;
|
|
uint8_t buf[4] = {0};
|
|
|
|
//---set xdata index---
|
|
buf[0] = 0xFF; //set index/page/addr command
|
|
buf[1] = (addr >> 15) & 0xFF;
|
|
buf[2] = (addr >> 7) & 0xFF;
|
|
ret = CTP_SPI_WRITE(ts->client, buf, 3);
|
|
if (ret) {
|
|
input_err(true, &ts->client->dev,"set page 0x%06X failed, ret = %d\n", addr, ret);
|
|
return ret;
|
|
}
|
|
|
|
//---write data to index---
|
|
buf[0] = addr & (0x7F);
|
|
buf[1] = data;
|
|
ret = CTP_SPI_WRITE(ts->client, buf, 2);
|
|
if (ret) {
|
|
input_err(true, &ts->client->dev,"write data to 0x%06X failed, ret = %d\n", addr, ret);
|
|
return ret;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*******************************************************
|
|
Description:
|
|
Novatek touchscreen enable hw bld crc function.
|
|
|
|
return:
|
|
N/A.
|
|
*******************************************************/
|
|
void nvt_bld_crc_enable(void)
|
|
{
|
|
uint8_t buf[4] = {0};
|
|
|
|
//---set xdata index to BLD_CRC_EN_ADDR---
|
|
nvt_set_page(ts->mmap->BLD_CRC_EN_ADDR);
|
|
|
|
//---read data from index---
|
|
buf[0] = ts->mmap->BLD_CRC_EN_ADDR & (0x7F);
|
|
buf[1] = 0xFF;
|
|
CTP_SPI_READ(ts->client, buf, 2);
|
|
|
|
//---write data to index---
|
|
buf[0] = ts->mmap->BLD_CRC_EN_ADDR & (0x7F);
|
|
buf[1] = buf[1] | (0x01 << 7);
|
|
CTP_SPI_WRITE(ts->client, buf, 2);
|
|
}
|
|
|
|
/*******************************************************
|
|
Description:
|
|
Novatek touchscreen clear status & enable fw crc function.
|
|
|
|
return:
|
|
N/A.
|
|
*******************************************************/
|
|
void nvt_fw_crc_enable(void)
|
|
{
|
|
uint8_t buf[4] = {0};
|
|
|
|
//---set xdata index to EVENT BUF ADDR---
|
|
nvt_set_page(ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_RESET_COMPLETE);
|
|
|
|
//---clear fw reset status---
|
|
buf[0] = EVENT_MAP_RESET_COMPLETE & (0x7F);
|
|
buf[1] = 0x00;
|
|
CTP_SPI_WRITE(ts->client, buf, 2);
|
|
|
|
//---set xdata index to EVENT BUF ADDR---
|
|
nvt_set_page(ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_HOST_CMD);
|
|
|
|
//---enable fw crc---
|
|
buf[0] = EVENT_MAP_HOST_CMD & (0x7F);
|
|
buf[1] = 0xAE; //enable fw crc command
|
|
CTP_SPI_WRITE(ts->client, buf, 2);
|
|
}
|
|
|
|
/*******************************************************
|
|
Description:
|
|
Novatek touchscreen set boot ready function.
|
|
|
|
return:
|
|
N/A.
|
|
*******************************************************/
|
|
void nvt_boot_ready(void)
|
|
{
|
|
//---write BOOT_RDY status cmds---
|
|
nvt_write_addr(ts->mmap->BOOT_RDY_ADDR, 1);
|
|
|
|
mdelay(5);
|
|
|
|
if (!ts->hw_crc) {
|
|
//---write BOOT_RDY status cmds---
|
|
nvt_write_addr(ts->mmap->BOOT_RDY_ADDR, 0);
|
|
|
|
//---write POR_CD cmds---
|
|
nvt_write_addr(ts->mmap->POR_CD_ADDR, 0xA0);
|
|
}
|
|
}
|
|
|
|
/*******************************************************
|
|
Description:
|
|
Novatek touchscreen eng reset cmd
|
|
function.
|
|
|
|
return:
|
|
n.a.
|
|
*******************************************************/
|
|
void nvt_eng_reset(void)
|
|
{
|
|
//---eng reset cmds to ENG_RST_ADDR---
|
|
nvt_write_addr(ENG_RST_ADDR, 0x5A);
|
|
|
|
mdelay(1); //wait tMCU_Idle2TP_REX_Hi after TP_RST
|
|
}
|
|
|
|
/*******************************************************
|
|
Description:
|
|
Novatek touchscreen reset MCU
|
|
function.
|
|
|
|
return:
|
|
n.a.
|
|
*******************************************************/
|
|
void nvt_sw_reset(void)
|
|
{
|
|
//---software reset cmds to SWRST_N8_ADDR---
|
|
nvt_write_addr(SWRST_N8_ADDR, 0x55);
|
|
|
|
msleep(10);
|
|
}
|
|
|
|
/*******************************************************
|
|
Description:
|
|
Novatek touchscreen reset MCU then into idle mode
|
|
function.
|
|
|
|
return:
|
|
n.a.
|
|
*******************************************************/
|
|
void nvt_sw_reset_idle(void)
|
|
{
|
|
//---MCU idle cmds to SWRST_N8_ADDR---
|
|
nvt_write_addr(SWRST_N8_ADDR, 0xAA);
|
|
|
|
msleep(15);
|
|
}
|
|
|
|
/*******************************************************
|
|
Description:
|
|
Novatek touchscreen reset MCU (boot) function.
|
|
|
|
return:
|
|
n.a.
|
|
*******************************************************/
|
|
void nvt_bootloader_reset(void)
|
|
{
|
|
//---reset cmds to SWRST_N8_ADDR---
|
|
nvt_write_addr(SWRST_N8_ADDR, 0x69);
|
|
|
|
mdelay(5); //wait tBRST2FR after Bootload RST
|
|
|
|
if (SPI_RD_FAST_ADDR) {
|
|
/* disable SPI_RD_FAST */
|
|
nvt_write_addr(SPI_RD_FAST_ADDR, 0x00);
|
|
}
|
|
}
|
|
|
|
/*******************************************************
|
|
Description:
|
|
Novatek touchscreen clear FW status function.
|
|
|
|
return:
|
|
Executive outcomes. 0---succeed. -1---fail.
|
|
*******************************************************/
|
|
int32_t nvt_clear_fw_status(void)
|
|
{
|
|
uint8_t buf[8] = {0};
|
|
int32_t i = 0;
|
|
const int32_t retry = 20;
|
|
|
|
for (i = 0; i < retry; i++) {
|
|
//---set xdata index to EVENT BUF ADDR---
|
|
nvt_set_page(ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE);
|
|
|
|
//---clear fw status---
|
|
buf[0] = EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE;
|
|
buf[1] = 0x00;
|
|
CTP_SPI_WRITE(ts->client, buf, 2);
|
|
|
|
//---read fw status---
|
|
buf[0] = EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE;
|
|
buf[1] = 0xFF;
|
|
CTP_SPI_READ(ts->client, buf, 2);
|
|
|
|
if (buf[1] == 0x00)
|
|
break;
|
|
|
|
usleep_range(10000, 10000);
|
|
}
|
|
|
|
if (i >= retry) {
|
|
input_err(true, &ts->client->dev,"failed, i=%d, buf[1]=0x%02X\n", i, buf[1]);
|
|
return -1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/*******************************************************
|
|
Description:
|
|
Novatek touchscreen check FW status function.
|
|
|
|
return:
|
|
Executive outcomes. 0---succeed. -1---failed.
|
|
*******************************************************/
|
|
int32_t nvt_check_fw_status(void)
|
|
{
|
|
uint8_t buf[8] = {0};
|
|
int32_t i = 0;
|
|
const int32_t retry = 50;
|
|
|
|
for (i = 0; i < retry; i++) {
|
|
//---set xdata index to EVENT BUF ADDR---
|
|
nvt_set_page(ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE);
|
|
|
|
//---read fw status---
|
|
buf[0] = EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE;
|
|
buf[1] = 0x00;
|
|
CTP_SPI_READ(ts->client, buf, 2);
|
|
|
|
if ((buf[1] & 0xF0) == 0xA0)
|
|
break;
|
|
|
|
usleep_range(10000, 10000);
|
|
}
|
|
|
|
if (i >= retry) {
|
|
input_err(true, &ts->client->dev,"failed, i=%d, buf[1]=0x%02X\n", i, buf[1]);
|
|
return -1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/*******************************************************
|
|
Description:
|
|
Novatek touchscreen check FW reset state function.
|
|
|
|
return:
|
|
Executive outcomes. 0---succeed. -1---failed.
|
|
*******************************************************/
|
|
int32_t nvt_check_fw_reset_state(RST_COMPLETE_STATE check_reset_state)
|
|
{
|
|
uint8_t buf[8] = {0};
|
|
int32_t ret = 0;
|
|
int32_t retry = 0;
|
|
int32_t retry_max = (check_reset_state == RESET_STATE_INIT) ? 10 : 50;
|
|
|
|
//---set xdata index to EVENT BUF ADDR---
|
|
nvt_set_page(ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_RESET_COMPLETE);
|
|
|
|
while (1) {
|
|
//---read reset state---
|
|
buf[0] = EVENT_MAP_RESET_COMPLETE;
|
|
buf[1] = 0x00;
|
|
CTP_SPI_READ(ts->client, buf, 6);
|
|
|
|
if ((buf[1] >= check_reset_state) && (buf[1] <= RESET_STATE_MAX)) {
|
|
ret = 0;
|
|
break;
|
|
}
|
|
|
|
retry++;
|
|
if(unlikely(retry > retry_max)) {
|
|
input_err(true, &ts->client->dev,"error, retry=%d, buf[1]=0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X\n",
|
|
retry, buf[1], buf[2], buf[3], buf[4], buf[5]);
|
|
ret = -1;
|
|
break;
|
|
}
|
|
|
|
usleep_range(10000, 10000);
|
|
}
|
|
|
|
if (!ret)
|
|
input_info(true, &ts->client->dev, "%s : retry=%d, buf[1] = %x\n", __func__, retry, buf[1]);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*******************************************************
|
|
Description:
|
|
Novatek touchscreen get novatek project id information
|
|
function.
|
|
|
|
return:
|
|
Executive outcomes. 0---success. -1---fail.
|
|
*******************************************************/
|
|
int32_t nvt_read_pid(void)
|
|
{
|
|
uint8_t buf[4] = {0};
|
|
int32_t ret = 0;
|
|
|
|
//---set xdata index to EVENT BUF ADDR---
|
|
nvt_set_page(ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_PROJECTID);
|
|
|
|
//---read project id---
|
|
buf[0] = EVENT_MAP_PROJECTID;
|
|
buf[1] = 0x00;
|
|
buf[2] = 0x00;
|
|
CTP_SPI_READ(ts->client, buf, 3);
|
|
|
|
ts->nvt_pid = (buf[2] << 8) + buf[1];
|
|
ts->fw_ver_ic[1] = buf[1];
|
|
|
|
//---set xdata index to EVENT BUF ADDR---
|
|
nvt_set_page(ts->mmap->EVENT_BUF_ADDR);
|
|
|
|
input_info(true, &ts->client->dev,"PID=%04X\n", ts->nvt_pid);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*******************************************************
|
|
Description:
|
|
Novatek touchscreen get firmware related information
|
|
function.
|
|
|
|
return:
|
|
Executive outcomes. 0---success. -1---fail.
|
|
*******************************************************/
|
|
int32_t nvt_get_fw_info(void)
|
|
{
|
|
uint8_t buf[64] = {0};
|
|
uint32_t retry_count = 0;
|
|
int32_t ret = 0;
|
|
|
|
info_retry:
|
|
//---set xdata index to EVENT BUF ADDR---
|
|
nvt_set_page(ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_FWINFO);
|
|
|
|
//---read fw info---
|
|
buf[0] = EVENT_MAP_FWINFO;
|
|
CTP_SPI_READ(ts->client, buf, 17);
|
|
ts->fw_ver = buf[1];
|
|
ts->platdata->x_num = buf[3];
|
|
ts->platdata->y_num = buf[4];
|
|
ts->platdata->abs_x_max = (uint16_t)((buf[5] << 8) | buf[6]);
|
|
ts->platdata->abs_y_max = (uint16_t)((buf[7] << 8) | buf[8]);
|
|
ts->max_button_num = buf[11];
|
|
|
|
input_info(true, &ts->client->dev, "%s : fw_ver=%d, x_num=%d, y_num=%d,"
|
|
"abs_x_max=%d, abs_y_max=%d, max_button_num=%d!\n",
|
|
__func__, ts->fw_ver, ts->platdata->x_num, ts->platdata->y_num,
|
|
ts->platdata->abs_x_max, ts->platdata->abs_y_max, ts->max_button_num);
|
|
|
|
//---clear x_num, y_num if fw info is broken---
|
|
if ((buf[1] + buf[2]) != 0xFF) {
|
|
input_err(true, &ts->client->dev,"FW info is broken! fw_ver=0x%02X, ~fw_ver=0x%02X\n", buf[1], buf[2]);
|
|
ts->fw_ver = 0;
|
|
ts->platdata->x_num = 18;
|
|
ts->platdata->y_num = 32;
|
|
ts->platdata->abs_x_max = TOUCH_DEFAULT_MAX_WIDTH;
|
|
ts->platdata->abs_y_max = TOUCH_DEFAULT_MAX_HEIGHT;
|
|
ts->max_button_num = TOUCH_KEY_NUM;
|
|
|
|
if(retry_count < 3) {
|
|
retry_count++;
|
|
input_err(true, &ts->client->dev,"retry_count=%d\n", retry_count);
|
|
goto info_retry;
|
|
} else {
|
|
input_err(true, &ts->client->dev,"Set default fw_ver=%d, x_num=%d, y_num=%d, "
|
|
"abs_x_max=%d, abs_y_max=%d, max_button_num=%d!\n",
|
|
ts->fw_ver, ts->platdata->x_num, ts->platdata->y_num,
|
|
ts->platdata->abs_x_max, ts->platdata->abs_y_max, ts->max_button_num);
|
|
ret = -1;
|
|
}
|
|
} else {
|
|
ret = 0;
|
|
}
|
|
|
|
input_info(true, &ts->client->dev,"fw_ver = 0x%02X, fw_type = 0x%02X\n", ts->fw_ver, buf[14]);
|
|
|
|
ts->fw_ver_ic[0] = buf[15];
|
|
|
|
//---Get Novatek PID---
|
|
nvt_read_pid();
|
|
|
|
//---get panel id---
|
|
//---set xdata index to EVENT BUF ADDR---
|
|
nvt_set_page(ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_PANEL);
|
|
buf[0] = EVENT_MAP_PANEL;
|
|
ret = CTP_SPI_READ(ts->client, buf, 2);
|
|
if (ret < 0) {
|
|
input_err(true, &ts->client->dev,"nvt_ts_i2c_read error(%d)\n", ret);
|
|
return ret;
|
|
}
|
|
ts->fw_ver_ic[2] = buf[1];
|
|
|
|
//---get firmware version---
|
|
//---set xdata index to EVENT BUF ADDR---
|
|
nvt_set_page(ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_FWINFO);
|
|
buf[0] = EVENT_MAP_FWINFO;
|
|
ret = CTP_SPI_READ(ts->client, buf, 2);
|
|
if (ret < 0) {
|
|
input_err(true, &ts->client->dev,"nvt_ts_i2c_read error(%d)\n", ret);
|
|
return ret;
|
|
}
|
|
ts->fw_ver_ic[3] = buf[1];
|
|
|
|
//---set xdata index to EVENT BUF ADDR---
|
|
nvt_set_page(ts->mmap->EVENT_BUF_ADDR);
|
|
|
|
input_info(true, &ts->client->dev,"fw_ver_ic = %02X%02X%02X%02X\n",
|
|
ts->fw_ver_ic[0], ts->fw_ver_ic[1], ts->fw_ver_ic[2], ts->fw_ver_ic[3]);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*******************************************************
|
|
Create Device Node (Proc Entry)
|
|
*******************************************************/
|
|
#if NVT_TOUCH_PROC
|
|
static struct proc_dir_entry *NVT_proc_entry;
|
|
#define DEVICE_NAME "NVTSPI"
|
|
|
|
/*******************************************************
|
|
Description:
|
|
Novatek touchscreen /proc/NVTSPI read function.
|
|
|
|
return:
|
|
Executive outcomes. 2---succeed. -5,-14---failed.
|
|
*******************************************************/
|
|
static ssize_t nvt_flash_read(struct file *file, char __user *buff, size_t count, loff_t *offp)
|
|
{
|
|
uint8_t *str = NULL;
|
|
int32_t ret = 0;
|
|
int32_t retries = 0;
|
|
int8_t spi_wr = 0;
|
|
uint8_t *buf;
|
|
|
|
if ((count > NVT_TRANSFER_LEN + 3) || (count < 3)) {
|
|
input_err(true, &ts->client->dev,"invalid transfer len!\n");
|
|
return -EFAULT;
|
|
}
|
|
|
|
/* allocate buffer for spi transfer */
|
|
str = (uint8_t *)kzalloc((count), GFP_KERNEL);
|
|
if(str == NULL) {
|
|
input_err(true, &ts->client->dev,"kzalloc for buf failed!\n");
|
|
ret = -ENOMEM;
|
|
goto kzalloc_failed;
|
|
}
|
|
|
|
buf = (uint8_t *)kzalloc((count), GFP_KERNEL | GFP_DMA);
|
|
if(buf == NULL) {
|
|
input_err(true, &ts->client->dev,"kzalloc for buf failed!\n");
|
|
ret = -ENOMEM;
|
|
kfree(str);
|
|
str = NULL;
|
|
goto kzalloc_failed;
|
|
}
|
|
|
|
if (copy_from_user(str, buff, count)) {
|
|
input_err(true, &ts->client->dev,"copy from user error\n");
|
|
ret = -EFAULT;
|
|
goto out;
|
|
}
|
|
|
|
#if NVT_TOUCH_ESD_PROTECT
|
|
/*
|
|
* stop esd check work to avoid case that 0x77 report righ after here to enable esd check again
|
|
* finally lead to trigger esd recovery bootloader reset
|
|
*/
|
|
cancel_delayed_work_sync(&nvt_esd_check_work);
|
|
nvt_esd_check_enable(false);
|
|
#endif /* #if NVT_TOUCH_ESD_PROTECT */
|
|
|
|
spi_wr = str[0] >> 7;
|
|
memcpy(buf, str+2, ((str[0] & 0x7F) << 8) | str[1]);
|
|
|
|
if (spi_wr == NVTWRITE) { //SPI write
|
|
while (retries < 20) {
|
|
ret = CTP_SPI_WRITE(ts->client, buf, ((str[0] & 0x7F) << 8) | str[1]);
|
|
if (!ret)
|
|
break;
|
|
else
|
|
input_err(true, &ts->client->dev,"error, retries=%d, ret=%d\n", retries, ret);
|
|
|
|
retries++;
|
|
}
|
|
|
|
if (unlikely(retries == 20)) {
|
|
input_err(true, &ts->client->dev,"error, ret = %d\n", ret);
|
|
ret = -EIO;
|
|
goto out;
|
|
}
|
|
} else if (spi_wr == NVTREAD) { //SPI read
|
|
while (retries < 20) {
|
|
ret = CTP_SPI_READ(ts->client, buf, ((str[0] & 0x7F) << 8) | str[1]);
|
|
if (!ret)
|
|
break;
|
|
else
|
|
input_err(true, &ts->client->dev,"error, retries=%d, ret=%d\n", retries, ret);
|
|
|
|
retries++;
|
|
}
|
|
|
|
memcpy(str+2, buf, ((str[0] & 0x7F) << 8) | str[1]);
|
|
// copy buff to user if spi transfer
|
|
if (retries < 20) {
|
|
if (copy_to_user(buff, str, count)) {
|
|
ret = -EFAULT;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
if (unlikely(retries == 20)) {
|
|
input_err(true, &ts->client->dev,"error, ret = %d\n", ret);
|
|
ret = -EIO;
|
|
goto out;
|
|
}
|
|
} else {
|
|
input_err(true, &ts->client->dev,"Call error, str[0]=%d\n", str[0]);
|
|
ret = -EFAULT;
|
|
goto out;
|
|
}
|
|
|
|
out:
|
|
kfree(str);
|
|
kfree(buf);
|
|
kzalloc_failed:
|
|
return ret;
|
|
}
|
|
|
|
/*******************************************************
|
|
Description:
|
|
Novatek touchscreen /proc/NVTSPI open function.
|
|
|
|
return:
|
|
Executive outcomes. 0---succeed. -12---failed.
|
|
*******************************************************/
|
|
static int32_t nvt_flash_open(struct inode *inode, struct file *file)
|
|
{
|
|
struct nvt_flash_data *dev;
|
|
|
|
dev = kzalloc(sizeof(struct nvt_flash_data), GFP_KERNEL);
|
|
if (dev == NULL) {
|
|
pr_err("Failed to allocate memory for nvt flash data\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
rwlock_init(&dev->lock);
|
|
file->private_data = dev;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*******************************************************
|
|
Description:
|
|
Novatek touchscreen /proc/NVTSPI close function.
|
|
|
|
return:
|
|
Executive outcomes. 0---succeed.
|
|
*******************************************************/
|
|
static int32_t nvt_flash_close(struct inode *inode, struct file *file)
|
|
{
|
|
struct nvt_flash_data *dev = file->private_data;
|
|
|
|
if (dev)
|
|
kfree(dev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct file_operations nvt_flash_fops = {
|
|
.owner = THIS_MODULE,
|
|
.open = nvt_flash_open,
|
|
.release = nvt_flash_close,
|
|
.read = nvt_flash_read,
|
|
};
|
|
|
|
/*******************************************************
|
|
Description:
|
|
Novatek touchscreen /proc/NVTSPI initial function.
|
|
|
|
return:
|
|
Executive outcomes. 0---succeed. -12---failed.
|
|
*******************************************************/
|
|
static int32_t nvt_flash_proc_init(void)
|
|
{
|
|
NVT_proc_entry = proc_create(DEVICE_NAME, 0444, NULL,&nvt_flash_fops);
|
|
if (NVT_proc_entry == NULL) {
|
|
input_err(true, &ts->client->dev, "%s: Failed!\n", __func__);
|
|
return -ENOMEM;
|
|
} else {
|
|
input_info(true, &ts->client->dev, "%s: Succeeded!\n", __func__);
|
|
}
|
|
|
|
input_info(true, &ts->client->dev, "%s: ============================================================\n", __func__);
|
|
input_info(true, &ts->client->dev, "%s: Create /proc/%s\n", __func__, DEVICE_NAME);
|
|
input_info(true, &ts->client->dev, "%s: ============================================================\n", __func__);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*******************************************************
|
|
Description:
|
|
Novatek touchscreen /proc/NVTSPI deinitial function.
|
|
|
|
return:
|
|
n.a.
|
|
*******************************************************/
|
|
static void nvt_flash_proc_deinit(void)
|
|
{
|
|
if (NVT_proc_entry != NULL) {
|
|
remove_proc_entry(DEVICE_NAME, NULL);
|
|
NVT_proc_entry = NULL;
|
|
input_info(true, &ts->client->dev, "%s: Removed /proc/%s\n", __func__, DEVICE_NAME);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if WAKEUP_GESTURE
|
|
|
|
/* customized gesture id */
|
|
#define DATA_PROTOCOL 30
|
|
|
|
/* function page definition */
|
|
#define FUNCPAGE_GESTURE 0x01
|
|
#if PROXIMITY_FUNCTION
|
|
#define FUNCPAGE_PROXIMITY 0x02
|
|
#endif
|
|
|
|
/*******************************************************
|
|
Description:
|
|
Novatek touchscreen wake up gesture key report function.
|
|
|
|
return:
|
|
n.a.
|
|
*******************************************************/
|
|
void nvt_ts_wakeup_gesture_report(uint8_t gesture_id, uint8_t *data)
|
|
{
|
|
uint32_t keycode = 0;
|
|
uint8_t func_type = data[2];
|
|
uint8_t func_id = data[3];
|
|
|
|
/* support fw specifal data protocol */
|
|
if ((gesture_id == DATA_PROTOCOL) && (func_type == FUNCPAGE_GESTURE)) {
|
|
gesture_id = func_id;
|
|
} else if (gesture_id > DATA_PROTOCOL) {
|
|
input_info(true, &ts->client->dev,"gesture_id %d is invalid, func_type=%d, func_id=%d\n",
|
|
gesture_id, func_type, func_id);
|
|
return;
|
|
}
|
|
|
|
input_info(true, &ts->client->dev,"gesture_id = %d\n", gesture_id);
|
|
|
|
switch (gesture_id) {
|
|
case GESTURE_WORD_C:
|
|
input_info(true, &ts->client->dev,"Gesture : Word-C.\n");
|
|
keycode = gesture_key_array[0];
|
|
break;
|
|
case GESTURE_WORD_W:
|
|
input_info(true, &ts->client->dev,"Gesture : Word-W.\n");
|
|
keycode = gesture_key_array[1];
|
|
break;
|
|
case GESTURE_WORD_V:
|
|
input_info(true, &ts->client->dev,"Gesture : Word-V.\n");
|
|
keycode = gesture_key_array[2];
|
|
break;
|
|
case GESTURE_DOUBLE_CLICK:
|
|
input_info(true, &ts->client->dev,"Gesture : Double Click.\n");
|
|
keycode = gesture_key_array[3];
|
|
break;
|
|
case GESTURE_WORD_Z:
|
|
input_info(true, &ts->client->dev,"Gesture : Word-Z.\n");
|
|
keycode = gesture_key_array[4];
|
|
break;
|
|
case GESTURE_WORD_M:
|
|
input_info(true, &ts->client->dev,"Gesture : Word-M.\n");
|
|
keycode = gesture_key_array[5];
|
|
break;
|
|
case GESTURE_WORD_O:
|
|
input_info(true, &ts->client->dev,"Gesture : Word-O.\n");
|
|
keycode = gesture_key_array[6];
|
|
break;
|
|
case GESTURE_WORD_e:
|
|
input_info(true, &ts->client->dev,"Gesture : Word-e.\n");
|
|
keycode = gesture_key_array[7];
|
|
break;
|
|
case GESTURE_WORD_S:
|
|
input_info(true, &ts->client->dev,"Gesture : Word-S.\n");
|
|
keycode = gesture_key_array[8];
|
|
break;
|
|
case GESTURE_SLIDE_UP:
|
|
input_info(true, &ts->client->dev,"Gesture : Slide UP.\n");
|
|
keycode = gesture_key_array[9];
|
|
break;
|
|
case GESTURE_SLIDE_DOWN:
|
|
input_info(true, &ts->client->dev,"Gesture : Slide DOWN.\n");
|
|
keycode = gesture_key_array[10];
|
|
break;
|
|
case GESTURE_SLIDE_LEFT:
|
|
input_info(true, &ts->client->dev,"Gesture : Slide LEFT.\n");
|
|
keycode = gesture_key_array[11];
|
|
break;
|
|
case GESTURE_SLIDE_RIGHT:
|
|
input_info(true, &ts->client->dev,"Gesture : Slide RIGHT.\n");
|
|
keycode = gesture_key_array[12];
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (keycode > 0) {
|
|
input_report_key(ts->input_dev, keycode, 1);
|
|
input_sync(ts->input_dev);
|
|
input_report_key(ts->input_dev, keycode, 0);
|
|
input_sync(ts->input_dev);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static int pinctrl_configure(struct nvt_ts_data *ts, bool enable)
|
|
{
|
|
struct pinctrl_state *state;
|
|
int ret = 0;
|
|
|
|
input_info(true, &ts->client->dev, "%s: %s\n", __func__, enable ? "ACTIVE" : "SUSPEND");
|
|
|
|
if (enable) {
|
|
state = pinctrl_lookup_state(ts->platdata->pinctrl, "on_state");
|
|
if (IS_ERR(ts->platdata->pinctrl))
|
|
input_info(true, &ts->client->dev, "%s: could not get active pinstate\n", __func__);
|
|
} else {
|
|
state = pinctrl_lookup_state(ts->platdata->pinctrl, "off_state");
|
|
if (IS_ERR(ts->platdata->pinctrl))
|
|
input_info(true, &ts->client->dev, "%s: could not get suspend pinstate\n", __func__);
|
|
}
|
|
|
|
if (!IS_ERR_OR_NULL(state)) {
|
|
ret = pinctrl_select_state(ts->platdata->pinctrl, state);
|
|
if (ret < 0)
|
|
input_err(true, &ts->client->dev, "%s: could not control pinstate\n", __func__);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*******************************************************
|
|
Description:
|
|
Novatek touchscreen parse device tree function.
|
|
|
|
return:
|
|
n.a.
|
|
*******************************************************/
|
|
#ifdef CONFIG_OF
|
|
static int nvt_parse_dt(struct device *dev)
|
|
{
|
|
struct nvt_ts_platdata *platdata = dev->platform_data;
|
|
struct device_node *np = dev->of_node;
|
|
int32_t ret = 0;
|
|
int tmp[3];
|
|
int lcd_id1_gpio = 0, lcd_id2_gpio = 0, lcd_id3_gpio = 0, dt_lcdtype;
|
|
int fw_name_cnt;
|
|
int lcdtype_cnt;
|
|
int fw_sel_idx = 0;
|
|
#if defined(CONFIG_EXYNOS_DPU30)
|
|
int lcdtype = 0;
|
|
int connected = 0;
|
|
#endif
|
|
|
|
input_info(true, dev, "%s: start!\n", __func__);
|
|
|
|
#if defined(CONFIG_EXYNOS_DPU30)
|
|
connected = get_lcd_info("connected");
|
|
if (connected < 0) {
|
|
input_err(true, dev, "%s: Failed to get lcd info\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!connected) {
|
|
input_err(true, dev, "%s: lcd is disconnected\n", __func__);
|
|
return -ENODEV;
|
|
}
|
|
|
|
input_info(true, dev, "%s: lcd is connected\n", __func__);
|
|
|
|
lcdtype = get_lcd_info("id");
|
|
if (lcdtype < 0) {
|
|
input_err(true, dev, "%s: Failed to get lcd info\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
#else
|
|
if (lcdtype == 0) {
|
|
input_err(true, dev, "%s: panel is not conneted\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
#endif
|
|
|
|
fw_name_cnt = of_property_count_strings(np, "novatek,fw_name");
|
|
|
|
if (fw_name_cnt == 0) {
|
|
input_err(true, dev, "%s: no fw_name in DT\n", __func__);
|
|
return -EINVAL;
|
|
|
|
} else if (fw_name_cnt == 1) {
|
|
ret = of_property_read_u32(np, "novatek,lcdtype", &dt_lcdtype);
|
|
if (ret < 0) {
|
|
input_err(true, dev, "%s: failed to read novatek,lcdtype\n", __func__);
|
|
|
|
} else {
|
|
input_info(true, dev, "%s: fw_name_cnt(1), ap lcdtype=0x%06X & dt lcdtype=0x%06X\n",
|
|
__func__, lcdtype, dt_lcdtype);
|
|
if (lcdtype != dt_lcdtype) {
|
|
input_err(true, dev, "%s: panel mismatched, unload driver\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
} else {
|
|
|
|
lcd_id1_gpio = of_get_named_gpio(np, "novatek,lcdid1-gpio", 0);
|
|
if (gpio_is_valid(lcd_id1_gpio))
|
|
input_info(true, dev, "%s: lcd id1_gpio %d(%d)\n", __func__, lcd_id1_gpio, gpio_get_value(lcd_id1_gpio));
|
|
else {
|
|
input_err(true, dev, "%s: Failed to get novatek,lcdid1-gpio\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
lcd_id2_gpio = of_get_named_gpio(np, "novatek,lcdid2-gpio", 0);
|
|
if (gpio_is_valid(lcd_id2_gpio))
|
|
input_info(true, dev, "%s: lcd id2_gpio %d(%d)\n", __func__, lcd_id2_gpio, gpio_get_value(lcd_id2_gpio));
|
|
else {
|
|
input_err(true, dev, "%s: Failed to get novatek,lcdid2-gpio\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* support lcd id3 */
|
|
lcd_id3_gpio = of_get_named_gpio(np, "novatek,lcdid3-gpio", 0);
|
|
if (gpio_is_valid(lcd_id3_gpio)) {
|
|
input_info(true, dev, "%s: lcd id3_gpio %d(%d)\n", __func__, lcd_id3_gpio, gpio_get_value(lcd_id3_gpio));
|
|
fw_sel_idx = (gpio_get_value(lcd_id3_gpio) << 2) | (gpio_get_value(lcd_id2_gpio) << 1) | gpio_get_value(lcd_id1_gpio);
|
|
|
|
} else {
|
|
input_err(true, dev, "%s: Failed to get novatek,lcdid3-gpio and use #1  id\n", __func__);
|
|
fw_sel_idx = (gpio_get_value(lcd_id2_gpio) << 1) | gpio_get_value(lcd_id1_gpio);
|
|
}
|
|
|
|
lcdtype_cnt = of_property_count_u32_elems(np, "novatek,lcdtype");
|
|
input_info(true, dev, "%s: fw_name_cnt(%d) & lcdtype_cnt(%d) & fw_sel_idx(%d)\n",
|
|
__func__, fw_name_cnt, lcdtype_cnt, fw_sel_idx);
|
|
|
|
if (lcdtype_cnt <= 0 || fw_name_cnt <= 0 || lcdtype_cnt <= fw_sel_idx || fw_name_cnt <= fw_sel_idx) {
|
|
input_err(true, dev, "%s: abnormal lcdtype & fw name count, fw_sel_idx(%d)\n", __func__, fw_sel_idx);
|
|
return -EINVAL;
|
|
}
|
|
of_property_read_u32_index(np, "novatek,lcdtype", fw_sel_idx, &dt_lcdtype);
|
|
input_info(true, dev, "%s: lcd id(%d), ap lcdtype=0x%06X & dt lcdtype=0x%06X\n",
|
|
__func__, fw_sel_idx, lcdtype, dt_lcdtype);
|
|
|
|
#if 0 //defined(CONFIG_EXYNOS_DPU30)
|
|
if (lcdtype != dt_lcdtype) {
|
|
input_err(true, dev, "%s: panel mismatched, unload driver\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
of_property_read_string_index(np, "novatek,fw_name", fw_sel_idx, &platdata->firmware_name);
|
|
if (platdata->firmware_name == NULL || strlen(platdata->firmware_name) == 0) {
|
|
input_err(true, dev, "%s: Failed to get fw name\n", __func__);
|
|
return -EINVAL;
|
|
} else {
|
|
input_info(true, dev, "%s: fw name(%s)\n", __func__, platdata->firmware_name);
|
|
}
|
|
|
|
of_property_read_string_index(np, "novatek,fw_name_mp", fw_sel_idx, &platdata->firmware_name_mp);
|
|
if (platdata->firmware_name_mp == NULL || strlen(platdata->firmware_name_mp) == 0) {
|
|
input_err(true, dev, "%s: Failed to get mp fw name\n", __func__);
|
|
return -EINVAL;
|
|
} else {
|
|
input_info(true, dev, "%s: fw name(%s)\n", __func__, platdata->firmware_name_mp);
|
|
}
|
|
|
|
if (of_property_read_string(np, "novatek,regulator_lcd_vdd", &platdata->regulator_lcd_vdd)) {
|
|
input_err(true, dev, "%s: Failed to get regulator_dvdd name property\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (of_property_read_string(np, "novatek,regulator_lcd_reset", &platdata->regulator_lcd_reset)) {
|
|
input_err(true, dev, "%s: Failed to get regulator_dvdd name property\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (of_property_read_string(np, "novatek,regulator_lcd_bl", &platdata->regulator_lcd_bl)) {
|
|
input_err(true, dev, "%s: Failed to get regulator_dvdd name property\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
#if NVT_TOUCH_SUPPORT_HW_RST
|
|
ts->reset_gpio = of_get_named_gpio_flags(np, "novatek,reset-gpio", 0, &ts->reset_flags);
|
|
input_info(true, dev, "%s: novatek,reset-gpio=%d\n", __func__, ts->reset_gpio);
|
|
#endif
|
|
platdata->irq_gpio = of_get_named_gpio_flags(np, "novatek,irq-gpio", 0, &platdata->irq_flags);
|
|
if (!gpio_is_valid(platdata->irq_gpio)) {
|
|
pr_err("failed to get irq-gpio(%d)\n", platdata->irq_gpio);
|
|
return -EINVAL;
|
|
} else if (!platdata->irq_flags) {
|
|
platdata->irq_flags = IRQ_TYPE_EDGE_RISING | IRQF_ONESHOT;
|
|
}
|
|
input_info(true, dev, "%s: novatek,irq-gpio=%d\n", __func__, platdata->irq_gpio);
|
|
|
|
platdata->cs_gpio = of_get_named_gpio(np, "novatek,cs-gpio", 0);
|
|
|
|
if (!gpio_is_valid(platdata->cs_gpio)) {
|
|
platdata->cs_gpio = -1;
|
|
input_info(true, dev, "%s: gpio_cs value is not valid\n", __func__);
|
|
|
|
} else {
|
|
int error;
|
|
input_info(true, dev, "%s: cs_gpio=%d\n", __func__, platdata->cs_gpio);
|
|
error = gpio_request(platdata->cs_gpio, "novatek,cs-gpio");
|
|
|
|
if (error < 0) {
|
|
input_err(true, dev, "%s: request gpio_cs pin failed\n", __func__);
|
|
} else {
|
|
gpio_direction_output(platdata->cs_gpio, 1);
|
|
}
|
|
}
|
|
|
|
ret = of_property_read_u32(np, "novatek,swrst-n8-addr", &SWRST_N8_ADDR);
|
|
if (ret) {
|
|
input_err(true, dev, "%s: error reading novatek,swrst-n8-addr. ret=%d\n", __func__, ret);
|
|
return -ENXIO;
|
|
} else {
|
|
input_info(true, dev, "%s: SWRST_N8_ADDR=0x%06X\n", __func__, SWRST_N8_ADDR);
|
|
}
|
|
|
|
ret = of_property_read_u32(np, "novatek,spi-rd-fast-addr", &SPI_RD_FAST_ADDR);
|
|
if (ret) {
|
|
input_info(true, dev, "%s: not support novatek,spi-rd-fast-addr\n", __func__);
|
|
SPI_RD_FAST_ADDR = 0;
|
|
// ret = ERR_PTR(-ENXIO);
|
|
} else {
|
|
input_info(true, dev, "%s: SPI_RD_FAST_ADDR=0x%06X\n", __func__, SPI_RD_FAST_ADDR);
|
|
}
|
|
|
|
ret = of_property_read_u32_array(np, "novatek,resolution", tmp, 2);
|
|
if (!ret) {
|
|
platdata->abs_x_max = tmp[0];
|
|
platdata->abs_y_max = tmp[1];
|
|
} else {
|
|
platdata->abs_x_max = TOUCH_DEFAULT_MAX_WIDTH;
|
|
platdata->abs_y_max = TOUCH_DEFAULT_MAX_HEIGHT;
|
|
}
|
|
|
|
ret = of_property_read_u32_array(np, "novatek,max_touch_num", tmp, 1);
|
|
if (!ret)
|
|
platdata->max_touch_num = tmp[0];
|
|
else
|
|
platdata->max_touch_num = TOUCH_MAX_FINGER_NUM;
|
|
|
|
if (of_property_read_u32_array(np, "novatek,area-size", tmp, 3)) {
|
|
platdata->area_indicator = 48;
|
|
platdata->area_navigation = 96;
|
|
platdata->area_edge = 60;
|
|
} else {
|
|
platdata->area_indicator = tmp[0];
|
|
platdata->area_navigation = tmp[1];
|
|
platdata->area_edge = tmp[2];
|
|
}
|
|
|
|
input_info(true, dev, "%s: zone's size - indicator:%d, navigation:%d, edge:%d\n",
|
|
__func__, platdata->area_indicator, platdata->area_navigation, platdata->area_edge);
|
|
|
|
input_info(true, dev, "%s: irq_flags: 0x%X, resolution: (%d, %d), max_touch_num: %d\n",
|
|
__func__, platdata->irq_flags,// platdata->firmware_name,
|
|
platdata->abs_x_max, platdata->abs_y_max,
|
|
platdata->max_touch_num);
|
|
|
|
platdata->support_ear_detect = of_property_read_bool(np, "novatek,support_ear_detect_mode");
|
|
input_info(true, dev, "%s: ED mode %s\n",
|
|
__func__, platdata->support_ear_detect ? "ON" : "OFF");
|
|
|
|
platdata->enable_settings_aot = of_property_read_bool(np, "novatek,enable_settings_aot");
|
|
input_info(true, dev, "%s: AOT mode %s\n",
|
|
__func__, platdata->enable_settings_aot ? "ON" : "OFF");
|
|
|
|
platdata->enable_sysinput_enabled = of_property_read_bool(np, "novatek,enable_sysinput_enabled");
|
|
input_info(true, dev, "%s: Sysinput enabled %s\n",
|
|
__func__, platdata->enable_sysinput_enabled ? "ON" : "OFF");
|
|
|
|
platdata->prox_lp_scan_enabled = of_property_read_bool(np, "novatek,prox_lp_scan_enabled");
|
|
input_info(true, dev, "%s: Prox LP Scan enabled %s\n",
|
|
__func__, platdata->prox_lp_scan_enabled ? "ON" : "OFF");
|
|
|
|
input_info(true, dev, "%s: end!\n", __func__);
|
|
|
|
return 0;
|
|
}
|
|
#else
|
|
static int nvt_parse_dt(struct device *dev)
|
|
{
|
|
struct nvt_ts_platdata *platdata = dev->platform_data;
|
|
|
|
input_err(true, &dev, "no platform data specified\n");
|
|
|
|
#if NVT_TOUCH_SUPPORT_HW_RST
|
|
ts->reset_gpio = NVTTOUCH_RST_PIN;
|
|
#endif
|
|
platdata->irq_gpio = NVTTOUCH_INT_PIN;
|
|
return platdata;
|
|
}
|
|
#endif
|
|
|
|
static int nvt_regulator_init(struct nvt_ts_data *ts)
|
|
{
|
|
ts->regulator_vdd = regulator_get(NULL, ts->platdata->regulator_lcd_vdd);
|
|
if (IS_ERR(ts->regulator_vdd)) {
|
|
input_err(true, &ts->client->dev, "%s: Failed to get %s regulator.\n",
|
|
__func__, "vdd_ldo28");
|
|
return PTR_ERR(ts->regulator_vdd);
|
|
}
|
|
|
|
ts->regulator_lcd_reset = regulator_get(NULL, ts->platdata->regulator_lcd_reset);
|
|
if (IS_ERR(ts->regulator_lcd_reset)) {
|
|
input_err(true, &ts->client->dev, "%s: Failed to get %s regulator.\n",
|
|
__func__, "gpio_lcd_rst");
|
|
return PTR_ERR(ts->regulator_lcd_reset);
|
|
}
|
|
|
|
ts->regulator_lcd_bl_en = regulator_get(NULL, ts->platdata->regulator_lcd_bl);
|
|
if (IS_ERR(ts->regulator_lcd_bl_en)) {
|
|
input_err(true, &ts->client->dev, "%s: Failed to get %s regulator.\n",
|
|
__func__, "gpio_lcd_bl_en");
|
|
return PTR_ERR(ts->regulator_lcd_bl_en);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*******************************************************
|
|
Description:
|
|
Novatek touchscreen config and request gpio
|
|
|
|
return:
|
|
Executive outcomes. 0---succeed. not 0---failed.
|
|
*******************************************************/
|
|
static int nvt_gpio_config(struct device *dev, struct nvt_ts_platdata *platdata)
|
|
{
|
|
int32_t ret = 0;
|
|
|
|
#if NVT_TOUCH_SUPPORT_HW_RST
|
|
/* request RST-pin (Output/High) */
|
|
if (gpio_is_valid(ts->reset_gpio)) {
|
|
ret = gpio_request_one(ts->reset_gpio, GPIOF_OUT_INIT_LOW, "NVT-tp-rst");
|
|
if (ret) {
|
|
input_err(true, &ts->client->dev,"Failed to request NVT-tp-rst GPIO\n");
|
|
goto err_request_reset_gpio;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* request INT-pin (Input) */
|
|
if (gpio_is_valid(platdata->irq_gpio)) {
|
|
ret = gpio_request_one(platdata->irq_gpio, GPIOF_IN, "NVT-int");
|
|
if (ret) {
|
|
input_err(true, &ts->client->dev,"Failed to request NVT-int GPIO\n");
|
|
goto err_request_irq_gpio;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
|
|
err_request_irq_gpio:
|
|
#if NVT_TOUCH_SUPPORT_HW_RST
|
|
gpio_free(ts->reset_gpio);
|
|
err_request_reset_gpio:
|
|
#endif
|
|
return ret;
|
|
}
|
|
|
|
/*******************************************************
|
|
Description:
|
|
Novatek touchscreen deconfig gpio
|
|
|
|
return:
|
|
n.a.
|
|
*******************************************************/
|
|
static void nvt_gpio_deconfig(struct nvt_ts_data *ts)
|
|
{
|
|
if (gpio_is_valid(ts->platdata->irq_gpio))
|
|
gpio_free(ts->platdata->irq_gpio);
|
|
#if NVT_TOUCH_SUPPORT_HW_RST
|
|
if (gpio_is_valid(ts->reset_gpio))
|
|
gpio_free(ts->reset_gpio);
|
|
#endif
|
|
}
|
|
|
|
static uint8_t nvt_fw_recovery(uint8_t *point_data)
|
|
{
|
|
uint8_t i = 0;
|
|
uint8_t detected = true;
|
|
|
|
/* check pattern */
|
|
for (i=1 ; i<7 ; i++) {
|
|
if (point_data[i] != 0x77) {
|
|
detected = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return detected;
|
|
}
|
|
|
|
#if NVT_TOUCH_ESD_PROTECT
|
|
void nvt_esd_check_enable(uint8_t enable)
|
|
{
|
|
/* update interrupt timer */
|
|
irq_timer = jiffies;
|
|
/* clear esd_retry counter, if protect function is enabled */
|
|
esd_retry = enable ? 0 : esd_retry;
|
|
/* enable/disable esd check flag */
|
|
esd_check = enable;
|
|
}
|
|
|
|
static void nvt_esd_check_func(struct work_struct *work)
|
|
{
|
|
unsigned int timer = jiffies_to_msecs(jiffies - irq_timer);
|
|
|
|
//pr_info("esd_check = %d (retry %d)\n", esd_check, esd_retry); //DEBUG
|
|
|
|
if ((timer > NVT_TOUCH_ESD_CHECK_PERIOD) && esd_check) {
|
|
mutex_lock(&ts->lock);
|
|
input_err(true, &ts->client->dev, "%s: do ESD recovery, timer = %d, retry = %d\n",
|
|
__func__, timer, esd_retry);
|
|
/* do esd recovery, reload fw */
|
|
nvt_update_firmware(ts->platdata->firmware_name);
|
|
if (nvt_check_fw_reset_state(RESET_STATE_REK))
|
|
input_err(true, &ts->client->dev, "%s: Check FW state failed after ESD recovery\n", __func__);
|
|
else
|
|
nvt_ts_mode_restore(ts);
|
|
mutex_unlock(&ts->lock);
|
|
/* update interrupt timer */
|
|
irq_timer = jiffies;
|
|
/* update esd_retry counter */
|
|
esd_retry++;
|
|
}
|
|
|
|
queue_delayed_work(nvt_esd_check_wq, &nvt_esd_check_work,
|
|
msecs_to_jiffies(NVT_TOUCH_ESD_CHECK_PERIOD));
|
|
}
|
|
#endif /* #if NVT_TOUCH_ESD_PROTECT */
|
|
|
|
#if NVT_TOUCH_WDT_RECOVERY
|
|
static uint8_t recovery_cnt = 0;
|
|
static uint8_t nvt_wdt_fw_recovery(uint8_t *point_data)
|
|
{
|
|
uint32_t recovery_cnt_max = 10;
|
|
uint8_t recovery_enable = false;
|
|
uint8_t i = 0;
|
|
|
|
recovery_cnt++;
|
|
|
|
/* check pattern */
|
|
for (i=1 ; i<7 ; i++) {
|
|
if ((point_data[i] != 0xFD) && (point_data[i] != 0xFE)) {
|
|
recovery_cnt = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (recovery_cnt > recovery_cnt_max){
|
|
recovery_enable = true;
|
|
recovery_cnt = 0;
|
|
}
|
|
|
|
return recovery_enable;
|
|
}
|
|
#endif /* #if NVT_TOUCH_WDT_RECOVERY */
|
|
|
|
void nvt_print_info(void)
|
|
{
|
|
if (!ts)
|
|
return;
|
|
|
|
ts->print_info_cnt_open++;
|
|
|
|
if (ts->print_info_cnt_open > 0xfff0)
|
|
ts->print_info_cnt_open = 0;
|
|
|
|
if (ts->touch_count == 0)
|
|
ts->print_info_cnt_release++;
|
|
|
|
input_info(true, &ts->client->dev,
|
|
"tc:%d ver:0x%02X%02X%02X%02X mode:%04X noise:%d aot:%d ed:%d res(%d,%d) // #%d %d\n",
|
|
ts->touch_count, ts->fw_ver_bin[0], ts->fw_ver_bin[1],
|
|
ts->fw_ver_bin[2], ts->fw_ver_bin[3],
|
|
ts->sec_function, ts->noise_mode, ts->aot_enable, ts->ear_detect_mode,
|
|
ts->early_resume_cnt, ts->resume_cnt,
|
|
ts->print_info_cnt_open, ts->print_info_cnt_release);
|
|
|
|
}
|
|
|
|
static void nvt_print_info_work(struct work_struct *work)
|
|
{
|
|
nvt_print_info();
|
|
|
|
schedule_delayed_work(&ts->work_print_info, msecs_to_jiffies(TOUCH_PRINT_INFO_DWORK_TIME));
|
|
}
|
|
|
|
|
|
#if POINT_DATA_CHECKSUM
|
|
static int32_t nvt_ts_point_data_checksum(uint8_t *buf, uint8_t length)
|
|
{
|
|
uint8_t checksum = 0;
|
|
int32_t i = 0;
|
|
|
|
// Generate checksum
|
|
for (i = 0; i < length - 1; i++) {
|
|
checksum += buf[i + 1];
|
|
}
|
|
checksum = (~checksum + 1);
|
|
|
|
// Compare ckecksum and dump fail data
|
|
if (checksum != buf[length]) {
|
|
input_info(true, &ts->client->dev,"i2c/spi packet checksum not match. (point_data[%d]=0x%02X, checksum=0x%02X)\n",
|
|
length, buf[length], checksum);
|
|
|
|
for (i = 0; i < 10; i++) {
|
|
input_info(true, &ts->client->dev,"%02X %02X %02X %02X %02X %02X\n",
|
|
buf[1 + i*6], buf[2 + i*6], buf[3 + i*6], buf[4 + i*6], buf[5 + i*6], buf[6 + i*6]);
|
|
}
|
|
|
|
input_info(true, &ts->client->dev,"%02X %02X %02X %02X %02X\n", buf[61], buf[62], buf[63], buf[64], buf[65]);
|
|
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif /* POINT_DATA_CHECKSUM */
|
|
|
|
#define NVT_TS_LOCATION_DETECT_SIZE 6
|
|
/************************************************************
|
|
* 720 * 1480 : <48 96 60> indicator: 24dp navigator:48dp edge:60px dpi=320
|
|
* 1080 * 2220 : 4096 * 4096 : <133 266 341> (approximately value)
|
|
************************************************************/
|
|
static void location_detect(struct nvt_ts_data *ts, char *loc, int x, int y)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < NVT_TS_LOCATION_DETECT_SIZE; ++i)
|
|
loc[i] = 0;
|
|
|
|
if (x < ts->platdata->area_edge)
|
|
strcat(loc, "E.");
|
|
else if (x < (ts->platdata->abs_x_max - ts->platdata->area_edge))
|
|
strcat(loc, "C.");
|
|
else
|
|
strcat(loc, "e.");
|
|
|
|
if (y < ts->platdata->area_indicator)
|
|
strcat(loc, "S");
|
|
else if (y < (ts->platdata->abs_y_max - ts->platdata->area_navigation))
|
|
strcat(loc, "C");
|
|
else
|
|
strcat(loc, "N");
|
|
}
|
|
|
|
static void nvt_ts_print_coord(struct nvt_ts_data *ts)
|
|
{
|
|
int i;
|
|
char location[NVT_TS_LOCATION_DETECT_SIZE] = { 0, };
|
|
|
|
for (i = 0; i < TOUCH_MAX_FINGER_NUM; i++) {
|
|
location_detect(ts, location, ts->coords[i].x, ts->coords[i].y);
|
|
|
|
if (ts->coords[i].press && !ts->coords[i].p_press) {
|
|
ts->touch_count++;
|
|
// ts->all_finger_count++;
|
|
|
|
#if !defined(CONFIG_SAMSUNG_PRODUCT_SHIP)
|
|
input_info(true, &ts->client->dev, "[P] tId:%d.%d x:%d y:%d major:%d minor:%d loc:%s tc:%d type:%X\n",
|
|
i, (ts->input_dev->mt->trkid - 1) & TRKID_MAX,
|
|
ts->coords[i].x, ts->coords[i].y, ts->coords[i].w_major, ts->coords[i].w_minor,
|
|
location, ts->touch_count, ts->coords[i].status);
|
|
#else
|
|
input_info(true, &ts->client->dev, "[P] tId:%d.%d major:%d minor:%d loc:%s tc:%d type:%X\n",
|
|
i, (ts->input_dev->mt->trkid - 1) & TRKID_MAX,
|
|
ts->coords[i].w_major, ts->coords[i].w_minor, location, ts->touch_count,
|
|
ts->coords[i].status);
|
|
#endif
|
|
ts->coords[i].p_press = ts->coords[i].press;
|
|
ts->coords[i].p_x = ts->coords[i].x;
|
|
ts->coords[i].p_y = ts->coords[i].y;
|
|
|
|
ts->coords[i].p_status = 0;
|
|
ts->coords[i].move_count = 0;
|
|
|
|
} else if (!ts->coords[i].press && ts->coords[i].p_press) {
|
|
ts->touch_count--;
|
|
|
|
// if (!ts->touch_count)
|
|
// ts->print_info_cnt_release = 0;
|
|
|
|
#if !defined(CONFIG_SAMSUNG_PRODUCT_SHIP)
|
|
input_info(true, &ts->client->dev, "[R] tId:%d loc:%s dd:%d,%d mc:%d tc:%d lx:%d ly:%d\n",
|
|
i, location, ts->coords[i].p_x - ts->coords[i].x,
|
|
ts->coords[i].p_y - ts->coords[i].y,
|
|
ts->coords[i].move_count, ts->touch_count,
|
|
ts->coords[i].x, ts->coords[i].y);
|
|
#else
|
|
input_info(true, &ts->client->dev, "[R] tId:%d loc:%s dd:%d,%d mc:%d tc:%d\n",
|
|
i, location, ts->coords[i].p_x - ts->coords[i].x,
|
|
ts->coords[i].p_y - ts->coords[i].y,
|
|
ts->coords[i].move_count, ts->touch_count);
|
|
#endif
|
|
ts->coords[i].p_press = false;
|
|
} else if (ts->coords[i].press) {
|
|
if (ts->coords[i].p_status && (ts->coords[i].status != ts->coords[i].p_status)) {
|
|
#if !defined(CONFIG_SAMSUNG_PRODUCT_SHIP)
|
|
input_info(true, &ts->client->dev, "[C] tId:%d x:%d y:%d major:%d minor:%d tc:%d type:%X\n",
|
|
i, ts->coords[i].x, ts->coords[i].y, ts->coords[i].w_major, ts->coords[i].w_minor,
|
|
ts->touch_count, ts->coords[i].status);
|
|
#else
|
|
input_info(true, &ts->client->dev, "[C] tId:%d major:%d minor:%d tc:%d type:%X\n",
|
|
i, ts->coords[i].w_major, ts->coords[i].w_minor,
|
|
ts->touch_count, ts->coords[i].status);
|
|
#endif
|
|
}
|
|
|
|
ts->coords[i].p_status = ts->coords[i].status;
|
|
ts->coords[i].move_count++;
|
|
}
|
|
}
|
|
}
|
|
|
|
#define POINT_DATA_LEN 108
|
|
#define FINGER_ENTER 0x01
|
|
#define FINGER_MOVING 0x02
|
|
#define GLOVE_TOUCH 0x06
|
|
|
|
#if PROXIMITY_FUNCTION
|
|
void nvt_ts_proximity_report(uint8_t *data)
|
|
{
|
|
struct nvt_ts_event_proximity *p_event_proximity;
|
|
u8 status = 0;
|
|
|
|
p_event_proximity = (struct nvt_ts_event_proximity *)&data[1];
|
|
|
|
/* check data page */
|
|
if (p_event_proximity->data_page != FUNCPAGE_PROXIMITY) {
|
|
input_info(true, &ts->client->dev,"proximity data page %d is invalid\n", p_event_proximity->data_page);
|
|
return;
|
|
}
|
|
|
|
status = p_event_proximity->status;
|
|
|
|
input_info(true, &ts->client->dev,"proximity->status = %d\n", status);
|
|
|
|
input_info(true, &ts->client->dev, "%s hover : %d\n", __func__, status);
|
|
ts->hover_event = status;
|
|
input_report_abs(ts->input_dev_proximity, ABS_MT_CUSTOM, status);
|
|
input_sync(ts->input_dev_proximity);
|
|
|
|
#if 0
|
|
switch (p_event_proximity->status) {
|
|
/* Case amount depends on the status amount */
|
|
case 0:
|
|
pr_info(true, &ts->client->dev,"Proximity State 0.\n");
|
|
/* Action have to be done, like notify DP to do something */
|
|
break;
|
|
case 1:
|
|
pr_info("Proximity State 1.\n");
|
|
/* Action have to be done, like notify DP to do something */
|
|
break;
|
|
case 2:
|
|
pr_info("Proximity State 2.\n");
|
|
/* Action have to be done, like notify DP to do something */
|
|
break;
|
|
case 3:
|
|
pr_info("Proximity State 3.\n");
|
|
/* Action have to be done, like notify DP to do something */
|
|
break;
|
|
case 4:
|
|
pr_info("Proximity State 4.\n");
|
|
/* Action have to be done, like notify DP to do something */
|
|
break;
|
|
case 5:
|
|
pr_info("Proximity State 5.\n");
|
|
/* Action have to be done, like notify DP to do something */
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
}
|
|
#endif
|
|
|
|
/*******************************************************
|
|
Description:
|
|
Novatek touchscreen work function.
|
|
|
|
return:
|
|
n.a.
|
|
*******************************************************/
|
|
static irqreturn_t nvt_ts_work_func(int irq, void *data)
|
|
{
|
|
struct nvt_ts_platdata *platdata = ts->platdata;
|
|
struct nvt_ts_event_coord *p_event_coord;
|
|
int32_t ret = -1;
|
|
uint8_t point_data[POINT_DATA_LEN + 1 + DUMMY_BYTES] = {0};
|
|
u16 x = 0, y = 0, w_major = 0, w_minor = 0, p = 0;
|
|
u8 id = 0, status = 0;
|
|
#if MT_PROTOCOL_B
|
|
uint8_t press_id[TOUCH_MAX_FINGER_NUM] = {0};
|
|
#endif /* MT_PROTOCOL_B */
|
|
int32_t i = 0;
|
|
int32_t finger_cnt = 0;
|
|
|
|
if (ts->power_status == LP_MODE_STATUS)
|
|
pm_wakeup_event(&ts->input_dev->dev, 5000);
|
|
|
|
mutex_lock(&ts->lock);
|
|
|
|
ret = CTP_SPI_READ(ts->client, point_data, POINT_DATA_LEN + 1);
|
|
if (ret < 0) {
|
|
input_err(true, &ts->client->dev, "%s: CTP_SPI_READ failed.(%d)\n", __func__, ret);
|
|
goto XFER_ERROR;
|
|
}
|
|
/*
|
|
//--- dump SPI buf ---
|
|
for (i = 0; i < 10; i++) {
|
|
printk("%02X %02X %02X %02X %02X %02X ",
|
|
point_data[1+i*6], point_data[2+i*6], point_data[3+i*6], point_data[4+i*6], point_data[5+i*6], point_data[6+i*6]);
|
|
}
|
|
printk("\n");
|
|
*/
|
|
|
|
#if NVT_TOUCH_WDT_RECOVERY
|
|
/* ESD protect by WDT */
|
|
if (nvt_wdt_fw_recovery(point_data)) {
|
|
input_err(true, &ts->client->dev,"Recover for fw reset, %02X\n", point_data[1]);
|
|
nvt_update_firmware(ts->platdata->firmware_name);
|
|
if (nvt_check_fw_reset_state(RESET_STATE_REK))
|
|
input_err(true, &ts->client->dev, "%s: Check FW state failed after FW reset recovery\n", __func__);
|
|
else
|
|
nvt_ts_mode_restore(ts);
|
|
|
|
goto XFER_ERROR;
|
|
}
|
|
#endif /* #if NVT_TOUCH_WDT_RECOVERY */
|
|
|
|
/* ESD protect by FW handshake */
|
|
if (nvt_fw_recovery(point_data)) {
|
|
#if NVT_TOUCH_ESD_PROTECT
|
|
nvt_esd_check_enable(true);
|
|
#endif /* #if NVT_TOUCH_ESD_PROTECT */
|
|
goto XFER_ERROR;
|
|
}
|
|
|
|
#if POINT_DATA_CHECKSUM
|
|
if (POINT_DATA_LEN >= POINT_DATA_CHECKSUM_LEN) {
|
|
ret = nvt_ts_point_data_checksum(point_data, POINT_DATA_CHECKSUM_LEN);
|
|
if (ret) {
|
|
goto XFER_ERROR;
|
|
}
|
|
}
|
|
#endif /* POINT_DATA_CHECKSUM */
|
|
|
|
if (ts->power_status == LP_MODE_STATUS && ts->lowpower_mode) {
|
|
id = (uint8_t)(point_data[1] >> 3);
|
|
nvt_ts_wakeup_gesture_report(id, point_data);
|
|
mutex_unlock(&ts->lock);
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
#if PROXIMITY_FUNCTION
|
|
if ((uint8_t)(point_data[1] >> 3) == DATA_PROTOCOL) {
|
|
nvt_ts_proximity_report(point_data);
|
|
mutex_unlock(&ts->lock);
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
if (ts->power_status == LP_MODE_STATUS) {
|
|
input_err(true, &ts->client->dev, "%s: invalid coordinate in lp\n", __func__);
|
|
mutex_unlock(&ts->lock);
|
|
return IRQ_HANDLED;
|
|
}
|
|
#endif
|
|
|
|
for (i = 0, finger_cnt = 0; i < platdata->max_touch_num; i++) {
|
|
p_event_coord = (struct nvt_ts_event_coord *)&point_data[1 + 6 * i];
|
|
id = p_event_coord->id;
|
|
if (!id || (id > platdata->max_touch_num))
|
|
continue;
|
|
|
|
id = id - 1;
|
|
status = p_event_coord->status;
|
|
if ((status == FINGER_ENTER) || (status == FINGER_MOVING) || (status == GLOVE_TOUCH)) {
|
|
#if NVT_TOUCH_ESD_PROTECT
|
|
/* update interrupt timer */
|
|
irq_timer = jiffies;
|
|
#endif /* #if NVT_TOUCH_ESD_PROTECT */
|
|
ts->coords[id].status = status;
|
|
x = ts->coords[id].x = (u32)(p_event_coord->x_11_4 << 4) + (u32)(p_event_coord->x_3_0);
|
|
y = ts->coords[id].y = (u32)(p_event_coord->y_11_4 << 4) + (u32)(p_event_coord->y_3_0);
|
|
if ((x > platdata->abs_x_max) || (y > platdata->abs_y_max)) {
|
|
input_err(true, &ts->client->dev, "%s: invalid coordinate (%d, %d)\n",
|
|
__func__, x, y);
|
|
continue;
|
|
}
|
|
|
|
// width major
|
|
w_major = (u32)(p_event_coord->w_major);
|
|
if (!w_major)
|
|
w_major = 1;
|
|
ts->coords[id].w_major = w_major;
|
|
|
|
// width minor
|
|
w_minor = (u32)point_data[i + 99];
|
|
if (!w_minor)
|
|
w_minor = 1;
|
|
ts->coords[id].w_minor = w_minor;
|
|
|
|
|
|
if (i < 2) {
|
|
p = (u32)(p_event_coord->pressure_7_0) + (u32)(point_data[i + 62] << 8);
|
|
if (p > TOUCH_FORCE_NUM)
|
|
p = TOUCH_FORCE_NUM;
|
|
} else {
|
|
p = (u32)(p_event_coord->pressure_7_0);
|
|
}
|
|
|
|
if (!p)
|
|
p = 1;
|
|
|
|
ts->coords[id].p = p;
|
|
|
|
#if MT_PROTOCOL_B
|
|
press_id[id] = ts->coords[id].press = true;
|
|
input_mt_slot(ts->input_dev, id);
|
|
input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, true);
|
|
#else /* MT_PROTOCOL_B */
|
|
input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, id);
|
|
input_report_key(ts->input_dev, BTN_TOUCH, 1);
|
|
#endif /* MT_PROTOCOL_B */
|
|
|
|
input_report_abs(ts->input_dev, ABS_MT_POSITION_X, x);
|
|
input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, y);
|
|
input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, w_major);
|
|
input_report_abs(ts->input_dev, ABS_MT_TOUCH_MINOR, w_minor);
|
|
// input_report_abs(ts->input_dev, ABS_MT_PRESSURE, p);
|
|
|
|
#if MT_PROTOCOL_B
|
|
#else /* MT_PROTOCOL_B */
|
|
input_mt_sync(ts->input_dev);
|
|
#endif /* MT_PROTOCOL_B */
|
|
|
|
finger_cnt++;
|
|
}
|
|
}
|
|
|
|
#if MT_PROTOCOL_B
|
|
for (i = 0; i < platdata->max_touch_num; i++) {
|
|
if (!press_id[i] && ts->coords[i].p_press) {
|
|
ts->coords[i].press = false;
|
|
|
|
input_mt_slot(ts->input_dev, i);
|
|
input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0);
|
|
input_report_abs(ts->input_dev, ABS_MT_TOUCH_MINOR, 0);
|
|
// input_report_abs(ts->input_dev, ABS_MT_PRESSURE, 0);
|
|
input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, false);
|
|
}
|
|
}
|
|
|
|
input_report_key(ts->input_dev, BTN_TOUCH, (finger_cnt > 0));
|
|
#else /* MT_PROTOCOL_B */
|
|
if (finger_cnt == 0) {
|
|
input_report_key(ts->input_dev, BTN_TOUCH, 0);
|
|
input_mt_sync(ts->input_dev);
|
|
}
|
|
#endif /* MT_PROTOCOL_B */
|
|
|
|
#if TOUCH_KEY_NUM > 0
|
|
if (point_data[61] == 0xF8) {
|
|
#if NVT_TOUCH_ESD_PROTECT
|
|
/* update interrupt timer */
|
|
irq_timer = jiffies;
|
|
#endif /* #if NVT_TOUCH_ESD_PROTECT */
|
|
for (i = 0; i < ts->max_button_num; i++) {
|
|
input_report_key(ts->input_dev, touch_key_array[i], ((point_data[62] >> i) & 0x01));
|
|
}
|
|
} else {
|
|
for (i = 0; i < ts->max_button_num; i++) {
|
|
input_report_key(ts->input_dev, touch_key_array[i], 0);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
input_sync(ts->input_dev);
|
|
|
|
nvt_ts_print_coord(ts);
|
|
|
|
XFER_ERROR:
|
|
|
|
mutex_unlock(&ts->lock);
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
|
|
/*******************************************************
|
|
Description:
|
|
Novatek touchscreen check chip version trim function.
|
|
|
|
return:
|
|
Executive outcomes. 0---NVT IC. -1---not NVT IC.
|
|
*******************************************************/
|
|
static int8_t nvt_ts_check_chip_ver_trim(void)
|
|
{
|
|
uint8_t buf[8] = {0};
|
|
int32_t retry = 0;
|
|
int32_t list = 0;
|
|
int32_t i = 0;
|
|
int32_t found_nvt_chip = 0;
|
|
int32_t ret = -1;
|
|
|
|
//---Check for 5 times---
|
|
for (retry = 5; retry > 0; retry--) {
|
|
|
|
nvt_bootloader_reset();
|
|
|
|
//---set xdata index to 0x1F600---
|
|
nvt_set_page(0x1F64E);
|
|
|
|
buf[0] = 0x4E;
|
|
buf[1] = 0x00;
|
|
buf[2] = 0x00;
|
|
buf[3] = 0x00;
|
|
buf[4] = 0x00;
|
|
buf[5] = 0x00;
|
|
buf[6] = 0x00;
|
|
CTP_SPI_READ(ts->client, buf, 7);
|
|
input_info(true, &ts->client->dev,"buf[1]=0x%02X, buf[2]=0x%02X, buf[3]=0x%02X, buf[4]=0x%02X, buf[5]=0x%02X, buf[6]=0x%02X\n",
|
|
buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]);
|
|
|
|
// compare read chip id on supported list
|
|
for (list = 0; list < (sizeof(trim_id_table) / sizeof(struct nvt_ts_trim_id_table)); list++) {
|
|
found_nvt_chip = 0;
|
|
|
|
// compare each byte
|
|
for (i = 0; i < NVT_ID_BYTE_MAX; i++) {
|
|
if (trim_id_table[list].mask[i]) {
|
|
if (buf[i + 1] != trim_id_table[list].id[i])
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i == NVT_ID_BYTE_MAX) {
|
|
found_nvt_chip = 1;
|
|
}
|
|
|
|
if (found_nvt_chip) {
|
|
input_info(true, &ts->client->dev,"This is NVT touch IC\n");
|
|
ts->mmap = trim_id_table[list].mmap;
|
|
ts->carrier_system = trim_id_table[list].hwinfo->carrier_system;
|
|
ts->hw_crc = trim_id_table[list].hwinfo->hw_crc;
|
|
// ts->fw_ver_ic[0] = ((buf[6] & 0x0F) << 4) | ((buf[5] & 0xF0) >> 4);
|
|
ret = 0;
|
|
goto out;
|
|
} else {
|
|
ts->mmap = NULL;
|
|
ret = -1;
|
|
}
|
|
}
|
|
|
|
msleep(10);
|
|
}
|
|
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
#if 0 // mtk
|
|
static unsigned int lcdtype;
|
|
static int __init get_lcd_type(char *arg)
|
|
{
|
|
get_option(&arg, &lcdtype);
|
|
|
|
printk("%s: lcdtype: %6X\n", __func__, lcdtype);
|
|
|
|
return 0;
|
|
}
|
|
early_param("lcdtype", get_lcd_type);
|
|
#endif
|
|
|
|
static void ts_remaining_var_init(void)
|
|
{
|
|
// u8 i;
|
|
|
|
input_info(true, &ts->client->dev, "%s : start\n", __func__);
|
|
|
|
#if PROXIMITY_FUNCTION
|
|
ts->ear_detect_mode = 0;
|
|
ts->prox_power_off = 0;
|
|
ts->hover_event = 0;
|
|
#endif
|
|
|
|
ts->sec_function = 0;
|
|
|
|
// clear ts->coords data
|
|
memset(ts->coords, 0, sizeof(ts->coords));
|
|
|
|
if (!ts->client->irq)
|
|
ts->irq_enabled = false;
|
|
|
|
ts->touch_count = 0;
|
|
ts->isUMS = false;
|
|
ts->aot_enable = 0;
|
|
ts->power_status = POWER_ON_STATUS;
|
|
ts->noise_mode = 0;
|
|
|
|
input_info(true, &ts->client->dev, "%s : end\n", __func__);
|
|
}
|
|
|
|
/*******************************************************
|
|
Description:
|
|
Novatek touchscreen driver probe function.
|
|
|
|
return:
|
|
Executive outcomes. 0---succeed. negative---failed
|
|
*******************************************************/
|
|
static int32_t nvt_ts_probe(struct spi_device *client)
|
|
{
|
|
int32_t ret = 0;
|
|
#if ((TOUCH_KEY_NUM > 0) || WAKEUP_GESTURE)
|
|
int32_t retry = 0;
|
|
#endif
|
|
struct nvt_ts_platdata *platdata;
|
|
|
|
input_info(true, &client->dev, "%s : start\n", __func__);
|
|
|
|
ts = kzalloc(sizeof(struct nvt_ts_data), GFP_KERNEL);
|
|
if (ts == NULL) {
|
|
input_err(true, &client->dev,"failed to allocated memory for nvt ts data\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
ts->xbuf = (uint8_t *)kzalloc((NVT_TRANSFER_LEN+1+DUMMY_BYTES), GFP_KERNEL);
|
|
if(ts->xbuf == NULL) {
|
|
input_err(true, &client->dev,"kzalloc for xbuf failed!\n");
|
|
ret = -ENOMEM;
|
|
goto err_malloc_xbuf;
|
|
}
|
|
|
|
ts->rbuf = (uint8_t *)kzalloc(NVT_READ_LEN, GFP_KERNEL);
|
|
if(ts->rbuf == NULL) {
|
|
input_err(true, &client->dev, "kzalloc for rbuf failed!\n");
|
|
ret = -ENOMEM;
|
|
goto err_malloc_rbuf;
|
|
}
|
|
|
|
if (client->dev.of_node) {
|
|
platdata = devm_kzalloc(&client->dev,sizeof(struct nvt_ts_platdata), GFP_KERNEL);
|
|
if (!platdata) {
|
|
input_err(true, &client->dev, "%s: Failed to allocate platform data\n", __func__);
|
|
goto err_alloc_platdata_failed;
|
|
}
|
|
|
|
client->dev.platform_data = platdata;
|
|
ret = nvt_parse_dt(&client->dev);
|
|
if (ret < 0) {
|
|
input_err(true, &client->dev, "%s: Failed to parse dt(%d)\n", __func__, ret);
|
|
goto err_parse_dt_failed;
|
|
}
|
|
} else {
|
|
goto err_parse_dt_failed;
|
|
}
|
|
|
|
//---request and config GPIOs---
|
|
ret = nvt_gpio_config(&client->dev, platdata);
|
|
if (ret) {
|
|
input_err(true, &client->dev,"gpio config error!\n");
|
|
goto err_gpio_config_failed;
|
|
}
|
|
|
|
ts->client = client;
|
|
ts->platdata = platdata;
|
|
spi_set_drvdata(client, ts);
|
|
|
|
ts->platdata->pinctrl = devm_pinctrl_get(&client->dev);
|
|
if (IS_ERR(ts->platdata->pinctrl))
|
|
input_info(true, &ts->client->dev, "%s: could not get pinctrl\n", __func__);
|
|
|
|
pinctrl_configure(ts, true);
|
|
|
|
nvt_regulator_init(ts);
|
|
|
|
//---prepare for spi parameter---
|
|
if (ts->client->master->flags & SPI_MASTER_HALF_DUPLEX) {
|
|
input_err(true, &client->dev,"Full duplex not supported by master\n");
|
|
ret = -EIO;
|
|
goto err_ckeck_full_duplex;
|
|
}
|
|
ts->client->bits_per_word = 8;
|
|
ts->client->mode = SPI_MODE_0;
|
|
|
|
ret = spi_setup(ts->client);
|
|
if (ret < 0) {
|
|
input_err(true, &client->dev,"Failed to perform SPI setup\n");
|
|
goto err_spi_setup;
|
|
}
|
|
|
|
#ifdef CONFIG_MTK_SPI
|
|
/* old usage of MTK spi API */
|
|
memcpy(&ts->spi_ctrl, &spi_ctrdata, sizeof(struct mt_chip_conf));
|
|
ts->client->controller_data = (void *)&ts->spi_ctrl;
|
|
#endif
|
|
|
|
#if defined(CONFIG_SPI_MT65XX)
|
|
/* new usage of MTK spi API */
|
|
memcpy(&ts->spi_ctrl, &spi_ctrdata, sizeof(struct mtk_chip_config));
|
|
ts->client->controller_data = (void *)&ts->spi_ctrl;
|
|
#endif
|
|
|
|
input_info(true, &client->dev,"mode=%d, max_speed_hz=%d\n", ts->client->mode, ts->client->max_speed_hz);
|
|
/*
|
|
//---parse dts---
|
|
ret = nvt_parse_dt(&client->dev);
|
|
if (ret) {
|
|
input_err(true, &client->dev,"parse dt error\n");
|
|
goto err_spi_setup;
|
|
}
|
|
|
|
//---request and config GPIOs---
|
|
ret = nvt_gpio_config(ts);
|
|
if (ret) {
|
|
input_err(true, &client->dev,"gpio config error!\n");
|
|
goto err_gpio_config_failed;
|
|
}
|
|
*/
|
|
|
|
mutex_init(&ts->lock);
|
|
mutex_init(&ts->xbuf_lock);
|
|
|
|
//---eng reset before TP_RESX high
|
|
ts->power_status = POWER_ON_STATUS;
|
|
nvt_eng_reset();
|
|
|
|
#if NVT_TOUCH_SUPPORT_HW_RST
|
|
gpio_set_value(ts->reset_gpio, 1);
|
|
#endif
|
|
|
|
// need 10ms delay after POR(power on reset)
|
|
msleep(10);
|
|
|
|
//---check chip version trim---
|
|
ret = nvt_ts_check_chip_ver_trim();
|
|
if (ret) {
|
|
input_err(true, &client->dev,"chip is not identified\n");
|
|
ret = -EINVAL;
|
|
goto err_chipvertrim_failed;
|
|
}
|
|
|
|
//---allocate input device---
|
|
ts->input_dev = input_allocate_device();
|
|
if (ts->input_dev == NULL) {
|
|
input_err(true, &client->dev,"allocate input device failed\n");
|
|
ret = -ENOMEM;
|
|
goto err_input_dev_alloc_failed;
|
|
}
|
|
|
|
#if PROXIMITY_FUNCTION
|
|
//---allocate input proximity device---
|
|
ts->input_dev_proximity = input_allocate_device();
|
|
if (ts->input_dev_proximity == NULL) {
|
|
input_err(true, &ts->client->dev, "%s: allocate input proximity device failed\n", __func__);
|
|
ret = -ENOMEM;
|
|
goto err_input_dev_prox_alloc_failed;
|
|
}
|
|
#endif
|
|
|
|
ts->platdata->max_touch_num = TOUCH_MAX_FINGER_NUM;
|
|
|
|
#if TOUCH_KEY_NUM > 0
|
|
ts->max_button_num = TOUCH_KEY_NUM;
|
|
#endif
|
|
|
|
//---set input device info.---
|
|
ts->input_dev->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS) ;
|
|
ts->input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
|
|
#if PROXIMITY_FUNCTION
|
|
ts->input_dev->keybit[BIT_WORD(KEY_INT_CANCEL)] = BIT_MASK(KEY_INT_CANCEL);
|
|
#endif
|
|
ts->input_dev->propbit[0] = BIT(INPUT_PROP_DIRECT);
|
|
|
|
#if MT_PROTOCOL_B
|
|
input_mt_init_slots(ts->input_dev, ts->platdata->max_touch_num, 0);
|
|
#endif
|
|
|
|
// input_set_abs_params(ts->input_dev, ABS_MT_PRESSURE, 0, TOUCH_FORCE_NUM, 0, 0); //pressure = TOUCH_FORCE_NUM
|
|
|
|
#if TOUCH_MAX_FINGER_NUM > 1
|
|
input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0); //w_major = 255
|
|
input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MINOR, 0, 255, 0, 0); //w_minor = 255
|
|
|
|
input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X, 0, ts->platdata->abs_x_max, 0, 0);
|
|
input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y, 0, ts->platdata->abs_y_max, 0, 0);
|
|
#if MT_PROTOCOL_B
|
|
// no need to set ABS_MT_TRACKING_ID, input_mt_init_slots() already set it
|
|
#else
|
|
input_set_abs_params(ts->input_dev, ABS_MT_TRACKING_ID, 0, ts->platdata->max_touch_num, 0, 0);
|
|
#endif //MT_PROTOCOL_B
|
|
#endif //TOUCH_MAX_FINGER_NUM > 1
|
|
|
|
#if TOUCH_KEY_NUM > 0
|
|
for (retry = 0; retry < ts->max_button_num; retry++) {
|
|
input_set_capability(ts->input_dev, EV_KEY, touch_key_array[retry]);
|
|
}
|
|
#endif
|
|
|
|
#if WAKEUP_GESTURE
|
|
for (retry = 0; retry < (sizeof(gesture_key_array) / sizeof(gesture_key_array[0])); retry++) {
|
|
input_set_capability(ts->input_dev, EV_KEY, gesture_key_array[retry]);
|
|
}
|
|
#endif
|
|
|
|
sprintf(ts->phys, "input/ts");
|
|
ts->input_dev->name = NVT_TS_NAME;
|
|
ts->input_dev->phys = ts->phys;
|
|
ts->input_dev->id.bustype = BUS_SPI;
|
|
|
|
//---set input proximity device info.---
|
|
ts->input_dev_proximity->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_SW);
|
|
ts->input_dev_proximity->propbit[0] = BIT(INPUT_PROP_DIRECT);
|
|
|
|
|
|
input_set_abs_params(ts->input_dev_proximity, ABS_MT_CUSTOM, 0, 0xFFFFFFFF, 0, 0);
|
|
|
|
#if PROXIMITY_FUNCTION
|
|
ts->input_dev_proximity->name = "sec_touchproximity";
|
|
ts->input_dev_proximity->phys = ts->phys;
|
|
ts->input_dev_proximity->id.bustype = BUS_SPI;
|
|
#endif
|
|
|
|
//---register input device---
|
|
ret = input_register_device(ts->input_dev);
|
|
if (ret) {
|
|
input_err(true, &client->dev,"register input device (%s) failed. ret=%d\n", ts->input_dev->name, ret);
|
|
goto err_input_register_device_failed;
|
|
}
|
|
|
|
#if PROXIMITY_FUNCTION
|
|
//---register input proximity device---
|
|
ret = input_register_device(ts->input_dev_proximity);
|
|
if (ret) {
|
|
input_err(true, &client->dev,"register input proximity device (%s) failed. ret=%d\n", ts->input_dev_proximity->name, ret);
|
|
goto err_input_register_proximity_device_failed;
|
|
}
|
|
#endif
|
|
|
|
//---set int-pin & request irq---
|
|
client->irq = gpio_to_irq(platdata->irq_gpio);
|
|
if (client->irq) {
|
|
input_info(true, &client->dev,"irq_flags=0x%X\n", ts->platdata->irq_flags);
|
|
ts->irq_enabled = true;
|
|
ret = request_threaded_irq(client->irq, NULL, nvt_ts_work_func,
|
|
ts->platdata->irq_flags, NVT_SPI_NAME, ts);
|
|
if (ret != 0) {
|
|
input_err(true, &client->dev,"request irq failed. ret=%d\n", ret);
|
|
goto err_int_request_failed;
|
|
} else {
|
|
nvt_irq_enable(false);
|
|
input_err(true, &client->dev,"request irq %d succeed\n", client->irq);
|
|
}
|
|
}
|
|
|
|
if (ts->platdata->support_ear_detect)
|
|
device_init_wakeup(&ts->input_dev->dev, 1);
|
|
|
|
#if BOOT_UPDATE_FIRMWARE
|
|
nvt_fwu_wq = alloc_workqueue("nvt_fwu_wq", WQ_UNBOUND | WQ_MEM_RECLAIM, 1);
|
|
if (!nvt_fwu_wq) {
|
|
input_err(true, &client->dev,"nvt_fwu_wq create workqueue failed\n");
|
|
ret = -ENOMEM;
|
|
goto err_create_nvt_fwu_wq_failed;
|
|
}
|
|
INIT_DELAYED_WORK(&ts->nvt_fwu_work, Boot_Update_Firmware);
|
|
// please make sure boot update start after display reset(RESX) sequence
|
|
queue_delayed_work(nvt_fwu_wq, &ts->nvt_fwu_work, msecs_to_jiffies(14000));
|
|
#endif
|
|
|
|
input_info(true, &client->dev,"NVT_TOUCH_ESD_PROTECT is %d\n", NVT_TOUCH_ESD_PROTECT);
|
|
#if NVT_TOUCH_ESD_PROTECT
|
|
INIT_DELAYED_WORK(&nvt_esd_check_work, nvt_esd_check_func);
|
|
nvt_esd_check_wq = alloc_workqueue("nvt_esd_check_wq", WQ_MEM_RECLAIM, 1);
|
|
if (!nvt_esd_check_wq) {
|
|
input_err(true, &client->dev,"nvt_esd_check_wq create workqueue failed\n");
|
|
ret = -ENOMEM;
|
|
goto err_create_nvt_esd_check_wq_failed;
|
|
}
|
|
queue_delayed_work(nvt_esd_check_wq, &nvt_esd_check_work,
|
|
msecs_to_jiffies(NVT_TOUCH_ESD_CHECK_PERIOD));
|
|
#endif /* #if NVT_TOUCH_ESD_PROTECT */
|
|
|
|
INIT_DELAYED_WORK(&ts->work_print_info, nvt_print_info_work);
|
|
|
|
//---set device node---
|
|
#if NVT_TOUCH_PROC
|
|
ret = nvt_flash_proc_init();
|
|
if (ret != 0) {
|
|
input_err(true, &client->dev,"nvt flash proc init failed. ret=%d\n", ret);
|
|
goto err_flash_proc_init_failed;
|
|
}
|
|
#endif
|
|
|
|
#if NVT_TOUCH_EXT_PROC
|
|
ret = nvt_extra_proc_init();
|
|
if (ret != 0) {
|
|
input_err(true, &client->dev,"nvt extra proc init failed. ret=%d\n", ret);
|
|
goto err_extra_proc_init_failed;
|
|
}
|
|
#endif
|
|
|
|
#if NVT_TOUCH_MP
|
|
ret = nvt_mp_proc_init();
|
|
if (ret != 0) {
|
|
input_err(true, &client->dev,"nvt mp proc init failed. ret=%d\n", ret);
|
|
goto err_mp_proc_init_failed;
|
|
}
|
|
#endif
|
|
|
|
ret = nvt_ts_sec_fn_init(ts);
|
|
if (ret) {
|
|
input_err(true, &client->dev,"failed to init for factory function\n");
|
|
goto err_init_sec_fn;
|
|
}
|
|
|
|
#if defined(CONFIG_FB)
|
|
if (!ts->platdata->enable_sysinput_enabled) {
|
|
#ifdef _MSM_DRM_NOTIFY_H_
|
|
ts->drm_notif.notifier_call = nvt_drm_notifier_callback;
|
|
ret = msm_drm_register_client(&ts->drm_notif);
|
|
if (ret) {
|
|
input_err(true, &client->dev, "register drm_notifier failed. ret=%d\n", ret);
|
|
goto err_register_notif_failed;
|
|
}
|
|
#else
|
|
ts->fb_notif.notifier_call = nvt_fb_notifier_callback;
|
|
ret = fb_register_client(&ts->fb_notif);
|
|
if (ret) {
|
|
input_err(true, &client->dev, "register fb_notifier failed. ret=%d\n", ret);
|
|
goto err_register_notif_failed;
|
|
}
|
|
#endif
|
|
}
|
|
#elif defined(CONFIG_HAS_EARLYSUSPEND)
|
|
ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
|
|
ts->early_suspend.suspend = nvt_ts_early_suspend;
|
|
ts->early_suspend.resume = nvt_ts_late_resume;
|
|
ret = register_early_suspend(&ts->early_suspend);
|
|
if(ret) {
|
|
input_err(true, &client->dev,"register early suspend failed. ret=%d\n", ret);
|
|
goto err_register_early_suspend_failed;
|
|
}
|
|
#endif
|
|
|
|
ts_remaining_var_init();
|
|
|
|
nvt_irq_enable(true);
|
|
input_err(true, &client->dev, "%s : end\n", __func__);
|
|
|
|
#ifdef CONFIG_BATTERY_SAMSUNG
|
|
if (lpcharge) {
|
|
input_info(true, &client->dev, "%s: enter sleep mode in lpcharge %d\n", __func__, lpcharge);
|
|
nvt_ts_suspend(&ts->client->dev);
|
|
#if BOOT_UPDATE_FIRMWARE
|
|
if (nvt_fwu_wq) {
|
|
cancel_delayed_work_sync(&ts->nvt_fwu_work);
|
|
destroy_workqueue(nvt_fwu_wq);
|
|
nvt_fwu_wq = NULL;
|
|
}
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
return 0;
|
|
|
|
#if defined(CONFIG_HAS_EARLYSUSPEND)
|
|
err_register_early_suspend_failed:
|
|
#elif defined(CONFIG_FB)
|
|
if (!ts->platdata->enable_sysinput_enabled) {
|
|
#ifdef _MSM_DRM_NOTIFY_H_
|
|
if (msm_drm_unregister_client(&ts->drm_notif))
|
|
input_err(true, &client->dev, "Error occurred while unregistering drm_notifier.\n");
|
|
#else
|
|
if (fb_unregister_client(&ts->fb_notif))
|
|
input_err(true, &client->dev, "Error occurred while unregistering fb_notifier.\n");
|
|
#endif
|
|
}
|
|
err_register_notif_failed:
|
|
#endif
|
|
err_init_sec_fn:
|
|
nvt_ts_sec_fn_remove(ts);
|
|
#if NVT_TOUCH_MP
|
|
nvt_mp_proc_deinit();
|
|
err_mp_proc_init_failed:
|
|
#endif
|
|
#if NVT_TOUCH_EXT_PROC
|
|
nvt_extra_proc_deinit();
|
|
err_extra_proc_init_failed:
|
|
#endif
|
|
#if NVT_TOUCH_PROC
|
|
nvt_flash_proc_deinit();
|
|
err_flash_proc_init_failed:
|
|
#endif
|
|
#if NVT_TOUCH_ESD_PROTECT
|
|
if (nvt_esd_check_wq) {
|
|
cancel_delayed_work_sync(&nvt_esd_check_work);
|
|
destroy_workqueue(nvt_esd_check_wq);
|
|
nvt_esd_check_wq = NULL;
|
|
}
|
|
err_create_nvt_esd_check_wq_failed:
|
|
#endif
|
|
#if BOOT_UPDATE_FIRMWARE
|
|
if (nvt_fwu_wq) {
|
|
cancel_delayed_work_sync(&ts->nvt_fwu_work);
|
|
destroy_workqueue(nvt_fwu_wq);
|
|
nvt_fwu_wq = NULL;
|
|
}
|
|
err_create_nvt_fwu_wq_failed:
|
|
#endif
|
|
if (ts->platdata->support_ear_detect)
|
|
device_init_wakeup(&ts->input_dev->dev, 0);
|
|
free_irq(client->irq, ts);
|
|
err_int_request_failed:
|
|
#if PROXIMITY_FUNCTION
|
|
input_unregister_device(ts->input_dev_proximity);
|
|
err_input_register_proximity_device_failed:
|
|
#endif
|
|
input_unregister_device(ts->input_dev);
|
|
ts->input_dev = NULL;
|
|
err_input_register_device_failed:
|
|
#if PROXIMITY_FUNCTION
|
|
if (ts->input_dev_proximity) {
|
|
input_free_device(ts->input_dev_proximity);
|
|
ts->input_dev_proximity = NULL;
|
|
}
|
|
err_input_dev_prox_alloc_failed:
|
|
#endif
|
|
if (ts->input_dev) {
|
|
input_free_device(ts->input_dev);
|
|
ts->input_dev = NULL;
|
|
}
|
|
err_input_dev_alloc_failed:
|
|
err_chipvertrim_failed:
|
|
mutex_destroy(&ts->xbuf_lock);
|
|
mutex_destroy(&ts->lock);
|
|
nvt_gpio_deconfig(ts);
|
|
err_spi_setup:
|
|
err_ckeck_full_duplex:
|
|
spi_set_drvdata(client, NULL);
|
|
regulator_put(ts->regulator_vdd);
|
|
regulator_put(ts->regulator_lcd_reset);
|
|
regulator_put(ts->regulator_lcd_bl_en);
|
|
err_gpio_config_failed:
|
|
err_parse_dt_failed:
|
|
if (ts->platdata) {
|
|
devm_kfree(&client->dev, ts->platdata);
|
|
ts->platdata = NULL;
|
|
}
|
|
err_alloc_platdata_failed:
|
|
if (ts->rbuf) {
|
|
kfree(ts->rbuf);
|
|
ts->rbuf = NULL;
|
|
}
|
|
err_malloc_rbuf:
|
|
if (ts->xbuf) {
|
|
kfree(ts->xbuf);
|
|
ts->xbuf = NULL;
|
|
}
|
|
err_malloc_xbuf:
|
|
if (ts) {
|
|
kfree(ts);
|
|
ts = NULL;
|
|
}
|
|
input_err(true, &client->dev, "%s : end - fail unload driver\n", __func__);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*******************************************************
|
|
Description:
|
|
Novatek touchscreen driver release function.
|
|
|
|
return:
|
|
Executive outcomes. 0---succeed.
|
|
*******************************************************/
|
|
static int32_t nvt_ts_remove(struct spi_device *client)
|
|
{
|
|
input_info(true, &client->dev, "%s : Removing driver...\n", __func__);
|
|
|
|
cancel_delayed_work_sync(&ts->work_print_info);
|
|
|
|
#if defined(CONFIG_FB)
|
|
if (!ts->platdata->enable_sysinput_enabled) {
|
|
#ifdef _MSM_DRM_NOTIFY_H_
|
|
if (msm_drm_unregister_client(&ts->drm_notif))
|
|
input_err(true, &client->dev,"Error occurred while unregistering drm_notifier.\n");
|
|
#else
|
|
if (fb_unregister_client(&ts->fb_notif))
|
|
input_err(true, &client->dev,"Error occurred while unregistering fb_notifier.\n");
|
|
#endif
|
|
}
|
|
#elif defined(CONFIG_HAS_EARLYSUSPEND)
|
|
unregister_early_suspend(&ts->early_suspend);
|
|
#endif
|
|
|
|
nvt_ts_sec_fn_remove(ts);
|
|
|
|
#if NVT_TOUCH_MP
|
|
nvt_mp_proc_deinit();
|
|
#endif
|
|
#if NVT_TOUCH_EXT_PROC
|
|
nvt_extra_proc_deinit();
|
|
#endif
|
|
#if NVT_TOUCH_PROC
|
|
nvt_flash_proc_deinit();
|
|
#endif
|
|
|
|
#if NVT_TOUCH_ESD_PROTECT
|
|
if (nvt_esd_check_wq) {
|
|
cancel_delayed_work_sync(&nvt_esd_check_work);
|
|
nvt_esd_check_enable(false);
|
|
destroy_workqueue(nvt_esd_check_wq);
|
|
nvt_esd_check_wq = NULL;
|
|
}
|
|
#endif
|
|
|
|
#if BOOT_UPDATE_FIRMWARE
|
|
if (nvt_fwu_wq) {
|
|
cancel_delayed_work_sync(&ts->nvt_fwu_work);
|
|
destroy_workqueue(nvt_fwu_wq);
|
|
nvt_fwu_wq = NULL;
|
|
}
|
|
#endif
|
|
|
|
if (ts->platdata->support_ear_detect)
|
|
device_init_wakeup(&ts->input_dev->dev, 0);
|
|
|
|
nvt_irq_enable(false);
|
|
pinctrl_configure(ts, false);
|
|
|
|
free_irq(client->irq, ts);
|
|
|
|
mutex_destroy(&ts->xbuf_lock);
|
|
mutex_destroy(&ts->lock);
|
|
|
|
nvt_gpio_deconfig(ts);
|
|
regulator_put(ts->regulator_vdd);
|
|
regulator_put(ts->regulator_lcd_reset);
|
|
regulator_put(ts->regulator_lcd_bl_en);
|
|
|
|
if (ts->input_dev) {
|
|
input_unregister_device(ts->input_dev);
|
|
ts->input_dev = NULL;
|
|
}
|
|
|
|
if (ts->input_dev_proximity) {
|
|
input_mt_destroy_slots(ts->input_dev_proximity);
|
|
input_unregister_device(ts->input_dev_proximity);
|
|
ts->input_dev_proximity = NULL;
|
|
}
|
|
|
|
spi_set_drvdata(client, NULL);
|
|
|
|
if (ts->platdata) {
|
|
devm_kfree(&client->dev, ts->platdata);
|
|
ts->platdata = NULL;
|
|
}
|
|
if (ts->rbuf) {
|
|
kfree(ts->rbuf);
|
|
ts->rbuf = NULL;
|
|
}
|
|
if (ts->xbuf) {
|
|
kfree(ts->xbuf);
|
|
ts->xbuf = NULL;
|
|
}
|
|
if (ts) {
|
|
kfree(ts);
|
|
ts = NULL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void nvt_ts_shutdown(struct spi_device *client)
|
|
{
|
|
input_info(true, &client->dev, "%s : Shutdown driver...\n", __func__);
|
|
|
|
cancel_delayed_work_sync(&ts->work_print_info);
|
|
|
|
nvt_irq_enable(false);
|
|
pinctrl_configure(ts, false);
|
|
|
|
#if defined(CONFIG_FB)
|
|
if (!ts->platdata->enable_sysinput_enabled) {
|
|
#ifdef _MSM_DRM_NOTIFY_H_
|
|
if (msm_drm_unregister_client(&ts->drm_notif))
|
|
input_err(true, &client->dev,"Error occurred while unregistering drm_notifier.\n");
|
|
#else
|
|
if (fb_unregister_client(&ts->fb_notif))
|
|
input_err(true, &client->dev,"Error occurred while unregistering fb_notifier.\n");
|
|
#endif
|
|
}
|
|
#elif defined(CONFIG_HAS_EARLYSUSPEND)
|
|
unregister_early_suspend(&ts->early_suspend);
|
|
#endif
|
|
|
|
nvt_ts_sec_fn_remove(ts);
|
|
|
|
#if NVT_TOUCH_MP
|
|
nvt_mp_proc_deinit();
|
|
#endif
|
|
#if NVT_TOUCH_EXT_PROC
|
|
nvt_extra_proc_deinit();
|
|
#endif
|
|
#if NVT_TOUCH_PROC
|
|
nvt_flash_proc_deinit();
|
|
#endif
|
|
|
|
#if NVT_TOUCH_ESD_PROTECT
|
|
if (nvt_esd_check_wq) {
|
|
cancel_delayed_work_sync(&nvt_esd_check_work);
|
|
nvt_esd_check_enable(false);
|
|
destroy_workqueue(nvt_esd_check_wq);
|
|
nvt_esd_check_wq = NULL;
|
|
}
|
|
#endif /* #if NVT_TOUCH_ESD_PROTECT */
|
|
|
|
#if BOOT_UPDATE_FIRMWARE
|
|
if (nvt_fwu_wq) {
|
|
cancel_delayed_work_sync(&ts->nvt_fwu_work);
|
|
destroy_workqueue(nvt_fwu_wq);
|
|
nvt_fwu_wq = NULL;
|
|
}
|
|
#endif
|
|
|
|
if (ts->platdata->support_ear_detect)
|
|
device_init_wakeup(&ts->input_dev->dev, 0);
|
|
}
|
|
|
|
|
|
int nvt_ts_lcd_reset_ctrl(bool on)
|
|
{
|
|
int retval;
|
|
static bool enabled;
|
|
|
|
if (enabled == on)
|
|
return 0;
|
|
|
|
if (on) {
|
|
retval = regulator_enable(ts->regulator_lcd_reset);
|
|
if (retval) {
|
|
input_err(true, &ts->client->dev, "%s: Failed to enable regulator_lcd_reset: %d\n", __func__, retval);
|
|
return retval;
|
|
}
|
|
|
|
} else {
|
|
regulator_disable(ts->regulator_lcd_reset);
|
|
}
|
|
|
|
enabled = on;
|
|
|
|
input_info(true, &ts->client->dev, "%s %d done\n", __func__, on);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int nvt_ts_lcd_power_ctrl(bool on)
|
|
{
|
|
int retval;
|
|
static bool enabled;
|
|
|
|
if (enabled == on)
|
|
return 0;
|
|
|
|
if (on) {
|
|
retval = regulator_enable(ts->regulator_vdd);
|
|
if (retval) {
|
|
input_err(true, &ts->client->dev, "%s: Failed to enable regulator_vdd: %d\n", __func__, retval);
|
|
return retval;
|
|
}
|
|
|
|
retval = regulator_enable(ts->regulator_lcd_bl_en);
|
|
if (retval) {
|
|
input_err(true, &ts->client->dev, "%s: Failed to enable regulator_lcd_bl_en: %d\n", __func__, retval);
|
|
return retval;
|
|
}
|
|
} else {
|
|
regulator_disable(ts->regulator_lcd_bl_en);
|
|
regulator_disable(ts->regulator_vdd);
|
|
}
|
|
|
|
enabled = on;
|
|
|
|
input_info(true, &ts->client->dev, "%s %d done\n", __func__, on);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*******************************************************
|
|
Description:
|
|
Novatek touchscreen driver suspend function.
|
|
|
|
return:
|
|
Executive outcomes. 0---succeed.
|
|
*******************************************************/
|
|
int32_t nvt_ts_suspend(struct device *dev)
|
|
{
|
|
struct nvt_ts_data *ts = dev_get_drvdata(dev);
|
|
uint8_t buf[4] = {0};
|
|
#if MT_PROTOCOL_B
|
|
uint32_t i = 0;
|
|
#endif
|
|
|
|
if (ts->power_status == POWER_OFF_STATUS || ts->power_status == LP_MODE_STATUS) {
|
|
input_info(true, &ts->client->dev, "Touch is already suspend(%d)\n", ts->power_status);
|
|
return 0;
|
|
}
|
|
|
|
#if NVT_TOUCH_ESD_PROTECT
|
|
input_info(true, &ts->client->dev,"cancel delayed work sync\n");
|
|
cancel_delayed_work_sync(&nvt_esd_check_work);
|
|
nvt_esd_check_enable(false);
|
|
#endif /* #if NVT_TOUCH_ESD_PROTECT */
|
|
|
|
input_info(true, &ts->client->dev, "%s : start %d, %d\n", __func__, ts->ear_detect_mode, ts->aot_enable);
|
|
|
|
if (ts->aot_enable && (ts->prox_power_off || ts->ear_detect_mode))
|
|
ts->lowpower_mode = 0; // for ed
|
|
|
|
input_info(true, &ts->client->dev, "%s : start %d, %d\n", __func__, ts->ear_detect_mode, ts->aot_enable);
|
|
|
|
if (ts->lowpower_mode || ts->lcdoff_test) {
|
|
nvt_irq_enable(false);
|
|
nvt_ts_lcd_power_ctrl(true);
|
|
nvt_ts_lcd_reset_ctrl(true);
|
|
mutex_lock(&ts->lock);
|
|
ts->power_status = LP_MODE_STATUS;
|
|
|
|
/* LPWG enter */
|
|
buf[0] = EVENT_MAP_HOST_CMD;
|
|
buf[1] = LPWG_ENTER;
|
|
CTP_SPI_WRITE(ts->client, buf, 2);
|
|
mutex_unlock(&ts->lock);
|
|
|
|
nvt_irq_enable(true);
|
|
enable_irq_wake(ts->client->irq);
|
|
|
|
input_info(true, &ts->client->dev, "%s: aot mode\n", __func__);
|
|
|
|
} else if (ts->ear_detect_mode) {
|
|
|
|
nvt_ts_lcd_power_ctrl(true);
|
|
nvt_ts_lcd_reset_ctrl(true);
|
|
mutex_lock(&ts->lock);
|
|
ts->power_status = LP_MODE_STATUS;
|
|
mutex_unlock(&ts->lock);
|
|
|
|
enable_irq_wake(ts->client->irq);
|
|
|
|
input_info(true, &ts->client->dev, "%s: ed mode\n", __func__);
|
|
|
|
} else {
|
|
nvt_irq_enable(false);
|
|
mutex_lock(&ts->lock);
|
|
|
|
/* write command to enter "deep sleep mode" */
|
|
buf[0] = EVENT_MAP_HOST_CMD;
|
|
buf[1] = DEEP_SLEEP_ENTER;
|
|
CTP_SPI_WRITE(ts->client, buf, 2);
|
|
|
|
pinctrl_configure(ts, false);
|
|
ts->power_status = POWER_OFF_STATUS;
|
|
mutex_unlock(&ts->lock);
|
|
input_info(true, &ts->client->dev, "%s: power off %d\n", __func__, ts->power_status);
|
|
}
|
|
|
|
if (ts->prox_power_off) {
|
|
input_info(true, &ts->client->dev, "%s : cancel touch\n", __func__);
|
|
input_report_key(ts->input_dev, KEY_INT_CANCEL, 1);
|
|
input_sync(ts->input_dev);
|
|
input_report_key(ts->input_dev, KEY_INT_CANCEL, 0);
|
|
input_sync(ts->input_dev);
|
|
}
|
|
|
|
/* release all touches */
|
|
#if MT_PROTOCOL_B
|
|
for (i = 0; i < ts->platdata->max_touch_num; i++) {
|
|
ts->coords[i].press = false;
|
|
input_mt_slot(ts->input_dev, i);
|
|
input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0);
|
|
input_report_abs(ts->input_dev, ABS_MT_TOUCH_MINOR, 0);
|
|
// input_report_abs(ts->input_dev, ABS_MT_PRESSURE, 0);
|
|
input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, 0);
|
|
}
|
|
#endif
|
|
input_report_key(ts->input_dev, BTN_TOUCH, 0);
|
|
#if !MT_PROTOCOL_B
|
|
input_mt_sync(ts->input_dev);
|
|
#endif
|
|
input_sync(ts->input_dev);
|
|
|
|
nvt_ts_print_coord(ts);
|
|
|
|
msleep(50);
|
|
|
|
cancel_delayed_work(&ts->work_print_info);
|
|
nvt_print_info();
|
|
|
|
input_info(true, &ts->client->dev, "%s : end\n", __func__);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void nvt_ts_early_resume(struct device *dev)
|
|
{
|
|
struct nvt_ts_data *ts = dev_get_drvdata(dev);
|
|
|
|
input_info(true, &ts->client->dev, "%s : start(%d)\n", __func__, ts->power_status);
|
|
|
|
if (++ts->early_resume_cnt > 0xfff0)
|
|
ts->early_resume_cnt = 0;
|
|
|
|
if (ts->power_status == LP_MODE_STATUS) {
|
|
if (device_may_wakeup(&ts->client->dev))
|
|
disable_irq_wake(ts->client->irq);
|
|
|
|
nvt_irq_enable(false);
|
|
|
|
mutex_lock(&ts->lock);
|
|
ts->power_status = LP_MODE_EXIT;
|
|
nvt_ts_lcd_reset_ctrl(false);
|
|
mutex_unlock(&ts->lock);
|
|
}
|
|
}
|
|
|
|
/*******************************************************
|
|
Description:
|
|
Novatek touchscreen driver resume function.
|
|
|
|
return:
|
|
Executive outcomes. 0---succeed.
|
|
*******************************************************/
|
|
int32_t nvt_ts_resume(struct device *dev)
|
|
{
|
|
struct nvt_ts_data *ts = dev_get_drvdata(dev);
|
|
|
|
if (ts->power_status == POWER_ON_STATUS) {
|
|
input_info(true, &ts->client->dev, "%s: Touch is already resume\n", __func__);
|
|
return 0;
|
|
}
|
|
|
|
if (++ts->resume_cnt > 0xfff0)
|
|
ts->resume_cnt = 0;
|
|
|
|
mutex_lock(&ts->lock);
|
|
|
|
if (ts->power_status == LP_MODE_EXIT) {
|
|
|
|
nvt_ts_lcd_power_ctrl(false);
|
|
} else {
|
|
pinctrl_configure(ts, true);
|
|
}
|
|
|
|
ts->lowpower_mode = ts->aot_enable;
|
|
|
|
ts->prox_power_off = 0;
|
|
ts->power_status = POWER_ON_STATUS;
|
|
|
|
// please make sure display reset(RESX) sequence and mipi dsi cmds sent before this
|
|
#if NVT_TOUCH_SUPPORT_HW_RST
|
|
gpio_set_value(ts->reset_gpio, 1);
|
|
#endif
|
|
|
|
if (nvt_update_firmware(ts->platdata->firmware_name)) {
|
|
input_err(true, &ts->client->dev,"download firmware failed, ignore check fw state\n");
|
|
} else {
|
|
nvt_check_fw_reset_state(RESET_STATE_REK);
|
|
}
|
|
|
|
nvt_ts_mode_restore(ts);
|
|
|
|
if (ts->ear_detect_mode == 0 && ts->ed_reset_flag) {
|
|
input_info(true, &ts->client->dev, "%s : set ed on & off\n", __func__);
|
|
if (set_ear_detect(ts, 1)) {
|
|
input_err(true, &ts->client->dev, "%s : Fail to set set_ear_detect on\n", __func__);
|
|
}
|
|
if (set_ear_detect(ts, 0)) {
|
|
input_err(true, &ts->client->dev, "%s : Fail to set set_ear_detect off\n", __func__);
|
|
}
|
|
}
|
|
ts->ed_reset_flag = false;
|
|
|
|
if (ts->ear_detect_mode) {
|
|
if (set_ear_detect(ts, ts->ear_detect_mode)) {
|
|
input_err(true, &ts->client->dev, "%s : Fail to set set_ear_detect\n", __func__);
|
|
}
|
|
}
|
|
|
|
#if NVT_TOUCH_ESD_PROTECT
|
|
nvt_esd_check_enable(false);
|
|
queue_delayed_work(nvt_esd_check_wq, &nvt_esd_check_work,
|
|
msecs_to_jiffies(NVT_TOUCH_ESD_CHECK_PERIOD));
|
|
#endif /* #if NVT_TOUCH_ESD_PROTECT */
|
|
|
|
mutex_unlock(&ts->lock);
|
|
|
|
nvt_irq_enable(true);
|
|
|
|
cancel_delayed_work(&ts->work_print_info);
|
|
ts->print_info_cnt_open = 0;
|
|
ts->print_info_cnt_release = 0;
|
|
schedule_work(&ts->work_print_info.work);
|
|
|
|
input_info(true, &ts->client->dev, "%s : end\n", __func__);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
#if defined(CONFIG_FB)
|
|
#ifdef _MSM_DRM_NOTIFY_H_
|
|
static int nvt_drm_notifier_callback(struct notifier_block *self, unsigned long event, void *data)
|
|
{
|
|
struct msm_drm_notifier *evdata = data;
|
|
int *blank;
|
|
struct nvt_ts_data *ts =
|
|
container_of(self, struct nvt_ts_data, drm_notif);
|
|
|
|
if (!evdata || (evdata->id != 0))
|
|
return 0;
|
|
|
|
if (evdata->data && ts) {
|
|
blank = evdata->data;
|
|
if (event == MSM_DRM_EARLY_EVENT_BLANK) {
|
|
if (*blank == MSM_DRM_BLANK_POWERDOWN) {
|
|
input_info(true, &ts->client->dev, "event=%lu, *blank=%d\n", event, *blank);
|
|
nvt_ts_suspend(&ts->client->dev);
|
|
}
|
|
} else if (event == MSM_DRM_EVENT_BLANK) {
|
|
if (*blank == MSM_DRM_BLANK_UNBLANK) {
|
|
input_info(true, &ts->client->dev, "event=%lu, *blank=%d\n", event, *blank);
|
|
nvt_ts_resume(&ts->client->dev);
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#else
|
|
static int nvt_fb_notifier_callback(struct notifier_block *self, unsigned long event, void *data)
|
|
{
|
|
struct fb_event *evdata = data;
|
|
int *blank;
|
|
struct nvt_ts_data *ts =
|
|
container_of(self, struct nvt_ts_data, fb_notif);
|
|
|
|
if (evdata && evdata->data && event == FB_EARLY_EVENT_BLANK) {
|
|
blank = evdata->data;
|
|
if (*blank == FB_BLANK_POWERDOWN) {
|
|
input_info(true, &ts->client->dev, "event=%lu, *blank=%d\n", event, *blank);
|
|
nvt_ts_suspend(&ts->client->dev);
|
|
} else if (*blank == FB_BLANK_UNBLANK) {
|
|
input_info(true, &ts->client->dev, "event=%lu, *blank=%d\n", event, *blank);
|
|
nvt_ts_early_resume(&ts->client->dev);
|
|
}
|
|
} else if (evdata && evdata->data && event == FB_EVENT_BLANK) {
|
|
blank = evdata->data;
|
|
if (*blank == FB_BLANK_UNBLANK) {
|
|
input_info(true, &ts->client->dev, "event=%lu, *blank=%d\n", event, *blank);
|
|
nvt_ts_resume(&ts->client->dev);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
#elif defined(CONFIG_HAS_EARLYSUSPEND)
|
|
/*******************************************************
|
|
Description:
|
|
Novatek touchscreen driver early suspend function.
|
|
|
|
return:
|
|
n.a.
|
|
*******************************************************/
|
|
static void nvt_ts_early_suspend(struct early_suspend *h)
|
|
{
|
|
nvt_ts_suspend(ts->client, PMSG_SUSPEND);
|
|
}
|
|
|
|
/*******************************************************
|
|
Description:
|
|
Novatek touchscreen driver late resume function.
|
|
|
|
return:
|
|
n.a.
|
|
*******************************************************/
|
|
static void nvt_ts_late_resume(struct early_suspend *h)
|
|
{
|
|
nvt_ts_resume(ts->client);
|
|
}
|
|
#endif
|
|
|
|
static const struct spi_device_id nvt_ts_id[] = {
|
|
{ NVT_SPI_NAME, 0 },
|
|
{ }
|
|
};
|
|
|
|
#ifdef CONFIG_OF
|
|
static struct of_device_id nvt_match_table[] = {
|
|
{ .compatible = "novatek,NVT-ts-spi",},
|
|
{ },
|
|
};
|
|
#endif
|
|
|
|
static struct spi_driver nvt_spi_driver = {
|
|
.probe = nvt_ts_probe,
|
|
.remove = nvt_ts_remove,
|
|
.shutdown = nvt_ts_shutdown,
|
|
.id_table = nvt_ts_id,
|
|
.driver = {
|
|
.name = NVT_SPI_NAME,
|
|
.owner = THIS_MODULE,
|
|
#ifdef CONFIG_OF
|
|
.of_match_table = nvt_match_table,
|
|
#endif
|
|
},
|
|
};
|
|
|
|
/*******************************************************
|
|
Description:
|
|
Driver Install function.
|
|
|
|
return:
|
|
Executive Outcomes. 0---succeed. not 0---failed.
|
|
********************************************************/
|
|
static int32_t __init nvt_driver_init(void)
|
|
{
|
|
int32_t ret = 0;
|
|
|
|
pr_info("[sec_input] %s : start\n", __func__);
|
|
|
|
//---add spi driver---
|
|
ret = spi_register_driver(&nvt_spi_driver);
|
|
if (ret) {
|
|
pr_err("[sec_input] failed to add spi driver");
|
|
goto err_driver;
|
|
}
|
|
|
|
pr_info("[sec_input] %s : finished\n", __func__);
|
|
|
|
err_driver:
|
|
return ret;
|
|
}
|
|
|
|
/*******************************************************
|
|
Description:
|
|
Driver uninstall function.
|
|
|
|
return:
|
|
n.a.
|
|
********************************************************/
|
|
static void __exit nvt_driver_exit(void)
|
|
{
|
|
spi_unregister_driver(&nvt_spi_driver);
|
|
}
|
|
|
|
//late_initcall(nvt_driver_init);
|
|
module_init(nvt_driver_init);
|
|
module_exit(nvt_driver_exit);
|
|
|
|
MODULE_DESCRIPTION("Novatek Touchscreen Driver");
|
|
MODULE_LICENSE("GPL");
|