mirror of
https://github.com/apache/nuttx.git
synced 2026-09-14 06:30:31 +00:00
CHAMPION-95 add nt38350 selftest Change-Id: I15cbaf04f9dc9131b4e661cd29e56d5bece6b4db Signed-off-by: futerigele <futerigele@xiaomi.com>
4512 lines
112 KiB
C
4512 lines
112 KiB
C
/***************************************************************************
|
|
* drivers/input/nt38350.c
|
|
*
|
|
* Copyright (C) 2021 Xiaomi Corporation
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
***************************************************************************/
|
|
|
|
/***************************************************************************
|
|
* Included Files
|
|
***************************************************************************/
|
|
|
|
#include <nuttx/config.h>
|
|
|
|
#include <stdint.h>
|
|
#include <sys/types.h>
|
|
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
#include <fcntl.h>
|
|
#include <poll.h>
|
|
#include <errno.h>
|
|
#include <assert.h>
|
|
#include <debug.h>
|
|
|
|
#include <nuttx/irq.h>
|
|
#include <nuttx/kmalloc.h>
|
|
#include <nuttx/arch.h>
|
|
#include <nuttx/fs/fs.h>
|
|
#include <nuttx/i2c/i2c_master.h>
|
|
#include <nuttx/semaphore.h>
|
|
#include <nuttx/wqueue.h>
|
|
#include <nuttx/random.h>
|
|
#include <nuttx/ioexpander/ioexpander.h>
|
|
|
|
#include <nuttx/signal.h>
|
|
#include <nuttx/input/touchscreen.h>
|
|
#include <nuttx/input/nt38350.h>
|
|
|
|
/***************************************************************************
|
|
* Pre-processor Definitions
|
|
***************************************************************************/
|
|
|
|
/* Configuration ***********************************************************/
|
|
|
|
/* Maximum number of threads than can be waiting for POLL events */
|
|
|
|
#ifndef CONFIG_NT38350_NPOLLWAITERS
|
|
# define CONFIG_NT38350_NPOLLWAITERS 2
|
|
#endif
|
|
|
|
#define NVT_DEV_FORMAT "/dev/input%d"
|
|
#define NVT_DEV_NAMELEN 16
|
|
#define NVT_IIC_RETRY_NUM 2
|
|
#define NVT_POINT_DATA_LEN 17
|
|
#define NVT_I2C_BLDR_ADDRESS 0x01
|
|
#define NVT_I2C_FW_ADDRESS 0x01
|
|
#define NVT_I2C_HW_ADDRESS 0x62
|
|
#define NVT_TOUCH_DEFAULT_MAX_WIDTH 430
|
|
#define NVT_TOUCH_DEFAULT_MAX_HEIGHT 372
|
|
#define NVT_TOUCH_MAX_FINGER_NUM 2
|
|
#define NVT_TOUCH_KEY_NUM 0
|
|
#define NVT_TOUCH_FORCE_NUM 1000
|
|
#define NVT_CHIP_VER_TRIM_ADDR 0x3f004
|
|
#define NVT_CHIP_VER_TRIM_OLD_ADDR 0x1f64e
|
|
#define NVT_ID_BYTE_MAX 6
|
|
|
|
#define NVT_SIZE_4KB 4096
|
|
#define NVT_FLASH_SECTOR_SIZE NVT_SIZE_4KB
|
|
#define NVT_SIZE_64KB 65536
|
|
#define NVT_BLOCK_64KB_NUM 4
|
|
#define NVT_FLASH_END_FLAG_LEN 3
|
|
|
|
#define NVT_BUS_TRANSFER_LENGTH 64
|
|
|
|
#define NVT_DELAY_100US 100
|
|
#define NVT_DELAY_1MS 1000
|
|
#define NVT_DELAY_5MS 5000
|
|
#define NVT_DELAY_10MS 10000
|
|
#define NVT_DELAY_15MS 15000
|
|
#define NVT_DELAY_20MS 20000
|
|
#define NVT_DELAY_35MS 35000
|
|
#define NVT_DELAY_80MS 80000
|
|
#define NVT_DELAY_83MS 83000
|
|
#define NVT_DELAY_100MS 100000
|
|
|
|
#define NVT_NORMAL_MODE 0x00
|
|
#define NVT_TEST_MODE_2 0x22
|
|
#define NVT_HANDSHAKING_HOST_READY 0xbb
|
|
#define NVT_XDATA_SECTOR_SIZE 256
|
|
|
|
#ifdef CONFIG_NVT_OFFLINE_LOG
|
|
#define NVT_POINT_DATA_EXT_LEN 4 /* Event buffer offset 0x11~0x14 */
|
|
#define NVT_S2D_DATA_LEN 59 /* Event buffer offset 0x15~0x4F
|
|
* (Byte 1~59 of 2Ddata)
|
|
*/
|
|
#define NVT_FW_CMD_HANDLE_LEN 18 /* Event buffer offset 0x50~0x61 */
|
|
#define NVT_S2D_DATA_EXT_LEN 13 /* Event buffer offset 0x62~0x6E
|
|
* (Byte 60~72 of 2Ddata)
|
|
*/
|
|
#define NVT_FW_FRAME_CNT_LEN 2 /* Event buffer offset 0x6F~0x70 */
|
|
#define NVT_POINT_DATA_EXBUF_LEN 113
|
|
#endif
|
|
|
|
#ifndef max
|
|
# define max(a,b) (((a) > (b)) ? (a) : (b))
|
|
#endif
|
|
#ifndef min
|
|
# define min(a,b) (((a) < (b)) ? (a) : (b))
|
|
#endif
|
|
|
|
typedef enum
|
|
{
|
|
RESET_STATE_INIT = 0xa0, /* IC reset */
|
|
RESET_STATE_REK, /* ReK baseline */
|
|
RESET_STATE_REK_FINISH, /* baseline is ready */
|
|
RESET_STATE_NORMAL_RUN, /* normal run */
|
|
RESET_STATE_MAX = 0xaf
|
|
} rst_complete_state_t;
|
|
|
|
typedef enum
|
|
{
|
|
EVENT_MAP_HOST_CMD = 0x50,
|
|
EVENT_MAP_HANDSHAKING_OR_SUB_CMD_BYTE = 0x51,
|
|
EVENT_MAP_RESET_COMPLETE = 0x60,
|
|
EVENT_MAP_FWINFO = 0x78,
|
|
EVENT_MAP_PROJECTID = 0x9a,
|
|
} i2c_event_map_t;
|
|
|
|
enum nt38350_contact_e
|
|
{
|
|
CONTACT_NONE = 0,
|
|
CONTACT_DOWN,
|
|
CONTACT_MOVE,
|
|
CONTACT_UP
|
|
};
|
|
|
|
enum nvt_finger_contact_e
|
|
{
|
|
FINGER_DOWN = 0x01,
|
|
FINGER_MOVE = 0x02,
|
|
FINGER_UP = 0xff
|
|
};
|
|
|
|
struct nvt_ts_mem_map_s
|
|
{
|
|
uint32_t event_buf_addr;
|
|
uint32_t raw_pipe0_addr;
|
|
uint32_t raw_pipe1_addr;
|
|
uint32_t baseline_addr;
|
|
uint32_t baseline_btn_addr;
|
|
uint32_t diff_pipe0_addr;
|
|
uint32_t diff_pipe1_addr;
|
|
uint32_t raw_btn_pipe0_addr;
|
|
uint32_t raw_btn_pipe1_addr;
|
|
uint32_t diff_btn_pipe0_addr;
|
|
uint32_t diff_btn_pipe1_addr;
|
|
uint32_t read_flash_checksum_addr;
|
|
uint32_t rw_flash_data_addr;
|
|
};
|
|
|
|
struct nvt_ts_hw_info_s
|
|
{
|
|
uint8_t carrier_system;
|
|
uint8_t hw_crc;
|
|
};
|
|
|
|
struct nvt_ts_trim_id_table_s
|
|
{
|
|
uint8_t id[NVT_ID_BYTE_MAX];
|
|
uint8_t mask[NVT_ID_BYTE_MAX];
|
|
const struct nvt_ts_mem_map_s *mmap;
|
|
const struct nvt_ts_hw_info_s *hwinfo;
|
|
};
|
|
|
|
struct ts_nt38350_sample_s
|
|
{
|
|
uint8_t id;
|
|
uint8_t contact;
|
|
bool valid;
|
|
uint16_t x;
|
|
uint16_t y;
|
|
uint16_t pressure;
|
|
uint16_t width;
|
|
};
|
|
|
|
struct nvt_diff_s
|
|
{
|
|
uint8_t x_num;
|
|
uint8_t y_num;
|
|
int16_t data[30]
|
|
};
|
|
|
|
/* This structure describes the state of one nt38350 driver instance */
|
|
|
|
struct nt38350_dev_s
|
|
{
|
|
#ifdef CONFIG_NT38350_REFCNT
|
|
uint8_t crefs; /* Number of times the device has been opened */
|
|
#endif
|
|
uint8_t nwaiters; /* Number of threads waiting for nt38350 data */
|
|
uint8_t id; /* Current touch point ID */
|
|
volatile bool valid; /* An True: New, valid touch data in touch_info */
|
|
sem_t devsem; /* Manages exclusive access to this structure */
|
|
sem_t waitsem; /* Used to wait for the availability of data */
|
|
uint32_t frequency;
|
|
|
|
FAR struct nt38350_config_s *config; /* Board configuration data */
|
|
FAR struct i2c_master_s *i2c; /* Saved I2C driver instance */
|
|
struct work_s work; /* Supports the interrupt handling "bottom half" */
|
|
struct ts_nt38350_sample_s sample; /* Last sampled touch point data */
|
|
|
|
/* Touch info */
|
|
|
|
char *fw_path;
|
|
uint8_t fw_ver;
|
|
uint8_t x_num;
|
|
uint8_t y_num;
|
|
uint8_t max_touch_num;
|
|
uint8_t max_button_num;
|
|
uint8_t carrier_system;
|
|
uint16_t abs_x_max;
|
|
uint16_t abs_y_max;
|
|
uint16_t nvt_pid;
|
|
uint32_t fw_size;
|
|
const struct nvt_ts_mem_map_s *mmap;
|
|
size_t fw_need_write_size;
|
|
#ifdef CONFIG_NVT_OFFLINE_LOG
|
|
struct work_s nvt_log_wq;
|
|
uint8_t point_xdata_temp[NVT_POINT_DATA_EXBUF_LEN];
|
|
#endif
|
|
|
|
/* The following is a list if poll structures of threads waiting for
|
|
* driver events. The 'struct pollfd' reference for each open is also
|
|
* retained in the f_priv field of the 'struct file'.
|
|
*/
|
|
|
|
struct pollfd *fds[CONFIG_NT38350_NPOLLWAITERS];
|
|
};
|
|
|
|
/***************************************************************************
|
|
* Private Function Prototypes
|
|
***************************************************************************/
|
|
|
|
static void nt38350_notify(FAR struct nt38350_dev_s *priv);
|
|
static int nt38350_sample(FAR struct nt38350_dev_s *priv,
|
|
FAR struct ts_nt38350_sample_s *sample);
|
|
|
|
static int nt38350_i2c_read(FAR struct i2c_master_s *dev,
|
|
FAR const struct i2c_config_s *config,
|
|
uint16_t address,
|
|
uint8_t *rdbuffer, int length);
|
|
static int nt38350_i2c_write(FAR struct i2c_master_s *dev,
|
|
FAR const struct i2c_config_s *config,
|
|
uint16_t address,
|
|
uint8_t *buffer, int length);
|
|
|
|
static int nt38350_read_reg(FAR struct nt38350_dev_s *priv,
|
|
uint16_t address,
|
|
FAR uint8_t *buffer,
|
|
uint16_t length);
|
|
static int nt38350_write_reg(FAR struct nt38350_dev_s *priv,
|
|
uint16_t address,
|
|
FAR uint8_t *buffer,
|
|
uint16_t length);
|
|
|
|
/* Character driver methods */
|
|
|
|
static int nt38350_open(FAR struct file *filep);
|
|
static int nt38350_close(FAR struct file *filep);
|
|
static ssize_t nt38350_read(FAR struct file *filep, FAR char *buffer,
|
|
size_t len);
|
|
static int nt38350_ioctl(FAR struct file *filep,
|
|
int cmd, unsigned long arg);
|
|
static int nt38350_poll(FAR struct file *filep,
|
|
struct pollfd *fds,
|
|
bool setup);
|
|
|
|
/***************************************************************************
|
|
* Private Data
|
|
***************************************************************************/
|
|
|
|
/* This the vtable that supports the character driver interface */
|
|
|
|
static const struct file_operations g_nt38350_fops =
|
|
{
|
|
nt38350_open,
|
|
nt38350_close,
|
|
nt38350_read,
|
|
0,
|
|
0,
|
|
nt38350_ioctl,
|
|
nt38350_poll
|
|
};
|
|
|
|
static const struct nvt_ts_mem_map_s g_nt38350_memory_map =
|
|
{
|
|
.event_buf_addr = 0x20000,
|
|
.raw_pipe0_addr = 0x20160,
|
|
.raw_pipe1_addr = 0x20660,
|
|
.baseline_addr = 0x20268,
|
|
.baseline_btn_addr = 0x20768,
|
|
.diff_pipe0_addr = 0x201fc,
|
|
.diff_pipe1_addr = 0x206fc,
|
|
.raw_btn_pipe0_addr = 0x20260,
|
|
.raw_btn_pipe1_addr = 0x20260,
|
|
.diff_btn_pipe0_addr = 0x20760,
|
|
.diff_btn_pipe1_addr = 0x20760,
|
|
.read_flash_checksum_addr = 0x21000,
|
|
.rw_flash_data_addr = 0x21002,
|
|
};
|
|
|
|
static const struct nvt_ts_hw_info_s g_nt38350_hw_info =
|
|
{
|
|
.carrier_system = 0,
|
|
.hw_crc = 1,
|
|
};
|
|
|
|
static const struct nvt_ts_trim_id_table_s g_trim_id_table[] =
|
|
{
|
|
{
|
|
.id =
|
|
{
|
|
0xff, 0xff, 0xff, 0x50, 0x83, 0x03
|
|
},
|
|
.mask =
|
|
{
|
|
0, 0, 0, 1, 1, 1
|
|
},
|
|
.mmap = &g_nt38350_memory_map,
|
|
.hwinfo = &g_nt38350_hw_info
|
|
},
|
|
};
|
|
|
|
#ifdef CONFIG_NVT_TOUCH_MP
|
|
static const uint8_t AIN_X[10] =
|
|
{
|
|
0, 1, 2, 3, 4, 5
|
|
};
|
|
|
|
static uint8_t AIN_Y[10] =
|
|
{
|
|
0, 1, 2, 3, 4, 5
|
|
};
|
|
|
|
static const int32_t ps_config_lmt_short_rawdata_p[10 * 10] =
|
|
{
|
|
14008, 14008, 14008, 14008, 14008, 14008,
|
|
14008, 14008, 14008, 14008, 14008, 14008,
|
|
14008, 14008, 14008, 14008, 14008, 14008,
|
|
14008, 14008, 14008, 14008, 14008, 14008,
|
|
14008, 14008, 14008, 14008, 14008, 14008,
|
|
14008, 14008, 14008, 14008, 14008, 14008
|
|
};
|
|
|
|
static const int32_t ps_config_lmt_short_rawdata_n[10 * 10] =
|
|
{
|
|
0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0
|
|
};
|
|
|
|
static const int32_t ps_config_lmt_open_rawdata_p[10 * 10] =
|
|
{
|
|
5120, 5120, 5120, 5120, 5120, 5120,
|
|
5120, 5120, 5120, 5120, 5120, 5120,
|
|
5120, 5120, 5120, 5120, 5120, 5120,
|
|
5120, 5120, 5120, 5120, 5120, 5120,
|
|
5120, 5120, 5120, 5120, 5120, 5120,
|
|
5120, 5120, 5120, 5120, 5120, 5120
|
|
};
|
|
|
|
static const int32_t ps_config_lmt_open_rawdata_n[10 * 10] =
|
|
{
|
|
-511, -511, -511, -511, -511, -511,
|
|
-511, -511, -511, -511, -511, -511,
|
|
-511, -511, -511, -511, -511, -511,
|
|
-511, -511, -511, -511, -511, -511,
|
|
-511, -511, -511, -511, -511, -511,
|
|
-511, -511, -511, -511, -511, -511
|
|
};
|
|
|
|
static const int32_t ps_config_lmt_fw_rawdata_p[10 * 10] =
|
|
{
|
|
10000, 10000, 10000, 10000, 10000, 10000,
|
|
10000, 10000, 10000, 10000, 10000, 10000,
|
|
10000, 10000, 10000, 10000, 10000, 10000,
|
|
10000, 10000, 10000, 10000, 10000, 10000,
|
|
10000, 10000, 10000, 10000, 10000, 10000,
|
|
10000, 10000, 10000, 10000, 10000, 10000
|
|
};
|
|
|
|
static const int32_t ps_config_lmt_fw_rawdata_n[10 * 10] =
|
|
{
|
|
0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0
|
|
};
|
|
|
|
static const int32_t ps_config_lmt_fw_cc_p[10 * 10] =
|
|
{
|
|
512, 512, 512, 512, 512, 512,
|
|
512, 512, 512, 512, 512, 512,
|
|
512, 512, 512, 512, 512, 512,
|
|
512, 512, 512, 512, 512, 512,
|
|
512, 512, 512, 512, 512, 512,
|
|
512, 512, 512, 512, 512, 512
|
|
};
|
|
|
|
static const int32_t ps_config_lmt_fw_cc_n[10 * 10] =
|
|
{
|
|
0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0
|
|
};
|
|
|
|
static const int32_t ps_config_lmt_fw_diff_p[10 * 10] =
|
|
{
|
|
300, 300, 300, 300, 300, 300,
|
|
300, 300, 300, 300, 300, 300,
|
|
300, 300, 300, 300, 300, 300,
|
|
300, 300, 300, 300, 300, 300,
|
|
300, 300, 300, 300, 300, 300,
|
|
300, 300, 300, 300, 300, 300,
|
|
};
|
|
|
|
static const int32_t ps_config_lmt_fw_diff_n[10 *10] =
|
|
{
|
|
-300, -300, -300, -300, -300, -300,
|
|
-300, -300, -300, -300, -300, -300,
|
|
-300, -300, -300, -300, -300, -300,
|
|
-300, -300, -300, -300, -300, -300,
|
|
-300, -300, -300, -300, -300, -300,
|
|
-300, -300, -300, -300, -300, -300
|
|
};
|
|
#endif
|
|
|
|
/***************************************************************************
|
|
* Private Functions
|
|
***************************************************************************/
|
|
|
|
static int nt38350_i2c_read(FAR struct i2c_master_s *dev,
|
|
FAR const struct i2c_config_s *config,
|
|
uint16_t address,
|
|
uint8_t *rdbuffer, int length)
|
|
{
|
|
/* Setup for the transfer */
|
|
|
|
struct i2c_msg_s msg[2];
|
|
int ret = -1;
|
|
int retries = 0;
|
|
|
|
msg[0].frequency = config->frequency;
|
|
msg[0].addr = address;
|
|
msg[0].flags = 0;
|
|
msg[0].buffer = &rdbuffer[0];
|
|
msg[0].length = 1;
|
|
|
|
msg[1].frequency = config->frequency;
|
|
msg[1].addr = address;
|
|
msg[1].flags = I2C_M_READ;
|
|
msg[1].buffer = &rdbuffer[1];
|
|
msg[1].length = length - 1;
|
|
|
|
/* Then perform the transfer dev addr and reg addr, read reg value */
|
|
|
|
while (retries < NVT_IIC_RETRY_NUM)
|
|
{
|
|
ret = I2C_TRANSFER(dev, msg, 2);
|
|
if (ret == 0)
|
|
break;
|
|
retries++;
|
|
}
|
|
|
|
if (retries >= NVT_IIC_RETRY_NUM)
|
|
{
|
|
/* Failed read */
|
|
|
|
syslog(LOG_ERR, "Failed to I2C read\n");
|
|
return -1;
|
|
}
|
|
|
|
return (ret >= 0) ? OK : ret;
|
|
}
|
|
|
|
static int nt38350_i2c_write(FAR struct i2c_master_s *dev,
|
|
FAR const struct i2c_config_s *config,
|
|
uint16_t address,
|
|
uint8_t *buffer, int length)
|
|
{
|
|
struct i2c_msg_s msg[1];
|
|
int ret;
|
|
|
|
/* Setup for the transfer */
|
|
|
|
msg[0].frequency = config->frequency;
|
|
msg[0].addr = address;
|
|
msg[0].flags = 0;
|
|
msg[0].buffer = (uint8_t *)buffer;
|
|
msg[0].length = length;
|
|
|
|
/* Then perform the transfer. */
|
|
|
|
ret = I2C_TRANSFER(dev, msg, 1);
|
|
return (ret >= 0) ? OK : ret;
|
|
}
|
|
|
|
static int nt38350_read_reg(FAR struct nt38350_dev_s *priv,
|
|
uint16_t address,
|
|
FAR uint8_t *buffer,
|
|
uint16_t length)
|
|
{
|
|
struct i2c_config_s config;
|
|
int ret;
|
|
|
|
/* Setup up the I2C configuration */
|
|
|
|
config.frequency = priv->config->frequency;
|
|
config.address = address;
|
|
config.addrlen = 7;
|
|
ret = nt38350_i2c_read(priv->config->i2c, &config,
|
|
address, buffer, length);
|
|
if (ret != OK)
|
|
{
|
|
/* Read error */
|
|
|
|
ierr("ERROR: %s Failed to read reg: %d\n", __func__, ret);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
static int nt38350_write_reg(FAR struct nt38350_dev_s *priv,
|
|
uint16_t address,
|
|
FAR uint8_t *buffer,
|
|
uint16_t length)
|
|
{
|
|
struct i2c_config_s config;
|
|
int ret;
|
|
|
|
/* Setup up the I2C configuration */
|
|
|
|
config.frequency = priv->config->frequency;
|
|
config.address = address;
|
|
config.addrlen = 7;
|
|
ret = nt38350_i2c_write(priv->config->i2c, &config,
|
|
address, buffer, length);
|
|
if (ret != OK)
|
|
{
|
|
ierr("ERROR: Failed to write reg %s : %d\n", __func__, __LINE__);
|
|
|
|
/* syserr("ERROR: i2c_write returned error code %d\n", ret); */
|
|
|
|
return ret;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
static void u8toi16(const uint8_t *stream, int32_t *data, uint32_t len)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < len / 2; i++)
|
|
{
|
|
data[i] = ((int32_t)stream[i * 2] +
|
|
(((int32_t)stream[i * 2 + 1]) << 8));
|
|
}
|
|
}
|
|
|
|
/***************************************************************************
|
|
*Description:
|
|
* Novatek touchscreen set index/page/addr address.
|
|
*
|
|
* return :
|
|
* Executive outcomes. 0---succeed. other---access fail.
|
|
***************************************************************************/
|
|
|
|
static int nvt_set_page(FAR struct nt38350_dev_s *priv, uint16_t i2c_addr,
|
|
uint32_t addr)
|
|
{
|
|
uint8_t buf[4];
|
|
buf[0] = 0xff;
|
|
buf[1] = (addr >> 16) & 0xff;
|
|
buf[2] = (addr >> 8) & 0xff;
|
|
|
|
return nt38350_write_reg(priv, i2c_addr, buf, 3);
|
|
}
|
|
|
|
/***************************************************************************
|
|
*Description:
|
|
* Novatek touchscreen reset MCU then into idle mode
|
|
*
|
|
*return:
|
|
* Executive outcomes. 0---succeed. other---access fail.
|
|
***************************************************************************/
|
|
|
|
static int nvt_sw_reset_idle(FAR struct nt38350_dev_s *priv)
|
|
{
|
|
uint8_t buf[4];
|
|
int ret;
|
|
|
|
buf[0] = 0x00;
|
|
buf[1] = 0xa5;
|
|
|
|
ret = nt38350_write_reg(priv, NVT_I2C_HW_ADDRESS, buf, 2);
|
|
|
|
usleep(NVT_DELAY_15MS);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/***************************************************************************
|
|
*Description:
|
|
* Novatek touchscreen reset MCU (boot) function.
|
|
*
|
|
*return:
|
|
* Executive outcomes. 0---succeed. other---access fail.
|
|
***************************************************************************/
|
|
|
|
static int nvt_bootloader_reset(FAR struct nt38350_dev_s *priv)
|
|
{
|
|
uint8_t buf[4];
|
|
int ret;
|
|
|
|
buf[0] = 0x00;
|
|
buf[1] = 0x69;
|
|
|
|
ret = nt38350_write_reg(priv, NVT_I2C_HW_ADDRESS, buf, 2);
|
|
if (ret != OK)
|
|
{
|
|
ierr("ERROR: Failed to reset bootloader\n");
|
|
}
|
|
|
|
usleep(NVT_DELAY_15MS);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/***************************************************************************
|
|
*Description:
|
|
* Novatek touchscreen clear FW status function.
|
|
*
|
|
*return:
|
|
* Executive outcomes. 0---succeed. other---access fail.
|
|
***************************************************************************/
|
|
|
|
static int nvt_clear_fw_status(FAR struct nt38350_dev_s *priv)
|
|
{
|
|
uint8_t buf[4];
|
|
int32_t i;
|
|
int ret;
|
|
const int32_t retry = 20;
|
|
|
|
for (i = 0; i < retry; i++)
|
|
{
|
|
nvt_set_page(priv, NVT_I2C_FW_ADDRESS, priv->mmap->event_buf_addr |
|
|
EVENT_MAP_HANDSHAKING_OR_SUB_CMD_BYTE);
|
|
|
|
buf[0] = EVENT_MAP_HANDSHAKING_OR_SUB_CMD_BYTE;
|
|
buf[1] = 0x00;
|
|
ret = nt38350_write_reg(priv, NVT_I2C_FW_ADDRESS, buf, 2);
|
|
|
|
buf[0] = EVENT_MAP_HANDSHAKING_OR_SUB_CMD_BYTE;
|
|
buf[1] = 0xff;
|
|
ret = nt38350_read_reg(priv, NVT_I2C_FW_ADDRESS, buf, 2);
|
|
|
|
if (buf[1] == 0x00)
|
|
{
|
|
break;
|
|
}
|
|
|
|
usleep(NVT_DELAY_10MS);
|
|
}
|
|
|
|
if (i >= retry)
|
|
{
|
|
ierr("ERROR: Failed to clear FW status\n");
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
return OK;
|
|
}
|
|
}
|
|
|
|
/***************************************************************************
|
|
*Description:
|
|
* Novatek touchscreen check FW status function.
|
|
*
|
|
*return:
|
|
* Executive outcomes. 0---succeed. other---access fail.
|
|
***************************************************************************/
|
|
|
|
static int nvt_check_fw_status(FAR struct nt38350_dev_s *priv)
|
|
{
|
|
uint8_t buf[4];
|
|
int32_t i;
|
|
int ret;
|
|
const int32_t retry = 50;
|
|
|
|
for (i = 0; i < retry; i++)
|
|
{
|
|
nvt_set_page(priv, NVT_I2C_FW_ADDRESS, priv->mmap->event_buf_addr |
|
|
EVENT_MAP_HANDSHAKING_OR_SUB_CMD_BYTE);
|
|
|
|
buf[0] = EVENT_MAP_HANDSHAKING_OR_SUB_CMD_BYTE;
|
|
buf[1] = 0x00;
|
|
ret = nt38350_read_reg(priv, NVT_I2C_FW_ADDRESS, buf, 2);
|
|
|
|
if ((buf[1] & 0xf0) == 0xa0)
|
|
break;
|
|
usleep(NVT_DELAY_10MS);
|
|
}
|
|
|
|
if (i >= retry)
|
|
{
|
|
ierr("ERROR: Failed to clear FW status\n");
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
return OK;
|
|
}
|
|
}
|
|
|
|
/***************************************************************************
|
|
*Description:
|
|
* Novatek touchscreen check FW reset state function.
|
|
*
|
|
*return:
|
|
* Executive outcomes. 0---succeed. other---access fail.
|
|
***************************************************************************/
|
|
|
|
static int nvt_check_fw_reset_state(FAR struct nt38350_dev_s *priv,
|
|
rst_complete_state_t check_reset_state)
|
|
{
|
|
uint8_t buf[8];
|
|
int32_t ret;
|
|
int32_t retry = 0;
|
|
|
|
while (1)
|
|
{
|
|
usleep(NVT_DELAY_10MS);
|
|
buf[0] = EVENT_MAP_RESET_COMPLETE;
|
|
buf[1] = 0x00;
|
|
ret = nt38350_read_reg(priv, NVT_I2C_FW_ADDRESS, buf, 2);
|
|
|
|
if ((buf[1] >= check_reset_state) && (buf[1] <= RESET_STATE_MAX))
|
|
{
|
|
ret = OK;
|
|
break;
|
|
}
|
|
|
|
retry++;
|
|
if (retry > 100)
|
|
{
|
|
ierr("ERROR: Failed to check FW state\n");
|
|
ret = -1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/***************************************************************************
|
|
*Description:
|
|
* Novatek touchscreen get novatek project id information
|
|
*
|
|
*return:
|
|
* Executive outcomes. 0---succeed. other---access fail.
|
|
***************************************************************************/
|
|
|
|
static int nvt_read_pid(FAR struct nt38350_dev_s *priv)
|
|
{
|
|
uint8_t buf[3];
|
|
uint16_t nvt_pid;
|
|
int ret;
|
|
|
|
nvt_set_page(priv, NVT_I2C_FW_ADDRESS, priv->mmap->event_buf_addr |
|
|
EVENT_MAP_PROJECTID);
|
|
buf[0] = EVENT_MAP_PROJECTID;
|
|
buf[1] = 0x00;
|
|
buf[2] = 0x00;
|
|
ret = nt38350_read_reg(priv, NVT_I2C_FW_ADDRESS, buf, 3);
|
|
|
|
nvt_pid = (buf[2] << 8) + buf[1];
|
|
priv->nvt_pid = nvt_pid;
|
|
|
|
#if NVT_DEBUG
|
|
iinfo("PID = %04x\n", nvt_pid);
|
|
#endif
|
|
|
|
return ret;
|
|
}
|
|
|
|
/***************************************************************************
|
|
*Description:
|
|
* Novatek touchscreen get firmware related information
|
|
*
|
|
*return:
|
|
* Executive outcomes. 0---succeed. other---access fail.
|
|
***************************************************************************/
|
|
|
|
static int nvt_get_fw_info(FAR struct nt38350_dev_s *priv)
|
|
{
|
|
uint8_t buf[64];
|
|
uint32_t retry_count = 0;
|
|
int32_t ret;
|
|
|
|
info_retry:
|
|
nvt_set_page(priv, NVT_I2C_FW_ADDRESS, priv->mmap->event_buf_addr |
|
|
EVENT_MAP_FWINFO);
|
|
buf[0] = EVENT_MAP_FWINFO;
|
|
ret = nt38350_read_reg(priv, NVT_I2C_FW_ADDRESS, buf, 17);
|
|
priv->fw_ver = buf[1];
|
|
priv->x_num = buf[3];
|
|
priv->y_num = buf[4];
|
|
priv->abs_x_max = (uint16_t)((buf[5] << 8) | buf[6]);
|
|
priv->abs_y_max = (uint16_t)((buf[7] << 8) | buf[8]);
|
|
priv->max_button_num = buf[11];
|
|
|
|
if ((buf[1] + buf[2]) != 0xff)
|
|
{
|
|
priv->fw_ver = 0;
|
|
priv->x_num = 6;
|
|
priv->y_num = 6;
|
|
priv->abs_x_max = NVT_TOUCH_DEFAULT_MAX_WIDTH;
|
|
priv->abs_y_max = NVT_TOUCH_DEFAULT_MAX_HEIGHT;
|
|
priv->max_button_num = NVT_TOUCH_KEY_NUM;
|
|
|
|
if (retry_count < 3)
|
|
{
|
|
retry_count++;
|
|
ierr("retry_count = %d\n", retry_count);
|
|
goto info_retry;
|
|
}
|
|
else
|
|
{
|
|
ret = -1;
|
|
}
|
|
}
|
|
|
|
nvt_read_pid(priv);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/***************************************************************************
|
|
*Description:
|
|
* Novatek touchscreen check and stop crc reboot loop
|
|
*
|
|
*return:
|
|
* Executive outcomes. 0---succeed. other---access fail.
|
|
***************************************************************************/
|
|
|
|
static int nvt_stop_crc_reboot(FAR struct nt38350_dev_s *priv)
|
|
{
|
|
uint8_t buf[8];
|
|
int32_t retry;
|
|
int ret;
|
|
|
|
/* ---change I2C index to prevent geting 0xFF, but not 0xFC--- */
|
|
|
|
nvt_set_page(priv, NVT_I2C_BLDR_ADDRESS, 0x1f64e);
|
|
|
|
/* ---read to check if buf is 0xFC which means IC is in CRC reboot */
|
|
|
|
buf[0] = 0x4e;
|
|
ret = nt38350_read_reg(priv, NVT_I2C_BLDR_ADDRESS, buf, 4);
|
|
|
|
if ((buf[1] == 0xfc) ||
|
|
((buf[1] == 0xff) && (buf[2] == 0xff) && (buf[3] == 0xff)))
|
|
{
|
|
for (retry = 5; retry > 0; retry--)
|
|
{
|
|
/* ---write i2c cmds to reset idle : 1st--- */
|
|
|
|
buf[0] = 0x00;
|
|
buf[1] = 0xa5;
|
|
ret = nt38350_write_reg(priv, NVT_I2C_HW_ADDRESS, buf, 2);
|
|
|
|
/* ---write i2c cmds to reset idle : 2ed--- */
|
|
|
|
buf[0] = 0x00;
|
|
buf[1] = 0xa5;
|
|
ret = nt38350_write_reg(priv, NVT_I2C_HW_ADDRESS, buf, 2);
|
|
usleep(NVT_DELAY_1MS);
|
|
|
|
/* ---clear CRC_ERR_FLAG--- */
|
|
|
|
nvt_set_page(priv, NVT_I2C_BLDR_ADDRESS, 0x3f135);
|
|
|
|
buf[0] = 0x35;
|
|
buf[1] = 0xa5;
|
|
ret = nt38350_write_reg(priv, NVT_I2C_BLDR_ADDRESS, buf, 2);
|
|
|
|
/* ---check CRC_ERR_FLAG--- */
|
|
|
|
nvt_set_page(priv, NVT_I2C_BLDR_ADDRESS, 0x3f135);
|
|
|
|
buf[0] = 0x35;
|
|
buf[1] = 0x00;
|
|
ret = nt38350_read_reg(priv, NVT_I2C_BLDR_ADDRESS, buf, 2);
|
|
if (buf[1] == 0xa5)
|
|
break;
|
|
}
|
|
|
|
if (retry == 0)
|
|
ierr("ERROR: CRC auto reboot is not able "
|
|
"to be stopped buf[1]=0x%02x\n", buf[1]);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/***************************************************************************
|
|
*Description:
|
|
* Novatek touchscreen check chip version trim function.
|
|
*
|
|
*return:
|
|
* Executive outcomes. 0---succeed. other---access fail.
|
|
***************************************************************************/
|
|
|
|
static int nvt_ts_check_chip_ver_trim(FAR struct nt38350_dev_s *priv,
|
|
uint32_t chip_ver_trim_addr)
|
|
{
|
|
int32_t found_nvt_chip = 0;
|
|
uint8_t buf[8];
|
|
int32_t retry;
|
|
int32_t list;
|
|
int32_t ret = -1;
|
|
int32_t i;
|
|
|
|
nvt_bootloader_reset(priv);
|
|
|
|
for (retry = 5; retry > 0; retry--)
|
|
{
|
|
nvt_sw_reset_idle(priv);
|
|
buf[0] = 0x00;
|
|
buf[1] = 0x35;
|
|
ret = nt38350_write_reg(priv, NVT_I2C_HW_ADDRESS, buf, 2);
|
|
usleep(NVT_DELAY_10MS);
|
|
|
|
nvt_set_page(priv, NVT_I2C_BLDR_ADDRESS, chip_ver_trim_addr);
|
|
|
|
buf[0] = chip_ver_trim_addr & 0xff;
|
|
buf[1] = 0x00;
|
|
buf[2] = 0x00;
|
|
buf[3] = 0x00;
|
|
buf[4] = 0x00;
|
|
buf[5] = 0x00;
|
|
buf[6] = 0x00;
|
|
ret = nt38350_read_reg(priv, NVT_I2C_BLDR_ADDRESS, buf, 7);
|
|
|
|
/* Get Touch IC ID */
|
|
|
|
#if NVT_DEBUG
|
|
iinfo("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]);
|
|
#endif
|
|
|
|
/* ---Stop CRC check to prevent IC auto reboot--- */
|
|
|
|
if ((buf[1] == 0xfc) ||
|
|
((buf[1] == 0xff) && (buf[2] == 0xff) && (buf[3] == 0xff)))
|
|
{
|
|
nvt_stop_crc_reboot(priv);
|
|
continue;
|
|
}
|
|
|
|
/* compare read chip id on supported list */
|
|
|
|
for (list = 0; list < (sizeof(g_trim_id_table) /
|
|
sizeof(struct nvt_ts_trim_id_table_s)); list++)
|
|
{
|
|
found_nvt_chip = 0;
|
|
|
|
/* compare each byte */
|
|
|
|
for (i = 0; i < NVT_ID_BYTE_MAX; i++)
|
|
{
|
|
if (g_trim_id_table[list].mask[i])
|
|
{
|
|
if (buf[i + 1] != g_trim_id_table[list].id[i])
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i == NVT_ID_BYTE_MAX)
|
|
{
|
|
found_nvt_chip = 1;
|
|
}
|
|
|
|
if (found_nvt_chip)
|
|
{
|
|
#if NVT_DEBUG
|
|
ierr("This NVT touch IC\n");
|
|
#endif
|
|
priv->mmap = g_trim_id_table[list].mmap;
|
|
priv->carrier_system =
|
|
g_trim_id_table[list].hwinfo->carrier_system;
|
|
ret = 0;
|
|
goto out;
|
|
}
|
|
else
|
|
{
|
|
priv->mmap = NULL;
|
|
ret = -1;
|
|
}
|
|
}
|
|
|
|
usleep(NVT_DELAY_10MS);
|
|
}
|
|
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
/***************************************************************************
|
|
*Description:
|
|
* Novatek touchscreen get firmware size
|
|
*
|
|
*return:
|
|
* Executive outcomes. 0---succeed. negative---failed.
|
|
***************************************************************************/
|
|
|
|
static int get_nvt_fw_size(FAR struct nt38350_dev_s *priv)
|
|
{
|
|
uint32_t fw_size;
|
|
char *fw_path = priv->fw_path;
|
|
FILE *fp = NULL;
|
|
|
|
fp = fopen(fw_path, "rb");
|
|
if (fp != NULL)
|
|
{
|
|
if (fseek(fp, 0, SEEK_END))
|
|
{
|
|
fclose(fp);
|
|
return -1;
|
|
}
|
|
|
|
fw_size = ftell(fp);
|
|
fclose(fp);
|
|
priv->fw_size = fw_size;
|
|
return 0;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
/***************************************************************************
|
|
*Description:
|
|
* Novatek touchscreen get firmware content
|
|
*
|
|
*return:
|
|
* Executive outcomes. 0---succeed. negative---failed.
|
|
***************************************************************************/
|
|
|
|
static int get_nvt_fw_content(FAR struct nt38350_dev_s *priv,
|
|
FAR uint8_t *fw_data)
|
|
{
|
|
int ret;
|
|
char *fw_path = priv->fw_path;
|
|
FILE *fp = NULL;
|
|
uint32_t fw_size = priv->fw_size;
|
|
|
|
fp = fopen(fw_path, "rb");
|
|
if (fp != NULL)
|
|
{
|
|
fseek(fp, 0, SEEK_SET);
|
|
|
|
ret = fread(fw_data, fw_size, 1, fp);
|
|
if (ret == 0)
|
|
{
|
|
ierr("ERROR: Failed get firmware content\n");
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
static int nvt_get_fw_need_write_size(FAR struct nt38350_dev_s *priv,
|
|
FAR uint8_t *data)
|
|
{
|
|
int ret;
|
|
int i;
|
|
uint32_t total_sectors_to_check;
|
|
|
|
total_sectors_to_check = priv->fw_size / NVT_FLASH_SECTOR_SIZE;
|
|
#if NVT_DEBUG
|
|
iinfo("total_sectors_to_check %d\n", total_sectors_to_check);
|
|
#endif
|
|
|
|
for (i = total_sectors_to_check; i > 0; i--)
|
|
{
|
|
/* check if there is end flag "NVT" at the end of this sector */
|
|
|
|
if (strncmp(&data[i * NVT_FLASH_SECTOR_SIZE -
|
|
NVT_FLASH_END_FLAG_LEN], "NVT", NVT_FLASH_END_FLAG_LEN) == 0)
|
|
{
|
|
priv->fw_need_write_size = i * NVT_FLASH_SECTOR_SIZE;
|
|
#if NVT_DEBUG
|
|
iinfo("fw_need_write_size = %zu(0x%zx)\n",
|
|
priv->fw_need_write_size,
|
|
priv->fw_need_write_size);
|
|
#endif
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
/***************************************************************************
|
|
*Description:
|
|
* Novatek touchscreen initial bootloader and flash
|
|
* block function.
|
|
*
|
|
*return:
|
|
* Executive outcomes. 0---succeed. negative---failed.
|
|
***************************************************************************/
|
|
|
|
static int nvt_init_bootloader(FAR struct nt38350_dev_s *priv)
|
|
{
|
|
int32_t ret;
|
|
int32_t retry;
|
|
uint8_t buf[8] =
|
|
{
|
|
0
|
|
};
|
|
|
|
/* SW Reset & Idle */
|
|
|
|
nvt_sw_reset_idle(priv);
|
|
|
|
/* Initiate Flash Block */
|
|
|
|
buf[0] = 0x00;
|
|
buf[1] = 0x00;
|
|
buf[2] = NVT_I2C_FW_ADDRESS;
|
|
ret = nt38350_write_reg(priv, NVT_I2C_HW_ADDRESS, buf, 3);
|
|
if (ret < 0)
|
|
{
|
|
ierr("ERROR: Inittial Flash Block error!!(%d)\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
/* Check 0xAA (Initiate Flash Block) */
|
|
|
|
retry = 0;
|
|
while (1)
|
|
{
|
|
usleep(NVT_DELAY_1MS);
|
|
buf[0] = 0x00;
|
|
buf[1] = 0x00;
|
|
ret = nt38350_read_reg(priv, NVT_I2C_HW_ADDRESS, buf, 2);
|
|
if (ret < 0)
|
|
{
|
|
ierr("ERROR: Check 0xAA (Inittial Flash Block) "
|
|
"error.(%d)\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
if (buf[1] == 0xaa)
|
|
{
|
|
break;
|
|
}
|
|
|
|
retry++;
|
|
|
|
if (retry > 20)
|
|
{
|
|
ierr("ERROR: Check 0xAA (Inittial Flash Block) error."
|
|
"status=0x%02X\n", buf[1]);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
usleep(NVT_DELAY_20MS);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/***************************************************************************
|
|
*Description:
|
|
* Novatek touchscreen resume from deep power down function.
|
|
*
|
|
*return:
|
|
* Executive outcomes. 0---succeed. negative---failed.
|
|
***************************************************************************/
|
|
|
|
static int nvt_resume_pd(FAR struct nt38350_dev_s *priv)
|
|
{
|
|
int32_t ret;
|
|
int32_t retry;
|
|
uint8_t buf[4] =
|
|
{
|
|
0
|
|
};
|
|
|
|
/* Resume Command */
|
|
|
|
buf[0] = 0x00;
|
|
buf[1] = 0xab;
|
|
ret = nt38350_write_reg(priv, NVT_I2C_HW_ADDRESS, buf, 2);
|
|
if (ret < 0)
|
|
{
|
|
ierr("ERROR: Write Enable error!!(%d)\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
/* Check 0xAA (Resume Command) */
|
|
|
|
retry = 0;
|
|
while (1)
|
|
{
|
|
usleep(NVT_DELAY_1MS);
|
|
buf[0] = 0x00;
|
|
buf[1] = 0x00;
|
|
ret = nt38350_read_reg(priv, NVT_I2C_HW_ADDRESS, buf, 2);
|
|
if (ret < 0)
|
|
{
|
|
ierr("ERROR: Check 0xAA (Resume Command) error!!(%d)\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
if (buf[1] == 0xaa)
|
|
{
|
|
break;
|
|
}
|
|
|
|
retry++;
|
|
|
|
if (retry > 20)
|
|
{
|
|
ierr("ERROR: Check 0xAA (Resume Command) error!!"
|
|
"status=0x%02X\n", buf[1]);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
usleep(NVT_DELAY_10MS);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/***************************************************************************
|
|
*Description:
|
|
* Novatek touchscreen erase flash sectors function.
|
|
*
|
|
*return:
|
|
* Executive outcomes. 0---succeed. negative---failed.
|
|
***************************************************************************/
|
|
|
|
static int nvt_erase_flash(FAR struct nt38350_dev_s *priv)
|
|
{
|
|
int32_t ret;
|
|
int32_t count;
|
|
int32_t i;
|
|
int32_t flash_address;
|
|
int32_t retry;
|
|
uint8_t buf[12] =
|
|
{
|
|
0
|
|
};
|
|
|
|
/* Write Enable */
|
|
|
|
buf[0] = 0x00;
|
|
buf[1] = 0x06;
|
|
ret = nt38350_write_reg(priv, NVT_I2C_HW_ADDRESS, buf, 2);
|
|
if (ret < 0)
|
|
{
|
|
ierr("ERROR: Write Enable (for Write Status Register)"
|
|
" error!!(%d)\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
/* Check 0xAA (Write Enable) */
|
|
|
|
retry = 0;
|
|
|
|
while (1)
|
|
{
|
|
usleep(NVT_DELAY_1MS);
|
|
buf[0] = 0x00;
|
|
buf[1] = 0x00;
|
|
ret = nt38350_read_reg(priv, NVT_I2C_HW_ADDRESS, buf, 2);
|
|
if (ret < 0)
|
|
{
|
|
ierr("ERROR: Check 0xAA (Write Enable for Write Status Register)"
|
|
" error!!(%d)\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
if (buf[1] == 0xaa)
|
|
{
|
|
break;
|
|
}
|
|
|
|
retry++;
|
|
if (retry > 20)
|
|
{
|
|
ierr("ERROR: Check 0xAA (Write Enable for Write "
|
|
"Status Register)error!! status=0x%02X\n", buf[1]);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/* Write Status Register */
|
|
|
|
buf[0] = 0x00;
|
|
buf[1] = 0x01;
|
|
buf[2] = 0x00;
|
|
ret = nt38350_write_reg(priv, NVT_I2C_HW_ADDRESS, buf, 3);
|
|
if (ret < 0)
|
|
{
|
|
ierr("ERROR: Write Status Register error!!(%d)\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
/* Check 0xAA (Write Status Register) */
|
|
|
|
retry = 0;
|
|
while (1)
|
|
{
|
|
usleep(NVT_DELAY_1MS);
|
|
buf[0] = 0x00;
|
|
buf[1] = 0x00;
|
|
ret = nt38350_read_reg(priv, NVT_I2C_HW_ADDRESS, buf, 2);
|
|
if (ret < 0)
|
|
{
|
|
ierr("ERROR: Check 0xAA (Write Status Register)"
|
|
" error!!(%d)\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
if (buf[1] == 0xaa)
|
|
{
|
|
break;
|
|
}
|
|
|
|
retry++;
|
|
if (retry > 20)
|
|
{
|
|
ierr("ERROR: Check 0xAA (Write Status Register) "
|
|
"error!! status=0x%02X\n", buf[1]);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/* Read Status */
|
|
|
|
retry = 0;
|
|
while (1)
|
|
{
|
|
usleep(NVT_DELAY_5MS);
|
|
buf[0] = 0x00;
|
|
buf[1] = 0x05;
|
|
ret = nt38350_write_reg(priv, NVT_I2C_HW_ADDRESS, buf, 2);
|
|
if (ret < 0)
|
|
{
|
|
ierr("ERROR: Read Status (for Write Status Register)"
|
|
"error!!(%d)\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
/* Check 0xAA (Read Status) */
|
|
|
|
buf[0] = 0x00;
|
|
buf[1] = 0x00;
|
|
buf[2] = 0x00;
|
|
ret = nt38350_read_reg(priv, NVT_I2C_HW_ADDRESS, buf, 3);
|
|
if (ret < 0)
|
|
{
|
|
ierr("ERROR: Check 0xAA (Read Status for"
|
|
"Write Status Register) error!!(%d)\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
if ((buf[1] == 0xaa) && (buf[2] == 0x00))
|
|
{
|
|
break;
|
|
}
|
|
|
|
retry++;
|
|
if (retry > 100)
|
|
{
|
|
ierr("ERROR: Check 0xAA (Read Status for Write Status"
|
|
" Register) failed,buf[1]=0x%02X, buf[2]=0x%02X,"
|
|
"retry=%d\n", buf[1], buf[2], retry);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (priv->fw_need_write_size % NVT_FLASH_SECTOR_SIZE)
|
|
count = priv->fw_need_write_size / NVT_FLASH_SECTOR_SIZE + 1;
|
|
else
|
|
count = priv->fw_need_write_size / NVT_FLASH_SECTOR_SIZE;
|
|
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
/* Write Enable */
|
|
|
|
buf[0] = 0x00;
|
|
buf[1] = 0x06;
|
|
ret = nt38350_write_reg(priv, NVT_I2C_HW_ADDRESS, buf, 2);
|
|
if (ret < 0)
|
|
{
|
|
ierr("ERROR: Write Enable error!!(%d,%d)\n", ret, i);
|
|
return ret;
|
|
}
|
|
|
|
/* Check 0xAA (Write Enable) */
|
|
|
|
retry = 0;
|
|
while (1)
|
|
{
|
|
usleep(NVT_DELAY_1MS);
|
|
buf[0] = 0x00;
|
|
buf[1] = 0x00;
|
|
ret = nt38350_read_reg(priv, NVT_I2C_HW_ADDRESS, buf, 2);
|
|
if (ret < 0)
|
|
{
|
|
ierr("ERROR: Check 0xAA (Write Enable) error!!(%d,%d)\n",
|
|
ret, i);
|
|
return ret;
|
|
}
|
|
|
|
if (buf[1] == 0xaa)
|
|
{
|
|
break;
|
|
}
|
|
|
|
retry++;
|
|
if (retry > 20)
|
|
{
|
|
ierr("ERROR: Check 0xAA (Write Enable) error!!"
|
|
"status=0x%02X\n", buf[1]);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
flash_address = i * NVT_FLASH_SECTOR_SIZE;
|
|
|
|
/* Sector Erase */
|
|
|
|
buf[0] = 0x00;
|
|
|
|
/* Command : Sector Erase */
|
|
|
|
buf[1] = 0x20;
|
|
buf[2] = ((flash_address >> 16) & 0xff);
|
|
buf[3] = ((flash_address >> 8) & 0xff);
|
|
buf[4] = (flash_address & 0xff);
|
|
ret = nt38350_write_reg(priv, NVT_I2C_HW_ADDRESS, buf, 5);
|
|
if (ret < 0)
|
|
{
|
|
ierr("ERROR: Sector Erase error!!(%d,%d)\n", ret, i);
|
|
return ret;
|
|
}
|
|
|
|
/* Check 0xAA (Sector Erase) */
|
|
|
|
retry = 0;
|
|
while (1)
|
|
{
|
|
usleep(NVT_DELAY_1MS);
|
|
buf[0] = 0x00;
|
|
buf[1] = 0x00;
|
|
ret = nt38350_read_reg(priv, NVT_I2C_HW_ADDRESS, buf, 2);
|
|
if (ret < 0)
|
|
{
|
|
ierr("ERROR: Check 0xAA (Sector Erase) error!!(%d,%d)\n",
|
|
ret, i);
|
|
return ret;
|
|
}
|
|
|
|
if (buf[1] == 0xaa)
|
|
{
|
|
break;
|
|
}
|
|
|
|
retry++;
|
|
|
|
if (retry > 20)
|
|
{
|
|
ierr("ERROR: Check 0xAA (Sector Erase) failed,"
|
|
"buf[1]=0x%02X,retry=%d\n", buf[1], retry);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/* Read Status */
|
|
|
|
retry = 0;
|
|
while (1)
|
|
{
|
|
usleep(NVT_DELAY_5MS);
|
|
buf[0] = 0x00;
|
|
buf[1] = 0x05;
|
|
ret = nt38350_write_reg(priv, NVT_I2C_HW_ADDRESS, buf, 2);
|
|
if (ret < 0)
|
|
{
|
|
ierr("ERROR: Read Status error!!(%d,%d)\n", ret, i);
|
|
return ret;
|
|
}
|
|
|
|
/* Check 0xAA (Read Status) */
|
|
|
|
buf[0] = 0x00;
|
|
buf[1] = 0x00;
|
|
buf[2] = 0x00;
|
|
ret = nt38350_read_reg(priv, NVT_I2C_HW_ADDRESS, buf, 3);
|
|
if (ret < 0)
|
|
{
|
|
ierr("ERROR: Check 0xAA (Read Status) error!!(%d,%d)\n",
|
|
ret, i);
|
|
return ret;
|
|
}
|
|
|
|
if ((buf[1] == 0xaa) && (buf[2] == 0x00))
|
|
{
|
|
break;
|
|
}
|
|
|
|
retry++;
|
|
|
|
if (retry > 100)
|
|
{
|
|
ierr("ERROR: Check 0xAA (Read Status) failed,"
|
|
"buf[1]=0x%02X, buf[2]=0x%02X,retry=%d\n",
|
|
buf[1], buf[2], retry);
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/***************************************************************************
|
|
*Description:
|
|
* Novatek touchscreen write flash sectors function.
|
|
*
|
|
*return:
|
|
* Executive outcomes. 0---succeed. negative---failed.
|
|
***************************************************************************/
|
|
|
|
static int nvt_write_flash(FAR struct nt38350_dev_s *priv, uint8_t *data)
|
|
{
|
|
int32_t i;
|
|
int32_t j;
|
|
int32_t k;
|
|
int32_t ret;
|
|
int32_t retry;
|
|
int32_t count;
|
|
uint8_t tmpvalue;
|
|
int32_t percent;
|
|
uint32_t flash_address;
|
|
int32_t previous_percent = -1;
|
|
uint32_t xdata_addr = priv->mmap->rw_flash_data_addr;
|
|
uint8_t buf[64] =
|
|
{
|
|
0
|
|
};
|
|
|
|
/* change I2C buffer index */
|
|
|
|
ret = nvt_set_page(priv, NVT_I2C_BLDR_ADDRESS, xdata_addr);
|
|
if (ret < 0)
|
|
{
|
|
ierr("ERROR: change I2C buffer index error!!(%d)\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
if (priv->fw_need_write_size % 256)
|
|
{
|
|
count = priv->fw_need_write_size / 256 + 1;
|
|
}
|
|
else
|
|
{
|
|
count = priv->fw_need_write_size / 256;
|
|
}
|
|
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
flash_address = i * 256;
|
|
|
|
/* Write Enable */
|
|
|
|
buf[0] = 0x00;
|
|
buf[1] = 0x06;
|
|
ret = nt38350_write_reg(priv, NVT_I2C_HW_ADDRESS, buf, 2);
|
|
if (ret < 0)
|
|
{
|
|
ierr("ERROR: Write Enable error!!(%d)\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
/* Check 0xAA (Write Enable) */
|
|
|
|
retry = 0;
|
|
while (1)
|
|
{
|
|
usleep(NVT_DELAY_100US);
|
|
buf[0] = 0x00;
|
|
buf[1] = 0x00;
|
|
ret = nt38350_read_reg(priv, NVT_I2C_HW_ADDRESS, buf, 2);
|
|
if (ret < 0)
|
|
{
|
|
ierr("ERROR: Check 0xAA (Write Enable) error!!(%d,%d)\n",
|
|
ret, i);
|
|
return ret;
|
|
}
|
|
|
|
if (buf[1] == 0xaa)
|
|
{
|
|
break;
|
|
}
|
|
|
|
retry++;
|
|
|
|
if (retry > 20)
|
|
{
|
|
ierr("ERROR: Check 0xAA (Write Enable) error!!"
|
|
"status=0x%02X\n", buf[1]);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/* Write Page : 256 bytes */
|
|
|
|
const size_t min_size = min(priv->fw_need_write_size - i * 256,
|
|
(size_t)256);
|
|
|
|
for (j = 0; j < min_size; j += 32)
|
|
{
|
|
buf[0] = (xdata_addr + j) & 0xff;
|
|
for (k = 0; k < 32; k++)
|
|
{
|
|
buf[1 + k] = data[flash_address + j + k];
|
|
}
|
|
|
|
ret = nt38350_write_reg(priv, NVT_I2C_BLDR_ADDRESS, buf, 33);
|
|
if (ret < 0)
|
|
{
|
|
ierr("ERROR: Write Page error!!(%d), j=%d\n", ret, j);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
if (priv->fw_need_write_size - flash_address >= 256)
|
|
tmpvalue = (flash_address >> 16) + ((flash_address >> 8) & 0xff) +
|
|
(flash_address & 0xff) + 0x00 + (255);
|
|
else
|
|
tmpvalue = (flash_address >> 16) + ((flash_address >> 8) & 0xff) +
|
|
(flash_address & 0xff) + 0x00 +
|
|
(priv->fw_need_write_size - flash_address - 1);
|
|
|
|
const size_t min_size_tmp = min(priv->fw_need_write_size -
|
|
flash_address, (size_t)256);
|
|
|
|
for (k = 0; k < min_size_tmp; k++)
|
|
tmpvalue += data[flash_address + k];
|
|
|
|
tmpvalue = 255 - tmpvalue + 1;
|
|
|
|
/* Page Program */
|
|
|
|
buf[0] = 0x00;
|
|
buf[1] = 0x02;
|
|
buf[2] = ((flash_address >> 16) & 0xff);
|
|
buf[3] = ((flash_address >> 8) & 0xff);
|
|
buf[4] = (flash_address & 0xff);
|
|
buf[5] = 0x00;
|
|
buf[6] = min(priv->fw_need_write_size - flash_address,
|
|
(size_t)256) - 1;
|
|
buf[7] = tmpvalue;
|
|
ret = nt38350_write_reg(priv, NVT_I2C_HW_ADDRESS, buf, 8);
|
|
if (ret < 0)
|
|
{
|
|
ierr("ERROR: Page Program error!!(%d), i=%d\n", ret, i);
|
|
return ret;
|
|
}
|
|
|
|
/* Check 0xAA (Page Program) */
|
|
|
|
retry = 0;
|
|
while (1)
|
|
{
|
|
usleep(NVT_DELAY_1MS);
|
|
buf[0] = 0x00;
|
|
buf[1] = 0x00;
|
|
ret = nt38350_read_reg(priv, NVT_I2C_HW_ADDRESS, buf, 2);
|
|
if (ret < 0)
|
|
{
|
|
ierr("ERROR: Page Program error!!(%d)\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
if (buf[1] == 0xaa || buf[1] == 0xea)
|
|
{
|
|
break;
|
|
}
|
|
|
|
retry++;
|
|
if (retry > 20)
|
|
{
|
|
ierr("ERROR: Check 0xAA (Page Program) failed,"
|
|
"buf[1]=0x%02X,retry=%d\n", buf[1], retry);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (buf[1] == 0xea)
|
|
{
|
|
ierr("ERROR: Page Program error!! i=%d %d\n", i, __LINE__);
|
|
return -3;
|
|
}
|
|
|
|
/* Read Status */
|
|
|
|
retry = 0;
|
|
while (1)
|
|
{
|
|
usleep(NVT_DELAY_5MS);
|
|
buf[0] = 0x00;
|
|
buf[1] = 0x05;
|
|
ret = nt38350_write_reg(priv, NVT_I2C_HW_ADDRESS, buf, 2);
|
|
if (ret < 0)
|
|
{
|
|
ierr("ERROR: Read Status error!!(%d)\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
/* Check 0xAA (Read Status) */
|
|
|
|
buf[0] = 0x00;
|
|
buf[1] = 0x00;
|
|
buf[2] = 0x00;
|
|
ret = nt38350_read_reg(priv, NVT_I2C_HW_ADDRESS, buf, 3);
|
|
if (ret < 0)
|
|
{
|
|
ierr("ERROR: Check 0xAA (Read Status) error!!(%d)\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
if (((buf[1] == 0xaa) && (buf[2] == 0x00)) || (buf[1] == 0xea))
|
|
{
|
|
break;
|
|
}
|
|
|
|
retry++;
|
|
if (retry > 100)
|
|
{
|
|
ierr("ERROR: Check 0xAA (Read Status) failed,"
|
|
"buf[1]=0x%02X, buf[2]=0x%02X,retry=%d\n",
|
|
buf[1], buf[2], retry);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (buf[1] == 0xea)
|
|
{
|
|
ierr("Page Program error!! i=%d %d\n", i, __LINE__);
|
|
return -4;
|
|
}
|
|
|
|
percent = ((i + 1) * 100) / count;
|
|
if (((percent % 10) == 0) && (percent != previous_percent))
|
|
{
|
|
iinfo("Programming...%2d%%\n", percent);
|
|
previous_percent = percent;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/***************************************************************************
|
|
*Description:
|
|
* Novatek touchscreen verify checksum of written
|
|
* flash function.
|
|
*
|
|
*return:
|
|
* Executive outcomes. 0---succeed. negative---failed.
|
|
***************************************************************************/
|
|
|
|
static int nvt_verify_flash(FAR struct nt38350_dev_s *priv, uint8_t *data)
|
|
{
|
|
int32_t i;
|
|
int32_t k;
|
|
int32_t ret;
|
|
int32_t retry;
|
|
size_t len_in_blk;
|
|
uint32_t xdata_addr = priv->mmap->read_flash_checksum_addr;
|
|
uint8_t buf[64] =
|
|
{
|
|
0
|
|
};
|
|
|
|
uint16_t wr_filechksum[NVT_BLOCK_64KB_NUM] =
|
|
{
|
|
0
|
|
};
|
|
|
|
uint16_t rd_filechksum[NVT_BLOCK_64KB_NUM] =
|
|
{
|
|
0
|
|
};
|
|
|
|
for (i = 0; i < NVT_BLOCK_64KB_NUM; i++)
|
|
{
|
|
if (priv->fw_need_write_size > (i * NVT_SIZE_64KB))
|
|
{
|
|
/* Calculate wr_filechksum of each 64KB block */
|
|
|
|
len_in_blk = min(priv->fw_need_write_size -
|
|
i * NVT_SIZE_64KB, (size_t)NVT_SIZE_64KB);
|
|
wr_filechksum[i] = i + 0x00 + 0x00 +
|
|
(((len_in_blk - 1) >> 8) & 0xff) + ((len_in_blk - 1) & 0xff);
|
|
for (k = 0; k < len_in_blk; k++)
|
|
{
|
|
wr_filechksum[i] += data[k + i * NVT_SIZE_64KB];
|
|
}
|
|
|
|
wr_filechksum[i] = 65535 - wr_filechksum[i] + 1;
|
|
|
|
/* Fast Read Command */
|
|
|
|
buf[0] = 0x00;
|
|
buf[1] = 0x07;
|
|
buf[2] = i;
|
|
buf[3] = 0x00;
|
|
buf[4] = 0x00;
|
|
buf[5] = ((len_in_blk - 1) >> 8) & 0xff;
|
|
buf[6] = (len_in_blk - 1) & 0xff;
|
|
ret = nt38350_write_reg(priv, NVT_I2C_HW_ADDRESS, buf, 7);
|
|
if (ret < 0)
|
|
{
|
|
ierr("ERROR: Fast Read Command error!!(%d)\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
/* Check 0xAA (Fast Read Command) */
|
|
|
|
retry = 0;
|
|
while (1)
|
|
{
|
|
usleep(NVT_DELAY_80MS);
|
|
buf[0] = 0x00;
|
|
buf[1] = 0x00;
|
|
ret = nt38350_read_reg(priv, NVT_I2C_HW_ADDRESS, buf, 2);
|
|
if (ret < 0)
|
|
{
|
|
ierr("ERROR: Check 0xAA (Fast Read Command)"
|
|
"error!!(%d)\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
if (buf[1] == 0xaa)
|
|
{
|
|
break;
|
|
}
|
|
|
|
retry++;
|
|
if (retry > 5)
|
|
{
|
|
ierr("ERROR: Check 0xAA (Fast Read Command)failed,"
|
|
"buf[1]=0x%02X,retry=%d\n", buf[1], retry);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/* Read Checksum (write addr high byte & middle byte) */
|
|
|
|
ret = nvt_set_page(priv, NVT_I2C_BLDR_ADDRESS, xdata_addr);
|
|
if (ret < 0)
|
|
{
|
|
ierr("ERROR: Read Checksum (write addr high byte &"
|
|
"middle byte)error!(%d)\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
/* Read Checksum */
|
|
|
|
buf[0] = (xdata_addr) & 0xff;
|
|
buf[1] = 0x00;
|
|
buf[2] = 0x00;
|
|
ret = nt38350_read_reg(priv, NVT_I2C_BLDR_ADDRESS, buf, 3);
|
|
if (ret < 0)
|
|
{
|
|
ierr("ERROR: Read Checksum error!!(%d)\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
rd_filechksum[i] = (uint16_t)((buf[2] << 8) | buf[1]);
|
|
if (wr_filechksum[i] != rd_filechksum[i])
|
|
{
|
|
ierr("ERROR: Verify Fail%d!!\n", i);
|
|
ierr("ERROR: rd_filechksum[%d]=0x%04X,"
|
|
"wr_filechksum[%d]=0x%04X\n",
|
|
i, rd_filechksum[i], i, wr_filechksum[i]);
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/***************************************************************************
|
|
*Description:
|
|
* Novatek touchscreen check firmware checksum function.
|
|
*
|
|
*return:
|
|
* Executive outcomes. 0---checksum not match.
|
|
* 1---checksum match. -1--- checksum read failed.
|
|
***************************************************************************/
|
|
|
|
static int nvt_check_checksum(FAR struct nt38350_dev_s *priv, uint8_t *data)
|
|
{
|
|
uint32_t xdata_addr = priv->mmap->read_flash_checksum_addr;
|
|
int32_t ret;
|
|
int32_t i;
|
|
int32_t k;
|
|
size_t len_in_blk;
|
|
int32_t retry;
|
|
uint8_t buf[64] =
|
|
{
|
|
0
|
|
};
|
|
|
|
uint16_t wr_filechksum[NVT_BLOCK_64KB_NUM] =
|
|
{
|
|
0
|
|
};
|
|
|
|
uint16_t rd_filechksum[NVT_BLOCK_64KB_NUM] =
|
|
{
|
|
0
|
|
};
|
|
|
|
if (nvt_resume_pd(priv))
|
|
{
|
|
ierr("ERROR: Resume PD error!!\n");
|
|
return -1;
|
|
}
|
|
|
|
for (i = 0; i < NVT_BLOCK_64KB_NUM; i++)
|
|
{
|
|
if (priv->fw_need_write_size > (i * NVT_SIZE_64KB))
|
|
{
|
|
/* Calculate wr_filechksum of each 64KB block */
|
|
|
|
len_in_blk = min(priv->fw_need_write_size -
|
|
i * NVT_SIZE_64KB, (size_t)NVT_SIZE_64KB);
|
|
wr_filechksum[i] = i + 0x00 + 0x00 +
|
|
(((len_in_blk - 1) >> 8) & 0xff) + ((len_in_blk - 1) & 0xff);
|
|
for (k = 0; k < len_in_blk; k++)
|
|
{
|
|
wr_filechksum[i] += data[k + i * NVT_SIZE_64KB];
|
|
}
|
|
|
|
wr_filechksum[i] = 65535 - wr_filechksum[i] + 1;
|
|
|
|
/* Fast Read Command */
|
|
|
|
buf[0] = 0x00;
|
|
buf[1] = 0x07;
|
|
buf[2] = i;
|
|
buf[3] = 0x00;
|
|
buf[4] = 0x00;
|
|
buf[5] = ((len_in_blk - 1) >> 8) & 0xff;
|
|
buf[6] = (len_in_blk - 1) & 0xff;
|
|
ret = nt38350_write_reg(priv, NVT_I2C_HW_ADDRESS, buf, 7);
|
|
if (ret < 0)
|
|
{
|
|
ierr("ERROR: Fast Read Command error!!(%d)\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
/* Check 0xAA (Fast Read Command) */
|
|
|
|
retry = 0;
|
|
while (1)
|
|
{
|
|
usleep(NVT_DELAY_80MS);
|
|
buf[0] = 0x00;
|
|
buf[1] = 0x00;
|
|
ret = nt38350_read_reg(priv, NVT_I2C_HW_ADDRESS, buf, 2);
|
|
if (ret < 0)
|
|
{
|
|
ierr("ERROR: Check 0xAA (Fast Read Command) "
|
|
"error!!(%d)\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
if (buf[1] == 0xaa)
|
|
{
|
|
break;
|
|
}
|
|
|
|
retry++;
|
|
|
|
if (retry > 5)
|
|
{
|
|
ierr("ERROR: Check 0xAA (Fast Read Command)failed,"
|
|
"buf[1]=0x%02X,retry=%d\n", buf[1], retry);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/* Read Checksum (write addr high byte & middle byte) */
|
|
|
|
ret = nvt_set_page(priv, NVT_I2C_BLDR_ADDRESS, xdata_addr);
|
|
if (ret < 0)
|
|
{
|
|
ierr("ERROR: Read Checksum (write addr high byte &"
|
|
"middle byte)error!(%d)\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
/* Read Checksum */
|
|
|
|
buf[0] = (xdata_addr) & 0xff;
|
|
buf[1] = 0x00;
|
|
buf[2] = 0x00;
|
|
ret = nt38350_read_reg(priv, NVT_I2C_BLDR_ADDRESS, buf, 3);
|
|
if (ret < 0)
|
|
{
|
|
ierr("ERROR: Read Checksum error!!(%d)\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
rd_filechksum[i] = (uint16_t)((buf[2] << 8) | buf[1]);
|
|
if (wr_filechksum[i] != rd_filechksum[i])
|
|
{
|
|
ierr("ERROR: rd_filechksum[%d]=0x%04X,"
|
|
"wr_filechksum[%d]=0x%04X\n",
|
|
i, rd_filechksum[i], i, wr_filechksum[i]);
|
|
ierr("ERROR: firmware checksum not match!!\n");
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/***************************************************************************
|
|
*Description:
|
|
* Novatek touchscreen request update firmware function.
|
|
*
|
|
*return:
|
|
* Executive outcomes. 0---succeed. -1,-22---failed.
|
|
***************************************************************************/
|
|
|
|
static int nvt_update_firmware_request(FAR struct nt38350_dev_s *priv,
|
|
FAR uint8_t *data)
|
|
{
|
|
size_t nvt_fw_bin_ver_offset;
|
|
size_t nvt_fw_bin_ver_bar_offset;
|
|
|
|
/* check FW need to write size */
|
|
|
|
if (nvt_get_fw_need_write_size(priv, data))
|
|
{
|
|
ierr("ERROR: get fw need to write size fail!\n");
|
|
return -1;
|
|
}
|
|
|
|
nvt_fw_bin_ver_offset = priv->fw_need_write_size - NVT_SIZE_4KB;
|
|
nvt_fw_bin_ver_bar_offset = nvt_fw_bin_ver_offset + 1;
|
|
|
|
/* check if FW version add FW version bar equals 0xFF */
|
|
|
|
if ((data[nvt_fw_bin_ver_offset]) +
|
|
(data[nvt_fw_bin_ver_bar_offset]) != 0xff)
|
|
{
|
|
ierr("ERROR: bin file FW_VER + FW_VER_BAR should be 0xFF!\n");
|
|
ierr("ERROR: FW_VER=0x%02X, FW_VER_BAR=0x%02X\n",
|
|
data[nvt_fw_bin_ver_offset],
|
|
data[nvt_fw_bin_ver_bar_offset]);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/***************************************************************************
|
|
*Description:
|
|
* Novatek touchscreen update firmware function.
|
|
*
|
|
*return:
|
|
* Executive outcomes. 0---succeed. negative---failed.
|
|
***************************************************************************/
|
|
|
|
static int nvt_update_firmware(FAR struct nt38350_dev_s *priv,
|
|
uint8_t *data)
|
|
{
|
|
int32_t ret;
|
|
|
|
/* ---Stop CRC check to prevent IC auto reboot--- */
|
|
|
|
nvt_stop_crc_reboot(priv);
|
|
|
|
/* Step 1 : initial bootloader */
|
|
|
|
ret = nvt_init_bootloader(priv);
|
|
if (ret)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
/* Step 2 : Resume PD */
|
|
|
|
ret = nvt_resume_pd(priv);
|
|
if (ret)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
/* Step 3 : Erase */
|
|
|
|
ret = nvt_erase_flash(priv);
|
|
if (ret)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
/* Step 4 : Program */
|
|
|
|
ret = nvt_write_flash(priv, data);
|
|
if (ret)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
/* Step 5 : Verify */
|
|
|
|
ret = nvt_verify_flash(priv, data);
|
|
if (ret)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
/* Step 6 : Bootloader Reset */
|
|
|
|
nvt_bootloader_reset(priv);
|
|
nvt_check_fw_reset_state(priv, RESET_STATE_INIT);
|
|
nvt_get_fw_info(priv);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/***************************************************************************
|
|
*Description:
|
|
* Novatek touchscreen check firmware version function.
|
|
*
|
|
*return:
|
|
* Executive outcomes. 0---need update. 1---need not
|
|
* update.
|
|
***************************************************************************/
|
|
|
|
static int nvt_check_fw_ver(FAR struct nt38350_dev_s *priv,
|
|
uint8_t *data)
|
|
{
|
|
int32_t ret;
|
|
size_t nvt_fw_bin_ver_offset = priv->fw_need_write_size - NVT_SIZE_4KB;
|
|
size_t nvt_fw_bin_ver_bar_offset = nvt_fw_bin_ver_offset + 1;
|
|
uint8_t buf[16] =
|
|
{
|
|
0
|
|
};
|
|
|
|
/* write i2c index to EVENT BUF ADDR */
|
|
|
|
ret = nvt_set_page(priv, NVT_I2C_BLDR_ADDRESS,
|
|
priv->mmap->event_buf_addr | EVENT_MAP_FWINFO);
|
|
if (ret < 0)
|
|
{
|
|
ierr("ERROR: i2c write error!(%d)\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
/* read Firmware Version */
|
|
|
|
buf[0] = EVENT_MAP_FWINFO;
|
|
buf[1] = 0x00;
|
|
buf[2] = 0x00;
|
|
ret = nt38350_read_reg(priv, NVT_I2C_BLDR_ADDRESS, buf, 3);
|
|
if (ret < 0)
|
|
{
|
|
ierr("ERROR: i2c read error!(%d)\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
#if NVT_DEBUG
|
|
iinfo("IC FW Ver = 0x%02X, FW Ver Bar = 0x%02X\n", buf[1], buf[2]);
|
|
iinfo("Bin FW Ver = 0x%02X, FW ver Bar = 0x%02X\n",
|
|
data[nvt_fw_bin_ver_offset],
|
|
data[nvt_fw_bin_ver_bar_offset]);
|
|
#endif
|
|
|
|
/* check IC FW_VER + FW_VER_BAR equals 0xFF or not,
|
|
* need to update if not
|
|
*/
|
|
|
|
if ((buf[1] + buf[2]) != 0xff)
|
|
{
|
|
ierr("ERROR: IC FW_VER + FW_VER_BAR not equals to 0xFF!\n");
|
|
return 0;
|
|
}
|
|
|
|
/* compare IC and binary FW version */
|
|
|
|
if (buf[1] > data[nvt_fw_bin_ver_offset])
|
|
{
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/***************************************************************************
|
|
*Description:
|
|
* Novatek touchscreen check flash end flag function.
|
|
*
|
|
*return:
|
|
* Executive outcomes. 0---succeed. 1,negative---failed.
|
|
***************************************************************************/
|
|
|
|
static int nvt_check_flash_end_flag(FAR struct nt38350_dev_s *priv)
|
|
{
|
|
int32_t ret;
|
|
size_t nvt_flash_end_flag_addr = priv->fw_need_write_size -
|
|
NVT_FLASH_END_FLAG_LEN;
|
|
uint8_t buf[8] =
|
|
{
|
|
0
|
|
};
|
|
|
|
uint8_t nvt_end_flag[NVT_FLASH_END_FLAG_LEN + 1] =
|
|
{
|
|
0
|
|
};
|
|
|
|
/* Step 1 : initial bootloader */
|
|
|
|
ret = nvt_init_bootloader(priv);
|
|
if (ret)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
/* Step 2 : Resume PD */
|
|
|
|
ret = nvt_resume_pd(priv);
|
|
if (ret)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
/* Step 3 : unlock */
|
|
|
|
buf[0] = 0x00;
|
|
buf[1] = 0x35;
|
|
ret = nt38350_write_reg(priv, NVT_I2C_HW_ADDRESS, buf, 2);
|
|
if (ret < 0)
|
|
{
|
|
ierr("ERROR: write unlock error!!(%d)\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
usleep(NVT_DELAY_10MS);
|
|
|
|
/* Step 4 : Flash Read Command */
|
|
|
|
buf[0] = 0x00;
|
|
buf[1] = 0x03;
|
|
buf[2] = (nvt_flash_end_flag_addr >> 16) & 0xff; /* Addr_H */
|
|
buf[3] = (nvt_flash_end_flag_addr >> 8) & 0xff; /* Addr_M */
|
|
buf[4] = nvt_flash_end_flag_addr & 0xff; /* Addr_L */
|
|
buf[5] = (NVT_FLASH_END_FLAG_LEN >> 8) & 0xff; /* Len_H */
|
|
buf[6] = NVT_FLASH_END_FLAG_LEN & 0xff; /* Len_L */
|
|
ret = nt38350_write_reg(priv, NVT_I2C_HW_ADDRESS, buf, 7);
|
|
if (ret < 0)
|
|
{
|
|
ierr("ERROR: write Read Command error!!(%d)\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
usleep(NVT_DELAY_10MS);
|
|
|
|
/* Check 0xAA (Read Command) */
|
|
|
|
buf[0] = 0x00;
|
|
buf[1] = 0x00;
|
|
ret = nt38350_read_reg(priv, NVT_I2C_HW_ADDRESS, buf, 2);
|
|
if (ret < 0)
|
|
{
|
|
ierr("ERROR: Check 0xaa (Read Command) error!!(%d)\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
if (buf[1] != 0xaa)
|
|
{
|
|
ierr("ERROR: Check 0xaa (Read Command) error!! status=0x%02X\n",
|
|
buf[1]);
|
|
return -1;
|
|
}
|
|
|
|
usleep(NVT_DELAY_10MS);
|
|
|
|
/* Step 5 : Read Flash Data */
|
|
|
|
ret = nvt_set_page(priv, NVT_I2C_BLDR_ADDRESS,
|
|
priv->mmap->read_flash_checksum_addr);
|
|
if (ret < 0)
|
|
{
|
|
ierr("ERROR: change index error!! (%d)\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
usleep(NVT_DELAY_10MS);
|
|
|
|
/* Read Back */
|
|
|
|
buf[0] = priv->mmap->read_flash_checksum_addr & 0xff;
|
|
ret = nt38350_read_reg(priv, NVT_I2C_BLDR_ADDRESS, buf, 6);
|
|
if (ret < 0)
|
|
{
|
|
ierr("ERROR: Read Back error!! (%d)\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
/* buf[3:5] => NVT End Flag */
|
|
|
|
strncpy(nvt_end_flag, &buf[3], NVT_FLASH_END_FLAG_LEN);
|
|
#if NVT_DEBUG
|
|
iinfo("nvt_end_flag=%s (%02X %02X %02X)\n",
|
|
nvt_end_flag, buf[3], buf[4], buf[5]);
|
|
#endif
|
|
|
|
if (strncmp(nvt_end_flag, "NVT", NVT_FLASH_END_FLAG_LEN) == 0)
|
|
{
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
ierr("ERROR: \"NVT\" end flag not found!\n");
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
/***************************************************************************
|
|
*Description:
|
|
* Novatek touchscreen update firmware when booting
|
|
* function.
|
|
*
|
|
*return:
|
|
* n.a.
|
|
***************************************************************************/
|
|
|
|
static int nvt_boot_update_firmware(FAR struct nt38350_dev_s *priv)
|
|
{
|
|
int32_t ret;
|
|
irqstate_t flags;
|
|
uint8_t *fw_data = NULL;
|
|
|
|
ret = get_nvt_fw_size(priv);
|
|
if (ret)
|
|
{
|
|
ierr("ERROR: Get nvt firmware size failed\n");
|
|
goto err;
|
|
}
|
|
|
|
fw_data = (uint8_t *)kmm_zalloc(priv->fw_size);
|
|
if (fw_data == NULL)
|
|
{
|
|
ierr("ERROR: Failed to malloc fw_data\n");
|
|
goto err;
|
|
}
|
|
|
|
ret = get_nvt_fw_content(priv, fw_data);
|
|
if (ret)
|
|
{
|
|
ierr("ERROR: Failed to get fimware content\n");
|
|
goto err;
|
|
}
|
|
|
|
ret = nvt_update_firmware_request(priv, fw_data);
|
|
if (ret)
|
|
{
|
|
ierr("ERROR: nvt_update_firmware_request failed. (%d)\n", ret);
|
|
goto err;
|
|
}
|
|
|
|
flags = enter_critical_section();
|
|
|
|
nvt_sw_reset_idle(priv);
|
|
|
|
ret = nvt_check_checksum(priv, fw_data);
|
|
|
|
/* read firmware checksum failed */
|
|
|
|
if (ret < 0)
|
|
{
|
|
iinfo("read firmware checksum failed\n");
|
|
nvt_update_firmware(priv, fw_data);
|
|
}
|
|
else if ((ret == 0) && (nvt_check_fw_ver(priv, fw_data) == 0))
|
|
{
|
|
/* (fw checksum not match) && (bin fw version >= ic fw version) */
|
|
|
|
iinfo("firmware version not match\n");
|
|
ret = nvt_update_firmware(priv, fw_data);
|
|
}
|
|
else if (nvt_check_flash_end_flag(priv))
|
|
{
|
|
iinfo("check flash end flag failed\n");
|
|
ret = nvt_update_firmware(priv, fw_data);
|
|
}
|
|
else
|
|
{
|
|
nvt_bootloader_reset(priv);
|
|
ret = nvt_check_fw_reset_state(priv, RESET_STATE_INIT);
|
|
if (ret)
|
|
{
|
|
ierr("ERROR: check fw reset state failed\n");
|
|
ret = nvt_update_firmware(priv, fw_data);
|
|
}
|
|
}
|
|
|
|
leave_critical_section(flags);
|
|
|
|
kmm_free(fw_data);
|
|
fw_data = NULL;
|
|
return ret;
|
|
|
|
err:
|
|
ierr("ERROR: Failed to update firmware\n");
|
|
kmm_free(fw_data);
|
|
fw_data = NULL;
|
|
return ret;
|
|
}
|
|
|
|
/***************************************************************************
|
|
*Description:
|
|
* Novatek touchscreen get firmware pipe function.
|
|
*
|
|
*return:
|
|
* Executive outcomes. 0---pipe 0. 1---pipe 1.
|
|
***************************************************************************/
|
|
|
|
static uint8_t nvt_get_fw_pipe(FAR struct nt38350_dev_s *priv)
|
|
{
|
|
uint8_t buf[2] =
|
|
{
|
|
0
|
|
};
|
|
|
|
/* ---set xdata index to EVENT BUF ADDR--- */
|
|
|
|
nvt_set_page(priv, NVT_I2C_FW_ADDRESS,
|
|
priv->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;
|
|
nt38350_read_reg(priv, NVT_I2C_FW_ADDRESS, buf, 2);
|
|
|
|
return (buf[1] & 0x01);
|
|
}
|
|
|
|
/***************************************************************************
|
|
*Description:
|
|
* Novatek touchscreen change mode function.
|
|
*
|
|
*return:
|
|
* n.a.
|
|
***************************************************************************/
|
|
|
|
static void nvt_change_mode(FAR struct nt38350_dev_s *priv, uint8_t mode)
|
|
{
|
|
uint8_t buf[8] =
|
|
{
|
|
0
|
|
};
|
|
|
|
/* ---set xdata index to EVENT BUF ADDR--- */
|
|
|
|
nvt_set_page(priv, NVT_I2C_FW_ADDRESS,
|
|
priv->mmap->event_buf_addr | EVENT_MAP_HOST_CMD);
|
|
|
|
/* ---set mode--- */
|
|
|
|
buf[0] = EVENT_MAP_HOST_CMD;
|
|
buf[1] = mode;
|
|
nt38350_write_reg(priv, NVT_I2C_FW_ADDRESS, buf, 2);
|
|
|
|
if (mode == NVT_NORMAL_MODE)
|
|
{
|
|
usleep(NVT_DELAY_20MS);
|
|
buf[0] = EVENT_MAP_HANDSHAKING_OR_SUB_CMD_BYTE;
|
|
buf[1] = NVT_HANDSHAKING_HOST_READY;
|
|
nt38350_write_reg(priv, NVT_I2C_FW_ADDRESS, buf, 2);
|
|
usleep(NVT_DELAY_20MS);
|
|
}
|
|
}
|
|
|
|
/***************************************************************************
|
|
*Description:
|
|
* Novatek touchscreen read meta data function.
|
|
*
|
|
*return:
|
|
* n.a.
|
|
***************************************************************************/
|
|
|
|
static void nvt_read_mdata(FAR struct nt38350_dev_s *priv,
|
|
uint32_t xdata_addr,
|
|
uint32_t xdata_btn_addr,
|
|
FAR int32_t *data)
|
|
{
|
|
int32_t i;
|
|
int32_t data_len;
|
|
int32_t loop_cnt;
|
|
int32_t residual_len;
|
|
uint32_t xdata_addr_tmp = xdata_addr;
|
|
uint8_t buf[NVT_BUS_TRANSFER_LENGTH + 1] =
|
|
{
|
|
0
|
|
};
|
|
|
|
/* ---set read data length--- */
|
|
|
|
data_len = priv->x_num * priv->y_num * 2;
|
|
loop_cnt = data_len / NVT_BUS_TRANSFER_LENGTH;
|
|
residual_len = data_len % NVT_BUS_TRANSFER_LENGTH;
|
|
|
|
#if NVT_DEBUG
|
|
iinfo("data_len=%d, loop_cnt=%d, residual_len=%d\n",
|
|
data_len, loop_cnt, residual_len);
|
|
#endif
|
|
|
|
/* read xdata : step 1 */
|
|
|
|
for (i = 0; i < loop_cnt; i++)
|
|
{
|
|
/* ---change xdata index--- */
|
|
|
|
nvt_set_page(priv, NVT_I2C_FW_ADDRESS,
|
|
(xdata_addr + i * NVT_BUS_TRANSFER_LENGTH));
|
|
|
|
/* ---read data--- */
|
|
|
|
buf[0] = ((xdata_addr + i * NVT_BUS_TRANSFER_LENGTH) & 0xff);
|
|
nt38350_read_reg(priv, NVT_I2C_FW_ADDRESS, buf,
|
|
NVT_BUS_TRANSFER_LENGTH + 1);
|
|
u8toi16((buf + 1 + i * NVT_BUS_TRANSFER_LENGTH),
|
|
data, NVT_BUS_TRANSFER_LENGTH);
|
|
}
|
|
|
|
/* read residual xdata : step2 */
|
|
|
|
if (residual_len != 0)
|
|
{
|
|
/* ---change xdata index--- */
|
|
|
|
nvt_set_page(priv, NVT_I2C_FW_ADDRESS, (xdata_addr_tmp +
|
|
loop_cnt * NVT_BUS_TRANSFER_LENGTH));
|
|
|
|
/* ---read data--- */
|
|
|
|
buf[0] = ((xdata_addr_tmp +
|
|
loop_cnt * NVT_BUS_TRANSFER_LENGTH) & 0xff);
|
|
nt38350_read_reg(priv, NVT_I2C_FW_ADDRESS, buf,
|
|
residual_len + 1);
|
|
u8toi16((buf + 1 + loop_cnt * NVT_BUS_TRANSFER_LENGTH),
|
|
data, data_len);
|
|
}
|
|
|
|
#if NVT_TOUCH_KEY_NUM > 0
|
|
|
|
/* read button xdata : step3
|
|
* ---change xdata index---
|
|
*/
|
|
|
|
nvt_set_page(priv, NVT_I2C_FW_ADDRESS, xdata_btn_addr);
|
|
|
|
/* ---read data--- */
|
|
|
|
buf[0] = (xdata_btn_addr & 0xff);
|
|
nt38350_read_reg(priv, NVT_I2C_FW_ADDRESS, buf,
|
|
(NVT_TOUCH_KEY_NUM * 2 + 1));
|
|
|
|
/* ---2bytes-to-1data--- */
|
|
|
|
for (i = 0; i < NVT_TOUCH_KEY_NUM; i++)
|
|
{
|
|
data[priv->x_num * priv->y_num + i] =
|
|
(int16_t)(buf[1 + i * 2] + 256 * buf[1 + i * 2 + 1]);
|
|
}
|
|
#endif
|
|
|
|
/* ---set xdata index to EVENT BUF ADDR--- */
|
|
|
|
nvt_set_page(priv, NVT_I2C_FW_ADDRESS,
|
|
priv->mmap->event_buf_addr);
|
|
}
|
|
|
|
/***************************************************************************
|
|
*Description:
|
|
* Novatek touchscreen nvt diff get function.
|
|
*
|
|
*return:
|
|
* Executive outcomes. 0---succeed. negative---failed.
|
|
***************************************************************************/
|
|
|
|
static int nvt_diff_get(FAR struct nt38350_dev_s *priv, int32_t *data)
|
|
{
|
|
irqstate_t flags;
|
|
int ret;
|
|
|
|
flags = enter_critical_section();
|
|
|
|
#if NVT_TOUCH_ESD_PROTECT
|
|
nvt_esd_check_enable(false);
|
|
#endif /* #if NVT_TOUCH_ESD_PROTECT */
|
|
|
|
if (nvt_clear_fw_status(priv))
|
|
{
|
|
ret = -EACCES;
|
|
goto err;
|
|
}
|
|
|
|
nvt_change_mode(priv, NVT_TEST_MODE_2);
|
|
|
|
if (nvt_check_fw_status(priv))
|
|
{
|
|
ret = -EACCES;
|
|
goto err;
|
|
}
|
|
|
|
if (nvt_get_fw_info(priv))
|
|
{
|
|
ret = -EACCES;
|
|
goto err;
|
|
}
|
|
|
|
if (nvt_get_fw_pipe(priv) == 0)
|
|
{
|
|
nvt_read_mdata(priv, priv->mmap->raw_pipe0_addr,
|
|
priv->mmap->diff_btn_pipe0_addr, data);
|
|
}
|
|
else
|
|
{
|
|
nvt_read_mdata(priv, priv->mmap->raw_pipe1_addr,
|
|
priv->mmap->diff_btn_pipe1_addr, data);
|
|
}
|
|
|
|
nvt_change_mode(priv, NVT_NORMAL_MODE);
|
|
|
|
leave_critical_section(flags);
|
|
return 0;
|
|
|
|
err:
|
|
leave_critical_section(flags);
|
|
return ret;
|
|
}
|
|
|
|
static int nvt_diff_show(FAR struct nt38350_dev_s *priv,
|
|
FAR struct nvt_diff_s *diff)
|
|
{
|
|
int ret;
|
|
int data_len = priv->x_num * priv->y_num;
|
|
int32_t read_bufx[30] =
|
|
{
|
|
0
|
|
};
|
|
|
|
diff->x_num = priv->x_num;
|
|
diff->y_num = priv->y_num;
|
|
|
|
ret = nvt_diff_get(priv, read_bufx);
|
|
if (ret == 0)
|
|
{
|
|
memcpy(diff->data, read_bufx, data_len);
|
|
}
|
|
else
|
|
{
|
|
ierr("ERROR: nvt diff show failed\n");
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef CONFIG_NVT_OFFLINE_LOG
|
|
static void nvt_log_data_to_csv(FAR void *arg)
|
|
{
|
|
int ret;
|
|
int32_t x = 0;
|
|
int32_t y = 0;
|
|
int32_t write_ret;
|
|
char date_buf[64];
|
|
char *fbufp = NULL;
|
|
struct file *fp = NULL;
|
|
int32_t iarrayindex = 0;
|
|
int32_t fw_frame_cnt = 0;
|
|
uint32_t output_len = 0;
|
|
uint32_t input_x1 = 0;
|
|
uint32_t input_x2 = 0;
|
|
uint32_t input_y1 = 0;
|
|
uint32_t input_y2 = 0;
|
|
char *csv_file_path = "/mnt/data/NVTLogData.csv";
|
|
FAR struct nt38350_dev_s *priv = (FAR struct nt38350_dev_s *)arg;
|
|
struct tm tm =
|
|
{
|
|
};
|
|
|
|
struct timespec ts =
|
|
{
|
|
};
|
|
|
|
fbufp = (char *)kmm_zalloc(8192);
|
|
if (!fbufp)
|
|
{
|
|
ierr("ERROR: kmm_zalloc for fbufp failed!\n");
|
|
return;
|
|
}
|
|
|
|
/* open csv file */
|
|
|
|
fp = open(csv_file_path, O_RDWR | O_CREAT);
|
|
if (fp == NULL || fp < 0)
|
|
{
|
|
ierr("ERROR: open %s failed\n", csv_file_path);
|
|
if (fbufp)
|
|
{
|
|
kmm_free(fbufp);
|
|
fbufp = NULL;
|
|
}
|
|
}
|
|
|
|
/* saved header info to csv file */
|
|
|
|
ret = read(fp, fbufp, 1);
|
|
if (fbufp[0] != 'L')
|
|
{
|
|
sprintf(fbufp, "LogVer:''2.0'',TLVer:''Driver'',ChipID:''38350'',"
|
|
"FWVer:''%2X'',Xch:''%2d'',Ych:''%2d'',Xresolution:''%4d'',"
|
|
"Yresolution:''%4d'',FingerNum:''2'',ButtonNum:''0'',"
|
|
"PIDVer:''%4X'',Frame_Info:''0''\r\n",
|
|
priv->fw_ver, priv->x_num, priv->y_num,
|
|
priv->abs_x_max, priv->abs_y_max, priv->nvt_pid);
|
|
write_ret = write(fp, fbufp, 192);
|
|
}
|
|
|
|
/* Formatted data such as below:
|
|
* Time(hh:mm:ss:ms), fw_frame_cnt, finger1 Coord_X, finger1 Coord_Y,
|
|
* finger2_Coord_X, finger2_CoordY 2D data
|
|
*/
|
|
|
|
clock_gettime(CLOCK_REALTIME, &ts);
|
|
|
|
localtime_r(&ts.tv_sec, &tm);
|
|
|
|
ret = strftime(date_buf, 64, "%e/%m/%Y %H:%M:%S", &tm);
|
|
|
|
input_x1 = (uint32_t)((priv->point_xdata_temp[1]) << 4) +
|
|
(uint32_t) ((priv->point_xdata_temp[3]) >> 4);
|
|
input_y1 = (uint32_t)((priv->point_xdata_temp[2]) << 4) +
|
|
(uint32_t) ((priv->point_xdata_temp[3]) & 0x0f);
|
|
input_x2 = (uint32_t)((priv->point_xdata_temp[7]) << 4) +
|
|
(uint32_t) ((priv->point_xdata_temp[9]) >> 4);
|
|
input_y2 = (uint32_t)((priv->point_xdata_temp[8]) << 4) +
|
|
(uint32_t) ((priv->point_xdata_temp[9]) & 0x0f);
|
|
fw_frame_cnt = (int32_t)((priv->point_xdata_temp[89]) +
|
|
256 * priv->point_xdata_temp[90]);
|
|
|
|
sprintf(fbufp, "timestamp: %s,Frame index: %5d ,"
|
|
"RawData: ,%2X,%4X,%5d,%5d,%5d,%5d\r\n",
|
|
date_buf, fw_frame_cnt, priv->fw_ver,
|
|
priv->nvt_pid, input_x1, input_y1, input_x2, input_y2);
|
|
for (y = 0; y < priv->y_num; y++)
|
|
{
|
|
for (x = 0; x < priv->x_num; x++)
|
|
{
|
|
iarrayindex = y * priv->x_num + x;
|
|
|
|
/* 2 bytes to 1 byte */
|
|
|
|
sprintf(fbufp + 93 + iarrayindex * 7 + y * 2, "%5d, ",
|
|
(int16_t)(priv->point_xdata_temp[NVT_POINT_DATA_LEN +
|
|
iarrayindex * 2] + 256 *
|
|
priv->point_xdata_temp[NVT_POINT_DATA_LEN +
|
|
iarrayindex * 2 + 1]));
|
|
}
|
|
|
|
sprintf(fbufp + 93 + (iarrayindex + 1) * 7 + y * 2, "\r\n");
|
|
}
|
|
|
|
sprintf(fbufp + 93 + (iarrayindex + 1) * 7 + (y * 2), "\r\n");
|
|
|
|
#if NVT_DEBUG
|
|
iinfo("%s", fbufp);
|
|
#endif
|
|
|
|
/* Saved data to csv file */
|
|
|
|
output_len = 93 + priv->y_num * priv->x_num * 7 +
|
|
priv->y_num * 2 + 2;
|
|
|
|
lseek(fp, 0, SEEK_END);
|
|
|
|
write_ret = write(fp, fbufp, output_len);
|
|
|
|
if (write_ret <= 0)
|
|
{
|
|
ierr("write %s failed\n", csv_file_path);
|
|
if (fp)
|
|
{
|
|
close(fp);
|
|
fp = NULL;
|
|
}
|
|
|
|
if (fbufp)
|
|
{
|
|
kmm_free(fbufp);
|
|
fbufp = NULL;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if (fp)
|
|
{
|
|
close(fp);
|
|
fp = NULL;
|
|
}
|
|
|
|
if (fbufp)
|
|
{
|
|
kmm_free(fbufp);
|
|
fbufp = NULL;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef CONFIG_NVT_TOUCH_MP
|
|
static void nvt_print_data_log_in_one_line(int32_t *data,
|
|
int32_t data_num)
|
|
{
|
|
char *tmp_log = NULL;
|
|
int32_t i = 0;
|
|
|
|
tmp_log = (char *)kmm_zalloc(data_num * 7 + 1);
|
|
if (!tmp_log)
|
|
{
|
|
ierr("ERROR: kzalloc for tmp_log failed!\n ");
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < data_num; i++)
|
|
{
|
|
sprintf(tmp_log + i * 7, "%5d, ", data[i]);
|
|
}
|
|
|
|
tmp_log[data_num * 7] = '\0';
|
|
|
|
iinfo("%s \n", tmp_log);
|
|
|
|
if (tmp_log)
|
|
{
|
|
kmm_free(tmp_log);
|
|
tmp_log = NULL;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static int8_t nvt_switch_freqhopendis(FAR struct nt38350_dev_s *priv,
|
|
uint8_t freqhopendis)
|
|
{
|
|
uint8_t retry = 0;
|
|
int8_t ret = 0;
|
|
uint8_t buf[8] =
|
|
{
|
|
0
|
|
};
|
|
|
|
for (retry = 0; retry < 20; retry++)
|
|
{
|
|
/* ---set xdata index to EVENT BUF ADDR--- */
|
|
|
|
nvt_set_page(priv, NVT_I2C_FW_ADDRESS, priv->mmap->event_buf_addr |
|
|
EVENT_MAP_HOST_CMD);
|
|
|
|
/* ---switch FreqHopEnDis--- */
|
|
|
|
buf[0] = EVENT_MAP_HOST_CMD;
|
|
buf[1] = freqhopendis;
|
|
nt38350_write_reg(priv, NVT_I2C_FW_ADDRESS, buf, 2);
|
|
|
|
usleep(NVT_DELAY_35MS);
|
|
|
|
buf[0] = EVENT_MAP_HOST_CMD;
|
|
buf[1] = 0xff;
|
|
nt38350_read_reg(priv, NVT_I2C_FW_ADDRESS, buf, 2);
|
|
|
|
if (buf[1] == 0x00)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (retry == 20)
|
|
{
|
|
ierr("ERROR: switch FreqHopEnDis 0x%02X failed, buf[1]=0x%02X\n",
|
|
freqhopendis, buf[1]);
|
|
ret = -1;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int32_t nvt_save_rawdata_to_csv(int32_t *rawdata,
|
|
uint8_t x_ch,
|
|
uint8_t y_ch,
|
|
const char *file_path,
|
|
uint32_t offset)
|
|
{
|
|
char *fbufp = NULL;
|
|
int32_t x = 0;
|
|
int32_t y = 0;
|
|
int32_t iarrayindex = 0;
|
|
int32_t write_ret = 0;
|
|
uint32_t output_len = 0;
|
|
struct file *fp = NULL;
|
|
|
|
fbufp = (char *)kmm_zalloc(8192);
|
|
if (!fbufp)
|
|
{
|
|
ierr("ERROR: kzalloc for fbufp failed!\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
for (y = 0; y < y_ch; y++)
|
|
{
|
|
for (x = 0; x < x_ch; x++)
|
|
{
|
|
iarrayindex = y * x_ch + x;
|
|
sprintf(fbufp + iarrayindex * 7 + y * 2, "%5d, ",
|
|
rawdata[iarrayindex]);
|
|
}
|
|
|
|
nvt_print_data_log_in_one_line(rawdata + y * x_ch, x_ch);
|
|
sprintf(fbufp + (iarrayindex + 1) * 7 + y * 2, "\r\n");
|
|
}
|
|
|
|
if (offset == 0)
|
|
{
|
|
fp = open(file_path, O_RDWR | O_CREAT | O_TRUNC);
|
|
}
|
|
else
|
|
{
|
|
fp = open(file_path, O_RDWR | O_CREAT);
|
|
}
|
|
|
|
if (fp == NULL)
|
|
{
|
|
ierr("ERROR: open %s failed\n", file_path);
|
|
if (fbufp)
|
|
{
|
|
kmm_free(fbufp);
|
|
fbufp = NULL;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
output_len = y_ch * x_ch * 7 + y_ch * 2;
|
|
|
|
if (offset > 0)
|
|
lseek(fp, 0, SEEK_END);
|
|
else
|
|
lseek(fp, 0, SEEK_SET);
|
|
|
|
write_ret = write(fp, fbufp, output_len);
|
|
if (write_ret <= 0)
|
|
{
|
|
ierr("ERROR: write %s failed\n", file_path);
|
|
if (fp)
|
|
{
|
|
close(fp);
|
|
fp = NULL;
|
|
}
|
|
|
|
if (fbufp)
|
|
{
|
|
kmm_free(fbufp);
|
|
fbufp = NULL;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
if (fp)
|
|
{
|
|
close(fp);
|
|
fp = NULL;
|
|
}
|
|
|
|
if (fbufp)
|
|
{
|
|
kmm_free(fbufp);
|
|
fbufp = NULL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void nvt_enable_short_test(FAR struct nt38350_dev_s *priv)
|
|
{
|
|
uint8_t buf[8] =
|
|
{
|
|
0
|
|
};
|
|
|
|
/* ---set xdata index to EVENT BUF ADDR--- */
|
|
|
|
nvt_set_page(priv, NVT_I2C_FW_ADDRESS, priv->mmap->event_buf_addr |
|
|
EVENT_MAP_HOST_CMD);
|
|
|
|
/* ---enable short test--- */
|
|
|
|
buf[0] = EVENT_MAP_HOST_CMD;
|
|
buf[1] = 0x43;
|
|
buf[2] = 0xaa;
|
|
buf[3] = 0x02;
|
|
buf[4] = 0x00;
|
|
nt38350_write_reg(priv, NVT_I2C_FW_ADDRESS, buf, 5);
|
|
}
|
|
|
|
static int32_t nvt_polling_hand_shake_status(FAR struct nt38350_dev_s *priv)
|
|
{
|
|
int32_t i = 0;
|
|
const int32_t retry = 250;
|
|
uint8_t buf[8] =
|
|
{
|
|
0
|
|
};
|
|
|
|
usleep(NVT_DELAY_20MS);
|
|
|
|
for (i = 0; i < retry; i++)
|
|
{
|
|
/* ---set xdata index to EVENT BUF ADDR--- */
|
|
|
|
nvt_set_page(priv, NVT_I2C_FW_ADDRESS, priv->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;
|
|
nt38350_read_reg(priv, NVT_I2C_FW_ADDRESS, buf, 2);
|
|
|
|
if ((buf[1] == 0xa0) || (buf[1] == 0xa1))
|
|
{
|
|
break;
|
|
}
|
|
|
|
usleep(NVT_DELAY_10MS);
|
|
}
|
|
|
|
if (i >= retry)
|
|
{
|
|
ierr("ERROR: polling hand shake status failed, "
|
|
"buf[1]=0x%02X\n", buf[1]);
|
|
|
|
/* Read back 5 bytes from offset EVENT_MAP_HOST_CMD for debug check */
|
|
|
|
nvt_set_page(priv, NVT_I2C_FW_ADDRESS, priv->mmap->event_buf_addr |
|
|
EVENT_MAP_HOST_CMD);
|
|
|
|
buf[0] = EVENT_MAP_HOST_CMD;
|
|
buf[1] = 0x00;
|
|
buf[2] = 0x00;
|
|
buf[3] = 0x00;
|
|
buf[4] = 0x00;
|
|
buf[5] = 0x00;
|
|
nt38350_read_reg(priv, NVT_I2C_FW_ADDRESS, buf, 6);
|
|
ierr("ERROR: Read back 5 bytes from offset EVENT_MAP_HOST_CMD: "
|
|
"0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X\n",
|
|
buf[1], buf[2], buf[3], buf[4], buf[5]);
|
|
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static int32_t nvt_read_fw_short(FAR struct nt38350_dev_s *priv,
|
|
FAR struct nvt_mp_test_s *mp)
|
|
{
|
|
uint32_t raw_pipe_addr = 0;
|
|
uint8_t *rawdata_buf = NULL;
|
|
uint32_t x = 0;
|
|
uint32_t y = 0;
|
|
int32_t iarrayindex = 0;
|
|
uint8_t buf[128] =
|
|
{
|
|
0
|
|
};
|
|
|
|
/* ---Enter Test Mode--- */
|
|
|
|
if (nvt_clear_fw_status(priv))
|
|
{
|
|
return -EAGAIN;
|
|
}
|
|
|
|
nvt_enable_short_test(priv);
|
|
|
|
if (nvt_polling_hand_shake_status(priv))
|
|
{
|
|
return -EAGAIN;
|
|
}
|
|
|
|
rawdata_buf = (uint8_t *)kmm_zalloc(NVT_X_CHANNEL * NVT_Y_CHANNEL * 2);
|
|
if (!rawdata_buf)
|
|
{
|
|
ierr("ERROR: kzalloc for rawdata_buf failed!\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
if (nvt_get_fw_pipe(priv) == 0)
|
|
{
|
|
raw_pipe_addr = priv->mmap->raw_pipe0_addr;
|
|
}
|
|
else
|
|
{
|
|
raw_pipe_addr = priv->mmap->raw_pipe1_addr;
|
|
}
|
|
|
|
for (y = 0; y < NVT_Y_CHANNEL; y++)
|
|
{
|
|
/* ---change xdata index--- */
|
|
|
|
nvt_set_page(priv, NVT_I2C_FW_ADDRESS,
|
|
raw_pipe_addr + y * NVT_X_CHANNEL * 2);
|
|
buf[0] = (uint8_t)((raw_pipe_addr + y * NVT_X_CHANNEL * 2) & 0xff);
|
|
nt38350_read_reg(priv, NVT_I2C_FW_ADDRESS, buf,
|
|
NVT_X_CHANNEL * 2 + 1);
|
|
memcpy(rawdata_buf + y * NVT_X_CHANNEL * 2,
|
|
buf + 1, NVT_X_CHANNEL * 2);
|
|
}
|
|
|
|
for (y = 0; y < NVT_Y_CHANNEL; y++)
|
|
{
|
|
for (x = 0; x < NVT_X_CHANNEL; x++)
|
|
{
|
|
iarrayindex = y * NVT_X_CHANNEL + x;
|
|
mp->raw_data[iarrayindex] = (int16_t)(rawdata_buf[iarrayindex * 2]
|
|
+ 256 * rawdata_buf[iarrayindex * 2 + 1]);
|
|
}
|
|
}
|
|
|
|
if (rawdata_buf)
|
|
{
|
|
kmm_free(rawdata_buf);
|
|
rawdata_buf = NULL;
|
|
}
|
|
|
|
/* ---Leave Test Mode--- */
|
|
|
|
nvt_change_mode(priv, NVT_NORMAL_MODE);
|
|
|
|
/* Save Rawdata to CSV file */
|
|
|
|
if (nvt_save_rawdata_to_csv(mp->raw_data, NVT_X_CHANNEL, NVT_Y_CHANNEL,
|
|
mp->test_item, 0) < 0)
|
|
{
|
|
ierr("ERROR: save rawdata to CSV file failed\n");
|
|
return -EAGAIN;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int32_t rawdatatest_singlepoint_sub(FAR int32_t raw_data[],
|
|
uint8_t record_result[],
|
|
uint8_t x_ch, uint8_t y_ch,
|
|
const int32_t rawdata_limit_postive[],
|
|
const int32_t rawdata_limit_negative[])
|
|
{
|
|
int32_t i = 0;
|
|
int32_t j = 0;
|
|
int32_t iarrayindex = 0;
|
|
bool ispass = true;
|
|
|
|
for (j = 0; j < y_ch; j++)
|
|
{
|
|
for (i = 0; i < x_ch; i++)
|
|
{
|
|
iarrayindex = j * x_ch + i;
|
|
|
|
record_result[iarrayindex] = 0x00; /* default value for pass */
|
|
|
|
if (raw_data[iarrayindex] > rawdata_limit_postive[iarrayindex])
|
|
{
|
|
record_result[iarrayindex] |= 0x01;
|
|
}
|
|
|
|
if (raw_data[iarrayindex] < rawdata_limit_negative[iarrayindex])
|
|
{
|
|
record_result[iarrayindex] |= 0x02;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* ---Check RecordResult--- */
|
|
|
|
for (j = 0; j < y_ch; j++)
|
|
{
|
|
for (i = 0; i < x_ch; i++)
|
|
{
|
|
if (record_result[j * x_ch + i] != 0)
|
|
{
|
|
ispass = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ispass == false)
|
|
{
|
|
return -1; /* FAIL */
|
|
}
|
|
else
|
|
{
|
|
return 0; /* PASS */
|
|
}
|
|
}
|
|
|
|
static int32_t nvt_read_baseline(FAR struct nt38350_dev_s *priv,
|
|
FAR struct nvt_mp_test_s *mp)
|
|
{
|
|
int32_t iarrayindex = 0;
|
|
|
|
nvt_read_mdata(priv, priv->mmap->baseline_addr,
|
|
priv->mmap->baseline_btn_addr, mp->raw_data);
|
|
|
|
/* Save Rawdata to CSV file */
|
|
|
|
if (nvt_save_rawdata_to_csv(mp->raw_data, NVT_X_CHANNEL,
|
|
NVT_Y_CHANNEL, mp->test_item, 0) < 0)
|
|
{
|
|
ierr("ERROR: save rawdata to CSV file failed\n");
|
|
return -EAGAIN;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int32_t nvt_read_cc(FAR struct nt38350_dev_s *priv,
|
|
FAR struct nvt_mp_test_s *mp)
|
|
{
|
|
if (nvt_get_fw_pipe(priv) == 0)
|
|
{
|
|
nvt_read_mdata(priv, priv->mmap->diff_pipe1_addr,
|
|
priv->mmap->diff_btn_pipe1_addr, mp->raw_data);
|
|
}
|
|
else
|
|
{
|
|
nvt_read_mdata(priv, priv->mmap->diff_pipe0_addr,
|
|
priv->mmap->diff_btn_pipe0_addr, mp->raw_data);
|
|
}
|
|
|
|
/* Save Rawdata to CSV file */
|
|
|
|
if (nvt_save_rawdata_to_csv(mp->raw_data, NVT_X_CHANNEL,
|
|
NVT_Y_CHANNEL, mp->test_item, 0) < 0)
|
|
{
|
|
ierr("ERROR: save rawdata to CSV file failed\n");
|
|
return -EAGAIN;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void nvt_enable_noise_collect(FAR struct nt38350_dev_s *priv,
|
|
int32_t frame_num)
|
|
{
|
|
uint8_t buf[8] =
|
|
{
|
|
0
|
|
};
|
|
|
|
nvt_set_page(priv, NVT_I2C_FW_ADDRESS, priv->mmap->event_buf_addr |
|
|
EVENT_MAP_HOST_CMD);
|
|
|
|
buf[0] = EVENT_MAP_HOST_CMD;
|
|
buf[1] = 0x47;
|
|
buf[2] = 0xaa;
|
|
buf[3] = frame_num;
|
|
buf[4] = 0x00;
|
|
nt38350_write_reg(priv, NVT_I2C_FW_ADDRESS, buf, 5);
|
|
}
|
|
|
|
static int32_t nvt_read_fw_noise(FAR struct nt38350_dev_s *priv,
|
|
FAR struct nvt_mp_test_s *mp,
|
|
FAR int *diffmax_flag,
|
|
FAR int *diffmin_flag)
|
|
{
|
|
uint32_t x = 0;
|
|
uint32_t y = 0;
|
|
int32_t iarrayindex = 0;
|
|
int32_t frame_num = 0;
|
|
uint32_t rawdata_diff_min_offset = 0;
|
|
int32_t *rawdata_diff_max;
|
|
int32_t *rawdata_diff_min;
|
|
|
|
/* ---Enter Test Mode--- */
|
|
|
|
rawdata_diff_max = (int8_t *)kmm_zalloc(NVT_RAWDATA_BUFSIZE);
|
|
if (!rawdata_diff_max)
|
|
{
|
|
ierr("ERROR: kzalloc for rawdata_diff_max failed!\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
rawdata_diff_min = (int8_t *)kmm_zalloc(NVT_RAWDATA_BUFSIZE);
|
|
if (!rawdata_diff_min)
|
|
{
|
|
ierr("ERROR: kzalloc for rawdata_diff_min failed!\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
if (nvt_clear_fw_status(priv))
|
|
{
|
|
return -EAGAIN;
|
|
}
|
|
|
|
frame_num = PS_CONFIG_DIFF_TEST_FRAME / 10;
|
|
if (frame_num <= 0)
|
|
{
|
|
frame_num = 1;
|
|
}
|
|
|
|
nvt_enable_noise_collect(priv, frame_num);
|
|
|
|
/* need wait PS_Config_Diff_Test_Frame * 8.3ms */
|
|
|
|
usleep(frame_num * NVT_DELAY_83MS);
|
|
|
|
if (nvt_polling_hand_shake_status(priv))
|
|
{
|
|
return -EAGAIN;
|
|
}
|
|
|
|
if (nvt_get_fw_pipe(priv) == 0)
|
|
{
|
|
nvt_read_mdata(priv, priv->mmap->diff_pipe0_addr,
|
|
priv->mmap->diff_btn_pipe0_addr, mp->raw_data);
|
|
}
|
|
else
|
|
{
|
|
nvt_read_mdata(priv, priv->mmap->diff_pipe1_addr,
|
|
priv->mmap->diff_btn_pipe1_addr, mp->raw_data);
|
|
}
|
|
|
|
for (y = 0; y < priv->y_num; y++)
|
|
{
|
|
for (x = 0; x < priv->x_num; x++)
|
|
{
|
|
iarrayindex = y * priv->x_num + x;
|
|
rawdata_diff_max[iarrayindex] =
|
|
(int8_t)((mp->raw_data[iarrayindex] >> 8) & 0xff);
|
|
rawdata_diff_min[iarrayindex] =
|
|
(int8_t)(mp->raw_data[iarrayindex] & 0xff);
|
|
}
|
|
}
|
|
|
|
/* ---Leave Test Mode--- */
|
|
|
|
nvt_change_mode(priv, NVT_NORMAL_MODE);
|
|
|
|
/* Save Rawdata to CSV file */
|
|
|
|
if (nvt_save_rawdata_to_csv(rawdata_diff_max, priv->x_num, priv->y_num,
|
|
mp->test_item, 0) < 0)
|
|
{
|
|
ierr("ERROR: save rawdata to CSV file failed\n");
|
|
return -EAGAIN;
|
|
}
|
|
|
|
rawdata_diff_min_offset = priv->y_num * priv->x_num * 7 +
|
|
priv->y_num * 2;
|
|
|
|
/* Save Rawdata to CSV file */
|
|
|
|
if (nvt_save_rawdata_to_csv(rawdata_diff_min, priv->x_num, priv->y_num,
|
|
mp->test_item, rawdata_diff_min_offset) < 0)
|
|
{
|
|
ierr("ERROR: save rawdata to CSV file failed\n");
|
|
return -EAGAIN;
|
|
}
|
|
|
|
*diffmax_flag = rawdatatest_singlepoint_sub(rawdata_diff_max,
|
|
mp->record_result, NVT_X_CHANNEL, NVT_Y_CHANNEL,
|
|
ps_config_lmt_fw_diff_p, ps_config_lmt_fw_diff_n);
|
|
|
|
*diffmin_flag = rawdatatest_singlepoint_sub(rawdata_diff_min,
|
|
mp->record_result, NVT_X_CHANNEL, NVT_Y_CHANNEL,
|
|
ps_config_lmt_fw_diff_p, ps_config_lmt_fw_diff_n);
|
|
if (rawdata_diff_max)
|
|
kmm_free(rawdata_diff_max);
|
|
|
|
if (rawdata_diff_min)
|
|
kmm_free(rawdata_diff_min);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void nvt_enable_open_test(FAR struct nt38350_dev_s *priv)
|
|
{
|
|
uint8_t buf[5] =
|
|
{
|
|
0
|
|
};
|
|
|
|
/* ---set xdata index to EVENT BUF ADDR--- */
|
|
|
|
nvt_set_page(priv, NVT_I2C_FW_ADDRESS, priv->mmap->event_buf_addr |
|
|
EVENT_MAP_HOST_CMD);
|
|
|
|
/* ---enable open test--- */
|
|
|
|
buf[0] = EVENT_MAP_HOST_CMD;
|
|
buf[1] = 0x45;
|
|
buf[2] = 0xaa;
|
|
buf[3] = 0x02;
|
|
buf[4] = 0x00;
|
|
nt38350_write_reg(priv, NVT_I2C_FW_ADDRESS, buf, 5);
|
|
}
|
|
|
|
static int32_t nvt_read_fw_open(FAR struct nt38350_dev_s *priv,
|
|
FAR struct nvt_mp_test_s *mp)
|
|
{
|
|
uint32_t raw_pipe_addr = 0;
|
|
uint8_t *rawdata_buf = NULL;
|
|
uint32_t x = 0;
|
|
uint32_t y = 0;
|
|
uint8_t buf[128] =
|
|
{
|
|
0
|
|
};
|
|
|
|
/* ---Enter Test Mode--- */
|
|
|
|
if (nvt_clear_fw_status(priv))
|
|
{
|
|
return -EAGAIN;
|
|
}
|
|
|
|
nvt_enable_open_test(priv);
|
|
|
|
if (nvt_polling_hand_shake_status(priv))
|
|
{
|
|
return -EAGAIN;
|
|
}
|
|
|
|
rawdata_buf = (uint8_t *)kmm_zalloc(NVT_IC_X_CFG_SIZE *
|
|
NVT_IC_Y_CFG_SIZE * 2);
|
|
if (!rawdata_buf)
|
|
{
|
|
ierr("ERROR: kzalloc for rawdata_buf failed!\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
if (nvt_get_fw_pipe(priv) == 0)
|
|
{
|
|
raw_pipe_addr = priv->mmap->raw_pipe0_addr;
|
|
}
|
|
else
|
|
{
|
|
raw_pipe_addr = priv->mmap->raw_pipe1_addr;
|
|
}
|
|
|
|
for (y = 0; y < NVT_IC_Y_CFG_SIZE; y++)
|
|
{
|
|
/* ---change xdata index--- */
|
|
|
|
nvt_set_page(priv, NVT_I2C_FW_ADDRESS, raw_pipe_addr +
|
|
y * NVT_IC_X_CFG_SIZE * 2);
|
|
buf[0] = (uint8_t)((raw_pipe_addr +
|
|
y * NVT_IC_X_CFG_SIZE * 2) & 0xff);
|
|
nt38350_read_reg(priv, NVT_I2C_FW_ADDRESS, buf,
|
|
NVT_IC_X_CFG_SIZE * 2 + 1);
|
|
memcpy(rawdata_buf + y * NVT_IC_X_CFG_SIZE * 2,
|
|
buf + 1, NVT_IC_X_CFG_SIZE * 2);
|
|
}
|
|
|
|
for (y = 0; y < NVT_IC_Y_CFG_SIZE; y++)
|
|
{
|
|
for (x = 0; x < NVT_IC_X_CFG_SIZE; x++)
|
|
{
|
|
if ((AIN_Y[y] != 0xff) && (AIN_X[x] != 0xff))
|
|
{
|
|
mp->raw_data[AIN_Y[y] * NVT_X_CHANNEL + AIN_X[x]] =
|
|
(int16_t)((rawdata_buf[(y * NVT_IC_X_CFG_SIZE + x) * 2] +
|
|
256 * rawdata_buf[(y * NVT_IC_X_CFG_SIZE + x) * 2 + 1]));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (rawdata_buf)
|
|
{
|
|
kmm_free(rawdata_buf);
|
|
rawdata_buf = NULL;
|
|
}
|
|
|
|
/* ---Leave Test Mode--- */
|
|
|
|
nvt_change_mode(priv, NVT_NORMAL_MODE);
|
|
|
|
/* Save RawData to CSV file */
|
|
|
|
if (nvt_save_rawdata_to_csv(mp->raw_data, NVT_X_CHANNEL,
|
|
NVT_Y_CHANNEL, mp->test_item, 0) < 0)
|
|
{
|
|
ierr("ERROR: save rawdata to CSV file failed\n");
|
|
return -EAGAIN;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void nvt_selftest(FAR struct nt38350_dev_s *priv,
|
|
FAR struct nvt_mp_test_s *mp)
|
|
{
|
|
if (nvt_check_fw_reset_state(priv, RESET_STATE_REK))
|
|
{
|
|
ierr("check fw reset state failed!\n");
|
|
}
|
|
|
|
if (nvt_switch_freqhopendis(priv, NVT_FREQ_HOP_DISABLE))
|
|
{
|
|
ierr("switch frequency hopping disable failed!\n");
|
|
}
|
|
|
|
if (nvt_check_fw_reset_state(priv, RESET_STATE_NORMAL_RUN))
|
|
{
|
|
ierr("check fw reset state failed!\n");
|
|
}
|
|
|
|
usleep(NVT_DELAY_100MS);
|
|
|
|
if (nvt_clear_fw_status(priv))
|
|
{
|
|
ierr("clear fw status failed!\n");
|
|
}
|
|
|
|
nvt_change_mode(priv, NVT_MP_MODE_CC);
|
|
|
|
if (nvt_check_fw_status(priv))
|
|
{
|
|
ierr("check fw status failed!\n");
|
|
}
|
|
|
|
switch (mp->cmd)
|
|
{
|
|
case NVT_FW_RAW:
|
|
mp->test_item = CONFIG_NVT_FW_RAWDATA_CSV_FILE;
|
|
mp->result_flag = 0;
|
|
if (nvt_read_baseline(priv, mp) != 0)
|
|
{
|
|
mp->result_flag = 1;
|
|
}
|
|
else
|
|
{
|
|
mp->result_flag = rawdatatest_singlepoint_sub(mp->raw_data,
|
|
mp->record_result, NVT_X_CHANNEL, NVT_Y_CHANNEL,
|
|
ps_config_lmt_fw_rawdata_p,
|
|
ps_config_lmt_fw_rawdata_n);
|
|
}
|
|
|
|
break;
|
|
case NVT_CC:
|
|
mp->test_item = CONFIG_NVT_FW_CC_CSV_FILE;
|
|
mp->result_flag = 0;
|
|
if (nvt_read_cc(priv, mp) != 0)
|
|
{
|
|
mp->result_flag = 1;
|
|
}
|
|
else
|
|
{
|
|
mp->result_flag = rawdatatest_singlepoint_sub(mp->raw_data,
|
|
mp->record_result, NVT_X_CHANNEL, NVT_Y_CHANNEL,
|
|
ps_config_lmt_fw_cc_p, ps_config_lmt_fw_cc_n);
|
|
}
|
|
|
|
break;
|
|
case NVT_FW_NOISE:
|
|
case NVT_FW_SHORT:
|
|
case NVT_FW_OPEN:
|
|
nvt_change_mode(priv, NVT_NORMAL_MODE);
|
|
if (mp->cmd == NVT_FW_NOISE)
|
|
{
|
|
int testresult_fw_diffmax;
|
|
int testresult_fw_diffmin;
|
|
mp->test_item = CONFIG_NVT_NOISE_TEST_CSV_FILE;
|
|
mp->result_flag = 0;
|
|
if (nvt_read_fw_noise(priv, mp, &testresult_fw_diffmax,
|
|
&testresult_fw_diffmin) != 0)
|
|
{
|
|
mp->result_flag = 1;
|
|
}
|
|
else
|
|
{
|
|
if ((testresult_fw_diffmax == -1) ||
|
|
(testresult_fw_diffmin == -1))
|
|
mp->result_flag = -1;
|
|
else
|
|
mp->result_flag = 0;
|
|
}
|
|
}
|
|
else if (mp->cmd == NVT_FW_SHORT)
|
|
{
|
|
mp->test_item = CONFIG_NVT_SHORT_TEST_CSV_FILE;
|
|
mp->result_flag = 0;
|
|
if (nvt_read_fw_short(priv, mp) != 0)
|
|
{
|
|
mp->result_flag = 1;
|
|
}
|
|
else
|
|
{
|
|
mp->result_flag = rawdatatest_singlepoint_sub(mp->raw_data,
|
|
mp->record_result, NVT_X_CHANNEL, NVT_Y_CHANNEL,
|
|
ps_config_lmt_short_rawdata_p,
|
|
ps_config_lmt_short_rawdata_n);
|
|
}
|
|
}
|
|
else if (mp->cmd == NVT_FW_OPEN)
|
|
{
|
|
mp->test_item = CONFIG_NVT_OPEN_TEST_CSV_FILE;
|
|
mp->result_flag = 0;
|
|
if (nvt_read_fw_open(priv, mp) != 0)
|
|
{
|
|
mp->result_flag = 1;
|
|
}
|
|
else
|
|
{
|
|
mp->result_flag = rawdatatest_singlepoint_sub(mp->raw_data,
|
|
mp->record_result, NVT_X_CHANNEL, NVT_Y_CHANNEL,
|
|
ps_config_lmt_open_rawdata_p,
|
|
ps_config_lmt_open_rawdata_n);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
;
|
|
}
|
|
|
|
break;
|
|
default:
|
|
ierr("cmd out of mp_test_e range! cmd: %d\n", mp->cmd);
|
|
break;
|
|
}
|
|
|
|
nvt_bootloader_reset(priv);
|
|
}
|
|
#endif
|
|
|
|
/***************************************************************************
|
|
* Name: nt38350_notify
|
|
***************************************************************************/
|
|
|
|
static void nt38350_notify(FAR struct nt38350_dev_s *priv)
|
|
{
|
|
int i;
|
|
|
|
/* If there are threads waiting on poll() for nt38350 data to become
|
|
* available, then wake them up now. NOTE: we wake up all waiting threads
|
|
* because we do not know that they are going to do. If they all try to
|
|
* read the data, then some make end up blocking after all.
|
|
*/
|
|
|
|
for (i = 0; i < CONFIG_NT38350_NPOLLWAITERS; i++)
|
|
{
|
|
struct pollfd *fds = priv->fds[i];
|
|
if (fds)
|
|
{
|
|
fds->revents |= POLLIN;
|
|
#if NVT_DEBUG
|
|
iinfo("Report events: %02x\n", fds->revents);
|
|
#endif
|
|
nxsem_post(fds->sem);
|
|
}
|
|
}
|
|
|
|
/* If there are threads waiting for read data, then signal one of them
|
|
* that the read data is available.
|
|
*/
|
|
|
|
if (priv->nwaiters > 0)
|
|
{
|
|
/* After posting this semaphore, we need to exit because the NT38350
|
|
* is no longer available.
|
|
*/
|
|
|
|
nxsem_post(&priv->waitsem);
|
|
}
|
|
}
|
|
|
|
/***************************************************************************
|
|
* Name: nt38350_sample
|
|
***************************************************************************/
|
|
|
|
static int nt38350_sample(FAR struct nt38350_dev_s *priv,
|
|
FAR struct ts_nt38350_sample_s *sample)
|
|
{
|
|
irqstate_t flags;
|
|
int ret = - EAGAIN;
|
|
|
|
flags = enter_critical_section();
|
|
|
|
if (priv->valid)
|
|
{
|
|
memcpy(sample, &priv->sample, sizeof(struct ts_nt38350_sample_s));
|
|
|
|
if (sample->contact == CONTACT_UP)
|
|
{
|
|
priv->sample.contact = CONTACT_NONE;
|
|
priv->sample.valid = false;
|
|
priv->id++;
|
|
}
|
|
else if (sample->contact == CONTACT_DOWN)
|
|
{
|
|
priv->sample.contact = CONTACT_MOVE;
|
|
}
|
|
|
|
priv->valid = false;
|
|
ret = OK;
|
|
}
|
|
|
|
leave_critical_section(flags);
|
|
return ret;
|
|
}
|
|
|
|
/***************************************************************************
|
|
* Name: nt38350_waitsample
|
|
***************************************************************************/
|
|
|
|
static int nt38350_waitsample(FAR struct nt38350_dev_s *priv,
|
|
struct ts_nt38350_sample_s *sample)
|
|
{
|
|
irqstate_t flags;
|
|
int ret;
|
|
|
|
sched_lock();
|
|
flags = enter_critical_section();
|
|
|
|
nxsem_post(&priv->devsem);
|
|
|
|
while (nt38350_sample(priv, sample) < 0)
|
|
{
|
|
/* Wait for a change in the nt38350 state */
|
|
|
|
priv->nwaiters++;
|
|
ret = nxsem_wait(&priv->waitsem);
|
|
priv->nwaiters--;
|
|
|
|
if (ret < 0)
|
|
{
|
|
ierr("ERROR: nxsem_wait: %d\n", ret);
|
|
goto errout;
|
|
}
|
|
}
|
|
|
|
ret = nxsem_wait(&priv->devsem);
|
|
|
|
errout:
|
|
leave_critical_section(flags);
|
|
sched_unlock();
|
|
return ret;
|
|
}
|
|
|
|
static void nt38350_data_worker(FAR void *arg)
|
|
{
|
|
FAR struct nt38350_dev_s *priv = (FAR struct nt38350_dev_s *)arg;
|
|
FAR struct nt38350_config_s *config;
|
|
int i;
|
|
int ret;
|
|
#ifdef CONFIG_NVT_OFFLINE_LOG
|
|
uint8_t point_data[NVT_POINT_DATA_EXBUF_LEN + 1] =
|
|
{
|
|
0
|
|
};
|
|
#else
|
|
uint8_t point_data[NVT_POINT_DATA_LEN + 1] =
|
|
{
|
|
0
|
|
};
|
|
#endif
|
|
|
|
uint32_t position = 0;
|
|
uint32_t input_x = 0;
|
|
uint32_t input_y = 0;
|
|
uint32_t input_w = 0;
|
|
uint32_t input_p = 0;
|
|
uint8_t input_id = 0;
|
|
|
|
DEBUGASSERT(priv != NULL);
|
|
|
|
config = priv->config;
|
|
DEBUGASSERT(config != NULL);
|
|
|
|
do
|
|
{
|
|
ret = nxsem_wait_uninterruptible(&priv->devsem);
|
|
|
|
DEBUGASSERT(ret == OK || ret == -ECANCELED);
|
|
}
|
|
while (ret < 0);
|
|
|
|
#ifdef CONFIG_NVT_OFFLINE_LOG
|
|
ret = nt38350_read_reg(priv, NVT_I2C_FW_ADDRESS, point_data,
|
|
NVT_POINT_DATA_EXBUF_LEN + 1);
|
|
#else
|
|
ret = nt38350_read_reg(priv, NVT_I2C_FW_ADDRESS, point_data,
|
|
NVT_POINT_DATA_LEN + 1);
|
|
#endif
|
|
if (ret < 0)
|
|
{
|
|
ierr("Point data read failed!\n");
|
|
}
|
|
|
|
#ifdef CONFIG_NVT_OFFLINE_LOG
|
|
memcpy(priv->point_xdata_temp, (point_data + 1), NVT_POINT_DATA_LEN);
|
|
memcpy((priv->point_xdata_temp + NVT_POINT_DATA_LEN),
|
|
(point_data + 1 + NVT_POINT_DATA_LEN + NVT_POINT_DATA_EXT_LEN),
|
|
NVT_S2D_DATA_LEN);
|
|
memcpy((priv->point_xdata_temp + NVT_POINT_DATA_LEN + NVT_S2D_DATA_LEN),
|
|
(point_data + 1 + NVT_POINT_DATA_LEN + NVT_POINT_DATA_EXT_LEN +
|
|
NVT_S2D_DATA_LEN + NVT_FW_CMD_HANDLE_LEN),
|
|
(NVT_S2D_DATA_EXT_LEN + NVT_FW_FRAME_CNT_LEN));
|
|
work_queue(LPWORK, &priv->nvt_log_wq, nvt_log_data_to_csv, priv, 100);
|
|
#endif
|
|
|
|
for (i = 0; i < priv->max_touch_num; i++)
|
|
{
|
|
position = 1 + 6 * i;
|
|
input_id = (uint8_t)(point_data[position + 0] >> 3);
|
|
if ((input_id == 0) || (input_id > priv->max_touch_num))
|
|
continue;
|
|
|
|
if (((point_data[position] & 0x07) == FINGER_DOWN) ||
|
|
((point_data[position] & 0x07) == FINGER_MOVE))
|
|
{
|
|
if ((point_data[position] & 0x07) == FINGER_DOWN)
|
|
priv->sample.contact = CONTACT_DOWN;
|
|
if ((point_data[position] & 0x07) == FINGER_MOVE)
|
|
priv->sample.contact = CONTACT_MOVE;
|
|
input_x = (uint32_t)(point_data[position + 1] << 4) +
|
|
(uint32_t) (point_data[position + 3] >> 4);
|
|
input_y = (uint32_t)(point_data[position + 2] << 4) +
|
|
(uint32_t) (point_data[position + 3] & 0x0f);
|
|
#ifdef CONFIG_ROTATION
|
|
priv->sample.x = NVT_TOUCH_DEFAULT_MAX_WIDTH - input_x;
|
|
priv->sample.y = NVT_TOUCH_DEFAULT_MAX_HEIGHT - input_y;
|
|
#else
|
|
priv->sample.x = input_x;
|
|
priv->sample.y = input_y;
|
|
#endif
|
|
input_w = (uint32_t)(point_data[position + 4]);
|
|
if (input_w == 0)
|
|
input_w = 1;
|
|
priv->sample.width = input_w;
|
|
if (i < 2)
|
|
{
|
|
input_p = (uint32_t)(point_data[position + 5]) +
|
|
(uint32_t)(point_data[i + 15] << 8);
|
|
if (input_p > NVT_TOUCH_FORCE_NUM)
|
|
input_p = NVT_TOUCH_FORCE_NUM;
|
|
}
|
|
else
|
|
{
|
|
input_p = (uint32_t)(point_data[position + 5]);
|
|
}
|
|
|
|
if (input_p == 0)
|
|
input_p = 1;
|
|
priv->sample.pressure = input_p;
|
|
}
|
|
else if (point_data[position] == FINGER_UP)
|
|
{
|
|
priv->sample.contact = CONTACT_UP;
|
|
#ifdef CONFIG_ROTATION
|
|
priv->sample.x = NVT_TOUCH_DEFAULT_MAX_WIDTH - input_x;
|
|
priv->sample.y = NVT_TOUCH_DEFAULT_MAX_HEIGHT - input_y;
|
|
#else
|
|
priv->sample.x = input_x;
|
|
priv->sample.y = input_y;
|
|
#endif
|
|
priv->sample.width = input_w;
|
|
priv->sample.pressure = input_p;
|
|
}
|
|
else
|
|
{
|
|
priv->sample.contact = CONTACT_NONE;
|
|
input_x = 0;
|
|
input_y = 0;
|
|
input_w = 0;
|
|
input_p = 0;
|
|
}
|
|
}
|
|
|
|
priv->sample.valid = true;
|
|
priv->sample.id = priv->id;
|
|
priv->valid = true;
|
|
|
|
nt38350_notify(priv);
|
|
|
|
config->enable(config, true);
|
|
nxsem_post(&priv->devsem);
|
|
}
|
|
|
|
/***************************************************************************
|
|
* Name: nt38350_data_interrupt
|
|
***************************************************************************/
|
|
|
|
static int nt38350_data_interrupt(FAR struct ioexpander_dev_s *dev,
|
|
ioe_pinset_t pinset, FAR void *arg)
|
|
{
|
|
FAR struct nt38350_dev_s *priv = arg;
|
|
FAR struct nt38350_config_s *config;
|
|
int ret;
|
|
|
|
DEBUGASSERT(priv != NULL);
|
|
|
|
/* Which nt38350 device caused the interrupt? */
|
|
|
|
/* Get a pointer the callbacks for convenience (and so the code is not so
|
|
* ugly).
|
|
*/
|
|
|
|
config = priv->config;
|
|
DEBUGASSERT(config != NULL);
|
|
|
|
/* Disable further interrupts */
|
|
|
|
config->enable(config, false);
|
|
|
|
/* Transfer processing to the worker thread. Since nt38350 interrupts
|
|
* are disabled while the work is pending, no special action should be
|
|
* required to protected the work queue.
|
|
*/
|
|
|
|
ret = work_queue(HPWORK, &priv->work, nt38350_data_worker, priv, 0);
|
|
if (ret != OK)
|
|
{
|
|
ierr("ERROR: Failed to queue work: %d\n", ret);
|
|
}
|
|
|
|
/* Clear any pending interrupts and return success */
|
|
|
|
config->clear(config);
|
|
return OK;
|
|
}
|
|
|
|
/***************************************************************************
|
|
* Name: nt38350_open
|
|
***************************************************************************/
|
|
|
|
static int nt38350_open(FAR struct file *filep)
|
|
{
|
|
#ifdef CONFIG_NT38350_REFCNT
|
|
FAR struct inode *inode;
|
|
FAR struct nt38350_dev_s *priv;
|
|
uint8_t tmp;
|
|
int ret;
|
|
|
|
DEBUGASSERT(filep);
|
|
inode = filep->f_inode;
|
|
|
|
DEBUGASSERT(inode && inode->i_private);
|
|
priv = (FAR struct nt38350_dev_s *)inode->i_private;
|
|
|
|
/* Get exclusive access to the driver data structure */
|
|
|
|
ret = nxsem_wait(&priv->devsem);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
/* Increment the reference count */
|
|
|
|
tmp = priv->crefs + 1;
|
|
if (tmp == 0)
|
|
{
|
|
/* More than 255 opens; uint8_t overflows to zero */
|
|
|
|
ret = -EMFILE;
|
|
goto errout_with_sem;
|
|
}
|
|
|
|
/* When the reference increments to 1, this is the first open event
|
|
* on the driver.. and an opportunity to do any one-time initialization.
|
|
*/
|
|
|
|
/* Save the new open count on success */
|
|
|
|
priv->crefs = tmp;
|
|
|
|
errout_with_sem:
|
|
nxsem_post(&priv->devsem);
|
|
return ret;
|
|
#else
|
|
return OK;
|
|
#endif
|
|
}
|
|
|
|
/***************************************************************************
|
|
* Name: nt38350_close
|
|
***************************************************************************/
|
|
|
|
static int nt38350_close(FAR struct file *filep)
|
|
{
|
|
#ifdef CONFIG_NT38350_REFCNT
|
|
FAR struct inode *inode;
|
|
FAR struct nt38350_dev_s *priv;
|
|
int ret;
|
|
|
|
DEBUGASSERT(filep);
|
|
inode = filep->f_inode;
|
|
|
|
DEBUGASSERT(inode && inode->i_private);
|
|
priv = (FAR struct nt38350_dev_s *)inode->i_private;
|
|
|
|
/* Get exclusive access to the driver data structure */
|
|
|
|
ret = nxsem_wait(&priv->devsem);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
/* Decrement the reference count unless it would decrement a negative
|
|
* value. When the count decrements to zero, there are no further
|
|
* open references to the driver.
|
|
*/
|
|
|
|
if (priv->crefs >= 1)
|
|
{
|
|
priv->crefs--;
|
|
}
|
|
|
|
nxsem_post(&priv->devsem);
|
|
#endif
|
|
return OK;
|
|
}
|
|
|
|
/***************************************************************************
|
|
* Name: nt38350_read
|
|
***************************************************************************/
|
|
|
|
static ssize_t nt38350_read(FAR struct file *filep, FAR char *buffer,
|
|
size_t len)
|
|
{
|
|
FAR struct inode *inode;
|
|
FAR struct nt38350_dev_s *priv;
|
|
FAR struct touch_sample_s *report;
|
|
struct ts_nt38350_sample_s sample;
|
|
int ret;
|
|
|
|
DEBUGASSERT(filep);
|
|
inode = filep->f_inode;
|
|
|
|
DEBUGASSERT(inode && inode->i_private);
|
|
priv = (FAR struct nt38350_dev_s *)inode->i_private;
|
|
|
|
/* Verify that the caller has provided a buffer large enough to receive
|
|
* the touch data.
|
|
*/
|
|
|
|
if (len < SIZEOF_TOUCH_SAMPLE_S(1))
|
|
{
|
|
/* We could provide logic to break up a touch report into segments and
|
|
* handle smaller reads... but why?
|
|
*/
|
|
|
|
ierr("ERROR: Unsupported read size: %d\n", len);
|
|
return -ENOSYS;
|
|
}
|
|
|
|
/* Get exclusive access to the driver data structure */
|
|
|
|
ret = nxsem_wait(&priv->devsem);
|
|
if (ret < 0)
|
|
{
|
|
ierr("ERROR: nxsem_wait: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
/* Try to read sample data. */
|
|
|
|
ret = nt38350_sample(priv, &sample);
|
|
if (ret < 0)
|
|
{
|
|
/* Sample data is not available now. We would ave to wait to get
|
|
* receive sample data. If the user has specified the O_NONBLOCK
|
|
* option, then just return an error.
|
|
*/
|
|
|
|
if (filep->f_oflags & O_NONBLOCK)
|
|
{
|
|
ret = -EAGAIN;
|
|
goto errout;
|
|
}
|
|
|
|
/* Wait for sample data */
|
|
|
|
ret = nt38350_waitsample(priv, &sample);
|
|
if (ret < 0)
|
|
{
|
|
/* We might have been awakened by a signal */
|
|
|
|
ierr("ERROR: nt38350_waitsample: %d\n", ret);
|
|
goto errout;
|
|
}
|
|
}
|
|
|
|
/* In any event, we now have sampled nt38350 data that we can report
|
|
* to the caller.
|
|
*/
|
|
|
|
report = (FAR struct touch_sample_s *)buffer;
|
|
memset(report, 0, SIZEOF_TOUCH_SAMPLE_S(1));
|
|
report->npoints = 1;
|
|
report->point[0].id = sample.id;
|
|
report->point[0].x = sample.x;
|
|
report->point[0].y = sample.y;
|
|
report->point[0].w = sample.width;
|
|
report->point[0].pressure = sample.pressure;
|
|
|
|
/* Report the appropriate flags */
|
|
|
|
if (sample.contact == CONTACT_UP)
|
|
{
|
|
/* Pen is now up. Is the positional data valid? This is important to
|
|
* know because the release will be sent to the window based on its
|
|
* last positional data.
|
|
*/
|
|
|
|
if (sample.valid)
|
|
{
|
|
report->point[0].flags = TOUCH_UP | TOUCH_ID_VALID |
|
|
TOUCH_POS_VALID;
|
|
}
|
|
else
|
|
{
|
|
report->point[0].flags = TOUCH_UP | TOUCH_ID_VALID;
|
|
}
|
|
}
|
|
else if (sample.contact == CONTACT_DOWN)
|
|
{
|
|
/* First contact */
|
|
|
|
report->point[0].flags = TOUCH_DOWN | TOUCH_ID_VALID |
|
|
TOUCH_POS_VALID;
|
|
}
|
|
else /* if (sample->contact == CONTACT_MOVE) */
|
|
{
|
|
/* Movement of the same contact */
|
|
|
|
report->point[0].flags = TOUCH_MOVE | TOUCH_ID_VALID |
|
|
TOUCH_POS_VALID;
|
|
}
|
|
|
|
#if NVT_DEBUG
|
|
iinfo(" id: %d\n", report->point[0].id);
|
|
iinfo(" flags: %02x\n", report->point[0].flags);
|
|
iinfo(" x: %d\n", report->point[0].x);
|
|
iinfo(" y: %d\n", report->point[0].y);
|
|
iinfo(" w: %d\n", report->point[0].w);
|
|
iinfo(" p: %d\n", report->point[0].pressure);
|
|
#endif
|
|
ret = SIZEOF_TOUCH_SAMPLE_S(1);
|
|
|
|
errout:
|
|
nxsem_post(&priv->devsem);
|
|
return ret;
|
|
}
|
|
|
|
/***************************************************************************
|
|
* Name: nt38350_ioctl
|
|
***************************************************************************/
|
|
|
|
static int nt38350_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
|
|
{
|
|
FAR struct inode *inode;
|
|
FAR struct nt38350_dev_s *priv;
|
|
int ret;
|
|
|
|
DEBUGASSERT(filep);
|
|
inode = filep->f_inode;
|
|
|
|
DEBUGASSERT(inode && inode->i_private);
|
|
priv = (FAR struct nt38350_dev_s *)inode->i_private;
|
|
|
|
/* Get exclusive access to the driver data structure */
|
|
|
|
ret = nxsem_wait(&priv->devsem);
|
|
if (ret < 0)
|
|
{
|
|
ierr("ERROR: nxsem_wait failed: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
/* Process the IOCTL by command */
|
|
|
|
switch (cmd)
|
|
{
|
|
case TSIOC_SETCALIB: /* arg: Pointer to int calibration value */
|
|
{
|
|
priv->config->rxplate = (uint16_t)arg;
|
|
}
|
|
break;
|
|
|
|
case TSIOC_GETCALIB: /* arg: Pointer to int calibration value */
|
|
{
|
|
FAR int *ptr = (FAR int *)((uintptr_t)arg);
|
|
*ptr = priv->config->rxplate;
|
|
}
|
|
break;
|
|
|
|
case TSIOC_SETFREQUENCY: /* arg: Pointer to uint32_t frequency value */
|
|
{
|
|
FAR uint32_t *ptr = (FAR uint32_t *)((uintptr_t)arg);
|
|
priv->config->frequency = *ptr;
|
|
}
|
|
break;
|
|
|
|
case TSIOC_GETFREQUENCY: /* arg: Pointer to uint32_t frequency value */
|
|
{
|
|
FAR uint32_t *ptr = (FAR uint32_t *)((uintptr_t)arg);
|
|
*ptr = priv->config->frequency;
|
|
}
|
|
break;
|
|
|
|
case TSIOC_GETFWVERSION: /* arg: Pointer to uint32_t fw version value */
|
|
{
|
|
FAR uint32_t *ptr = (FAR uint32_t *)((uintptr_t)arg);
|
|
*ptr = (uint32_t)priv->fw_ver;
|
|
}
|
|
break;
|
|
#ifdef CONFIG_NVT_OFFLINE_LOG
|
|
case TSIOC_GETNVTDIFF: /* arg: Pointer to struct nvt_diff_s */
|
|
{
|
|
FAR struct nvt_diff_s *ptr = (FAR struct nvt_diff_s *)arg;
|
|
nvt_diff_show(priv, ptr);
|
|
}
|
|
break;
|
|
#endif
|
|
#ifdef CONFIG_NVT_TOUCH_MP
|
|
case TSIOC_SELFTEST: /* arg: Pointer to struct nvt_mp_test_s */
|
|
{
|
|
FAR struct nvt_mp_test_s *ptr = (FAR struct nvt_mp_test_s *)arg;
|
|
nvt_selftest(priv, ptr);
|
|
}
|
|
break;
|
|
#endif
|
|
default:
|
|
ret = -ENOTTY;
|
|
break;
|
|
}
|
|
|
|
nxsem_post(&priv->devsem);
|
|
return ret;
|
|
}
|
|
|
|
/***************************************************************************
|
|
* Name: nt38350_poll
|
|
***************************************************************************/
|
|
|
|
static int nt38350_poll(FAR struct file *filep, FAR struct pollfd *fds,
|
|
bool setup)
|
|
{
|
|
FAR struct nt38350_dev_s *priv;
|
|
FAR struct inode *inode;
|
|
int ret;
|
|
int i;
|
|
|
|
DEBUGASSERT(filep && fds);
|
|
inode = filep->f_inode;
|
|
|
|
DEBUGASSERT(inode && inode->i_private);
|
|
priv = (FAR struct nt38350_dev_s *)inode->i_private;
|
|
|
|
/* Are we setting up the poll? Or tearing it down? */
|
|
|
|
ret = nxsem_wait(&priv->devsem);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
if (setup)
|
|
{
|
|
/* Ignore waits that do not include POLLIN */
|
|
|
|
if ((fds->events & POLLIN) == 0)
|
|
{
|
|
ret = -EDEADLK;
|
|
goto errout;
|
|
}
|
|
|
|
/* This is a request to set up the poll. Find an available
|
|
* slot for the poll structure reference
|
|
*/
|
|
|
|
for (i = 0; i < CONFIG_NT38350_NPOLLWAITERS; i++)
|
|
{
|
|
/* Find an available slot */
|
|
|
|
if (!priv->fds[i])
|
|
{
|
|
/* Bind the poll structure and this slot */
|
|
|
|
priv->fds[i] = fds;
|
|
fds->priv = &priv->fds[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i >= CONFIG_NT38350_NPOLLWAITERS)
|
|
{
|
|
fds->priv = NULL;
|
|
ret = -EBUSY;
|
|
goto errout;
|
|
}
|
|
|
|
/* Should we immediately notify on any of the requested events? */
|
|
|
|
if (priv->valid)
|
|
{
|
|
nt38350_notify(priv);
|
|
}
|
|
}
|
|
else if (fds->priv)
|
|
{
|
|
/* This is a request to tear down the poll. */
|
|
|
|
struct pollfd **slot = (struct pollfd **)fds->priv;
|
|
DEBUGASSERT(slot != NULL);
|
|
|
|
/* Remove all memory of the poll setup */
|
|
|
|
*slot = NULL;
|
|
fds->priv = NULL;
|
|
}
|
|
|
|
errout:
|
|
nxsem_post(&priv->devsem);
|
|
return ret;
|
|
}
|
|
|
|
/***************************************************************************
|
|
* Public Functions
|
|
***************************************************************************/
|
|
|
|
int nt38350_register(FAR struct nt38350_config_s *config,
|
|
const char *devname)
|
|
{
|
|
FAR struct nt38350_dev_s *priv;
|
|
int ret;
|
|
|
|
#if NVT_DEBUG
|
|
iinfo("dev: %p devname: %s \n", dev, devname);
|
|
#endif
|
|
|
|
DEBUGASSERT(config != NULL && devname != NULL);
|
|
DEBUGASSERT(config->attach != NULL && config->enable != NULL &&
|
|
config->clear != NULL);
|
|
|
|
priv = kmm_zalloc(sizeof(struct nt38350_dev_s));
|
|
if (priv == NULL)
|
|
{
|
|
ierr("ERROR: Failed to allocate instance\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
/* Initialize the nt38350 device driver instance */
|
|
|
|
priv->config = config; /* Save the board configuration */
|
|
|
|
priv->fw_path = CONFIG_NT38350_FW_PATH; /* Set up firmware path */
|
|
|
|
#if NVT_DEBUG
|
|
iinfo("RST %d IRQ %d %d\n", priv->config->gpio_rst_pin,
|
|
priv->config->gpio_irq_pin, __LINE__);
|
|
#endif
|
|
|
|
nxsem_init(&priv->devsem, 0, 1); /* Initialize device structure semaphore */
|
|
nxsem_init(&priv->waitsem, 0, 0); /* Initialize pen event wait semaphore */
|
|
|
|
/* The event wait semaphore is used for signaling and, hence, should not
|
|
* have priority inheritance enabled.
|
|
*/
|
|
|
|
nxsem_set_protocol(&priv->waitsem, SEM_PRIO_NONE);
|
|
|
|
/* Hardware Initial */
|
|
|
|
usleep(NVT_DELAY_10MS);
|
|
|
|
ret = nvt_ts_check_chip_ver_trim(priv, NVT_CHIP_VER_TRIM_ADDR);
|
|
if (ret)
|
|
{
|
|
ret = nvt_ts_check_chip_ver_trim(priv, NVT_CHIP_VER_TRIM_OLD_ADDR);
|
|
if (ret)
|
|
{
|
|
ierr("ERROR: Chip is not identified\n");
|
|
ret = -ENODEV;
|
|
goto errout_with_priv;
|
|
}
|
|
}
|
|
|
|
nvt_bootloader_reset(priv);
|
|
nvt_check_fw_reset_state(priv, RESET_STATE_INIT);
|
|
nvt_get_fw_info(priv);
|
|
|
|
priv->max_touch_num = NVT_TOUCH_MAX_FINGER_NUM;
|
|
|
|
#ifdef CONFIG_NT38350_NEED_UPGRADE_FW
|
|
ret = nvt_boot_update_firmware(priv);
|
|
if (ret)
|
|
{
|
|
ierr("ERROR: Failed to nvt_boot_update_firmware\n");
|
|
ret = -ENODEV;
|
|
goto errout_with_priv;
|
|
}
|
|
#endif
|
|
|
|
ret = config->attach(config, nt38350_data_interrupt, priv);
|
|
if (ret < 0)
|
|
{
|
|
ierr("ERROR: Failed to attach interrupt\n");
|
|
ret = -ENODEV;
|
|
goto errout_with_priv;
|
|
}
|
|
|
|
config->enable(config, true);
|
|
|
|
ret = register_driver(devname, &g_nt38350_fops, 0666, priv);
|
|
if (ret < 0)
|
|
{
|
|
ierr("ERROR: register_driver() failed: %d\n", ret);
|
|
config->detach(config, nt38350_data_interrupt);
|
|
ret = -ENODEV;
|
|
goto errout_with_priv;
|
|
}
|
|
|
|
/* And return success (?) */
|
|
|
|
return OK;
|
|
errout_with_priv:
|
|
kmm_free(priv);
|
|
nxsem_destroy(&priv->devsem);
|
|
return ret;
|
|
}
|