From nobody Wed Dec 17 14:42:17 2025 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 67883C25B6E for ; Wed, 25 Oct 2023 08:22:23 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S233994AbjJYIWX (ORCPT ); Wed, 25 Oct 2023 04:22:23 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:55382 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S234052AbjJYIWR (ORCPT ); Wed, 25 Oct 2023 04:22:17 -0400 Received: from mail-pg1-x52f.google.com (mail-pg1-x52f.google.com [IPv6:2607:f8b0:4864:20::52f]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 7166E123; Wed, 25 Oct 2023 01:22:07 -0700 (PDT) Received: by mail-pg1-x52f.google.com with SMTP id 41be03b00d2f7-5b5354da665so3132756a12.2; Wed, 25 Oct 2023 01:22:07 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1698222127; x=1698826927; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=vttA6hJLS6zADD5JYNpQXBw4vrNOMLTCm/2gpINgzWs=; b=Ir+w2scY0yenAlmxHpJtYAm609v7mU0AA1SOVRzHReyT8VKFMoT9C+Necjtypz7hn/ 7xHbRjFKh/1cP9wg23P9B6gSMGFuE2PfBREibhZdR4Y2jY6haX2EMJaWOLUn4l3Xg7E6 TUKk21kjePULcmR6+gpzNUUUrXf01W1wdKIJCOOdPXcjmSaOwoo3VdCQzxhSqVrlmkrQ 7JXtwDYyW1ADzQejwU/jW5XrHLywZEXq4VsUujLrNCJm68MTDYyi9rX3EOJjn3eHAWFn lFH64ySpUkwlvAD6oZYop6rhRcYIBWlzJUAsEquvRGFDg7tJ1wvvYJSmyfSaqvbQflHz /JNw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1698222127; x=1698826927; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=vttA6hJLS6zADD5JYNpQXBw4vrNOMLTCm/2gpINgzWs=; b=i3dLo2urCNUiqGOpGhhQ9dc/Y13+XfBTzL16AQ+O0KQFgijQT/J1uLyUZIHgEVWKBP SnC1ZWQVOb67Wu2oNgiHF4u8KVuKbI4YH1jPUaNeziLg6CvwN6m2kzn8LmIp19JX3HAY /IUOuceKsM5KqB6TWh+nbNbqY0SZcfQcG5HF0uHUVxXg9LxmJzeTY2A7oUzmSFTaOiLL Nro46oCp92+z64KIXmV4Gyr5t23Q8x6E2n/2IrZe6ccqa9a5q7bQ1OeMtNam/ON9bD2p ayouJGY4RDX9PSp2HFQRM2Eb++WiKyktURFoHeUecQi9zPn+856aLuwJtJeqXm9XcOGq bH9A== X-Gm-Message-State: AOJu0YykCLA3lfs3iE5dauDsLTvLly6NUECBjWS5WJwjXha7bHnHguPe 7uz9edTP7/uuzpJqpmDT86c= X-Google-Smtp-Source: AGHT+IFOe9dCYMaSFbcRaSbB2P9YLbC0P5pERbLpQRlnvJ/IHRTvjzJRLtL/XB/66UHmS8Pp1gxFWw== X-Received: by 2002:a05:6a20:914d:b0:174:52b7:f63 with SMTP id x13-20020a056a20914d00b0017452b70f63mr4967040pzc.26.1698222126561; Wed, 25 Oct 2023 01:22:06 -0700 (PDT) Received: from localhost.localdomain (2001-b400-e38c-defd-6dc9-16c9-6f6d-2997.emome-ip6.hinet.net. [2001:b400:e38c:defd:6dc9:16c9:6f6d:2997]) by smtp.gmail.com with ESMTPSA id y66-20020a62ce45000000b00690ca4356f1sm9255860pfg.198.2023.10.25.01.22.03 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 25 Oct 2023 01:22:06 -0700 (PDT) From: Wei-Shih Lin X-Google-Original-From: Wei-Shih Lin To: dmitry.torokhov@gmail.com, robh+dt@kernel.org, krzysztof.kozlowski+dt@linaro.org, conor+dt@kernel.org Cc: linux-input@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH 2/2] Input: Add driver for Novatek NT519XX series touchscreen devices Date: Wed, 25 Oct 2023 16:20:54 +0800 Message-ID: <20231025082054.1190-3-Weishih_Lin@novatek.com.tw> X-Mailer: git-send-email 2.42.0.windows.2 In-Reply-To: <20231025082054.1190-1-Weishih_Lin@novatek.com.tw> References: <20231025082054.1190-1-Weishih_Lin@novatek.com.tw> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" This patch adds support for Novatek NT519XX series touchscreen devices. Existing Novatek touchscreen driver code developed for Acer Iconia One 7 B1-750 tablet with Novatek IC NT11205 is novatek-nvt-ts.c in the path drivers/input/touchscreen/. However, this patch supports touch features for automotive display with Novatek TDDI NT519XX. Signed-off-by: Wei-Shih Lin --- drivers/input/touchscreen/Kconfig | 12 + drivers/input/touchscreen/Makefile | 1 + drivers/input/touchscreen/nt519xx.c | 995 ++++++++++++++++++++ drivers/input/touchscreen/nt519xx.h | 130 +++ drivers/input/touchscreen/nt519xx_mem_map.h | 262 ++++++ 5 files changed, 1400 insertions(+) create mode 100644 drivers/input/touchscreen/nt519xx.c create mode 100644 drivers/input/touchscreen/nt519xx.h create mode 100644 drivers/input/touchscreen/nt519xx_mem_map.h diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/= Kconfig index c2cbd332af1d..6f84e31e7dbd 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -1389,4 +1389,16 @@ config TOUCHSCREEN_HIMAX_HX83112B To compile this driver as a module, choose M here: the module will be called himax_hx83112b. =20 +config TOUCHSCREEN_NT519XX + tristate "Novatek NT519XX based touchscreens" + depends on I2C + help + Say Y here if you have a Novatek NT519XX based touchscreen + controller. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called nt519xx. + endif diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen= /Makefile index 159cd5136fdb..598d38e4e06a 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -117,3 +117,4 @@ obj-$(CONFIG_TOUCHSCREEN_RASPBERRYPI_FW) +=3D raspberry= pi-ts.o obj-$(CONFIG_TOUCHSCREEN_IQS5XX) +=3D iqs5xx.o obj-$(CONFIG_TOUCHSCREEN_ZINITIX) +=3D zinitix.o obj-$(CONFIG_TOUCHSCREEN_HIMAX_HX83112B) +=3D himax_hx83112b.o +obj-$(CONFIG_TOUCHSCREEN_NT519XX) +=3D nt519xx.o diff --git a/drivers/input/touchscreen/nt519xx.c b/drivers/input/touchscree= n/nt519xx.c new file mode 100644 index 000000000000..f3cc884c65e9 --- /dev/null +++ b/drivers/input/touchscreen/nt519xx.c @@ -0,0 +1,995 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Driver for Novatek NT519xx touchscreens + * + * Copyright (C) 2023 Wei-Shih Lin + * Copyright (C) 2023 Leo LS Chang + * + * Copyright (C) 2010 - 2023 Novatek, Inc. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "nt519xx.h" + +struct nvt_ts_data *ts; + +static int nvt_fb_notifier_callback(struct notifier_block *self, + unsigned long event, void *data); + +static uint8_t bTouchIsAwake; +static uint8_t debug_log; + +static void nvt_irq_enable(bool enable) +{ + struct irq_desc *desc; + + if (enable) { + if (!ts->irq_enabled) { + enable_irq(ts->client->irq); + ts->irq_enabled =3D true; + } + } else { + if (ts->irq_enabled) { + disable_irq(ts->client->irq); + ts->irq_enabled =3D false; + } + } + + desc =3D irq_to_desc(ts->client->irq); + NVT_LOG("enable=3D%d, desc->depth=3D%d\n", enable, desc->depth); +} + +int32_t CTP_I2C_READ(struct i2c_client *client, uint16_t address, uint8_t = *buf, + uint16_t len) +{ + struct i2c_msg msgs[2]; + int32_t ret =3D -1; + int32_t retries =3D 0; + + mutex_lock(&ts->xbuf_lock); + + msgs[0].flags =3D !I2C_M_RD; + msgs[0].addr =3D address; + msgs[0].len =3D 1; + msgs[0].buf =3D &buf[0]; + + msgs[1].flags =3D I2C_M_RD; + msgs[1].addr =3D address; + msgs[1].len =3D len - 1; + msgs[1].buf =3D ts->xbuf; + + while (retries < 5) { + ret =3D i2c_transfer(client->adapter, msgs, 2); + if (ret =3D=3D 2) + break; + retries++; + } + + if (unlikely(retries =3D=3D 5)) { + NVT_ERR("ret=3D%d\n", ret); + ret =3D -EIO; + } + + memcpy(buf + 1, ts->xbuf, len - 1); + + mutex_unlock(&ts->xbuf_lock); + + return ret; +} + +int32_t CTP_I2C_WRITE(struct i2c_client *client, uint16_t address, uint8_t= *buf, + uint16_t len) +{ + struct i2c_msg msg; + int32_t ret =3D -1; + int32_t retries =3D 0; + + mutex_lock(&ts->xbuf_lock); + + msg.flags =3D !I2C_M_RD; + msg.addr =3D address; + msg.len =3D len; + memcpy(ts->xbuf, buf, len); + msg.buf =3D ts->xbuf; + + while (retries < 5) { + ret =3D i2c_transfer(client->adapter, &msg, 1); + if (ret =3D=3D 1) + break; + retries++; + } + + if (unlikely(retries =3D=3D 5)) { + NVT_ERR("ret=3D%d\n", ret); + ret =3D -EIO; + } + + mutex_unlock(&ts->xbuf_lock); + + return ret; +} + +int32_t nvt_set_page(uint16_t i2c_addr, uint32_t addr) +{ + uint8_t buf[4] =3D { 0 }; + + buf[0] =3D 0xFF; // novatek set page command + buf[1] =3D (addr >> 16) & 0xFF; + buf[2] =3D (addr >> 8) & 0xFF; + + return CTP_I2C_WRITE(ts->client, i2c_addr, buf, 3); +} + +int32_t nvt_write_addr(uint32_t addr, uint8_t data) +{ + int32_t ret =3D 0; + uint8_t buf[2] =3D { 0 }; + + ret =3D nvt_set_page(I2C_FW_ADDRESS, addr); + if (ret < 0) { + NVT_ERR("Set page 0x%06X failed! ret=3D%d\n", addr, ret); + return ret; + } + + buf[0] =3D addr & 0xFF; + buf[1] =3D data; + ret =3D CTP_I2C_WRITE(ts->client, I2C_FW_ADDRESS, buf, 2); + if (ret < 0) { + NVT_ERR("Write data to 0x%06X failed! ret=3D%d\n", addr, ret); + return ret; + } + ret =3D 0; + + return ret; +} + +int32_t nvt_read_reg(struct nvt_ts_reg reg, uint8_t *val) +{ + int32_t ret =3D 0; + uint32_t addr =3D 0; + uint8_t bitmask =3D 0; + uint8_t shift =3D 0; + uint8_t buf[8] =3D { 0 }; + uint8_t temp =3D 0; + + addr =3D reg.addr; + bitmask =3D reg.bitmask; + temp =3D reg.bitmask; + shift =3D 0; + while (1) { + if ((temp >> shift) & 0x01) + break; + if (shift =3D=3D 8) { + NVT_ERR("bitmask all bits zero!\n"); + ret =3D -1; + break; + } + shift++; + } + + nvt_set_page(I2C_FW_ADDRESS, addr); + buf[0] =3D addr & 0xFF; + buf[1] =3D 0x00; + ret =3D CTP_I2C_READ(ts->client, I2C_FW_ADDRESS, buf, 2); + if (ret < 0) { + NVT_ERR("CTP_I2C_READ failed! ret=3D%d\n", ret); + goto nvt_read_register_exit; + } + *val =3D (buf[1] & bitmask) >> shift; + +nvt_read_register_exit: + return ret; +} + +void nvt_sw_reset_idle(void) +{ + nvt_write_addr(SWRST_SIF_ADDR, 0xAA); + + usleep_range(15000, 16000); + + nvt_clear_crc_err_flag(); +} + +void nvt_bootloader_reset(void) +{ + NVT_LOG("start\n"); + + nvt_write_addr(SWRST_SIF_ADDR, 0x69); + + msleep(35); + + NVT_LOG("end\n"); +} + +bool nvt_check_fw_reset_state(enum rst_complete_state check_reset_state) +{ + uint8_t buf[8] =3D { 0 }; + bool ret =3D true; + int32_t retry =3D 0; + + nvt_set_page(I2C_FW_ADDRESS, ts->mmap->EVENT_BUF_ADDR); + + while (1) { + usleep_range(10000, 11000); + + buf[0] =3D EVENT_MAP_RESET_COMPLETE; + buf[1] =3D 0x00; + CTP_I2C_READ(ts->client, I2C_FW_ADDRESS, buf, 6); + + if ((buf[1] >=3D check_reset_state) && + (buf[1] <=3D RESET_STATE_MAX)) { + ret =3D true; + break; + } + + retry++; + if (unlikely(retry > 100)) { + NVT_ERR("retry=3D%d, buf[1]=3D0x%02X\n", retry, buf[1]); + ret =3D false; + break; + } + } + + return ret; +} + +int32_t nvt_check_cascade_chip_numbers(void) +{ + uint8_t enb_casc =3D 0; + uint8_t sdloc =3D 0; + uint8_t ic_num =3D 0; + int32_t ret =3D 0; + + if (ts->cascade_type) { + if (ts->cascade_type =3D=3D CASCADE_ENB_CASC) { + if (!ts->mmap->ENB_CASC_REG.addr) { + NVT_ERR("ENB_CASC_REG.addr is missing!\n"); + return (-EIO); + } + + nvt_set_page(I2C_FW_ADDRESS, + ts->mmap->ENB_CASC_REG.addr); + nvt_read_reg(ts->mmap->ENB_CASC_REG, &enb_casc); + + if (enb_casc =3D=3D ENB_CASC_1CHIP) + ts->cascade_num =3D CASCADE_1CHIP; + else + ts->cascade_num =3D CASCADE_2CHIP; + + } else if (ts->cascade_type =3D=3D CASCADE_SDLOC) { + if (!ts->mmap->SDLOC_REG.addr) { + NVT_ERR("SDLOC_REG.addr is missing!\n"); + return (-EIO); + } + + nvt_set_page(I2C_FW_ADDRESS, ts->mmap->SDLOC_REG.addr); + nvt_read_reg(ts->mmap->SDLOC_REG, &sdloc); + + switch (sdloc) { + case SDLOC_1CHIP: + ts->cascade_num =3D CASCADE_1CHIP; + break; + case SDLOC_2CHIP: + ts->cascade_num =3D CASCADE_2CHIP; + break; + case SDLOC_3CHIP_AND_ABOVE: + ts->cascade_num =3D CASCADE_3CHIP; + break; + default: + ts->cascade_num =3D CASCADE_CHECK_ERR; + NVT_ERR("Undefined CASCADE_TYPE_SDLOC: 0x%X!\n", + sdloc); + ret =3D (-EIO); + break; + } + } else if (ts->cascade_type =3D=3D CASCADE_IC_NUM) { + if (!ts->mmap->IC_NUM_REG.addr) { + NVT_ERR("IC_NUM_REG.addr is missing!\n"); + return (-EIO); + } + + nvt_set_page(I2C_FW_ADDRESS, ts->mmap->IC_NUM_REG.addr); + nvt_read_reg(ts->mmap->IC_NUM_REG, &ic_num); + + switch (ic_num) { + case IC_NUM_1CHIP: + ts->cascade_num =3D CASCADE_1CHIP; + break; + case IC_NUM_2CHIP: + ts->cascade_num =3D CASCADE_2CHIP; + break; + case IC_NUM_3CHIP: + case IC_NUM_3CHIP_AND_ABOVE: + ts->cascade_num =3D CASCADE_3CHIP; + break; + default: + ts->cascade_num =3D CASCADE_CHECK_ERR; + NVT_ERR("Undefined CASCADE_TYPE_IC_NUM: 0x%X!\n", + ic_num); + ret =3D (-EIO); + break; + } + } + } else { + ts->cascade_num =3D NONE_CASCADE_CASE; + NVT_LOG("CASCADE_NONE cascade_type: %d, cascade_num: %d\n", + ts->cascade_type, ts->cascade_num); + } + + return ret; +} + +int32_t nvt_get_fw_info(void) +{ + uint8_t buf[64] =3D { 0 }; + uint8_t retry_count =3D 2; + int32_t ret =3D 0; + +info_retry: + nvt_set_page(I2C_FW_ADDRESS, + ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_FWINFO); + + buf[0] =3D EVENT_MAP_FWINFO; + CTP_I2C_READ(ts->client, I2C_FW_ADDRESS, buf, 17); + if ((buf[1] + buf[2]) !=3D 0xFF) { + NVT_ERR("FW info is broken! fw_ver=3D0x%02X, ~fw_ver=3D0x%02X\n", + buf[1], buf[2]); + if (retry_count < 3) { + retry_count++; + NVT_ERR("retry_count=3D%d\n", retry_count); + goto info_retry; + } else { + ts->fw_ver =3D 0; + NVT_ERR("Set default fw_ver=3D0x%02X\n", ts->fw_ver); + ret =3D -1; + goto out; + } + } + ts->fw_ver =3D buf[1]; + ts->x_num =3D buf[3]; + ts->y_num =3D buf[4]; + ts->nvt_pid =3D (uint16_t)((buf[36] << 8) | buf[35]); + + NVT_LOG("fw_ver=3D0x%02X, x_num=3D%0d, y_num=3D%0d, fw_type=3D0x%02X, PID= =3D0x%04X\n", + ts->fw_ver, ts->x_num, ts->y_num, buf[14], ts->nvt_pid); + + ret =3D 0; +out: + return ret; +} + +#ifdef CONFIG_OF +static void nvt_parse_dt(struct device *dev) +{ + struct device_node *np =3D dev->of_node; + + ts->irq_gpio =3D of_get_named_gpio_flags(np, "novatek,irq-gpio", 0, + &ts->irq_flags); + NVT_LOG("novatek,irq-gpio=3D%d\n", ts->irq_gpio); +} +#else /* CONFIG_OF */ +static void nvt_parse_dt(struct device *dev) +{ + ts->irq_gpio =3D NVTTOUCH_INT_PIN; +} +#endif /* CONFIG_OF */ + +static int nvt_gpio_config(struct nvt_ts_data *ts) +{ + int32_t ret =3D 0; + + if (gpio_is_valid(ts->irq_gpio)) { + ret =3D gpio_request_one(ts->irq_gpio, GPIOF_IN, "NVT-int"); + if (ret) { + NVT_ERR("Failed to request NVT-int GPIO!\n"); + goto err_request_irq_gpio; + } + } + + return ret; + +err_request_irq_gpio: + return ret; +} + +static void nvt_gpio_deconfig(struct nvt_ts_data *ts) +{ + if (gpio_is_valid(ts->irq_gpio)) + gpio_free(ts->irq_gpio); +} + +static uint8_t nvt_calculate_crc8(uint8_t *buf, uint8_t length) +{ + uint8_t i =3D 0, j =3D 0; + uint8_t crc8 =3D 0; + uint8_t poly =3D 0x1D; // x8 + x4 + x3 + x2 + 1 + + for (i =3D 0; i < length; i++) { + crc8 ^=3D *(buf + i); + + for (j =3D 0; j < 8; j++) { + if (crc8 & 0x80) + crc8 =3D (crc8 << 1) ^ poly; + else + crc8 =3D (crc8 << 1); + } + } + + crc8 ^=3D (uint8_t)0x00; + return crc8; +} + +static int32_t nvt_ts_event_crc8(uint8_t *buf, uint8_t length) +{ + uint8_t crc8 =3D nvt_calculate_crc8(buf, length - 1); + uint32_t i =3D 0; + + /* The lastest item is CRC8 which is reported by FW */ + if (buf[length - 1] !=3D crc8) { + NVT_ERR("CRC8 not match, FW reported: 0x%02X, Calulated CRC8: 0x%02X\n", + buf[length - 1], crc8); + if (debug_log) { + NVT_ERR("length=3D%d\n", length); + for (i =3D 0; i < length; i++) { + NVT_LOG("%02X ", buf[i]); + if (i % POINT_DATA_LEN =3D=3D 0) + NVT_LOG("\n"); + } + NVT_LOG("\n"); + } + return -1; + } + return crc8; +} + +static irqreturn_t nvt_ts_work_func(int irq, void *data) +{ + int32_t ret =3D -1; + uint8_t touch_event[DUMMY_BYTES + TOUCH_EVENT_CRC_LEN] =3D { 0 }; + uint32_t event_size =3D ARRAY_SIZE(touch_event); + uint32_t position =3D 0; + uint32_t input_x =3D 0; + uint32_t input_y =3D 0; + uint8_t input_id =3D 0; + uint8_t press_id[TOUCH_MAX_FINGER_NUM] =3D { 0 }; + int32_t i =3D 0; + int32_t finger_cnt =3D 0; + + mutex_lock(&ts->lock); + + ret =3D CTP_I2C_READ(ts->client, I2C_FW_ADDRESS, touch_event, event_size); + if (ret < 0) { + NVT_ERR("CTP_I2C_READ failed! ret=3D%d\n", ret); + goto nvt_ts_work_func_exit; + } + + if (debug_log) { + NVT_LOG("Touch event buffer length: %d\n", event_size); + for (i =3D DUMMY_BYTES; i < event_size; i++) { + NVT_LOG("%02X ", touch_event[i]); + if (i % POINT_DATA_LEN =3D=3D 0) + NVT_LOG("\n"); + } + NVT_LOG("\n"); + } + + ret =3D nvt_ts_event_crc8(touch_event + DUMMY_BYTES, + event_size - DUMMY_BYTES); + if (ret < 0) + goto nvt_ts_work_func_exit; + + finger_cnt =3D 0; + + for (i =3D 0; i < ts->max_touch_num; i++) { + position =3D DUMMY_BYTES + ASIL_FLAG_LEN + POINT_DATA_LEN * i; + input_id =3D (uint8_t)(touch_event[position] >> 4); + + if ((input_id =3D=3D 0) || (input_id > ts->max_touch_num)) + continue; + + /* Finger down (enter or moving) */ + if (((touch_event[position] & 0x07) =3D=3D 0x01) || + ((touch_event[position] & 0x07) =3D=3D 0x02)) { + input_x =3D (uint32_t)(touch_event[position + 1] << 8) + + (uint32_t)(touch_event[position + 2]); + input_y =3D (uint32_t)(touch_event[position + 3] << 8) + + (uint32_t)(touch_event[position + 4]); + + if ((input_x > TOUCH_MAX_WIDTH) || + (input_y > TOUCH_MAX_HEIGHT)) + continue; + + press_id[input_id - 1] =3D 1; + input_mt_slot(ts->input_dev, input_id - 1); + input_mt_report_slot_state(ts->input_dev, + MT_TOOL_FINGER, true); + + input_report_abs(ts->input_dev, ABS_MT_POSITION_X, + input_x); + input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, + input_y); + + finger_cnt++; + } + } + + for (i =3D 0; i < ts->max_touch_num; i++) { + if (press_id[i] !=3D 1) { + input_mt_slot(ts->input_dev, i); + input_mt_report_slot_state(ts->input_dev, + MT_TOOL_FINGER, false); + } + } + + input_report_key(ts->input_dev, BTN_TOUCH, (finger_cnt > 0)); + + input_sync(ts->input_dev); + +nvt_ts_work_func_exit: + mutex_unlock(&ts->lock); + + return IRQ_HANDLED; +} + +int32_t nvt_clear_crc_err_flag(void) +{ + uint8_t buf[2] =3D { 0x00 }; + int ret =3D 0; + + nvt_set_page(I2C_FW_ADDRESS, (uint32_t)BLD_SPE_PUPS_ADDR); + + buf[0] =3D (uint32_t)BLD_SPE_PUPS_ADDR & 0xFF; + buf[1] =3D 0xA5; + ret =3D CTP_I2C_WRITE(ts->client, I2C_FW_ADDRESS, buf, 2); + if (ret < 0) { + NVT_LOG("Write 0x%02X to BLD_SPE_PUPS(0x%X) failed, ret=3D%d!\n", + (uint32_t)BLD_SPE_PUPS_ADDR, buf[1], ret); + return ret; + } + + buf[0] =3D (uint32_t)BLD_SPE_PUPS_ADDR & 0xFF; + buf[1] =3D 0x00; + ret =3D CTP_I2C_READ(ts->client, I2C_FW_ADDRESS, buf, 2); + if (ret < 0) { + NVT_LOG("Read BLD_SPE_PUPS (0x%X) failed, ret=3D%d!\n", + (uint32_t)BLD_SPE_PUPS_ADDR, ret); + return ret; + } + + NVT_LOG("BLD_SPE_PUPS(0x%X)=3D0x%02X\n", (uint32_t)BLD_SPE_PUPS_ADDR, + buf[1]); + + if (buf[1] !=3D 0xA5) + return (-EIO); + + return ret; +} + +void nvt_stop_crc_reboot(void) +{ + uint8_t buf[4] =3D { 0 }; + int32_t i =3D 0; + + /* Read dummy buffer to check CRC fail reboot is happening or not */ + nvt_set_page(I2C_FW_ADDRESS, (uint32_t)CHIP_VER_TRIM_ADDR); + + /* Read to check if buf is 0xFC which means IC is in CRC reboot */ + buf[0] =3D (uint32_t)CHIP_VER_TRIM_ADDR & 0xFF; + CTP_I2C_READ(ts->client, I2C_FW_ADDRESS, buf, 4); + + if (((buf[1] =3D=3D 0xFB) && (buf[2] =3D=3D 0xFB) && (buf[3] =3D=3D 0xFB)= ) || + ((buf[1] =3D=3D 0xFC) && (buf[2] =3D=3D 0xFC) && (buf[3] =3D=3D 0xFC)= ) || + ((buf[1] =3D=3D 0xFF) && (buf[2] =3D=3D 0xFF) && (buf[3] =3D=3D 0xFF)= )) { + /* IC is in CRC fail reboot loop, needs to be stopped! */ + for (i =3D 5; i > 0; i--) { + /* + * Reset idle twice to ensure that the cmd will be + * executed in CRC reboot process + */ + nvt_sw_reset_idle(); + + nvt_sw_reset_idle(); + usleep_range(1000, 2000); + + if (nvt_clear_crc_err_flag() >=3D 0) + break; + } + if (i =3D=3D 0) + NVT_ERR("CRC auto reboot is not able to be stopped! buf[1]=3D0x%02X\n", + buf[1]); + } +} + +static int8_t nvt_ts_check_chip_ver_trim(void) +{ + uint8_t buf[8] =3D { 0 }; + int32_t i =3D 0; + int32_t j =3D 0; + int32_t k =3D 0; + int32_t found_nvt_chip =3D 0; + int32_t ret =3D -1; + + nvt_bootloader_reset(); + + for (i =3D 5; i > 0; i--) { + nvt_sw_reset_idle(); + + nvt_set_page(I2C_FW_ADDRESS, (uint32_t)CHIP_VER_TRIM_ADDR); + + buf[0] =3D ((uint32_t)CHIP_VER_TRIM_ADDR & 0xFF); + buf[1] =3D 0x00; + buf[2] =3D 0x00; + buf[3] =3D 0x00; + buf[4] =3D 0x00; + buf[5] =3D 0x00; + buf[6] =3D 0x00; + CTP_I2C_READ(ts->client, I2C_FW_ADDRESS, buf, 7); + NVT_LOG("EXT_CHIP_ID_RD(0x%X)=3D0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, = 0x%02X\n", + (uint32_t)CHIP_VER_TRIM_ADDR, buf[1], buf[2], buf[3], + buf[4], buf[5], buf[6]); + + /* Stop CRC check to prevent IC auto reboot */ + if (((buf[1] =3D=3D 0xFB) && (buf[2] =3D=3D 0xFB) && + (buf[3] =3D=3D 0xFB)) || + ((buf[1] =3D=3D 0xFC) && (buf[2] =3D=3D 0xFC) && + (buf[3] =3D=3D 0xFC)) || + ((buf[1] =3D=3D 0xFF) && (buf[2] =3D=3D 0xFF) && + (buf[3] =3D=3D 0xFF))) { + nvt_stop_crc_reboot(); + continue; + } + + /* Compare read chip id on supported list */ + for (j =3D 0; j < ARRAY_SIZE(trim_id_table); j++) { + found_nvt_chip =3D 0; + + /* Compare each byte */ + for (k =3D 0; k < NVT_ID_BYTE_MAX; k++) { + if (trim_id_table[j].mask[k]) { + if (buf[k + 1] !=3D + trim_id_table[j].id[k]) + break; + } + } + + if (k =3D=3D NVT_ID_BYTE_MAX) + found_nvt_chip =3D 1; + + if (found_nvt_chip) { + ts->mmap =3D trim_id_table[j].mmap; + ts->cascade_type =3D + trim_id_table[j].hwinfo->cascade_type; + + ret =3D nvt_check_cascade_chip_numbers(); + if (ret < 0) + NVT_ERR("Get cascade chip numbers failed! ret=3D%d\n", + ret); + + /* Find out the memory map of target chip numbers */ + if (ts->cascade_num =3D=3D + trim_id_table[j].hwinfo->cascade_num) { + if (ts->cascade_type =3D=3D + CASCADE_ENB_CASC) { + NVT_LOG("ENB_CASC_REG.addr(0x%X)\n", + ts->mmap->ENB_CASC_REG + .addr); + } else if (ts->cascade_type =3D=3D + CASCADE_IC_NUM) { + NVT_LOG("IC_NUM_REG.addr(0x%X)\n", + ts->mmap->IC_NUM_REG.addr; + } else { + NVT_LOG("Non cascade case\n"); + } + NVT_LOG("trim_id_table[%d] is matched.\n", + j); + NVT_LOG("This is NVT touch IC.\n"); + goto out; + } + } + } + usleep_range(10000, 11000); + } + + ts->mmap =3D NULL; + ret =3D -1; + +out: + return ret; +} + +static int32_t nvt_ts_probe(struct i2c_client *client) +{ + int32_t ret =3D 0; + + ts =3D kzalloc(sizeof(struct nvt_ts_data), GFP_KERNEL); + if (ts =3D=3D NULL) { + NVT_ERR("Failed to allocated memory for nvt ts data!\n"); + return -ENOMEM; + } + + ts->xbuf =3D kzalloc(NVT_XBUF_LEN, GFP_KERNEL); + if (ts->xbuf =3D=3D NULL) { + NVT_ERR("kzalloc for xbuf failed!\n"); + ret =3D -ENOMEM; + goto err_malloc_xbuf; + } + + ts->client =3D client; + i2c_set_clientdata(client, ts); + + nvt_parse_dt(&client->dev); + + ret =3D nvt_gpio_config(ts); + if (ret) { + NVT_ERR("Gpio config error!\n"); + goto err_gpio_config_failed; + } + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + NVT_ERR("i2c_check_functionality() failed! (no I2C_FUNC_I2C)\n"); + ret =3D -ENODEV; + goto err_check_functionality_failed; + } + + mutex_init(&ts->lock); + mutex_init(&ts->xbuf_lock); + + /* Need 10ms delay after POR (power on reset) */ + usleep_range(10000, 11000); + + ret =3D nvt_ts_check_chip_ver_trim(); + if (ret) { + NVT_ERR("This chip is not identified in mapping table!\n"); + ret =3D -EINVAL; + goto err_chipvertrim_failed; + } + + nvt_bootloader_reset(); + nvt_check_fw_reset_state(RESET_STATE_INIT); + nvt_get_fw_info(); + + ts->input_dev =3D input_allocate_device(); + if (ts->input_dev =3D=3D NULL) { + NVT_ERR("Allocate input device failed!\n"); + ret =3D -ENOMEM; + goto err_input_dev_alloc_failed; + } + + ts->max_touch_num =3D TOUCH_MAX_FINGER_NUM; + + ts->int_trigger_type =3D INT_TRIGGER_TYPE; + ts->input_dev->evbit[0] =3D BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | + BIT_MASK(EV_ABS); + ts->input_dev->keybit[BIT_WORD(BTN_TOUCH)] =3D BIT_MASK(BTN_TOUCH); + ts->input_dev->propbit[0] =3D BIT(INPUT_PROP_DIRECT); + + input_mt_init_slots(ts->input_dev, ts->max_touch_num, 0); + +#if TOUCH_MAX_FINGER_NUM > 1 + input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X, 0, + TOUCH_MAX_WIDTH, 0, 0); + input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y, 0, + TOUCH_MAX_HEIGHT, 0, 0); +#endif + + sprintf(ts->phys, "input/ts"); + ts->input_dev->name =3D NVT_TS_NAME; + ts->input_dev->phys =3D ts->phys; + ts->input_dev->id.bustype =3D BUS_I2C; + + ret =3D input_register_device(ts->input_dev); + if (ret) { + NVT_ERR("Register input device (%s) failed! ret=3D%d\n", + ts->input_dev->name, ret); + goto err_input_register_device_failed; + } + + client->irq =3D gpio_to_irq(ts->irq_gpio); + if (client->irq) { + NVT_LOG("int_trigger_type=3D%d\n", ts->int_trigger_type); + ts->irq_enabled =3D true; + ret =3D request_threaded_irq(client->irq, NULL, nvt_ts_work_func, + ts->int_trigger_type | IRQF_ONESHOT, + NVT_I2C_NAME, ts); + if (ret !=3D 0) { + NVT_ERR("Request irq failed! ret=3D%d\n", ret); + goto err_int_request_failed; + } else { + nvt_irq_enable(false); + NVT_LOG("Request irq %d succeed.\n", client->irq); + } + } + + ts->fb_notif.notifier_call =3D nvt_fb_notifier_callback; + ret =3D fb_register_client(&ts->fb_notif); + if (ret) { + NVT_ERR("Register fb_notifier failed! ret=3D%d\n", ret); + goto err_register_fb_notif_failed; + } + + bTouchIsAwake =3D 1; + NVT_LOG("end\n"); + + nvt_irq_enable(true); + + return 0; + + if (fb_unregister_client(&ts->fb_notif)) + NVT_ERR("Error occurred while unregistering fb_notifier!\n"); +err_register_fb_notif_failed: + free_irq(client->irq, ts); +err_int_request_failed: + input_unregister_device(ts->input_dev); + ts->input_dev =3D NULL; +err_input_register_device_failed: + if (ts->input_dev) { + input_free_device(ts->input_dev); + ts->input_dev =3D NULL; + } +err_input_dev_alloc_failed: +err_chipvertrim_failed: + mutex_destroy(&ts->xbuf_lock); + mutex_destroy(&ts->lock); +err_check_functionality_failed: + nvt_gpio_deconfig(ts); +err_gpio_config_failed: + i2c_set_clientdata(client, NULL); + kfree(ts->xbuf); + ts->xbuf =3D NULL; +err_malloc_xbuf: + kfree(ts); + ts =3D NULL; + + return ret; +} + +static void nvt_ts_remove(struct i2c_client *client) +{ + NVT_LOG("Removing driver...\n"); + + if (fb_unregister_client(&ts->fb_notif)) + NVT_ERR("Error occurred while unregistering fb_notifier!\n"); + + nvt_irq_enable(false); + free_irq(client->irq, ts); + + mutex_destroy(&ts->xbuf_lock); + mutex_destroy(&ts->lock); + + nvt_gpio_deconfig(ts); + + if (ts->input_dev) { + input_unregister_device(ts->input_dev); + ts->input_dev =3D NULL; + } + + i2c_set_clientdata(client, NULL); + + kfree(ts->xbuf); + ts->xbuf =3D NULL; + + kfree(ts); + ts =3D NULL; +} + +static void nvt_ts_shutdown(struct i2c_client *client) +{ + NVT_LOG("Shutdown driver...\n"); + + nvt_irq_enable(false); + + if (fb_unregister_client(&ts->fb_notif)) + NVT_ERR("Error occurred while unregistering fb_notifier!\n"); +} + +static int32_t nvt_ts_resume(struct device *dev) +{ + if (bTouchIsAwake) { + NVT_LOG("Touch is already resume.\n"); + return 0; + } + + mutex_lock(&ts->lock); + + NVT_LOG("start\n"); + + nvt_bootloader_reset(); + nvt_check_fw_reset_state(RESET_STATE_NORMAL_RUN); + + nvt_irq_enable(true); + + bTouchIsAwake =3D 1; + + mutex_unlock(&ts->lock); + + NVT_LOG("end\n"); + + return 0; +} + +static int nvt_fb_notifier_callback(struct notifier_block *self, + unsigned long event, void *data) +{ + struct fb_event *evdata =3D data; + int *blank; + struct nvt_ts_data *ts =3D + container_of(self, struct nvt_ts_data, fb_notif); + + if (evdata && evdata->data && event =3D=3D FB_EVENT_BLANK) { + blank =3D evdata->data; + if (*blank =3D=3D FB_BLANK_UNBLANK) { + NVT_LOG("event=3D%lu, *blank=3D%d\n", event, *blank); + nvt_ts_resume(&ts->client->dev); + } + } + + return 0; +} + +static const struct i2c_device_id nvt_ts_id[] =3D { { NVT_I2C_NAME, 0 }, {= } }; + +#ifdef CONFIG_OF +static const struct of_device_id nvt_match_table[] =3D { + { + .compatible =3D "novatek,NVT-ts", + }, + {}, +}; +#endif + +static struct i2c_driver nvt_i2c_driver =3D { + .probe =3D nvt_ts_probe, + .remove =3D nvt_ts_remove, + .shutdown =3D nvt_ts_shutdown, + .id_table =3D nvt_ts_id, + .driver =3D { + .name =3D NVT_I2C_NAME, + .owner =3D THIS_MODULE, +#ifdef CONFIG_OF + .of_match_table =3D nvt_match_table, +#endif + }, +}; + +static int32_t __init nvt_driver_init(void) +{ + int32_t ret =3D 0; + + NVT_LOG("start\n"); + + bTouchIsAwake =3D 0; + + ret =3D i2c_add_driver(&nvt_i2c_driver); + if (ret) { + NVT_ERR("Failed to add i2c driver!"); + goto err_driver; + } + + NVT_LOG("end\n"); + +err_driver: + return ret; +} + +static void __exit nvt_driver_exit(void) +{ + i2c_del_driver(&nvt_i2c_driver); +} + +module_init(nvt_driver_init); +module_exit(nvt_driver_exit); + +MODULE_DESCRIPTION("Novatek Touchscreen Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/nt519xx.h b/drivers/input/touchscree= n/nt519xx.h new file mode 100644 index 000000000000..27521416cc6c --- /dev/null +++ b/drivers/input/touchscreen/nt519xx.h @@ -0,0 +1,130 @@ +/* SPDX-License-Identifier: GPL-2.0-only + * + * Driver for Novatek NT519xx touchscreens + * + * Copyright (C) 2023 Wei-Shih Lin + * Copyright (C) 2023 Leo LS Chang + * + * Copyright (C) 2010 - 2023 Novatek, Inc. + * + */ +#ifndef _LINUX_NVT_TOUCH_H +#define _LINUX_NVT_TOUCH_H + +#include +#include +#include +#include +#include + +#include "nt519xx_mem_map.h" + +#define NVT_DEBUG (1) + +/* GPIO Number */ +#define NVTTOUCH_RST_PIN (980) +#define NVTTOUCH_INT_PIN (943) + +/* INT Trigger Mode */ +#define INT_TRIGGER_TYPE (IRQ_TYPE_EDGE_RISING) + +/* I2C Driver Info */ +#define NVT_I2C_NAME "NVT-ts" +#define I2C_FW_ADDRESS (0x01) +#define I2C_HW_ADDRESS (0x62) + +#if NVT_DEBUG +#define NVT_LOG(fmt, args...) = \ + dev_err(&ts->client->dev, "[%s] %s %d: " fmt, NVT_I2C_NAME, __func__, \ + __LINE__, ##args) +#else +#define NVT_LOG(fmt, args...) = \ + dev_info(&ts->client->dev, "[%s] %s %d: " fmt, NVT_I2C_NAME, __func__, \ + __LINE__, ##args) +#endif +#define NVT_ERR(fmt, args...) = \ + dev_err(&ts->client->dev, "[ERROR][%s] %s %d: " fmt, NVT_I2C_NAME, \ + __func__, __LINE__, ##args) + +/* Input Device Info */ +#define NVT_TS_NAME "NVTCapacitiveTouchScreen" + +/* Touch Info */ +#define TOUCH_MAX_WIDTH (1920) +#define TOUCH_MAX_HEIGHT (1080) +#define TOUCH_MAX_FINGER_NUM (10) + +/* Each data length in event buffer (unit: byte) */ +#define ASIL_FLAG_LEN (1) +#define POINT_DATA_LEN (7) +#define BUTTON_STATUS_LEN (2) +#define TOUCH_EVENT_LEN \ + (ASIL_FLAG_LEN + (TOUCH_MAX_FINGER_NUM * POINT_DATA_LEN) + \ + BUTTON_STATUS_LEN) +#define ASIL_INFO_LEN (6) +#define CRC_VALUE_LEN (1) +#define TOUCH_EVENT_CRC_LEN (TOUCH_EVENT_LEN + ASIL_INFO_LEN + CRC_VALUE_L= EN) + +#define DUMMY_BYTES (1) +#define NVT_XBUF_LEN (1025) + +struct nvt_ts_data { + struct i2c_client *client; + struct input_dev *input_dev; + int8_t phys[32]; + struct notifier_block fb_notif; + uint8_t fw_ver; + uint8_t x_num; + uint8_t y_num; + uint8_t max_touch_num; + uint32_t int_trigger_type; + int32_t irq_gpio; + uint32_t irq_flags; + int32_t reset_gpio; + uint32_t reset_flags; + struct mutex lock; + const struct nvt_ts_mem_map *mmap; + enum cascade_type cascade_type; + enum cascade_chip_num cascade_num; + uint16_t nvt_pid; + uint8_t *xbuf; + struct mutex xbuf_lock; + bool irq_enabled; +}; + +enum rst_complete_state { + RESET_STATE_INIT =3D 0xA0, + RESET_STATE_REK, + RESET_STATE_REK_FINISH, + RESET_STATE_NORMAL_RUN, + RESET_STATE_MAX =3D 0xAF +}; + +enum { + EVENT_MAP_HOST_CMD =3D 0x50, + EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE =3D 0x51, + EVENT_MAP_RESET_COMPLETE =3D 0x60, + EVENT_MAP_FWINFO =3D 0x78, + EVENT_MAP_CASCADE_CHIP_NUMBERS =3D 0x99, + EVENT_MAP_PROJECTID =3D 0x9A, +}; + +extern struct nvt_ts_data *ts; + +int32_t CTP_I2C_READ(struct i2c_client *client, uint16_t address, uint8_t = *buf, + uint16_t len); +int32_t CTP_I2C_WRITE(struct i2c_client *client, uint16_t address, uint8_t= *buf, + uint16_t len); +void nvt_bootloader_reset(void); +void nvt_sw_reset_idle(void); +int32_t nvt_clear_crc_err_flag(void); +bool nvt_check_fw_reset_state(enum rst_complete_state check_reset_state); +int32_t nvt_get_fw_info(void); +int32_t nvt_check_cascade_chip_numbers(void); +int32_t nvt_set_page(uint16_t i2c_addr, uint32_t addr); +int32_t nvt_write_addr(uint32_t addr, uint8_t data); +int32_t nvt_read_reg(struct nvt_ts_reg reg, uint8_t *val); + +void nvt_stop_crc_reboot(void); + +#endif /* _LINUX_NVT_TOUCH_H */ diff --git a/drivers/input/touchscreen/nt519xx_mem_map.h b/drivers/input/to= uchscreen/nt519xx_mem_map.h new file mode 100644 index 000000000000..9e9847d73292 --- /dev/null +++ b/drivers/input/touchscreen/nt519xx_mem_map.h @@ -0,0 +1,262 @@ +/* SPDX-License-Identifier: GPL-2.0-only + * + * Driver for Novatek NT519xx touchscreens + * + * Copyright (C) 2023 Wei-Shih Lin + * Copyright (C) 2023 Leo LS Chang + * + * Copyright (C) 2010 - 2023 Novatek, Inc. + * + */ + +#define CHIP_VER_TRIM_ADDR (0xFF004) +#define SWRST_SIF_ADDR (0xFF0FE) +#define BLD_SPE_PUPS_ADDR (0xFF135) + +enum { + BIT0 =3D 0x01, + BIT1 =3D 0x02, + BIT2 =3D 0x04, + BIT3 =3D 0x08, + BIT4 =3D 0x10, + BIT5 =3D 0x20, + BIT6 =3D 0x40, + BIT7 =3D 0x80 +}; + +enum cascade_type { + CASCADE_NONE =3D 0x00, + CASCADE_ENB_CASC =3D 0x01, + CASCADE_SDLOC =3D 0x02, + CASCADE_IC_NUM =3D 0x03 +}; + +enum { ENB_CASC_2CHIP =3D 0x00, ENB_CASC_1CHIP =3D 0x01 }; + +enum { SDLOC_1CHIP =3D 0x04, SDLOC_2CHIP =3D 0x06, SDLOC_3CHIP_AND_ABOVE = =3D 0x07 }; + +enum { + IC_NUM_3CHIP =3D 0x00, + IC_NUM_1CHIP =3D 0x01, + IC_NUM_2CHIP =3D 0x02, + IC_NUM_3CHIP_AND_ABOVE =3D 0x03 +}; + +enum cascade_chip_num { + CASCADE_CHECK_ERR =3D 0, + NONE_CASCADE_CASE =3D 1, + CASCADE_1CHIP =3D NONE_CASCADE_CASE, + CASCADE_2CHIP =3D 2, + CASCADE_3CHIP =3D 3, + CASCADE_4CHIP =3D 4, + CASCADE_5CHIP =3D 5, + CASCADE_CHIP_MAX =3D CASCADE_5CHIP +}; + +struct nvt_ts_reg { + uint32_t addr; + uint8_t bitmask; +}; + +struct nvt_ts_mem_map { + uint32_t EVENT_BUF_ADDR; + struct nvt_ts_reg ENB_CASC_REG; + struct nvt_ts_reg SDLOC_REG; + struct nvt_ts_reg IC_NUM_REG; +}; + +struct nvt_ts_hw_info { + enum cascade_type cascade_type; + enum cascade_chip_num cascade_num; + uint8_t default_x_num; + uint8_t default_y_num; + uint16_t default_abs_x_max; + uint16_t default_abs_y_max; + uint8_t default_max_touch_num; + uint8_t default_max_button_num; +}; + +static const struct nvt_ts_mem_map NT51900_memory_map =3D { + .EVENT_BUF_ADDR =3D 0x2A800, + .ENB_CASC_REG =3D { .addr =3D 0x3F02C, .bitmask =3D BIT0 }, +}; + +static const struct nvt_ts_mem_map NT51920_1chip_memory_map =3D { + .EVENT_BUF_ADDR =3D 0x30500, + .ENB_CASC_REG =3D { .addr =3D 0x3F02C, .bitmask =3D BIT0 }, +}; + +static const struct nvt_ts_mem_map NT51920_2chip_memory_map =3D { + .EVENT_BUF_ADDR =3D 0x30500, + .ENB_CASC_REG =3D { .addr =3D 0x3F02C, .bitmask =3D BIT0 }, +}; + +static const struct nvt_ts_mem_map NT51923_1chip_memory_map =3D { + .EVENT_BUF_ADDR =3D 0x94000, + .SDLOC_REG =3D { .addr =3D 0xFF02C, .bitmask =3D (BIT2 | BIT1 | BIT0) }, +}; + +static const struct nvt_ts_mem_map NT51923_2chip_memory_map =3D { + .EVENT_BUF_ADDR =3D 0x94000, + .SDLOC_REG =3D { .addr =3D 0xFF02C, .bitmask =3D (BIT2 | BIT1 | BIT0) }, +}; + +static const struct nvt_ts_mem_map NT51923_3chip_memory_map =3D { + .EVENT_BUF_ADDR =3D 0x94000, + .SDLOC_REG =3D { .addr =3D 0xFF02C, .bitmask =3D (BIT2 | BIT1 | BIT0) }, +}; + +static const struct nvt_ts_mem_map NT51926_1chip_memory_map =3D { + .EVENT_BUF_ADDR =3D 0x96A00, + .IC_NUM_REG =3D { .addr =3D 0xFF02C, .bitmask =3D (BIT3 | BIT2) }, +}; + +static const struct nvt_ts_mem_map NT51926_2chip_memory_map =3D { + .EVENT_BUF_ADDR =3D 0x96A00, + .IC_NUM_REG =3D { .addr =3D 0xFF02C, .bitmask =3D (BIT3 | BIT2) }, +}; + +static const struct nvt_ts_mem_map NT51926_3chip_memory_map =3D { + .EVENT_BUF_ADDR =3D 0x96A00, + .IC_NUM_REG =3D { .addr =3D 0xFF02C, .bitmask =3D (BIT3 | BIT2) }, +}; + +static struct nvt_ts_hw_info NT51900_hw_info =3D { + .cascade_type =3D CASCADE_NONE, + .cascade_num =3D NONE_CASCADE_CASE, + + .default_x_num =3D 40, + .default_y_num =3D 24, + .default_abs_x_max =3D 1280, + .default_abs_y_max =3D 720, + .default_max_button_num =3D 0, +}; + +static struct nvt_ts_hw_info NT51920_1chip_hw_info =3D { + .cascade_type =3D CASCADE_ENB_CASC, + .cascade_num =3D CASCADE_1CHIP, + + .default_x_num =3D 48, + .default_y_num =3D 20, + .default_abs_x_max =3D 960, + .default_abs_y_max =3D 540, + .default_max_button_num =3D 0, +}; + +static struct nvt_ts_hw_info NT51920_2chip_hw_info =3D { + .cascade_type =3D CASCADE_ENB_CASC, + .cascade_num =3D CASCADE_2CHIP, + + .default_x_num =3D 56, + .default_y_num =3D 21, + .default_abs_x_max =3D 2880, + .default_abs_y_max =3D 1080, + .default_max_button_num =3D 0, +}; + +static struct nvt_ts_hw_info NT51923_1chip_hw_info =3D { + .cascade_type =3D CASCADE_SDLOC, + .cascade_num =3D CASCADE_1CHIP, + + .default_x_num =3D 24, + .default_y_num =3D 60, + .default_abs_x_max =3D 2880, + .default_abs_y_max =3D 1080, + .default_max_button_num =3D 0, +}; + +static struct nvt_ts_hw_info NT51923_2chip_hw_info =3D { + .cascade_type =3D CASCADE_SDLOC, + .cascade_num =3D CASCADE_2CHIP, + + .default_x_num =3D 48, + .default_y_num =3D 60, + .default_abs_x_max =3D 2880, + .default_abs_y_max =3D 1080, + .default_max_button_num =3D 0, +}; + +static struct nvt_ts_hw_info NT51923_3chip_hw_info =3D { + .cascade_type =3D CASCADE_SDLOC, + .cascade_num =3D CASCADE_3CHIP, + + .default_x_num =3D 72, + .default_y_num =3D 40, + .default_abs_x_max =3D 2880, + .default_abs_y_max =3D 1620, + .default_max_button_num =3D 0, +}; + +static struct nvt_ts_hw_info NT51926_1chip_hw_info =3D { + .cascade_type =3D CASCADE_IC_NUM, + .cascade_num =3D CASCADE_1CHIP, + .default_x_num =3D 16, + .default_y_num =3D 60, + .default_abs_x_max =3D 2880, + .default_abs_y_max =3D 1080, + .default_max_button_num =3D 0, +}; +static struct nvt_ts_hw_info NT51926_2chip_hw_info =3D { + .cascade_type =3D CASCADE_IC_NUM, + .cascade_num =3D CASCADE_2CHIP, + .default_x_num =3D 32, + .default_y_num =3D 60, + .default_abs_x_max =3D 1768, + .default_abs_y_max =3D 828, + .default_max_button_num =3D 0, +}; +static struct nvt_ts_hw_info NT51926_3chip_hw_info =3D { + .cascade_type =3D CASCADE_IC_NUM, + .cascade_num =3D CASCADE_3CHIP, + .default_x_num =3D 48, + .default_y_num =3D 60, + .default_abs_x_max =3D 2880, + .default_abs_y_max =3D 1080, + .default_max_button_num =3D 0, +}; +#define NVT_ID_BYTE_MAX 6 +struct nvt_ts_trim_id_table { + uint8_t id[NVT_ID_BYTE_MAX]; + uint8_t mask[NVT_ID_BYTE_MAX]; + const struct nvt_ts_mem_map *mmap; + const struct nvt_ts_hw_info *hwinfo; +}; + +static const struct nvt_ts_trim_id_table trim_id_table[] =3D { + { .id =3D { 0x00, 0x00, 0x00, 0x00, 0x19, 0x05 }, + .mask =3D { 0, 0, 0, 1, 1, 1 }, + .mmap =3D &NT51900_memory_map, + .hwinfo =3D &NT51900_hw_info }, + { .id =3D { 0x00, 0x00, 0x01, 0x20, 0x19, 0x05 }, + .mask =3D { 0, 0, 0, 1, 1, 1 }, + .mmap =3D &NT51920_1chip_memory_map, + .hwinfo =3D &NT51920_1chip_hw_info }, + { .id =3D { 0x00, 0x00, 0x01, 0x20, 0x19, 0x05 }, + .mask =3D { 0, 0, 0, 1, 1, 1 }, + .mmap =3D &NT51920_2chip_memory_map, + .hwinfo =3D &NT51920_2chip_hw_info }, + { .id =3D { 0x00, 0x00, 0x00, 0x23, 0x19, 0x05 }, + .mask =3D { 0, 0, 0, 1, 1, 1 }, + .mmap =3D &NT51923_1chip_memory_map, + .hwinfo =3D &NT51923_1chip_hw_info }, + { .id =3D { 0x00, 0x00, 0x00, 0x23, 0x19, 0x05 }, + .mask =3D { 0, 0, 0, 1, 1, 1 }, + .mmap =3D &NT51923_2chip_memory_map, + .hwinfo =3D &NT51923_2chip_hw_info }, + { .id =3D { 0x00, 0x00, 0x00, 0x23, 0x19, 0x05 }, + .mask =3D { 0, 0, 0, 1, 1, 1 }, + .mmap =3D &NT51923_3chip_memory_map, + .hwinfo =3D &NT51923_3chip_hw_info }, + { .id =3D { 0x00, 0x00, 0x00, 0x26, 0x19, 0x05 }, + .mask =3D { 0, 0, 0, 1, 1, 1 }, + .mmap =3D &NT51926_1chip_memory_map, + .hwinfo =3D &NT51926_1chip_hw_info }, + { .id =3D { 0x00, 0x00, 0x00, 0x26, 0x19, 0x05 }, + .mask =3D { 0, 0, 0, 1, 1, 1 }, + .mmap =3D &NT51926_2chip_memory_map, + .hwinfo =3D &NT51926_2chip_hw_info }, + { .id =3D { 0x00, 0x00, 0x00, 0x26, 0x19, 0x05 }, + .mask =3D { 0, 0, 0, 1, 1, 1 }, + .mmap =3D &NT51926_3chip_memory_map, + .hwinfo =3D &NT51926_3chip_hw_info }, +}; --=20 2.26.1