* [PATCH RFC/RFT 0/3] Add support to Novatek's touch IC nt36xxx
@ 2024-10-15 12:53 George Chan via B4 Relay
2024-10-15 12:53 ` [PATCH RFC/RFT 1/3] dt-bindings: touchscreen: Add binding for Novatek NT36xxx series driver George Chan via B4 Relay
` (3 more replies)
0 siblings, 4 replies; 7+ messages in thread
From: George Chan via B4 Relay @ 2024-10-15 12:53 UTC (permalink / raw)
To: Dmitry Torokhov, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Del Regno, Henrik Rydberg
Cc: linux-input, devicetree, linux-kernel, George Chan
Initially support for nt36xxx series spi device. Below
list all supported varients:
- NT36675
- NT36672A
- NT36772(?)
- NT36525
- NT36676F
I had tested it with Redmi note 9 pro, aka NT36675 chip.
This series is based on my repo below:
https://github.com/99degree/linux/tree/nt36xxx
There is a boot-and-functional tree for miatoll device:
https://github.com/99degree/linux/tree/working-20241015
And the older dev history:
https://github.com/99degree/linux/tree/nt36xxx_old
https://github.com/99degree/linux/tree/working-20230528/drivers/input/touchscreen
This driver is based on
AngeloGioacchino Del Regno for i2c based drive
https://patchwork.kernel.org/project/linux-input/cover/20201028221302.66583-1-kholk11@gmail.com/#24831734
_AND_
Neil Armstrong for the spi device codes
https://patchwork.kernel.org/project/linux-input/patch/20231213-topic-goodix-berlin-upstream-initial-v13-2-5d7a26a5eaa2@linaro.org/
Download fw function is adapted from original vendor driver
https://github.com/LineageOS/android_kernel_xiaomi_sm6250/tree/lineage-21/drivers/input/touchscreen/nt36xxx_spi/
Panel follower functionality is finally added.
Since the driver is split into core+spi so i2c function is relatively
easily to add.
Signed-off-by: George Chan <gchan9527@gmail.com>
---
George Chan (3):
dt-bindings: touchscreen: Add binding for Novatek NT36xxx series driver
[RFC/RFT]Input: Add Novatek NT36xxx touchscreen driver
dts: sm7125-xiaomi-joyeuse: Sample device tree for reference
.../bindings/input/touchscreen/nt36xxx.yaml | 70 +
arch/arm64/boot/dts/qcom/Makefile | 1 +
.../boot/dts/qcom/sm7125-xiaomi-joyeuse-touch.dts | 183 +++
drivers/input/touchscreen/Kconfig | 13 +
drivers/input/touchscreen/Makefile | 2 +
drivers/input/touchscreen/nt36xxx.h | 142 ++
drivers/input/touchscreen/nt36xxx_core.c | 1422 ++++++++++++++++++++
drivers/input/touchscreen/nt36xxx_spi.c | 256 ++++
8 files changed, 2089 insertions(+)
---
base-commit: b852e1e7a0389ed6168ef1d38eb0bad71a6b11e8
change-id: 20241015-nt36xxx-07e458ba2877
Best regards,
--
George Chan <gchan9527@gmail.com>
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH RFC/RFT 1/3] dt-bindings: touchscreen: Add binding for Novatek NT36xxx series driver
2024-10-15 12:53 [PATCH RFC/RFT 0/3] Add support to Novatek's touch IC nt36xxx George Chan via B4 Relay
@ 2024-10-15 12:53 ` George Chan via B4 Relay
2024-10-15 13:39 ` Krzysztof Kozlowski
2024-10-15 14:27 ` Rob Herring (Arm)
2024-10-15 12:53 ` [PATCH RFC/RFT 2/3] Input: Add Novatek NT36xxx touchscreen driver George Chan via B4 Relay
` (2 subsequent siblings)
3 siblings, 2 replies; 7+ messages in thread
From: George Chan via B4 Relay @ 2024-10-15 12:53 UTC (permalink / raw)
To: Dmitry Torokhov, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Del Regno, Henrik Rydberg
Cc: linux-input, devicetree, linux-kernel, George Chan
From: George Chan <gchan9527@gmail.com>
Add binding for the Novatek NT36xxx series touchscreen driver.
Signed-off-by: AngeloGioacchino Del Regno <kholk11@gmail.com>
Signed-off-by: George Chan <gchan9527@gmail.com>
---
.../bindings/input/touchscreen/nt36xxx.yaml | 70 ++++++++++++++++++++++
1 file changed, 70 insertions(+)
diff --git a/Documentation/devicetree/bindings/input/touchscreen/nt36xxx.yaml b/Documentation/devicetree/bindings/input/touchscreen/nt36xxx.yaml
new file mode 100644
index 0000000000..3919f0d026
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/touchscreen/nt36xxx.yaml
@@ -0,0 +1,70 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/input/touchscreen/nt36xxx.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Novatek NT36xxx series touchscreen controller Bindings
+
+maintainers:
+ - AngeloGioacchino Del Regno <kholk11@gmail.com>
+ - George Chan <gchan9527@gmail.com>
+
+allOf:
+ - $ref: touchscreen.yaml#
+
+properties:
+ compatible:
+ enum:
+ - novatek,nt36525-spi
+ - novatek,nt36672a-spi
+ - novatek,nt36675-spi
+ - novatek,nt36676f-spi
+ - novatek,nt36772-spi
+
+ reg:
+ maxItems: 1
+
+ irq-gpio:
+ maxItems: 1
+
+ vdd-supply:
+ description: Power supply regulator for VDD pin
+
+ vio-supply:
+ description: Power supply regulator on VDD-IO pin
+
+ firmware-name:
+ description: Firmware for device initialization, if unspecify, all
+ other IC treat as no firmware needed. For nt36675, default
+ to "novatek_ts_tianma_fw.bin".
+
+ spi-max-frequency:
+ description: Set max frequency to spi bus communication. This is optional.
+
+unevaluatedProperties: false
+
+required:
+ - compatible
+ - reg
+ - irq-gpio
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/irq.h>
+ #include <dt-bindings/gpio/gpio.h>
+
+ spi {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ touchscreen@0 {
+ compatible = "novatek,nt36675-spi";
+ reg = <0>;
+ spi-max-frequency = <4000000>;
+ irq-gpio = <&tlmm 9 0x2001>;
+ firmware-name = "novatek_ts_tianma_fw.bin";
+ };
+ };
+
+...
--
2.43.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH RFC/RFT 2/3] Input: Add Novatek NT36xxx touchscreen driver
2024-10-15 12:53 [PATCH RFC/RFT 0/3] Add support to Novatek's touch IC nt36xxx George Chan via B4 Relay
2024-10-15 12:53 ` [PATCH RFC/RFT 1/3] dt-bindings: touchscreen: Add binding for Novatek NT36xxx series driver George Chan via B4 Relay
@ 2024-10-15 12:53 ` George Chan via B4 Relay
2024-10-15 12:53 ` [PATCH RFC/RFT 3/3] dts: sm7125-xiaomi-joyeuse: Sample device tree for reference George Chan via B4 Relay
2024-10-15 14:17 ` [PATCH RFC/RFT 0/3] Add support to Novatek's touch IC nt36xxx Rob Herring (Arm)
3 siblings, 0 replies; 7+ messages in thread
From: George Chan via B4 Relay @ 2024-10-15 12:53 UTC (permalink / raw)
To: Dmitry Torokhov, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Del Regno, Henrik Rydberg
Cc: linux-input, devicetree, linux-kernel, George Chan
From: George Chan <gchan9527@gmail.com>
This is a driver for the Novatek in-cell touch controller and
supports various chips from the NT36xxx family, currently
including below as listed:
- NT36525
- NT36672A
- NT36675
- NT36676F
- NT36772
- NT36870
Functionality like wake gestures and firmware flashing is not
included. Firmware loading at probe is supported.
This driver is lightly based on the downstream implementation [1].
[1] https://github.com/Rasenkai/caf-tsoft-Novatek-nt36xxx
And rewrite as standard i2c driver [2][3]
[2] https://lore.kernel.org/all/20201026173045.165236-4-kholk11@gmail.com/T/
[3] https://lore.kernel.org/lkml/20230808-topic-nt36xxx-v10-1-dd135dfa0b5e@linaro.org/T/
Then a new rewrite further happened for spi driver [4]
[4] https://github.com/99degree/linux/tree/nt36xxx
This driver referenced to goodix berlin driver spi ops that had
been in mainline kernel. Also, implementation included
panel-follower ops, pm_runtime ops as well as firmware download
ops that is added as spi driver.
Signed-off-by: AngeloGioacchino Del Regno <kholk11@gmail.com>
Signed-off-by: George Chan <gchan9527@gmail.com>
---
drivers/input/touchscreen/Kconfig | 13 +
drivers/input/touchscreen/Makefile | 2 +
drivers/input/touchscreen/nt36xxx.h | 142 +++
drivers/input/touchscreen/nt36xxx_core.c | 1422 ++++++++++++++++++++++++++++++
drivers/input/touchscreen/nt36xxx_spi.c | 256 ++++++
5 files changed, 1835 insertions(+)
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 1ac26fc2e3..654877da4c 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -663,6 +663,19 @@ config TOUCHSCREEN_IMAGIS
To compile this driver as a module, choose M here: the
module will be called imagis.
+config TOUCHSCREEN_NT36XXX_SPI
+ tristate "Novatek NT36XXX In-Cell SPI touchscreen controller"
+ depends on SPI_MASTER
+ select REGMAP
+ help
+ Say Y here if you have a Novatek NT36xxx series In-Cell
+ touchscreen connected to your system over SPI.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called nt36xxx_ts_spi.
+
config TOUCHSCREEN_IMX6UL_TSC
tristate "Freescale i.MX6UL touchscreen controller"
depends on ((OF && GPIOLIB) || COMPILE_TEST) && HAS_IOMEM
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 82bc837ca0..6358a636a5 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -7,6 +7,7 @@
wm97xx-ts-y := wm97xx-core.o
goodix_ts-y := goodix.o goodix_fwupload.o
+nt36xxx_spi_ts-y := nt36xxx_spi.o nt36xxx_core.o
obj-$(CONFIG_TOUCHSCREEN_88PM860X) += 88pm860x-ts.o
obj-$(CONFIG_TOUCHSCREEN_AD7877) += ad7877.o
@@ -67,6 +68,7 @@ obj-$(CONFIG_TOUCHSCREEN_MSG2638) += msg2638.o
obj-$(CONFIG_TOUCHSCREEN_MTOUCH) += mtouch.o
obj-$(CONFIG_TOUCHSCREEN_MK712) += mk712.o
obj-$(CONFIG_TOUCHSCREEN_NOVATEK_NVT_TS) += novatek-nvt-ts.o
+obj-$(CONFIG_TOUCHSCREEN_NT36XXX_SPI) += nt36xxx_spi_ts.o
obj-$(CONFIG_TOUCHSCREEN_HP600) += hp680_ts_input.o
obj-$(CONFIG_TOUCHSCREEN_HP7XX) += jornada720_ts.o
obj-$(CONFIG_TOUCHSCREEN_IPAQ_MICRO) += ipaq-micro-ts.o
diff --git a/drivers/input/touchscreen/nt36xxx.h b/drivers/input/touchscreen/nt36xxx.h
new file mode 100644
index 0000000000..7bf1f72290
--- /dev/null
+++ b/drivers/input/touchscreen/nt36xxx.h
@@ -0,0 +1,142 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2010 - 2017 Novatek, Inc.
+ * Copyright (C) 2020 AngeloGioacchino Del Regno <kholk11@gmail.com>
+ * Copyright (C) 2023-2024 George Chan <gchan9527@gmail.com>
+ */
+
+#ifndef NT36XXX_H
+#define NT36XXX_H
+
+#define NT36XXX_INPUT_DEVICE_NAME "Novatek NT36XXX Touch Sensor"
+#define MAX_SPI_FREQ_HZ 5000000
+
+/* FW Param address */
+#define NT36XXX_FW_ADDR 0x01
+
+#define NT36XXX_TRANSFER_LEN (63*1024)
+
+/* due to extra framework layer, the transfer trunk is as small as
+ * 128 otherwize dma error happened, all routed to spi_sync()
+*/
+
+/* Number of bytes for chip identification */
+#define NT36XXX_ID_LEN_MAX 6
+
+/* Touch info */
+#define TOUCH_DEFAULT_MAX_WIDTH 1080
+#define TOUCH_DEFAULT_MAX_HEIGHT 2246
+#define TOUCH_MAX_FINGER_NUM 10
+#define TOUCH_MAX_PRESSURE 1000
+
+/* Point data length */
+#define POINT_DATA_LEN 65
+
+/* Misc */
+#define NT36XXX_NUM_SUPPLIES 4
+#define NT36XXX_MAX_RETRIES 5
+#define NT36XXX_MAX_FW_RST_RETRY 50
+
+enum nt36xxx_chips {
+ NT36525_IC = 0x1,
+ NT36672A_IC,
+ NT36676F_IC,
+ NT36772_IC,
+ NT36675_IC,
+ NT36870_IC,
+ NTMAX_IC,
+};
+
+enum nt36xxx_cmds {
+ NT36XXX_CMD_ENTER_SLEEP = 0x11,
+ NT36XXX_CMD_BOOTLOADER_RESET = 0x69,
+};
+
+enum nt36xxx_events {
+ NT36XXX_EVT_REPORT = 0x00,
+ NT36XXX_EVT_CRC = 0x35,
+ NT36XXX_EVT_HOST_CMD = 0x50,
+ NT36XXX_EVT_HS_OR_SUBCMD = 0x51, /* Handshake or subcommand byte */
+ NT36XXX_EVT_RESET_COMPLETE = 0x60,
+ NT36XXX_EVT_FWINFO = 0x78,
+ NT36XXX_EVT_READ_PID = 0x80,
+ NT36XXX_EVT_PROJECTID = 0x9a, /* Excess 0x80 write bit, messed trouble, ignored */
+};
+
+enum nt36xxx_fw_state {
+ NT36XXX_STATE_INIT = 0xa0, /* IC Reset */
+ NT36XXX_STATE_REK = 0xa1, /* ReK baseline */
+ NT36XXX_STATE_REK_FINISH = 0xa2, /* Baseline is ready */
+ NT36XXX_STATE_NORMAL_RUN = 0xa3, /* Firmware is running */
+ NT36XXX_STATE_MAX = 0xaf
+};
+
+struct nt36xxx_ts;
+
+struct nvt_fw_parse_data {
+ uint8_t partition;
+ uint8_t ilm_dlm_num;
+};
+
+struct nvt_ts_bin_map {
+ char name[12];
+ uint32_t bin_addr;
+ uint32_t sram_addr;
+ uint32_t size;
+ uint32_t crc;
+ uint32_t loaded;
+};
+
+struct nvt_ts_hw_info {
+ uint8_t carrier_system;
+ uint8_t hw_crc;
+};
+
+struct nt36xxx_abs_object {
+ u16 x;
+ u16 y;
+ u16 z;
+ u8 tm;
+};
+
+struct nt36xxx_fw_info {
+ u8 fw_ver;
+ u8 x_num;
+ u8 y_num;
+ u8 max_buttons;
+ u16 abs_x_max;
+ u16 abs_y_max;
+ u16 nvt_pid;
+};
+
+struct nt36xxx_chip_data {
+ const u32 *mmap;
+ const struct regmap_config *config;
+
+ const char* fw_name;
+ unsigned int max_x;
+ unsigned int max_y;
+ unsigned int abs_x_max;
+ unsigned int abs_y_max;
+ unsigned int max_button;
+ const struct input_id *id;
+};
+
+struct nt36xxx_trim_table {
+ u8 id[NT36XXX_ID_LEN_MAX];
+ u8 mask[NT36XXX_ID_LEN_MAX];
+ enum nt36xxx_chips mapid;
+ uint8_t carrier_system;
+ uint8_t hw_crc;
+};
+
+int nt36xxx_probe(struct device *dev, int irq, const struct input_id *id,
+ struct regmap *regmap);
+
+extern const struct dev_pm_ops nt36xxx_pm_ops;
+extern const u32 nt36675_memory_maps[];
+extern const u32 nt36672a_memory_maps[];
+extern const u32 nt36772_memory_maps[];
+extern const u32 nt36676f_memory_maps[];
+extern const u32 nt36525_memory_maps[];
+#endif
diff --git a/drivers/input/touchscreen/nt36xxx_core.c b/drivers/input/touchscreen/nt36xxx_core.c
new file mode 100644
index 0000000000..a27abe566b
--- /dev/null
+++ b/drivers/input/touchscreen/nt36xxx_core.c
@@ -0,0 +1,1422 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Driver for Novatek NT36xxx series touchscreens
+ *
+ * Copyright (C) 2010 - 2018 Novatek, Inc.
+ * Copyright (C) 2020 XiaoMi, Inc.
+ * Copyright (C) 2020 AngeloGioacchino Del Regno <kholk11@gmail.com>
+ * Copyright (C) 2023-2024 George Chan <gchan9527@gmail.com>
+ *
+ * Based on nt36xxx.c i2c driver from AngeloGioacchino Del Regno
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/devm-helpers.h>
+#include <linux/firmware.h>
+#include <linux/gpio/consumer.h>
+#include <linux/input/mt.h>
+#include <linux/input/touchscreen.h>
+#include <linux/interrupt.h>
+#include <linux/irqnr.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/printk.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/unaligned.h>
+#include <drm/drm_panel.h>
+
+#include "nt36xxx.h"
+
+/* Main mmap to spi addr */
+enum {
+ MMAP_BASELINE_ADDR,
+ MMAP_BASELINE_BTN_ADDR,
+ MMAP_BLD_CRC_EN_ADDR,
+ MMAP_BLD_DES_ADDR,
+ MMAP_BLD_ILM_DLM_CRC_ADDR,
+ MMAP_BLD_LENGTH_ADDR,
+ MMAP_BOOT_RDY_ADDR,
+ MMAP_DIFF_BTN_PIPE0_ADDR,
+ MMAP_DIFF_BTN_PIPE1_ADDR,
+ MMAP_DIFF_PIPE0_ADDR,
+ MMAP_DIFF_PIPE1_ADDR,
+ MMAP_DLM_DES_ADDR,
+ MMAP_DLM_LENGTH_ADDR,
+ MMAP_DMA_CRC_EN_ADDR,
+ MMAP_DMA_CRC_FLAG_ADDR,
+ MMAP_ENG_RST_ADDR,
+ MMAP_EVENT_BUF_ADDR,
+ MMAP_G_DLM_CHECKSUM_ADDR,
+ MMAP_G_ILM_CHECKSUM_ADDR,
+ MMAP_ILM_DES_ADDR,
+ MMAP_ILM_LENGTH_ADDR,
+ MMAP_POR_CD_ADDR,
+ MMAP_RAW_BTN_PIPE0_ADDR,
+ MMAP_RAW_BTN_PIPE1_ADDR,
+ MMAP_RAW_PIPE0_ADDR,
+ MMAP_RAW_PIPE1_ADDR,
+ MMAP_READ_FLASH_CHECKSUM_ADDR,
+ MMAP_RW_FLASH_DATA_ADDR,
+ MMAP_R_DLM_CHECKSUM_ADDR,
+ MMAP_R_ILM_CHECKSUM_ADDR,
+ MMAP_SPI_RD_FAST_ADDR,
+ MMAP_SWRST_N8_ADDR,
+
+ /* below are magic numbers in source code */
+ MMAP_MAGIC_NUMBER_0X1F64E_ADDR,
+
+ /* this addr is not specific to */
+ MMAP_TOP_ADDR,
+ MMAP_MAX_ADDR = MMAP_TOP_ADDR,
+} nt36xxx_ts_mem_map;
+
+static struct drm_panel_follower_funcs nt36xxx_panel_follower_funcs;
+
+struct nt36xxx_ts {
+ struct regmap *regmap;
+
+ struct input_dev *input;
+ struct regulator_bulk_data *supplies;
+ struct gpio_desc *reset_gpio;
+ struct gpio_desc *irq_gpio;
+ int irq;
+ struct device *dev;
+
+ struct mutex lock;
+
+#define NT36XXX_STATUS_SUSPEND BIT(0)
+#define NT36XXX_STATUS_DOWNLOAD_COMPLETE BIT(1)
+#define NT36XXX_STATUS_DOWNLOAD_RECOVER BIT(2)
+#define NT36XXX_STATUS_PREPARE_FIRMWARE BIT(3)
+#define NT36XXX_STATUS_NEED_FIRMWARE BIT(4)
+
+ unsigned int status;
+
+ struct touchscreen_properties prop;
+ struct nt36xxx_fw_info fw_info;
+ struct nt36xxx_abs_object abs_obj;
+
+ struct drm_panel_follower panel_follower;
+
+ struct delayed_work work;
+
+ /* this is a duplicate with nt36xxx_chip_data and since the address might
+ * change in boot/init/download stages so make it a copy of initial map and
+ * update accordingly
+ */
+ u32 *mmap;
+ u32 mmap_data[MMAP_MAX_ADDR];
+
+ struct nvt_fw_parse_data fw_data;
+ struct nvt_ts_bin_map *bin_map;
+
+ uint8_t hw_crc;
+
+ const char * fw_name;
+ struct firmware fw_entry; /* containing request fw data */
+ const struct nt36xxx_chip_data *data;
+};
+
+static const struct nt36xxx_trim_table trim_id_table[] = {
+ /* TODO: port and test all related module */
+ {
+ .id = { 0x0A, 0xFF, 0xFF, 0x72, 0x66, 0x03 },
+ .mask = { 1, 0, 0, 1, 1, 1 },
+ .mapid = NT36672A_IC,
+ },
+ {
+ .id = { 0x55, 0x00, 0xFF, 0x00, 0x00, 0x00 },
+ .mask = { 1, 1, 0, 1, 1, 1 },
+ .mapid = NT36772_IC,
+ },
+ {
+ .id = { 0x55, 0x72, 0xFF, 0x00, 0x00, 0x00 },
+ .mask = { 1, 1, 0, 1, 1, 1 },
+ .mapid = NT36772_IC,
+ },
+ {
+ .id = { 0xAA, 0x00, 0xFF, 0x00, 0x00, 0x00 },
+ .mask = { 1, 1, 0, 1, 1, 1 },
+ .mapid = NT36772_IC,
+ },
+ {
+ .id = { 0xAA, 0x72, 0xFF, 0x00, 0x00, 0x00 },
+ .mask = { 1, 1, 0, 1, 1, 1 },
+ .mapid = NT36772_IC,
+ },
+ {
+ .id = { 0xFF, 0xFF, 0xFF, 0x72, 0x67, 0x03 },
+ .mask = { 0, 0, 0, 1, 1, 1 },
+ .mapid = NT36772_IC,
+ },
+ {
+ .id = { 0xFF, 0xFF, 0xFF, 0x70, 0x66, 0x03 },
+ .mask = { 0, 0, 0, 1, 1, 1 },
+ .mapid = NT36772_IC,
+ },
+ {
+ .id = { 0xFF, 0xFF, 0xFF, 0x70, 0x67, 0x03 },
+ .mask = { 0, 0, 0, 1, 1, 1 },
+ .mapid = NT36772_IC,
+ },
+ {
+ .id = { 0xFF, 0xFF, 0xFF, 0x72, 0x66, 0x03 },
+ .mask = { 0, 0, 0, 1, 1, 1 },
+ .mapid = NT36772_IC,
+ },
+ {
+ .id = { 0xFF, 0xFF, 0xFF, 0x25, 0x65, 0x03 },
+ .mask = { 0, 0, 0, 1, 1, 1 },
+ .mapid = NT36772_IC,
+ },
+ {
+ .id = { 0xFF, 0xFF, 0xFF, 0x70, 0x68, 0x03 },
+ .mask = { 0, 0, 0, 1, 1, 1 },
+ .mapid = NT36772_IC,
+ },
+ {
+ .id = { 0xFF, 0xFF, 0xFF, 0x76, 0x66, 0x03 },
+ .mask = { 0, 0, 0, 1, 1, 1 },
+ .mapid = NT36676F_IC,
+ },
+ {
+ .id = { 0xFF, 0xFF, 0xFF, 0x75, 0x66, 0x03},
+ .mask = { 0, 0, 0, 1, 1, 1 },
+ .mapid = NT36675_IC,
+ .hw_crc = 2,
+ },
+ { },
+};
+
+const u32 nt36675_memory_maps[] = {
+ [MMAP_EVENT_BUF_ADDR] = 0x22D00,
+ [MMAP_RAW_PIPE0_ADDR] = 0x24000,
+ [MMAP_RAW_PIPE1_ADDR] = 0x24000,
+ [MMAP_BASELINE_ADDR] = 0x21B90,
+ [MMAP_DIFF_PIPE0_ADDR] = 0x20C60,
+ [MMAP_DIFF_PIPE1_ADDR] = 0x24C60,
+ [MMAP_READ_FLASH_CHECKSUM_ADDR] = 0x24000,
+ [MMAP_RW_FLASH_DATA_ADDR] = 0x24002,
+ [MMAP_BOOT_RDY_ADDR] = 0x3F10D,
+ [MMAP_BLD_LENGTH_ADDR] = 0x3F138,
+ [MMAP_ILM_LENGTH_ADDR] = 0x3F118,
+ [MMAP_DLM_LENGTH_ADDR] = 0x3F130,
+ [MMAP_BLD_DES_ADDR] = 0x3F114,
+ [MMAP_ILM_DES_ADDR] = 0x3F128,
+ [MMAP_DLM_DES_ADDR] = 0x3F12C,
+ [MMAP_G_ILM_CHECKSUM_ADDR] = 0x3F100,
+ [MMAP_G_DLM_CHECKSUM_ADDR] = 0x3F104,
+ [MMAP_R_ILM_CHECKSUM_ADDR] = 0x3F120,
+ [MMAP_R_DLM_CHECKSUM_ADDR] = 0x3F124,
+ [MMAP_BLD_CRC_EN_ADDR] = 0x3F30E,
+ [MMAP_DMA_CRC_EN_ADDR] = 0x3F136,
+ [MMAP_BLD_ILM_DLM_CRC_ADDR] = 0x3F133,
+ [MMAP_DMA_CRC_FLAG_ADDR] = 0x3F134,
+
+ /* below are specified by dts, so it might change by project-based */
+ [MMAP_SPI_RD_FAST_ADDR] = 0x03F310,
+ [MMAP_SWRST_N8_ADDR] = 0x03F0FE,
+
+ [MMAP_ENG_RST_ADDR] = 0x7FFF80,
+ [MMAP_MAGIC_NUMBER_0X1F64E_ADDR] = 0x1F64E,
+
+ [MMAP_TOP_ADDR] = 0xffffff,
+};
+
+const u32 nt36672a_memory_maps[] = {
+ [MMAP_EVENT_BUF_ADDR] = 0x21C00,
+ [MMAP_RAW_PIPE0_ADDR] = 0x20000,
+ [MMAP_RAW_PIPE1_ADDR] = 0x23000,
+ [MMAP_BASELINE_ADDR] = 0x20BFC,
+ [MMAP_BASELINE_BTN_ADDR] = 0x23BFC,
+ [MMAP_DIFF_PIPE0_ADDR] = 0x206DC,
+ [MMAP_DIFF_PIPE1_ADDR] = 0x236DC,
+ [MMAP_RAW_BTN_PIPE0_ADDR] = 0x20510,
+ [MMAP_RAW_BTN_PIPE1_ADDR] = 0x23510,
+ [MMAP_DIFF_BTN_PIPE0_ADDR] = 0x20BF0,
+ [MMAP_DIFF_BTN_PIPE1_ADDR] = 0x23BF0,
+ [MMAP_READ_FLASH_CHECKSUM_ADDR] = 0x24000,
+ [MMAP_RW_FLASH_DATA_ADDR] = 0x24002,
+ /* Phase 2 Host Download */
+ [MMAP_BOOT_RDY_ADDR] = 0x3F10D,
+ /* BLD CRC */
+ [MMAP_BLD_LENGTH_ADDR] = 0x3F10E, //0x3F10E ~ 0x3F10F (2 bytes)
+ [MMAP_ILM_LENGTH_ADDR] = 0x3F118, //0x3F118 ~ 0x3F119 (2 bytes)
+ [MMAP_DLM_LENGTH_ADDR] = 0x3F130, //0x3F130 ~ 0x3F131 (2 bytes)
+ [MMAP_BLD_DES_ADDR] = 0x3F114, //0x3F114 ~ 0x3F116 (3 bytes)
+ [MMAP_ILM_DES_ADDR] = 0x3F128, //0x3F128 ~ 0x3F12A (3 bytes)
+ [MMAP_DLM_DES_ADDR] = 0x3F12C, //0x3F12C ~ 0x3F12E (3 bytes)
+ [MMAP_G_ILM_CHECKSUM_ADDR] = 0x3F100, //0x3F100 ~ 0x3F103 (4 bytes)
+ [MMAP_G_DLM_CHECKSUM_ADDR] = 0x3F104, //0x3F104 ~ 0x3F107 (4 bytes)
+ [MMAP_R_ILM_CHECKSUM_ADDR] = 0x3F120, //0x3F120 ~ 0x3F123 (4 bytes)
+ [MMAP_R_DLM_CHECKSUM_ADDR] = 0x3F124, //0x3F124 ~ 0x3F127 (4 bytes)
+ [MMAP_BLD_CRC_EN_ADDR] = 0x3F30E,
+ [MMAP_DMA_CRC_EN_ADDR] = 0x3F132,
+ [MMAP_BLD_ILM_DLM_CRC_ADDR] = 0x3F133,
+ [MMAP_DMA_CRC_FLAG_ADDR] = 0x3F134,
+
+ /* below are specified by dts, so it might change by project-based */
+ [MMAP_SPI_RD_FAST_ADDR] = 0x03F310,
+ [MMAP_SWRST_N8_ADDR] = 0x03F0FE,
+
+ [MMAP_ENG_RST_ADDR] = 0x7FFF80,
+ [MMAP_MAGIC_NUMBER_0X1F64E_ADDR] = 0x1F64E,
+
+ [MMAP_TOP_ADDR] = 0xffffff,
+};
+
+const u32 nt36676f_memory_maps[] = {
+ [MMAP_EVENT_BUF_ADDR] = 0x11A00,
+ [MMAP_RAW_PIPE0_ADDR] = 0x10000,
+ [MMAP_RAW_PIPE1_ADDR] = 0x12000,
+ [MMAP_BASELINE_ADDR] = 0x10B08,
+ [MMAP_BASELINE_BTN_ADDR] = 0x12B08,
+ [MMAP_DIFF_PIPE0_ADDR] = 0x1064C,
+ [MMAP_DIFF_PIPE1_ADDR] = 0x1264C,
+ [MMAP_RAW_BTN_PIPE0_ADDR] = 0x10634,
+ [MMAP_RAW_BTN_PIPE1_ADDR] = 0x12634,
+ [MMAP_DIFF_BTN_PIPE0_ADDR] = 0x10AFC,
+ [MMAP_DIFF_BTN_PIPE1_ADDR] = 0x12AFC,
+ [MMAP_READ_FLASH_CHECKSUM_ADDR] = 0x14000,
+ [MMAP_RW_FLASH_DATA_ADDR] = 0x14002,
+
+ /* below are specified by dts, so it might change by project-based */
+ [MMAP_SPI_RD_FAST_ADDR] = 0x03F310,
+ [MMAP_SWRST_N8_ADDR] = 0x03F0FE,
+
+ [MMAP_ENG_RST_ADDR] = 0x7FFF80,
+ [MMAP_MAGIC_NUMBER_0X1F64E_ADDR] = 0x1F64E,
+
+ [MMAP_TOP_ADDR] = 0xffffff,
+};
+
+const u32 nt36772_memory_maps[] = {
+ [MMAP_EVENT_BUF_ADDR] = 0x11E00,
+ [MMAP_RAW_PIPE0_ADDR] = 0x10000,
+ [MMAP_RAW_PIPE1_ADDR] = 0x12000,
+ [MMAP_BASELINE_ADDR] = 0x10E70,
+ [MMAP_BASELINE_BTN_ADDR] = 0x12E70,
+ [MMAP_DIFF_PIPE0_ADDR] = 0x10830,
+ [MMAP_DIFF_PIPE1_ADDR] = 0x12830,
+ [MMAP_RAW_BTN_PIPE0_ADDR] = 0x10E60,
+ [MMAP_RAW_BTN_PIPE1_ADDR] = 0x12E60,
+ [MMAP_DIFF_BTN_PIPE0_ADDR] = 0x10E68,
+ [MMAP_DIFF_BTN_PIPE1_ADDR] = 0x12E68,
+ [MMAP_READ_FLASH_CHECKSUM_ADDR] = 0x14000,
+ [MMAP_RW_FLASH_DATA_ADDR] = 0x14002,
+ /* Phase 2 Host Download */
+ [MMAP_BOOT_RDY_ADDR] = 0x1F141,
+ [MMAP_POR_CD_ADDR] = 0x1F61C,
+ /* BLD CRC */
+ [MMAP_R_ILM_CHECKSUM_ADDR] = 0x1BF00,
+
+ /* below are specified by dts, so it might change by project-based */
+ [MMAP_SPI_RD_FAST_ADDR] = 0x03F310,
+ [MMAP_SWRST_N8_ADDR] = 0x03F0FE,
+
+ [MMAP_ENG_RST_ADDR] = 0x7FFF80,
+ [MMAP_MAGIC_NUMBER_0X1F64E_ADDR] = 0x1F64E,
+
+ [MMAP_TOP_ADDR] = 0xffffff,
+};
+
+const u32 nt36525_memory_maps[] = {
+ [MMAP_EVENT_BUF_ADDR] = 0x11A00,
+ [MMAP_RAW_PIPE0_ADDR] = 0x10000,
+ [MMAP_RAW_PIPE1_ADDR] = 0x12000,
+ [MMAP_BASELINE_ADDR] = 0x10B08,
+ [MMAP_BASELINE_BTN_ADDR] = 0x12B08,
+ [MMAP_DIFF_PIPE0_ADDR] = 0x1064C,
+ [MMAP_DIFF_PIPE1_ADDR] = 0x1264C,
+ [MMAP_RAW_BTN_PIPE0_ADDR] = 0x10634,
+ [MMAP_RAW_BTN_PIPE1_ADDR] = 0x12634,
+ [MMAP_DIFF_BTN_PIPE0_ADDR] = 0x10AFC,
+ [MMAP_DIFF_BTN_PIPE1_ADDR] = 0x12AFC,
+ [MMAP_READ_FLASH_CHECKSUM_ADDR] = 0x14000,
+ [MMAP_RW_FLASH_DATA_ADDR] = 0x14002,
+
+ /* Phase 2 Host Download */
+ [MMAP_BOOT_RDY_ADDR] = 0x1F141,
+ [MMAP_POR_CD_ADDR] = 0x1F61C,
+ /* BLD CRC */
+ [MMAP_R_ILM_CHECKSUM_ADDR] = 0x1BF00,
+
+ /* below are specified by dts, so it might change by project-based */
+ [MMAP_SPI_RD_FAST_ADDR] = 0x03F310,
+ [MMAP_SWRST_N8_ADDR] = 0x03F0FE,
+
+ [MMAP_ENG_RST_ADDR] = 0x7FFF80,
+ [MMAP_MAGIC_NUMBER_0X1F64E_ADDR] = 0x1F64E,
+
+ [MMAP_TOP_ADDR] = 0xffffff,
+};
+
+void __maybe_unused _debug_irq(struct nt36xxx_ts *ts, int line) {
+ struct irq_desc *desc;
+ desc = irq_data_to_desc( irq_get_irq_data(ts->irq));
+ dev_info(ts->dev, "%d irq_desc depth=%d", line, desc->depth );
+}
+
+#define debug_irq(a) _debug_irq(a, __LINE__)
+
+static int nt36xxx_eng_reset_idle(struct nt36xxx_ts *ts)
+{
+ int ret;
+
+ if(!ts) {
+ dev_err(ts->dev, "%s %s empty", __func__, "nt36xxx_ts");
+ return -EINVAL;
+ }
+
+ if(!ts->mmap) {
+ dev_err(ts->dev, "%s %s empty", __func__, "ts->mmap");
+ return -EINVAL;
+ }
+
+ if(ts->mmap[MMAP_ENG_RST_ADDR] == 0) {
+ dev_err(ts->dev, "%s %s empty", __func__, "MMAP_ENG_RST_ADDR");
+ return -EINVAL;
+ }
+
+ /* HACK to output something without read */
+ ret = regmap_write(ts->regmap, ts->mmap[MMAP_ENG_RST_ADDR],
+ 0x5a);
+ if (ret) {
+ dev_err(ts->dev, "%s regmap write error\n", __func__);
+ return ret;
+ }
+
+ /* Wait until the MCU resets the fw state */
+ usleep_range(15000, 16000);
+
+ /* seemed not long enough */
+ msleep(30);
+ return ret;
+}
+
+/*
+ * nt36xxx_bootloader_reset - Reset MCU to bootloader
+ * @ts: Main driver structure
+ *
+ * Return: Always zero for success, negative number for error
+ */
+static int nt36xxx_bootloader_reset(struct nt36xxx_ts *ts)
+{
+ int ret = 0;
+
+ //in spi version, need to set page to SWRST_N8_ADDR
+ if (ts->mmap[MMAP_SWRST_N8_ADDR]) {
+ ret = regmap_write(ts->regmap, ts->mmap[MMAP_SWRST_N8_ADDR],
+ NT36XXX_CMD_BOOTLOADER_RESET);
+ if (ret)
+ return ret;
+ } else {
+ pr_info("plz make sure MMAP_SWRST_N8_ADDR is set!\n");
+ return -EINVAL;
+ }
+
+ /* MCU has to reboot from bootloader: this is the typical boot time */
+ msleep(35);
+
+ if (ts->mmap[MMAP_SPI_RD_FAST_ADDR]) {
+ ret = regmap_write(ts->regmap, ts->mmap[MMAP_SPI_RD_FAST_ADDR], 0);
+ if (ret)
+ return ret;
+ }
+
+ return ret;
+}
+
+/**
+ * nt36xxx_check_reset_state - Check the boot state during reset
+ * @ts: Main driver structure
+ * @fw_state: Enumeration containing firmware states
+ *
+ * Return: Always zero for success, negative number for error
+ */
+static int nt36xxx_check_reset_state(struct nt36xxx_ts *ts,
+ enum nt36xxx_fw_state fw_state)
+{
+ u8 buf[8] = { 0 };
+ int ret = 0, retry = NT36XXX_MAX_FW_RST_RETRY;
+
+ do {
+ ret = regmap_raw_read(ts->regmap, ts->mmap[MMAP_EVENT_BUF_ADDR]
+ | NT36XXX_EVT_RESET_COMPLETE, buf, 6);
+ if (likely(ret == 0) &&
+ (buf[1] >= fw_state) &&
+ (buf[1] <= NT36XXX_STATE_MAX)) {
+ ret = 0;
+ break;
+ }
+ usleep_range(10000, 11000);
+ } while (--retry);
+
+ if (!retry) {
+ dev_err(ts->dev, "Firmware reset failed.\n");
+ ret = -EBUSY;
+ }
+
+ return ret;
+}
+
+/**
+ * nt36xxx_report - Report touch events
+ * @ts: Main driver structure
+ *
+ * Return: Always zero for success, negative number for error
+ */
+static void nt36xxx_report(struct nt36xxx_ts *ts)
+{
+ struct nt36xxx_abs_object *obj = &ts->abs_obj;
+ struct input_dev *input = ts->input;
+ u8 input_id = 0;
+ u8 point[POINT_DATA_LEN + 1] = { 0 };
+ unsigned int ppos = 0;
+ int i, ret, finger_cnt = 0;
+ uint8_t press_id[TOUCH_MAX_FINGER_NUM] = {0};
+
+ ret = regmap_raw_read(ts->regmap, ts->mmap[MMAP_EVENT_BUF_ADDR],
+ point, sizeof(point));
+ if (ret < 0) {
+ dev_err(ts->dev,
+ "Cannot read touch point data: %d\n", ret);
+ goto xfer_error;
+ }
+
+ /* wdt recovery and esd check */
+ for (i = 0; i < 7; i++) {
+ if ((point[i] != 0xFD) && (point[i] != 0xFE) && (point[i] != 0x77)) {
+ break;
+ }
+
+ mutex_lock(&ts->lock);
+ ts->status |= NT36XXX_STATUS_DOWNLOAD_RECOVER;
+ mutex_unlock(&ts->lock);
+ goto xfer_error;
+ }
+
+ for (i = 0; i < TOUCH_MAX_FINGER_NUM; i++) {
+ ppos = 6 * i + 1;
+ input_id = point[ppos + 0] >> 3;
+
+ if ((input_id == 0) || (input_id > TOUCH_MAX_FINGER_NUM)) {
+ continue;
+ }
+
+ if (((point[ppos] & 0x07) == 0x01) ||
+ ((point[ppos] & 0x07) == 0x02)) {
+ obj->x = (point[ppos + 1] << 4) +
+ (point[ppos + 3] >> 4);
+ obj->y = (point[ppos + 2] << 4) +
+ (point[ppos + 3] & 0xf);
+
+ if ((obj->x > ts->prop.max_x) ||
+ (obj->y > ts->prop.max_y))
+ continue;
+
+ obj->tm = point[ppos + 4];
+ if (obj->tm == 0)
+ obj->tm = 1;
+
+ obj->z = point[ppos + 5];
+ if (i < 2) {
+ obj->z += point[i + 63] << 8;
+ if (obj->z > TOUCH_MAX_PRESSURE)
+ obj->z = TOUCH_MAX_PRESSURE;
+ }
+
+ if (obj->z == 0)
+ obj->z = 1;
+
+ press_id[input_id - 1] = 1;
+
+ input_mt_slot(input, input_id - 1);
+ input_mt_report_slot_state(input,
+ MT_TOOL_FINGER, true);
+ touchscreen_report_pos(input, &ts->prop,
+ obj->x,
+ obj->y, true);
+
+ input_report_abs(input, ABS_MT_TOUCH_MAJOR, obj->tm);
+ input_report_abs(input, ABS_MT_PRESSURE, obj->z);
+
+ finger_cnt++;
+ }
+ }
+
+ input_mt_sync_frame(input);
+
+ input_sync(input);
+
+xfer_error:
+ return;
+}
+
+static irqreturn_t nt36xxx_irq_handler(int irq, void *dev_id)
+{
+ struct nt36xxx_ts *ts = dev_id;
+
+ if (!ts->mmap)
+ goto exit;
+
+ disable_irq_nosync(ts->irq);
+
+ nt36xxx_report(ts);
+
+ enable_irq(ts->irq);
+
+exit:
+ if (ts->status & NT36XXX_STATUS_DOWNLOAD_RECOVER) {
+ mutex_lock(&ts->lock);
+ ts->status &= ~NT36XXX_STATUS_DOWNLOAD_RECOVER;
+ mutex_unlock(&ts->lock);
+ /* TODO: other builtin eeprom model might have another reset
+ * approach other than download, might add here afterward */
+ if (ts->fw_name)
+ schedule_delayed_work(&ts->work, 40000);
+ }
+
+ return IRQ_HANDLED;
+}
+
+
+/**
+ * nt36xxx_chip_version_init - Detect Novatek NT36xxx family IC
+ * @ts: Main driver structure
+ *
+ * This function reads the ChipID from the IC and sets the right
+ * memory map for the detected chip.
+ *
+ * Return: Always zero for success, negative number for error
+ */
+static int nt36xxx_chip_version_init(struct nt36xxx_ts *ts)
+{
+ u8 buf[32] = { 0 };
+ int retry = NT36XXX_MAX_RETRIES;
+ int sz = sizeof(trim_id_table) / sizeof(struct nt36xxx_trim_table);
+ int i, list, mapid, ret;
+
+ ret = nt36xxx_bootloader_reset(ts);
+ if (ret) {
+ dev_err(ts->dev, "Can't reset the nvt IC\n");
+ return ret;
+ }
+
+ do {
+ ret = regmap_raw_read(ts->regmap, ts->mmap[MMAP_MAGIC_NUMBER_0X1F64E_ADDR], buf, 7);
+
+ if (ret)
+ continue;
+
+ dev_dbg(ts->dev, "%s buf[0]=0x%02X, buf[1]=0x%02X, buf[2]=0x%02X, buf[3]=0x%02X, buf[4]=0x%02X, buf[5]=0x%02X, buf[6]=0x%02X sz=%d\n",
+ __func__, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], sz);
+
+ /* Compare read chip id with trim list */
+ for (list = 0; list < sz; list++) {
+
+ /* Compare each not masked byte */
+ for (i = 0; i < NT36XXX_ID_LEN_MAX; i++) {
+ if (trim_id_table[list].mask[i] &&
+ buf[i + 1] != trim_id_table[list].id[i])
+ break;
+ }
+
+ /* found and match with mask */
+ if (i == NT36XXX_ID_LEN_MAX) {
+ mapid = trim_id_table[list].mapid;
+ ret = 0;
+ ts->hw_crc = trim_id_table[list].hw_crc;
+
+ if (mapid == 0) {
+ dev_info(ts->dev, "NVT touch IC hw not found i=%d list=%d\n", i, list);
+ ret = -ENOENT;
+ goto exit;
+ }
+
+ WARN_ON(ts->hw_crc < 1);
+
+ dev_dbg(ts->dev, "hw crc support=%d\n", ts->hw_crc);
+
+ dev_info(ts->dev, "This is NVT touch IC, %06x, mapid %d", *(int*)&buf[4], mapid);
+ return 0;
+ }
+
+ ret = -ENOENT;
+ }
+
+ usleep_range(10000, 11000);
+ } while (--retry);
+
+exit:
+ return ret;
+}
+
+/*
+ * this function is nearly direct copy from vendor source
+*/
+static int32_t nvt_bin_header_parser(struct device *dev, int hw_crc, const u8 *fwdata, size_t fwsize, struct nvt_ts_bin_map **bin_map_ptr, uint8_t *partition_ptr, uint8_t ilm_dlm_num)
+{
+ uint8_t list = 0;
+ uint32_t pos = 0x00;
+ uint32_t end = 0x00;
+ uint8_t info_sec_num = 0;
+ uint8_t ovly_sec_num = 0;
+ uint8_t ovly_info = 0;
+ uint8_t partition;
+ struct nvt_ts_bin_map *bin_map;
+
+ /* Find the header size */
+ end = fwdata[0] + (fwdata[1] << 8) + (fwdata[2] << 16) + (fwdata[3] << 24);
+ pos = 0x30; /* info section start at 0x30 offset */
+ while (pos < end) {
+ info_sec_num ++;
+ pos += 0x10; /* each header info is 16 bytes */
+ }
+
+ /*
+ * Find the DLM OVLY section
+ * [0:3] Overlay Section Number
+ * [4] Overlay Info
+ */
+ ovly_info = (fwdata[0x28] & 0x10) >> 4;
+ ovly_sec_num = (ovly_info) ? (fwdata[0x28] & 0x0F) : 0;
+
+ /*
+ * calculate all partition number
+ * ilm_dlm_num (ILM & DLM) + ovly_sec_num + info_sec_num
+ */
+ *partition_ptr = partition = ilm_dlm_num + ovly_sec_num + info_sec_num;
+ dev_dbg(dev, "ovly_info = %d, ilm_dlm_num = %d, ovly_sec_num = %d, info_sec_num = %d, partition = %d\n",
+ ovly_info, ilm_dlm_num, ovly_sec_num, info_sec_num, partition);
+
+ /* allocated memory for header info */
+ *bin_map_ptr = bin_map = (struct nvt_ts_bin_map *)kzalloc((partition + 1) * sizeof(struct nvt_ts_bin_map), GFP_KERNEL);
+ if(bin_map == NULL) {
+ dev_err(dev, "kzalloc for bin_map failed!\n");
+ return -ENOMEM;
+ }
+
+ for (list = 0; list < partition; list++) {
+ /*
+ * [1] parsing ILM & DLM header info
+ * bin_addr : sram_addr : size (12-bytes)
+ * crc located at 0x18 & 0x1C
+ */
+ if (list < ilm_dlm_num) {
+ memcpy(&bin_map[list].bin_addr, &(fwdata[0 + list*12]), 4);
+ memcpy(&bin_map[list].sram_addr, &(fwdata[4 + list*12]), 4);
+ memcpy(&bin_map[list].size, &(fwdata[8 + list*12]), 4);
+ memcpy(&bin_map[list].crc, &(fwdata[0x18 + list*4]), 4);
+
+ if (!hw_crc) {
+ dev_err(dev, "%s %d sw-crc not support", __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ if (list == 0)
+ sprintf(bin_map[list].name, "ILM");
+ else if (list == 1)
+ sprintf(bin_map[list].name, "DLM");
+ }
+
+ /*
+ * [2] parsing others header info
+ * sram_addr : size : bin_addr : crc (16-bytes)
+ */
+ if ((list >= ilm_dlm_num) && (list < (ilm_dlm_num + info_sec_num))) {
+
+ /* others partition located at 0x30 offset */
+ pos = 0x30 + (0x10 * (list - ilm_dlm_num));
+
+ memcpy(&bin_map[list].sram_addr, &(fwdata[pos]), 4);
+ memcpy(&bin_map[list].size, &(fwdata[pos+4]), 4);
+ memcpy(&bin_map[list].bin_addr, &(fwdata[pos+8]), 4);
+ memcpy(&bin_map[list].crc, &(fwdata[pos+12]), 4);
+
+ if (!hw_crc) {
+ dev_info(dev, "ok, hw_crc not presents!");
+ return -EINVAL;
+ }
+
+ /* detect header end to protect parser function */
+ if ((bin_map[list].bin_addr == 0) && (bin_map[list].size != 0)) {
+ sprintf(bin_map[list].name, "Header");
+ } else {
+ sprintf(bin_map[list].name, "Info-%d", (list - ilm_dlm_num));
+ }
+ }
+
+ /*
+ * [3] parsing overlay section header info
+ * sram_addr : size : bin_addr : crc (16-bytes)
+ */
+ if (list >= (ilm_dlm_num + info_sec_num)) {
+ /* overlay info located at DLM (list = 1) start addr */
+ pos = bin_map[1].bin_addr + (0x10 * (list- ilm_dlm_num - info_sec_num));
+
+ memcpy(&bin_map[list].sram_addr, &(fwdata[pos]), 4);
+ memcpy(&bin_map[list].size, &(fwdata[pos+4]), 4);
+ memcpy(&bin_map[list].bin_addr, &(fwdata[pos+8]), 4);
+ memcpy(&bin_map[list].crc, &(fwdata[pos+12]), 4);
+
+ if (!hw_crc) {
+ dev_err(dev, "%s %d sw_crc not support", __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ sprintf(bin_map[list].name, "Overlay-%d", (list- ilm_dlm_num - info_sec_num));
+ }
+
+ /* BIN size error detect */
+ if ((bin_map[list].bin_addr + bin_map[list].size) > fwsize) {
+ dev_err(dev, "access range (0x%08X to 0x%08X) is larger than bin size!\n",
+ bin_map[list].bin_addr, bin_map[list].bin_addr + bin_map[list].size);
+ return -EINVAL;
+ }
+
+ dev_dbg(dev, "[%d][%s] SRAM (0x%08X), SIZE (0x%08X), BIN (0x%08X), CRC (0x%08X)\n",
+ list, bin_map[list].name,
+ bin_map[list].sram_addr, bin_map[list].size, bin_map[list].bin_addr, bin_map[list].crc);
+ }
+
+ return 0;
+}
+
+static int32_t nt36xxx_download_firmware_hw_crc(struct nt36xxx_ts *ts) {
+ uint32_t list = 0;
+ uint32_t bin_addr, sram_addr, size;
+ struct nvt_ts_bin_map *bin_map = ts->bin_map;
+
+ nt36xxx_bootloader_reset(ts);
+
+ for (list = 0; list < ts->fw_data.partition; list++) {
+ int j;
+
+ /* initialize variable */
+ sram_addr = bin_map[list].sram_addr;
+ size = bin_map[list].size;
+ bin_addr = bin_map[list].bin_addr;
+
+ /* ignore reserved partition (Reserved Partition size is zero) */
+ if (!size) {
+ dev_dbg(ts->dev, "found empty part %d. skipping ", list);
+ continue;
+ } else {
+ size = size + 1;
+ dev_dbg(ts->dev, "found useful part %d. size 0x%x ", list, size);
+ }
+
+ bin_map[list].loaded = 1;
+
+ if (size / NT36XXX_TRANSFER_LEN)
+ dev_dbg(ts->dev, "%s %d paged write [%s] 0x%x, window 0x%x, residue 0x%x",
+ __func__, __LINE__, bin_map[list].name, size,
+ NT36XXX_TRANSFER_LEN, size % NT36XXX_TRANSFER_LEN);
+
+ for (j = 0; j < size; j += NT36XXX_TRANSFER_LEN) {
+ int window_size = ((size - j) / NT36XXX_TRANSFER_LEN) ? NT36XXX_TRANSFER_LEN :
+ ((size - j) % NT36XXX_TRANSFER_LEN);
+
+ regmap_bulk_write(ts->regmap, sram_addr + j, &ts->fw_entry.data[bin_addr + j],
+ window_size);
+ }
+
+ }
+
+ return 0;
+}
+
+static void nt36xxx_release_memory(void *data);
+static int _nt36xxx_boot_prepare_firmware(struct nt36xxx_ts *ts) {
+ int i, ret;
+ size_t fw_need_write_size = 0;
+ const struct firmware *fw_entry;
+ void *data;
+
+ WARN_ON(ts->hw_crc != 2);
+
+ /* add one more guard */
+ if (ts->status & NT36XXX_STATUS_PREPARE_FIRMWARE)
+ return 0;
+
+ /* supposed we need to load once and use many time */
+ if (ts->fw_entry.data)
+ return 0;
+
+ ret = request_firmware(&fw_entry, ts->fw_name, ts->dev);
+ if (ret) {
+ dev_err(ts->dev, "request fw fail name=%s\n", ts->fw_name);
+ return -ENOMEM;
+ }
+
+ /*
+ * must allocate in DMA buffer otherwise fail spi tx DMA
+ * so we need to manage our own fw struct
+ * pm_resume need to re-upload fw for NT36675 IC
+ *
+ */
+ ts->fw_entry.data = data = kmemdup(fw_entry->data, fw_entry->size, GFP_KERNEL | GFP_DMA);
+
+ release_firmware(fw_entry);
+ if (!ts->fw_entry.data) {
+ dev_err(ts->dev, "memdup fw_data fail\n");
+ return -ENOMEM;
+ }
+ ts->fw_entry.size = fw_entry->size;
+
+ WARN_ON(ts->fw_entry.data[0] != fw_entry->data[0]);
+
+ for (i = (ts->fw_entry.size / 4096); i > 0; i--) {
+ if (strncmp(&ts->fw_entry.data[i * 4096 - 3], "NVT", 3) == 0) {
+ fw_need_write_size = i * 4096;
+ break;
+ }
+
+ if (strncmp(&ts->fw_entry.data[i * 4096 - 3], "MOD", 3) == 0) {
+ fw_need_write_size = i * 4096;
+ break;
+ }
+ }
+
+ if (fw_need_write_size == 0) {
+ dev_err(ts->dev, "fw parsing error\n");
+ kfree (data);
+ if (ts->bin_map) {
+ kfree(ts->bin_map);
+ ts->bin_map = NULL;
+ }
+ return -EIO;
+ }
+
+ if (*(ts->fw_entry.data + (fw_need_write_size - 4096)) + *(ts->fw_entry.data +
+ ((fw_need_write_size - 4096) + 1)) != 0xFF) {
+ dev_err(ts->dev, "bin file FW_VER + FW_VER_BAR should be 0xFF!");
+ dev_err(ts->dev, "FW_VER=0x%02X, FW_VER_BAR=0x%02X\n",
+ *(ts->fw_entry.data+(fw_need_write_size - 4096)),
+ *(ts->fw_entry.data+(fw_need_write_size - 4096 + 1)));
+
+ kfree (data);
+ if (ts->bin_map) {
+ kfree(ts->bin_map);
+ ts->bin_map = NULL;
+ }
+ return -EIO;
+ }
+
+ ts->fw_data.ilm_dlm_num = 2;
+
+ ret = nvt_bin_header_parser(ts->dev, ts->hw_crc, ts->fw_entry.data, ts->fw_entry.size,
+ &ts->bin_map, &ts->fw_data.partition, ts->fw_data.ilm_dlm_num);
+ if (ret) {
+ kfree (data);
+ if(ret != -ENOMEM){
+ if (ts->bin_map) {
+ kfree(ts->bin_map);
+ ts->bin_map = NULL;
+ }
+ }
+
+ /* really dont let the tasklet re-enter since no needed for broken fw data */
+ ts->status |= NT36XXX_STATUS_DOWNLOAD_COMPLETE;
+ dev_err(ts->dev, "Parsing fw error, stop re-loading fw now on, ret=0x%x!", ret);
+ return ret;
+ }
+
+ ts->status |= NT36XXX_STATUS_PREPARE_FIRMWARE;
+
+ ret = devm_add_action_or_reset(ts->dev, nt36xxx_release_memory, ts);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int _nt36xxx_boot_download_firmware(struct nt36xxx_ts *ts) {
+ int i, ret, retry = 0;
+ u8 val[8 * 4] = {0};
+
+ if (!(ts->status & NT36XXX_STATUS_PREPARE_FIRMWARE))
+ return -EIO;
+
+ if (ts->hw_crc) {
+ ret = nt36xxx_download_firmware_hw_crc(ts);
+ if (ret) {
+ dev_err(ts->dev, "nt36xxx_download_firmware_hw_crc fail!");
+ return ret;
+ }
+
+ } else {
+ dev_err(ts->dev, "non-hw_crc model is not support yet!");
+ return -EIO;
+ }
+
+ /* set ilm & dlm reg bank */
+ for (i = 0; i < ts->fw_data.partition; i++) {
+ if (0 == strncmp(ts->bin_map[i].name, "ILM", 3)) {
+ regmap_raw_write(ts->regmap, ts->mmap[MMAP_ILM_DES_ADDR], &ts->bin_map[i].sram_addr, 3);
+ regmap_raw_write(ts->regmap, ts->mmap[MMAP_ILM_LENGTH_ADDR], &ts->bin_map[i].size, 3);
+
+ /* crc > 1 then len = 4, crc = 1 then len = 3 */
+ regmap_raw_write(ts->regmap, ts->mmap[MMAP_G_ILM_CHECKSUM_ADDR], &ts->bin_map[i].crc,
+ sizeof(ts->bin_map[i].crc));
+ }
+ if (0 == strncmp(ts->bin_map[i].name, "DLM", 3)) {
+ regmap_raw_write(ts->regmap, ts->mmap[MMAP_DLM_DES_ADDR], &ts->bin_map[i].sram_addr, 3);
+ regmap_raw_write(ts->regmap, ts->mmap[MMAP_DLM_LENGTH_ADDR], &ts->bin_map[i].size, 3);
+
+ /* crc > 1 then len = 4, crc = 1 then len = 3 */
+ regmap_raw_write(ts->regmap, ts->mmap[MMAP_G_DLM_CHECKSUM_ADDR], &ts->bin_map[i].crc,
+ sizeof(ts->bin_map[i].crc));
+ }
+ }
+
+ /* nvt_bld_crc_enable() */
+ /* crc enable */
+ regmap_raw_read(ts->regmap, ts->mmap[MMAP_BLD_CRC_EN_ADDR], val, 1);
+
+ val[0] |= 1 << 7;
+ regmap_raw_write(ts->regmap, ts->mmap[MMAP_BLD_CRC_EN_ADDR], val, 1);
+
+ /* enable fw crc */
+ val[0] = 0;
+ regmap_raw_write(ts->regmap, ts->mmap[MMAP_EVENT_BUF_ADDR] | NT36XXX_EVT_RESET_COMPLETE, val, 1);
+
+ val[0] = 0xae;
+ regmap_raw_write(ts->regmap, ts->mmap[MMAP_EVENT_BUF_ADDR] | NT36XXX_EVT_HOST_CMD, val, 1);
+
+ /* nvt_boot_ready() */
+ /* Set Boot Ready Bit */
+ val[0] = 0x1;
+ regmap_raw_write(ts->regmap, ts->mmap[MMAP_BOOT_RDY_ADDR], val, 1);
+
+ /* old logic 5ms, retention to 10ms */
+ usleep_range(10000, 11000);
+
+ /* nvt_check_fw_reset_state() */
+ ret = nt36xxx_check_reset_state(ts, NT36XXX_STATE_INIT);
+ if (ret)
+ return ret;
+
+check_fw:
+ /* nvt_get_fw_info() */
+ ret = regmap_raw_read(ts->regmap, ts->mmap[MMAP_EVENT_BUF_ADDR] | NT36XXX_EVT_FWINFO, val, 16);
+ if (ret)
+ return ret;
+
+ dev_dbg(ts->dev, "Get default fw_ver=%d, max_x=%d, max_y=%d, by default max_x=%d max_y=%d\n",
+ val[2], ts->prop.max_x, ts->prop.max_y, ts->data->max_x, ts->data->max_y);
+
+ if (val[0] != 0xff && retry < 5) {
+ dev_err(ts->dev, "FW info is broken! fw_ver=0x%02X, ~fw_ver=0x%02X\n", val[1], val[2]);
+ retry++;
+ goto check_fw;
+ }
+
+ dev_info(ts->dev, "Touch IC fw loaded ok");
+
+ ts->status |= NT36XXX_STATUS_DOWNLOAD_COMPLETE;
+
+ return 0;
+}
+
+static void nt36xxx_download_firmware(struct work_struct *work) {
+ struct nt36xxx_ts *ts = container_of(work, struct nt36xxx_ts, work.work);
+ int ret;
+
+ cancel_delayed_work(&ts->work);
+
+ mutex_lock(&ts->lock);
+ _nt36xxx_boot_prepare_firmware(ts);
+ mutex_unlock(&ts->lock);
+
+ if (!(ts->status & NT36XXX_STATUS_PREPARE_FIRMWARE))
+ goto exit;
+
+ /* so the pm resume might have code to enable regulators. */
+ ret = pm_runtime_resume_and_get(ts->dev);
+ if (ret) {
+ dev_err(ts->dev, "%s resume fail 0x%x", __func__, ret);
+ goto exit;
+ }
+
+ disable_irq_nosync(ts->irq);
+
+ mutex_lock(&ts->lock);
+
+ ret = nt36xxx_eng_reset_idle(ts);
+ if (ret) {
+ dev_err(ts->dev, "Failed to check chip version\n");
+ goto unlock;
+ }
+
+ /* Set memory maps for the specific chip version */
+ ret = nt36xxx_chip_version_init(ts);
+ if (ret) {
+ dev_err(ts->dev, "Failed to check chip version\n");
+ goto unlock;
+ }
+
+ dev_dbg(ts->dev, "ts->status=0x%x", ts->status);
+
+ _nt36xxx_boot_download_firmware(ts);
+unlock:
+ mutex_unlock(&ts->lock);
+ enable_irq(ts->irq);
+
+ pm_runtime_put(ts->dev);
+exit:
+ if (!(ts->status & NT36XXX_STATUS_DOWNLOAD_COMPLETE)) {
+ schedule_delayed_work(&ts->work, 4000);
+ }
+}
+
+static void nt36xxx_release_memory(void *data)
+{
+ struct nt36xxx_ts *ts = data;
+ kfree(ts->bin_map);
+ kfree(ts->fw_entry.data);
+}
+
+static void nt36xxx_disable_regulators(void *data)
+{
+ struct nt36xxx_ts *ts = data;
+
+ regulator_bulk_disable(NT36XXX_NUM_SUPPLIES, ts->supplies);
+}
+
+static int nt36xxx_input_dev_config(struct nt36xxx_ts *ts, const struct input_id *id)
+{
+ struct device *dev = ts->dev;
+ int ret;
+
+ ts->input = devm_input_allocate_device(dev);
+ if (!ts->input)
+ return -ENOMEM;
+
+ input_set_drvdata(ts->input, ts);
+
+ ts->input->phys = devm_kasprintf(dev, GFP_KERNEL,
+ "%s/input0", dev_name(dev));
+ if (!ts->input->phys)
+ return -ENOMEM;
+
+ ts->input->name = "nt36xxx_spi_0";
+ ts->input->dev.parent = dev;
+ ts->input->id = *id;
+
+ input_set_abs_params(ts->input, ABS_MT_PRESSURE, 0,
+ TOUCH_MAX_PRESSURE, 0, 0);
+ input_set_abs_params(ts->input, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
+
+ input_set_abs_params(ts->input, ABS_MT_POSITION_X, 0,
+ ts->data->abs_x_max - 1, 0, 0);
+ input_set_abs_params(ts->input, ABS_MT_POSITION_Y, 0,
+ ts->data->abs_y_max - 1, 0, 0);
+
+ touchscreen_parse_properties(ts->input, true, &ts->prop);
+
+ WARN_ON(ts->prop.max_x < 1);
+
+ ret = input_mt_init_slots(ts->input, TOUCH_MAX_FINGER_NUM,
+ INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
+ if (ret) {
+ dev_err(dev, "Cannot init MT slots (%d)\n", ret);
+ return ret;
+ }
+
+ ret = input_register_device(ts->input);
+ if (ret) {
+ dev_err(dev, "Failed to register input device: %d\n",
+ ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int nt36xxx_of_compatible(struct device *dev)
+{
+ struct device_node *np = dev->of_node;
+
+ if (!of_device_is_compatible(np, "novatek,NVT-default-spi")) {
+ const char *path = "/chosen";
+ struct device_node *dt_node;
+ const char *bootargs;
+
+ dt_node = of_find_node_by_path(path);
+ if (!dt_node) {
+ dev_err(dev, "Failed to find device-tree node: %s\n", path);
+ return -ENODEV;
+ }
+
+ if (!of_property_read_string(dt_node, "bootargs", &bootargs))
+ if (!strstr(bootargs, "tianma") && !strstr(bootargs, "nt36"))
+ return -ENODEV;
+
+ dev_info(dev, "Try to probe novatek/tianma panel as specified in chosen/bootargs.");
+ }
+ return 0;
+}
+
+int nt36xxx_probe(struct device *dev, int irq, const struct input_id *id,
+ struct regmap *regmap)
+{
+ const struct nt36xxx_chip_data *chip_data;
+ const char *signed_fwname = NULL;
+ int ret;
+
+ struct nt36xxx_ts *ts = devm_kzalloc(dev, sizeof(struct nt36xxx_ts), GFP_KERNEL);
+ if (!ts)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, ts);
+
+ chip_data = of_device_get_match_data(dev);
+ if(!chip_data)
+ return -EINVAL;
+
+ ts->dev = dev;
+ ts->regmap = regmap;
+ ts->irq = irq;
+
+ ts->data = chip_data;
+ memcpy(ts->mmap_data, chip_data->mmap, sizeof(ts->mmap_data));
+ ts->mmap = ts->mmap_data;
+
+ ts->supplies = devm_kcalloc(dev, NT36XXX_NUM_SUPPLIES,
+ sizeof(*ts->supplies), GFP_KERNEL);
+ if (!ts->supplies)
+ return -ENOMEM;
+
+ ts->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(ts->reset_gpio))
+ return PTR_ERR(ts->reset_gpio);
+
+ gpiod_set_consumer_name(ts->reset_gpio, "nt36xxx_reset");
+
+ ts->irq_gpio = devm_gpiod_get_optional(dev, "irq", GPIOD_IN);
+ if (IS_ERR(ts->irq_gpio))
+ return PTR_ERR(ts->irq_gpio);
+
+ if (irq <= 0) {
+ ts->irq = gpiod_to_irq(ts->irq_gpio);
+ if (ts->irq <=0) {
+ dev_err(dev, "either need irq or irq-gpio specified in devicetree node!\n");
+ return -EINVAL;
+ }
+
+ dev_info(ts->dev, "irq %d", ts->irq);
+ }
+
+ gpiod_set_consumer_name(ts->irq_gpio, "nt36xxx_irq");
+
+ if (drm_is_panel_follower(dev))
+ goto skip_regulators;
+
+ /* These supplies are optional, also shared with LCD panel */
+ ts->supplies[0].supply = "vdd";
+ ts->supplies[1].supply = "vio";
+ ts->supplies[2].supply = "vio2";
+ ts->supplies[3].supply = "vio3";
+ ret = devm_regulator_bulk_get(dev,
+ NT36XXX_NUM_SUPPLIES,
+ ts->supplies);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Cannot get supplies: %d\n", ret);
+
+ ret = regulator_bulk_enable(NT36XXX_NUM_SUPPLIES, ts->supplies);
+ if (ret)
+ return ret;
+
+ usleep_range(10000, 11000);
+
+ ret = devm_add_action_or_reset(dev, nt36xxx_disable_regulators, ts);
+ if (ret)
+ return ret;
+
+skip_regulators:
+ mutex_init(&ts->lock);
+
+ ret = nt36xxx_eng_reset_idle(ts);
+ if (ret) {
+ dev_err(dev, "Failed to check chip version\n");
+ return ret;
+ }
+
+ /* Set memory maps for the specific chip version */
+ ret = nt36xxx_chip_version_init(ts);
+ if (ret) {
+ dev_err(dev, "Failed to check chip version\n");
+ return ret;
+ }
+
+ ret = nt36xxx_of_compatible(dev);
+ if (ret) {
+ return ret;
+ }
+
+ /* copy the const mmap into drvdata */
+ memcpy(ts->mmap_data, ts->data->mmap, sizeof(ts->mmap_data));
+ ts->mmap = ts->mmap_data;
+
+ ret = nt36xxx_input_dev_config(ts, ts->data->id);
+ if (ret) {
+ dev_err(dev, "failed set input device: %d\n", ret);
+ return ret;
+ }
+
+ ret = devm_request_threaded_irq(dev, ts->irq, NULL, nt36xxx_irq_handler,
+ IRQ_TYPE_EDGE_RISING | IRQF_ONESHOT, dev_name(dev), ts);
+ if (ret) {
+ dev_err(dev, "request irq failed: %d\n", ret);
+ return ret;
+ }
+
+ /* init with default name */
+ ts->fw_name = ts->data->fw_name;
+ /* support overriding fw name */
+ of_property_read_string_index(ts->dev->of_node, "firmware-name", 0, &signed_fwname);
+ if (signed_fwname)
+ ts->fw_name = signed_fwname;
+
+ if (drm_is_panel_follower(dev)) {
+ ts->panel_follower.funcs = &nt36xxx_panel_follower_funcs;
+ devm_drm_panel_add_follower(dev, &ts->panel_follower);
+ }
+
+ pm_runtime_enable(dev);
+
+ /* have to make sure this is first time schedule work, if devm_drm_panel_add_follower
+ * called into internal resume with schedule_delay_work, then block it over there */
+ if (ts->fw_name) {
+ ts->status |= NT36XXX_STATUS_NEED_FIRMWARE;
+
+ /* make the driver sleep while waiting tasklet fw download */
+ pm_runtime_suspend(dev);
+
+ devm_delayed_work_autocancel(dev, &ts->work, nt36xxx_download_firmware);
+ schedule_delayed_work(&ts->work, 0);
+ }
+
+ dev_info(dev, "probe ok!");
+ return 0;
+}
+
+EXPORT_SYMBOL_GPL(nt36xxx_probe);
+
+static int __maybe_unused nt36xxx_internal_pm_suspend(struct device *dev)
+{
+ struct nt36xxx_ts *ts = dev_get_drvdata(dev);
+ int ret = 0;
+
+ mutex_lock(&ts->lock);
+ ts->status |= NT36XXX_STATUS_SUSPEND;
+ mutex_unlock(&ts->lock);
+
+ cancel_delayed_work_sync(&ts->work);
+
+ /* adding the mutex is to protect concurrent with download_task */
+ mutex_lock(&ts->lock);
+ if (ts->mmap[MMAP_EVENT_BUF_ADDR]) {
+ ret = regmap_write(ts->regmap, ts->mmap[MMAP_EVENT_BUF_ADDR], NT36XXX_CMD_ENTER_SLEEP);
+ }
+
+ if (ret)
+ dev_err(ts->dev, "Cannot enter suspend!\n");
+ mutex_unlock(&ts->lock);
+
+ return 0;
+}
+
+static int __maybe_unused nt36xxx_pm_suspend(struct device *dev)
+{
+ struct nt36xxx_ts *ts = dev_get_drvdata(dev);
+ int ret=0;
+
+ if (drm_is_panel_follower(dev))
+ return 0;
+
+ disable_irq_nosync(ts->irq);
+
+ regulator_bulk_disable(NT36XXX_NUM_SUPPLIES, ts->supplies);
+
+ ret = nt36xxx_internal_pm_suspend(dev);
+ return ret;
+}
+
+static int __maybe_unused nt36xxx_internal_pm_resume(struct device *dev)
+{
+ struct nt36xxx_ts *ts = dev_get_drvdata(dev);
+
+ mutex_lock(&ts->lock);
+ if(ts->status & (NT36XXX_STATUS_SUSPEND | NT36XXX_STATUS_DOWNLOAD_COMPLETE))
+ ts->status &= ~(NT36XXX_STATUS_SUSPEND | NT36XXX_STATUS_DOWNLOAD_COMPLETE);
+ mutex_unlock(&ts->lock);
+
+ if (ts->status & NT36XXX_STATUS_NEED_FIRMWARE)
+ schedule_delayed_work(&ts->work, 0);
+
+ return 0;
+}
+
+static int __maybe_unused nt36xxx_pm_resume(struct device *dev)
+{
+ struct nt36xxx_ts *ts = dev_get_drvdata(dev);
+ int ret=0;
+
+ if (drm_is_panel_follower(dev))
+ return 0;
+
+ enable_irq(ts->irq);
+
+ ret = regulator_bulk_enable(NT36XXX_NUM_SUPPLIES, ts->supplies);
+
+ ret = nt36xxx_internal_pm_resume(dev);
+ return ret;
+}
+
+EXPORT_GPL_SIMPLE_DEV_PM_OPS(nt36xxx_pm_ops,
+ nt36xxx_pm_suspend,
+ nt36xxx_pm_resume);
+
+static int panel_prepared(struct drm_panel_follower *follower)
+{
+ struct nt36xxx_ts *ts = container_of(follower, struct nt36xxx_ts, panel_follower);
+
+ if (ts->status & NT36XXX_STATUS_SUSPEND)
+ enable_irq(ts->irq);
+
+ /* supposed to clear the flag here, but leave to internal_pm_resume
+ * for greater purpose, then clear flag as:
+ * ts->status &= ~NT36XXX_STATUS_SUSPEND;
+ */
+ return nt36xxx_internal_pm_resume(ts->dev);
+}
+
+static int panel_unpreparing(struct drm_panel_follower *follower)
+{
+ struct nt36xxx_ts *ts = container_of(follower, struct nt36xxx_ts, panel_follower);
+
+ mutex_lock(&ts->lock);
+ ts->status |= NT36XXX_STATUS_SUSPEND;
+ mutex_unlock(&ts->lock);
+
+ disable_irq_nosync(ts->irq);
+
+ return nt36xxx_internal_pm_suspend(ts->dev);
+}
+
+static struct drm_panel_follower_funcs nt36xxx_panel_follower_funcs = {
+ .panel_prepared = panel_prepared,
+ .panel_unpreparing = panel_unpreparing,
+};
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("NT36XXX Touchscreen driver");
+MODULE_AUTHOR("AngeloGioacchino Del Regno <kholk11@gmail.com>");
+MODULE_AUTHOR("George Chan <gchan9527@gmail.com>");
diff --git a/drivers/input/touchscreen/nt36xxx_spi.c b/drivers/input/touchscreen/nt36xxx_spi.c
new file mode 100644
index 0000000000..21d2a4c79c
--- /dev/null
+++ b/drivers/input/touchscreen/nt36xxx_spi.c
@@ -0,0 +1,256 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * NT36XXX SPI Touchscreen Driver
+ *
+ * Copyright (C) 2020 - 2021 Goodix, Inc.
+ * Copyright (C) 2023 Linaro Ltd.
+ * Copyright (C) 2023-2024 George Chan <gchan9527@gmail.com>
+ *
+ * Based on goodix_ts_berlin driver.
+ */
+#include <linux/input.h>
+#include <linux/input/touchscreen.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+#include <linux/unaligned.h>
+
+#include "nt36xxx.h"
+
+#define SPI_READ_PREFIX_LEN 1
+#define SPI_WRITE_PREFIX_LEN 1
+
+#define DEBUG 0
+
+/*
+ * there are two kinds of spi read/write:
+ * (a)spi_read()/spi_write()/spi_write_then_read(),
+ * (b)and the spi_sync itself.
+ *
+ * we have to choose one and stick together, cross-use otherwise caused problem.
+ * the addressing mode is | 0xff 0xXX 0xYY | 0xZ1 ... data1...| 0xZ2 ...data2... | ...
+ * 0xXX is bit[23..16]
+ * 0xYY is bit[15..7]
+ * above describe a 'page select' ops
+ * 0xZ1 is bit[7..0], addr for read ops
+ * 0xZ2 is bit[7..0] | 0x80, addr for write ops
+ * there is no restriction on the read write order.
+*/
+static int nt36xxx_spi_write(void *dev, const void *data,
+ size_t len)
+{
+ struct spi_device *spi = to_spi_device((struct device *)dev);
+ int32_t ret;
+
+ void *data1 = kmemdup(data, len, GFP_KERNEL|GFP_DMA);
+ if (!data1)
+ return -ENOMEM;
+
+ u8 addr[4] = { 0xff, *(u32 *)data >> 15, *(u32 *)data >> 7, (*(u32 *)data & 0x7f) | 0x80};
+ memcpy(data1, addr, 4);
+
+ dev_dbg(dev, "%s len=0x%lx", __func__, len);
+
+ spi_write(spi, data1, 3);
+ ret = spi_write(spi, data1 + 3, len - 3);
+ if (ret)
+ dev_err(dev, "transfer err %d\n ", ret);
+ else if (DEBUG) {
+
+ print_hex_dump(KERN_INFO, __func__, DUMP_PREFIX_OFFSET,
+ 16, 1, data, 3, true);
+
+ print_hex_dump(KERN_INFO, __func__, DUMP_PREFIX_OFFSET,
+ 16, 1, data + 3, (len - 3) > 0x20 ? 0x20 : len - 3 , true);
+ }
+
+ kfree(data1);
+ return ret;
+}
+
+static int nt36xxx_spi_read(void *dev, const void *reg_buf,
+ size_t reg_size, void *val_buf,
+ size_t val_size)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ int ret;
+ u8 addr[4] = { 0xff, *(u32 *)reg_buf >> 15, *(u32 *)reg_buf >> 7, *(u32 *)reg_buf & 0x7f };
+
+ ret = spi_write(spi, addr, 3);
+ if (ret) {
+ dev_err(dev, "transfer0 err %s %d ret=%d", __func__, __LINE__, ret);
+ return ret;
+ }
+
+ ret = spi_write_then_read(spi, &addr[3] , 1, val_buf, val_size);
+ if (ret) {
+ dev_err(dev, "transfer1 err %s %d ret=%d", __func__, __LINE__, ret);
+ return ret;
+ }
+
+ if (DEBUG) {
+ print_hex_dump(KERN_INFO, __func__, DUMP_PREFIX_OFFSET,
+ 16, 1, addr, 3, true);
+
+ print_hex_dump(KERN_INFO, __func__, DUMP_PREFIX_OFFSET,
+ 16, 1, addr, (val_size) > 0x20 ? 0x20 : val_size % 0x20 , true);
+
+ print_hex_dump(KERN_INFO, __func__, DUMP_PREFIX_OFFSET,
+ 16, 1, val_buf, (val_size > 0x20) ? 0x20 : val_size % 0x20 , true);
+ }
+
+ return ret;
+}
+
+const struct regmap_config nt36xxx_regmap_config_32bit = {
+ .name = "nt36xxx_hw",
+ .reg_bits = 32,
+ .val_bits = 8,
+ .read = nt36xxx_spi_read,
+ .write = nt36xxx_spi_write,
+
+ .max_raw_read = NT36XXX_TRANSFER_LEN + 8,
+ .max_raw_write = NT36XXX_TRANSFER_LEN + 8,
+
+ .zero_flag_mask = true, /* this is needed to make sure addr is not write_masked */
+ .cache_type = REGCACHE_NONE,
+};
+
+static const struct input_id nt36xxx_spi_input_id = {
+ .bustype = BUS_SPI,
+};
+
+static int nt36xxx_spi_probe(struct spi_device *spi)
+{
+ struct regmap_config *regmap_config;
+ struct regmap *regmap;
+ size_t max_size;
+ int ret = 0;
+
+ dev_dbg(&spi->dev, "%s %d", __func__, __LINE__);
+
+ regmap_config = devm_kmemdup(&spi->dev, &nt36xxx_regmap_config_32bit,
+ sizeof(*regmap_config), GFP_KERNEL);
+ if (!regmap_config) {
+ dev_err(&spi->dev, "memdup regmap_config fail\n");
+ return -ENOMEM;
+ }
+
+ spi->mode = SPI_MODE_0;
+ spi->bits_per_word = 8;
+ ret = spi_setup(spi);
+ if (ret) {
+ dev_err(&spi->dev, "SPI setup error %d\n", ret);
+ return ret;
+ }
+
+ /* don't exceed max specified SPI CLK frequency */
+ if (spi->max_speed_hz > MAX_SPI_FREQ_HZ) {
+ dev_err(&spi->dev, "SPI CLK %d Hz?\n", spi->max_speed_hz);
+ return -EINVAL;
+ }
+
+ max_size = spi_max_transfer_size(spi);
+ regmap_config->max_raw_read = max_size - SPI_READ_PREFIX_LEN;
+ regmap_config->max_raw_write = max_size - SPI_WRITE_PREFIX_LEN;
+
+ regmap = devm_regmap_init(&spi->dev, NULL, spi, regmap_config);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return nt36xxx_probe(&spi->dev, spi->irq,
+ &nt36xxx_spi_input_id, regmap);
+}
+
+const struct nt36xxx_chip_data default_config = {
+ .config = &nt36xxx_regmap_config_32bit,
+ .mmap = nt36676f_memory_maps,
+ .max_x = 1080,
+ .max_y = 2400,
+ .abs_x_max = 1080,
+ .abs_y_max = 2400,
+ .id = &nt36xxx_spi_input_id,
+};
+
+const struct nt36xxx_chip_data miatoll_tianma_nt36675 = {
+ .config = &nt36xxx_regmap_config_32bit,
+ .mmap = nt36675_memory_maps,
+ .fw_name = "novatek_ts_tianma_fw.bin",
+ .max_x = 1080,
+ .max_y = 2400,
+ .abs_x_max = 1080,
+ .abs_y_max = 2400,
+ .id = &nt36xxx_spi_input_id,
+};
+
+const struct nt36xxx_chip_data generic_nt36676f = {
+ .config = &nt36xxx_regmap_config_32bit,
+ .mmap = nt36676f_memory_maps,
+ .max_x = 1080,
+ .max_y = 2400,
+ .abs_x_max = 1080,
+ .abs_y_max = 2400,
+ .id = &nt36xxx_spi_input_id,
+};
+
+const struct nt36xxx_chip_data generic_nt36772 = {
+ .config = &nt36xxx_regmap_config_32bit,
+ .mmap = nt36772_memory_maps,
+ .max_x = 1080,
+ .max_y = 2400,
+ .abs_x_max = 1080,
+ .abs_y_max = 2400,
+ .id = &nt36xxx_spi_input_id,
+};
+
+const struct nt36xxx_chip_data generic_nt36525 = {
+ .config = &nt36xxx_regmap_config_32bit,
+ .mmap = nt36525_memory_maps,
+ .max_x = 1080,
+ .max_y = 2400,
+ .abs_x_max = 1080,
+ .abs_y_max = 2400,
+ .id = &nt36xxx_spi_input_id,
+};
+
+static const struct spi_device_id nt36xxx_spi_ids[] = {
+ { "nt36675-spi", 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(spi, nt36xxx_spi_ids);
+
+static const struct of_device_id nt36xxx_spi_of_match[] = {
+ { .compatible = "novatek,nt36675-spi", .data = &miatoll_tianma_nt36675, },
+ { .compatible = "novatek,nt36672a-spi", .data = &miatoll_tianma_nt36675, },
+ { .compatible = "novatek,nt36676f-spi", .data = &generic_nt36676f, },
+ { .compatible = "novatek,nt36772-spi", .data = &generic_nt36772, },
+ { .compatible = "novatek,nt36525-spi", .data = &generic_nt36525, },
+ /*
+ * this is served for two special purpose.
+ * (1) detect/display model only, and bail out in the end
+ * (2) checking device varients, mixed use of novatek and focaltech spi ic
+ * TODO: might add auto select mmap for unknown nvt device.
+ */
+ { .compatible = "novatek,NVT-default-spi", .data = &default_config, },
+ { }
+};
+MODULE_DEVICE_TABLE(of, nt36xxx_spi_of_match);
+
+static struct spi_driver nt36xxx_spi_driver = {
+ .driver = {
+ .name = "nt36675-spi",
+ .of_match_table = nt36xxx_spi_of_match,
+ .pm = pm_sleep_ptr(&nt36xxx_pm_ops),
+ },
+ .probe = nt36xxx_spi_probe,
+ .id_table = nt36xxx_spi_ids,
+};
+module_spi_driver(nt36xxx_spi_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("NT36XXX SPI Touchscreen driver");
+MODULE_AUTHOR("Neil Armstrong <neil.armstrong@linaro.org>");
+MODULE_AUTHOR("George Chan <gchan9527@gmail.com>");
--
2.43.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH RFC/RFT 3/3] dts: sm7125-xiaomi-joyeuse: Sample device tree for reference
2024-10-15 12:53 [PATCH RFC/RFT 0/3] Add support to Novatek's touch IC nt36xxx George Chan via B4 Relay
2024-10-15 12:53 ` [PATCH RFC/RFT 1/3] dt-bindings: touchscreen: Add binding for Novatek NT36xxx series driver George Chan via B4 Relay
2024-10-15 12:53 ` [PATCH RFC/RFT 2/3] Input: Add Novatek NT36xxx touchscreen driver George Chan via B4 Relay
@ 2024-10-15 12:53 ` George Chan via B4 Relay
2024-10-15 14:17 ` [PATCH RFC/RFT 0/3] Add support to Novatek's touch IC nt36xxx Rob Herring (Arm)
3 siblings, 0 replies; 7+ messages in thread
From: George Chan via B4 Relay @ 2024-10-15 12:53 UTC (permalink / raw)
To: Dmitry Torokhov, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Del Regno, Henrik Rydberg
Cc: linux-input, devicetree, linux-kernel, George Chan
From: George Chan <gchan9527@gmail.com>
Provide a include-made-easy devicetree file for demo.
This sample file aimed including novatek touch support.
Reviewers please ignore this patch.
The full device tree is at below:
https://github.com/99degree/linux/tree/working-20241015/arch/arm64/boot/dts/qcom
Signed-off-by: George Chan <gchan9527@gmail.com>
---
arch/arm64/boot/dts/qcom/Makefile | 1 +
.../boot/dts/qcom/sm7125-xiaomi-joyeuse-touch.dts | 183 +++++++++++++++++++++
2 files changed, 184 insertions(+)
diff --git a/arch/arm64/boot/dts/qcom/Makefile b/arch/arm64/boot/dts/qcom/Makefile
index aea1d69db5..ba9786555b 100644
--- a/arch/arm64/boot/dts/qcom/Makefile
+++ b/arch/arm64/boot/dts/qcom/Makefile
@@ -235,6 +235,7 @@ dtb-$(CONFIG_ARCH_QCOM) += sm6350-sony-xperia-lena-pdx213.dtb
dtb-$(CONFIG_ARCH_QCOM) += sm6375-sony-xperia-murray-pdx225.dtb
dtb-$(CONFIG_ARCH_QCOM) += sm7125-xiaomi-curtana.dtb
dtb-$(CONFIG_ARCH_QCOM) += sm7125-xiaomi-joyeuse.dtb
+dtb-$(CONFIG_ARCH_QCOM) += sm7125-xiaomi-joyeuse-touch.dtb
dtb-$(CONFIG_ARCH_QCOM) += sm7225-fairphone-fp4.dtb
dtb-$(CONFIG_ARCH_QCOM) += sm8150-hdk.dtb
dtb-$(CONFIG_ARCH_QCOM) += sm8150-microsoft-surface-duo.dtb
diff --git a/arch/arm64/boot/dts/qcom/sm7125-xiaomi-joyeuse-touch.dts b/arch/arm64/boot/dts/qcom/sm7125-xiaomi-joyeuse-touch.dts
new file mode 100644
index 0000000000..4a43db701d
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sm7125-xiaomi-joyeuse-touch.dts
@@ -0,0 +1,183 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+
+#ifndef SM7125_XIAOMI_JOYEUSE_TOUCH_DTS
+#define SM7125_XIAOMI_JOYEUSE_TOUCH_DTS
+
+#include <dt-bindings/dma/qcom-gpi.h>
+#include <dt-bindings/gpio/gpio.h>
+
+#include "sm7125-xiaomi-joyeuse-display.dts"
+
+&soc {
+ gpi_dma0: dma-controller@800000 {
+ compatible = "qcom,sm7125-gpi-dma", "qcom,sm6350-gpi-dma";
+ reg = <0 0x00800000 0 0x60000>;
+ interrupts = <GIC_SPI 244 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 245 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 246 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 247 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 248 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 249 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 250 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 251 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 252 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 253 IRQ_TYPE_LEVEL_HIGH>;
+ dma-channels = <10>;
+ dma-channel-mask = <0x1f>;
+ iommus = <&apps_smmu 0x56 0x0>;
+ #dma-cells = <3>;
+
+ status = "disabled";
+ };
+
+ gpi_dma1: dma-controller@a00000 {
+ compatible = "qcom,sm7125-gpi-dma", "qcom,sm6350-gpi-dma";
+ reg = <0 0x00a00000 0 0x60000>;
+ interrupts = <GIC_SPI 645 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 646 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 647 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 648 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 649 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 650 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 651 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 652 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 653 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 654 IRQ_TYPE_LEVEL_HIGH>;
+ dma-channels = <10>;
+ dma-channel-mask = <0x3f>;
+ iommus = <&apps_smmu 0x4d6 0x0>;
+ #dma-cells = <3>;
+
+ status = "disabled";
+ };
+};
+
+//spi@880000
+&spi0 {
+ dmas = <&gpi_dma0 0 0 QCOM_GPI_SPI>,
+ <&gpi_dma0 1 0 QCOM_GPI_SPI>;
+ dma-names = "tx", "rx";
+};
+
+//spi@884000
+&spi1 {
+ dmas = <&gpi_dma0 0 1 QCOM_GPI_SPI>,
+ <&gpi_dma0 1 1 QCOM_GPI_SPI>;
+ dma-names = "tx", "rx";
+};
+//spi@88c000
+&spi3 {
+ dmas = <&gpi_dma0 0 3 QCOM_GPI_SPI>,
+ <&gpi_dma0 1 3 QCOM_GPI_SPI>;
+ dma-names = "tx", "rx";
+};
+
+//spi@88c000
+&spi3 {
+ dmas = <&gpi_dma0 0 3 QCOM_GPI_SPI>,
+ <&gpi_dma0 1 3 QCOM_GPI_SPI>;
+ dma-names = "tx", "rx";
+};
+
+//spi@894000
+&spi5 {
+ dmas = <&gpi_dma0 0 5 QCOM_GPI_SPI>,
+ <&gpi_dma0 1 5 QCOM_GPI_SPI>;
+ dma-names = "tx", "rx";
+};
+
+//spi@a80000
+&spi6 {
+ dmas = <&gpi_dma1 0 0 QCOM_GPI_SPI>,
+ <&gpi_dma1 1 0 QCOM_GPI_SPI>;
+ dma-names = "tx", "rx";
+};
+
+//spi@a88000
+&spi8 {
+ dmas = <&gpi_dma1 0 2 QCOM_GPI_SPI>,
+ <&gpi_dma1 1 2 QCOM_GPI_SPI>;
+ dma-names = "tx", "rx";
+};
+
+//spi@a90000
+&spi10 {
+ dma-names = "tx", "rx";
+ dmas = <&gpi_dma1 0 4 QCOM_GPI_SPI>,
+ <&gpi_dma1 1 4 QCOM_GPI_SPI>;
+};
+
+//spi@a94000
+&spi11 {
+ dma-names = "tx", "rx";
+ dmas = <&gpi_dma1 0 5 QCOM_GPI_SPI>,
+ <&gpi_dma1 1 5 QCOM_GPI_SPI>;
+};
+
+&spi11 {
+ status = "okay";
+
+ touchscreen: touchscreen@0 {
+ compatible = "novatek,nt36675-spi",
+ "novatek,nt36xxx-spi",
+ "novatek,NVT-ts-spi";
+
+ reg = <0>;
+
+ /* caught from joyeuse dtb*/
+ spi-max-frequency = <4000000>;
+
+ /* ts->irq report 194 */
+ /* interrupts = <&tlmm 194 IRQ_TYPE_EDGE_FALLING>; */
+ /* interrupt= <&tlmm 13 2>; */ //dtb specified, but GPIO13 is CAM_MCLK0
+
+ novatek,reset-gpio = <&tlmm 8 0x00>;
+ novatek,irq-gpio = <&tlmm 9 0x2001>;
+
+ /* 672C */
+ novatek,swrst-n8-addr = <0x03F0FE>;
+ novatek,spi-rd-fast-addr = <0x03F310>;
+
+ reset-gpio = <&tlmm 8 0x00>;
+ /* dtb show <&tlmm 13 2>*/
+ irq-gpio = <&tlmm 9 0x2001>;
+
+/*
+ touch_ibb-supply = <0x241>; //lcdb_ncp
+ touch_lab-supply = <0x240>; //qcom,qpnp-lcdb-regulator ldo
+ touch_vddio-supply = <0x33c>; //pm6150_l18
+*/
+
+ vio-supply = <&vreg_l18a_3p0>;
+ vdd-supply = <&vreg_l18a_3p0>;
+
+ panel = <&panel0>;
+ status = "okay";
+ };
+};
+
+
+&qup_spi11_spi {
+ drive-strength = <2>;
+ //bias-disable;
+};
+
+&qup_spi11_cs {
+ drive-strength = <2>;
+ //bias-disable;
+};
+
+&qup_spi11_cs_gpio {
+ drive-strength = <2>;
+ bias-disable;
+};
+
+&gpi_dma0 {
+ status = "okay";
+};
+
+&gpi_dma1 {
+ status = "okay";
+};
+
+#endif
--
2.43.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH RFC/RFT 1/3] dt-bindings: touchscreen: Add binding for Novatek NT36xxx series driver
2024-10-15 12:53 ` [PATCH RFC/RFT 1/3] dt-bindings: touchscreen: Add binding for Novatek NT36xxx series driver George Chan via B4 Relay
@ 2024-10-15 13:39 ` Krzysztof Kozlowski
2024-10-15 14:27 ` Rob Herring (Arm)
1 sibling, 0 replies; 7+ messages in thread
From: Krzysztof Kozlowski @ 2024-10-15 13:39 UTC (permalink / raw)
To: gchan9527, Dmitry Torokhov, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Del Regno, Henrik Rydberg
Cc: linux-input, devicetree, linux-kernel
On 15/10/2024 14:53, George Chan via B4 Relay wrote:
> From: George Chan <gchan9527@gmail.com>
>
> Add binding for the Novatek NT36xxx series touchscreen driver.
Several issues here.
1. A nit, subject: drop second/last, redundant "bindings". The
"dt-bindings" prefix is already stating that these are bindings.
See also:
https://elixir.bootlin.com/linux/v6.7-rc8/source/Documentation/devicetree/bindings/submitting-patches.rst#L18
2. Commit msg: Bindings are about hardware, not driver.
>
> Signed-off-by: AngeloGioacchino Del Regno <kholk11@gmail.com>
That's odd sequence.
> Signed-off-by: George Chan <gchan9527@gmail.com>
> ---
> .../bindings/input/touchscreen/nt36xxx.yaml | 70 ++++++++++++++++++++++
> 1 file changed, 70 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/input/touchscreen/nt36xxx.yaml b/Documentation/devicetree/bindings/input/touchscreen/nt36xxx.yaml
> new file mode 100644
> index 0000000000..3919f0d026
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/input/touchscreen/nt36xxx.yaml
Use compatible as filename.
> @@ -0,0 +1,70 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/input/touchscreen/nt36xxx.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Novatek NT36xxx series touchscreen controller Bindings
Not tested.
> +
> +maintainers:
> + - AngeloGioacchino Del Regno <kholk11@gmail.com>
> + - George Chan <gchan9527@gmail.com>
> +
> +allOf:
> + - $ref: touchscreen.yaml#
> +
> +properties:
> + compatible:
> + enum:
> + - novatek,nt36525-spi
> + - novatek,nt36672a-spi
> + - novatek,nt36675-spi
> + - novatek,nt36676f-spi
> + - novatek,nt36772-spi
This just does not work and was not tested... Limited review follows:
Drop spi and explain why this cannot be part of existing nt36672a.
I finished review here.
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH RFC/RFT 0/3] Add support to Novatek's touch IC nt36xxx
2024-10-15 12:53 [PATCH RFC/RFT 0/3] Add support to Novatek's touch IC nt36xxx George Chan via B4 Relay
` (2 preceding siblings ...)
2024-10-15 12:53 ` [PATCH RFC/RFT 3/3] dts: sm7125-xiaomi-joyeuse: Sample device tree for reference George Chan via B4 Relay
@ 2024-10-15 14:17 ` Rob Herring (Arm)
3 siblings, 0 replies; 7+ messages in thread
From: Rob Herring (Arm) @ 2024-10-15 14:17 UTC (permalink / raw)
To: George Chan
Cc: linux-input, Henrik Rydberg, Conor Dooley, linux-kernel,
devicetree, Krzysztof Kozlowski, Del Regno, Dmitry Torokhov
On Tue, 15 Oct 2024 20:53:28 +0800, George Chan wrote:
> Initially support for nt36xxx series spi device. Below
> list all supported varients:
>
> - NT36675
> - NT36672A
> - NT36772(?)
> - NT36525
> - NT36676F
>
> I had tested it with Redmi note 9 pro, aka NT36675 chip.
>
> This series is based on my repo below:
> https://github.com/99degree/linux/tree/nt36xxx
>
> There is a boot-and-functional tree for miatoll device:
> https://github.com/99degree/linux/tree/working-20241015
>
> And the older dev history:
> https://github.com/99degree/linux/tree/nt36xxx_old
> https://github.com/99degree/linux/tree/working-20230528/drivers/input/touchscreen
>
> This driver is based on
> AngeloGioacchino Del Regno for i2c based drive
> https://patchwork.kernel.org/project/linux-input/cover/20201028221302.66583-1-kholk11@gmail.com/#24831734
>
> _AND_
> Neil Armstrong for the spi device codes
> https://patchwork.kernel.org/project/linux-input/patch/20231213-topic-goodix-berlin-upstream-initial-v13-2-5d7a26a5eaa2@linaro.org/
>
> Download fw function is adapted from original vendor driver
> https://github.com/LineageOS/android_kernel_xiaomi_sm6250/tree/lineage-21/drivers/input/touchscreen/nt36xxx_spi/
>
> Panel follower functionality is finally added.
>
> Since the driver is split into core+spi so i2c function is relatively
> easily to add.
>
> Signed-off-by: George Chan <gchan9527@gmail.com>
> ---
> George Chan (3):
> dt-bindings: touchscreen: Add binding for Novatek NT36xxx series driver
> [RFC/RFT]Input: Add Novatek NT36xxx touchscreen driver
> dts: sm7125-xiaomi-joyeuse: Sample device tree for reference
>
> .../bindings/input/touchscreen/nt36xxx.yaml | 70 +
> arch/arm64/boot/dts/qcom/Makefile | 1 +
> .../boot/dts/qcom/sm7125-xiaomi-joyeuse-touch.dts | 183 +++
> drivers/input/touchscreen/Kconfig | 13 +
> drivers/input/touchscreen/Makefile | 2 +
> drivers/input/touchscreen/nt36xxx.h | 142 ++
> drivers/input/touchscreen/nt36xxx_core.c | 1422 ++++++++++++++++++++
> drivers/input/touchscreen/nt36xxx_spi.c | 256 ++++
> 8 files changed, 2089 insertions(+)
> ---
> base-commit: b852e1e7a0389ed6168ef1d38eb0bad71a6b11e8
> change-id: 20241015-nt36xxx-07e458ba2877
>
> Best regards,
> --
> George Chan <gchan9527@gmail.com>
>
>
>
My bot found new DTB warnings on the .dts files added or changed in this
series.
Some warnings may be from an existing SoC .dtsi. Or perhaps the warnings
are fixed by another series. Ultimately, it is up to the platform
maintainer whether these warnings are acceptable or not. No need to reply
unless the platform maintainer has comments.
If you already ran DT checks and didn't see these error(s), then
make sure dt-schema is up to date:
pip3 install dtschema --upgrade
New warnings running 'make CHECK_DTBS=y qcom/sm7125-xiaomi-joyeuse-touch.dtb' for 20241015-nt36xxx-v1-0-3919d0bffee6@gmail.com:
arch/arm64/boot/dts/qcom/sm7125-xiaomi-joyeuse-touch.dts:9:10: fatal error: sm7125-xiaomi-joyeuse-display.dts: No such file or directory
9 | #include "sm7125-xiaomi-joyeuse-display.dts"
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
compilation terminated.
make[3]: *** [scripts/Makefile.dtbs:131: arch/arm64/boot/dts/qcom/sm7125-xiaomi-joyeuse-touch.dtb] Error 1
make[2]: *** [scripts/Makefile.build:478: arch/arm64/boot/dts/qcom] Error 2
make[2]: Target 'arch/arm64/boot/dts/qcom/sm7125-xiaomi-joyeuse-touch.dtb' not remade because of errors.
make[1]: *** [/home/rob/proj/linux-dt-testing/Makefile:1412: qcom/sm7125-xiaomi-joyeuse-touch.dtb] Error 2
make: *** [Makefile:224: __sub-make] Error 2
make: Target 'qcom/sm7125-xiaomi-joyeuse-touch.dtb' not remade because of errors.
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH RFC/RFT 1/3] dt-bindings: touchscreen: Add binding for Novatek NT36xxx series driver
2024-10-15 12:53 ` [PATCH RFC/RFT 1/3] dt-bindings: touchscreen: Add binding for Novatek NT36xxx series driver George Chan via B4 Relay
2024-10-15 13:39 ` Krzysztof Kozlowski
@ 2024-10-15 14:27 ` Rob Herring (Arm)
1 sibling, 0 replies; 7+ messages in thread
From: Rob Herring (Arm) @ 2024-10-15 14:27 UTC (permalink / raw)
To: George Chan
Cc: devicetree, Krzysztof Kozlowski, Henrik Rydberg, linux-input,
Del Regno, linux-kernel, Dmitry Torokhov, Conor Dooley
On Tue, 15 Oct 2024 20:53:29 +0800, George Chan wrote:
> Add binding for the Novatek NT36xxx series touchscreen driver.
>
> Signed-off-by: AngeloGioacchino Del Regno <kholk11@gmail.com>
> Signed-off-by: George Chan <gchan9527@gmail.com>
> ---
> .../bindings/input/touchscreen/nt36xxx.yaml | 70 ++++++++++++++++++++++
> 1 file changed, 70 insertions(+)
>
My bot found errors running 'make dt_binding_check' on your patch:
yamllint warnings/errors:
./Documentation/devicetree/bindings/input/touchscreen/nt36xxx.yaml:19:9: [warning] wrong indentation: expected 6 but found 8 (indentation)
dtschema/dtc warnings/errors:
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/input/touchscreen/nt36xxx.yaml: title: 'Novatek NT36xxx series touchscreen controller Bindings' should not be valid under {'pattern': '([Bb]inding| [Ss]chema)'}
hint: Everything is a binding/schema, no need to say it. Describe what hardware the binding is for.
from schema $id: http://devicetree.org/meta-schemas/base.yaml#
doc reference errors (make refcheckdocs):
See https://patchwork.ozlabs.org/project/devicetree-bindings/patch/20241015-nt36xxx-v1-1-3919d0bffee6@gmail.com
The base for the series is generally the latest rc1. A different dependency
should be noted in *this* patch.
If you already ran 'make dt_binding_check' and didn't see the above
error(s), then make sure 'yamllint' is installed and dt-schema is up to
date:
pip3 install dtschema --upgrade
Please check and re-submit after running the above command yourself. Note
that DT_SCHEMA_FILES can be set to your schema file to speed up checking
your schema. However, it must be unset to test all examples with your schema.
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2024-10-15 14:27 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-10-15 12:53 [PATCH RFC/RFT 0/3] Add support to Novatek's touch IC nt36xxx George Chan via B4 Relay
2024-10-15 12:53 ` [PATCH RFC/RFT 1/3] dt-bindings: touchscreen: Add binding for Novatek NT36xxx series driver George Chan via B4 Relay
2024-10-15 13:39 ` Krzysztof Kozlowski
2024-10-15 14:27 ` Rob Herring (Arm)
2024-10-15 12:53 ` [PATCH RFC/RFT 2/3] Input: Add Novatek NT36xxx touchscreen driver George Chan via B4 Relay
2024-10-15 12:53 ` [PATCH RFC/RFT 3/3] dts: sm7125-xiaomi-joyeuse: Sample device tree for reference George Chan via B4 Relay
2024-10-15 14:17 ` [PATCH RFC/RFT 0/3] Add support to Novatek's touch IC nt36xxx Rob Herring (Arm)
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).