162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2012-2017 Hideep, Inc. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/module.h> 762306a36Sopenharmony_ci#include <linux/of.h> 862306a36Sopenharmony_ci#include <linux/firmware.h> 962306a36Sopenharmony_ci#include <linux/delay.h> 1062306a36Sopenharmony_ci#include <linux/gpio/consumer.h> 1162306a36Sopenharmony_ci#include <linux/i2c.h> 1262306a36Sopenharmony_ci#include <linux/acpi.h> 1362306a36Sopenharmony_ci#include <linux/interrupt.h> 1462306a36Sopenharmony_ci#include <linux/regmap.h> 1562306a36Sopenharmony_ci#include <linux/sysfs.h> 1662306a36Sopenharmony_ci#include <linux/input.h> 1762306a36Sopenharmony_ci#include <linux/input/mt.h> 1862306a36Sopenharmony_ci#include <linux/input/touchscreen.h> 1962306a36Sopenharmony_ci#include <linux/regulator/consumer.h> 2062306a36Sopenharmony_ci#include <asm/unaligned.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#define HIDEEP_TS_NAME "HiDeep Touchscreen" 2362306a36Sopenharmony_ci#define HIDEEP_I2C_NAME "hideep_ts" 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#define HIDEEP_MT_MAX 10 2662306a36Sopenharmony_ci#define HIDEEP_KEY_MAX 3 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci/* count(2) + touch data(100) + key data(6) */ 2962306a36Sopenharmony_ci#define HIDEEP_MAX_EVENT 108UL 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#define HIDEEP_TOUCH_EVENT_INDEX 2 3262306a36Sopenharmony_ci#define HIDEEP_KEY_EVENT_INDEX 102 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci/* Touch & key event */ 3562306a36Sopenharmony_ci#define HIDEEP_EVENT_ADDR 0x240 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci/* command list */ 3862306a36Sopenharmony_ci#define HIDEEP_WORK_MODE 0x081e 3962306a36Sopenharmony_ci#define HIDEEP_RESET_CMD 0x9800 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci/* event bit */ 4262306a36Sopenharmony_ci#define HIDEEP_MT_RELEASED BIT(4) 4362306a36Sopenharmony_ci#define HIDEEP_KEY_PRESSED BIT(7) 4462306a36Sopenharmony_ci#define HIDEEP_KEY_FIRST_PRESSED BIT(8) 4562306a36Sopenharmony_ci#define HIDEEP_KEY_PRESSED_MASK (HIDEEP_KEY_PRESSED | \ 4662306a36Sopenharmony_ci HIDEEP_KEY_FIRST_PRESSED) 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci#define HIDEEP_KEY_IDX_MASK 0x0f 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci/* For NVM */ 5162306a36Sopenharmony_ci#define HIDEEP_YRAM_BASE 0x40000000 5262306a36Sopenharmony_ci#define HIDEEP_PERIPHERAL_BASE 0x50000000 5362306a36Sopenharmony_ci#define HIDEEP_ESI_BASE (HIDEEP_PERIPHERAL_BASE + 0x00000000) 5462306a36Sopenharmony_ci#define HIDEEP_FLASH_BASE (HIDEEP_PERIPHERAL_BASE + 0x01000000) 5562306a36Sopenharmony_ci#define HIDEEP_SYSCON_BASE (HIDEEP_PERIPHERAL_BASE + 0x02000000) 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci#define HIDEEP_SYSCON_MOD_CON (HIDEEP_SYSCON_BASE + 0x0000) 5862306a36Sopenharmony_ci#define HIDEEP_SYSCON_SPC_CON (HIDEEP_SYSCON_BASE + 0x0004) 5962306a36Sopenharmony_ci#define HIDEEP_SYSCON_CLK_CON (HIDEEP_SYSCON_BASE + 0x0008) 6062306a36Sopenharmony_ci#define HIDEEP_SYSCON_CLK_ENA (HIDEEP_SYSCON_BASE + 0x000C) 6162306a36Sopenharmony_ci#define HIDEEP_SYSCON_RST_CON (HIDEEP_SYSCON_BASE + 0x0010) 6262306a36Sopenharmony_ci#define HIDEEP_SYSCON_WDT_CON (HIDEEP_SYSCON_BASE + 0x0014) 6362306a36Sopenharmony_ci#define HIDEEP_SYSCON_WDT_CNT (HIDEEP_SYSCON_BASE + 0x0018) 6462306a36Sopenharmony_ci#define HIDEEP_SYSCON_PWR_CON (HIDEEP_SYSCON_BASE + 0x0020) 6562306a36Sopenharmony_ci#define HIDEEP_SYSCON_PGM_ID (HIDEEP_SYSCON_BASE + 0x00F4) 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci#define HIDEEP_FLASH_CON (HIDEEP_FLASH_BASE + 0x0000) 6862306a36Sopenharmony_ci#define HIDEEP_FLASH_STA (HIDEEP_FLASH_BASE + 0x0004) 6962306a36Sopenharmony_ci#define HIDEEP_FLASH_CFG (HIDEEP_FLASH_BASE + 0x0008) 7062306a36Sopenharmony_ci#define HIDEEP_FLASH_TIM (HIDEEP_FLASH_BASE + 0x000C) 7162306a36Sopenharmony_ci#define HIDEEP_FLASH_CACHE_CFG (HIDEEP_FLASH_BASE + 0x0010) 7262306a36Sopenharmony_ci#define HIDEEP_FLASH_PIO_SIG (HIDEEP_FLASH_BASE + 0x400000) 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci#define HIDEEP_ESI_TX_INVALID (HIDEEP_ESI_BASE + 0x0008) 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci#define HIDEEP_PERASE 0x00040000 7762306a36Sopenharmony_ci#define HIDEEP_WRONLY 0x00100000 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci#define HIDEEP_NVM_MASK_OFS 0x0000000C 8062306a36Sopenharmony_ci#define HIDEEP_NVM_DEFAULT_PAGE 0 8162306a36Sopenharmony_ci#define HIDEEP_NVM_SFR_WPAGE 1 8262306a36Sopenharmony_ci#define HIDEEP_NVM_SFR_RPAGE 2 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci#define HIDEEP_PIO_SIG 0x00400000 8562306a36Sopenharmony_ci#define HIDEEP_PROT_MODE 0x03400000 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci#define HIDEEP_NVM_PAGE_SIZE 128 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci#define HIDEEP_DWZ_INFO 0x000002C0 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_cistruct hideep_event { 9262306a36Sopenharmony_ci __le16 x; 9362306a36Sopenharmony_ci __le16 y; 9462306a36Sopenharmony_ci __le16 z; 9562306a36Sopenharmony_ci u8 w; 9662306a36Sopenharmony_ci u8 flag; 9762306a36Sopenharmony_ci u8 type; 9862306a36Sopenharmony_ci u8 index; 9962306a36Sopenharmony_ci}; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_cistruct dwz_info { 10262306a36Sopenharmony_ci __be32 code_start; 10362306a36Sopenharmony_ci u8 code_crc[12]; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci __be32 c_code_start; 10662306a36Sopenharmony_ci __be16 gen_ver; 10762306a36Sopenharmony_ci __be16 c_code_len; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci __be32 vr_start; 11062306a36Sopenharmony_ci __be16 rsv0; 11162306a36Sopenharmony_ci __be16 vr_len; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci __be32 ft_start; 11462306a36Sopenharmony_ci __be16 vr_version; 11562306a36Sopenharmony_ci __be16 ft_len; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci __be16 core_ver; 11862306a36Sopenharmony_ci __be16 boot_ver; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci __be16 release_ver; 12162306a36Sopenharmony_ci __be16 custom_ver; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci u8 factory_id; 12462306a36Sopenharmony_ci u8 panel_type; 12562306a36Sopenharmony_ci u8 model_name[6]; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci __be16 extra_option; 12862306a36Sopenharmony_ci __be16 product_code; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci __be16 vendor_id; 13162306a36Sopenharmony_ci __be16 product_id; 13262306a36Sopenharmony_ci}; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_cistruct pgm_packet { 13562306a36Sopenharmony_ci struct { 13662306a36Sopenharmony_ci u8 unused[3]; 13762306a36Sopenharmony_ci u8 len; 13862306a36Sopenharmony_ci __be32 addr; 13962306a36Sopenharmony_ci } header; 14062306a36Sopenharmony_ci __be32 payload[HIDEEP_NVM_PAGE_SIZE / sizeof(__be32)]; 14162306a36Sopenharmony_ci}; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci#define HIDEEP_XFER_BUF_SIZE sizeof(struct pgm_packet) 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_cistruct hideep_ts { 14662306a36Sopenharmony_ci struct i2c_client *client; 14762306a36Sopenharmony_ci struct input_dev *input_dev; 14862306a36Sopenharmony_ci struct regmap *reg; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci struct touchscreen_properties prop; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci struct gpio_desc *reset_gpio; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci struct regulator *vcc_vdd; 15562306a36Sopenharmony_ci struct regulator *vcc_vid; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci struct mutex dev_mutex; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci u32 tch_count; 16062306a36Sopenharmony_ci u32 lpm_count; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci /* 16362306a36Sopenharmony_ci * Data buffer to read packet from the device (contacts and key 16462306a36Sopenharmony_ci * states). We align it on double-word boundary to keep word-sized 16562306a36Sopenharmony_ci * fields in contact data and double-word-sized fields in program 16662306a36Sopenharmony_ci * packet aligned. 16762306a36Sopenharmony_ci */ 16862306a36Sopenharmony_ci u8 xfer_buf[HIDEEP_XFER_BUF_SIZE] __aligned(4); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci int key_num; 17162306a36Sopenharmony_ci u32 key_codes[HIDEEP_KEY_MAX]; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci struct dwz_info dwz_info; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci unsigned int fw_size; 17662306a36Sopenharmony_ci u32 nvm_mask; 17762306a36Sopenharmony_ci}; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_cistatic int hideep_pgm_w_mem(struct hideep_ts *ts, u32 addr, 18062306a36Sopenharmony_ci const __be32 *data, size_t count) 18162306a36Sopenharmony_ci{ 18262306a36Sopenharmony_ci struct pgm_packet *packet = (void *)ts->xfer_buf; 18362306a36Sopenharmony_ci size_t len = count * sizeof(*data); 18462306a36Sopenharmony_ci struct i2c_msg msg = { 18562306a36Sopenharmony_ci .addr = ts->client->addr, 18662306a36Sopenharmony_ci .len = len + sizeof(packet->header.len) + 18762306a36Sopenharmony_ci sizeof(packet->header.addr), 18862306a36Sopenharmony_ci .buf = &packet->header.len, 18962306a36Sopenharmony_ci }; 19062306a36Sopenharmony_ci int ret; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci if (len > HIDEEP_NVM_PAGE_SIZE) 19362306a36Sopenharmony_ci return -EINVAL; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci packet->header.len = 0x80 | (count - 1); 19662306a36Sopenharmony_ci packet->header.addr = cpu_to_be32(addr); 19762306a36Sopenharmony_ci memcpy(packet->payload, data, len); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci ret = i2c_transfer(ts->client->adapter, &msg, 1); 20062306a36Sopenharmony_ci if (ret != 1) 20162306a36Sopenharmony_ci return ret < 0 ? ret : -EIO; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci return 0; 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cistatic int hideep_pgm_r_mem(struct hideep_ts *ts, u32 addr, 20762306a36Sopenharmony_ci __be32 *data, size_t count) 20862306a36Sopenharmony_ci{ 20962306a36Sopenharmony_ci struct pgm_packet *packet = (void *)ts->xfer_buf; 21062306a36Sopenharmony_ci size_t len = count * sizeof(*data); 21162306a36Sopenharmony_ci struct i2c_msg msg[] = { 21262306a36Sopenharmony_ci { 21362306a36Sopenharmony_ci .addr = ts->client->addr, 21462306a36Sopenharmony_ci .len = sizeof(packet->header.len) + 21562306a36Sopenharmony_ci sizeof(packet->header.addr), 21662306a36Sopenharmony_ci .buf = &packet->header.len, 21762306a36Sopenharmony_ci }, 21862306a36Sopenharmony_ci { 21962306a36Sopenharmony_ci .addr = ts->client->addr, 22062306a36Sopenharmony_ci .flags = I2C_M_RD, 22162306a36Sopenharmony_ci .len = len, 22262306a36Sopenharmony_ci .buf = (u8 *)data, 22362306a36Sopenharmony_ci }, 22462306a36Sopenharmony_ci }; 22562306a36Sopenharmony_ci int ret; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci if (len > HIDEEP_NVM_PAGE_SIZE) 22862306a36Sopenharmony_ci return -EINVAL; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci packet->header.len = count - 1; 23162306a36Sopenharmony_ci packet->header.addr = cpu_to_be32(addr); 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci ret = i2c_transfer(ts->client->adapter, msg, ARRAY_SIZE(msg)); 23462306a36Sopenharmony_ci if (ret != ARRAY_SIZE(msg)) 23562306a36Sopenharmony_ci return ret < 0 ? ret : -EIO; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci return 0; 23862306a36Sopenharmony_ci} 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_cistatic int hideep_pgm_r_reg(struct hideep_ts *ts, u32 addr, u32 *val) 24162306a36Sopenharmony_ci{ 24262306a36Sopenharmony_ci __be32 data; 24362306a36Sopenharmony_ci int error; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci error = hideep_pgm_r_mem(ts, addr, &data, 1); 24662306a36Sopenharmony_ci if (error) { 24762306a36Sopenharmony_ci dev_err(&ts->client->dev, 24862306a36Sopenharmony_ci "read of register %#08x failed: %d\n", 24962306a36Sopenharmony_ci addr, error); 25062306a36Sopenharmony_ci return error; 25162306a36Sopenharmony_ci } 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci *val = be32_to_cpu(data); 25462306a36Sopenharmony_ci return 0; 25562306a36Sopenharmony_ci} 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_cistatic int hideep_pgm_w_reg(struct hideep_ts *ts, u32 addr, u32 val) 25862306a36Sopenharmony_ci{ 25962306a36Sopenharmony_ci __be32 data = cpu_to_be32(val); 26062306a36Sopenharmony_ci int error; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci error = hideep_pgm_w_mem(ts, addr, &data, 1); 26362306a36Sopenharmony_ci if (error) { 26462306a36Sopenharmony_ci dev_err(&ts->client->dev, 26562306a36Sopenharmony_ci "write to register %#08x (%#08x) failed: %d\n", 26662306a36Sopenharmony_ci addr, val, error); 26762306a36Sopenharmony_ci return error; 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci return 0; 27162306a36Sopenharmony_ci} 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci#define SW_RESET_IN_PGM(clk) \ 27462306a36Sopenharmony_ci{ \ 27562306a36Sopenharmony_ci __be32 data = cpu_to_be32(0x01); \ 27662306a36Sopenharmony_ci hideep_pgm_w_reg(ts, HIDEEP_SYSCON_WDT_CNT, (clk)); \ 27762306a36Sopenharmony_ci hideep_pgm_w_reg(ts, HIDEEP_SYSCON_WDT_CON, 0x03); \ 27862306a36Sopenharmony_ci /* \ 27962306a36Sopenharmony_ci * The first write may already cause a reset, use a raw \ 28062306a36Sopenharmony_ci * write for the second write to avoid error logging. \ 28162306a36Sopenharmony_ci */ \ 28262306a36Sopenharmony_ci hideep_pgm_w_mem(ts, HIDEEP_SYSCON_WDT_CON, &data, 1); \ 28362306a36Sopenharmony_ci} 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci#define SET_FLASH_PIO(ce) \ 28662306a36Sopenharmony_ci hideep_pgm_w_reg(ts, HIDEEP_FLASH_CON, \ 28762306a36Sopenharmony_ci 0x01 | ((ce) << 1)) 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci#define SET_PIO_SIG(x, y) \ 29062306a36Sopenharmony_ci hideep_pgm_w_reg(ts, HIDEEP_FLASH_PIO_SIG + (x), (y)) 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci#define SET_FLASH_HWCONTROL() \ 29362306a36Sopenharmony_ci hideep_pgm_w_reg(ts, HIDEEP_FLASH_CON, 0x00) 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci#define NVM_W_SFR(x, y) \ 29662306a36Sopenharmony_ci{ \ 29762306a36Sopenharmony_ci SET_FLASH_PIO(1); \ 29862306a36Sopenharmony_ci SET_PIO_SIG(x, y); \ 29962306a36Sopenharmony_ci SET_FLASH_PIO(0); \ 30062306a36Sopenharmony_ci} 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_cistatic void hideep_pgm_set(struct hideep_ts *ts) 30362306a36Sopenharmony_ci{ 30462306a36Sopenharmony_ci hideep_pgm_w_reg(ts, HIDEEP_SYSCON_WDT_CON, 0x00); 30562306a36Sopenharmony_ci hideep_pgm_w_reg(ts, HIDEEP_SYSCON_SPC_CON, 0x00); 30662306a36Sopenharmony_ci hideep_pgm_w_reg(ts, HIDEEP_SYSCON_CLK_ENA, 0xFF); 30762306a36Sopenharmony_ci hideep_pgm_w_reg(ts, HIDEEP_SYSCON_CLK_CON, 0x01); 30862306a36Sopenharmony_ci hideep_pgm_w_reg(ts, HIDEEP_SYSCON_PWR_CON, 0x01); 30962306a36Sopenharmony_ci hideep_pgm_w_reg(ts, HIDEEP_FLASH_TIM, 0x03); 31062306a36Sopenharmony_ci hideep_pgm_w_reg(ts, HIDEEP_FLASH_CACHE_CFG, 0x00); 31162306a36Sopenharmony_ci} 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_cistatic int hideep_pgm_get_pattern(struct hideep_ts *ts, u32 *pattern) 31462306a36Sopenharmony_ci{ 31562306a36Sopenharmony_ci u16 p1 = 0xAF39; 31662306a36Sopenharmony_ci u16 p2 = 0xDF9D; 31762306a36Sopenharmony_ci int error; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci error = regmap_bulk_write(ts->reg, p1, &p2, 1); 32062306a36Sopenharmony_ci if (error) { 32162306a36Sopenharmony_ci dev_err(&ts->client->dev, 32262306a36Sopenharmony_ci "%s: regmap_bulk_write() failed with %d\n", 32362306a36Sopenharmony_ci __func__, error); 32462306a36Sopenharmony_ci return error; 32562306a36Sopenharmony_ci } 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci usleep_range(1000, 1100); 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci /* flush invalid Tx load register */ 33062306a36Sopenharmony_ci error = hideep_pgm_w_reg(ts, HIDEEP_ESI_TX_INVALID, 0x01); 33162306a36Sopenharmony_ci if (error) 33262306a36Sopenharmony_ci return error; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci error = hideep_pgm_r_reg(ts, HIDEEP_SYSCON_PGM_ID, pattern); 33562306a36Sopenharmony_ci if (error) 33662306a36Sopenharmony_ci return error; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci return 0; 33962306a36Sopenharmony_ci} 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_cistatic int hideep_enter_pgm(struct hideep_ts *ts) 34262306a36Sopenharmony_ci{ 34362306a36Sopenharmony_ci int retry_count = 10; 34462306a36Sopenharmony_ci u32 pattern; 34562306a36Sopenharmony_ci int error; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci while (retry_count--) { 34862306a36Sopenharmony_ci error = hideep_pgm_get_pattern(ts, &pattern); 34962306a36Sopenharmony_ci if (error) { 35062306a36Sopenharmony_ci dev_err(&ts->client->dev, 35162306a36Sopenharmony_ci "hideep_pgm_get_pattern failed: %d\n", error); 35262306a36Sopenharmony_ci } else if (pattern != 0x39AF9DDF) { 35362306a36Sopenharmony_ci dev_err(&ts->client->dev, "%s: bad pattern: %#08x\n", 35462306a36Sopenharmony_ci __func__, pattern); 35562306a36Sopenharmony_ci } else { 35662306a36Sopenharmony_ci dev_dbg(&ts->client->dev, "found magic code"); 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci hideep_pgm_set(ts); 35962306a36Sopenharmony_ci usleep_range(1000, 1100); 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci return 0; 36262306a36Sopenharmony_ci } 36362306a36Sopenharmony_ci } 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci dev_err(&ts->client->dev, "failed to enter pgm mode\n"); 36662306a36Sopenharmony_ci SW_RESET_IN_PGM(1000); 36762306a36Sopenharmony_ci return -EIO; 36862306a36Sopenharmony_ci} 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_cistatic int hideep_nvm_unlock(struct hideep_ts *ts) 37162306a36Sopenharmony_ci{ 37262306a36Sopenharmony_ci u32 unmask_code; 37362306a36Sopenharmony_ci int error; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci hideep_pgm_w_reg(ts, HIDEEP_FLASH_CFG, HIDEEP_NVM_SFR_RPAGE); 37662306a36Sopenharmony_ci error = hideep_pgm_r_reg(ts, 0x0000000C, &unmask_code); 37762306a36Sopenharmony_ci hideep_pgm_w_reg(ts, HIDEEP_FLASH_CFG, HIDEEP_NVM_DEFAULT_PAGE); 37862306a36Sopenharmony_ci if (error) 37962306a36Sopenharmony_ci return error; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci /* make it unprotected code */ 38262306a36Sopenharmony_ci unmask_code &= ~HIDEEP_PROT_MODE; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci /* compare unmask code */ 38562306a36Sopenharmony_ci if (unmask_code != ts->nvm_mask) 38662306a36Sopenharmony_ci dev_warn(&ts->client->dev, 38762306a36Sopenharmony_ci "read mask code different %#08x vs %#08x", 38862306a36Sopenharmony_ci unmask_code, ts->nvm_mask); 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci hideep_pgm_w_reg(ts, HIDEEP_FLASH_CFG, HIDEEP_NVM_SFR_WPAGE); 39162306a36Sopenharmony_ci SET_FLASH_PIO(0); 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci NVM_W_SFR(HIDEEP_NVM_MASK_OFS, ts->nvm_mask); 39462306a36Sopenharmony_ci SET_FLASH_HWCONTROL(); 39562306a36Sopenharmony_ci hideep_pgm_w_reg(ts, HIDEEP_FLASH_CFG, HIDEEP_NVM_DEFAULT_PAGE); 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci return 0; 39862306a36Sopenharmony_ci} 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_cistatic int hideep_check_status(struct hideep_ts *ts) 40162306a36Sopenharmony_ci{ 40262306a36Sopenharmony_ci int time_out = 100; 40362306a36Sopenharmony_ci int status; 40462306a36Sopenharmony_ci int error; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci while (time_out--) { 40762306a36Sopenharmony_ci error = hideep_pgm_r_reg(ts, HIDEEP_FLASH_STA, &status); 40862306a36Sopenharmony_ci if (!error && status) 40962306a36Sopenharmony_ci return 0; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci usleep_range(1000, 1100); 41262306a36Sopenharmony_ci } 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci return -ETIMEDOUT; 41562306a36Sopenharmony_ci} 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_cistatic int hideep_program_page(struct hideep_ts *ts, u32 addr, 41862306a36Sopenharmony_ci const __be32 *ucode, size_t xfer_count) 41962306a36Sopenharmony_ci{ 42062306a36Sopenharmony_ci u32 val; 42162306a36Sopenharmony_ci int error; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci error = hideep_check_status(ts); 42462306a36Sopenharmony_ci if (error) 42562306a36Sopenharmony_ci return -EBUSY; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci addr &= ~(HIDEEP_NVM_PAGE_SIZE - 1); 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci SET_FLASH_PIO(0); 43062306a36Sopenharmony_ci SET_FLASH_PIO(1); 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci /* erase page */ 43362306a36Sopenharmony_ci SET_PIO_SIG(HIDEEP_PERASE | addr, 0xFFFFFFFF); 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci SET_FLASH_PIO(0); 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci error = hideep_check_status(ts); 43862306a36Sopenharmony_ci if (error) 43962306a36Sopenharmony_ci return -EBUSY; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci /* write page */ 44262306a36Sopenharmony_ci SET_FLASH_PIO(1); 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci val = be32_to_cpu(ucode[0]); 44562306a36Sopenharmony_ci SET_PIO_SIG(HIDEEP_WRONLY | addr, val); 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci hideep_pgm_w_mem(ts, HIDEEP_FLASH_PIO_SIG | HIDEEP_WRONLY, 44862306a36Sopenharmony_ci ucode, xfer_count); 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci val = be32_to_cpu(ucode[xfer_count - 1]); 45162306a36Sopenharmony_ci SET_PIO_SIG(124, val); 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci SET_FLASH_PIO(0); 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci usleep_range(1000, 1100); 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci error = hideep_check_status(ts); 45862306a36Sopenharmony_ci if (error) 45962306a36Sopenharmony_ci return -EBUSY; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci SET_FLASH_HWCONTROL(); 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci return 0; 46462306a36Sopenharmony_ci} 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_cistatic int hideep_program_nvm(struct hideep_ts *ts, 46762306a36Sopenharmony_ci const __be32 *ucode, size_t ucode_len) 46862306a36Sopenharmony_ci{ 46962306a36Sopenharmony_ci struct pgm_packet *packet_r = (void *)ts->xfer_buf; 47062306a36Sopenharmony_ci __be32 *current_ucode = packet_r->payload; 47162306a36Sopenharmony_ci size_t xfer_len; 47262306a36Sopenharmony_ci size_t xfer_count; 47362306a36Sopenharmony_ci u32 addr = 0; 47462306a36Sopenharmony_ci int error; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci error = hideep_nvm_unlock(ts); 47762306a36Sopenharmony_ci if (error) 47862306a36Sopenharmony_ci return error; 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci while (ucode_len > 0) { 48162306a36Sopenharmony_ci xfer_len = min_t(size_t, ucode_len, HIDEEP_NVM_PAGE_SIZE); 48262306a36Sopenharmony_ci xfer_count = xfer_len / sizeof(*ucode); 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci error = hideep_pgm_r_mem(ts, 0x00000000 + addr, 48562306a36Sopenharmony_ci current_ucode, xfer_count); 48662306a36Sopenharmony_ci if (error) { 48762306a36Sopenharmony_ci dev_err(&ts->client->dev, 48862306a36Sopenharmony_ci "%s: failed to read page at offset %#08x: %d\n", 48962306a36Sopenharmony_ci __func__, addr, error); 49062306a36Sopenharmony_ci return error; 49162306a36Sopenharmony_ci } 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci /* See if the page needs updating */ 49462306a36Sopenharmony_ci if (memcmp(ucode, current_ucode, xfer_len)) { 49562306a36Sopenharmony_ci error = hideep_program_page(ts, addr, 49662306a36Sopenharmony_ci ucode, xfer_count); 49762306a36Sopenharmony_ci if (error) { 49862306a36Sopenharmony_ci dev_err(&ts->client->dev, 49962306a36Sopenharmony_ci "%s: iwrite failure @%#08x: %d\n", 50062306a36Sopenharmony_ci __func__, addr, error); 50162306a36Sopenharmony_ci return error; 50262306a36Sopenharmony_ci } 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci usleep_range(1000, 1100); 50562306a36Sopenharmony_ci } 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci ucode += xfer_count; 50862306a36Sopenharmony_ci addr += xfer_len; 50962306a36Sopenharmony_ci ucode_len -= xfer_len; 51062306a36Sopenharmony_ci } 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci return 0; 51362306a36Sopenharmony_ci} 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_cistatic int hideep_verify_nvm(struct hideep_ts *ts, 51662306a36Sopenharmony_ci const __be32 *ucode, size_t ucode_len) 51762306a36Sopenharmony_ci{ 51862306a36Sopenharmony_ci struct pgm_packet *packet_r = (void *)ts->xfer_buf; 51962306a36Sopenharmony_ci __be32 *current_ucode = packet_r->payload; 52062306a36Sopenharmony_ci size_t xfer_len; 52162306a36Sopenharmony_ci size_t xfer_count; 52262306a36Sopenharmony_ci u32 addr = 0; 52362306a36Sopenharmony_ci int i; 52462306a36Sopenharmony_ci int error; 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci while (ucode_len > 0) { 52762306a36Sopenharmony_ci xfer_len = min_t(size_t, ucode_len, HIDEEP_NVM_PAGE_SIZE); 52862306a36Sopenharmony_ci xfer_count = xfer_len / sizeof(*ucode); 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci error = hideep_pgm_r_mem(ts, 0x00000000 + addr, 53162306a36Sopenharmony_ci current_ucode, xfer_count); 53262306a36Sopenharmony_ci if (error) { 53362306a36Sopenharmony_ci dev_err(&ts->client->dev, 53462306a36Sopenharmony_ci "%s: failed to read page at offset %#08x: %d\n", 53562306a36Sopenharmony_ci __func__, addr, error); 53662306a36Sopenharmony_ci return error; 53762306a36Sopenharmony_ci } 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci if (memcmp(ucode, current_ucode, xfer_len)) { 54062306a36Sopenharmony_ci const u8 *ucode_bytes = (const u8 *)ucode; 54162306a36Sopenharmony_ci const u8 *current_bytes = (const u8 *)current_ucode; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci for (i = 0; i < xfer_len; i++) 54462306a36Sopenharmony_ci if (ucode_bytes[i] != current_bytes[i]) 54562306a36Sopenharmony_ci dev_err(&ts->client->dev, 54662306a36Sopenharmony_ci "%s: mismatch @%#08x: (%#02x vs %#02x)\n", 54762306a36Sopenharmony_ci __func__, addr + i, 54862306a36Sopenharmony_ci ucode_bytes[i], 54962306a36Sopenharmony_ci current_bytes[i]); 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci return -EIO; 55262306a36Sopenharmony_ci } 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci ucode += xfer_count; 55562306a36Sopenharmony_ci addr += xfer_len; 55662306a36Sopenharmony_ci ucode_len -= xfer_len; 55762306a36Sopenharmony_ci } 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci return 0; 56062306a36Sopenharmony_ci} 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_cistatic int hideep_load_dwz(struct hideep_ts *ts) 56362306a36Sopenharmony_ci{ 56462306a36Sopenharmony_ci u16 product_code; 56562306a36Sopenharmony_ci int error; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci error = hideep_enter_pgm(ts); 56862306a36Sopenharmony_ci if (error) 56962306a36Sopenharmony_ci return error; 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci msleep(50); 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci error = hideep_pgm_r_mem(ts, HIDEEP_DWZ_INFO, 57462306a36Sopenharmony_ci (void *)&ts->dwz_info, 57562306a36Sopenharmony_ci sizeof(ts->dwz_info) / sizeof(__be32)); 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci SW_RESET_IN_PGM(10); 57862306a36Sopenharmony_ci msleep(50); 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci if (error) { 58162306a36Sopenharmony_ci dev_err(&ts->client->dev, 58262306a36Sopenharmony_ci "failed to fetch DWZ data: %d\n", error); 58362306a36Sopenharmony_ci return error; 58462306a36Sopenharmony_ci } 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci product_code = be16_to_cpu(ts->dwz_info.product_code); 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci switch (product_code & 0xF0) { 58962306a36Sopenharmony_ci case 0x40: 59062306a36Sopenharmony_ci dev_dbg(&ts->client->dev, "used crimson IC"); 59162306a36Sopenharmony_ci ts->fw_size = 1024 * 48; 59262306a36Sopenharmony_ci ts->nvm_mask = 0x00310000; 59362306a36Sopenharmony_ci break; 59462306a36Sopenharmony_ci case 0x60: 59562306a36Sopenharmony_ci dev_dbg(&ts->client->dev, "used lime IC"); 59662306a36Sopenharmony_ci ts->fw_size = 1024 * 64; 59762306a36Sopenharmony_ci ts->nvm_mask = 0x0030027B; 59862306a36Sopenharmony_ci break; 59962306a36Sopenharmony_ci default: 60062306a36Sopenharmony_ci dev_err(&ts->client->dev, "product code is wrong: %#04x", 60162306a36Sopenharmony_ci product_code); 60262306a36Sopenharmony_ci return -EINVAL; 60362306a36Sopenharmony_ci } 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci dev_dbg(&ts->client->dev, "firmware release version: %#04x", 60662306a36Sopenharmony_ci be16_to_cpu(ts->dwz_info.release_ver)); 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci return 0; 60962306a36Sopenharmony_ci} 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_cistatic int hideep_flash_firmware(struct hideep_ts *ts, 61262306a36Sopenharmony_ci const __be32 *ucode, size_t ucode_len) 61362306a36Sopenharmony_ci{ 61462306a36Sopenharmony_ci int retry_cnt = 3; 61562306a36Sopenharmony_ci int error; 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci while (retry_cnt--) { 61862306a36Sopenharmony_ci error = hideep_program_nvm(ts, ucode, ucode_len); 61962306a36Sopenharmony_ci if (!error) { 62062306a36Sopenharmony_ci error = hideep_verify_nvm(ts, ucode, ucode_len); 62162306a36Sopenharmony_ci if (!error) 62262306a36Sopenharmony_ci return 0; 62362306a36Sopenharmony_ci } 62462306a36Sopenharmony_ci } 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci return error; 62762306a36Sopenharmony_ci} 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_cistatic int hideep_update_firmware(struct hideep_ts *ts, 63062306a36Sopenharmony_ci const __be32 *ucode, size_t ucode_len) 63162306a36Sopenharmony_ci{ 63262306a36Sopenharmony_ci int error, error2; 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci dev_dbg(&ts->client->dev, "starting firmware update"); 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci /* enter program mode */ 63762306a36Sopenharmony_ci error = hideep_enter_pgm(ts); 63862306a36Sopenharmony_ci if (error) 63962306a36Sopenharmony_ci return error; 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci error = hideep_flash_firmware(ts, ucode, ucode_len); 64262306a36Sopenharmony_ci if (error) 64362306a36Sopenharmony_ci dev_err(&ts->client->dev, 64462306a36Sopenharmony_ci "firmware update failed: %d\n", error); 64562306a36Sopenharmony_ci else 64662306a36Sopenharmony_ci dev_dbg(&ts->client->dev, "firmware updated successfully\n"); 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci SW_RESET_IN_PGM(1000); 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci error2 = hideep_load_dwz(ts); 65162306a36Sopenharmony_ci if (error2) 65262306a36Sopenharmony_ci dev_err(&ts->client->dev, 65362306a36Sopenharmony_ci "failed to load dwz after firmware update: %d\n", 65462306a36Sopenharmony_ci error2); 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci return error ?: error2; 65762306a36Sopenharmony_ci} 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_cistatic int hideep_power_on(struct hideep_ts *ts) 66062306a36Sopenharmony_ci{ 66162306a36Sopenharmony_ci int error = 0; 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci error = regulator_enable(ts->vcc_vdd); 66462306a36Sopenharmony_ci if (error) 66562306a36Sopenharmony_ci dev_err(&ts->client->dev, 66662306a36Sopenharmony_ci "failed to enable 'vdd' regulator: %d", error); 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci usleep_range(999, 1000); 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci error = regulator_enable(ts->vcc_vid); 67162306a36Sopenharmony_ci if (error) 67262306a36Sopenharmony_ci dev_err(&ts->client->dev, 67362306a36Sopenharmony_ci "failed to enable 'vcc_vid' regulator: %d", 67462306a36Sopenharmony_ci error); 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci msleep(30); 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci if (ts->reset_gpio) { 67962306a36Sopenharmony_ci gpiod_set_value_cansleep(ts->reset_gpio, 0); 68062306a36Sopenharmony_ci } else { 68162306a36Sopenharmony_ci error = regmap_write(ts->reg, HIDEEP_RESET_CMD, 0x01); 68262306a36Sopenharmony_ci if (error) 68362306a36Sopenharmony_ci dev_err(&ts->client->dev, 68462306a36Sopenharmony_ci "failed to send 'reset' command: %d\n", error); 68562306a36Sopenharmony_ci } 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci msleep(50); 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci return error; 69062306a36Sopenharmony_ci} 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_cistatic void hideep_power_off(void *data) 69362306a36Sopenharmony_ci{ 69462306a36Sopenharmony_ci struct hideep_ts *ts = data; 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci if (ts->reset_gpio) 69762306a36Sopenharmony_ci gpiod_set_value(ts->reset_gpio, 1); 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci regulator_disable(ts->vcc_vid); 70062306a36Sopenharmony_ci regulator_disable(ts->vcc_vdd); 70162306a36Sopenharmony_ci} 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci#define __GET_MT_TOOL_TYPE(type) ((type) == 0x01 ? MT_TOOL_FINGER : MT_TOOL_PEN) 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_cistatic void hideep_report_slot(struct input_dev *input, 70662306a36Sopenharmony_ci const struct hideep_event *event) 70762306a36Sopenharmony_ci{ 70862306a36Sopenharmony_ci input_mt_slot(input, event->index & 0x0f); 70962306a36Sopenharmony_ci input_mt_report_slot_state(input, 71062306a36Sopenharmony_ci __GET_MT_TOOL_TYPE(event->type), 71162306a36Sopenharmony_ci !(event->flag & HIDEEP_MT_RELEASED)); 71262306a36Sopenharmony_ci if (!(event->flag & HIDEEP_MT_RELEASED)) { 71362306a36Sopenharmony_ci input_report_abs(input, ABS_MT_POSITION_X, 71462306a36Sopenharmony_ci le16_to_cpup(&event->x)); 71562306a36Sopenharmony_ci input_report_abs(input, ABS_MT_POSITION_Y, 71662306a36Sopenharmony_ci le16_to_cpup(&event->y)); 71762306a36Sopenharmony_ci input_report_abs(input, ABS_MT_PRESSURE, 71862306a36Sopenharmony_ci le16_to_cpup(&event->z)); 71962306a36Sopenharmony_ci input_report_abs(input, ABS_MT_TOUCH_MAJOR, event->w); 72062306a36Sopenharmony_ci } 72162306a36Sopenharmony_ci} 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_cistatic void hideep_parse_and_report(struct hideep_ts *ts) 72462306a36Sopenharmony_ci{ 72562306a36Sopenharmony_ci const struct hideep_event *events = 72662306a36Sopenharmony_ci (void *)&ts->xfer_buf[HIDEEP_TOUCH_EVENT_INDEX]; 72762306a36Sopenharmony_ci const u8 *keys = &ts->xfer_buf[HIDEEP_KEY_EVENT_INDEX]; 72862306a36Sopenharmony_ci int touch_count = ts->xfer_buf[0]; 72962306a36Sopenharmony_ci int key_count = ts->xfer_buf[1] & 0x0f; 73062306a36Sopenharmony_ci int lpm_count = ts->xfer_buf[1] & 0xf0; 73162306a36Sopenharmony_ci int i; 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci /* get touch event count */ 73462306a36Sopenharmony_ci dev_dbg(&ts->client->dev, "mt = %d, key = %d, lpm = %02x", 73562306a36Sopenharmony_ci touch_count, key_count, lpm_count); 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci touch_count = min(touch_count, HIDEEP_MT_MAX); 73862306a36Sopenharmony_ci for (i = 0; i < touch_count; i++) 73962306a36Sopenharmony_ci hideep_report_slot(ts->input_dev, events + i); 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci key_count = min(key_count, HIDEEP_KEY_MAX); 74262306a36Sopenharmony_ci for (i = 0; i < key_count; i++) { 74362306a36Sopenharmony_ci u8 key_data = keys[i * 2]; 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci input_report_key(ts->input_dev, 74662306a36Sopenharmony_ci ts->key_codes[key_data & HIDEEP_KEY_IDX_MASK], 74762306a36Sopenharmony_ci key_data & HIDEEP_KEY_PRESSED_MASK); 74862306a36Sopenharmony_ci } 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci input_mt_sync_frame(ts->input_dev); 75162306a36Sopenharmony_ci input_sync(ts->input_dev); 75262306a36Sopenharmony_ci} 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_cistatic irqreturn_t hideep_irq(int irq, void *handle) 75562306a36Sopenharmony_ci{ 75662306a36Sopenharmony_ci struct hideep_ts *ts = handle; 75762306a36Sopenharmony_ci int error; 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci BUILD_BUG_ON(HIDEEP_MAX_EVENT > HIDEEP_XFER_BUF_SIZE); 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci error = regmap_bulk_read(ts->reg, HIDEEP_EVENT_ADDR, 76262306a36Sopenharmony_ci ts->xfer_buf, HIDEEP_MAX_EVENT / 2); 76362306a36Sopenharmony_ci if (error) { 76462306a36Sopenharmony_ci dev_err(&ts->client->dev, "failed to read events: %d\n", error); 76562306a36Sopenharmony_ci goto out; 76662306a36Sopenharmony_ci } 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci hideep_parse_and_report(ts); 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ciout: 77162306a36Sopenharmony_ci return IRQ_HANDLED; 77262306a36Sopenharmony_ci} 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_cistatic int hideep_get_axis_info(struct hideep_ts *ts) 77562306a36Sopenharmony_ci{ 77662306a36Sopenharmony_ci __le16 val[2]; 77762306a36Sopenharmony_ci int error; 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci error = regmap_bulk_read(ts->reg, 0x28, val, ARRAY_SIZE(val)); 78062306a36Sopenharmony_ci if (error) 78162306a36Sopenharmony_ci return error; 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci ts->prop.max_x = le16_to_cpup(val); 78462306a36Sopenharmony_ci ts->prop.max_y = le16_to_cpup(val + 1); 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci dev_dbg(&ts->client->dev, "X: %d, Y: %d", 78762306a36Sopenharmony_ci ts->prop.max_x, ts->prop.max_y); 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci return 0; 79062306a36Sopenharmony_ci} 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_cistatic int hideep_init_input(struct hideep_ts *ts) 79362306a36Sopenharmony_ci{ 79462306a36Sopenharmony_ci struct device *dev = &ts->client->dev; 79562306a36Sopenharmony_ci int i; 79662306a36Sopenharmony_ci int error; 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci ts->input_dev = devm_input_allocate_device(dev); 79962306a36Sopenharmony_ci if (!ts->input_dev) { 80062306a36Sopenharmony_ci dev_err(dev, "failed to allocate input device\n"); 80162306a36Sopenharmony_ci return -ENOMEM; 80262306a36Sopenharmony_ci } 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci ts->input_dev->name = HIDEEP_TS_NAME; 80562306a36Sopenharmony_ci ts->input_dev->id.bustype = BUS_I2C; 80662306a36Sopenharmony_ci input_set_drvdata(ts->input_dev, ts); 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci input_set_capability(ts->input_dev, EV_ABS, ABS_MT_POSITION_X); 80962306a36Sopenharmony_ci input_set_capability(ts->input_dev, EV_ABS, ABS_MT_POSITION_Y); 81062306a36Sopenharmony_ci input_set_abs_params(ts->input_dev, ABS_MT_PRESSURE, 0, 65535, 0, 0); 81162306a36Sopenharmony_ci input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0); 81262306a36Sopenharmony_ci input_set_abs_params(ts->input_dev, ABS_MT_TOOL_TYPE, 81362306a36Sopenharmony_ci 0, MT_TOOL_MAX, 0, 0); 81462306a36Sopenharmony_ci touchscreen_parse_properties(ts->input_dev, true, &ts->prop); 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci if (ts->prop.max_x == 0 || ts->prop.max_y == 0) { 81762306a36Sopenharmony_ci error = hideep_get_axis_info(ts); 81862306a36Sopenharmony_ci if (error) 81962306a36Sopenharmony_ci return error; 82062306a36Sopenharmony_ci } 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci error = input_mt_init_slots(ts->input_dev, HIDEEP_MT_MAX, 82362306a36Sopenharmony_ci INPUT_MT_DIRECT); 82462306a36Sopenharmony_ci if (error) 82562306a36Sopenharmony_ci return error; 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci ts->key_num = device_property_count_u32(dev, "linux,keycodes"); 82862306a36Sopenharmony_ci if (ts->key_num > HIDEEP_KEY_MAX) { 82962306a36Sopenharmony_ci dev_err(dev, "too many keys defined: %d\n", 83062306a36Sopenharmony_ci ts->key_num); 83162306a36Sopenharmony_ci return -EINVAL; 83262306a36Sopenharmony_ci } 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci if (ts->key_num <= 0) { 83562306a36Sopenharmony_ci dev_dbg(dev, 83662306a36Sopenharmony_ci "missing or malformed 'linux,keycodes' property\n"); 83762306a36Sopenharmony_ci } else { 83862306a36Sopenharmony_ci error = device_property_read_u32_array(dev, "linux,keycodes", 83962306a36Sopenharmony_ci ts->key_codes, 84062306a36Sopenharmony_ci ts->key_num); 84162306a36Sopenharmony_ci if (error) { 84262306a36Sopenharmony_ci dev_dbg(dev, "failed to read keymap: %d", error); 84362306a36Sopenharmony_ci return error; 84462306a36Sopenharmony_ci } 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci if (ts->key_num) { 84762306a36Sopenharmony_ci ts->input_dev->keycode = ts->key_codes; 84862306a36Sopenharmony_ci ts->input_dev->keycodesize = sizeof(ts->key_codes[0]); 84962306a36Sopenharmony_ci ts->input_dev->keycodemax = ts->key_num; 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci for (i = 0; i < ts->key_num; i++) 85262306a36Sopenharmony_ci input_set_capability(ts->input_dev, EV_KEY, 85362306a36Sopenharmony_ci ts->key_codes[i]); 85462306a36Sopenharmony_ci } 85562306a36Sopenharmony_ci } 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci error = input_register_device(ts->input_dev); 85862306a36Sopenharmony_ci if (error) { 85962306a36Sopenharmony_ci dev_err(dev, "failed to register input device: %d", error); 86062306a36Sopenharmony_ci return error; 86162306a36Sopenharmony_ci } 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci return 0; 86462306a36Sopenharmony_ci} 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_cistatic ssize_t hideep_update_fw(struct device *dev, 86762306a36Sopenharmony_ci struct device_attribute *attr, 86862306a36Sopenharmony_ci const char *buf, size_t count) 86962306a36Sopenharmony_ci{ 87062306a36Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 87162306a36Sopenharmony_ci struct hideep_ts *ts = i2c_get_clientdata(client); 87262306a36Sopenharmony_ci const struct firmware *fw_entry; 87362306a36Sopenharmony_ci char *fw_name; 87462306a36Sopenharmony_ci int mode; 87562306a36Sopenharmony_ci int error; 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci error = kstrtoint(buf, 0, &mode); 87862306a36Sopenharmony_ci if (error) 87962306a36Sopenharmony_ci return error; 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci fw_name = kasprintf(GFP_KERNEL, "hideep_ts_%04x.bin", 88262306a36Sopenharmony_ci be16_to_cpu(ts->dwz_info.product_id)); 88362306a36Sopenharmony_ci if (!fw_name) 88462306a36Sopenharmony_ci return -ENOMEM; 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci error = request_firmware(&fw_entry, fw_name, dev); 88762306a36Sopenharmony_ci if (error) { 88862306a36Sopenharmony_ci dev_err(dev, "failed to request firmware %s: %d", 88962306a36Sopenharmony_ci fw_name, error); 89062306a36Sopenharmony_ci goto out_free_fw_name; 89162306a36Sopenharmony_ci } 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci if (fw_entry->size % sizeof(__be32)) { 89462306a36Sopenharmony_ci dev_err(dev, "invalid firmware size %zu\n", fw_entry->size); 89562306a36Sopenharmony_ci error = -EINVAL; 89662306a36Sopenharmony_ci goto out_release_fw; 89762306a36Sopenharmony_ci } 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci if (fw_entry->size > ts->fw_size) { 90062306a36Sopenharmony_ci dev_err(dev, "fw size (%zu) is too big (memory size %d)\n", 90162306a36Sopenharmony_ci fw_entry->size, ts->fw_size); 90262306a36Sopenharmony_ci error = -EFBIG; 90362306a36Sopenharmony_ci goto out_release_fw; 90462306a36Sopenharmony_ci } 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_ci mutex_lock(&ts->dev_mutex); 90762306a36Sopenharmony_ci disable_irq(client->irq); 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci error = hideep_update_firmware(ts, (const __be32 *)fw_entry->data, 91062306a36Sopenharmony_ci fw_entry->size); 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ci enable_irq(client->irq); 91362306a36Sopenharmony_ci mutex_unlock(&ts->dev_mutex); 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ciout_release_fw: 91662306a36Sopenharmony_ci release_firmware(fw_entry); 91762306a36Sopenharmony_ciout_free_fw_name: 91862306a36Sopenharmony_ci kfree(fw_name); 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci return error ?: count; 92162306a36Sopenharmony_ci} 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_cistatic ssize_t hideep_fw_version_show(struct device *dev, 92462306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 92562306a36Sopenharmony_ci{ 92662306a36Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 92762306a36Sopenharmony_ci struct hideep_ts *ts = i2c_get_clientdata(client); 92862306a36Sopenharmony_ci ssize_t len; 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci mutex_lock(&ts->dev_mutex); 93162306a36Sopenharmony_ci len = scnprintf(buf, PAGE_SIZE, "%04x\n", 93262306a36Sopenharmony_ci be16_to_cpu(ts->dwz_info.release_ver)); 93362306a36Sopenharmony_ci mutex_unlock(&ts->dev_mutex); 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci return len; 93662306a36Sopenharmony_ci} 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_cistatic ssize_t hideep_product_id_show(struct device *dev, 93962306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 94062306a36Sopenharmony_ci{ 94162306a36Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 94262306a36Sopenharmony_ci struct hideep_ts *ts = i2c_get_clientdata(client); 94362306a36Sopenharmony_ci ssize_t len; 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_ci mutex_lock(&ts->dev_mutex); 94662306a36Sopenharmony_ci len = scnprintf(buf, PAGE_SIZE, "%04x\n", 94762306a36Sopenharmony_ci be16_to_cpu(ts->dwz_info.product_id)); 94862306a36Sopenharmony_ci mutex_unlock(&ts->dev_mutex); 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_ci return len; 95162306a36Sopenharmony_ci} 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_cistatic DEVICE_ATTR(version, 0664, hideep_fw_version_show, NULL); 95462306a36Sopenharmony_cistatic DEVICE_ATTR(product_id, 0664, hideep_product_id_show, NULL); 95562306a36Sopenharmony_cistatic DEVICE_ATTR(update_fw, 0664, NULL, hideep_update_fw); 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_cistatic struct attribute *hideep_ts_sysfs_entries[] = { 95862306a36Sopenharmony_ci &dev_attr_version.attr, 95962306a36Sopenharmony_ci &dev_attr_product_id.attr, 96062306a36Sopenharmony_ci &dev_attr_update_fw.attr, 96162306a36Sopenharmony_ci NULL, 96262306a36Sopenharmony_ci}; 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_cistatic const struct attribute_group hideep_ts_attr_group = { 96562306a36Sopenharmony_ci .attrs = hideep_ts_sysfs_entries, 96662306a36Sopenharmony_ci}; 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_cistatic void hideep_set_work_mode(struct hideep_ts *ts) 96962306a36Sopenharmony_ci{ 97062306a36Sopenharmony_ci /* 97162306a36Sopenharmony_ci * Reset touch report format to the native HiDeep 20 protocol if requested. 97262306a36Sopenharmony_ci * This is necessary to make touchscreens which come up in I2C-HID mode 97362306a36Sopenharmony_ci * work with this driver. 97462306a36Sopenharmony_ci * 97562306a36Sopenharmony_ci * Note this is a kernel internal device-property set by x86 platform code, 97662306a36Sopenharmony_ci * this MUST not be used in devicetree files without first adding it to 97762306a36Sopenharmony_ci * the DT bindings. 97862306a36Sopenharmony_ci */ 97962306a36Sopenharmony_ci if (device_property_read_bool(&ts->client->dev, "hideep,force-native-protocol")) 98062306a36Sopenharmony_ci regmap_write(ts->reg, HIDEEP_WORK_MODE, 0x00); 98162306a36Sopenharmony_ci} 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_cistatic int hideep_suspend(struct device *dev) 98462306a36Sopenharmony_ci{ 98562306a36Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 98662306a36Sopenharmony_ci struct hideep_ts *ts = i2c_get_clientdata(client); 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci disable_irq(client->irq); 98962306a36Sopenharmony_ci hideep_power_off(ts); 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci return 0; 99262306a36Sopenharmony_ci} 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_cistatic int hideep_resume(struct device *dev) 99562306a36Sopenharmony_ci{ 99662306a36Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 99762306a36Sopenharmony_ci struct hideep_ts *ts = i2c_get_clientdata(client); 99862306a36Sopenharmony_ci int error; 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_ci error = hideep_power_on(ts); 100162306a36Sopenharmony_ci if (error) { 100262306a36Sopenharmony_ci dev_err(&client->dev, "power on failed"); 100362306a36Sopenharmony_ci return error; 100462306a36Sopenharmony_ci } 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_ci hideep_set_work_mode(ts); 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_ci enable_irq(client->irq); 100962306a36Sopenharmony_ci 101062306a36Sopenharmony_ci return 0; 101162306a36Sopenharmony_ci} 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_cistatic DEFINE_SIMPLE_DEV_PM_OPS(hideep_pm_ops, hideep_suspend, hideep_resume); 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_cistatic const struct regmap_config hideep_regmap_config = { 101662306a36Sopenharmony_ci .reg_bits = 16, 101762306a36Sopenharmony_ci .reg_format_endian = REGMAP_ENDIAN_LITTLE, 101862306a36Sopenharmony_ci .val_bits = 16, 101962306a36Sopenharmony_ci .val_format_endian = REGMAP_ENDIAN_LITTLE, 102062306a36Sopenharmony_ci .max_register = 0xffff, 102162306a36Sopenharmony_ci}; 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_cistatic int hideep_probe(struct i2c_client *client) 102462306a36Sopenharmony_ci{ 102562306a36Sopenharmony_ci struct hideep_ts *ts; 102662306a36Sopenharmony_ci int error; 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_ci /* check i2c bus */ 102962306a36Sopenharmony_ci if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { 103062306a36Sopenharmony_ci dev_err(&client->dev, "check i2c device error"); 103162306a36Sopenharmony_ci return -ENODEV; 103262306a36Sopenharmony_ci } 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci if (client->irq <= 0) { 103562306a36Sopenharmony_ci dev_err(&client->dev, "missing irq: %d\n", client->irq); 103662306a36Sopenharmony_ci return -EINVAL; 103762306a36Sopenharmony_ci } 103862306a36Sopenharmony_ci 103962306a36Sopenharmony_ci ts = devm_kzalloc(&client->dev, sizeof(*ts), GFP_KERNEL); 104062306a36Sopenharmony_ci if (!ts) 104162306a36Sopenharmony_ci return -ENOMEM; 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_ci ts->client = client; 104462306a36Sopenharmony_ci i2c_set_clientdata(client, ts); 104562306a36Sopenharmony_ci mutex_init(&ts->dev_mutex); 104662306a36Sopenharmony_ci 104762306a36Sopenharmony_ci ts->reg = devm_regmap_init_i2c(client, &hideep_regmap_config); 104862306a36Sopenharmony_ci if (IS_ERR(ts->reg)) { 104962306a36Sopenharmony_ci error = PTR_ERR(ts->reg); 105062306a36Sopenharmony_ci dev_err(&client->dev, 105162306a36Sopenharmony_ci "failed to initialize regmap: %d\n", error); 105262306a36Sopenharmony_ci return error; 105362306a36Sopenharmony_ci } 105462306a36Sopenharmony_ci 105562306a36Sopenharmony_ci ts->vcc_vdd = devm_regulator_get(&client->dev, "vdd"); 105662306a36Sopenharmony_ci if (IS_ERR(ts->vcc_vdd)) 105762306a36Sopenharmony_ci return PTR_ERR(ts->vcc_vdd); 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_ci ts->vcc_vid = devm_regulator_get(&client->dev, "vid"); 106062306a36Sopenharmony_ci if (IS_ERR(ts->vcc_vid)) 106162306a36Sopenharmony_ci return PTR_ERR(ts->vcc_vid); 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci ts->reset_gpio = devm_gpiod_get_optional(&client->dev, 106462306a36Sopenharmony_ci "reset", GPIOD_OUT_HIGH); 106562306a36Sopenharmony_ci if (IS_ERR(ts->reset_gpio)) 106662306a36Sopenharmony_ci return PTR_ERR(ts->reset_gpio); 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_ci error = hideep_power_on(ts); 106962306a36Sopenharmony_ci if (error) { 107062306a36Sopenharmony_ci dev_err(&client->dev, "power on failed: %d\n", error); 107162306a36Sopenharmony_ci return error; 107262306a36Sopenharmony_ci } 107362306a36Sopenharmony_ci 107462306a36Sopenharmony_ci error = devm_add_action_or_reset(&client->dev, hideep_power_off, ts); 107562306a36Sopenharmony_ci if (error) 107662306a36Sopenharmony_ci return error; 107762306a36Sopenharmony_ci 107862306a36Sopenharmony_ci error = hideep_load_dwz(ts); 107962306a36Sopenharmony_ci if (error) { 108062306a36Sopenharmony_ci dev_err(&client->dev, "failed to load dwz: %d", error); 108162306a36Sopenharmony_ci return error; 108262306a36Sopenharmony_ci } 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_ci hideep_set_work_mode(ts); 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_ci error = hideep_init_input(ts); 108762306a36Sopenharmony_ci if (error) 108862306a36Sopenharmony_ci return error; 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_ci error = devm_request_threaded_irq(&client->dev, client->irq, 109162306a36Sopenharmony_ci NULL, hideep_irq, IRQF_ONESHOT, 109262306a36Sopenharmony_ci client->name, ts); 109362306a36Sopenharmony_ci if (error) { 109462306a36Sopenharmony_ci dev_err(&client->dev, "failed to request irq %d: %d\n", 109562306a36Sopenharmony_ci client->irq, error); 109662306a36Sopenharmony_ci return error; 109762306a36Sopenharmony_ci } 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_ci error = devm_device_add_group(&client->dev, &hideep_ts_attr_group); 110062306a36Sopenharmony_ci if (error) { 110162306a36Sopenharmony_ci dev_err(&client->dev, 110262306a36Sopenharmony_ci "failed to add sysfs attributes: %d\n", error); 110362306a36Sopenharmony_ci return error; 110462306a36Sopenharmony_ci } 110562306a36Sopenharmony_ci 110662306a36Sopenharmony_ci return 0; 110762306a36Sopenharmony_ci} 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_cistatic const struct i2c_device_id hideep_i2c_id[] = { 111062306a36Sopenharmony_ci { HIDEEP_I2C_NAME, 0 }, 111162306a36Sopenharmony_ci { } 111262306a36Sopenharmony_ci}; 111362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, hideep_i2c_id); 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_ci#ifdef CONFIG_ACPI 111662306a36Sopenharmony_cistatic const struct acpi_device_id hideep_acpi_id[] = { 111762306a36Sopenharmony_ci { "HIDP0001", 0 }, 111862306a36Sopenharmony_ci { } 111962306a36Sopenharmony_ci}; 112062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, hideep_acpi_id); 112162306a36Sopenharmony_ci#endif 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_ci#ifdef CONFIG_OF 112462306a36Sopenharmony_cistatic const struct of_device_id hideep_match_table[] = { 112562306a36Sopenharmony_ci { .compatible = "hideep,hideep-ts" }, 112662306a36Sopenharmony_ci { } 112762306a36Sopenharmony_ci}; 112862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, hideep_match_table); 112962306a36Sopenharmony_ci#endif 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_cistatic struct i2c_driver hideep_driver = { 113262306a36Sopenharmony_ci .driver = { 113362306a36Sopenharmony_ci .name = HIDEEP_I2C_NAME, 113462306a36Sopenharmony_ci .of_match_table = of_match_ptr(hideep_match_table), 113562306a36Sopenharmony_ci .acpi_match_table = ACPI_PTR(hideep_acpi_id), 113662306a36Sopenharmony_ci .pm = pm_sleep_ptr(&hideep_pm_ops), 113762306a36Sopenharmony_ci }, 113862306a36Sopenharmony_ci .id_table = hideep_i2c_id, 113962306a36Sopenharmony_ci .probe = hideep_probe, 114062306a36Sopenharmony_ci}; 114162306a36Sopenharmony_ci 114262306a36Sopenharmony_cimodule_i2c_driver(hideep_driver); 114362306a36Sopenharmony_ci 114462306a36Sopenharmony_ciMODULE_DESCRIPTION("Driver for HiDeep Touchscreen Controller"); 114562306a36Sopenharmony_ciMODULE_AUTHOR("anthony.kim@hideep.com"); 114662306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 1147