18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Weida HiTech WDT87xx TouchScreen I2C driver 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (c) 2015 Weida Hi-Tech Co., Ltd. 58c2ecf20Sopenharmony_ci * HN Chen <hn.chen@weidahitech.com> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * This software is licensed under the terms of the GNU General Public 88c2ecf20Sopenharmony_ci * License, as published by the Free Software Foundation, and 98c2ecf20Sopenharmony_ci * may be copied, distributed, and modified under those terms. 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/i2c.h> 138c2ecf20Sopenharmony_ci#include <linux/input.h> 148c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 158c2ecf20Sopenharmony_ci#include <linux/delay.h> 168c2ecf20Sopenharmony_ci#include <linux/irq.h> 178c2ecf20Sopenharmony_ci#include <linux/io.h> 188c2ecf20Sopenharmony_ci#include <linux/module.h> 198c2ecf20Sopenharmony_ci#include <linux/slab.h> 208c2ecf20Sopenharmony_ci#include <linux/firmware.h> 218c2ecf20Sopenharmony_ci#include <linux/input/mt.h> 228c2ecf20Sopenharmony_ci#include <linux/acpi.h> 238c2ecf20Sopenharmony_ci#include <asm/unaligned.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#define WDT87XX_NAME "wdt87xx_i2c" 268c2ecf20Sopenharmony_ci#define WDT87XX_FW_NAME "wdt87xx_fw.bin" 278c2ecf20Sopenharmony_ci#define WDT87XX_CFG_NAME "wdt87xx_cfg.bin" 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#define MODE_ACTIVE 0x01 308c2ecf20Sopenharmony_ci#define MODE_READY 0x02 318c2ecf20Sopenharmony_ci#define MODE_IDLE 0x03 328c2ecf20Sopenharmony_ci#define MODE_SLEEP 0x04 338c2ecf20Sopenharmony_ci#define MODE_STOP 0xFF 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#define WDT_MAX_FINGER 10 368c2ecf20Sopenharmony_ci#define WDT_RAW_BUF_COUNT 54 378c2ecf20Sopenharmony_ci#define WDT_V1_RAW_BUF_COUNT 74 388c2ecf20Sopenharmony_ci#define WDT_FIRMWARE_ID 0xa9e368f5 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci#define PG_SIZE 0x1000 418c2ecf20Sopenharmony_ci#define MAX_RETRIES 3 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci#define MAX_UNIT_AXIS 0x7FFF 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci#define PKT_READ_SIZE 72 468c2ecf20Sopenharmony_ci#define PKT_WRITE_SIZE 80 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci/* the finger definition of the report event */ 498c2ecf20Sopenharmony_ci#define FINGER_EV_OFFSET_ID 0 508c2ecf20Sopenharmony_ci#define FINGER_EV_OFFSET_X 1 518c2ecf20Sopenharmony_ci#define FINGER_EV_OFFSET_Y 3 528c2ecf20Sopenharmony_ci#define FINGER_EV_SIZE 5 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci#define FINGER_EV_V1_OFFSET_ID 0 558c2ecf20Sopenharmony_ci#define FINGER_EV_V1_OFFSET_W 1 568c2ecf20Sopenharmony_ci#define FINGER_EV_V1_OFFSET_P 2 578c2ecf20Sopenharmony_ci#define FINGER_EV_V1_OFFSET_X 3 588c2ecf20Sopenharmony_ci#define FINGER_EV_V1_OFFSET_Y 5 598c2ecf20Sopenharmony_ci#define FINGER_EV_V1_SIZE 7 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci/* The definition of a report packet */ 628c2ecf20Sopenharmony_ci#define TOUCH_PK_OFFSET_REPORT_ID 0 638c2ecf20Sopenharmony_ci#define TOUCH_PK_OFFSET_EVENT 1 648c2ecf20Sopenharmony_ci#define TOUCH_PK_OFFSET_SCAN_TIME 51 658c2ecf20Sopenharmony_ci#define TOUCH_PK_OFFSET_FNGR_NUM 53 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci#define TOUCH_PK_V1_OFFSET_REPORT_ID 0 688c2ecf20Sopenharmony_ci#define TOUCH_PK_V1_OFFSET_EVENT 1 698c2ecf20Sopenharmony_ci#define TOUCH_PK_V1_OFFSET_SCAN_TIME 71 708c2ecf20Sopenharmony_ci#define TOUCH_PK_V1_OFFSET_FNGR_NUM 73 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci/* The definition of the controller parameters */ 738c2ecf20Sopenharmony_ci#define CTL_PARAM_OFFSET_FW_ID 0 748c2ecf20Sopenharmony_ci#define CTL_PARAM_OFFSET_PLAT_ID 2 758c2ecf20Sopenharmony_ci#define CTL_PARAM_OFFSET_XMLS_ID1 4 768c2ecf20Sopenharmony_ci#define CTL_PARAM_OFFSET_XMLS_ID2 6 778c2ecf20Sopenharmony_ci#define CTL_PARAM_OFFSET_PHY_CH_X 8 788c2ecf20Sopenharmony_ci#define CTL_PARAM_OFFSET_PHY_CH_Y 10 798c2ecf20Sopenharmony_ci#define CTL_PARAM_OFFSET_PHY_X0 12 808c2ecf20Sopenharmony_ci#define CTL_PARAM_OFFSET_PHY_X1 14 818c2ecf20Sopenharmony_ci#define CTL_PARAM_OFFSET_PHY_Y0 16 828c2ecf20Sopenharmony_ci#define CTL_PARAM_OFFSET_PHY_Y1 18 838c2ecf20Sopenharmony_ci#define CTL_PARAM_OFFSET_PHY_W 22 848c2ecf20Sopenharmony_ci#define CTL_PARAM_OFFSET_PHY_H 24 858c2ecf20Sopenharmony_ci#define CTL_PARAM_OFFSET_FACTOR 32 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci/* The definition of the device descriptor */ 888c2ecf20Sopenharmony_ci#define WDT_GD_DEVICE 1 898c2ecf20Sopenharmony_ci#define DEV_DESC_OFFSET_VID 8 908c2ecf20Sopenharmony_ci#define DEV_DESC_OFFSET_PID 10 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci/* Communication commands */ 938c2ecf20Sopenharmony_ci#define PACKET_SIZE 56 948c2ecf20Sopenharmony_ci#define VND_REQ_READ 0x06 958c2ecf20Sopenharmony_ci#define VND_READ_DATA 0x07 968c2ecf20Sopenharmony_ci#define VND_REQ_WRITE 0x08 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci#define VND_CMD_START 0x00 998c2ecf20Sopenharmony_ci#define VND_CMD_STOP 0x01 1008c2ecf20Sopenharmony_ci#define VND_CMD_RESET 0x09 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci#define VND_CMD_ERASE 0x1A 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci#define VND_GET_CHECKSUM 0x66 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci#define VND_SET_DATA 0x83 1078c2ecf20Sopenharmony_ci#define VND_SET_COMMAND_DATA 0x84 1088c2ecf20Sopenharmony_ci#define VND_SET_CHECKSUM_CALC 0x86 1098c2ecf20Sopenharmony_ci#define VND_SET_CHECKSUM_LENGTH 0x87 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci#define VND_CMD_SFLCK 0xFC 1128c2ecf20Sopenharmony_ci#define VND_CMD_SFUNL 0xFD 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci#define CMD_SFLCK_KEY 0xC39B 1158c2ecf20Sopenharmony_ci#define CMD_SFUNL_KEY 0x95DA 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci#define STRIDX_PLATFORM_ID 0x80 1188c2ecf20Sopenharmony_ci#define STRIDX_PARAMETERS 0x81 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci#define CMD_BUF_SIZE 8 1218c2ecf20Sopenharmony_ci#define PKT_BUF_SIZE 64 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci/* The definition of the command packet */ 1248c2ecf20Sopenharmony_ci#define CMD_REPORT_ID_OFFSET 0x0 1258c2ecf20Sopenharmony_ci#define CMD_TYPE_OFFSET 0x1 1268c2ecf20Sopenharmony_ci#define CMD_INDEX_OFFSET 0x2 1278c2ecf20Sopenharmony_ci#define CMD_KEY_OFFSET 0x3 1288c2ecf20Sopenharmony_ci#define CMD_LENGTH_OFFSET 0x4 1298c2ecf20Sopenharmony_ci#define CMD_DATA_OFFSET 0x8 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci/* The definition of firmware chunk tags */ 1328c2ecf20Sopenharmony_ci#define FOURCC_ID_RIFF 0x46464952 1338c2ecf20Sopenharmony_ci#define FOURCC_ID_WHIF 0x46494857 1348c2ecf20Sopenharmony_ci#define FOURCC_ID_FRMT 0x544D5246 1358c2ecf20Sopenharmony_ci#define FOURCC_ID_FRWR 0x52575246 1368c2ecf20Sopenharmony_ci#define FOURCC_ID_CNFG 0x47464E43 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci#define CHUNK_ID_FRMT FOURCC_ID_FRMT 1398c2ecf20Sopenharmony_ci#define CHUNK_ID_FRWR FOURCC_ID_FRWR 1408c2ecf20Sopenharmony_ci#define CHUNK_ID_CNFG FOURCC_ID_CNFG 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci#define FW_FOURCC1_OFFSET 0 1438c2ecf20Sopenharmony_ci#define FW_SIZE_OFFSET 4 1448c2ecf20Sopenharmony_ci#define FW_FOURCC2_OFFSET 8 1458c2ecf20Sopenharmony_ci#define FW_PAYLOAD_OFFSET 40 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci#define FW_CHUNK_ID_OFFSET 0 1488c2ecf20Sopenharmony_ci#define FW_CHUNK_SIZE_OFFSET 4 1498c2ecf20Sopenharmony_ci#define FW_CHUNK_TGT_START_OFFSET 8 1508c2ecf20Sopenharmony_ci#define FW_CHUNK_PAYLOAD_LEN_OFFSET 12 1518c2ecf20Sopenharmony_ci#define FW_CHUNK_SRC_START_OFFSET 16 1528c2ecf20Sopenharmony_ci#define FW_CHUNK_VERSION_OFFSET 20 1538c2ecf20Sopenharmony_ci#define FW_CHUNK_ATTR_OFFSET 24 1548c2ecf20Sopenharmony_ci#define FW_CHUNK_PAYLOAD_OFFSET 32 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci/* Controller requires minimum 300us between commands */ 1578c2ecf20Sopenharmony_ci#define WDT_COMMAND_DELAY_MS 2 1588c2ecf20Sopenharmony_ci#define WDT_FLASH_WRITE_DELAY_MS 4 1598c2ecf20Sopenharmony_ci#define WDT_FLASH_ERASE_DELAY_MS 200 1608c2ecf20Sopenharmony_ci#define WDT_FW_RESET_TIME 2500 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_cistruct wdt87xx_sys_param { 1638c2ecf20Sopenharmony_ci u16 fw_id; 1648c2ecf20Sopenharmony_ci u16 plat_id; 1658c2ecf20Sopenharmony_ci u16 xmls_id1; 1668c2ecf20Sopenharmony_ci u16 xmls_id2; 1678c2ecf20Sopenharmony_ci u16 phy_ch_x; 1688c2ecf20Sopenharmony_ci u16 phy_ch_y; 1698c2ecf20Sopenharmony_ci u16 phy_w; 1708c2ecf20Sopenharmony_ci u16 phy_h; 1718c2ecf20Sopenharmony_ci u16 scaling_factor; 1728c2ecf20Sopenharmony_ci u32 max_x; 1738c2ecf20Sopenharmony_ci u32 max_y; 1748c2ecf20Sopenharmony_ci u16 vendor_id; 1758c2ecf20Sopenharmony_ci u16 product_id; 1768c2ecf20Sopenharmony_ci}; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_cistruct wdt87xx_data { 1798c2ecf20Sopenharmony_ci struct i2c_client *client; 1808c2ecf20Sopenharmony_ci struct input_dev *input; 1818c2ecf20Sopenharmony_ci /* Mutex for fw update to prevent concurrent access */ 1828c2ecf20Sopenharmony_ci struct mutex fw_mutex; 1838c2ecf20Sopenharmony_ci struct wdt87xx_sys_param param; 1848c2ecf20Sopenharmony_ci u8 phys[32]; 1858c2ecf20Sopenharmony_ci}; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_cistatic int wdt87xx_i2c_xfer(struct i2c_client *client, 1888c2ecf20Sopenharmony_ci void *txdata, size_t txlen, 1898c2ecf20Sopenharmony_ci void *rxdata, size_t rxlen) 1908c2ecf20Sopenharmony_ci{ 1918c2ecf20Sopenharmony_ci struct i2c_msg msgs[] = { 1928c2ecf20Sopenharmony_ci { 1938c2ecf20Sopenharmony_ci .addr = client->addr, 1948c2ecf20Sopenharmony_ci .flags = 0, 1958c2ecf20Sopenharmony_ci .len = txlen, 1968c2ecf20Sopenharmony_ci .buf = txdata, 1978c2ecf20Sopenharmony_ci }, 1988c2ecf20Sopenharmony_ci { 1998c2ecf20Sopenharmony_ci .addr = client->addr, 2008c2ecf20Sopenharmony_ci .flags = I2C_M_RD, 2018c2ecf20Sopenharmony_ci .len = rxlen, 2028c2ecf20Sopenharmony_ci .buf = rxdata, 2038c2ecf20Sopenharmony_ci }, 2048c2ecf20Sopenharmony_ci }; 2058c2ecf20Sopenharmony_ci int error; 2068c2ecf20Sopenharmony_ci int ret; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); 2098c2ecf20Sopenharmony_ci if (ret != ARRAY_SIZE(msgs)) { 2108c2ecf20Sopenharmony_ci error = ret < 0 ? ret : -EIO; 2118c2ecf20Sopenharmony_ci dev_err(&client->dev, "%s: i2c transfer failed: %d\n", 2128c2ecf20Sopenharmony_ci __func__, error); 2138c2ecf20Sopenharmony_ci return error; 2148c2ecf20Sopenharmony_ci } 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci return 0; 2178c2ecf20Sopenharmony_ci} 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_cistatic int wdt87xx_get_desc(struct i2c_client *client, u8 desc_idx, 2208c2ecf20Sopenharmony_ci u8 *buf, size_t len) 2218c2ecf20Sopenharmony_ci{ 2228c2ecf20Sopenharmony_ci u8 tx_buf[] = { 0x22, 0x00, 0x10, 0x0E, 0x23, 0x00 }; 2238c2ecf20Sopenharmony_ci int error; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci tx_buf[2] |= desc_idx & 0xF; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci error = wdt87xx_i2c_xfer(client, tx_buf, sizeof(tx_buf), 2288c2ecf20Sopenharmony_ci buf, len); 2298c2ecf20Sopenharmony_ci if (error) { 2308c2ecf20Sopenharmony_ci dev_err(&client->dev, "get desc failed: %d\n", error); 2318c2ecf20Sopenharmony_ci return error; 2328c2ecf20Sopenharmony_ci } 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci if (buf[0] != len) { 2358c2ecf20Sopenharmony_ci dev_err(&client->dev, "unexpected response to get desc: %d\n", 2368c2ecf20Sopenharmony_ci buf[0]); 2378c2ecf20Sopenharmony_ci return -EINVAL; 2388c2ecf20Sopenharmony_ci } 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci mdelay(WDT_COMMAND_DELAY_MS); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci return 0; 2438c2ecf20Sopenharmony_ci} 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_cistatic int wdt87xx_get_string(struct i2c_client *client, u8 str_idx, 2468c2ecf20Sopenharmony_ci u8 *buf, size_t len) 2478c2ecf20Sopenharmony_ci{ 2488c2ecf20Sopenharmony_ci u8 tx_buf[] = { 0x22, 0x00, 0x13, 0x0E, str_idx, 0x23, 0x00 }; 2498c2ecf20Sopenharmony_ci u8 rx_buf[PKT_WRITE_SIZE]; 2508c2ecf20Sopenharmony_ci size_t rx_len = len + 2; 2518c2ecf20Sopenharmony_ci int error; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci if (rx_len > sizeof(rx_buf)) 2548c2ecf20Sopenharmony_ci return -EINVAL; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci error = wdt87xx_i2c_xfer(client, tx_buf, sizeof(tx_buf), 2578c2ecf20Sopenharmony_ci rx_buf, rx_len); 2588c2ecf20Sopenharmony_ci if (error) { 2598c2ecf20Sopenharmony_ci dev_err(&client->dev, "get string failed: %d\n", error); 2608c2ecf20Sopenharmony_ci return error; 2618c2ecf20Sopenharmony_ci } 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci if (rx_buf[1] != 0x03) { 2648c2ecf20Sopenharmony_ci dev_err(&client->dev, "unexpected response to get string: %d\n", 2658c2ecf20Sopenharmony_ci rx_buf[1]); 2668c2ecf20Sopenharmony_ci return -EINVAL; 2678c2ecf20Sopenharmony_ci } 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci rx_len = min_t(size_t, len, rx_buf[0]); 2708c2ecf20Sopenharmony_ci memcpy(buf, &rx_buf[2], rx_len); 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci mdelay(WDT_COMMAND_DELAY_MS); 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci return 0; 2758c2ecf20Sopenharmony_ci} 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_cistatic int wdt87xx_get_feature(struct i2c_client *client, 2788c2ecf20Sopenharmony_ci u8 *buf, size_t buf_size) 2798c2ecf20Sopenharmony_ci{ 2808c2ecf20Sopenharmony_ci u8 tx_buf[8]; 2818c2ecf20Sopenharmony_ci u8 rx_buf[PKT_WRITE_SIZE]; 2828c2ecf20Sopenharmony_ci size_t tx_len = 0; 2838c2ecf20Sopenharmony_ci size_t rx_len = buf_size + 2; 2848c2ecf20Sopenharmony_ci int error; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci if (rx_len > sizeof(rx_buf)) 2878c2ecf20Sopenharmony_ci return -EINVAL; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci /* Get feature command packet */ 2908c2ecf20Sopenharmony_ci tx_buf[tx_len++] = 0x22; 2918c2ecf20Sopenharmony_ci tx_buf[tx_len++] = 0x00; 2928c2ecf20Sopenharmony_ci if (buf[CMD_REPORT_ID_OFFSET] > 0xF) { 2938c2ecf20Sopenharmony_ci tx_buf[tx_len++] = 0x30; 2948c2ecf20Sopenharmony_ci tx_buf[tx_len++] = 0x02; 2958c2ecf20Sopenharmony_ci tx_buf[tx_len++] = buf[CMD_REPORT_ID_OFFSET]; 2968c2ecf20Sopenharmony_ci } else { 2978c2ecf20Sopenharmony_ci tx_buf[tx_len++] = 0x30 | buf[CMD_REPORT_ID_OFFSET]; 2988c2ecf20Sopenharmony_ci tx_buf[tx_len++] = 0x02; 2998c2ecf20Sopenharmony_ci } 3008c2ecf20Sopenharmony_ci tx_buf[tx_len++] = 0x23; 3018c2ecf20Sopenharmony_ci tx_buf[tx_len++] = 0x00; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci error = wdt87xx_i2c_xfer(client, tx_buf, tx_len, rx_buf, rx_len); 3048c2ecf20Sopenharmony_ci if (error) { 3058c2ecf20Sopenharmony_ci dev_err(&client->dev, "get feature failed: %d\n", error); 3068c2ecf20Sopenharmony_ci return error; 3078c2ecf20Sopenharmony_ci } 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci rx_len = min_t(size_t, buf_size, get_unaligned_le16(rx_buf)); 3108c2ecf20Sopenharmony_ci memcpy(buf, &rx_buf[2], rx_len); 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci mdelay(WDT_COMMAND_DELAY_MS); 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci return 0; 3158c2ecf20Sopenharmony_ci} 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_cistatic int wdt87xx_set_feature(struct i2c_client *client, 3188c2ecf20Sopenharmony_ci const u8 *buf, size_t buf_size) 3198c2ecf20Sopenharmony_ci{ 3208c2ecf20Sopenharmony_ci u8 tx_buf[PKT_WRITE_SIZE]; 3218c2ecf20Sopenharmony_ci int tx_len = 0; 3228c2ecf20Sopenharmony_ci int error; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci /* Set feature command packet */ 3258c2ecf20Sopenharmony_ci tx_buf[tx_len++] = 0x22; 3268c2ecf20Sopenharmony_ci tx_buf[tx_len++] = 0x00; 3278c2ecf20Sopenharmony_ci if (buf[CMD_REPORT_ID_OFFSET] > 0xF) { 3288c2ecf20Sopenharmony_ci tx_buf[tx_len++] = 0x30; 3298c2ecf20Sopenharmony_ci tx_buf[tx_len++] = 0x03; 3308c2ecf20Sopenharmony_ci tx_buf[tx_len++] = buf[CMD_REPORT_ID_OFFSET]; 3318c2ecf20Sopenharmony_ci } else { 3328c2ecf20Sopenharmony_ci tx_buf[tx_len++] = 0x30 | buf[CMD_REPORT_ID_OFFSET]; 3338c2ecf20Sopenharmony_ci tx_buf[tx_len++] = 0x03; 3348c2ecf20Sopenharmony_ci } 3358c2ecf20Sopenharmony_ci tx_buf[tx_len++] = 0x23; 3368c2ecf20Sopenharmony_ci tx_buf[tx_len++] = 0x00; 3378c2ecf20Sopenharmony_ci tx_buf[tx_len++] = (buf_size & 0xFF); 3388c2ecf20Sopenharmony_ci tx_buf[tx_len++] = ((buf_size & 0xFF00) >> 8); 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci if (tx_len + buf_size > sizeof(tx_buf)) 3418c2ecf20Sopenharmony_ci return -EINVAL; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci memcpy(&tx_buf[tx_len], buf, buf_size); 3448c2ecf20Sopenharmony_ci tx_len += buf_size; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci error = i2c_master_send(client, tx_buf, tx_len); 3478c2ecf20Sopenharmony_ci if (error < 0) { 3488c2ecf20Sopenharmony_ci dev_err(&client->dev, "set feature failed: %d\n", error); 3498c2ecf20Sopenharmony_ci return error; 3508c2ecf20Sopenharmony_ci } 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci mdelay(WDT_COMMAND_DELAY_MS); 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci return 0; 3558c2ecf20Sopenharmony_ci} 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_cistatic int wdt87xx_send_command(struct i2c_client *client, int cmd, int value) 3588c2ecf20Sopenharmony_ci{ 3598c2ecf20Sopenharmony_ci u8 cmd_buf[CMD_BUF_SIZE]; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci /* Set the command packet */ 3628c2ecf20Sopenharmony_ci cmd_buf[CMD_REPORT_ID_OFFSET] = VND_REQ_WRITE; 3638c2ecf20Sopenharmony_ci cmd_buf[CMD_TYPE_OFFSET] = VND_SET_COMMAND_DATA; 3648c2ecf20Sopenharmony_ci put_unaligned_le16((u16)cmd, &cmd_buf[CMD_INDEX_OFFSET]); 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci switch (cmd) { 3678c2ecf20Sopenharmony_ci case VND_CMD_START: 3688c2ecf20Sopenharmony_ci case VND_CMD_STOP: 3698c2ecf20Sopenharmony_ci case VND_CMD_RESET: 3708c2ecf20Sopenharmony_ci /* Mode selector */ 3718c2ecf20Sopenharmony_ci put_unaligned_le32((value & 0xFF), &cmd_buf[CMD_LENGTH_OFFSET]); 3728c2ecf20Sopenharmony_ci break; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci case VND_CMD_SFLCK: 3758c2ecf20Sopenharmony_ci put_unaligned_le16(CMD_SFLCK_KEY, &cmd_buf[CMD_KEY_OFFSET]); 3768c2ecf20Sopenharmony_ci break; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci case VND_CMD_SFUNL: 3798c2ecf20Sopenharmony_ci put_unaligned_le16(CMD_SFUNL_KEY, &cmd_buf[CMD_KEY_OFFSET]); 3808c2ecf20Sopenharmony_ci break; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci case VND_CMD_ERASE: 3838c2ecf20Sopenharmony_ci case VND_SET_CHECKSUM_CALC: 3848c2ecf20Sopenharmony_ci case VND_SET_CHECKSUM_LENGTH: 3858c2ecf20Sopenharmony_ci put_unaligned_le32(value, &cmd_buf[CMD_KEY_OFFSET]); 3868c2ecf20Sopenharmony_ci break; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci default: 3898c2ecf20Sopenharmony_ci cmd_buf[CMD_REPORT_ID_OFFSET] = 0; 3908c2ecf20Sopenharmony_ci dev_err(&client->dev, "Invalid command: %d\n", cmd); 3918c2ecf20Sopenharmony_ci return -EINVAL; 3928c2ecf20Sopenharmony_ci } 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci return wdt87xx_set_feature(client, cmd_buf, sizeof(cmd_buf)); 3958c2ecf20Sopenharmony_ci} 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_cistatic int wdt87xx_sw_reset(struct i2c_client *client) 3988c2ecf20Sopenharmony_ci{ 3998c2ecf20Sopenharmony_ci int error; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci dev_dbg(&client->dev, "resetting device now\n"); 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci error = wdt87xx_send_command(client, VND_CMD_RESET, 0); 4048c2ecf20Sopenharmony_ci if (error) { 4058c2ecf20Sopenharmony_ci dev_err(&client->dev, "reset failed\n"); 4068c2ecf20Sopenharmony_ci return error; 4078c2ecf20Sopenharmony_ci } 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci /* Wait the device to be ready */ 4108c2ecf20Sopenharmony_ci msleep(WDT_FW_RESET_TIME); 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci return 0; 4138c2ecf20Sopenharmony_ci} 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_cistatic const void *wdt87xx_get_fw_chunk(const struct firmware *fw, u32 id) 4168c2ecf20Sopenharmony_ci{ 4178c2ecf20Sopenharmony_ci size_t pos = FW_PAYLOAD_OFFSET; 4188c2ecf20Sopenharmony_ci u32 chunk_id, chunk_size; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci while (pos < fw->size) { 4218c2ecf20Sopenharmony_ci chunk_id = get_unaligned_le32(fw->data + 4228c2ecf20Sopenharmony_ci pos + FW_CHUNK_ID_OFFSET); 4238c2ecf20Sopenharmony_ci if (chunk_id == id) 4248c2ecf20Sopenharmony_ci return fw->data + pos; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci chunk_size = get_unaligned_le32(fw->data + 4278c2ecf20Sopenharmony_ci pos + FW_CHUNK_SIZE_OFFSET); 4288c2ecf20Sopenharmony_ci pos += chunk_size + 2 * sizeof(u32); /* chunk ID + size */ 4298c2ecf20Sopenharmony_ci } 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci return NULL; 4328c2ecf20Sopenharmony_ci} 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_cistatic int wdt87xx_get_sysparam(struct i2c_client *client, 4358c2ecf20Sopenharmony_ci struct wdt87xx_sys_param *param) 4368c2ecf20Sopenharmony_ci{ 4378c2ecf20Sopenharmony_ci u8 buf[PKT_READ_SIZE]; 4388c2ecf20Sopenharmony_ci int error; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci error = wdt87xx_get_desc(client, WDT_GD_DEVICE, buf, 18); 4418c2ecf20Sopenharmony_ci if (error) { 4428c2ecf20Sopenharmony_ci dev_err(&client->dev, "failed to get device desc\n"); 4438c2ecf20Sopenharmony_ci return error; 4448c2ecf20Sopenharmony_ci } 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci param->vendor_id = get_unaligned_le16(buf + DEV_DESC_OFFSET_VID); 4478c2ecf20Sopenharmony_ci param->product_id = get_unaligned_le16(buf + DEV_DESC_OFFSET_PID); 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci error = wdt87xx_get_string(client, STRIDX_PARAMETERS, buf, 34); 4508c2ecf20Sopenharmony_ci if (error) { 4518c2ecf20Sopenharmony_ci dev_err(&client->dev, "failed to get parameters\n"); 4528c2ecf20Sopenharmony_ci return error; 4538c2ecf20Sopenharmony_ci } 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci param->xmls_id1 = get_unaligned_le16(buf + CTL_PARAM_OFFSET_XMLS_ID1); 4568c2ecf20Sopenharmony_ci param->xmls_id2 = get_unaligned_le16(buf + CTL_PARAM_OFFSET_XMLS_ID2); 4578c2ecf20Sopenharmony_ci param->phy_ch_x = get_unaligned_le16(buf + CTL_PARAM_OFFSET_PHY_CH_X); 4588c2ecf20Sopenharmony_ci param->phy_ch_y = get_unaligned_le16(buf + CTL_PARAM_OFFSET_PHY_CH_Y); 4598c2ecf20Sopenharmony_ci param->phy_w = get_unaligned_le16(buf + CTL_PARAM_OFFSET_PHY_W) / 10; 4608c2ecf20Sopenharmony_ci param->phy_h = get_unaligned_le16(buf + CTL_PARAM_OFFSET_PHY_H) / 10; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci /* Get the scaling factor of pixel to logical coordinate */ 4638c2ecf20Sopenharmony_ci param->scaling_factor = 4648c2ecf20Sopenharmony_ci get_unaligned_le16(buf + CTL_PARAM_OFFSET_FACTOR); 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci param->max_x = MAX_UNIT_AXIS; 4678c2ecf20Sopenharmony_ci param->max_y = DIV_ROUND_CLOSEST(MAX_UNIT_AXIS * param->phy_h, 4688c2ecf20Sopenharmony_ci param->phy_w); 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci error = wdt87xx_get_string(client, STRIDX_PLATFORM_ID, buf, 8); 4718c2ecf20Sopenharmony_ci if (error) { 4728c2ecf20Sopenharmony_ci dev_err(&client->dev, "failed to get platform id\n"); 4738c2ecf20Sopenharmony_ci return error; 4748c2ecf20Sopenharmony_ci } 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci param->plat_id = buf[1]; 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci buf[0] = 0xf2; 4798c2ecf20Sopenharmony_ci error = wdt87xx_get_feature(client, buf, 16); 4808c2ecf20Sopenharmony_ci if (error) { 4818c2ecf20Sopenharmony_ci dev_err(&client->dev, "failed to get firmware id\n"); 4828c2ecf20Sopenharmony_ci return error; 4838c2ecf20Sopenharmony_ci } 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci if (buf[0] != 0xf2) { 4868c2ecf20Sopenharmony_ci dev_err(&client->dev, "wrong id of fw response: 0x%x\n", 4878c2ecf20Sopenharmony_ci buf[0]); 4888c2ecf20Sopenharmony_ci return -EINVAL; 4898c2ecf20Sopenharmony_ci } 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci param->fw_id = get_unaligned_le16(&buf[1]); 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci dev_info(&client->dev, 4948c2ecf20Sopenharmony_ci "fw_id: 0x%x, plat_id: 0x%x, xml_id1: %04x, xml_id2: %04x\n", 4958c2ecf20Sopenharmony_ci param->fw_id, param->plat_id, 4968c2ecf20Sopenharmony_ci param->xmls_id1, param->xmls_id2); 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci return 0; 4998c2ecf20Sopenharmony_ci} 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_cistatic int wdt87xx_validate_firmware(struct wdt87xx_data *wdt, 5028c2ecf20Sopenharmony_ci const struct firmware *fw) 5038c2ecf20Sopenharmony_ci{ 5048c2ecf20Sopenharmony_ci const void *fw_chunk; 5058c2ecf20Sopenharmony_ci u32 data1, data2; 5068c2ecf20Sopenharmony_ci u32 size; 5078c2ecf20Sopenharmony_ci u8 fw_chip_id; 5088c2ecf20Sopenharmony_ci u8 chip_id; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci data1 = get_unaligned_le32(fw->data + FW_FOURCC1_OFFSET); 5118c2ecf20Sopenharmony_ci data2 = get_unaligned_le32(fw->data + FW_FOURCC2_OFFSET); 5128c2ecf20Sopenharmony_ci if (data1 != FOURCC_ID_RIFF || data2 != FOURCC_ID_WHIF) { 5138c2ecf20Sopenharmony_ci dev_err(&wdt->client->dev, "check fw tag failed\n"); 5148c2ecf20Sopenharmony_ci return -EINVAL; 5158c2ecf20Sopenharmony_ci } 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci size = get_unaligned_le32(fw->data + FW_SIZE_OFFSET); 5188c2ecf20Sopenharmony_ci if (size != fw->size) { 5198c2ecf20Sopenharmony_ci dev_err(&wdt->client->dev, 5208c2ecf20Sopenharmony_ci "fw size mismatch: expected %d, actual %zu\n", 5218c2ecf20Sopenharmony_ci size, fw->size); 5228c2ecf20Sopenharmony_ci return -EINVAL; 5238c2ecf20Sopenharmony_ci } 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci /* 5268c2ecf20Sopenharmony_ci * Get the chip_id from the firmware. Make sure that it is the 5278c2ecf20Sopenharmony_ci * right controller to do the firmware and config update. 5288c2ecf20Sopenharmony_ci */ 5298c2ecf20Sopenharmony_ci fw_chunk = wdt87xx_get_fw_chunk(fw, CHUNK_ID_FRWR); 5308c2ecf20Sopenharmony_ci if (!fw_chunk) { 5318c2ecf20Sopenharmony_ci dev_err(&wdt->client->dev, 5328c2ecf20Sopenharmony_ci "unable to locate firmware chunk\n"); 5338c2ecf20Sopenharmony_ci return -EINVAL; 5348c2ecf20Sopenharmony_ci } 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci fw_chip_id = (get_unaligned_le32(fw_chunk + 5378c2ecf20Sopenharmony_ci FW_CHUNK_VERSION_OFFSET) >> 12) & 0xF; 5388c2ecf20Sopenharmony_ci chip_id = (wdt->param.fw_id >> 12) & 0xF; 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci if (fw_chip_id != chip_id) { 5418c2ecf20Sopenharmony_ci dev_err(&wdt->client->dev, 5428c2ecf20Sopenharmony_ci "fw version mismatch: fw %d vs. chip %d\n", 5438c2ecf20Sopenharmony_ci fw_chip_id, chip_id); 5448c2ecf20Sopenharmony_ci return -ENODEV; 5458c2ecf20Sopenharmony_ci } 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci return 0; 5488c2ecf20Sopenharmony_ci} 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_cistatic int wdt87xx_validate_fw_chunk(const void *data, int id) 5518c2ecf20Sopenharmony_ci{ 5528c2ecf20Sopenharmony_ci if (id == CHUNK_ID_FRWR) { 5538c2ecf20Sopenharmony_ci u32 fw_id; 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci fw_id = get_unaligned_le32(data + FW_CHUNK_PAYLOAD_OFFSET); 5568c2ecf20Sopenharmony_ci if (fw_id != WDT_FIRMWARE_ID) 5578c2ecf20Sopenharmony_ci return -EINVAL; 5588c2ecf20Sopenharmony_ci } 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci return 0; 5618c2ecf20Sopenharmony_ci} 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_cistatic int wdt87xx_write_data(struct i2c_client *client, const char *data, 5648c2ecf20Sopenharmony_ci u32 address, int length) 5658c2ecf20Sopenharmony_ci{ 5668c2ecf20Sopenharmony_ci u16 packet_size; 5678c2ecf20Sopenharmony_ci int count = 0; 5688c2ecf20Sopenharmony_ci int error; 5698c2ecf20Sopenharmony_ci u8 pkt_buf[PKT_BUF_SIZE]; 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci /* Address and length should be 4 bytes aligned */ 5728c2ecf20Sopenharmony_ci if ((address & 0x3) != 0 || (length & 0x3) != 0) { 5738c2ecf20Sopenharmony_ci dev_err(&client->dev, 5748c2ecf20Sopenharmony_ci "addr & len must be 4 bytes aligned %x, %x\n", 5758c2ecf20Sopenharmony_ci address, length); 5768c2ecf20Sopenharmony_ci return -EINVAL; 5778c2ecf20Sopenharmony_ci } 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci while (length) { 5808c2ecf20Sopenharmony_ci packet_size = min(length, PACKET_SIZE); 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci pkt_buf[CMD_REPORT_ID_OFFSET] = VND_REQ_WRITE; 5838c2ecf20Sopenharmony_ci pkt_buf[CMD_TYPE_OFFSET] = VND_SET_DATA; 5848c2ecf20Sopenharmony_ci put_unaligned_le16(packet_size, &pkt_buf[CMD_INDEX_OFFSET]); 5858c2ecf20Sopenharmony_ci put_unaligned_le32(address, &pkt_buf[CMD_LENGTH_OFFSET]); 5868c2ecf20Sopenharmony_ci memcpy(&pkt_buf[CMD_DATA_OFFSET], data, packet_size); 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci error = wdt87xx_set_feature(client, pkt_buf, sizeof(pkt_buf)); 5898c2ecf20Sopenharmony_ci if (error) 5908c2ecf20Sopenharmony_ci return error; 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci length -= packet_size; 5938c2ecf20Sopenharmony_ci data += packet_size; 5948c2ecf20Sopenharmony_ci address += packet_size; 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci /* Wait for the controller to finish the write */ 5978c2ecf20Sopenharmony_ci mdelay(WDT_FLASH_WRITE_DELAY_MS); 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci if ((++count % 32) == 0) { 6008c2ecf20Sopenharmony_ci /* Delay for fw to clear watch dog */ 6018c2ecf20Sopenharmony_ci msleep(20); 6028c2ecf20Sopenharmony_ci } 6038c2ecf20Sopenharmony_ci } 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci return 0; 6068c2ecf20Sopenharmony_ci} 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_cistatic u16 misr(u16 cur_value, u8 new_value) 6098c2ecf20Sopenharmony_ci{ 6108c2ecf20Sopenharmony_ci u32 a, b; 6118c2ecf20Sopenharmony_ci u32 bit0; 6128c2ecf20Sopenharmony_ci u32 y; 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci a = cur_value; 6158c2ecf20Sopenharmony_ci b = new_value; 6168c2ecf20Sopenharmony_ci bit0 = a ^ (b & 1); 6178c2ecf20Sopenharmony_ci bit0 ^= a >> 1; 6188c2ecf20Sopenharmony_ci bit0 ^= a >> 2; 6198c2ecf20Sopenharmony_ci bit0 ^= a >> 4; 6208c2ecf20Sopenharmony_ci bit0 ^= a >> 5; 6218c2ecf20Sopenharmony_ci bit0 ^= a >> 7; 6228c2ecf20Sopenharmony_ci bit0 ^= a >> 11; 6238c2ecf20Sopenharmony_ci bit0 ^= a >> 15; 6248c2ecf20Sopenharmony_ci y = (a << 1) ^ b; 6258c2ecf20Sopenharmony_ci y = (y & ~1) | (bit0 & 1); 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci return (u16)y; 6288c2ecf20Sopenharmony_ci} 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_cistatic u16 wdt87xx_calculate_checksum(const u8 *data, size_t length) 6318c2ecf20Sopenharmony_ci{ 6328c2ecf20Sopenharmony_ci u16 checksum = 0; 6338c2ecf20Sopenharmony_ci size_t i; 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci for (i = 0; i < length; i++) 6368c2ecf20Sopenharmony_ci checksum = misr(checksum, data[i]); 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci return checksum; 6398c2ecf20Sopenharmony_ci} 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_cistatic int wdt87xx_get_checksum(struct i2c_client *client, u16 *checksum, 6428c2ecf20Sopenharmony_ci u32 address, int length) 6438c2ecf20Sopenharmony_ci{ 6448c2ecf20Sopenharmony_ci int error; 6458c2ecf20Sopenharmony_ci int time_delay; 6468c2ecf20Sopenharmony_ci u8 pkt_buf[PKT_BUF_SIZE]; 6478c2ecf20Sopenharmony_ci u8 cmd_buf[CMD_BUF_SIZE]; 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci error = wdt87xx_send_command(client, VND_SET_CHECKSUM_LENGTH, length); 6508c2ecf20Sopenharmony_ci if (error) { 6518c2ecf20Sopenharmony_ci dev_err(&client->dev, "failed to set checksum length\n"); 6528c2ecf20Sopenharmony_ci return error; 6538c2ecf20Sopenharmony_ci } 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci error = wdt87xx_send_command(client, VND_SET_CHECKSUM_CALC, address); 6568c2ecf20Sopenharmony_ci if (error) { 6578c2ecf20Sopenharmony_ci dev_err(&client->dev, "failed to set checksum address\n"); 6588c2ecf20Sopenharmony_ci return error; 6598c2ecf20Sopenharmony_ci } 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci /* Wait the operation to complete */ 6628c2ecf20Sopenharmony_ci time_delay = DIV_ROUND_UP(length, 1024); 6638c2ecf20Sopenharmony_ci msleep(time_delay * 30); 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci memset(cmd_buf, 0, sizeof(cmd_buf)); 6668c2ecf20Sopenharmony_ci cmd_buf[CMD_REPORT_ID_OFFSET] = VND_REQ_READ; 6678c2ecf20Sopenharmony_ci cmd_buf[CMD_TYPE_OFFSET] = VND_GET_CHECKSUM; 6688c2ecf20Sopenharmony_ci error = wdt87xx_set_feature(client, cmd_buf, sizeof(cmd_buf)); 6698c2ecf20Sopenharmony_ci if (error) { 6708c2ecf20Sopenharmony_ci dev_err(&client->dev, "failed to request checksum\n"); 6718c2ecf20Sopenharmony_ci return error; 6728c2ecf20Sopenharmony_ci } 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci memset(pkt_buf, 0, sizeof(pkt_buf)); 6758c2ecf20Sopenharmony_ci pkt_buf[CMD_REPORT_ID_OFFSET] = VND_READ_DATA; 6768c2ecf20Sopenharmony_ci error = wdt87xx_get_feature(client, pkt_buf, sizeof(pkt_buf)); 6778c2ecf20Sopenharmony_ci if (error) { 6788c2ecf20Sopenharmony_ci dev_err(&client->dev, "failed to read checksum\n"); 6798c2ecf20Sopenharmony_ci return error; 6808c2ecf20Sopenharmony_ci } 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci *checksum = get_unaligned_le16(&pkt_buf[CMD_DATA_OFFSET]); 6838c2ecf20Sopenharmony_ci return 0; 6848c2ecf20Sopenharmony_ci} 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_cistatic int wdt87xx_write_firmware(struct i2c_client *client, const void *chunk) 6878c2ecf20Sopenharmony_ci{ 6888c2ecf20Sopenharmony_ci u32 start_addr = get_unaligned_le32(chunk + FW_CHUNK_TGT_START_OFFSET); 6898c2ecf20Sopenharmony_ci u32 size = get_unaligned_le32(chunk + FW_CHUNK_PAYLOAD_LEN_OFFSET); 6908c2ecf20Sopenharmony_ci const void *data = chunk + FW_CHUNK_PAYLOAD_OFFSET; 6918c2ecf20Sopenharmony_ci int error; 6928c2ecf20Sopenharmony_ci int err1; 6938c2ecf20Sopenharmony_ci int page_size; 6948c2ecf20Sopenharmony_ci int retry = 0; 6958c2ecf20Sopenharmony_ci u16 device_checksum, firmware_checksum; 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci dev_dbg(&client->dev, "start 4k page program\n"); 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci error = wdt87xx_send_command(client, VND_CMD_STOP, MODE_STOP); 7008c2ecf20Sopenharmony_ci if (error) { 7018c2ecf20Sopenharmony_ci dev_err(&client->dev, "stop report mode failed\n"); 7028c2ecf20Sopenharmony_ci return error; 7038c2ecf20Sopenharmony_ci } 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci error = wdt87xx_send_command(client, VND_CMD_SFUNL, 0); 7068c2ecf20Sopenharmony_ci if (error) { 7078c2ecf20Sopenharmony_ci dev_err(&client->dev, "unlock failed\n"); 7088c2ecf20Sopenharmony_ci goto out_enable_reporting; 7098c2ecf20Sopenharmony_ci } 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci mdelay(10); 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci while (size) { 7148c2ecf20Sopenharmony_ci dev_dbg(&client->dev, "%s: %x, %x\n", __func__, 7158c2ecf20Sopenharmony_ci start_addr, size); 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci page_size = min_t(u32, size, PG_SIZE); 7188c2ecf20Sopenharmony_ci size -= page_size; 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci for (retry = 0; retry < MAX_RETRIES; retry++) { 7218c2ecf20Sopenharmony_ci error = wdt87xx_send_command(client, VND_CMD_ERASE, 7228c2ecf20Sopenharmony_ci start_addr); 7238c2ecf20Sopenharmony_ci if (error) { 7248c2ecf20Sopenharmony_ci dev_err(&client->dev, 7258c2ecf20Sopenharmony_ci "erase failed at %#08x\n", start_addr); 7268c2ecf20Sopenharmony_ci break; 7278c2ecf20Sopenharmony_ci } 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci msleep(WDT_FLASH_ERASE_DELAY_MS); 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci error = wdt87xx_write_data(client, data, start_addr, 7328c2ecf20Sopenharmony_ci page_size); 7338c2ecf20Sopenharmony_ci if (error) { 7348c2ecf20Sopenharmony_ci dev_err(&client->dev, 7358c2ecf20Sopenharmony_ci "write failed at %#08x (%d bytes)\n", 7368c2ecf20Sopenharmony_ci start_addr, page_size); 7378c2ecf20Sopenharmony_ci break; 7388c2ecf20Sopenharmony_ci } 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci error = wdt87xx_get_checksum(client, &device_checksum, 7418c2ecf20Sopenharmony_ci start_addr, page_size); 7428c2ecf20Sopenharmony_ci if (error) { 7438c2ecf20Sopenharmony_ci dev_err(&client->dev, 7448c2ecf20Sopenharmony_ci "failed to retrieve checksum for %#08x (len: %d)\n", 7458c2ecf20Sopenharmony_ci start_addr, page_size); 7468c2ecf20Sopenharmony_ci break; 7478c2ecf20Sopenharmony_ci } 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci firmware_checksum = 7508c2ecf20Sopenharmony_ci wdt87xx_calculate_checksum(data, page_size); 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci if (device_checksum == firmware_checksum) 7538c2ecf20Sopenharmony_ci break; 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci dev_err(&client->dev, 7568c2ecf20Sopenharmony_ci "checksum fail: %d vs %d, retry %d\n", 7578c2ecf20Sopenharmony_ci device_checksum, firmware_checksum, retry); 7588c2ecf20Sopenharmony_ci } 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci if (retry == MAX_RETRIES) { 7618c2ecf20Sopenharmony_ci dev_err(&client->dev, "page write failed\n"); 7628c2ecf20Sopenharmony_ci error = -EIO; 7638c2ecf20Sopenharmony_ci goto out_lock_device; 7648c2ecf20Sopenharmony_ci } 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci start_addr = start_addr + page_size; 7678c2ecf20Sopenharmony_ci data = data + page_size; 7688c2ecf20Sopenharmony_ci } 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_ciout_lock_device: 7718c2ecf20Sopenharmony_ci err1 = wdt87xx_send_command(client, VND_CMD_SFLCK, 0); 7728c2ecf20Sopenharmony_ci if (err1) 7738c2ecf20Sopenharmony_ci dev_err(&client->dev, "lock failed\n"); 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci mdelay(10); 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ciout_enable_reporting: 7788c2ecf20Sopenharmony_ci err1 = wdt87xx_send_command(client, VND_CMD_START, 0); 7798c2ecf20Sopenharmony_ci if (err1) 7808c2ecf20Sopenharmony_ci dev_err(&client->dev, "start to report failed\n"); 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci return error ? error : err1; 7838c2ecf20Sopenharmony_ci} 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_cistatic int wdt87xx_load_chunk(struct i2c_client *client, 7868c2ecf20Sopenharmony_ci const struct firmware *fw, u32 ck_id) 7878c2ecf20Sopenharmony_ci{ 7888c2ecf20Sopenharmony_ci const void *chunk; 7898c2ecf20Sopenharmony_ci int error; 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci chunk = wdt87xx_get_fw_chunk(fw, ck_id); 7928c2ecf20Sopenharmony_ci if (!chunk) { 7938c2ecf20Sopenharmony_ci dev_err(&client->dev, "unable to locate chunk (type %d)\n", 7948c2ecf20Sopenharmony_ci ck_id); 7958c2ecf20Sopenharmony_ci return -EINVAL; 7968c2ecf20Sopenharmony_ci } 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci error = wdt87xx_validate_fw_chunk(chunk, ck_id); 7998c2ecf20Sopenharmony_ci if (error) { 8008c2ecf20Sopenharmony_ci dev_err(&client->dev, "invalid chunk (type %d): %d\n", 8018c2ecf20Sopenharmony_ci ck_id, error); 8028c2ecf20Sopenharmony_ci return error; 8038c2ecf20Sopenharmony_ci } 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci error = wdt87xx_write_firmware(client, chunk); 8068c2ecf20Sopenharmony_ci if (error) { 8078c2ecf20Sopenharmony_ci dev_err(&client->dev, 8088c2ecf20Sopenharmony_ci "failed to write fw chunk (type %d): %d\n", 8098c2ecf20Sopenharmony_ci ck_id, error); 8108c2ecf20Sopenharmony_ci return error; 8118c2ecf20Sopenharmony_ci } 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci return 0; 8148c2ecf20Sopenharmony_ci} 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_cistatic int wdt87xx_do_update_firmware(struct i2c_client *client, 8178c2ecf20Sopenharmony_ci const struct firmware *fw, 8188c2ecf20Sopenharmony_ci unsigned int chunk_id) 8198c2ecf20Sopenharmony_ci{ 8208c2ecf20Sopenharmony_ci struct wdt87xx_data *wdt = i2c_get_clientdata(client); 8218c2ecf20Sopenharmony_ci int error; 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci error = wdt87xx_validate_firmware(wdt, fw); 8248c2ecf20Sopenharmony_ci if (error) 8258c2ecf20Sopenharmony_ci return error; 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci error = mutex_lock_interruptible(&wdt->fw_mutex); 8288c2ecf20Sopenharmony_ci if (error) 8298c2ecf20Sopenharmony_ci return error; 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_ci disable_irq(client->irq); 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci error = wdt87xx_load_chunk(client, fw, chunk_id); 8348c2ecf20Sopenharmony_ci if (error) { 8358c2ecf20Sopenharmony_ci dev_err(&client->dev, 8368c2ecf20Sopenharmony_ci "firmware load failed (type: %d): %d\n", 8378c2ecf20Sopenharmony_ci chunk_id, error); 8388c2ecf20Sopenharmony_ci goto out; 8398c2ecf20Sopenharmony_ci } 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci error = wdt87xx_sw_reset(client); 8428c2ecf20Sopenharmony_ci if (error) { 8438c2ecf20Sopenharmony_ci dev_err(&client->dev, "soft reset failed: %d\n", error); 8448c2ecf20Sopenharmony_ci goto out; 8458c2ecf20Sopenharmony_ci } 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_ci /* Refresh the parameters */ 8488c2ecf20Sopenharmony_ci error = wdt87xx_get_sysparam(client, &wdt->param); 8498c2ecf20Sopenharmony_ci if (error) 8508c2ecf20Sopenharmony_ci dev_err(&client->dev, 8518c2ecf20Sopenharmony_ci "failed to refresh system parameters: %d\n", error); 8528c2ecf20Sopenharmony_ciout: 8538c2ecf20Sopenharmony_ci enable_irq(client->irq); 8548c2ecf20Sopenharmony_ci mutex_unlock(&wdt->fw_mutex); 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_ci return error ? error : 0; 8578c2ecf20Sopenharmony_ci} 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_cistatic int wdt87xx_update_firmware(struct device *dev, 8608c2ecf20Sopenharmony_ci const char *fw_name, unsigned int chunk_id) 8618c2ecf20Sopenharmony_ci{ 8628c2ecf20Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 8638c2ecf20Sopenharmony_ci const struct firmware *fw; 8648c2ecf20Sopenharmony_ci int error; 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_ci error = request_firmware(&fw, fw_name, dev); 8678c2ecf20Sopenharmony_ci if (error) { 8688c2ecf20Sopenharmony_ci dev_err(&client->dev, "unable to retrieve firmware %s: %d\n", 8698c2ecf20Sopenharmony_ci fw_name, error); 8708c2ecf20Sopenharmony_ci return error; 8718c2ecf20Sopenharmony_ci } 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci error = wdt87xx_do_update_firmware(client, fw, chunk_id); 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_ci release_firmware(fw); 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci return error ? error : 0; 8788c2ecf20Sopenharmony_ci} 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_cistatic ssize_t config_csum_show(struct device *dev, 8818c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 8828c2ecf20Sopenharmony_ci{ 8838c2ecf20Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 8848c2ecf20Sopenharmony_ci struct wdt87xx_data *wdt = i2c_get_clientdata(client); 8858c2ecf20Sopenharmony_ci u32 cfg_csum; 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci cfg_csum = wdt->param.xmls_id1; 8888c2ecf20Sopenharmony_ci cfg_csum = (cfg_csum << 16) | wdt->param.xmls_id2; 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci return scnprintf(buf, PAGE_SIZE, "%x\n", cfg_csum); 8918c2ecf20Sopenharmony_ci} 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_cistatic ssize_t fw_version_show(struct device *dev, 8948c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 8958c2ecf20Sopenharmony_ci{ 8968c2ecf20Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 8978c2ecf20Sopenharmony_ci struct wdt87xx_data *wdt = i2c_get_clientdata(client); 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci return scnprintf(buf, PAGE_SIZE, "%x\n", wdt->param.fw_id); 9008c2ecf20Sopenharmony_ci} 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_cistatic ssize_t plat_id_show(struct device *dev, 9038c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 9048c2ecf20Sopenharmony_ci{ 9058c2ecf20Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 9068c2ecf20Sopenharmony_ci struct wdt87xx_data *wdt = i2c_get_clientdata(client); 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_ci return scnprintf(buf, PAGE_SIZE, "%x\n", wdt->param.plat_id); 9098c2ecf20Sopenharmony_ci} 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_cistatic ssize_t update_config_store(struct device *dev, 9128c2ecf20Sopenharmony_ci struct device_attribute *attr, 9138c2ecf20Sopenharmony_ci const char *buf, size_t count) 9148c2ecf20Sopenharmony_ci{ 9158c2ecf20Sopenharmony_ci int error; 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_ci error = wdt87xx_update_firmware(dev, WDT87XX_CFG_NAME, CHUNK_ID_CNFG); 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_ci return error ? error : count; 9208c2ecf20Sopenharmony_ci} 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_cistatic ssize_t update_fw_store(struct device *dev, 9238c2ecf20Sopenharmony_ci struct device_attribute *attr, 9248c2ecf20Sopenharmony_ci const char *buf, size_t count) 9258c2ecf20Sopenharmony_ci{ 9268c2ecf20Sopenharmony_ci int error; 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_ci error = wdt87xx_update_firmware(dev, WDT87XX_FW_NAME, CHUNK_ID_FRWR); 9298c2ecf20Sopenharmony_ci 9308c2ecf20Sopenharmony_ci return error ? error : count; 9318c2ecf20Sopenharmony_ci} 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(config_csum); 9348c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(fw_version); 9358c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(plat_id); 9368c2ecf20Sopenharmony_cistatic DEVICE_ATTR_WO(update_config); 9378c2ecf20Sopenharmony_cistatic DEVICE_ATTR_WO(update_fw); 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_cistatic struct attribute *wdt87xx_attrs[] = { 9408c2ecf20Sopenharmony_ci &dev_attr_config_csum.attr, 9418c2ecf20Sopenharmony_ci &dev_attr_fw_version.attr, 9428c2ecf20Sopenharmony_ci &dev_attr_plat_id.attr, 9438c2ecf20Sopenharmony_ci &dev_attr_update_config.attr, 9448c2ecf20Sopenharmony_ci &dev_attr_update_fw.attr, 9458c2ecf20Sopenharmony_ci NULL 9468c2ecf20Sopenharmony_ci}; 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_cistatic const struct attribute_group wdt87xx_attr_group = { 9498c2ecf20Sopenharmony_ci .attrs = wdt87xx_attrs, 9508c2ecf20Sopenharmony_ci}; 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_cistatic void wdt87xx_report_contact(struct input_dev *input, 9538c2ecf20Sopenharmony_ci struct wdt87xx_sys_param *param, 9548c2ecf20Sopenharmony_ci u8 *buf) 9558c2ecf20Sopenharmony_ci{ 9568c2ecf20Sopenharmony_ci int finger_id; 9578c2ecf20Sopenharmony_ci u32 x, y, w; 9588c2ecf20Sopenharmony_ci u8 p; 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_ci finger_id = (buf[FINGER_EV_V1_OFFSET_ID] >> 3) - 1; 9618c2ecf20Sopenharmony_ci if (finger_id < 0) 9628c2ecf20Sopenharmony_ci return; 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ci /* Check if this is an active contact */ 9658c2ecf20Sopenharmony_ci if (!(buf[FINGER_EV_V1_OFFSET_ID] & 0x1)) 9668c2ecf20Sopenharmony_ci return; 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_ci w = buf[FINGER_EV_V1_OFFSET_W]; 9698c2ecf20Sopenharmony_ci w *= param->scaling_factor; 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_ci p = buf[FINGER_EV_V1_OFFSET_P]; 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_ci x = get_unaligned_le16(buf + FINGER_EV_V1_OFFSET_X); 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci y = get_unaligned_le16(buf + FINGER_EV_V1_OFFSET_Y); 9768c2ecf20Sopenharmony_ci y = DIV_ROUND_CLOSEST(y * param->phy_h, param->phy_w); 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_ci /* Refuse incorrect coordinates */ 9798c2ecf20Sopenharmony_ci if (x > param->max_x || y > param->max_y) 9808c2ecf20Sopenharmony_ci return; 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_ci dev_dbg(input->dev.parent, "tip on (%d), x(%d), y(%d)\n", 9838c2ecf20Sopenharmony_ci finger_id, x, y); 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_ci input_mt_slot(input, finger_id); 9868c2ecf20Sopenharmony_ci input_mt_report_slot_state(input, MT_TOOL_FINGER, 1); 9878c2ecf20Sopenharmony_ci input_report_abs(input, ABS_MT_TOUCH_MAJOR, w); 9888c2ecf20Sopenharmony_ci input_report_abs(input, ABS_MT_PRESSURE, p); 9898c2ecf20Sopenharmony_ci input_report_abs(input, ABS_MT_POSITION_X, x); 9908c2ecf20Sopenharmony_ci input_report_abs(input, ABS_MT_POSITION_Y, y); 9918c2ecf20Sopenharmony_ci} 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_cistatic irqreturn_t wdt87xx_ts_interrupt(int irq, void *dev_id) 9948c2ecf20Sopenharmony_ci{ 9958c2ecf20Sopenharmony_ci struct wdt87xx_data *wdt = dev_id; 9968c2ecf20Sopenharmony_ci struct i2c_client *client = wdt->client; 9978c2ecf20Sopenharmony_ci int i, fingers; 9988c2ecf20Sopenharmony_ci int error; 9998c2ecf20Sopenharmony_ci u8 raw_buf[WDT_V1_RAW_BUF_COUNT] = {0}; 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_ci error = i2c_master_recv(client, raw_buf, WDT_V1_RAW_BUF_COUNT); 10028c2ecf20Sopenharmony_ci if (error < 0) { 10038c2ecf20Sopenharmony_ci dev_err(&client->dev, "read v1 raw data failed: %d\n", error); 10048c2ecf20Sopenharmony_ci goto irq_exit; 10058c2ecf20Sopenharmony_ci } 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_ci fingers = raw_buf[TOUCH_PK_V1_OFFSET_FNGR_NUM]; 10088c2ecf20Sopenharmony_ci if (!fingers) 10098c2ecf20Sopenharmony_ci goto irq_exit; 10108c2ecf20Sopenharmony_ci 10118c2ecf20Sopenharmony_ci for (i = 0; i < WDT_MAX_FINGER; i++) 10128c2ecf20Sopenharmony_ci wdt87xx_report_contact(wdt->input, 10138c2ecf20Sopenharmony_ci &wdt->param, 10148c2ecf20Sopenharmony_ci &raw_buf[TOUCH_PK_V1_OFFSET_EVENT + 10158c2ecf20Sopenharmony_ci i * FINGER_EV_V1_SIZE]); 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_ci input_mt_sync_frame(wdt->input); 10188c2ecf20Sopenharmony_ci input_sync(wdt->input); 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_ciirq_exit: 10218c2ecf20Sopenharmony_ci return IRQ_HANDLED; 10228c2ecf20Sopenharmony_ci} 10238c2ecf20Sopenharmony_ci 10248c2ecf20Sopenharmony_cistatic int wdt87xx_ts_create_input_device(struct wdt87xx_data *wdt) 10258c2ecf20Sopenharmony_ci{ 10268c2ecf20Sopenharmony_ci struct device *dev = &wdt->client->dev; 10278c2ecf20Sopenharmony_ci struct input_dev *input; 10288c2ecf20Sopenharmony_ci unsigned int res = DIV_ROUND_CLOSEST(MAX_UNIT_AXIS, wdt->param.phy_w); 10298c2ecf20Sopenharmony_ci int error; 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_ci input = devm_input_allocate_device(dev); 10328c2ecf20Sopenharmony_ci if (!input) { 10338c2ecf20Sopenharmony_ci dev_err(dev, "failed to allocate input device\n"); 10348c2ecf20Sopenharmony_ci return -ENOMEM; 10358c2ecf20Sopenharmony_ci } 10368c2ecf20Sopenharmony_ci wdt->input = input; 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_ci input->name = "WDT87xx Touchscreen"; 10398c2ecf20Sopenharmony_ci input->id.bustype = BUS_I2C; 10408c2ecf20Sopenharmony_ci input->id.vendor = wdt->param.vendor_id; 10418c2ecf20Sopenharmony_ci input->id.product = wdt->param.product_id; 10428c2ecf20Sopenharmony_ci input->phys = wdt->phys; 10438c2ecf20Sopenharmony_ci 10448c2ecf20Sopenharmony_ci input_set_abs_params(input, ABS_MT_POSITION_X, 0, 10458c2ecf20Sopenharmony_ci wdt->param.max_x, 0, 0); 10468c2ecf20Sopenharmony_ci input_set_abs_params(input, ABS_MT_POSITION_Y, 0, 10478c2ecf20Sopenharmony_ci wdt->param.max_y, 0, 0); 10488c2ecf20Sopenharmony_ci input_abs_set_res(input, ABS_MT_POSITION_X, res); 10498c2ecf20Sopenharmony_ci input_abs_set_res(input, ABS_MT_POSITION_Y, res); 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_ci input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 10528c2ecf20Sopenharmony_ci 0, wdt->param.max_x, 0, 0); 10538c2ecf20Sopenharmony_ci input_set_abs_params(input, ABS_MT_PRESSURE, 0, 0xFF, 0, 0); 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_ci input_mt_init_slots(input, WDT_MAX_FINGER, 10568c2ecf20Sopenharmony_ci INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED); 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_ci error = input_register_device(input); 10598c2ecf20Sopenharmony_ci if (error) { 10608c2ecf20Sopenharmony_ci dev_err(dev, "failed to register input device: %d\n", error); 10618c2ecf20Sopenharmony_ci return error; 10628c2ecf20Sopenharmony_ci } 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_ci return 0; 10658c2ecf20Sopenharmony_ci} 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_cistatic int wdt87xx_ts_probe(struct i2c_client *client, 10688c2ecf20Sopenharmony_ci const struct i2c_device_id *id) 10698c2ecf20Sopenharmony_ci{ 10708c2ecf20Sopenharmony_ci struct wdt87xx_data *wdt; 10718c2ecf20Sopenharmony_ci int error; 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci dev_dbg(&client->dev, "adapter=%d, client irq: %d\n", 10748c2ecf20Sopenharmony_ci client->adapter->nr, client->irq); 10758c2ecf20Sopenharmony_ci 10768c2ecf20Sopenharmony_ci /* Check if the I2C function is ok in this adaptor */ 10778c2ecf20Sopenharmony_ci if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) 10788c2ecf20Sopenharmony_ci return -ENXIO; 10798c2ecf20Sopenharmony_ci 10808c2ecf20Sopenharmony_ci wdt = devm_kzalloc(&client->dev, sizeof(*wdt), GFP_KERNEL); 10818c2ecf20Sopenharmony_ci if (!wdt) 10828c2ecf20Sopenharmony_ci return -ENOMEM; 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_ci wdt->client = client; 10858c2ecf20Sopenharmony_ci mutex_init(&wdt->fw_mutex); 10868c2ecf20Sopenharmony_ci i2c_set_clientdata(client, wdt); 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_ci snprintf(wdt->phys, sizeof(wdt->phys), "i2c-%u-%04x/input0", 10898c2ecf20Sopenharmony_ci client->adapter->nr, client->addr); 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_ci error = wdt87xx_get_sysparam(client, &wdt->param); 10928c2ecf20Sopenharmony_ci if (error) 10938c2ecf20Sopenharmony_ci return error; 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_ci error = wdt87xx_ts_create_input_device(wdt); 10968c2ecf20Sopenharmony_ci if (error) 10978c2ecf20Sopenharmony_ci return error; 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_ci error = devm_request_threaded_irq(&client->dev, client->irq, 11008c2ecf20Sopenharmony_ci NULL, wdt87xx_ts_interrupt, 11018c2ecf20Sopenharmony_ci IRQF_ONESHOT, 11028c2ecf20Sopenharmony_ci client->name, wdt); 11038c2ecf20Sopenharmony_ci if (error) { 11048c2ecf20Sopenharmony_ci dev_err(&client->dev, "request irq failed: %d\n", error); 11058c2ecf20Sopenharmony_ci return error; 11068c2ecf20Sopenharmony_ci } 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_ci error = devm_device_add_group(&client->dev, &wdt87xx_attr_group); 11098c2ecf20Sopenharmony_ci if (error) { 11108c2ecf20Sopenharmony_ci dev_err(&client->dev, "create sysfs failed: %d\n", error); 11118c2ecf20Sopenharmony_ci return error; 11128c2ecf20Sopenharmony_ci } 11138c2ecf20Sopenharmony_ci 11148c2ecf20Sopenharmony_ci return 0; 11158c2ecf20Sopenharmony_ci} 11168c2ecf20Sopenharmony_ci 11178c2ecf20Sopenharmony_cistatic int __maybe_unused wdt87xx_suspend(struct device *dev) 11188c2ecf20Sopenharmony_ci{ 11198c2ecf20Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 11208c2ecf20Sopenharmony_ci int error; 11218c2ecf20Sopenharmony_ci 11228c2ecf20Sopenharmony_ci disable_irq(client->irq); 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_ci error = wdt87xx_send_command(client, VND_CMD_STOP, MODE_IDLE); 11258c2ecf20Sopenharmony_ci if (error) { 11268c2ecf20Sopenharmony_ci enable_irq(client->irq); 11278c2ecf20Sopenharmony_ci dev_err(&client->dev, 11288c2ecf20Sopenharmony_ci "failed to stop device when suspending: %d\n", 11298c2ecf20Sopenharmony_ci error); 11308c2ecf20Sopenharmony_ci return error; 11318c2ecf20Sopenharmony_ci } 11328c2ecf20Sopenharmony_ci 11338c2ecf20Sopenharmony_ci return 0; 11348c2ecf20Sopenharmony_ci} 11358c2ecf20Sopenharmony_ci 11368c2ecf20Sopenharmony_cistatic int __maybe_unused wdt87xx_resume(struct device *dev) 11378c2ecf20Sopenharmony_ci{ 11388c2ecf20Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 11398c2ecf20Sopenharmony_ci int error; 11408c2ecf20Sopenharmony_ci 11418c2ecf20Sopenharmony_ci /* 11428c2ecf20Sopenharmony_ci * The chip may have been reset while system is resuming, 11438c2ecf20Sopenharmony_ci * give it some time to settle. 11448c2ecf20Sopenharmony_ci */ 11458c2ecf20Sopenharmony_ci msleep(100); 11468c2ecf20Sopenharmony_ci 11478c2ecf20Sopenharmony_ci error = wdt87xx_send_command(client, VND_CMD_START, 0); 11488c2ecf20Sopenharmony_ci if (error) 11498c2ecf20Sopenharmony_ci dev_err(&client->dev, 11508c2ecf20Sopenharmony_ci "failed to start device when resuming: %d\n", 11518c2ecf20Sopenharmony_ci error); 11528c2ecf20Sopenharmony_ci 11538c2ecf20Sopenharmony_ci enable_irq(client->irq); 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_ci return 0; 11568c2ecf20Sopenharmony_ci} 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(wdt87xx_pm_ops, wdt87xx_suspend, wdt87xx_resume); 11598c2ecf20Sopenharmony_ci 11608c2ecf20Sopenharmony_cistatic const struct i2c_device_id wdt87xx_dev_id[] = { 11618c2ecf20Sopenharmony_ci { WDT87XX_NAME, 0 }, 11628c2ecf20Sopenharmony_ci { } 11638c2ecf20Sopenharmony_ci}; 11648c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, wdt87xx_dev_id); 11658c2ecf20Sopenharmony_ci 11668c2ecf20Sopenharmony_cistatic const struct acpi_device_id wdt87xx_acpi_id[] = { 11678c2ecf20Sopenharmony_ci { "WDHT0001", 0 }, 11688c2ecf20Sopenharmony_ci { } 11698c2ecf20Sopenharmony_ci}; 11708c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, wdt87xx_acpi_id); 11718c2ecf20Sopenharmony_ci 11728c2ecf20Sopenharmony_cistatic struct i2c_driver wdt87xx_driver = { 11738c2ecf20Sopenharmony_ci .probe = wdt87xx_ts_probe, 11748c2ecf20Sopenharmony_ci .id_table = wdt87xx_dev_id, 11758c2ecf20Sopenharmony_ci .driver = { 11768c2ecf20Sopenharmony_ci .name = WDT87XX_NAME, 11778c2ecf20Sopenharmony_ci .pm = &wdt87xx_pm_ops, 11788c2ecf20Sopenharmony_ci .acpi_match_table = ACPI_PTR(wdt87xx_acpi_id), 11798c2ecf20Sopenharmony_ci }, 11808c2ecf20Sopenharmony_ci}; 11818c2ecf20Sopenharmony_cimodule_i2c_driver(wdt87xx_driver); 11828c2ecf20Sopenharmony_ci 11838c2ecf20Sopenharmony_ciMODULE_AUTHOR("HN Chen <hn.chen@weidahitech.com>"); 11848c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("WeidaHiTech WDT87XX Touchscreen driver"); 11858c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 1186