Quellcode durchsuchen

Add 'qcom/opensource/touch-drivers/' from commit '0abb70a15bd5d3469505bb0249d49266a4a5595e'

git-subtree-dir: qcom/opensource/touch-drivers
git-subtree-mainline: 51ff30338b6f88097833fca9400b9c676377e24f
git-subtree-split: 0abb70a15bd5d3469505bb0249d49266a4a5595e
Change-Id:
repo: https://git.codelinaro.org/clo/la/platform/vendor/opensource/touch-drivers
tag: LA.VENDOR.14.3.0.r1-17300-lanai.QSSI15.0
David Wronek vor 5 Monaten
Ursprung
Commit
e44c5532de
100 geänderte Dateien mit 91896 neuen und 0 gelöschten Zeilen
  1. 7 0
      qcom/opensource/touch-drivers/Android.bp
  2. 403 0
      qcom/opensource/touch-drivers/Android.mk
  3. 82 0
      qcom/opensource/touch-drivers/BUILD.bazel
  4. 247 0
      qcom/opensource/touch-drivers/Kbuild
  5. 16 0
      qcom/opensource/touch-drivers/Makefile
  6. 24 0
      qcom/opensource/touch-drivers/Makefile.am
  7. 191 0
      qcom/opensource/touch-drivers/NOTICE
  8. 4014 0
      qcom/opensource/touch-drivers/atmel_mxt/atmel_mxt_ts.c
  9. 7 0
      qcom/opensource/touch-drivers/config/gki_bengaltouch.conf
  10. 11 0
      qcom/opensource/touch-drivers/config/gki_bengaltouchconf.h
  11. 10 0
      qcom/opensource/touch-drivers/config/gki_blairtouch.conf
  12. 13 0
      qcom/opensource/touch-drivers/config/gki_blairtouchconf.h
  13. 2 0
      qcom/opensource/touch-drivers/config/gki_crowtouch.conf
  14. 6 0
      qcom/opensource/touch-drivers/config/gki_crowtouchconf.h
  15. 5 0
      qcom/opensource/touch-drivers/config/gki_kalamatouch.conf
  16. 9 0
      qcom/opensource/touch-drivers/config/gki_kalamatouchconf.h
  17. 6 0
      qcom/opensource/touch-drivers/config/gki_khajetouch.conf
  18. 11 0
      qcom/opensource/touch-drivers/config/gki_khajetouchconf.h
  19. 4 0
      qcom/opensource/touch-drivers/config/gki_konatouch.conf
  20. 7 0
      qcom/opensource/touch-drivers/config/gki_konatouchconf.h
  21. 7 0
      qcom/opensource/touch-drivers/config/gki_monacotouch.conf
  22. 8 0
      qcom/opensource/touch-drivers/config/gki_monacotouchconf.h
  23. 5 0
      qcom/opensource/touch-drivers/config/gki_pineappletouch.conf
  24. 8 0
      qcom/opensource/touch-drivers/config/gki_pineappletouchconf.h
  25. 5 0
      qcom/opensource/touch-drivers/config/gki_trinkettouch.conf
  26. 9 0
      qcom/opensource/touch-drivers/config/gki_trinkettouchconf.h
  27. 6 0
      qcom/opensource/touch-drivers/config/gki_waipiotouch.conf
  28. 10 0
      qcom/opensource/touch-drivers/config/gki_waipiotouchconf.h
  29. 87 0
      qcom/opensource/touch-drivers/dummy_touch/dummy_touch.c
  30. 167 0
      qcom/opensource/touch-drivers/focaltech_touch/focaltech_common.h
  31. 278 0
      qcom/opensource/touch-drivers/focaltech_touch/focaltech_config.h
  32. 3423 0
      qcom/opensource/touch-drivers/focaltech_touch/focaltech_core.c
  33. 391 0
      qcom/opensource/touch-drivers/focaltech_touch/focaltech_core.h
  34. 465 0
      qcom/opensource/touch-drivers/focaltech_touch/focaltech_esdcheck.c
  35. 1229 0
      qcom/opensource/touch-drivers/focaltech_touch/focaltech_ex_fun.c
  36. 359 0
      qcom/opensource/touch-drivers/focaltech_touch/focaltech_ex_mode.c
  37. 2093 0
      qcom/opensource/touch-drivers/focaltech_touch/focaltech_flash.c
  38. 216 0
      qcom/opensource/touch-drivers/focaltech_touch/focaltech_flash.h
  39. 292 0
      qcom/opensource/touch-drivers/focaltech_touch/focaltech_flash/focaltech_upgrade_ft3518.c
  40. 477 0
      qcom/opensource/touch-drivers/focaltech_touch/focaltech_gesture.c
  41. 548 0
      qcom/opensource/touch-drivers/focaltech_touch/focaltech_i2c.c
  42. 135 0
      qcom/opensource/touch-drivers/focaltech_touch/focaltech_point_report_check.c
  43. 126 0
      qcom/opensource/touch-drivers/glink_interface_ts/glink_interface.c
  44. 74 0
      qcom/opensource/touch-drivers/glink_interface_ts/glink_interface.h
  45. 1360 0
      qcom/opensource/touch-drivers/goodix_berlin_driver/goodix_brl_fwupdate.c
  46. 1526 0
      qcom/opensource/touch-drivers/goodix_berlin_driver/goodix_brl_hw.c
  47. 272 0
      qcom/opensource/touch-drivers/goodix_berlin_driver/goodix_brl_i2c.c
  48. 317 0
      qcom/opensource/touch-drivers/goodix_berlin_driver/goodix_brl_spi.c
  49. 343 0
      qcom/opensource/touch-drivers/goodix_berlin_driver/goodix_cfg_bin.c
  50. 2710 0
      qcom/opensource/touch-drivers/goodix_berlin_driver/goodix_ts_core.c
  51. 707 0
      qcom/opensource/touch-drivers/goodix_berlin_driver/goodix_ts_core.h
  52. 446 0
      qcom/opensource/touch-drivers/goodix_berlin_driver/goodix_ts_gesture.c
  53. 2981 0
      qcom/opensource/touch-drivers/goodix_berlin_driver/goodix_ts_inspect.c
  54. 503 0
      qcom/opensource/touch-drivers/goodix_berlin_driver/goodix_ts_tools.c
  55. 208 0
      qcom/opensource/touch-drivers/goodix_berlin_driver/goodix_ts_utils.c
  56. 4608 0
      qcom/opensource/touch-drivers/nt36xxx/nt36xxx.c
  57. 386 0
      qcom/opensource/touch-drivers/nt36xxx/nt36xxx.h
  58. 1537 0
      qcom/opensource/touch-drivers/nt36xxx/nt36xxx_ext_proc.c
  59. 2002 0
      qcom/opensource/touch-drivers/nt36xxx/nt36xxx_fw_update.c
  60. 607 0
      qcom/opensource/touch-drivers/nt36xxx/nt36xxx_mem_map.h
  61. 1481 0
      qcom/opensource/touch-drivers/nt36xxx/nt36xxx_mp_ctrlram.c
  62. 460 0
      qcom/opensource/touch-drivers/nt36xxx/nt36xxx_mp_ctrlram.h
  63. 8 0
      qcom/opensource/touch-drivers/nt36xxx/nt36xxx_spi.c
  64. 8 0
      qcom/opensource/touch-drivers/nt36xxx/nt36xxx_spi_ext_proc.c
  65. 8 0
      qcom/opensource/touch-drivers/nt36xxx/nt36xxx_spi_fw_update.c
  66. 53 0
      qcom/opensource/touch-drivers/pt/Makefile
  67. 522 0
      qcom/opensource/touch-drivers/pt/pt_btn.c
  68. 18570 0
      qcom/opensource/touch-drivers/pt/pt_core.c
  69. 230 0
      qcom/opensource/touch-drivers/pt/pt_core.h
  70. 556 0
      qcom/opensource/touch-drivers/pt/pt_debug.c
  71. 6746 0
      qcom/opensource/touch-drivers/pt/pt_device_access.c
  72. 1128 0
      qcom/opensource/touch-drivers/pt/pt_devtree.c
  73. 328 0
      qcom/opensource/touch-drivers/pt/pt_i2c.c
  74. 5437 0
      qcom/opensource/touch-drivers/pt/pt_loader.c
  75. 1006 0
      qcom/opensource/touch-drivers/pt/pt_mt_common.c
  76. 143 0
      qcom/opensource/touch-drivers/pt/pt_mta.c
  77. 144 0
      qcom/opensource/touch-drivers/pt/pt_mtb.c
  78. 572 0
      qcom/opensource/touch-drivers/pt/pt_pen.c
  79. 1051 0
      qcom/opensource/touch-drivers/pt/pt_platform.c
  80. 69 0
      qcom/opensource/touch-drivers/pt/pt_platform.h
  81. 815 0
      qcom/opensource/touch-drivers/pt/pt_proximity.c
  82. 1769 0
      qcom/opensource/touch-drivers/pt/pt_regs.h
  83. 429 0
      qcom/opensource/touch-drivers/pt/pt_spi.c
  84. 1814 0
      qcom/opensource/touch-drivers/qts/qts_core.c
  85. 169 0
      qcom/opensource/touch-drivers/qts/qts_core.h
  86. 46 0
      qcom/opensource/touch-drivers/qts/qts_core_common.h
  87. 54 0
      qcom/opensource/touch-drivers/raydium/Config.h
  88. 4 0
      qcom/opensource/touch-drivers/raydium/Makefile
  89. 516 0
      qcom/opensource/touch-drivers/raydium/chip_raydium/f303_ic_control.c
  90. 31 0
      qcom/opensource/touch-drivers/raydium/chip_raydium/f303_ic_control.h
  91. 281 0
      qcom/opensource/touch-drivers/raydium/chip_raydium/f303_ic_reg.h
  92. 2277 0
      qcom/opensource/touch-drivers/raydium/chip_raydium/f303_ic_test.c
  93. 65 0
      qcom/opensource/touch-drivers/raydium/chip_raydium/f303_ic_test.h
  94. 127 0
      qcom/opensource/touch-drivers/raydium/chip_raydium/ic_drv_global.c
  95. 190 0
      qcom/opensource/touch-drivers/raydium/chip_raydium/ic_drv_global.h
  96. 229 0
      qcom/opensource/touch-drivers/raydium/chip_raydium/ic_drv_interface.c
  97. 211 0
      qcom/opensource/touch-drivers/raydium/chip_raydium/ic_drv_interface.h
  98. 468 0
      qcom/opensource/touch-drivers/raydium/drv_interface.c
  99. 84 0
      qcom/opensource/touch-drivers/raydium/drv_interface.h
  100. 7791 0
      qcom/opensource/touch-drivers/raydium/rad_fw_image_30.h

+ 7 - 0
qcom/opensource/touch-drivers/Android.bp

@@ -0,0 +1,7 @@
+cc_library_headers {
+    name: "qti_glink_touch_kernel_headers",
+    export_include_dirs: [
+        "glink_interface_ts",
+    ],
+    vendor_available: true,
+}

+ 403 - 0
qcom/opensource/touch-drivers/Android.mk

@@ -0,0 +1,403 @@
+# Android makefile for display kernel modules
+
+TOUCH_DLKM_ENABLE := true
+ifeq ($(TARGET_KERNEL_DLKM_DISABLE), true)
+       ifeq ($(TARGET_KERNEL_DLKM_TOUCH_OVERRIDE), false)
+               TOUCH_DLKM_ENABLE := false
+       endif
+endif
+
+ifeq ($(TOUCH_DLKM_ENABLE),  true)
+       TOUCH_SELECT := CONFIG_MSM_TOUCH=m
+       BOARD_OPENSOURCE_DIR ?= vendor/qcom/opensource
+       BOARD_COMMON_DIR ?= device/qcom/common
+       
+       LOCAL_PATH := $(call my-dir)
+       ifeq ($(TARGET_BOARD_PLATFORM), pineapple)
+              LOCAL_MODULE_DDK_BUILD := true
+       endif
+
+       ifeq ($(TARGET_BOARD_PLATFORM), blair)
+              LOCAL_MODULE_DDK_BUILD := true
+       endif
+
+       ifeq ($(TARGET_BOARD_PLATFORM), pitti)
+              LOCAL_MODULE_DDK_BUILD := true
+       endif
+
+       ifeq ($(TARGET_BOARD_PLATFORM), monaco)
+              LOCAL_MODULE_DDK_BUILD := true
+       endif
+
+       ifeq ($(TARGET_BOARD_PLATFORM), volcano)
+              LOCAL_MODULE_DDK_BUILD := true
+       endif
+
+       include $(CLEAR_VARS)
+
+       # This makefile is only for DLKM
+       ifneq ($(findstring vendor,$(LOCAL_PATH)),)
+
+       ifneq ($(findstring opensource,$(LOCAL_PATH)),)
+               TOUCH_BLD_DIR := $(shell pwd)/$(BOARD_OPENSOURCE_DIR)/touch-drivers
+       endif # opensource
+
+       DLKM_DIR := $(TOP)/$(BOARD_COMMON_DIR)/dlkm
+
+       LOCAL_ADDITIONAL_DEPENDENCIES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*)
+
+       # Build
+       ###########################################################
+       # This is set once per LOCAL_PATH, not per (kernel) module
+       KBUILD_OPTIONS := TOUCH_ROOT=$(TOUCH_BLD_DIR)
+
+       KBUILD_OPTIONS += MODNAME=touch_dlkm
+       KBUILD_OPTIONS += BOARD_PLATFORM=$(TARGET_BOARD_PLATFORM)
+       KBUILD_OPTIONS += $(TOUCH_SELECT)
+
+       ###########################################################
+
+ifeq ($(TARGET_BOARD_PLATFORM), monaco)
+
+       ###########################################################
+       include $(CLEAR_VARS)
+       LOCAL_SRC_FILES   := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*)
+       LOCAL_MODULE              := pt_ts.ko
+       LOCAL_MODULE_KBUILD_NAME  := pt_ts.ko
+       LOCAL_MODULE_TAGS         := optional
+       #LOCAL_MODULE_DEBUG_ENABLE := true
+       LOCAL_MODULE_PATH         := $(KERNEL_MODULES_OUT)
+       include $(DLKM_DIR)/Build_external_kernelmodule.mk
+       ###########################################################
+
+       ###########################################################
+       include $(CLEAR_VARS)
+       LOCAL_SRC_FILES   := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*)
+       LOCAL_MODULE              := pt_i2c.ko
+       LOCAL_MODULE_KBUILD_NAME  := pt_i2c.ko
+       LOCAL_MODULE_TAGS         := optional
+       #LOCAL_MODULE_DEBUG_ENABLE := true
+       LOCAL_MODULE_PATH         := $(KERNEL_MODULES_OUT)
+       include $(DLKM_DIR)/Build_external_kernelmodule.mk
+       ###########################################################
+
+       ###########################################################
+       include $(CLEAR_VARS)
+       LOCAL_SRC_FILES   := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*)
+       LOCAL_MODULE              := pt_device_access.ko
+       LOCAL_MODULE_KBUILD_NAME  := pt_device_access.ko
+       LOCAL_MODULE_TAGS         := optional
+       #LOCAL_MODULE_DEBUG_ENABLE := true
+       LOCAL_MODULE_PATH         := $(KERNEL_MODULES_OUT)
+       include $(DLKM_DIR)/Build_external_kernelmodule.mk
+       ###########################################################
+
+       ###########################################################
+       include $(CLEAR_VARS)
+       LOCAL_SRC_FILES   := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*)
+       LOCAL_MODULE              := glink_comm.ko
+       LOCAL_MODULE_KBUILD_NAME  := glink_comm.ko
+       LOCAL_MODULE_TAGS         := optional
+       #LOCAL_MODULE_DEBUG_ENABLE := true
+       LOCAL_MODULE_PATH         := $(KERNEL_MODULES_OUT)
+       include $(DLKM_DIR)/Build_external_kernelmodule.mk
+       ###########################################################
+
+       ###########################################################
+       include $(CLEAR_VARS)
+       LOCAL_SRC_FILES   := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*)
+       LOCAL_MODULE              := raydium_ts.ko
+       LOCAL_MODULE_KBUILD_NAME  := raydium_ts.ko
+       LOCAL_MODULE_TAGS         := optional
+       #LOCAL_MODULE_DEBUG_ENABLE := true
+       LOCAL_MODULE_PATH         := $(KERNEL_MODULES_OUT)
+       include $(DLKM_DIR)/Build_external_kernelmodule.mk
+       ###########################################################
+
+else ifeq ($(TARGET_BOARD_PLATFORM), kona)
+
+       ###########################################################
+       include $(CLEAR_VARS)
+       LOCAL_SRC_FILES   := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*)
+       LOCAL_MODULE              := focaltech_fts.ko
+       LOCAL_MODULE_KBUILD_NAME  := focaltech_fts.ko
+       LOCAL_MODULE_TAGS         := optional
+       #LOCAL_MODULE_DEBUG_ENABLE := true
+       LOCAL_MODULE_PATH         := $(KERNEL_MODULES_OUT)
+       include $(DLKM_DIR)/Build_external_kernelmodule.mk
+       ###########################################################
+
+else ifeq ($(TARGET_BOARD_PLATFORM), pineapple)
+
+       ###########################################################
+       include $(CLEAR_VARS)
+       LOCAL_SRC_FILES   := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*)
+       LOCAL_MODULE              := nt36xxx-i2c.ko
+       LOCAL_MODULE_KBUILD_NAME  := nt36xxx-i2c.ko
+       LOCAL_MODULE_TAGS         := optional
+       #LOCAL_MODULE_DEBUG_ENABLE := true
+       LOCAL_MODULE_PATH         := $(KERNEL_MODULES_OUT)
+       include $(DLKM_DIR)/Build_external_kernelmodule.mk
+       ###########################################################
+
+       ###########################################################
+       include $(CLEAR_VARS)
+       LOCAL_SRC_FILES   := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*)
+       LOCAL_MODULE              := goodix_ts.ko
+       LOCAL_MODULE_KBUILD_NAME  := goodix_ts.ko
+       LOCAL_MODULE_TAGS         := optional
+       #LOCAL_MODULE_DEBUG_ENABLE := true
+       LOCAL_MODULE_PATH         := $(KERNEL_MODULES_OUT)
+       include $(DLKM_DIR)/Build_external_kernelmodule.mk
+       ###########################################################
+
+       ###########################################################
+       include $(CLEAR_VARS)
+       LOCAL_SRC_FILES   := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*)
+       LOCAL_MODULE              := atmel_mxt_ts.ko
+       LOCAL_MODULE_KBUILD_NAME  := atmel_mxt_ts.ko
+       LOCAL_MODULE_TAGS         := optional
+       #LOCAL_MODULE_DEBUG_ENABLE := true
+       LOCAL_MODULE_PATH         := $(KERNEL_MODULES_OUT)
+       include $(DLKM_DIR)/Build_external_kernelmodule.mk
+       ###########################################################
+
+else ifeq ($(TARGET_BOARD_PLATFORM), kalama)
+
+       ###########################################################
+       include $(CLEAR_VARS)
+       LOCAL_SRC_FILES   := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*)
+       LOCAL_MODULE              := nt36xxx-i2c.ko
+       LOCAL_MODULE_KBUILD_NAME  := nt36xxx-i2c.ko
+       LOCAL_MODULE_TAGS         := optional
+       #LOCAL_MODULE_DEBUG_ENABLE := true
+       LOCAL_MODULE_PATH         := $(KERNEL_MODULES_OUT)
+       include $(DLKM_DIR)/Build_external_kernelmodule.mk
+       ###########################################################
+
+       ###########################################################
+       include $(CLEAR_VARS)
+       LOCAL_SRC_FILES   := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*)
+       LOCAL_MODULE              := goodix_ts.ko
+       LOCAL_MODULE_KBUILD_NAME  := goodix_ts.ko
+       LOCAL_MODULE_TAGS         := optional
+       #LOCAL_MODULE_DEBUG_ENABLE := true
+       LOCAL_MODULE_PATH         := $(KERNEL_MODULES_OUT)
+       include $(DLKM_DIR)/Build_external_kernelmodule.mk
+       ###########################################################
+
+       ###########################################################
+       include $(CLEAR_VARS)
+       LOCAL_SRC_FILES   := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*)
+       LOCAL_MODULE              := atmel_mxt_ts.ko
+       LOCAL_MODULE_KBUILD_NAME  := atmel_mxt_ts.ko
+       LOCAL_MODULE_TAGS         := optional
+       #LOCAL_MODULE_DEBUG_ENABLE := true
+       LOCAL_MODULE_PATH         := $(KERNEL_MODULES_OUT)
+       include $(DLKM_DIR)/Build_external_kernelmodule.mk
+       ###########################################################
+
+else ifeq ($(TARGET_BOARD_PLATFORM), blair)
+
+       ###########################################################
+       include $(CLEAR_VARS)
+       LOCAL_SRC_FILES   := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*)
+       LOCAL_MODULE              := focaltech_fts.ko
+       LOCAL_MODULE_KBUILD_NAME  := focaltech_fts.ko
+       LOCAL_MODULE_TAGS         := optional
+       #LOCAL_MODULE_DEBUG_ENABLE := true
+       LOCAL_MODULE_PATH         := $(KERNEL_MODULES_OUT)
+       include $(DLKM_DIR)/Build_external_kernelmodule.mk
+       ###########################################################
+
+       ###########################################################
+       include $(CLEAR_VARS)
+       LOCAL_SRC_FILES   := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*)
+       LOCAL_MODULE              := nt36xxx-i2c.ko
+       LOCAL_MODULE_KBUILD_NAME  := nt36xxx-i2c.ko
+       LOCAL_MODULE_TAGS         := optional
+       #LOCAL_MODULE_DEBUG_ENABLE := true
+       LOCAL_MODULE_PATH         := $(KERNEL_MODULES_OUT)
+       include $(DLKM_DIR)/Build_external_kernelmodule.mk
+       ###########################################################
+
+       ###########################################################
+       include $(CLEAR_VARS)
+       LOCAL_SRC_FILES   := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*)
+       LOCAL_MODULE              := synaptics_tcm_ts.ko
+       LOCAL_MODULE_KBUILD_NAME  := synaptics_tcm_ts.ko
+       LOCAL_MODULE_TAGS         := optional
+       #LOCAL_MODULE_DEBUG_ENABLE := true
+       LOCAL_MODULE_PATH         := $(KERNEL_MODULES_OUT)
+       include $(DLKM_DIR)/Build_external_kernelmodule.mk
+       ###########################################################
+
+       ###########################################################
+       include $(CLEAR_VARS)
+       LOCAL_SRC_FILES   := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*)
+       LOCAL_MODULE              := goodix_ts.ko
+       LOCAL_MODULE_KBUILD_NAME  := goodix_ts.ko
+       LOCAL_MODULE_TAGS         := optional
+       #LOCAL_MODULE_DEBUG_ENABLE := true
+       LOCAL_MODULE_PATH         := $(KERNEL_MODULES_OUT)
+       include $(DLKM_DIR)/Build_external_kernelmodule.mk
+       ###########################################################
+
+else ifeq ($(TARGET_BOARD_PLATFORM), crow)
+
+       ###########################################################
+       include $(CLEAR_VARS)
+       LOCAL_SRC_FILES   := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*)
+       LOCAL_MODULE              := goodix_ts.ko
+       LOCAL_MODULE_KBUILD_NAME  := goodix_ts.ko
+       LOCAL_MODULE_TAGS         := optional
+       #LOCAL_MODULE_DEBUG_ENABLE := true
+       LOCAL_MODULE_PATH         := $(KERNEL_MODULES_OUT)
+       include $(DLKM_DIR)/Build_external_kernelmodule.mk
+       ###########################################################
+
+else ifeq ($(TARGET_BOARD_PLATFORM), bengal)
+
+       ###########################################################
+       include $(CLEAR_VARS)
+       LOCAL_SRC_FILES   := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*)
+       LOCAL_MODULE              := synaptics_tcm_ts.ko
+       LOCAL_MODULE_KBUILD_NAME  := synaptics_tcm_ts.ko
+       LOCAL_MODULE_TAGS         := optional
+       #LOCAL_MODULE_DEBUG_ENABLE := true
+       LOCAL_MODULE_PATH         := $(KERNEL_MODULES_OUT)
+       include $(DLKM_DIR)/Build_external_kernelmodule.mk
+       ###########################################################
+
+       ###########################################################
+       include $(CLEAR_VARS)
+       LOCAL_SRC_FILES   := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*)
+       LOCAL_MODULE              := nt36xxx-i2c.ko
+       LOCAL_MODULE_KBUILD_NAME  := nt36xxx-i2c.ko
+       LOCAL_MODULE_TAGS         := optional
+       #LOCAL_MODULE_DEBUG_ENABLE := true
+       LOCAL_MODULE_PATH         := $(KERNEL_MODULES_OUT)
+       include $(DLKM_DIR)/Build_external_kernelmodule.mk
+       ###########################################################
+
+       ###########################################################
+       include $(CLEAR_VARS)
+       LOCAL_SRC_FILES   := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*)
+       LOCAL_MODULE              := focaltech_fts.ko
+       LOCAL_MODULE_KBUILD_NAME  := focaltech_fts.ko
+       LOCAL_MODULE_TAGS         := optional
+       #LOCAL_MODULE_DEBUG_ENABLE := true
+       LOCAL_MODULE_PATH         := $(KERNEL_MODULES_OUT)
+       include $(DLKM_DIR)/Build_external_kernelmodule.mk
+       ###########################################################
+
+else ifeq ($(TARGET_BOARD_PLATFORM), trinket)
+
+       ###########################################################
+       include $(CLEAR_VARS)
+       LOCAL_SRC_FILES   := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*)
+       LOCAL_MODULE              := synaptics_tcm_ts.ko
+       LOCAL_MODULE_KBUILD_NAME  := synaptics_tcm_ts.ko
+       LOCAL_MODULE_TAGS         := optional
+       #LOCAL_MODULE_DEBUG_ENABLE := true
+       LOCAL_MODULE_PATH         := $(KERNEL_MODULES_OUT)
+       include $(DLKM_DIR)/Build_external_kernelmodule.mk
+       ###########################################################
+
+else ifeq ($(TARGET_BOARD_PLATFORM), pitti)
+
+       ###########################################################
+       include $(CLEAR_VARS)
+       LOCAL_SRC_FILES   := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*)
+       LOCAL_MODULE              := focaltech_fts.ko
+       LOCAL_MODULE_KBUILD_NAME  := focaltech_fts.ko
+       LOCAL_MODULE_TAGS         := optional
+       #LOCAL_MODULE_DEBUG_ENABLE := true
+       LOCAL_MODULE_PATH         := $(KERNEL_MODULES_OUT)
+       include $(DLKM_DIR)/Build_external_kernelmodule.mk
+       ###########################################################
+
+       ###########################################################
+       include $(CLEAR_VARS)
+       LOCAL_SRC_FILES   := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*)
+       LOCAL_MODULE              := goodix_ts.ko
+       LOCAL_MODULE_KBUILD_NAME  := goodix_ts.ko
+       LOCAL_MODULE_TAGS         := optional
+       #LOCAL_MODULE_DEBUG_ENABLE := true
+       LOCAL_MODULE_PATH         := $(KERNEL_MODULES_OUT)
+       include $(DLKM_DIR)/Build_external_kernelmodule.mk
+       ###########################################################
+
+else ifeq ($(TARGET_BOARD_PLATFORM), volcano)
+
+       ###########################################################
+       include $(CLEAR_VARS)
+       LOCAL_SRC_FILES   := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*)
+       LOCAL_MODULE              := goodix_ts.ko
+       LOCAL_MODULE_KBUILD_NAME  := goodix_ts.ko
+       LOCAL_MODULE_TAGS         := optional
+       #LOCAL_MODULE_DEBUG_ENABLE := true
+       LOCAL_MODULE_PATH         := $(KERNEL_MODULES_OUT)
+       include $(DLKM_DIR)/Build_external_kernelmodule.mk
+       ###########################################################
+
+       ###########################################################
+       include $(CLEAR_VARS)
+       LOCAL_SRC_FILES   := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*)
+       LOCAL_MODULE              := focaltech_fts.ko
+       LOCAL_MODULE_KBUILD_NAME  := focaltech_fts.ko
+       LOCAL_MODULE_TAGS         := optional
+       #LOCAL_MODULE_DEBUG_ENABLE := true
+       LOCAL_MODULE_PATH         := $(KERNEL_MODULES_OUT)
+       include $(DLKM_DIR)/Build_external_kernelmodule.mk
+       ###########################################################
+
+else
+
+       ###########################################################
+       include $(CLEAR_VARS)
+       LOCAL_SRC_FILES   := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*)
+       LOCAL_MODULE              := nt36xxx-i2c.ko
+       LOCAL_MODULE_KBUILD_NAME  := nt36xxx-i2c.ko
+       LOCAL_MODULE_TAGS         := optional
+       #LOCAL_MODULE_DEBUG_ENABLE := true
+       LOCAL_MODULE_PATH         := $(KERNEL_MODULES_OUT)
+       include $(DLKM_DIR)/Build_external_kernelmodule.mk
+       ###########################################################
+
+       ###########################################################
+       include $(CLEAR_VARS)
+       LOCAL_SRC_FILES   := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*)
+       LOCAL_MODULE              := goodix_ts.ko
+       LOCAL_MODULE_KBUILD_NAME  := goodix_ts.ko
+       LOCAL_MODULE_TAGS         := optional
+       #LOCAL_MODULE_DEBUG_ENABLE := true
+       LOCAL_MODULE_PATH         := $(KERNEL_MODULES_OUT)
+       include $(DLKM_DIR)/Build_external_kernelmodule.mk
+       ###########################################################
+
+       ###########################################################
+       include $(CLEAR_VARS)
+       LOCAL_SRC_FILES   := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*)
+       LOCAL_MODULE              := atmel_mxt_ts.ko
+       LOCAL_MODULE_KBUILD_NAME  := atmel_mxt_ts.ko
+       LOCAL_MODULE_TAGS         := optional
+       #LOCAL_MODULE_DEBUG_ENABLE := true
+       LOCAL_MODULE_PATH         := $(KERNEL_MODULES_OUT)
+       include $(DLKM_DIR)/Build_external_kernelmodule.mk
+       ###########################################################
+
+       ###########################################################
+       include $(CLEAR_VARS)
+       LOCAL_SRC_FILES   := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*)
+       LOCAL_MODULE              := synaptics_tcm_ts.ko
+       LOCAL_MODULE_KBUILD_NAME  := synaptics_tcm_ts.ko
+       LOCAL_MODULE_TAGS         := optional
+       #LOCAL_MODULE_DEBUG_ENABLE := true
+       LOCAL_MODULE_PATH         := $(KERNEL_MODULES_OUT)
+       include $(DLKM_DIR)/Build_external_kernelmodule.mk
+       ###########################################################
+
+endif #kona
+       endif # DLKM check
+endif

+ 82 - 0
qcom/opensource/touch-drivers/BUILD.bazel

@@ -0,0 +1,82 @@
+load("//build/kernel/kleaf:kernel.bzl", "ddk_headers")
+
+package(
+    default_visibility = [
+        "//visibility:public"],
+)
+
+ddk_headers(
+    name = "goodix_ts_headers",
+    hdrs = glob([
+            "goodix_berlin_driver/*.h",
+            "qts/*.h"
+        ]
+    )
+)
+
+ddk_headers(
+    name = "nt36xxx_headers",
+    hdrs = glob([
+            "nt36xxx/*.h"
+        ]
+    )
+)
+
+ddk_headers(
+    name = "focaltech_headers",
+    hdrs = glob([
+            "focaltech_touch/*.h"
+        ]
+    )
+)
+
+ddk_headers(
+    name = "synaptics_tcm_headers",
+    hdrs = glob([
+            "synaptics_tcm/*.h"
+        ]
+    )
+)
+
+ddk_headers(
+    name = "config_headers",
+    hdrs = glob([
+            "config/*.h"
+        ]
+    ),
+    includes = ["config"]
+)
+
+ddk_headers(
+    name = "glink_interface_ts_headers",
+    hdrs = glob([
+            "glink_interface_ts/*.h"
+        ]
+    ),
+    includes = ["glink_interface_ts"]
+)
+
+ddk_headers(
+    name = "pt_headers",
+    hdrs = glob([
+            "pt/*.h"
+        ]
+    )
+)
+
+ddk_headers(
+    name = "raydium_headers",
+    hdrs = glob([
+            "raydium/*.h",
+            "raydium/chip_raydium/*.h"
+        ]
+    )
+)
+
+ddk_headers(
+    name = "touch_drivers_headers",
+    hdrs = [":goodix_ts_headers", ":nt36xxx_headers", ":focaltech_headers", ":synaptics_tcm_headers", ":glink_interface_ts_headers", ":pt_headers", ":raydium_headers", ":config_headers"]
+)
+
+load(":target.bzl", "define_touch_target")
+define_touch_target()

+ 247 - 0
qcom/opensource/touch-drivers/Kbuild

@@ -0,0 +1,247 @@
+
+KDIR := $(TOP)/kernel_platform/common
+
+ifeq ($(CONFIG_ARCH_WAIPIO), y)
+	include $(TOUCH_ROOT)/config/gki_waipiotouch.conf
+	LINUX_INC += -include $(TOUCH_ROOT)/config/gki_waipiotouchconf.h
+endif
+
+ifeq ($(CONFIG_ARCH_KALAMA), y)
+	include $(TOUCH_ROOT)/config/gki_kalamatouch.conf
+	LINUX_INC += -include $(TOUCH_ROOT)/config/gki_kalamatouchconf.h
+endif
+
+ifeq ($(CONFIG_ARCH_KHAJE), y)
+	include $(TOUCH_ROOT)/config/gki_khajetouch.conf
+	LINUX_INC += -include $(TOUCH_ROOT)/config/gki_khajetouchconf.h
+endif
+
+ifeq ($(CONFIG_ARCH_PINEAPPLE), y)
+	include $(TOUCH_ROOT)/config/gki_pineappletouch.conf
+	LINUX_INC += -include $(TOUCH_ROOT)/config/gki_pineappletouchconf.h
+endif
+
+ifeq ($(CONFIG_ARCH_MONACO), y)
+	include $(TOUCH_ROOT)/config/gki_monacotouch.conf
+	LINUX_INC += -include $(TOUCH_ROOT)/config/gki_monacotouchconf.h
+endif
+
+ifeq ($(CONFIG_ARCH_KONA), y)
+        include $(TOUCH_ROOT)/config/gki_konatouch.conf
+        LINUX_INC += -include $(TOUCH_ROOT)/config/gki_konatouchconf.h
+endif
+
+ifeq ($(CONFIG_ARCH_BLAIR), y)
+        include $(TOUCH_ROOT)/config/gki_blairtouch.conf
+        LINUX_INC += -include $(TOUCH_ROOT)/config/gki_blairtouchconf.h
+endif
+
+ifeq ($(CONFIG_ARCH_CROW), y)
+        include $(TOUCH_ROOT)/config/gki_crowtouch.conf
+        LINUX_INC += -include $(TOUCH_ROOT)/config/gki_crowtouchconf.h
+endif
+
+ifeq ($(CONFIG_ARCH_TRINKET), y)
+        include $(TOUCH_ROOT)/config/gki_trinkettouch.conf
+        LINUX_INC += -include $(TOUCH_ROOT)/config/gki_trinkettouchconf.h
+endif
+
+ifeq ($(CONFIG_ARCH_BENGAL), y)
+        include $(TOUCH_ROOT)/config/gki_bengaltouch.conf
+        LINUX_INC += -include $(TOUCH_ROOT)/config/gki_bengaltouchconf.h
+endif
+
+LINUX_INC +=	-Iinclude/linux \
+		-Iinclude/linux/drm \
+		-Iinclude/linux/gunyah \
+		-Iinclude/linux/input
+
+CDEFINES +=	-DANI_LITTLE_BYTE_ENDIAN \
+	-DANI_LITTLE_BIT_ENDIAN \
+	-DDOT11F_LITTLE_ENDIAN_HOST \
+	-DANI_COMPILER_TYPE_GCC \
+	-DANI_OS_TYPE_ANDROID=6 \
+	-DPTT_SOCK_SVC_ENABLE \
+	-Wall\
+	-Werror\
+	-D__linux__
+
+KBUILD_CPPFLAGS += $(CDEFINES)
+
+ccflags-y += $(LINUX_INC)
+
+ifeq ($(call cc-option-yn, -Wmaybe-uninitialized),y)
+EXTRA_CFLAGS += -Wmaybe-uninitialized
+endif
+
+ifeq ($(call cc-option-yn, -Wheader-guard),y)
+EXTRA_CFLAGS += -Wheader-guard
+endif
+
+######### CONFIG_MSM_TOUCH ########
+
+ifeq ($(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX), y)
+
+	LINUX_INC += -include $(TOUCH_ROOT)/synaptics_dsx/synaptics_dsx.h
+	LINUX_INC += -include $(TOUCH_ROOT)/synaptics_dsx/synaptics_dsx_core.h
+
+	synaptics_dsx-y := \
+		 ./synaptics_dsx/synaptics_dsx_core.o \
+		 ./synaptics_dsx/synaptics_dsx_i2c.o
+
+	obj-$(CONFIG_MSM_TOUCH) += synaptics_dsx.o
+endif
+
+ifeq ($(CONFIG_TOUCH_FOCALTECH), y)
+	LINUX_INC += -include $(TOUCH_ROOT)/focaltech_touch/focaltech_common.h
+	LINUX_INC += -include $(TOUCH_ROOT)/focaltech_touch/focaltech_config.h
+	LINUX_INC += -include $(TOUCH_ROOT)/focaltech_touch/focaltech_core.h
+	LINUX_INC += -include $(TOUCH_ROOT)/focaltech_touch/focaltech_flash.h
+
+	focaltech_fts-y := \
+		 ./focaltech_touch/focaltech_core.o \
+		 ./focaltech_touch/focaltech_ex_fun.o \
+		 ./focaltech_touch/focaltech_ex_mode.o \
+		 ./focaltech_touch/focaltech_gesture.o \
+		 ./focaltech_touch/focaltech_esdcheck.o \
+		 ./focaltech_touch/focaltech_point_report_check.o \
+		 ./focaltech_touch/focaltech_i2c.o \
+		 ./focaltech_touch/focaltech_flash.o \
+		 ./focaltech_touch/focaltech_flash/focaltech_upgrade_ft3518.o
+
+	obj-$(CONFIG_MSM_TOUCH) += focaltech_fts.o
+endif
+
+ifeq ($(CONFIG_TOUCHSCREEN_NT36XXX_I2C), y)
+	LINUX_INC += -include $(TOUCH_ROOT)/nt36xxx/nt36xxx.h
+	LINUX_INC += -include $(TOUCH_ROOT)/nt36xxx/nt36xxx_mem_map.h
+	LINUX_INC += -include $(TOUCH_ROOT)/nt36xxx/nt36xxx_mp_ctrlram.h
+
+	nt36xxx-i2c-y := \
+		 ./nt36xxx/nt36xxx.o \
+		 ./nt36xxx/nt36xxx_fw_update.o \
+		 ./nt36xxx/nt36xxx_ext_proc.o \
+		 ./nt36xxx/nt36xxx_mp_ctrlram.o
+
+	obj-$(CONFIG_MSM_TOUCH) += nt36xxx-i2c.o
+endif
+
+ifeq ($(CONFIG_TOUCHSCREEN_GOODIX_BRL), y)
+	LINUX_INC += -include $(TOUCH_ROOT)/goodix_berlin_driver/goodix_ts_core.h
+	LINUX_INC += -include $(TOUCH_ROOT)/qts/qts_core.h
+	LINUX_INC += -include $(TOUCH_ROOT)/qts/qts_core_common.h
+
+	goodix_ts-y := \
+		 ./goodix_berlin_driver/goodix_ts_core.o \
+		 ./goodix_berlin_driver/goodix_brl_hw.o \
+		 ./goodix_berlin_driver/goodix_cfg_bin.o \
+		 ./goodix_berlin_driver/goodix_ts_utils.o \
+		 ./goodix_berlin_driver/goodix_brl_fwupdate.o \
+		 ./goodix_berlin_driver/goodix_ts_tools.o \
+		 ./goodix_berlin_driver/goodix_ts_gesture.o \
+		 ./goodix_berlin_driver/goodix_ts_inspect.o \
+		 ./goodix_berlin_driver/goodix_brl_spi.o \
+		 ./goodix_berlin_driver/goodix_brl_i2c.o \
+		 ./qts/qts_core.o
+
+	obj-$(CONFIG_MSM_TOUCH) += goodix_ts.o
+endif
+
+ifeq ($(CONFIG_TOUCHSCREEN_ATMEL_MXT), y)
+
+	atmel_mxt_ts-y := \
+		 ./atmel_mxt/atmel_mxt_ts.o
+
+	obj-$(CONFIG_MSM_TOUCH) += atmel_mxt_ts.o
+endif
+
+ifeq ($(CONFIG_TOUCHSCREEN_DUMMY), y)
+	dummy_ts-y := ./dummy_touch/dummy_touch.o
+
+	obj-$(CONFIG_MSM_TOUCH) += dummy_ts.o
+endif
+
+ifeq ($(CONFIG_TOUCHSCREEN_MSM_GLINK), y)
+
+	LINUXINCLUDE    += -I$(TOUCH_ROOT)/glink_interface_ts
+
+	glink_comm-y := ./glink_interface_ts/glink_interface.o
+
+	obj-$(CONFIG_MSM_TOUCH) += glink_comm.o
+endif
+
+ifeq ($(CONFIG_TOUCHSCREEN_SYNAPTICS_TCM), y)
+	synaptics_tcm_ts-y := \
+		 ./synaptics_tcm/synaptics_tcm_core.o \
+		 ./synaptics_tcm/synaptics_tcm_i2c.o \
+		 ./synaptics_tcm/synaptics_tcm_touch.o
+
+	obj-$(CONFIG_MSM_TOUCH) += synaptics_tcm_ts.o
+
+endif
+
+ifneq ($(CONFIG_ARCH_PINEAPPLE), y)
+	ifeq ($(CONFIG_TOUCHSCREEN_PARADE), y)
+		LINUX_INC += -include $(TOUCH_ROOT)/pt/pt_regs.h
+		LINUX_INC += -include $(TOUCH_ROOT)/pt/pt_core.h
+		LINUX_INC += -include $(TOUCH_ROOT)/pt/pt_platform.h
+
+		pt_ts-y := \
+			./pt/pt_core.o \
+			./pt/pt_mt_common.o \
+			./pt/pt_platform.o \
+			./pt/pt_devtree.o \
+			./pt/pt_btn.o \
+			./pt/pt_mtb.o \
+			./pt/pt_proximity.o
+
+		obj-$(CONFIG_MSM_TOUCH) += pt_ts.o
+	endif
+
+	ifeq ($(CONFIG_TOUCHSCREEN_PARADE_I2C), y)
+		LINUX_INC += -include $(TOUCH_ROOT)/pt/pt_regs.h
+
+		pt_i2c-y := \
+			./pt/pt_i2c.o
+
+		obj-$(CONFIG_MSM_TOUCH) += pt_i2c.o
+	endif
+
+	ifeq ($(CONFIG_TOUCHSCREEN_PARADE_DEVICE_ACCESS), y)
+		LINUX_INC += -include $(TOUCH_ROOT)/pt/pt_regs.h
+
+		pt_device_access-y := \
+			./pt/pt_device_access.o
+
+		obj-$(CONFIG_MSM_TOUCH) += pt_device_access.o
+	endif
+
+	ifeq ($(CONFIG_TOUCHSCREEN_RM_TS), y)
+		LINUX_INC += -include $(TOUCH_ROOT)/raydium/Config.h
+		LINUX_INC += -include $(TOUCH_ROOT)/raydium/drv_interface.h
+		LINUX_INC += -include $(TOUCH_ROOT)/raydium/rad_fw_image_30.h
+		LINUX_INC += -include $(TOUCH_ROOT)/raydium/raydium_driver.h
+		LINUX_INC += -include $(TOUCH_ROOT)/raydium/raydium_selftest.h
+		LINUX_INC += -include $(TOUCH_ROOT)/raydium/tpselftest_30.h
+		LINUX_INC += -include $(TOUCH_ROOT)/raydium/chip_raydium/f303_ic_control.h
+		LINUX_INC += -include $(TOUCH_ROOT)/raydium/chip_raydium/f303_ic_reg.h
+		LINUX_INC += -include $(TOUCH_ROOT)/raydium/chip_raydium/f303_ic_test.h
+		LINUX_INC += -include $(TOUCH_ROOT)/raydium/chip_raydium/ic_drv_global.h
+		LINUX_INC += -include $(TOUCH_ROOT)/raydium/chip_raydium/ic_drv_interface.h
+
+		raydium_ts-y := \
+			./raydium/drv_interface.o \
+			./raydium/raydium_driver.o \
+			./raydium/raydium_fw_update.o \
+			./raydium/raydium_selftest.o \
+			./raydium/raydium_sysfs.o \
+			./raydium/chip_raydium/f303_ic_control.o \
+			./raydium/chip_raydium/f303_ic_test.o \
+			./raydium/chip_raydium/ic_drv_global.o \
+			./raydium/chip_raydium/ic_drv_interface.o
+
+			obj-$(CONFIG_MSM_TOUCH) += raydium_ts.o
+	endif
+endif # pineapple
+
+CDEFINES += -DBUILD_TIMESTAMP=\"$(shell date -u +'%Y-%m-%dT%H:%M:%SZ')\"

+ 16 - 0
qcom/opensource/touch-drivers/Makefile

@@ -0,0 +1,16 @@
+
+KBUILD_OPTIONS+= TOUCH_ROOT=$(KERNEL_SRC)/$(M)
+KBUILD_OPTIONS += MODNAME?=touch_dlkm
+
+all:
+	$(MAKE) -C $(KERNEL_SRC) M=$(M) modules $(KBUILD_OPTIONS)
+
+modules_install:
+	$(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(M) modules_install
+
+%:
+	$(MAKE) -C $(KERNEL_SRC) M=$(M) $@ $(KBUILD_OPTIONS)
+
+clean:
+	rm -f *.o *.ko *.mod.c *.mod.o *~ .*.cmd Module.symvers
+	rm -rf .tmp_versions

+ 24 - 0
qcom/opensource/touch-drivers/Makefile.am

@@ -0,0 +1,24 @@
+
+TOUCH_ROOT=$(ROOTDIR)vendor/qcom/opensource/touch-drivers
+KBUILD_OPTIONS := TOUCH_ROOT=$(TOUCH_ROOT) CONFIG_MSM_TOUCH=m
+
+ifeq ($(TARGET_SUPPORT),genericarmv8)
+	KBUILD_OPTIONS += CONFIG_ARCH_WAIPIO=y
+endif
+
+ifeq ($(TARGET_SUPPORT),genericarmv8)
+	KBUILD_OPTIONS += CONFIG_ARCH_PINEAPPLE=y
+endif
+
+all:
+	$(MAKE) -C $(KERNEL_SRC) M=$(M) modules $(KBUILD_OPTIONS)
+
+modules_install:
+	$(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(M) modules_install
+
+%:
+	$(MAKE) -C $(KERNEL_SRC) M=$(M) $@ $(KBUILD_OPTIONS)
+
+clean:
+	rm -f *.o *.ko *.mod.c *.mod.o *~ .*.cmd Module.symvers
+	rm -rf .tmp_versions

+ 191 - 0
qcom/opensource/touch-drivers/NOTICE

@@ -0,0 +1,191 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2012-2019, The Linux Foundation. All rights reserved.
+*/
+
+/*
+ *
+ * Raydium TouchScreen driver.
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ * Qualcomm Innovation Center, Inc. chooses to use it under GPLv2
+ * Copyright (c) 2021  Raydium tech Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * BSD LICENSE
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name of Google Inc. or Linaro Ltd. nor the names of
+ *    its contributors may be used to endorse or promote products
+ *    derived from this software without specific prior written
+ *    permission.
+ * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ *
+ * Parade TouchScreen driver.
+ *
+ * Copyright (C) 2015-2020 Parade Technologies
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * Contact Parade Technologies at www.paradetech.com <[email protected]>
+ */
+
+/*
+ *
+ * FocalTech fts TouchScreen driver.
+ *
+ * Copyright (c) 2012-2019, Focaltech Ltd. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+/*
+ * Synaptics DSX touchscreen driver
+ *
+ * Copyright (C) 2012-2016 Synaptics Incorporated. All rights reserved.
+ *
+ * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
+ * Copyright (C) 2012 Alexandra Chin <[email protected]>
+ * Copyright (C) 2012 Scott Lin <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND SYNAPTICS
+ * EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING ANY
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE,
+ * AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS.
+ * IN NO EVENT SHALL SYNAPTICS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED
+ * AND BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF COMPETENT JURISDICTION DOES
+ * NOT PERMIT THE DISCLAIMER OF DIRECT DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS'
+ * TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S.
+ * DOLLARS.
+ */
+
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2010 - 2018 Novatek, Inc.
+ *
+ * $Revision: 47247 $
+ * $Date: 2019-07-10 10:41:36 +0800 (Wed, 10 Jul 2019) $
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * FTS Capacitive touch screen controller (FingerTipS)
+ *
+ * Copyright (C) 2016-2019, STMicroelectronics Limited.
+ * Authors: AMG(Analog Mems Group) <[email protected]>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Goodix Touchscreen Driver
+ * Copyright (C) 2020 - 2021 Goodix, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be a reference
+ * to you, when you are integrating the GOODiX's CTP IC into your system,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ */
+
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Atmel maXTouch Touchscreen driver
+ *
+ * Copyright (C) 2010 Samsung Electronics Co.Ltd
+ * Copyright (C) 2011-2014 Atmel Corporation
+ * Copyright (C) 2012 Google, Inc.
+ * Copyright (C) 2016 Zodiac Inflight Innovations
+ *
+ * Author: Joonyoung Shim <[email protected]>
+ */

+ 4014 - 0
qcom/opensource/touch-drivers/atmel_mxt/atmel_mxt_ts.c

@@ -0,0 +1,4014 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Atmel maXTouch Touchscreen driver
+ *
+ * Copyright (C) 2010 Samsung Electronics Co.Ltd
+ * Copyright (C) 2011-2014 Atmel Corporation
+ * Copyright (C) 2012 Google, Inc.
+ * Copyright (C) 2016 Zodiac Inflight Innovations
+ *
+ * Author: Joonyoung Shim <[email protected]>
+ */
+
+#include <linux/acpi.h>
+#include <linux/dmi.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/interrupt.h>
+#include <linux/regulator/consumer.h>
+#include <linux/irq.h>
+#include <linux/of.h>
+#include <linux/soc/qcom/panel_event_notifier.h>
+#include <linux/property.h>
+#include <linux/slab.h>
+#include <linux/gpio/consumer.h>
+#include <linux/property.h>
+#include <asm/unaligned.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-v4l2.h>
+#include <media/videobuf2-vmalloc.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/version.h>
+
+#ifdef CONFIG_DRM
+#include <drm/drm_panel.h>
+#endif
+
+/* Firmware files */
+#define MXT_FW_NAME		"maxtouch.fw"
+#define MXT_CFG_NAME		"maxtouch.cfg"
+#define MXT_CFG_MAGIC		"OBP_RAW V1"
+
+/* Registers */
+#define MXT_OBJECT_START	0x07
+#define MXT_OBJECT_SIZE		6
+#define MXT_INFO_CHECKSUM_SIZE	3
+#define MXT_MAX_BLOCK_WRITE	256
+
+/* Object types */
+#define MXT_DEBUG_DIAGNOSTIC_T37	37
+#define MXT_GEN_MESSAGE_T5		5
+#define MXT_GEN_COMMAND_T6		6
+#define MXT_GEN_POWER_T7		7
+#define MXT_GEN_ACQUIRE_T8		8
+#define MXT_GEN_DATASOURCE_T53		53
+#define MXT_TOUCH_MULTI_T9		9
+#define MXT_TOUCH_KEYARRAY_T15		15
+#define MXT_TOUCH_PROXIMITY_T23		23
+#define MXT_TOUCH_PROXKEY_T52		52
+#define MXT_PROCI_GRIPFACE_T20		20
+#define MXT_PROCG_NOISE_T22		22
+#define MXT_PROCI_ONETOUCH_T24		24
+#define MXT_PROCI_TWOTOUCH_T27		27
+#define MXT_PROCI_GRIP_T40		40
+#define MXT_PROCI_PALM_T41		41
+#define MXT_PROCI_TOUCHSUPPRESSION_T42	42
+#define MXT_PROCI_STYLUS_T47		47
+#define MXT_PROCG_NOISESUPPRESSION_T48	48
+#define MXT_SPT_COMMSCONFIG_T18		18
+#define MXT_SPT_GPIOPWM_T19		19
+#define MXT_SPT_SELFTEST_T25		25
+#define MXT_SPT_CTECONFIG_T28		28
+#define MXT_SPT_USERDATA_T38		38
+#define MXT_SPT_DIGITIZER_T43		43
+#define MXT_SPT_MESSAGECOUNT_T44	44
+#define MXT_SPT_CTECONFIG_T46		46
+#define MXT_SPT_DYNAMICCONFIGURATIONCONTAINER_T71 71
+#define MXT_TOUCH_MULTITOUCHSCREEN_T100 100
+
+/* Delay times */
+#define MXT_BACKUP_TIME		50	/* msec */
+#define MXT_RESET_TIME		200	/* msec */
+#define MXT_RESET_TIMEOUT	3000	/* msec */
+#define MXT_CRC_TIMEOUT		1000	/* msec */
+#define MXT_FW_RESET_TIME	3000	/* msec */
+#define MXT_FW_CHG_TIMEOUT	300	/* msec */
+#define MXT_WAKEUP_TIME		25	/* msec */
+#define MXT_REGULATOR_DELAY	150	/* msec */
+#define MXT_POWERON_DELAY	150	/* msec */
+
+/* recommended voltage specifications */
+#define MXT_VDD_VTG_MIN_UV	1800000
+#define MXT_VDD_VTG_MAX_UV	1800000
+#define MXT_AVDD_VTG_MIN_UV	3000000
+#define MXT_AVDD_VTG_MAX_UV	3300000
+#define MXT_XVDD_VTG_MIN_UV	2700000
+#define MXT_XVDD_VTG_MAX_UV	10000000
+
+/* recommended load specifications */
+#define MXT_ENABLE_LOAD		10000
+#define MXT_DISABLE_LOAD	0
+
+/* MXT_GEN_MESSAGE_T5 object */
+#define MXT_RPTID_NOMSG		0xff
+
+/* MXT_GEN_COMMAND_T6 field */
+#define MXT_COMMAND_RESET	0
+#define MXT_COMMAND_BACKUPNV	1
+#define MXT_COMMAND_CALIBRATE	2
+#define MXT_COMMAND_REPORTALL	3
+#define MXT_COMMAND_DIAGNOSTIC	5
+
+/* Define for T6 status byte */
+#define MXT_T6_STATUS_RESET	BIT(7)
+#define MXT_T6_STATUS_OFL	BIT(6)
+#define MXT_T6_STATUS_SIGERR	BIT(5)
+#define MXT_T6_STATUS_CAL	BIT(4)
+#define MXT_T6_STATUS_CFGERR	BIT(3)
+#define MXT_T6_STATUS_COMSERR	BIT(2)
+
+/* MXT_GEN_POWER_T7 field */
+struct t7_config {
+	u8 idle;
+	u8 active;
+} __packed;
+
+#define MXT_POWER_CFG_RUN		0
+#define MXT_POWER_CFG_DEEPSLEEP		1
+
+/* MXT_TOUCH_MULTI_T9 field */
+#define MXT_T9_CTRL		0
+#define MXT_T9_XSIZE		3
+#define MXT_T9_YSIZE		4
+#define MXT_T9_ORIENT		9
+#define MXT_T9_RANGE		18
+
+/* MXT_TOUCH_MULTI_T9 status */
+#define MXT_T9_UNGRIP		BIT(0)
+#define MXT_T9_SUPPRESS		BIT(1)
+#define MXT_T9_AMP		BIT(2)
+#define MXT_T9_VECTOR		BIT(3)
+#define MXT_T9_MOVE		BIT(4)
+#define MXT_T9_RELEASE		BIT(5)
+#define MXT_T9_PRESS		BIT(6)
+#define MXT_T9_DETECT		BIT(7)
+
+struct t9_range {
+	__le16 x;
+	__le16 y;
+} __packed;
+
+/* MXT_TOUCH_MULTI_T9 orient */
+#define MXT_T9_ORIENT_SWITCH	BIT(0)
+#define MXT_T9_ORIENT_INVERTX	BIT(1)
+#define MXT_T9_ORIENT_INVERTY	BIT(2)
+
+/* MXT_SPT_COMMSCONFIG_T18 */
+#define MXT_COMMS_CTRL		0
+#define MXT_COMMS_CMD		1
+#define MXT_COMMS_RETRIGEN	BIT(6)
+
+/* MXT_DEBUG_DIAGNOSTIC_T37 */
+#define MXT_DIAGNOSTIC_PAGEUP	0x01
+#define MXT_DIAGNOSTIC_DELTAS	0x10
+#define MXT_DIAGNOSTIC_REFS	0x11
+#define MXT_DIAGNOSTIC_SIZE	128
+
+#define MXT_FAMILY_1386			160
+#define MXT1386_COLUMNS			3
+#define MXT1386_PAGES_PER_COLUMN	8
+
+struct t37_debug {
+#ifdef CONFIG_TOUCHSCREEN_ATMEL_MXT_T37
+	u8 mode;
+	u8 page;
+	u8 data[MXT_DIAGNOSTIC_SIZE];
+#endif
+};
+
+/* Define for MXT_GEN_COMMAND_T6 */
+#define MXT_BOOT_VALUE		0xa5
+#define MXT_RESET_VALUE		0x01
+#define MXT_BACKUP_VALUE	0x55
+
+/* T100 Multiple Touch Touchscreen */
+#define MXT_T100_CTRL		0
+#define MXT_T100_CFG1		1
+#define MXT_T100_TCHAUX		3
+#define MXT_T100_XSIZE		9
+#define MXT_T100_XRANGE		13
+#define MXT_T100_YSIZE		20
+#define MXT_T100_YRANGE		24
+
+#define MXT_T100_CFG_SWITCHXY	BIT(5)
+#define MXT_T100_CFG_INVERTY	BIT(6)
+#define MXT_T100_CFG_INVERTX	BIT(7)
+
+#define MXT_T100_TCHAUX_VECT	BIT(0)
+#define MXT_T100_TCHAUX_AMPL	BIT(1)
+#define MXT_T100_TCHAUX_AREA	BIT(2)
+
+#define MXT_T100_DETECT		BIT(7)
+#define MXT_T100_TYPE_MASK	0x70
+
+enum t100_type {
+	MXT_T100_TYPE_FINGER		= 1,
+	MXT_T100_TYPE_PASSIVE_STYLUS	= 2,
+	MXT_T100_TYPE_HOVERING_FINGER	= 4,
+	MXT_T100_TYPE_GLOVE		= 5,
+	MXT_T100_TYPE_LARGE_TOUCH	= 6,
+};
+
+#define MXT_DISTANCE_ACTIVE_TOUCH	0
+#define MXT_DISTANCE_HOVERING		1
+
+#define MXT_TOUCH_MAJOR_DEFAULT		1
+#define MXT_PRESSURE_DEFAULT		1
+
+/* Delay times */
+#define MXT_BACKUP_TIME		50	/* msec */
+#define MXT_RESET_GPIO_TIME	20	/* msec */
+#define MXT_RESET_INVALID_CHG	100	/* msec */
+#define MXT_RESET_TIME		200	/* msec */
+#define MXT_RESET_TIMEOUT	3000	/* msec */
+#define MXT_CRC_TIMEOUT		1000	/* msec */
+#define MXT_FW_RESET_TIME	3000	/* msec */
+#define MXT_FW_CHG_TIMEOUT	300	/* msec */
+
+/* Command to unlock bootloader */
+#define MXT_UNLOCK_CMD_MSB	0xaa
+#define MXT_UNLOCK_CMD_LSB	0xdc
+
+/* Bootloader mode status */
+#define MXT_WAITING_BOOTLOAD_CMD	0xc0	/* valid 7 6 bit only */
+#define MXT_WAITING_FRAME_DATA	0x80	/* valid 7 6 bit only */
+#define MXT_FRAME_CRC_CHECK	0x02
+#define MXT_FRAME_CRC_FAIL	0x03
+#define MXT_FRAME_CRC_PASS	0x04
+#define MXT_APP_CRC_FAIL	0x40	/* valid 7 8 bit only */
+#define MXT_BOOT_STATUS_MASK	0x3f
+#define MXT_BOOT_EXTENDED_ID	BIT(5)
+#define MXT_BOOT_ID_MASK	0x1f
+
+/* Touchscreen absolute values */
+#define MXT_MAX_AREA		0xff
+
+#define MXT_PIXELS_PER_MM	20
+
+#define MXT_COORDS_ARR_SIZE	4
+
+struct mxt_info {
+	u8 family_id;
+	u8 variant_id;
+	u8 version;
+	u8 build;
+	u8 matrix_xsize;
+	u8 matrix_ysize;
+	u8 object_num;
+};
+
+struct mxt_object {
+	u8 type;
+	u16 start_address;
+	u8 size_minus_one;
+	u8 instances_minus_one;
+	u8 num_report_ids;
+} __packed;
+
+struct mxt_dbg {
+	u16 t37_address;
+	u16 diag_cmd_address;
+	struct t37_debug *t37_buf;
+	unsigned int t37_pages;
+	unsigned int t37_nodes;
+
+	struct v4l2_device v4l2;
+	struct v4l2_pix_format format;
+	struct video_device vdev;
+	struct vb2_queue queue;
+	struct mutex lock;
+	int input;
+};
+
+enum v4l_dbg_inputs {
+	MXT_V4L_INPUT_DELTAS,
+	MXT_V4L_INPUT_REFS,
+	MXT_V4L_INPUT_MAX,
+};
+
+enum mxt_suspend_mode {
+	MXT_SUSPEND_DEEP_SLEEP	= 0,
+	MXT_SUSPEND_T9_CTRL	= 1,
+};
+
+/* Config update context */
+struct mxt_cfg {
+	u8 *raw;
+	size_t raw_size;
+	off_t raw_pos;
+
+	u8 *mem;
+	size_t mem_size;
+	int start_ofs;
+
+	struct mxt_info info;
+};
+
+/* Each client has this additional data */
+struct mxt_data {
+	struct i2c_client *client;
+	struct input_dev *input_dev;
+	char phys[64];		/* device physical location */
+	struct mxt_object *object_table;
+	struct mxt_info *info;
+	void *raw_info_block;
+	unsigned int irq;
+	unsigned int max_x;
+	unsigned int max_y;
+	bool invertx;
+	bool inverty;
+	bool xy_switch;
+	u8 xsize;
+	u8 ysize;
+	bool in_bootloader;
+	u16 mem_size;
+	u8 t100_aux_ampl;
+	u8 t100_aux_area;
+	u8 t100_aux_vect;
+	u8 max_reportid;
+	u32 config_crc;
+	u32 info_crc;
+	u8 bootloader_addr;
+	u8 *msg_buf;
+	u8 *cmd_buf;
+	u8 *read_buf;
+	u8 t6_status;
+	bool update_input;
+	u8 last_message_count;
+	u8 num_touchids;
+	u8 multitouch;
+	struct t7_config t7_cfg;
+	struct mxt_dbg dbg;
+	struct gpio_desc *reset_gpio;
+	struct gpio_desc *irq_gpio;
+	bool use_retrigen_workaround;
+
+	/* Cached parameters from object table */
+	u16 T5_address;
+	u8 T5_msg_size;
+	u8 T6_reportid;
+	u16 T6_address;
+	u16 T7_address;
+	u16 T71_address;
+	u8 T9_reportid_min;
+	u8 T9_reportid_max;
+	u16 T18_address;
+	u8 T19_reportid;
+	u16 T44_address;
+	u8 T100_reportid_min;
+	u8 T100_reportid_max;
+
+	/* for fw update in bootloader */
+	struct completion bl_completion;
+
+	/* for reset handling */
+	struct completion reset_completion;
+
+	/* for config update handling */
+	struct completion crc_completion;
+
+	/* Enable reporting of input events */
+	bool enable_reporting;
+
+	/* Indicates whether device is in suspend */
+	bool suspended;
+
+	u32 *t19_keymap;
+	unsigned int t19_num_keys;
+
+	enum mxt_suspend_mode suspend_mode;
+
+	bool use_regulator;
+	struct regulator *reg_vdd;
+	struct regulator *reg_avdd;
+	struct regulator *reg_xvdd;
+	struct pinctrl *ts_pinctrl;
+	struct pinctrl_state *gpio_state_active;
+	struct pinctrl_state *gpio_state_suspend;
+
+	u32 panel_minx, panel_miny, panel_maxx, panel_maxy;
+	u32 disp_minx, disp_miny, disp_maxx, disp_maxy;
+	void *notifier_cookie;
+};
+
+struct mxt_vb2_buffer {
+	struct vb2_buffer	vb;
+	struct list_head	list;
+};
+
+static size_t mxt_obj_size(const struct mxt_object *obj)
+{
+	return obj->size_minus_one + 1;
+}
+
+static size_t mxt_obj_instances(const struct mxt_object *obj)
+{
+	return obj->instances_minus_one + 1;
+}
+
+static int mxt_regulator_configure(struct mxt_data *data, bool enable);
+
+static bool mxt_object_readable(unsigned int type)
+{
+	switch (type) {
+	case MXT_GEN_COMMAND_T6:
+	case MXT_GEN_POWER_T7:
+	case MXT_GEN_ACQUIRE_T8:
+	case MXT_GEN_DATASOURCE_T53:
+	case MXT_TOUCH_MULTI_T9:
+	case MXT_TOUCH_KEYARRAY_T15:
+	case MXT_TOUCH_PROXIMITY_T23:
+	case MXT_TOUCH_PROXKEY_T52:
+	case MXT_TOUCH_MULTITOUCHSCREEN_T100:
+	case MXT_PROCI_GRIPFACE_T20:
+	case MXT_PROCG_NOISE_T22:
+	case MXT_PROCI_ONETOUCH_T24:
+	case MXT_PROCI_TWOTOUCH_T27:
+	case MXT_PROCI_GRIP_T40:
+	case MXT_PROCI_PALM_T41:
+	case MXT_PROCI_TOUCHSUPPRESSION_T42:
+	case MXT_PROCI_STYLUS_T47:
+	case MXT_PROCG_NOISESUPPRESSION_T48:
+	case MXT_SPT_COMMSCONFIG_T18:
+	case MXT_SPT_GPIOPWM_T19:
+	case MXT_SPT_SELFTEST_T25:
+	case MXT_SPT_CTECONFIG_T28:
+	case MXT_SPT_USERDATA_T38:
+	case MXT_SPT_DIGITIZER_T43:
+	case MXT_SPT_CTECONFIG_T46:
+	case MXT_SPT_DYNAMICCONFIGURATIONCONTAINER_T71:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static void mxt_dump_message(struct mxt_data *data, u8 *message)
+{
+	dev_dbg(&data->client->dev, "message: %*ph\n",
+		data->T5_msg_size, message);
+}
+
+static int mxt_wait_for_completion(struct mxt_data *data,
+				   struct completion *comp,
+				   unsigned int timeout_ms)
+{
+	struct device *dev = &data->client->dev;
+	unsigned long timeout = msecs_to_jiffies(timeout_ms);
+	long ret;
+
+	ret = wait_for_completion_interruptible_timeout(comp, timeout);
+	if (ret < 0) {
+		return ret;
+	} else if (ret == 0) {
+		dev_err(dev, "Wait for completion timed out.\n");
+		return -ETIMEDOUT;
+	}
+	return 0;
+}
+
+static int mxt_bootloader_read(struct mxt_data *data,
+			       u8 *val, unsigned int count)
+{
+	int ret;
+	struct i2c_msg msg;
+
+	msg.addr = data->bootloader_addr;
+	msg.flags = data->client->flags & I2C_M_TEN;
+	msg.flags |= I2C_M_RD;
+	msg.len = count;
+	msg.buf = data->read_buf;
+
+	ret = i2c_transfer(data->client->adapter, &msg, 1);
+	if (ret == 1) {
+		memcpy(val, msg.buf, count);
+		ret = 0;
+	} else {
+		ret = ret < 0 ? ret : -EIO;
+		dev_err(&data->client->dev, "%s: i2c recv failed (%d)\n",
+			__func__, ret);
+	}
+
+	return ret;
+}
+
+static int mxt_bootloader_write(struct mxt_data *data,
+				const u8 * const val, unsigned int count)
+{
+	int ret;
+	struct i2c_msg msg;
+
+	msg.addr = data->bootloader_addr;
+	msg.flags = data->client->flags & I2C_M_TEN;
+	msg.len = count;
+	msg.buf = (u8 *)val;
+
+	ret = i2c_transfer(data->client->adapter, &msg, 1);
+	if (ret == 1) {
+		ret = 0;
+	} else {
+		ret = ret < 0 ? ret : -EIO;
+		dev_err(&data->client->dev, "%s: i2c send failed (%d)\n",
+			__func__, ret);
+	}
+
+	return ret;
+}
+
+static int mxt_lookup_bootloader_address(struct mxt_data *data, bool retry)
+{
+	u8 appmode = data->client->addr;
+	u8 bootloader;
+	u8 family_id = data->info ? data->info->family_id : 0;
+
+	switch (appmode) {
+	case 0x4a:
+	case 0x4b:
+		/* Chips after 1664S use different scheme */
+		if (retry || family_id >= 0xa2) {
+			bootloader = appmode - 0x24;
+			break;
+		}
+		fallthrough;	/* for normal case */
+	case 0x4c:
+	case 0x4d:
+	case 0x5a:
+	case 0x5b:
+		bootloader = appmode - 0x26;
+		break;
+
+	default:
+		dev_err(&data->client->dev,
+			"Appmode i2c address 0x%02x not found\n",
+			appmode);
+		return -EINVAL;
+	}
+
+	data->bootloader_addr = bootloader;
+	return 0;
+}
+
+static int mxt_probe_bootloader(struct mxt_data *data, bool alt_address)
+{
+	struct device *dev = &data->client->dev;
+	int error;
+	u8 val;
+	bool crc_failure;
+
+	error = mxt_lookup_bootloader_address(data, alt_address);
+	if (error)
+		return error;
+
+	error = mxt_bootloader_read(data, &val, 1);
+	if (error)
+		return error;
+
+	/* Check app crc fail mode */
+	crc_failure = (val & ~MXT_BOOT_STATUS_MASK) == MXT_APP_CRC_FAIL;
+
+	dev_err(dev, "Detected bootloader, status:%02X%s\n",
+			val, crc_failure ? ", APP_CRC_FAIL" : "");
+
+	return 0;
+}
+
+static u8 mxt_get_bootloader_version(struct mxt_data *data, u8 val)
+{
+	struct device *dev = &data->client->dev;
+	u8 ver = 0;
+	u8 *buf = kzalloc(4, GFP_KERNEL);
+
+	if ((val & MXT_BOOT_EXTENDED_ID) && buf) {
+		if (mxt_bootloader_read(data, buf, 3) != 0) {
+			dev_err(dev, "%s: i2c failure\n", __func__);
+			return val;
+		}
+
+		dev_dbg(dev, "Bootloader ID:%d Version:%d\n",
+				*(buf + 1), *(buf + 2));
+		ver = *(buf + 0);
+		kfree(buf);
+		return ver;
+	} else {
+		dev_dbg(dev, "Bootloader ID:%d\n", val & MXT_BOOT_ID_MASK);
+
+		return val;
+	}
+}
+
+static int mxt_check_bootloader(struct mxt_data *data, unsigned int state,
+				bool wait)
+{
+	struct device *dev = &data->client->dev;
+	u8 val;
+	int ret;
+
+recheck:
+	if (wait) {
+		/*
+		 * In application update mode, the interrupt
+		 * line signals state transitions. We must wait for the
+		 * CHG assertion before reading the status byte.
+		 * Once the status byte has been read, the line is deasserted.
+		 */
+		ret = mxt_wait_for_completion(data, &data->bl_completion,
+					      MXT_FW_CHG_TIMEOUT);
+		if (ret) {
+			/*
+			 * TODO: handle -ERESTARTSYS better by terminating
+			 * fw update process before returning to userspace
+			 * by writing length 0x000 to device (iff we are in
+			 * WAITING_FRAME_DATA state).
+			 */
+			dev_err(dev, "Update wait error %d\n", ret);
+			return ret;
+		}
+	}
+
+	ret = mxt_bootloader_read(data, &val, 1);
+	if (ret)
+		return ret;
+
+	if (state == MXT_WAITING_BOOTLOAD_CMD)
+		val = mxt_get_bootloader_version(data, val);
+
+	switch (state) {
+	case MXT_WAITING_BOOTLOAD_CMD:
+	case MXT_WAITING_FRAME_DATA:
+	case MXT_APP_CRC_FAIL:
+		val &= ~MXT_BOOT_STATUS_MASK;
+		break;
+	case MXT_FRAME_CRC_PASS:
+		if (val == MXT_FRAME_CRC_CHECK) {
+			goto recheck;
+		} else if (val == MXT_FRAME_CRC_FAIL) {
+			dev_err(dev, "Bootloader CRC fail\n");
+			return -EINVAL;
+		}
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (val != state) {
+		dev_err(dev, "Invalid bootloader state %02X != %02X\n",
+			val, state);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int mxt_send_bootloader_cmd(struct mxt_data *data, bool unlock)
+{
+	int ret;
+	u8 *buf = data->cmd_buf;
+
+	if (unlock) {
+		*buf = MXT_UNLOCK_CMD_LSB;
+		*(buf + 1) = MXT_UNLOCK_CMD_MSB;
+	} else {
+		*buf = 0x01;
+		*(buf + 1) = 0x01;
+	}
+
+	ret = mxt_bootloader_write(data, buf, 2);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int __mxt_read_reg(struct mxt_data *data, u16 reg, u16 len, void *val)
+{
+	struct i2c_msg xfer[2];
+	int ret;
+	u8 *buf = data->cmd_buf;
+	struct i2c_client *client = data->client;
+
+	*buf = reg & 0xff;
+	*(buf + 1) = (reg >> 8) & 0xff;
+
+	/* Write register */
+	xfer[0].addr = client->addr;
+	xfer[0].flags = 0;
+	xfer[0].len = 2;
+	xfer[0].buf = buf;
+
+	/* Read data */
+	xfer[1].addr = client->addr;
+	xfer[1].flags = I2C_M_RD;
+	xfer[1].len = len;
+	xfer[1].buf = val;
+
+	ret = i2c_transfer(client->adapter, xfer, 2);
+	if (ret == 2) {
+		ret = 0;
+	} else {
+		if (ret >= 0)
+			ret = -EIO;
+		dev_err(&client->dev, "%s: i2c transfer failed (%d)\n",
+			__func__, ret);
+	}
+
+	return ret;
+}
+
+static int __mxt_write_reg(struct i2c_client *client, u16 reg, u16 len,
+			   const void *val)
+{
+	u8 *buf;
+	size_t count;
+	int ret;
+
+	count = len + 2;
+	buf = kmalloc(count, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	buf[0] = reg & 0xff;
+	buf[1] = (reg >> 8) & 0xff;
+	memcpy(&buf[2], val, len);
+
+	ret = i2c_master_send(client, buf, count);
+	if (ret == count) {
+		ret = 0;
+	} else {
+		if (ret >= 0)
+			ret = -EIO;
+		dev_dbg(&client->dev, "%s: i2c send failed (%d)\n",
+			__func__, ret);
+	}
+
+	kfree(buf);
+	return ret;
+}
+
+static int mxt_write_reg(struct i2c_client *client, u16 reg, u8 val)
+{
+	return __mxt_write_reg(client, reg, 1, &val);
+}
+
+static struct mxt_object *
+mxt_get_object(struct mxt_data *data, u8 type)
+{
+	struct mxt_object *object;
+	int i;
+
+	for (i = 0; i < data->info->object_num; i++) {
+		object = data->object_table + i;
+		if (object->type == type)
+			return object;
+	}
+
+	dev_warn(&data->client->dev, "Invalid object type T%u\n", type);
+	return NULL;
+}
+
+static void mxt_proc_t6_messages(struct mxt_data *data, u8 *msg)
+{
+	struct device *dev = &data->client->dev;
+	u8 status = msg[1];
+	u32 crc = msg[2] | (msg[3] << 8) | (msg[4] << 16);
+
+	if (crc != data->config_crc) {
+		data->config_crc = crc;
+		dev_dbg(dev, "T6 Config Checksum: 0x%06X\n", crc);
+	}
+
+	complete(&data->crc_completion);
+
+	/* Detect reset */
+	if (status & MXT_T6_STATUS_RESET)
+		complete(&data->reset_completion);
+
+	/* Output debug if status has changed */
+	if (status != data->t6_status)
+		dev_dbg(dev, "T6 Status 0x%02X%s%s%s%s%s%s%s\n",
+			status,
+			status == 0 ? " OK" : "",
+			status & MXT_T6_STATUS_RESET ? " RESET" : "",
+			status & MXT_T6_STATUS_OFL ? " OFL" : "",
+			status & MXT_T6_STATUS_SIGERR ? " SIGERR" : "",
+			status & MXT_T6_STATUS_CAL ? " CAL" : "",
+			status & MXT_T6_STATUS_CFGERR ? " CFGERR" : "",
+			status & MXT_T6_STATUS_COMSERR ? " COMSERR" : "");
+
+	/* Save current status */
+	data->t6_status = status;
+}
+
+static int mxt_write_object(struct mxt_data *data,
+				 u8 type, u8 offset, u8 val)
+{
+	struct mxt_object *object;
+	u16 reg;
+
+	object = mxt_get_object(data, type);
+	if (!object || offset >= mxt_obj_size(object))
+		return -EINVAL;
+
+	reg = object->start_address;
+	return mxt_write_reg(data->client, reg + offset, val);
+}
+
+static void mxt_input_button(struct mxt_data *data, u8 *message)
+{
+	struct input_dev *input = data->input_dev;
+	int i;
+
+	for (i = 0; i < data->t19_num_keys; i++) {
+		if (data->t19_keymap[i] == KEY_RESERVED)
+			continue;
+
+		/* Active-low switch */
+		input_report_key(input, data->t19_keymap[i],
+				 !(message[1] & BIT(i)));
+	}
+}
+
+static void mxt_input_sync(struct mxt_data *data)
+{
+	input_mt_report_pointer_emulation(data->input_dev,
+					  data->t19_num_keys);
+	input_sync(data->input_dev);
+}
+
+static void mxt_proc_t9_message(struct mxt_data *data, u8 *message)
+{
+	struct device *dev = &data->client->dev;
+	struct input_dev *input_dev = data->input_dev;
+	int id;
+	u8 status;
+	int x;
+	int y;
+	int area;
+	int amplitude;
+
+	id = message[0] - data->T9_reportid_min;
+	status = message[1];
+	x = (message[2] << 4) | ((message[4] >> 4) & 0xf);
+	y = (message[3] << 4) | ((message[4] & 0xf));
+
+	/* Handle 10/12 bit switching */
+	if (data->max_x < 1024)
+		x >>= 2;
+	if (data->max_y < 1024)
+		y >>= 2;
+
+	area = message[5];
+	amplitude = message[6];
+
+	dev_dbg(dev,
+		"[%u] %c%c%c%c%c%c%c%c x: %5u y: %5u area: %3u amp: %3u\n",
+		id,
+		(status & MXT_T9_DETECT) ? 'D' : '.',
+		(status & MXT_T9_PRESS) ? 'P' : '.',
+		(status & MXT_T9_RELEASE) ? 'R' : '.',
+		(status & MXT_T9_MOVE) ? 'M' : '.',
+		(status & MXT_T9_VECTOR) ? 'V' : '.',
+		(status & MXT_T9_AMP) ? 'A' : '.',
+		(status & MXT_T9_SUPPRESS) ? 'S' : '.',
+		(status & MXT_T9_UNGRIP) ? 'U' : '.',
+		x, y, area, amplitude);
+
+	input_mt_slot(input_dev, id);
+
+	if (status & MXT_T9_DETECT) {
+		/*
+		 * Multiple bits may be set if the host is slow to read
+		 * the status messages, indicating all the events that
+		 * have happened.
+		 */
+		if (status & MXT_T9_RELEASE) {
+			input_mt_report_slot_inactive(input_dev);
+			mxt_input_sync(data);
+		}
+
+		/* if active, pressure must be non-zero */
+		if (!amplitude)
+			amplitude = MXT_PRESSURE_DEFAULT;
+
+		/* Handle orientation */
+		if (data->xy_switch)
+			swap(x, y);
+		if (data->invertx)
+			x = data->max_x - x;
+		if (data->inverty)
+			y = data->max_y - y;
+
+		/* Touch active */
+		input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, 1);
+		input_report_abs(input_dev, ABS_MT_POSITION_X,
+			x - data->disp_minx);
+		input_report_abs(input_dev, ABS_MT_POSITION_Y,
+			y - data->disp_miny);
+		input_report_abs(input_dev, ABS_MT_PRESSURE, amplitude);
+		input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, area);
+	} else {
+		/* Touch no longer active, close out slot */
+		input_mt_report_slot_inactive(input_dev);
+	}
+
+	data->update_input = true;
+}
+
+static void mxt_proc_t100_message(struct mxt_data *data, u8 *message)
+{
+	struct device *dev = &data->client->dev;
+	struct input_dev *input_dev = data->input_dev;
+	int id;
+	u8 status;
+	u8 type = 0;
+	u16 x;
+	u16 y;
+	int distance = 0;
+	int tool = 0;
+	u8 major = 0;
+	u8 pressure = 0;
+	u8 orientation = 0;
+
+	id = message[0] - data->T100_reportid_min - 2;
+
+	/* ignore SCRSTATUS events */
+	if (id < 0)
+		return;
+
+	status = message[1];
+	x = get_unaligned_le16(&message[2]);
+	y = get_unaligned_le16(&message[4]);
+
+	if (status & MXT_T100_DETECT) {
+		type = (status & MXT_T100_TYPE_MASK) >> 4;
+
+		switch (type) {
+		case MXT_T100_TYPE_HOVERING_FINGER:
+			tool = MT_TOOL_FINGER;
+			distance = MXT_DISTANCE_HOVERING;
+
+			if (data->t100_aux_vect)
+				orientation = message[data->t100_aux_vect];
+
+			break;
+
+		case MXT_T100_TYPE_FINGER:
+		case MXT_T100_TYPE_GLOVE:
+			tool = MT_TOOL_FINGER;
+			distance = MXT_DISTANCE_ACTIVE_TOUCH;
+
+			if (data->t100_aux_area)
+				major = message[data->t100_aux_area];
+
+			if (data->t100_aux_ampl)
+				pressure = message[data->t100_aux_ampl];
+
+			if (data->t100_aux_vect)
+				orientation = message[data->t100_aux_vect];
+
+			break;
+
+		case MXT_T100_TYPE_PASSIVE_STYLUS:
+			tool = MT_TOOL_PEN;
+
+			/*
+			 * Passive stylus is reported with size zero so
+			 * hardcode.
+			 */
+			major = MXT_TOUCH_MAJOR_DEFAULT;
+
+			if (data->t100_aux_ampl)
+				pressure = message[data->t100_aux_ampl];
+
+			break;
+
+		case MXT_T100_TYPE_LARGE_TOUCH:
+			/* Ignore suppressed touch */
+			break;
+
+		default:
+			dev_dbg(dev, "Unexpected T100 type\n");
+			return;
+		}
+	}
+
+	/*
+	 * Values reported should be non-zero if tool is touching the
+	 * device
+	 */
+	if (!pressure && type != MXT_T100_TYPE_HOVERING_FINGER)
+		pressure = MXT_PRESSURE_DEFAULT;
+
+	input_mt_slot(input_dev, id);
+
+	if (status & MXT_T100_DETECT) {
+		dev_dbg(dev, "[%u] type:%u x:%u y:%u a:%02X p:%02X v:%02X\n",
+			id, type, x, y, major, pressure, orientation);
+
+		/* Handle orientation */
+		if (data->xy_switch)
+			swap(x, y);
+		if (data->invertx)
+			x = data->max_x - x;
+		if (data->inverty)
+			y = data->max_y - y;
+
+		input_mt_report_slot_state(input_dev, tool, 1);
+		input_report_abs(input_dev, ABS_MT_POSITION_X,
+			x - data->disp_minx);
+		input_report_abs(input_dev, ABS_MT_POSITION_Y,
+			y - data->disp_miny);
+		input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, major);
+		input_report_abs(input_dev, ABS_MT_PRESSURE, pressure);
+		input_report_abs(input_dev, ABS_MT_DISTANCE, distance);
+		input_report_abs(input_dev, ABS_MT_ORIENTATION, orientation);
+	} else {
+		dev_dbg(dev, "[%u] release\n", id);
+
+		/* close out slot */
+		input_mt_report_slot_inactive(input_dev);
+	}
+
+	data->update_input = true;
+}
+
+static int mxt_proc_message(struct mxt_data *data, u8 *message)
+{
+	u8 report_id = message[0];
+
+	if (report_id == MXT_RPTID_NOMSG)
+		return 0;
+
+	if (report_id == data->T6_reportid) {
+		mxt_proc_t6_messages(data, message);
+	} else if (!data->input_dev) {
+		/*
+		 * Do not report events if input device
+		 * is not yet registered.
+		 */
+		mxt_dump_message(data, message);
+	} else if (report_id >= data->T9_reportid_min &&
+		   report_id <= data->T9_reportid_max) {
+		mxt_proc_t9_message(data, message);
+	} else if (report_id >= data->T100_reportid_min &&
+		   report_id <= data->T100_reportid_max) {
+		mxt_proc_t100_message(data, message);
+	} else if (report_id == data->T19_reportid) {
+		mxt_input_button(data, message);
+		data->update_input = true;
+	} else {
+		mxt_dump_message(data, message);
+	}
+
+	return 1;
+}
+
+static int mxt_read_and_process_messages(struct mxt_data *data, u8 count)
+{
+	struct device *dev = &data->client->dev;
+	int ret;
+	int i;
+	u8 num_valid = 0;
+
+	/* Safety check for msg_buf */
+	if (count > data->max_reportid)
+		return -EINVAL;
+
+	/* Process remaining messages if necessary */
+	ret = __mxt_read_reg(data, data->T5_address,
+				data->T5_msg_size * count, data->msg_buf);
+	if (ret) {
+		dev_err(dev, "Failed to read %u messages (%d)\n", count, ret);
+		return ret;
+	}
+
+	for (i = 0;  i < count; i++) {
+		ret = mxt_proc_message(data,
+			data->msg_buf + data->T5_msg_size * i);
+
+		if (ret == 1)
+			num_valid++;
+	}
+
+	/* return number of messages read */
+	return num_valid;
+}
+
+static irqreturn_t mxt_process_messages_t44(struct mxt_data *data)
+{
+	struct device *dev = &data->client->dev;
+	int ret;
+	u8 count, num_left;
+
+	/* Read T44 and T5 together */
+	ret = __mxt_read_reg(data, data->T44_address,
+		data->T5_msg_size + 1, data->msg_buf);
+	if (ret) {
+		dev_err(dev, "Failed to read T44 and T5 (%d)\n", ret);
+		return IRQ_NONE;
+	}
+
+	count = data->msg_buf[0];
+
+	/*
+	 * This condition may be caused by the CHG line being configured in
+	 * Mode 0. It results in unnecessary I2C operations but it is benign.
+	 */
+	if (count == 0)
+		return IRQ_NONE;
+
+	if (count > data->max_reportid) {
+		dev_warn(dev, "T44 count %d exceeded max report id\n", count);
+		count = data->max_reportid;
+	}
+
+	/* Process first message */
+	ret = mxt_proc_message(data, data->msg_buf + 1);
+	if (ret < 0) {
+		dev_warn(dev, "Unexpected invalid message\n");
+		return IRQ_NONE;
+	}
+
+	num_left = count - 1;
+
+	/* Process remaining messages if necessary */
+	if (num_left) {
+		ret = mxt_read_and_process_messages(data, num_left);
+		if (ret < 0)
+			goto end;
+		else if (ret != num_left)
+			dev_warn(dev, "Unexpected invalid message\n");
+	}
+
+end:
+	if (data->update_input) {
+		mxt_input_sync(data);
+		data->update_input = false;
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int mxt_process_messages_until_invalid(struct mxt_data *data)
+{
+	struct device *dev = &data->client->dev;
+	int count, read;
+	u8 tries = 2;
+
+	count = data->max_reportid;
+
+	/* Read messages until we force an invalid */
+	do {
+		read = mxt_read_and_process_messages(data, count);
+		if (read < count)
+			return 0;
+	} while (--tries);
+
+	if (data->update_input) {
+		mxt_input_sync(data);
+		data->update_input = false;
+	}
+
+	dev_err(dev, "CHG pin isn't cleared\n");
+	return -EBUSY;
+}
+
+static irqreturn_t mxt_process_messages(struct mxt_data *data)
+{
+	int total_handled, num_handled;
+	u8 count = data->last_message_count;
+
+	if (count < 1 || count > data->max_reportid)
+		count = 1;
+
+	/* include final invalid message */
+	total_handled = mxt_read_and_process_messages(data, count + 1);
+	if (total_handled < 0)
+		return IRQ_NONE;
+	/* if there were invalid messages, then we are done */
+	else if (total_handled <= count)
+		goto update_count;
+
+	/* keep reading two msgs until one is invalid or reportid limit */
+	do {
+		num_handled = mxt_read_and_process_messages(data, 2);
+		if (num_handled < 0)
+			return IRQ_NONE;
+
+		total_handled += num_handled;
+
+		if (num_handled < 2)
+			break;
+	} while (total_handled < data->num_touchids);
+
+update_count:
+	data->last_message_count = total_handled;
+
+	if (data->update_input) {
+		mxt_input_sync(data);
+		data->update_input = false;
+	}
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t mxt_interrupt(int irq, void *dev_id)
+{
+	struct mxt_data *data = dev_id;
+
+	if (data->in_bootloader) {
+		/* bootloader state transition completion */
+		complete(&data->bl_completion);
+		return IRQ_HANDLED;
+	}
+
+	if (!data->object_table)
+		return IRQ_HANDLED;
+
+	if (data->T44_address) {
+		return mxt_process_messages_t44(data);
+	} else {
+		return mxt_process_messages(data);
+	}
+}
+
+static int mxt_t6_command(struct mxt_data *data, u16 cmd_offset,
+			  u8 value, bool wait)
+{
+	u16 reg;
+	u8 command_register;
+	int timeout_counter = 0;
+	int ret;
+
+	reg = data->T6_address + cmd_offset;
+
+	ret = mxt_write_reg(data->client, reg, value);
+	if (ret)
+		return ret;
+
+	if (!wait)
+		return 0;
+
+	do {
+		msleep(20);
+		ret = __mxt_read_reg(data, reg, 1, data->read_buf);
+		if (ret)
+			return ret;
+		command_register = *(data->read_buf);
+	} while (command_register != 0 && timeout_counter++ <= 100);
+
+	if (timeout_counter > 100) {
+		dev_err(&data->client->dev, "Command failed!\n");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int mxt_acquire_irq(struct mxt_data *data)
+{
+	int error;
+
+	enable_irq(data->irq);
+
+	if (data->use_retrigen_workaround) {
+		error = mxt_process_messages_until_invalid(data);
+		if (error)
+			return error;
+	}
+
+	return 0;
+}
+
+static int mxt_soft_reset(struct mxt_data *data)
+{
+	struct device *dev = &data->client->dev;
+	int ret = 0;
+
+	dev_dbg(dev, "Resetting device\n");
+
+	disable_irq(data->irq);
+
+	reinit_completion(&data->reset_completion);
+
+	ret = mxt_t6_command(data, MXT_COMMAND_RESET, MXT_RESET_VALUE, false);
+	if (ret)
+		return ret;
+
+	/* Ignore CHG line for 100ms after reset */
+	msleep(MXT_RESET_INVALID_CHG);
+
+	mxt_acquire_irq(data);
+
+	ret = mxt_wait_for_completion(data, &data->reset_completion,
+				      MXT_RESET_TIMEOUT);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static void mxt_update_crc(struct mxt_data *data, u8 cmd, u8 value)
+{
+	/*
+	 * On failure, CRC is set to 0 and config will always be
+	 * downloaded.
+	 */
+	data->config_crc = 0;
+	reinit_completion(&data->crc_completion);
+
+	mxt_t6_command(data, cmd, value, true);
+
+	/*
+	 * Wait for crc message. On failure, CRC is set to 0 and config will
+	 * always be downloaded.
+	 */
+	mxt_wait_for_completion(data, &data->crc_completion, MXT_CRC_TIMEOUT);
+}
+
+static void mxt_calc_crc24(u32 *crc, u8 firstbyte, u8 secondbyte)
+{
+	static const unsigned int crcpoly = 0x80001B;
+	u32 result;
+	u32 data_word;
+
+	data_word = (secondbyte << 8) | firstbyte;
+	result = ((*crc << 1) ^ data_word);
+
+	if (result & 0x1000000)
+		result ^= crcpoly;
+
+	*crc = result;
+}
+
+static u32 mxt_calculate_crc(u8 *base, off_t start_off, off_t end_off)
+{
+	u32 crc = 0;
+	u8 *ptr = base + start_off;
+	u8 *last_val = base + end_off - 1;
+
+	if (end_off < start_off)
+		return -EINVAL;
+
+	while (ptr < last_val) {
+		mxt_calc_crc24(&crc, *ptr, *(ptr + 1));
+		ptr += 2;
+	}
+
+	/* if len is odd, fill the last byte with 0 */
+	if (ptr == last_val)
+		mxt_calc_crc24(&crc, *ptr, 0);
+
+	/* Mask to 24-bit */
+	crc &= 0x00FFFFFF;
+
+	return crc;
+}
+
+static int mxt_check_retrigen(struct mxt_data *data)
+{
+	struct i2c_client *client = data->client;
+	int error;
+	int val = 0;
+	struct irq_data *irqd;
+
+	data->use_retrigen_workaround = false;
+
+	irqd = irq_get_irq_data(data->irq);
+	if (!irqd)
+		return -EINVAL;
+
+	if (irqd_is_level_type(irqd))
+		return 0;
+
+	if (data->T18_address) {
+		error = __mxt_read_reg(data,
+				       data->T18_address + MXT_COMMS_CTRL,
+				       1, &val);
+		if (error)
+			return error;
+
+		if (val & MXT_COMMS_RETRIGEN)
+			return 0;
+	}
+
+	dev_warn(&client->dev, "Enabling RETRIGEN workaround\n");
+	data->use_retrigen_workaround = true;
+	return 0;
+}
+
+static int mxt_prepare_cfg_mem(struct mxt_data *data, struct mxt_cfg *cfg)
+{
+	struct device *dev = &data->client->dev;
+	struct mxt_object *object;
+	unsigned int type, instance, size, byte_offset;
+	int offset;
+	int ret;
+	int i;
+	u16 reg;
+	u8 val;
+
+	while (cfg->raw_pos < cfg->raw_size) {
+		/* Read type, instance, length */
+		ret = sscanf(cfg->raw + cfg->raw_pos, "%x %x %x%n",
+			     &type, &instance, &size, &offset);
+		if (ret == 0) {
+			/* EOF */
+			break;
+		} else if (ret != 3) {
+			dev_err(dev, "Bad format: failed to parse object\n");
+			return -EINVAL;
+		}
+
+		if (cfg->raw_pos + offset >= cfg->raw_size)
+			return -EINVAL;
+
+		cfg->raw_pos += offset;
+
+		object = mxt_get_object(data, type);
+		if (!object) {
+			/* Skip object */
+			for (i = 0; i < size; i++) {
+				ret = sscanf(cfg->raw + cfg->raw_pos, "%hhx%n",
+					     &val, &offset);
+				if (ret != 1 || (cfg->raw_pos + offset >=
+					cfg->raw_size)) {
+					dev_err(dev, "Bad format in T%d at %d\n",
+						type, i);
+					return -EINVAL;
+				}
+				cfg->raw_pos += offset;
+
+			}
+			continue;
+		}
+
+		if (size > mxt_obj_size(object)) {
+			/*
+			 * Either we are in fallback mode due to wrong
+			 * config or config from a later fw version,
+			 * or the file is corrupt or hand-edited.
+			 */
+			dev_warn(dev, "Discarding %zu byte(s) in T%u\n",
+				 size - mxt_obj_size(object), type);
+		} else if (mxt_obj_size(object) > size) {
+			/*
+			 * If firmware is upgraded, new bytes may be added to
+			 * end of objects. It is generally forward compatible
+			 * to zero these bytes - previous behaviour will be
+			 * retained. However this does invalidate the CRC and
+			 * will force fallback mode until the configuration is
+			 * updated. We warn here but do nothing else - the
+			 * malloc has zeroed the entire configuration.
+			 */
+			dev_warn(dev, "Zeroing %zu byte(s) in T%d\n",
+				 mxt_obj_size(object) - size, type);
+		}
+
+		if (instance >= mxt_obj_instances(object)) {
+			dev_err(dev, "Object instances exceeded!\n");
+			return -EINVAL;
+		}
+
+		reg = object->start_address + mxt_obj_size(object) * instance;
+
+		for (i = 0; i < size; i++) {
+			ret = sscanf(cfg->raw + cfg->raw_pos, "%hhx%n",
+				     &val,
+				     &offset);
+			if (ret != 1 ||
+				(cfg->raw_pos + offset >= cfg->raw_size)) {
+				dev_err(dev, "Bad format in T%d at %d\n",
+					type, i);
+				return -EINVAL;
+			}
+			cfg->raw_pos += offset;
+
+			if (i > mxt_obj_size(object))
+				continue;
+
+			byte_offset = reg + i - cfg->start_ofs;
+
+			if (byte_offset >= 0 && byte_offset < cfg->mem_size) {
+				*(cfg->mem + byte_offset) = val;
+			} else {
+				dev_err(dev, "Bad object: reg:%d, T%d, ofs=%d\n",
+					reg, object->type, byte_offset);
+				return -EINVAL;
+			}
+		}
+	}
+
+	return 0;
+}
+
+static int mxt_upload_cfg_mem(struct mxt_data *data, struct mxt_cfg *cfg)
+{
+	unsigned int byte_offset = 0;
+	int error;
+
+	/* Write configuration as blocks */
+	while (byte_offset < cfg->mem_size) {
+		unsigned int size = cfg->mem_size - byte_offset;
+
+		if (size > MXT_MAX_BLOCK_WRITE)
+			size = MXT_MAX_BLOCK_WRITE;
+
+		error = __mxt_write_reg(data->client,
+					cfg->start_ofs + byte_offset,
+					size, cfg->mem + byte_offset);
+		if (error) {
+			dev_err(&data->client->dev,
+				"Config write error, ret=%d\n", error);
+			return error;
+		}
+
+		byte_offset += size;
+	}
+
+	return 0;
+}
+
+static int mxt_init_t7_power_cfg(struct mxt_data *data);
+
+/*
+ * mxt_update_cfg - download configuration to chip
+ *
+ * Atmel Raw Config File Format
+ *
+ * The first four lines of the raw config file contain:
+ *  1) Version
+ *  2) Chip ID Information (first 7 bytes of device memory)
+ *  3) Chip Information Block 24-bit CRC Checksum
+ *  4) Chip Configuration 24-bit CRC Checksum
+ *
+ * The rest of the file consists of one line per object instance:
+ *   <TYPE> <INSTANCE> <SIZE> <CONTENTS>
+ *
+ *   <TYPE> - 2-byte object type as hex
+ *   <INSTANCE> - 2-byte object instance number as hex
+ *   <SIZE> - 2-byte object size as hex
+ *   <CONTENTS> - array of <SIZE> 1-byte hex values
+ */
+static int mxt_update_cfg(struct mxt_data *data, const struct firmware *fw)
+{
+	struct device *dev = &data->client->dev;
+	struct mxt_cfg cfg;
+	int ret;
+	int offset;
+	int i;
+	u32 info_crc, config_crc, calculated_crc;
+	u16 crc_start = 0;
+
+	/* Make zero terminated copy of the OBP_RAW file */
+	cfg.raw = kmemdup_nul(fw->data, fw->size, GFP_KERNEL);
+	if (!cfg.raw)
+		return -ENOMEM;
+
+	cfg.raw_size = fw->size;
+
+	mxt_update_crc(data, MXT_COMMAND_REPORTALL, 1);
+
+	if (memcmp(cfg.raw, MXT_CFG_MAGIC, strlen(MXT_CFG_MAGIC))) {
+		dev_err(dev, "Unrecognised config file\n");
+		ret = -EINVAL;
+		goto release_raw;
+	}
+
+	cfg.raw_pos = strlen(MXT_CFG_MAGIC);
+
+	/* Load information block and check */
+	for (i = 0; i < sizeof(struct mxt_info); i++) {
+		ret = sscanf(cfg.raw + cfg.raw_pos, "%hhx%n",
+			     (unsigned char *)&cfg.info + i,
+			     &offset);
+		if (ret != 1 || (cfg.raw_pos + offset >= cfg.raw_size)) {
+			dev_err(dev, "Bad format\n");
+			ret = -EINVAL;
+			goto release_raw;
+		}
+
+		cfg.raw_pos += offset;
+	}
+
+	if (cfg.info.family_id != data->info->family_id) {
+		dev_err(dev, "Family ID mismatch!\n");
+		ret = -EINVAL;
+		goto release_raw;
+	}
+
+	if (cfg.info.variant_id != data->info->variant_id) {
+		dev_err(dev, "Variant ID mismatch!\n");
+		ret = -EINVAL;
+		goto release_raw;
+	}
+
+	/* Read CRCs */
+	ret = sscanf(cfg.raw + cfg.raw_pos, "%x%n", &info_crc, &offset);
+	if (ret != 1 || (cfg.raw_pos + offset >= cfg.raw_size)) {
+		dev_err(dev, "Bad format: failed to parse Info CRC\n");
+		ret = -EINVAL;
+		goto release_raw;
+	}
+	cfg.raw_pos += offset;
+
+	ret = sscanf(cfg.raw + cfg.raw_pos, "%x%n", &config_crc, &offset);
+	if (ret != 1 || (cfg.raw_pos + offset >= cfg.raw_size)) {
+		dev_err(dev, "Bad format: failed to parse Config CRC\n");
+		ret = -EINVAL;
+		goto release_raw;
+	}
+	cfg.raw_pos += offset;
+
+	/*
+	 * The Info Block CRC is calculated over mxt_info and the object
+	 * table. If it does not match then we are trying to load the
+	 * configuration from a different chip or firmware version, so
+	 * the configuration CRC is invalid anyway.
+	 */
+	if (info_crc == data->info_crc) {
+		if (config_crc == 0 || data->config_crc == 0) {
+			dev_info(dev, "CRC zero, attempting to apply config\n");
+		} else if (config_crc == data->config_crc) {
+			dev_dbg(dev, "Config CRC 0x%06X: OK\n",
+				 data->config_crc);
+			ret = 0;
+			goto release_raw;
+		} else {
+			dev_info(dev, "Config CRC 0x%06X: does not match file 0x%06X\n",
+				 data->config_crc, config_crc);
+		}
+	} else {
+		dev_warn(dev,
+			 "Warning: Info CRC error - device=0x%06X file=0x%06X\n",
+			 data->info_crc, info_crc);
+	}
+
+	/* Malloc memory to store configuration */
+	cfg.start_ofs = MXT_OBJECT_START +
+			data->info->object_num * sizeof(struct mxt_object) +
+			MXT_INFO_CHECKSUM_SIZE;
+	cfg.mem_size = data->mem_size - cfg.start_ofs;
+	cfg.mem = kzalloc(cfg.mem_size, GFP_KERNEL);
+	if (!cfg.mem) {
+		ret = -ENOMEM;
+		goto release_raw;
+	}
+
+	ret = mxt_prepare_cfg_mem(data, &cfg);
+	if (ret)
+		goto release_mem;
+
+	/* Calculate crc of the received configs (not the raw config file) */
+	if (data->T71_address)
+		crc_start = data->T71_address;
+	else if (data->T7_address)
+		crc_start = data->T7_address;
+	else
+		dev_warn(dev, "Could not find CRC start\n");
+
+	if (crc_start > cfg.start_ofs) {
+		calculated_crc = mxt_calculate_crc(cfg.mem,
+						   crc_start - cfg.start_ofs,
+						   cfg.mem_size);
+
+		if (config_crc > 0 && config_crc != calculated_crc)
+			dev_warn(dev, "Config CRC in file inconsistent, calculated=%06X, file=%06X\n",
+				 calculated_crc, config_crc);
+	}
+
+	ret = mxt_upload_cfg_mem(data, &cfg);
+	if (ret)
+		goto release_mem;
+
+	mxt_update_crc(data, MXT_COMMAND_BACKUPNV, MXT_BACKUP_VALUE);
+
+	ret = mxt_check_retrigen(data);
+	if (ret)
+		goto release_mem;
+
+	ret = mxt_soft_reset(data);
+	if (ret)
+		goto release_mem;
+
+	dev_info(dev, "Config successfully updated\n");
+
+	/* T7 config may have changed */
+	mxt_init_t7_power_cfg(data);
+
+release_mem:
+	kfree(cfg.mem);
+release_raw:
+	kfree(cfg.raw);
+	return ret;
+}
+
+static void mxt_free_input_device(struct mxt_data *data)
+{
+	if (data->input_dev) {
+		input_unregister_device(data->input_dev);
+		data->input_dev = NULL;
+	}
+}
+
+static void mxt_free_object_table(struct mxt_data *data)
+{
+#ifdef CONFIG_TOUCHSCREEN_ATMEL_MXT_T37
+	video_unregister_device(&data->dbg.vdev);
+	v4l2_device_unregister(&data->dbg.v4l2);
+#endif
+	data->object_table = NULL;
+	data->info = NULL;
+	kfree(data->raw_info_block);
+	data->raw_info_block = NULL;
+	kfree(data->msg_buf);
+	data->msg_buf = NULL;
+	data->T5_address = 0;
+	data->T5_msg_size = 0;
+	data->T6_reportid = 0;
+	data->T7_address = 0;
+	data->T71_address = 0;
+	data->T9_reportid_min = 0;
+	data->T9_reportid_max = 0;
+	data->T18_address = 0;
+	data->T19_reportid = 0;
+	data->T44_address = 0;
+	data->T100_reportid_min = 0;
+	data->T100_reportid_max = 0;
+	data->max_reportid = 0;
+}
+
+static int mxt_parse_object_table(struct mxt_data *data,
+				  struct mxt_object *object_table)
+{
+	struct i2c_client *client = data->client;
+	int i;
+	u8 reportid;
+	u16 end_address;
+
+	/* Valid Report IDs start counting from 1 */
+	reportid = 1;
+	data->mem_size = 0;
+	for (i = 0; i < data->info->object_num; i++) {
+		struct mxt_object *object = object_table + i;
+		u8 min_id, max_id;
+
+		le16_to_cpus(&object->start_address);
+
+		if (object->num_report_ids) {
+			min_id = reportid;
+			reportid += object->num_report_ids *
+					mxt_obj_instances(object);
+			max_id = reportid - 1;
+		} else {
+			min_id = 0;
+			max_id = 0;
+		}
+
+		dev_dbg(&data->client->dev,
+			"T%u Start:%u Size:%zu Instances:%zu Report IDs:%u-%u\n",
+			object->type, object->start_address,
+			mxt_obj_size(object), mxt_obj_instances(object),
+			min_id, max_id);
+
+		switch (object->type) {
+		case MXT_GEN_MESSAGE_T5:
+			if (data->info->family_id == 0x80 &&
+			    data->info->version < 0x20) {
+				/*
+				 * On mXT224 firmware versions prior to V2.0
+				 * read and discard unused CRC byte otherwise
+				 * DMA reads are misaligned.
+				 */
+				data->T5_msg_size = mxt_obj_size(object);
+			} else {
+				/* CRC not enabled, so skip last byte */
+				data->T5_msg_size = mxt_obj_size(object) - 1;
+			}
+			data->T5_address = object->start_address;
+			break;
+		case MXT_GEN_COMMAND_T6:
+			data->T6_reportid = min_id;
+			data->T6_address = object->start_address;
+			break;
+		case MXT_GEN_POWER_T7:
+			data->T7_address = object->start_address;
+			break;
+		case MXT_SPT_DYNAMICCONFIGURATIONCONTAINER_T71:
+			data->T71_address = object->start_address;
+			break;
+		case MXT_TOUCH_MULTI_T9:
+			data->multitouch = MXT_TOUCH_MULTI_T9;
+			/* Only handle messages from first T9 instance */
+			data->T9_reportid_min = min_id;
+			data->T9_reportid_max = min_id +
+						object->num_report_ids - 1;
+			data->num_touchids = object->num_report_ids;
+			break;
+		case MXT_SPT_COMMSCONFIG_T18:
+			data->T18_address = object->start_address;
+			break;
+		case MXT_SPT_MESSAGECOUNT_T44:
+			data->T44_address = object->start_address;
+			break;
+		case MXT_SPT_GPIOPWM_T19:
+			data->T19_reportid = min_id;
+			break;
+		case MXT_TOUCH_MULTITOUCHSCREEN_T100:
+			data->multitouch = MXT_TOUCH_MULTITOUCHSCREEN_T100;
+			data->T100_reportid_min = min_id;
+			data->T100_reportid_max = max_id;
+			/* first two report IDs reserved */
+			data->num_touchids = object->num_report_ids - 2;
+			break;
+		}
+
+		end_address = object->start_address
+			+ mxt_obj_size(object) * mxt_obj_instances(object) - 1;
+
+		if (end_address >= data->mem_size)
+			data->mem_size = end_address + 1;
+	}
+
+	/* Store maximum reportid */
+	data->max_reportid = reportid;
+
+	/* If T44 exists, T5 position has to be directly after */
+	if (data->T44_address && (data->T5_address != data->T44_address + 1)) {
+		dev_err(&client->dev, "Invalid T44 position\n");
+		return -EINVAL;
+	}
+
+	data->msg_buf = kcalloc(data->max_reportid,
+				data->T5_msg_size, GFP_KERNEL);
+	if (!data->msg_buf)
+		return -ENOMEM;
+
+	return 0;
+}
+
+static int mxt_read_info_block(struct mxt_data *data)
+{
+	struct i2c_client *client = data->client;
+	int error;
+	size_t size;
+	void *id_buf, *buf;
+	uint8_t num_objects;
+	u32 calculated_crc;
+	u8 *crc_ptr;
+
+	/* If info block already allocated, free it */
+	if (data->raw_info_block)
+		mxt_free_object_table(data);
+
+	/* Read 7-byte ID information block starting at address 0 */
+	size = sizeof(struct mxt_info);
+	id_buf = kzalloc(size, GFP_KERNEL);
+	if (!id_buf)
+		return -ENOMEM;
+
+	error = __mxt_read_reg(data, 0, size, id_buf);
+	if (error)
+		goto err_free_mem;
+
+	/* Resize buffer to give space for rest of info block */
+	num_objects = ((struct mxt_info *)id_buf)->object_num;
+	size += (num_objects * sizeof(struct mxt_object))
+		+ MXT_INFO_CHECKSUM_SIZE;
+
+	buf = krealloc(id_buf, size, GFP_KERNEL);
+	if (!buf) {
+		error = -ENOMEM;
+		goto err_free_mem;
+	}
+	id_buf = buf;
+
+	/* Read rest of info block */
+	error = __mxt_read_reg(data, MXT_OBJECT_START,
+			       size - MXT_OBJECT_START,
+			       id_buf + MXT_OBJECT_START);
+	if (error)
+		goto err_free_mem;
+
+	/* Extract & calculate checksum */
+	crc_ptr = id_buf + size - MXT_INFO_CHECKSUM_SIZE;
+	data->info_crc = crc_ptr[0] | (crc_ptr[1] << 8) | (crc_ptr[2] << 16);
+
+	calculated_crc = mxt_calculate_crc(id_buf, 0,
+					   size - MXT_INFO_CHECKSUM_SIZE);
+
+	/*
+	 * CRC mismatch can be caused by data corruption due to I2C comms
+	 * issue or else device is not using Object Based Protocol (eg i2c-hid)
+	 */
+	if ((data->info_crc == 0) || (data->info_crc != calculated_crc)) {
+		dev_err(&client->dev,
+			"Info Block CRC error calculated=0x%06X read=0x%06X\n",
+			calculated_crc, data->info_crc);
+		error = -EIO;
+		goto err_free_mem;
+	}
+
+	data->raw_info_block = id_buf;
+	data->info = (struct mxt_info *)id_buf;
+
+	dev_info(&client->dev,
+		 "Family: %u Variant: %u Firmware V%u.%u.%02X Objects: %u\n",
+		 data->info->family_id, data->info->variant_id,
+		 data->info->version >> 4, data->info->version & 0xf,
+		 data->info->build, data->info->object_num);
+
+	/* Parse object table information */
+	error = mxt_parse_object_table(data, id_buf + MXT_OBJECT_START);
+	if (error) {
+		dev_err(&client->dev, "Error %d parsing object table\n", error);
+		mxt_free_object_table(data);
+		goto err_free_mem;
+	}
+
+	data->object_table = (struct mxt_object *)(id_buf + MXT_OBJECT_START);
+
+	return 0;
+
+err_free_mem:
+	kfree(id_buf);
+	return error;
+}
+
+static int mxt_pinctrl_init(struct mxt_data *data)
+{
+	int error;
+
+	/* Get pinctrl if target uses pinctrl */
+	data->ts_pinctrl = devm_pinctrl_get((&data->client->dev));
+	if (IS_ERR_OR_NULL(data->ts_pinctrl)) {
+		dev_dbg(&data->client->dev,
+			"Device does not use pinctrl\n");
+		error = PTR_ERR(data->ts_pinctrl);
+		data->ts_pinctrl = NULL;
+		return error;
+	}
+
+	data->gpio_state_active
+		= pinctrl_lookup_state(data->ts_pinctrl, "pmx_ts_active");
+	if (IS_ERR_OR_NULL(data->gpio_state_active)) {
+		dev_dbg(&data->client->dev,
+			"Can not get ts default pinstate\n");
+		error = PTR_ERR(data->gpio_state_active);
+		data->ts_pinctrl = NULL;
+		return error;
+	}
+
+	data->gpio_state_suspend
+		= pinctrl_lookup_state(data->ts_pinctrl, "pmx_ts_suspend");
+	if (IS_ERR_OR_NULL(data->gpio_state_suspend)) {
+		dev_dbg(&data->client->dev,
+			"Can not get ts sleep pinstate\n");
+		error = PTR_ERR(data->gpio_state_suspend);
+		data->ts_pinctrl = NULL;
+		return error;
+	}
+
+	return 0;
+}
+
+static int mxt_pinctrl_select(struct mxt_data *data, bool on)
+{
+	struct pinctrl_state *pins_state;
+	int error;
+
+	pins_state = on ? data->gpio_state_active
+		: data->gpio_state_suspend;
+	if (!IS_ERR_OR_NULL(pins_state)) {
+		error = pinctrl_select_state(data->ts_pinctrl, pins_state);
+		if (error) {
+			dev_err(&data->client->dev,
+				"can not set %s pins\n",
+				on ? "pmx_ts_active" : "pmx_ts_suspend");
+			return error;
+		}
+	} else {
+		dev_err(&data->client->dev,
+			"not a valid '%s' pinstate\n",
+				on ? "pmx_ts_active" : "pmx_ts_suspend");
+	}
+
+	return 0;
+}
+
+static int mxt_gpio_enable(struct mxt_data *data, bool enable)
+{
+	int error;
+
+	if (data->ts_pinctrl) {
+		error = mxt_pinctrl_select(data, enable);
+		if (error < 0)
+			return error;
+	}
+
+	if (enable) {
+		error = gpiod_direction_input(data->irq_gpio);
+		if (error)
+			dev_err(&data->client->dev,
+				"unable to set dir for %d gpio(%d)\n",
+				desc_to_gpio(data->irq_gpio), error);
+		if (data->reset_gpio)
+			gpiod_set_value(data->reset_gpio, 1);
+	} else  {
+		if (data->reset_gpio)
+			gpiod_set_value(data->reset_gpio, 0);
+	}
+
+	return 0;
+}
+
+static int mxt_regulator_enable(struct mxt_data *data)
+{
+	int error;
+
+	if (!data->use_regulator)
+		return 0;
+
+	gpiod_set_value(data->reset_gpio, 0);
+
+	error = mxt_regulator_configure(data, true);
+	if (error) {
+		dev_err(&data->client->dev, "Failed to configure regulators on\n");
+		return error;
+	}
+
+	error = regulator_enable(data->reg_avdd);
+	if (error) {
+		dev_err(&data->client->dev,
+			"avdd enable failed, error=%d\n", error);
+		goto err_dis_configure;
+	}
+	usleep_range(10000, 15000);
+
+	error = regulator_enable(data->reg_vdd);
+	if (error) {
+		dev_err(&data->client->dev,
+			"vdd enable failed, error=%d\n", error);
+		goto err_dis_avdd;
+	}
+
+	if (!IS_ERR(data->reg_xvdd)) {
+		usleep_range(10000, 15000);
+		error = regulator_enable(data->reg_xvdd);
+		if (error) {
+			dev_err(&data->client->dev,
+				"xvdd enable failed, error=%d\n", error);
+			goto err_dis_vdd;
+		}
+	}
+	msleep(MXT_REGULATOR_DELAY);
+
+	reinit_completion(&data->bl_completion);
+	gpiod_set_value(data->reset_gpio, 1);
+	mxt_wait_for_completion(data, &data->bl_completion, MXT_POWERON_DELAY);
+
+	return 0;
+
+err_dis_vdd:
+	regulator_disable(data->reg_vdd);
+err_dis_avdd:
+	regulator_disable(data->reg_avdd);
+err_dis_configure:
+	mxt_regulator_configure(data, false);
+	return error;
+}
+
+static void mxt_regulator_disable(struct mxt_data *data)
+{
+	int error;
+
+	regulator_disable(data->reg_vdd);
+	regulator_disable(data->reg_avdd);
+	if (!IS_ERR(data->reg_xvdd))
+		regulator_disable(data->reg_xvdd);
+
+	error = mxt_regulator_configure(data, false);
+	if (error)
+		dev_err(&data->client->dev, "Failed to configure regulators off\n");
+
+}
+
+static int mxt_regulator_parse(struct mxt_data *data)
+{
+	struct device *dev = &data->client->dev;
+	struct device_node *np = dev->of_node;
+	struct property *prop;
+	int error = 0;
+
+	if (!data->reset_gpio) {
+		dev_warn(dev, "Must have reset GPIO to use regulator support\n");
+		return 0;
+	}
+
+	data->reg_vdd = regulator_get(dev, "vdd");
+	if (IS_ERR(data->reg_vdd)) {
+		error = PTR_ERR(data->reg_vdd);
+		dev_err(dev, "Error %d getting vdd regulator\n", error);
+		return error;
+	}
+
+	data->reg_avdd = regulator_get(dev, "avdd");
+	if (IS_ERR(data->reg_avdd)) {
+		error = PTR_ERR(data->reg_avdd);
+		dev_err(dev, "Error %d getting avdd regulator\n", error);
+		goto fail_put_vdd;
+	}
+
+	data->reg_xvdd = regulator_get(dev, "xvdd");
+	if (IS_ERR(data->reg_xvdd)) {
+		error = PTR_ERR(data->reg_xvdd);
+		prop = of_find_property(np, "xvdd-supply", NULL);
+		if (prop && (error == -EPROBE_DEFER))
+			return -EPROBE_DEFER;
+		dev_dbg(dev, "xvdd regulator is not used\n");
+	}
+
+	data->use_regulator = true;
+
+	dev_err(dev, "Initialised regulators\n");
+	return 0;
+
+fail_put_vdd:
+	regulator_put(data->reg_vdd);
+	return error;
+}
+
+static int mxt_regulator_configure(struct mxt_data *data, bool enable)
+{
+	struct device *dev = &data->client->dev;
+	int error = 0;
+
+	/* According to maXTouch power sequencing specification, RESET line
+	 * must be kept low until some time after regulators come up to
+	 * voltage
+	 */
+	if (!data->reset_gpio) {
+		dev_warn(dev, "Must have reset GPIO to use regulator support\n");
+		return 0;
+	}
+
+	if (!enable) {
+		if (regulator_count_voltages(data->reg_avdd) > 0) {
+			error = regulator_set_load(data->reg_avdd, MXT_DISABLE_LOAD);
+			if (error) {
+				dev_err(&data->client->dev,
+					"avdd set_load failed err=%d\n", error);
+				return error;
+			}
+		}
+
+		if (regulator_count_voltages(data->reg_vdd) > 0) {
+			error = regulator_set_load(data->reg_vdd, MXT_DISABLE_LOAD);
+			if (error) {
+				dev_err(&data->client->dev,
+					"vdd set_load failed err=%d\n", error);
+				return error;
+			}
+		}
+
+		if (!IS_ERR(data->reg_xvdd)) {
+			if (regulator_count_voltages(data->reg_xvdd) > 0) {
+				error = regulator_set_load(data->reg_xvdd, MXT_DISABLE_LOAD);
+				if (error)
+					dev_err(&data->client->dev,
+						"xvdd set_load failed err=%d\n", error);
+			}
+		}
+	} else {
+		if (regulator_count_voltages(data->reg_vdd) > 0) {
+			error = regulator_set_load(data->reg_vdd, MXT_ENABLE_LOAD);
+			if (error) {
+				dev_err(&data->client->dev,
+					"vdd set_load failed err=%d\n", error);
+				return error;
+			}
+			error = regulator_set_voltage(data->reg_vdd,
+				MXT_VDD_VTG_MIN_UV, MXT_VDD_VTG_MAX_UV);
+			if (error) {
+				dev_err(&data->client->dev,
+					"vdd set_vtg failed err=%d\n", error);
+				goto err_vdd_load;
+			}
+		}
+
+		if (regulator_count_voltages(data->reg_avdd) > 0) {
+			error = regulator_set_load(data->reg_avdd, MXT_ENABLE_LOAD);
+			if (error) {
+				dev_err(&data->client->dev,
+					"avdd set_load failed err=%d\n", error);
+				goto err_vdd_load;
+			}
+			error = regulator_set_voltage(data->reg_avdd,
+				MXT_AVDD_VTG_MIN_UV, MXT_AVDD_VTG_MAX_UV);
+			if (error) {
+				dev_err(&data->client->dev,
+					"avdd set_vtg failed err=%d\n", error);
+				goto err_avdd_load;
+			}
+		}
+
+		if (!IS_ERR(data->reg_xvdd)) {
+			if (regulator_count_voltages(data->reg_xvdd) > 0) {
+				error = regulator_set_load(data->reg_xvdd, MXT_ENABLE_LOAD);
+				if (error) {
+					dev_err(&data->client->dev,
+						"xvdd set_load failed err=%d\n", error);
+					goto err_avdd_load;
+				}
+				error = regulator_set_voltage(data->reg_xvdd,
+					MXT_XVDD_VTG_MIN_UV, MXT_XVDD_VTG_MAX_UV);
+				if (error) {
+					dev_err(&data->client->dev,
+						"xvdd set_vtg failed err=%d\n", error);
+					goto err_xvdd_load;
+				}
+			}
+		}
+	}
+	return error;
+
+err_xvdd_load:
+	regulator_set_load(data->reg_xvdd, MXT_DISABLE_LOAD);
+err_avdd_load:
+	regulator_set_load(data->reg_avdd, MXT_DISABLE_LOAD);
+err_vdd_load:
+	regulator_set_load(data->reg_vdd, MXT_DISABLE_LOAD);
+	return error;
+}
+
+static int mxt_read_t9_resolution(struct mxt_data *data)
+{
+	int error;
+	unsigned char orient;
+	struct mxt_object *object;
+
+	object = mxt_get_object(data, MXT_TOUCH_MULTI_T9);
+	if (!object)
+		return -EINVAL;
+
+	error = __mxt_read_reg(data,
+			       object->start_address + MXT_T9_XSIZE,
+			       sizeof(data->xsize), &data->xsize);
+	if (error)
+		return error;
+
+	error = __mxt_read_reg(data,
+			       object->start_address + MXT_T9_YSIZE,
+			       sizeof(data->ysize), &data->ysize);
+	if (error)
+		return error;
+
+	error = __mxt_read_reg(data,
+			       object->start_address + MXT_T9_RANGE,
+			       sizeof(struct t9_range), data->read_buf);
+	if (error)
+		return error;
+
+	data->max_x = get_unaligned_le16(data->read_buf);
+	data->max_y = get_unaligned_le16(data->read_buf + sizeof(__le16));
+
+	error = __mxt_read_reg(data,
+				object->start_address + MXT_T9_ORIENT,
+				1, data->read_buf);
+	if (error)
+		return error;
+	orient = *(data->read_buf);
+
+	if (data->xy_switch != (orient & MXT_T9_ORIENT_SWITCH))
+		dev_warn(&data->client->dev,
+			"T9 data->xy_switch:%d\n",
+			data->xy_switch);
+
+	if (data->invertx != (orient & MXT_T9_ORIENT_INVERTX))
+		dev_warn(&data->client->dev,
+			"T9 data->data->invertx:%d\n",
+			data->invertx);
+
+	if (data->inverty != (orient & MXT_T9_ORIENT_INVERTY))
+		dev_warn(&data->client->dev,
+			"T9 data->data->inverty:%d\n",
+			data->inverty);
+
+	return 0;
+}
+
+static int mxt_read_t100_config(struct mxt_data *data)
+{
+	int error;
+	struct mxt_object *object;
+	u8 cfg, tchaux;
+	u8 aux;
+
+	object = mxt_get_object(data, MXT_TOUCH_MULTITOUCHSCREEN_T100);
+	if (!object)
+		return -EINVAL;
+
+	/* read touchscreen dimensions */
+	error = __mxt_read_reg(data,
+			       object->start_address + MXT_T100_XRANGE,
+			       sizeof(__le16), data->read_buf);
+	if (error)
+		return error;
+
+	data->max_x = get_unaligned_le16(data->read_buf);
+
+	error = __mxt_read_reg(data,
+			       object->start_address + MXT_T100_YRANGE,
+			       sizeof(__le16), data->read_buf);
+
+	if (error)
+		return error;
+
+	data->max_y = get_unaligned_le16(data->read_buf);
+
+	error = __mxt_read_reg(data,
+			       object->start_address + MXT_T100_XSIZE,
+			       sizeof(data->xsize), &data->xsize);
+	if (error)
+		return error;
+
+	error = __mxt_read_reg(data,
+			       object->start_address + MXT_T100_YSIZE,
+			       sizeof(data->ysize), &data->ysize);
+	if (error)
+		return error;
+
+	/* read orientation config */
+	error = __mxt_read_reg(data,
+				object->start_address + MXT_T100_CFG1,
+				1, data->read_buf);
+	if (error)
+		return error;
+	cfg = *(data->read_buf);
+
+	if (data->xy_switch != (cfg & MXT_T100_CFG_SWITCHXY))
+		dev_warn(&data->client->dev,
+			"T100 data->xy_switch:%d\n",
+			data->xy_switch);
+
+	if (data->invertx != (cfg & MXT_T100_CFG_INVERTX))
+		dev_warn(&data->client->dev,
+			"T100 data->data->invertx:%d\n",
+			data->invertx);
+
+	if (data->inverty != (cfg & MXT_T100_CFG_INVERTY))
+		dev_warn(&data->client->dev,
+			"T100 data->data->inverty:%d\n",
+			data->inverty);
+
+	/* allocate aux bytes */
+	error =  __mxt_read_reg(data,
+				object->start_address + MXT_T100_TCHAUX,
+				1, data->read_buf);
+	if (error)
+		return error;
+	tchaux = *(data->read_buf);
+
+	aux = 6;
+
+	if (tchaux & MXT_T100_TCHAUX_VECT)
+		data->t100_aux_vect = aux++;
+
+	if (tchaux & MXT_T100_TCHAUX_AMPL)
+		data->t100_aux_ampl = aux++;
+
+	if (tchaux & MXT_T100_TCHAUX_AREA)
+		data->t100_aux_area = aux++;
+
+	dev_dbg(&data->client->dev,
+		"T100 aux mappings vect:%u ampl:%u area:%u\n",
+		data->t100_aux_vect, data->t100_aux_ampl, data->t100_aux_area);
+
+	return 0;
+}
+
+static int mxt_input_open(struct input_dev *dev);
+static void mxt_input_close(struct input_dev *dev);
+
+static void mxt_set_up_as_touchpad(struct input_dev *input_dev,
+				   struct mxt_data *data)
+{
+	int i;
+
+	input_dev->name = "Atmel maXTouch Touchpad";
+
+	__set_bit(INPUT_PROP_BUTTONPAD, input_dev->propbit);
+
+	input_abs_set_res(input_dev, ABS_X, MXT_PIXELS_PER_MM);
+	input_abs_set_res(input_dev, ABS_Y, MXT_PIXELS_PER_MM);
+	input_abs_set_res(input_dev, ABS_MT_POSITION_X,
+			  MXT_PIXELS_PER_MM);
+	input_abs_set_res(input_dev, ABS_MT_POSITION_Y,
+			  MXT_PIXELS_PER_MM);
+
+	for (i = 0; i < data->t19_num_keys; i++)
+		if (data->t19_keymap[i] != KEY_RESERVED)
+			input_set_capability(input_dev, EV_KEY,
+					     data->t19_keymap[i]);
+}
+
+static int mxt_initialize_input_device(struct mxt_data *data)
+{
+	struct device *dev = &data->client->dev;
+	struct input_dev *input_dev;
+	int error;
+	unsigned int num_mt_slots;
+	unsigned int mt_flags = 0;
+
+	switch (data->multitouch) {
+	case MXT_TOUCH_MULTI_T9:
+		num_mt_slots = data->T9_reportid_max - data->T9_reportid_min + 1;
+		error = mxt_read_t9_resolution(data);
+		if (error)
+			dev_warn(dev, "Failed to initialize T9 resolution\n");
+		break;
+
+	case MXT_TOUCH_MULTITOUCHSCREEN_T100:
+		num_mt_slots = data->num_touchids;
+		error = mxt_read_t100_config(data);
+		if (error)
+			dev_warn(dev, "Failed to read T100 config\n");
+		break;
+
+	default:
+		dev_err(dev, "Invalid multitouch object\n");
+		return -EINVAL;
+	}
+
+	/* Handle default values and orientation switch */
+	if (data->max_x == 0)
+		data->max_x = 1023;
+
+	if (data->max_y == 0)
+		data->max_y = 1023;
+
+	if (data->xy_switch)
+		swap(data->max_x, data->max_y);
+
+	dev_info(dev, "Touchscreen size X%uY%u\n", data->max_x, data->max_y);
+
+	/* Register input device */
+	input_dev = input_allocate_device();
+	if (!input_dev)
+		return -ENOMEM;
+
+	input_dev->name = "Atmel maXTouch Touchscreen";
+	input_dev->phys = data->phys;
+	input_dev->id.bustype = BUS_I2C;
+	input_dev->dev.parent = dev;
+	input_dev->open = mxt_input_open;
+	input_dev->close = mxt_input_close;
+
+	input_set_capability(input_dev, EV_KEY, BTN_TOUCH);
+
+	if (data->disp_maxx == 0 || data->disp_maxx > data->max_x)
+		data->disp_maxx = data->max_x;
+	if (data->disp_maxy == 0 || data->disp_maxy > data->max_y)
+		data->disp_maxy = data->max_y;
+
+	if (data->multitouch == MXT_TOUCH_MULTI_T9 ||
+	    (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100 &&
+	     data->t100_aux_ampl)) {
+		input_set_abs_params(input_dev, ABS_PRESSURE, 0, 255, 0, 0);
+	}
+
+	/* If device has buttons we assume it is a touchpad */
+	if (data->t19_num_keys) {
+		mxt_set_up_as_touchpad(input_dev, data);
+		mt_flags |= INPUT_MT_POINTER;
+	} else {
+		mt_flags |= INPUT_MT_DIRECT;
+	}
+
+	/* For multi touch */
+	error = input_mt_init_slots(input_dev, num_mt_slots, mt_flags);
+	if (error) {
+		dev_err(dev, "Error %d initialising slots\n", error);
+		goto err_free_mem;
+	}
+
+	if (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100) {
+		input_set_abs_params(input_dev, ABS_MT_TOOL_TYPE,
+				     0, MT_TOOL_MAX, 0, 0);
+		input_set_abs_params(input_dev, ABS_MT_DISTANCE,
+				     MXT_DISTANCE_ACTIVE_TOUCH,
+				     MXT_DISTANCE_HOVERING,
+				     0, 0);
+	}
+
+	input_set_abs_params(input_dev, ABS_MT_POSITION_X,
+			0, data->disp_maxx - data->disp_minx, 0, 0);
+	input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
+			0, data->disp_maxy - data->disp_miny, 0, 0);
+
+	if (data->multitouch == MXT_TOUCH_MULTI_T9 ||
+	    (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100 &&
+	     data->t100_aux_area)) {
+		input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
+				     0, MXT_MAX_AREA, 0, 0);
+	}
+
+	if (data->multitouch == MXT_TOUCH_MULTI_T9 ||
+	    (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100 &&
+	     data->t100_aux_ampl)) {
+		input_set_abs_params(input_dev, ABS_MT_PRESSURE,
+				     0, 255, 0, 0);
+	}
+
+	if (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100 &&
+	    data->t100_aux_vect) {
+		input_set_abs_params(input_dev, ABS_MT_ORIENTATION,
+				     0, 255, 0, 0);
+	}
+
+	if (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100 &&
+	    data->t100_aux_vect) {
+		input_set_abs_params(input_dev, ABS_MT_ORIENTATION,
+				     0, 255, 0, 0);
+	}
+
+	input_set_drvdata(input_dev, data);
+
+	error = input_register_device(input_dev);
+	if (error) {
+		dev_err(dev, "Error %d registering input device\n", error);
+		goto err_free_mem;
+	}
+
+	data->input_dev = input_dev;
+
+	return 0;
+
+err_free_mem:
+	input_free_device(input_dev);
+	return error;
+}
+
+static int mxt_configure_objects(struct mxt_data *data,
+				 const struct firmware *cfg);
+
+static void mxt_config_cb(const struct firmware *cfg, void *ctx)
+{
+	mxt_configure_objects(ctx, cfg);
+	release_firmware(cfg);
+}
+
+static int mxt_initialize(struct mxt_data *data)
+{
+	struct i2c_client *client = data->client;
+	int recovery_attempts = 0;
+	int error;
+
+	while (1) {
+		error = mxt_read_info_block(data);
+		if (!error)
+			break;
+
+		/* Check bootloader state */
+		error = mxt_probe_bootloader(data, false);
+		if (error) {
+			dev_info(&client->dev, "Trying alternate bootloader address\n");
+			mxt_soft_reset(data);
+			error = mxt_probe_bootloader(data, true);
+			if (error) {
+				/* Chip is not in appmode or bootloader mode */
+				dev_err(&client->dev, "Fail probe boot, continue\n");
+			}
+		}
+
+		/* OK, we are in bootloader, see if we can recover */
+		if (++recovery_attempts > 10) {
+			dev_err(&client->dev, "Could not recover from bootloader mode\n");
+			/*
+			 * We can reflash from this state, so do not
+			 * abort initialization.
+			 */
+			data->in_bootloader = true;
+			return 0;
+		}
+
+		/* Attempt to exit bootloader into app mode */
+		mxt_send_bootloader_cmd(data, false);
+		msleep(MXT_FW_RESET_TIME);
+	}
+
+	error = mxt_check_retrigen(data);
+	if (error)
+		return error;
+
+	error = mxt_acquire_irq(data);
+	if (error)
+		return error;
+
+	error = request_firmware_nowait(THIS_MODULE, true, MXT_CFG_NAME,
+					&client->dev, GFP_KERNEL, data,
+					mxt_config_cb);
+	if (error) {
+		dev_err(&client->dev, "Failed to invoke firmware loader: %d\n",
+			error);
+		return error;
+	}
+
+	return 0;
+}
+
+static int mxt_set_t7_power_cfg(struct mxt_data *data, u8 sleep)
+{
+	struct device *dev = &data->client->dev;
+	int error;
+	struct t7_config *new_config;
+	struct t7_config deepsleep = { .active = 0, .idle = 0 };
+
+	if (sleep == MXT_POWER_CFG_DEEPSLEEP)
+		new_config = &deepsleep;
+	else
+		new_config = &data->t7_cfg;
+
+	error = __mxt_write_reg(data->client, data->T7_address,
+				sizeof(data->t7_cfg), new_config);
+	if (error)
+		return error;
+
+	dev_dbg(dev, "Set T7 ACTV:%d IDLE:%d\n",
+		new_config->active, new_config->idle);
+
+	return 0;
+}
+
+static int mxt_init_t7_power_cfg(struct mxt_data *data)
+{
+	struct device *dev = &data->client->dev;
+	int error;
+	bool retry = false;
+
+recheck:
+	error = __mxt_read_reg(data, data->T7_address,
+				sizeof(data->t7_cfg), &data->t7_cfg);
+	if (error)
+		return error;
+
+	if (data->t7_cfg.active == 0 || data->t7_cfg.idle == 0) {
+		if (!retry) {
+			dev_dbg(dev, "T7 cfg zero, resetting\n");
+			mxt_soft_reset(data);
+			retry = true;
+			goto recheck;
+		} else {
+			dev_dbg(dev, "T7 cfg zero after reset, overriding\n");
+			data->t7_cfg.active = 20;
+			data->t7_cfg.idle = 100;
+			return mxt_set_t7_power_cfg(data, MXT_POWER_CFG_RUN);
+		}
+	}
+
+	dev_dbg(dev, "Initialized power cfg: ACTV %d, IDLE %d\n",
+		data->t7_cfg.active, data->t7_cfg.idle);
+	return 0;
+}
+
+#ifdef CONFIG_TOUCHSCREEN_ATMEL_MXT_T37
+static const struct v4l2_file_operations mxt_video_fops = {
+	.owner = THIS_MODULE,
+	.open = v4l2_fh_open,
+	.release = vb2_fop_release,
+	.unlocked_ioctl = video_ioctl2,
+	.read = vb2_fop_read,
+	.mmap = vb2_fop_mmap,
+	.poll = vb2_fop_poll,
+};
+
+static u16 mxt_get_debug_value(struct mxt_data *data, unsigned int x,
+			       unsigned int y)
+{
+	struct mxt_info *info = data->info;
+	struct mxt_dbg *dbg = &data->dbg;
+	unsigned int ofs, page;
+	unsigned int col = 0;
+	unsigned int col_width;
+
+	if (info->family_id == MXT_FAMILY_1386) {
+		col_width = info->matrix_ysize / MXT1386_COLUMNS;
+		col = y / col_width;
+		y = y % col_width;
+	} else {
+		col_width = info->matrix_ysize;
+	}
+
+	ofs = (y + (x * col_width)) * sizeof(u16);
+	page = ofs / MXT_DIAGNOSTIC_SIZE;
+	ofs %= MXT_DIAGNOSTIC_SIZE;
+
+	if (info->family_id == MXT_FAMILY_1386)
+		page += col * MXT1386_PAGES_PER_COLUMN;
+
+	return get_unaligned_le16(&dbg->t37_buf[page].data[ofs]);
+}
+
+static int mxt_convert_debug_pages(struct mxt_data *data, u16 *outbuf)
+{
+	struct mxt_dbg *dbg = &data->dbg;
+	unsigned int x = 0;
+	unsigned int y = 0;
+	unsigned int i, rx, ry;
+
+	for (i = 0; i < dbg->t37_nodes; i++) {
+		/* Handle orientation */
+		rx = data->xy_switch ? y : x;
+		ry = data->xy_switch ? x : y;
+		rx = data->invertx ? (data->xsize - 1 - rx) : rx;
+		ry = data->inverty ? (data->ysize - 1 - ry) : ry;
+
+		outbuf[i] = mxt_get_debug_value(data, rx, ry);
+
+		/* Next value */
+		if (++x >= (data->xy_switch ? data->ysize : data->xsize)) {
+			x = 0;
+			y++;
+		}
+	}
+
+	return 0;
+}
+
+static int mxt_read_diagnostic_debug(struct mxt_data *data, u8 mode,
+				     u16 *outbuf)
+{
+	struct mxt_dbg *dbg = &data->dbg;
+	int retries = 0;
+	int page;
+	int ret;
+	u8 cmd = mode;
+	struct t37_debug *p;
+	u8 cmd_poll;
+
+	for (page = 0; page < dbg->t37_pages; page++) {
+		p = dbg->t37_buf + page;
+
+		ret = mxt_write_reg(data->client, dbg->diag_cmd_address,
+				    cmd);
+		if (ret)
+			return ret;
+
+		retries = 0;
+		msleep(20);
+wait_cmd:
+		/* Read back command byte */
+		ret = __mxt_read_reg(data, dbg->diag_cmd_address,
+				     sizeof(cmd_poll), data->read_buf);
+		if (ret)
+			return ret;
+
+		cmd_poll = *(data->read_buf);
+		/* Field is cleared once the command has been processed */
+		if (cmd_poll) {
+			if (retries++ > 100)
+				return -EINVAL;
+
+			msleep(20);
+			goto wait_cmd;
+		}
+
+		/* Read T37 page */
+		ret = __mxt_read_reg(data, dbg->t37_address,
+				     sizeof(struct t37_debug), p);
+		if (ret)
+			return ret;
+
+		if (p->mode != mode || p->page != page) {
+			dev_err(&data->client->dev, "T37 page mismatch\n");
+			return -EINVAL;
+		}
+
+		dev_dbg(&data->client->dev, "%s page:%d retries:%d\n",
+			__func__, page, retries);
+
+		/* For remaining pages, write PAGEUP rather than mode */
+		cmd = MXT_DIAGNOSTIC_PAGEUP;
+	}
+
+	return mxt_convert_debug_pages(data, outbuf);
+}
+
+static int mxt_queue_setup(struct vb2_queue *q,
+		       unsigned int *nbuffers, unsigned int *nplanes,
+		       unsigned int sizes[], struct device *alloc_devs[])
+{
+	struct mxt_data *data = q->drv_priv;
+	size_t size = data->dbg.t37_nodes * sizeof(u16);
+
+	if (*nplanes)
+		return sizes[0] < size ? -EINVAL : 0;
+
+	*nplanes = 1;
+	sizes[0] = size;
+
+	return 0;
+}
+
+static void mxt_buffer_queue(struct vb2_buffer *vb)
+{
+	struct mxt_data *data = vb2_get_drv_priv(vb->vb2_queue);
+	u16 *ptr;
+	int ret;
+	u8 mode;
+
+	ptr = vb2_plane_vaddr(vb, 0);
+	if (!ptr) {
+		dev_err(&data->client->dev, "Error acquiring frame ptr\n");
+		goto fault;
+	}
+
+	switch (data->dbg.input) {
+	case MXT_V4L_INPUT_DELTAS:
+	default:
+		mode = MXT_DIAGNOSTIC_DELTAS;
+		break;
+
+	case MXT_V4L_INPUT_REFS:
+		mode = MXT_DIAGNOSTIC_REFS;
+		break;
+	}
+
+	ret = mxt_read_diagnostic_debug(data, mode, ptr);
+	if (ret)
+		goto fault;
+
+	vb2_set_plane_payload(vb, 0, data->dbg.t37_nodes * sizeof(u16));
+	vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
+	return;
+
+fault:
+	vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
+}
+
+/* V4L2 structures */
+static const struct vb2_ops mxt_queue_ops = {
+	.queue_setup		= mxt_queue_setup,
+	.buf_queue		= mxt_buffer_queue,
+	.wait_prepare		= vb2_ops_wait_prepare,
+	.wait_finish		= vb2_ops_wait_finish,
+};
+
+static const struct vb2_queue mxt_queue = {
+	.type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+	.io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ,
+	.buf_struct_size = sizeof(struct mxt_vb2_buffer),
+	.ops = &mxt_queue_ops,
+	.mem_ops = &vb2_vmalloc_memops,
+	.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC,
+	.min_buffers_needed = 1,
+};
+
+static int mxt_vidioc_querycap(struct file *file, void *priv,
+				 struct v4l2_capability *cap)
+{
+	struct mxt_data *data = video_drvdata(file);
+
+	strlcpy(cap->driver, "atmel_mxt_ts", sizeof(cap->driver));
+	strlcpy(cap->card, "atmel_mxt_ts touch", sizeof(cap->card));
+	snprintf(cap->bus_info, sizeof(cap->bus_info),
+		 "I2C:%s", dev_name(&data->client->dev));
+	return 0;
+}
+
+static int mxt_vidioc_enum_input(struct file *file, void *priv,
+				   struct v4l2_input *i)
+{
+	if (i->index >= MXT_V4L_INPUT_MAX)
+		return -EINVAL;
+
+	i->type = V4L2_INPUT_TYPE_TOUCH;
+
+	switch (i->index) {
+	case MXT_V4L_INPUT_REFS:
+		strlcpy(i->name, "Mutual Capacitance References",
+			sizeof(i->name));
+		break;
+	case MXT_V4L_INPUT_DELTAS:
+		strlcpy(i->name, "Mutual Capacitance Deltas", sizeof(i->name));
+		break;
+	}
+
+	return 0;
+}
+
+static int mxt_set_input(struct mxt_data *data, unsigned int i)
+{
+	struct v4l2_pix_format *f = &data->dbg.format;
+
+	if (i >= MXT_V4L_INPUT_MAX)
+		return -EINVAL;
+
+	if (i == MXT_V4L_INPUT_DELTAS)
+		f->pixelformat = V4L2_TCH_FMT_DELTA_TD16;
+	else
+		f->pixelformat = V4L2_TCH_FMT_TU16;
+
+	f->width = data->xy_switch ? data->ysize : data->xsize;
+	f->height = data->xy_switch ? data->xsize : data->ysize;
+	f->field = V4L2_FIELD_NONE;
+	f->colorspace = V4L2_COLORSPACE_RAW;
+	f->bytesperline = f->width * sizeof(u16);
+	f->sizeimage = f->width * f->height * sizeof(u16);
+
+	data->dbg.input = i;
+
+	return 0;
+}
+
+static int mxt_vidioc_s_input(struct file *file, void *priv, unsigned int i)
+{
+	return mxt_set_input(video_drvdata(file), i);
+}
+
+static int mxt_vidioc_g_input(struct file *file, void *priv, unsigned int *i)
+{
+	struct mxt_data *data = video_drvdata(file);
+
+	*i = data->dbg.input;
+
+	return 0;
+}
+
+static int mxt_vidioc_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+	struct mxt_data *data = video_drvdata(file);
+
+	f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	f->fmt.pix = data->dbg.format;
+
+	return 0;
+}
+
+static int mxt_vidioc_enum_fmt(struct file *file, void *priv,
+				 struct v4l2_fmtdesc *fmt)
+{
+	if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+
+	switch (fmt->index) {
+	case 0:
+		fmt->pixelformat = V4L2_TCH_FMT_TU16;
+		break;
+
+	case 1:
+		fmt->pixelformat = V4L2_TCH_FMT_DELTA_TD16;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int mxt_vidioc_g_parm(struct file *file, void *fh,
+			     struct v4l2_streamparm *a)
+{
+	if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+
+	a->parm.capture.readbuffers = 1;
+	a->parm.capture.timeperframe.numerator = 1;
+	a->parm.capture.timeperframe.denominator = 10;
+	return 0;
+}
+
+static const struct v4l2_ioctl_ops mxt_video_ioctl_ops = {
+	.vidioc_querycap        = mxt_vidioc_querycap,
+
+	.vidioc_enum_fmt_vid_cap = mxt_vidioc_enum_fmt,
+	.vidioc_s_fmt_vid_cap   = mxt_vidioc_fmt,
+	.vidioc_g_fmt_vid_cap   = mxt_vidioc_fmt,
+	.vidioc_try_fmt_vid_cap	= mxt_vidioc_fmt,
+	.vidioc_g_parm		= mxt_vidioc_g_parm,
+
+	.vidioc_enum_input      = mxt_vidioc_enum_input,
+	.vidioc_g_input         = mxt_vidioc_g_input,
+	.vidioc_s_input         = mxt_vidioc_s_input,
+
+	.vidioc_reqbufs         = vb2_ioctl_reqbufs,
+	.vidioc_create_bufs     = vb2_ioctl_create_bufs,
+	.vidioc_querybuf        = vb2_ioctl_querybuf,
+	.vidioc_qbuf            = vb2_ioctl_qbuf,
+	.vidioc_dqbuf           = vb2_ioctl_dqbuf,
+	.vidioc_expbuf          = vb2_ioctl_expbuf,
+
+	.vidioc_streamon        = vb2_ioctl_streamon,
+	.vidioc_streamoff       = vb2_ioctl_streamoff,
+};
+
+static const struct video_device mxt_video_device = {
+	.name = "Atmel maxTouch",
+	.fops = &mxt_video_fops,
+	.ioctl_ops = &mxt_video_ioctl_ops,
+	.release = video_device_release_empty,
+	.device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TOUCH |
+		       V4L2_CAP_READWRITE | V4L2_CAP_STREAMING,
+};
+
+static void mxt_debug_init(struct mxt_data *data)
+{
+	struct mxt_info *info = data->info;
+	struct mxt_dbg *dbg = &data->dbg;
+	struct mxt_object *object;
+	int error;
+
+	object = mxt_get_object(data, MXT_GEN_COMMAND_T6);
+	if (!object)
+		goto error;
+
+	dbg->diag_cmd_address = object->start_address + MXT_COMMAND_DIAGNOSTIC;
+
+	object = mxt_get_object(data, MXT_DEBUG_DIAGNOSTIC_T37);
+	if (!object)
+		goto error;
+
+	if (mxt_obj_size(object) != sizeof(struct t37_debug)) {
+		dev_warn(&data->client->dev, "Bad T37 size");
+		goto error;
+	}
+
+	dbg->t37_address = object->start_address;
+
+	/* Calculate size of data and allocate buffer */
+	dbg->t37_nodes = data->xsize * data->ysize;
+
+	if (info->family_id == MXT_FAMILY_1386)
+		dbg->t37_pages = MXT1386_COLUMNS * MXT1386_PAGES_PER_COLUMN;
+	else
+		dbg->t37_pages = DIV_ROUND_UP(data->xsize *
+					      info->matrix_ysize *
+					      sizeof(u16),
+					      sizeof(dbg->t37_buf->data));
+
+	dbg->t37_buf = devm_kmalloc_array(&data->client->dev, dbg->t37_pages,
+					  sizeof(struct t37_debug), GFP_KERNEL);
+	if (!dbg->t37_buf)
+		goto error;
+
+	/* init channel to zero */
+	mxt_set_input(data, 0);
+
+	/* register video device */
+	snprintf(dbg->v4l2.name, sizeof(dbg->v4l2.name), "%s", "atmel_mxt_ts");
+	error = v4l2_device_register(&data->client->dev, &dbg->v4l2);
+	if (error)
+		goto error;
+
+	/* initialize the queue */
+	mutex_init(&dbg->lock);
+	dbg->queue = mxt_queue;
+	dbg->queue.drv_priv = data;
+	dbg->queue.lock = &dbg->lock;
+	dbg->queue.dev = &data->client->dev;
+
+	error = vb2_queue_init(&dbg->queue);
+	if (error)
+		goto error_unreg_v4l2;
+
+	dbg->vdev = mxt_video_device;
+	dbg->vdev.v4l2_dev = &dbg->v4l2;
+	dbg->vdev.lock = &dbg->lock;
+	dbg->vdev.vfl_dir = VFL_DIR_RX;
+	dbg->vdev.queue = &dbg->queue;
+	video_set_drvdata(&dbg->vdev, data);
+
+	error = video_register_device(&dbg->vdev, VFL_TYPE_TOUCH, -1);
+	if (error)
+		goto error_unreg_v4l2;
+
+	return;
+
+error_unreg_v4l2:
+	v4l2_device_unregister(&dbg->v4l2);
+error:
+	dev_warn(&data->client->dev, "Error initializing T37\n");
+}
+#else
+static void mxt_debug_init(struct mxt_data *data)
+{
+}
+#endif
+
+static int mxt_configure_objects(struct mxt_data *data,
+				 const struct firmware *cfg)
+{
+	struct device *dev = &data->client->dev;
+	int error;
+
+	error = mxt_init_t7_power_cfg(data);
+	if (error) {
+		dev_err(dev, "Failed to initialize power cfg\n");
+		return error;
+	}
+
+	if (cfg) {
+		error = mxt_update_cfg(data, cfg);
+		if (error)
+			dev_warn(dev, "Error %d updating config\n", error);
+	}
+
+	if (data->multitouch) {
+		error = mxt_initialize_input_device(data);
+		if (error)
+			return error;
+	} else {
+		dev_warn(dev, "No touch object detected\n");
+	}
+
+	mxt_debug_init(data);
+
+	enable_irq(data->irq);
+	return 0;
+}
+
+/* Firmware Version is returned as Major.Minor.Build */
+static ssize_t mxt_fw_version_show(struct device *dev,
+				   struct device_attribute *attr, char *buf)
+{
+	struct mxt_data *data = dev_get_drvdata(dev);
+	struct mxt_info *info = data->info;
+	return scnprintf(buf, PAGE_SIZE, "%u.%u.%02X\n",
+			 info->version >> 4, info->version & 0xf, info->build);
+}
+
+/* Hardware Version is returned as FamilyID.VariantID */
+static ssize_t mxt_hw_version_show(struct device *dev,
+				   struct device_attribute *attr, char *buf)
+{
+	struct mxt_data *data = dev_get_drvdata(dev);
+	struct mxt_info *info = data->info;
+	return scnprintf(buf, PAGE_SIZE, "%u.%u\n",
+			 info->family_id, info->variant_id);
+}
+
+static ssize_t mxt_show_instance(char *buf, int count,
+				 struct mxt_object *object, int instance,
+				 const u8 *val)
+{
+	int i;
+
+	if (mxt_obj_instances(object) > 1)
+		count += scnprintf(buf + count, PAGE_SIZE - count,
+				   "Instance %u\n", instance);
+
+	for (i = 0; i < mxt_obj_size(object); i++)
+		count += scnprintf(buf + count, PAGE_SIZE - count,
+				"\t[%2u]: %02x (%d)\n", i, val[i], val[i]);
+	count += scnprintf(buf + count, PAGE_SIZE - count, "\n");
+
+	return count;
+}
+
+static ssize_t mxt_object_show(struct device *dev,
+				    struct device_attribute *attr, char *buf)
+{
+	struct mxt_data *data = dev_get_drvdata(dev);
+	struct mxt_object *object;
+	int count = 0;
+	int i, j;
+	int error;
+	u8 *obuf;
+
+	/* Pre-allocate buffer large enough to hold max sized object. */
+	obuf = kmalloc(256, GFP_KERNEL);
+	if (!obuf)
+		return -ENOMEM;
+
+	error = 0;
+	for (i = 0; i < data->info->object_num; i++) {
+		object = data->object_table + i;
+
+		if (!mxt_object_readable(object->type))
+			continue;
+
+		count += scnprintf(buf + count, PAGE_SIZE - count,
+				"T%u:\n", object->type);
+
+		for (j = 0; j < mxt_obj_instances(object); j++) {
+			u16 size = mxt_obj_size(object);
+			u16 addr = object->start_address + j * size;
+
+			error = __mxt_read_reg(data, addr, size, obuf);
+			if (error)
+				goto done;
+
+			count = mxt_show_instance(buf, count, object, j, obuf);
+		}
+	}
+
+done:
+	kfree(obuf);
+	return error ?: count;
+}
+
+static int mxt_check_firmware_format(struct device *dev,
+				     const struct firmware *fw)
+{
+	unsigned int pos = 0;
+	char c;
+
+	while (pos < fw->size) {
+		c = *(fw->data + pos);
+
+		if (c < '0' || (c > '9' && c < 'A') || c > 'F')
+			return 0;
+
+		pos++;
+	}
+
+	/*
+	 * To convert file try:
+	 * xxd -r -p mXTXXX__APP_VX-X-XX.enc > maxtouch.fw
+	 */
+	dev_err(dev, "Aborting: firmware file must be in binary format\n");
+
+	return -EINVAL;
+}
+
+static int mxt_load_fw(struct device *dev, const char *fn)
+{
+	struct mxt_data *data = dev_get_drvdata(dev);
+	const struct firmware *fw = NULL;
+	unsigned int frame_size;
+	unsigned int pos = 0;
+	unsigned int retry = 0;
+	unsigned int frame = 0;
+	int ret;
+
+	ret = request_firmware(&fw, fn, dev);
+	if (ret) {
+		dev_err(dev, "Unable to open firmware %s\n", fn);
+		return ret;
+	}
+
+	/* Check for incorrect enc file */
+	ret = mxt_check_firmware_format(dev, fw);
+	if (ret)
+		goto release_firmware;
+
+	if (!data->in_bootloader) {
+		/* Change to the bootloader mode */
+		data->in_bootloader = true;
+
+		ret = mxt_t6_command(data, MXT_COMMAND_RESET,
+				     MXT_BOOT_VALUE, false);
+		if (ret)
+			goto release_firmware;
+
+		msleep(MXT_RESET_TIME);
+
+		/* Do not need to scan since we know family ID */
+		ret = mxt_lookup_bootloader_address(data, 0);
+		if (ret)
+			goto release_firmware;
+
+		mxt_free_input_device(data);
+		mxt_free_object_table(data);
+	} else {
+		enable_irq(data->irq);
+	}
+
+	reinit_completion(&data->bl_completion);
+
+	ret = mxt_check_bootloader(data, MXT_WAITING_BOOTLOAD_CMD, false);
+	if (ret) {
+		/* Bootloader may still be unlocked from previous attempt */
+		ret = mxt_check_bootloader(data, MXT_WAITING_FRAME_DATA, false);
+		if (ret)
+			goto disable_irq;
+	} else {
+		dev_info(dev, "Unlocking bootloader\n");
+
+		/* Unlock bootloader */
+		ret = mxt_send_bootloader_cmd(data, true);
+		if (ret)
+			goto disable_irq;
+	}
+
+	while (pos < fw->size) {
+		ret = mxt_check_bootloader(data, MXT_WAITING_FRAME_DATA, true);
+		if (ret)
+			goto disable_irq;
+
+		frame_size = ((*(fw->data + pos) << 8) | *(fw->data + pos + 1));
+
+		/* Take account of CRC bytes */
+		frame_size += 2;
+
+		/* Write one frame to device */
+		ret = mxt_bootloader_write(data, fw->data + pos, frame_size);
+		if (ret)
+			goto disable_irq;
+
+		ret = mxt_check_bootloader(data, MXT_FRAME_CRC_PASS, true);
+		if (ret) {
+			retry++;
+
+			/* Back off by 20ms per retry */
+			msleep(retry * 20);
+
+			if (retry > 20) {
+				dev_err(dev, "Retry count exceeded\n");
+				goto disable_irq;
+			}
+		} else {
+			retry = 0;
+			pos += frame_size;
+			frame++;
+		}
+
+		if (frame % 50 == 0)
+			dev_dbg(dev, "Sent %d frames, %d/%zd bytes\n",
+				frame, pos, fw->size);
+	}
+
+	/* Wait for flash. */
+	ret = mxt_wait_for_completion(data, &data->bl_completion,
+				      MXT_FW_RESET_TIME);
+	if (ret)
+		goto disable_irq;
+
+	dev_dbg(dev, "Sent %d frames, %d bytes\n", frame, pos);
+
+	/*
+	 * Wait for device to reset. Some bootloader versions do not assert
+	 * the CHG line after bootloading has finished, so ignore potential
+	 * errors.
+	 */
+	mxt_wait_for_completion(data, &data->bl_completion, MXT_FW_RESET_TIME);
+
+	data->in_bootloader = false;
+
+disable_irq:
+	disable_irq(data->irq);
+release_firmware:
+	release_firmware(fw);
+	return ret;
+}
+
+static ssize_t mxt_update_fw_store(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf, size_t count)
+{
+	struct mxt_data *data = dev_get_drvdata(dev);
+	int error;
+
+	error = mxt_load_fw(dev, MXT_FW_NAME);
+	if (error) {
+		dev_err(dev, "The firmware update failed(%d)\n", error);
+		count = error;
+	} else {
+		dev_info(dev, "The firmware update succeeded\n");
+
+		error = mxt_initialize(data);
+		if (error)
+			return error;
+	}
+
+	return count;
+}
+
+static DEVICE_ATTR(fw_version, S_IRUGO, mxt_fw_version_show, NULL);
+static DEVICE_ATTR(hw_version, S_IRUGO, mxt_hw_version_show, NULL);
+static DEVICE_ATTR(object, S_IRUGO, mxt_object_show, NULL);
+static DEVICE_ATTR(update_fw, S_IWUSR, NULL, mxt_update_fw_store);
+
+static struct attribute *mxt_attrs[] = {
+	&dev_attr_fw_version.attr,
+	&dev_attr_hw_version.attr,
+	&dev_attr_object.attr,
+	&dev_attr_update_fw.attr,
+	NULL
+};
+
+static const struct attribute_group mxt_attr_group = {
+	.attrs = mxt_attrs,
+};
+
+static void mxt_start(struct mxt_data *data)
+{
+	if (!data->suspended || data->in_bootloader)
+		return;
+
+	/* enable gpios */
+	mxt_gpio_enable(data, true);
+
+	/* enable regulators */
+	mxt_regulator_enable(data);
+
+	switch (data->suspend_mode) {
+	case MXT_SUSPEND_T9_CTRL:
+		mxt_soft_reset(data);
+
+		/* Touch enable */
+		/* 0x83 = SCANEN | RPTEN | ENABLE */
+		mxt_write_object(data,
+				MXT_TOUCH_MULTI_T9, MXT_T9_CTRL, 0x83);
+		break;
+
+	case MXT_SUSPEND_DEEP_SLEEP:
+	default:
+		mxt_set_t7_power_cfg(data, MXT_POWER_CFG_RUN);
+
+		/* Recalibrate since chip has been in deep sleep */
+		mxt_t6_command(data, MXT_COMMAND_CALIBRATE, 1, false);
+		break;
+	}
+
+	mxt_acquire_irq(data);
+	data->suspended = false;
+}
+
+static void mxt_stop(struct mxt_data *data)
+{
+	if (data->suspended || data->in_bootloader)
+		return;
+
+	disable_irq(data->irq);
+
+	switch (data->suspend_mode) {
+	case MXT_SUSPEND_T9_CTRL:
+		/* Touch disable */
+		mxt_write_object(data,
+				MXT_TOUCH_MULTI_T9, MXT_T9_CTRL, 0);
+		break;
+
+	case MXT_SUSPEND_DEEP_SLEEP:
+	default:
+		mxt_set_t7_power_cfg(data, MXT_POWER_CFG_DEEPSLEEP);
+		break;
+	}
+
+	/* disable regulators */
+	mxt_regulator_disable(data);
+	/* disable gpios */
+	mxt_gpio_enable(data, false);
+
+	data->suspended = true;
+}
+
+static int mxt_input_open(struct input_dev *dev)
+{
+#if !defined(CONFIG_DRM)
+	struct mxt_data *data = input_get_drvdata(dev);
+
+	mxt_start(data);
+#endif
+	return 0;
+}
+
+static void mxt_input_close(struct input_dev *dev)
+{
+#if !defined(CONFIG_DRM)
+	struct mxt_data *data = input_get_drvdata(dev);
+
+	mxt_stop(data);
+#endif
+}
+
+static int mxt_parse_device_properties(struct mxt_data *data)
+{
+	static const char keymap_property[] = "linux,gpio-keymap";
+	struct device *dev = &data->client->dev;
+	u32 *keymap;
+	int n_keys;
+	int error;
+
+	if (device_property_present(dev, keymap_property)) {
+		n_keys = device_property_read_u32_array(dev, keymap_property,
+							NULL, 0);
+		if (n_keys <= 0) {
+			error = n_keys < 0 ? n_keys : -EINVAL;
+			dev_err(dev, "invalid/malformed '%s' property: %d\n",
+				keymap_property, error);
+			return error;
+		}
+
+		keymap = devm_kmalloc_array(dev, n_keys, sizeof(*keymap),
+					    GFP_KERNEL);
+		if (!keymap)
+			return -ENOMEM;
+
+		error = device_property_read_u32_array(dev, keymap_property,
+						       keymap, n_keys);
+		if (error) {
+			dev_err(dev, "failed to parse '%s' property: %d\n",
+				keymap_property, error);
+			return error;
+		}
+
+		data->t19_keymap = keymap;
+		data->t19_num_keys = n_keys;
+	}
+
+	return 0;
+}
+
+#ifdef CONFIG_OF
+
+static int mxt_check_dedicated_touch(struct device_node *dt, const char *prop,
+		const char *active_prop)
+{
+	const char *active_touch;
+	const char *compatible;
+	char *temp;
+	int ret = 0;
+
+	ret = of_property_read_string(dt->parent, active_prop, &active_touch);
+	if (ret < 0) {
+		pr_info(" %s:not dedicated active touch\n", __func__);
+		return -ENODEV;
+	}
+
+	ret = of_property_read_string(dt, prop, &compatible);
+	if (ret < 0) {
+		pr_err(" %s:fail to read %s %d\n", __func__, "compatible", ret);
+		return -ENODEV;
+	}
+
+	temp = strnstr(active_touch, compatible, strlen(active_touch));
+	if (!temp) {
+		pr_info(" %s:no match compatible, %s, %s\n",
+			__func__, compatible, active_touch);
+		return -ENODEV;
+
+	}
+
+	return ret;
+}
+
+#ifdef CONFIG_DRM
+static struct drm_panel *active_panel;
+static int mxt_check_dt(struct device_node *np)
+{
+	int i;
+	int count;
+	struct device_node *node;
+	struct drm_panel *panel;
+
+	count = of_count_phandle_with_args(np, "panel", NULL);
+	if (count <= 0)
+		return 0;
+
+	for (i = 0; i < count; i++) {
+		node = of_parse_phandle(np, "panel", i);
+		panel = of_drm_find_panel(node);
+		of_node_put(node);
+		if (!IS_ERR(panel)) {
+			active_panel = panel;
+			return 0;
+		}
+	}
+
+	return PTR_ERR(panel);
+}
+#endif
+
+static int mxt_get_dt_coords(struct device *dev, char *name,
+				struct mxt_data *data)
+{
+	u32 coords[MXT_COORDS_ARR_SIZE];
+	struct property *prop;
+	struct device_node *np = dev->of_node;
+	size_t coords_size;
+	int error;
+
+	prop = of_find_property(np, name, NULL);
+	if (!prop)
+		return -EINVAL;
+	if (!prop->value)
+		return -ENODATA;
+
+	coords_size = prop->length / sizeof(u32);
+	if (coords_size != MXT_COORDS_ARR_SIZE) {
+		dev_err(dev, "invalid %s\n", name);
+		return -EINVAL;
+	}
+
+	error = of_property_read_u32_array(np, name, coords, coords_size);
+	if (error && (error != -EINVAL)) {
+		dev_err(dev, "Unable to read %s\n", name);
+		return error;
+	}
+
+	if (strcmp(name, "atmel,panel-coords") == 0) {
+		data->panel_minx = coords[0];
+		data->panel_miny = coords[1];
+		data->panel_maxx = coords[2];
+		data->panel_maxy = coords[3];
+	} else if (strcmp(name, "atmel,display-coords") == 0) {
+		data->disp_minx = coords[0];
+		data->disp_miny = coords[1];
+		data->disp_maxx = coords[2];
+		data->disp_maxy = coords[3];
+	} else {
+		dev_err(dev, "unsupported property %s\n", name);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+#endif
+
+static const struct dmi_system_id chromebook_T9_suspend_dmi[] = {
+	{
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "Link"),
+		},
+	},
+	{
+		.matches = {
+			DMI_MATCH(DMI_PRODUCT_NAME, "Peppy"),
+		},
+	},
+	{ }
+};
+
+#ifdef CONFIG_DRM
+
+static void mxt_panel_notifier_callback(enum panel_event_notifier_tag tag,
+		struct panel_event_notification *notification,
+		void *client_data)
+{
+	struct mxt_data *mxt = client_data;
+	struct input_dev *input_dev;
+
+	if (!notification) {
+		pr_err("Invalid notification\n");
+		return;
+	}
+
+	if (!mxt) {
+		pr_err("Invalid mxt data\n");
+		return;
+	}
+
+	pr_debug("Notification type:%d, early_trigger:%d\n",
+			notification->notif_type,
+			notification->notif_data.early_trigger);
+
+	input_dev = mxt->input_dev;
+
+	switch (notification->notif_type) {
+	case DRM_PANEL_EVENT_UNBLANK:
+		if (!notification->notif_data.early_trigger) {
+			if (input_dev)
+				mutex_lock(&input_dev->mutex);
+
+			mxt_start(mxt);
+
+			if (input_dev)
+				mutex_unlock(&input_dev->mutex);
+		}
+		break;
+	case DRM_PANEL_EVENT_BLANK:
+		if (input_dev)
+			mutex_lock(&input_dev->mutex);
+
+		mxt_stop(mxt);
+
+		if (input_dev)
+			mutex_unlock(&input_dev->mutex);
+		break;
+	default:
+		break;
+	}
+}
+
+static void mxt_register_for_panel_events(struct device_node *dt,
+		struct mxt_data *data)
+{
+	void *cookie = NULL;
+
+	cookie = panel_event_notifier_register(
+			PANEL_EVENT_NOTIFICATION_PRIMARY,
+			PANEL_EVENT_NOTIFIER_CLIENT_PRIMARY_TOUCH,
+			active_panel, &mxt_panel_notifier_callback, data);
+	if (!cookie) {
+		pr_err("Failed to register for panel events\n");
+		return;
+	}
+	pr_debug("registered for panel notifications on panel: 0x%x\n",
+		       active_panel);
+
+	data->notifier_cookie = cookie;
+}
+#endif
+
+static int mxt_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+	struct mxt_data *data;
+	int error = 0;
+#ifdef CONFIG_OF
+	struct device_node *dt = client->dev.of_node;
+#endif
+	/*
+	 * Ignore devices that do not have device properties attached to
+	 * them, as we need help determining whether we are dealing with
+	 * touch screen or touchpad.
+	 *
+	 * So far on x86 the only users of Atmel touch controllers are
+	 * Chromebooks, and chromeos_laptop driver will ensure that
+	 * necessary properties are provided (if firmware does not do that).
+	 */
+	if (!device_property_present(&client->dev, "compatible"))
+		return -ENXIO;
+
+	/*
+	 * Ignore ACPI devices representing bootloader mode.
+	 *
+	 * This is a bit of a hack: Google Chromebook BIOS creates ACPI
+	 * devices for both application and bootloader modes, but we are
+	 * interested in application mode only (if device is in bootloader
+	 * mode we'll end up switching into application anyway). So far
+	 * application mode addresses were all above 0x40, so we'll use it
+	 * as a threshold.
+	 */
+	if (ACPI_COMPANION(&client->dev) && client->addr < 0x40)
+		return -ENXIO;
+
+#ifdef CONFIG_OF
+#ifdef CONFIG_DRM
+	error = mxt_check_dt(dt);
+	if (error == -EPROBE_DEFER)
+		return error;
+
+	if (error) {
+		if (!(mxt_check_dedicated_touch(dt, "compatible",
+			"qcom,i2c-touch-active")))
+			return -EPROBE_DEFER;
+		else
+			return -ENODEV;
+	}
+#else
+	if (mxt_check_dedicated_touch(dt, "compatible",
+				"qcom,i2c-touch-active") < 0)
+		return -ENODEV;
+#endif
+#endif
+	data = devm_kzalloc(&client->dev, sizeof(struct mxt_data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	snprintf(data->phys, sizeof(data->phys), "i2c-%u-%04x/input0",
+		 client->adapter->nr, client->addr);
+
+	data->client = client;
+	data->irq = client->irq;
+	i2c_set_clientdata(client, data);
+
+#ifdef CONFIG_OF
+	error = mxt_get_dt_coords(&client->dev, "atmel,display-coords", data);
+	if (error)
+		return error;
+
+	data->xy_switch = of_property_read_bool(dt, "atmel,xy_switch");
+	data->invertx = of_property_read_bool(dt, "atmel,invertx");
+	data->inverty = of_property_read_bool(dt, "atmel,inverty");
+#endif
+
+	data->cmd_buf = devm_kzalloc(&client->dev, 64, GFP_KERNEL);
+	if (!data->cmd_buf)
+		return -ENOMEM;
+	data->read_buf = data->cmd_buf + 32;
+
+	init_completion(&data->bl_completion);
+	init_completion(&data->reset_completion);
+	init_completion(&data->crc_completion);
+
+#if defined(CONFIG_OF) && defined(CONFIG_DRM)
+	data->suspend_mode = MXT_SUSPEND_T9_CTRL;
+#else
+	data->suspend_mode = dmi_check_system(chromebook_T9_suspend_dmi) ?
+		MXT_SUSPEND_T9_CTRL : MXT_SUSPEND_DEEP_SLEEP;
+#endif
+
+	error = mxt_parse_device_properties(data);
+	if (error)
+		return error;
+
+	error = mxt_pinctrl_init(data);
+	if (error)
+		dev_info(&client->dev, "No pinctrl support\n");
+
+	data->reset_gpio = devm_gpiod_get_optional(&client->dev,
+						"reset", GPIOD_OUT_LOW);
+	data->irq_gpio = devm_gpiod_get_optional(&client->dev,
+						"irq", GPIOD_IN);
+	if (IS_ERR(data->reset_gpio)) {
+		error = PTR_ERR(data->reset_gpio);
+		dev_err(&client->dev, "Failed to get reset gpio: %d\n", error);
+		return error;
+	}
+
+	gpiod_direction_input(data->irq_gpio);
+	gpiod_direction_output(data->reset_gpio, 0);
+
+	error = mxt_regulator_parse(data);
+	if (error) {
+		dev_err(&client->dev, "Failed to parse regulators\n");
+		return error;
+	}
+
+	error = mxt_regulator_enable(data);
+	if (error) {
+		dev_err(&client->dev, "Error %d enabling regulators\n", error);
+		return error;
+	}
+
+	error = devm_request_threaded_irq(&client->dev, client->irq,
+					  NULL, mxt_interrupt, IRQF_ONESHOT,
+					  client->name, data);
+	if (error) {
+		dev_err(&client->dev, "Failed to register interrupt\n");
+		goto err_disable_regulator;
+	}
+
+	disable_irq(client->irq);
+
+	if (data->reset_gpio) {
+		gpiod_set_value(data->reset_gpio, 0);
+		msleep(MXT_RESET_GPIO_TIME);
+		gpiod_set_value(data->reset_gpio, 1);
+		msleep(MXT_RESET_INVALID_CHG);
+	}
+
+	error = mxt_gpio_enable(data, true);
+	if (error)
+		dev_err(&client->dev, "Failed to configure gpios\n");
+
+	data->irq = data->client->irq =
+				gpiod_to_irq(data->irq_gpio);
+
+	error = mxt_initialize(data);
+	if (error)
+		goto err_disable_regulator;
+
+	error = sysfs_create_group(&client->dev.kobj, &mxt_attr_group);
+	if (error) {
+		dev_err(&client->dev, "Failure %d creating sysfs group\n",
+			error);
+		goto err_free_object;
+	}
+#ifdef CONFIG_DRM
+	if (active_panel)
+		mxt_register_for_panel_events(dt, data);
+#endif
+	return 0;
+
+err_free_object:
+	mxt_free_input_device(data);
+	mxt_free_object_table(data);
+
+err_disable_regulator:
+	mxt_regulator_disable(data);
+	return error;
+}
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0))
+static void mxt_remove(struct i2c_client *client)
+{
+	struct mxt_data *data = i2c_get_clientdata(client);
+
+#ifdef CONFIG_DRM
+	if (active_panel && data->notifier_cookie)
+		panel_event_notifier_unregister(data->notifier_cookie);
+#endif
+	disable_irq(data->irq);
+	mxt_regulator_disable(data);
+	sysfs_remove_group(&client->dev.kobj, &mxt_attr_group);
+	mxt_free_input_device(data);
+	mxt_free_object_table(data);
+}
+#else
+static int mxt_remove(struct i2c_client *client)
+{
+	struct mxt_data *data = i2c_get_clientdata(client);
+
+#ifdef CONFIG_DRM
+	if (active_panel && data->notifier_cookie)
+		panel_event_notifier_unregister(data->notifier_cookie);
+#endif
+	disable_irq(data->irq);
+	mxt_regulator_disable(data);
+	sysfs_remove_group(&client->dev.kobj, &mxt_attr_group);
+	mxt_free_input_device(data);
+	mxt_free_object_table(data);
+
+	return 0;
+}
+#endif
+
+static int __maybe_unused mxt_suspend(struct device *dev)
+{
+#if !defined(CONFIG_DRM)
+	struct i2c_client *client = to_i2c_client(dev);
+	struct mxt_data *data = i2c_get_clientdata(client);
+	struct input_dev *input_dev = data->input_dev;
+
+	if (!input_dev)
+		return 0;
+
+	mutex_lock(&input_dev->mutex);
+
+	if (input_dev->users)
+		mxt_stop(data);
+
+	mutex_unlock(&input_dev->mutex);
+
+	disable_irq(data->irq);
+#endif
+
+	return 0;
+}
+
+static int __maybe_unused mxt_resume(struct device *dev)
+{
+#if !defined(CONFIG_DRM)
+	struct i2c_client *client = to_i2c_client(dev);
+	struct mxt_data *data = i2c_get_clientdata(client);
+	struct input_dev *input_dev = data->input_dev;
+
+	if (!input_dev)
+		return 0;
+
+	enable_irq(data->irq);
+
+	mutex_lock(&input_dev->mutex);
+
+	if (input_dev->users)
+		mxt_start(data);
+
+	mutex_unlock(&input_dev->mutex);
+#endif
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(mxt_pm_ops, mxt_suspend, mxt_resume);
+
+static const struct of_device_id mxt_of_match[] = {
+	{ .compatible = "atmel,maxtouch", },
+	/* Compatibles listed below are deprecated */
+	{ .compatible = "atmel,qt602240_ts", },
+	{ .compatible = "atmel,atmel_mxt_ts", },
+	{ .compatible = "atmel,atmel_mxt_tp", },
+	{ .compatible = "atmel,mXT224", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, mxt_of_match);
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id mxt_acpi_id[] = {
+	{ "ATML0000", 0 },	/* Touchpad */
+	{ "ATML0001", 0 },	/* Touchscreen */
+	{ }
+};
+MODULE_DEVICE_TABLE(acpi, mxt_acpi_id);
+#endif
+
+static const struct i2c_device_id mxt_id[] = {
+	{ "qt602240_ts", 0 },
+	{ "atmel_mxt_ts", 0 },
+	{ "atmel_mxt_tp", 0 },
+	{ "maxtouch", 0 },
+	{ "mXT224", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, mxt_id);
+
+static struct i2c_driver mxt_driver = {
+	.driver = {
+		.name	= "atmel_mxt_ts",
+		.of_match_table = mxt_of_match,
+		.acpi_match_table = ACPI_PTR(mxt_acpi_id),
+		.pm	= &mxt_pm_ops,
+	},
+	.probe		= mxt_probe,
+	.remove		= mxt_remove,
+	.id_table	= mxt_id,
+};
+
+module_i2c_driver(mxt_driver);
+
+/* Module information */
+MODULE_AUTHOR("Joonyoung Shim <[email protected]>");
+MODULE_DESCRIPTION("Atmel maXTouch Touchscreen driver");
+MODULE_LICENSE("GPL");

+ 7 - 0
qcom/opensource/touch-drivers/config/gki_bengaltouch.conf

@@ -0,0 +1,7 @@
+export CONFIG_MSM_TOUCH=m
+export CONFIG_TOUCHSCREEN_SYNAPTICS_TCM=y
+export CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_I2C=y
+export CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_CORE=y
+export CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_TOUCH=y
+export CONFIG_TOUCH_FOCALTECH=y
+export CONFIG_TOUCHSCREEN_FTS_DIRECTORY="focaltech_touch"

+ 11 - 0
qcom/opensource/touch-drivers/config/gki_bengaltouchconf.h

@@ -0,0 +1,11 @@
+/*
+* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
+* SPDX-License-Identifier: GPL-2.0-only
+*/
+
+#define CONFIG_TOUCHSCREEN_SYNAPTICS_TCM 1
+#define CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_I2C 1
+#define CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_CORE 1
+#define CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_TOUCH 1
+#define CONFIG_TOUCH_FOCALTECH 1
+#define CONFIG_TOUCHSCREEN_FTS_DIRECTORY "focaltech_touch"

+ 10 - 0
qcom/opensource/touch-drivers/config/gki_blairtouch.conf

@@ -0,0 +1,10 @@
+export CONFIG_MSM_TOUCH=m
+export CONFIG_TOUCH_FOCALTECH=y
+export CONFIG_TOUCHSCREEN_NT36XXX_I2C=y
+export CONFIG_TOUCHSCREEN_SYNAPTICS_TCM=y
+export CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_I2C=y
+export CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_CORE=y
+export CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_TOUCH=y
+export CONFIG_TOUCHSCREEN_GOODIX_BRL=y
+export CONFIG_TOUCHSCREEN_FTS_DIRECTORY="focaltech_touch"
+export CONFIG_FTS_TRUSTED_TOUCH=n

+ 13 - 0
qcom/opensource/touch-drivers/config/gki_blairtouchconf.h

@@ -0,0 +1,13 @@
+/*
+* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
+* SPDX-License-Identifier: GPL-2.0-only
+*/
+
+#define CONFIG_TOUCH_FOCALTECH 1
+#define CONFIG_TOUCHSCREEN_NT36XXX_I2C 1
+#define CONFIG_TOUCHSCREEN_SYNAPTICS_TCM 1
+#define CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_I2C 1
+#define CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_CORE 1
+#define CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_TOUCH 1
+#define CONFIG_TOUCHSCREEN_GOODIX_BRL 1
+#define CONFIG_TOUCHSCREEN_FTS_DIRECTORY "focaltech_touch"

+ 2 - 0
qcom/opensource/touch-drivers/config/gki_crowtouch.conf

@@ -0,0 +1,2 @@
+export CONFIG_MSM_TOUCH=m
+export CONFIG_TOUCHSCREEN_GOODIX_BRL=y

+ 6 - 0
qcom/opensource/touch-drivers/config/gki_crowtouchconf.h

@@ -0,0 +1,6 @@
+/*
+* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
+* SPDX-License-Identifier: GPL-2.0-only
+*/
+
+#define CONFIG_TOUCHSCREEN_GOODIX_BRL 1

+ 5 - 0
qcom/opensource/touch-drivers/config/gki_kalamatouch.conf

@@ -0,0 +1,5 @@
+export CONFIG_TOUCHSCREEN_NT36XXX_I2C=y
+export CONFIG_TOUCHSCREEN_GOODIX_BRL=y
+export CONFIG_TOUCHSCREEN_ATMEL_MXT=y
+export CONFIG_TOUCHSCREEN_DUMMY=y
+export CONFIG_MSM_TOUCH=m

+ 9 - 0
qcom/opensource/touch-drivers/config/gki_kalamatouchconf.h

@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2021, Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#define CONFIG_TOUCHSCREEN_NT36XXX_I2C 1
+#define CONFIG_TOUCHSCREEN_GOODIX_BRL 1
+#define CONFIG_TOUCHSCREEN_ATMEL_MXT 1
+

+ 6 - 0
qcom/opensource/touch-drivers/config/gki_khajetouch.conf

@@ -0,0 +1,6 @@
+export CONFIG_TOUCHSCREEN_SYNAPTICS_TCM=y
+export CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_I2C=y
+export CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_CORE=y
+export CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_TOUCH=y
+export CONFIG_TOUCHSCREEN_NT36XXX_I2C=y
+export CONFIG_MSM_TOUCH=m

+ 11 - 0
qcom/opensource/touch-drivers/config/gki_khajetouchconf.h

@@ -0,0 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#define CONFIG_TOUCHSCREEN_SYNAPTICS_TCM 1
+#define CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_I2C 1
+#define CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_CORE 1
+#define CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_TOUCH 1
+#define CONFIG_TOUCHSCREEN_NT36XXX_I2C 1
+

+ 4 - 0
qcom/opensource/touch-drivers/config/gki_konatouch.conf

@@ -0,0 +1,4 @@
+export CONFIG_MSM_TOUCH=m
+export CONFIG_TOUCH_FOCALTECH=y
+export CONFIG_TOUCHSCREEN_FTS_DIRECTORY="focaltech_touch"
+export CONFIG_FTS_TRUSTED_TOUCH=n

+ 7 - 0
qcom/opensource/touch-drivers/config/gki_konatouchconf.h

@@ -0,0 +1,7 @@
+/*
+* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
+* SPDX-License-Identifier: GPL-2.0-only
+*/
+
+#define CONFIG_TOUCH_FOCALTECH 1
+#define CONFIG_TOUCHSCREEN_FTS_DIRECTORY "focaltech_touch"

+ 7 - 0
qcom/opensource/touch-drivers/config/gki_monacotouch.conf

@@ -0,0 +1,7 @@
+export CONFIG_MSM_TOUCH=m
+export CONFIG_TOUCHSCREEN_PARADE=y
+export CONFIG_TOUCHSCREEN_PARADE_DEVICETREE_SUPPORT=y
+export CONFIG_TOUCHSCREEN_PARADE_I2C=y
+export CONFIG_TOUCHSCREEN_PARADE_DEVICE_ACCESS=y
+export CONFIG_TOUCHSCREEN_RM_TS=y
+export CONFIG_TOUCHSCREEN_MSM_GLINK=y

+ 8 - 0
qcom/opensource/touch-drivers/config/gki_monacotouchconf.h

@@ -0,0 +1,8 @@
+#define CONFIG_TOUCHSCREEN_PARADE 1
+#define CONFIG_TOUCHSCREEN_PARADE_DEVICETREE_SUPPORT 1
+#define CONFIG_TOUCHSCREEN_PARADE_I2C 1
+#define CONFIG_TOUCHSCREEN_PARADE_DEVICE_ACCESS 1
+#define CONFIG_TOUCHSCREEN_PARADE_BUTTON 1
+#define CONFIG_TOUCHSCREEN_PARADE_PROXIMITY 1
+#define CONFIG_TOUCHSCREEN_RM_TS 1
+#define CONFIG_TOUCHSCREEN_MSM_GLINK 1

+ 5 - 0
qcom/opensource/touch-drivers/config/gki_pineappletouch.conf

@@ -0,0 +1,5 @@
+export CONFIG_TOUCHSCREEN_NT36XXX_I2C=y
+export CONFIG_TOUCHSCREEN_GOODIX_BRL=y
+export CONFIG_TOUCHSCREEN_ATMEL_MXT=y
+export CONFIG_TOUCHSCREEN_DUMMY=y
+export CONFIG_MSM_TOUCH=m

+ 8 - 0
qcom/opensource/touch-drivers/config/gki_pineappletouchconf.h

@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#define CONFIG_TOUCHSCREEN_NT36XXX_I2C 1
+#define CONFIG_TOUCHSCREEN_GOODIX_BRL 1
+#define CONFIG_TOUCHSCREEN_ATMEL_MXT 1

+ 5 - 0
qcom/opensource/touch-drivers/config/gki_trinkettouch.conf

@@ -0,0 +1,5 @@
+export CONFIG_MSM_TOUCH=m
+export CONFIG_TOUCHSCREEN_SYNAPTICS_TCM=y
+export CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_I2C=y
+export CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_CORE=y
+export CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_TOUCH=y

+ 9 - 0
qcom/opensource/touch-drivers/config/gki_trinkettouchconf.h

@@ -0,0 +1,9 @@
+/*
+* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
+* SPDX-License-Identifier: GPL-2.0-only
+*/
+
+#define CONFIG_TOUCHSCREEN_SYNAPTICS_TCM 1
+#define CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_I2C 1
+#define CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_CORE 1
+#define CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_TOUCH 1

+ 6 - 0
qcom/opensource/touch-drivers/config/gki_waipiotouch.conf

@@ -0,0 +1,6 @@
+export CONFIG_TOUCH_FOCALTECH=y
+export CONFIG_TOUCHSCREEN_FTS_DIRECTORY="focaltech_touch"
+export CONFIG_FTS_TRUSTED_TOUCH=y
+export CONFIG_TOUCHSCREEN_SYNAPTICS_DSX=y
+export CONFIG_TOUCHSCREEN_NT36XXX_I2C=y
+export CONFIG_TOUCH_BUILD=m

+ 10 - 0
qcom/opensource/touch-drivers/config/gki_waipiotouchconf.h

@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2021, The Linux Foundation. All rights reserved.
+ */
+
+#define CONFIG_TOUCH_FOCALTECH 1
+#define CONFIG_TOUCHSCREEN_FTS_DIRECTORY "focaltech_touch"
+#define CONFIG_FTS_TRUSTED_TOUCH 1
+#define CONFIG_TOUCHSCREEN_SYNAPTICS_DSX 1
+#define CONFIG_TOUCHSCREEN_NT36XXX_I2C 1

+ 87 - 0
qcom/opensource/touch-drivers/dummy_touch/dummy_touch.c

@@ -0,0 +1,87 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/platform_device.h>
+#include <linux/mod_devicetable.h>
+
+MODULE_DESCRIPTION("QTI dummy touchscreen driver");
+MODULE_LICENSE("GPL v2");
+
+#define DEVICE_COMPATIBLE "qti,dummy_ts"
+#define DRIVER_NAME "qti_dummy_ts"
+
+static int dummy_touch_probe(struct platform_device *device)
+{
+	struct input_dev *touch_dev = NULL;
+	int rc;
+
+	touch_dev = input_allocate_device();
+	if (!touch_dev) {
+		pr_err("%s: input device allocate failed\n", __func__);
+		return -ENOMEM;
+	}
+
+	touch_dev->name = "qti_dummy_ts";
+	touch_dev->id.bustype = BUS_VIRTUAL;
+	touch_dev->evbit[0] = BIT_MASK(EV_KEY);
+	touch_dev->keybit[BIT_WORD(BTN_0)] = BIT_MASK(BTN_0);
+	touch_dev->dev.parent = NULL;
+
+	rc = input_register_device(touch_dev);
+	if (rc) {
+		pr_err("%s: touch device register failed, rc = %d\n", __func__, rc);
+		input_free_device(touch_dev);
+		return rc;
+	}
+
+	pr_info("qti_dummy_ts device registered\n");
+
+	platform_set_drvdata(device, (void *)touch_dev);
+
+	return 0;
+}
+
+static int dummy_touch_remove(struct platform_device *device)
+{
+	struct input_dev *touch_dev = NULL;
+
+	touch_dev = (struct input_dev *)platform_get_drvdata(device);
+	if (touch_dev)
+		input_free_device(touch_dev);
+
+	return 0;
+}
+
+static const struct of_device_id  dummy_touch_id[] = {
+	{.compatible = DEVICE_COMPATIBLE},
+	{}
+};
+
+static struct platform_driver dummy_touch_driver = {
+
+	.driver = {
+		.name = DRIVER_NAME,
+		.of_match_table = dummy_touch_id,
+	},
+	.probe = dummy_touch_probe,
+	.remove = dummy_touch_remove,
+};
+
+static int __init dummy_touch_init(void)
+{
+	platform_driver_register(&dummy_touch_driver);
+
+	return 0;
+}
+
+static void __exit dummy_touch_exit(void)
+{
+	platform_driver_unregister(&dummy_touch_driver);
+}
+
+late_initcall(dummy_touch_init);
+module_exit(dummy_touch_exit);

+ 167 - 0
qcom/opensource/touch-drivers/focaltech_touch/focaltech_common.h

@@ -0,0 +1,167 @@
+/*
+ *
+ * FocalTech fts TouchScreen driver.
+ *
+ * Copyright (c) 2012-2019, Focaltech Ltd. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+/*****************************************************************************
+*
+* File Name: focaltech_common.h
+*
+* Author: Focaltech Driver Team
+*
+* Created: 2016-08-16
+*
+* Abstract:
+*
+* Reference:
+*
+*****************************************************************************/
+
+#ifndef __LINUX_FOCALTECH_COMMON_H__
+#define __LINUX_FOCALTECH_COMMON_H__
+
+#include "focaltech_config.h"
+
+/*****************************************************************************
+* Macro definitions using #define
+*****************************************************************************/
+#define FTS_DRIVER_VERSION      "Focaltech V3.2 20200422"
+
+#define BYTE_OFF_0(x)           (u8)((x) & 0xFF)
+#define BYTE_OFF_8(x)           (u8)(((x) >> 8) & 0xFF)
+#define BYTE_OFF_16(x)          (u8)(((x) >> 16) & 0xFF)
+#define BYTE_OFF_24(x)          (u8)(((x) >> 24) & 0xFF)
+#define FLAGBIT(x)              (0x00000001 << (x))
+#define FLAGBITS(x, y)          ((0xFFFFFFFF >> (32 - (y) - 1)) & (0xFFFFFFFF << (x)))
+
+#define FLAG_ICSERIALS_LEN      8
+#define FLAG_HID_BIT            10
+#define FLAG_IDC_BIT            11
+
+#define IC_SERIALS(type)        ((type) & FLAGBITS(0, FLAG_ICSERIALS_LEN-1))
+#define IC_TO_SERIALS(x)        ((x) & FLAGBITS(0, FLAG_ICSERIALS_LEN-1))
+#define FTS_CHIP_IDC(type)            (((type) & FLAGBIT(FLAG_IDC_BIT)) == FLAGBIT(FLAG_IDC_BIT))
+#define FTS_HID_SUPPORTTED(type)      (((type) & FLAGBIT(FLAG_HID_BIT)) == FLAGBIT(FLAG_HID_BIT))
+
+#define FILE_NAME_LENGTH                    128
+#define ENABLE                              1
+#define DISABLE                             0
+#define VALID                               1
+#define INVALID                             0
+#define FTS_CMD_START1                      0x55
+#define FTS_CMD_START2                      0xAA
+#define FTS_CMD_START_DELAY                 12
+#define FTS_CMD_READ_ID                     0x90
+#define FTS_CMD_READ_ID_LEN                 4
+#define FTS_CMD_READ_ID_LEN_INCELL          1
+#define FTS_CMD_READ_FW_CONF                0xA8
+/*register address*/
+#define FTS_REG_INT_CNT                     0x8F
+#define FTS_REG_FLOW_WORK_CNT               0x91
+#define FTS_REG_WORKMODE                    0x00
+#define FTS_REG_WORKMODE_FACTORY_VALUE      0x40
+#define FTS_REG_WORKMODE_WORK_VALUE         0x00
+#define FTS_REG_ESDCHECK_DISABLE            0x8D
+#define FTS_REG_CHIP_ID                     0xA3
+#define FTS_REG_CHIP_ID2                    0x9F
+#define FTS_REG_POWER_MODE                  0xA5
+#define FTS_REG_POWER_MODE_SLEEP            0x03
+#define FTS_REG_FW_VER                      0xA6
+#define FTS_REG_VENDOR_ID                   0xA8
+#define FTS_REG_LCD_BUSY_NUM                0xAB
+#define FTS_REG_FACE_DEC_MODE_EN            0xB0
+#define FTS_REG_FACTORY_MODE_DETACH_FLAG    0xB4
+#define FTS_REG_FACE_DEC_MODE_STATUS        0x01
+#define FTS_REG_IDE_PARA_VER_ID             0xB5
+#define FTS_REG_IDE_PARA_STATUS             0xB6
+#define FTS_REG_GLOVE_MODE_EN               0xC0
+#define FTS_REG_COVER_MODE_EN               0xC1
+#define FTS_REG_REPORT_RATE                 0x88
+#define FTS_REG_CHARGER_MODE_EN             0x8B
+#define FTS_REG_GESTURE_EN                  0xD0
+#define FTS_REG_GESTURE_OUTPUT_ADDRESS      0xD3
+#define FTS_REG_MODULE_ID                   0xE3
+#define FTS_REG_LIC_VER                     0xE4
+#define FTS_REG_ESD_SATURATE                0xED
+
+#define FTS_SYSFS_ECHO_ON(buf)      (buf[0] == '1')
+#define FTS_SYSFS_ECHO_OFF(buf)     (buf[0] == '0')
+
+#define kfree_safe(pbuf) do {\
+	if (pbuf) {\
+		kfree(pbuf);\
+		pbuf = NULL;\
+	}\
+} while(0)
+
+/*****************************************************************************
+*  Alternative mode (When something goes wrong,
+* the modules may be able to solve the problem.)
+*****************************************************************************/
+/*
+ * point report check
+ * default: disable
+ */
+#define FTS_POINT_REPORT_CHECK_EN               0
+
+/*****************************************************************************
+* Global variable or extern global variabls/functions
+*****************************************************************************/
+struct ft_chip_t {
+	u64 type;
+	u8 chip_idh;
+	u8 chip_idl;
+	u8 rom_idh;
+	u8 rom_idl;
+	u8 pb_idh;
+	u8 pb_idl;
+	u8 bl_idh;
+	u8 bl_idl;
+};
+
+struct ts_ic_info {
+	bool is_incell;
+	bool hid_supported;
+	struct ft_chip_t ids;
+};
+
+/*****************************************************************************
+* DEBUG function define here
+*****************************************************************************/
+#if FTS_DEBUG_EN
+#define FTS_DEBUG(fmt, args...) do { \
+	printk("[FTS_TS]%s:"fmt"\n", __func__, ##args); \
+} while (0)
+
+#define FTS_FUNC_ENTER() do { \
+	printk("[FTS_TS]%s: Enter\n", __func__); \
+} while (0)
+
+#define FTS_FUNC_EXIT() do { \
+	printk("[FTS_TS]%s: Exit(%d)\n", __func__, __LINE__); \
+} while (0)
+#else /* #if FTS_DEBUG_EN*/
+#define FTS_DEBUG(fmt, args...)
+#define FTS_FUNC_ENTER()
+#define FTS_FUNC_EXIT()
+#endif
+
+#define FTS_INFO(fmt, args...) do { \
+	printk(KERN_INFO "[FTS_TS/I]%s:"fmt"\n", __func__, ##args); \
+} while (0)
+
+#define FTS_ERROR(fmt, args...) do { \
+	printk(KERN_ERR "[FTS_TS/E]%s:"fmt"\n", __func__, ##args); \
+} while (0)
+#endif /* __LINUX_FOCALTECH_COMMON_H__ */

+ 278 - 0
qcom/opensource/touch-drivers/focaltech_touch/focaltech_config.h

@@ -0,0 +1,278 @@
+/*
+ *
+ * FocalTech TouchScreen driver.
+ *
+ * Copyright (c) 2012-2019, FocalTech Systems, Ltd., all rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+/************************************************************************
+*
+* File Name: focaltech_config.h
+*
+* Author: Focaltech Driver Team
+*
+* Created: 2016-08-08
+*
+* Abstract: global configurations
+*
+* Version: v1.0
+*
+************************************************************************/
+#ifndef _LINUX_FOCLATECH_CONFIG_H_
+#define _LINUX_FOCLATECH_CONFIG_H_
+
+/**************************************************/
+/****** G: A, I: B, S: C, U: D  ******************/
+/****** chip type defines, do not modify *********/
+#define _FT8716             0x87160805
+#define _FT8736             0x87360806
+#define _FT8006M            0x80060807
+#define _FT8607             0x86070809
+#define _FT8006U            0x8006D80B
+#define _FT8006S            0x8006A80B
+#define _FT8613             0x8613080C
+#define _FT8719             0x8719080D
+#define _FT8739             0x8739080E
+#define _FT8615             0x8615080F
+#define _FT8201             0x82010810
+#define _FT8006P            0x86220811
+#define _FT7251             0x72510812
+#define _FT7252             0x72520813
+#define _FT8613S            0x8613C814
+#define _FT8756             0x87560815
+#define _FT8302             0x83020816
+#define _FT8009             0x80090817
+#define _FT8656             0x86560818
+#define _FT8006S_AA         0x86320819
+#define _FT7250             0x7250081A
+#define _FT7120             0x7120081B
+#define _FT8720             0x8720081C
+#define _FT8726             0x8726081C
+#define _FT8016             0x8016081D
+
+
+#define _FT5416             0x54160402
+#define _FT5426             0x54260402
+#define _FT5435             0x54350402
+#define _FT5436             0x54360402
+#define _FT5526             0x55260402
+#define _FT5526I            0x5526B402
+#define _FT5446             0x54460402
+#define _FT5346             0x53460402
+#define _FT5446I            0x5446B402
+#define _FT5346I            0x5346B402
+#define _FT7661             0x76610402
+#define _FT7511             0x75110402
+#define _FT7421             0x74210402
+#define _FT7681             0x76810402
+#define _FT3C47U            0x3C47D402
+#define _FT3417             0x34170402
+#define _FT3517             0x35170402
+#define _FT3327             0x33270402
+#define _FT3427             0x34270402
+#define _FT7311             0x73110402
+#define _FT5526_V00         0x5526C402
+
+#define _FT5626             0x56260401
+#define _FT5726             0x57260401
+#define _FT5826B            0x5826B401
+#define _FT5826S            0x5826C401
+#define _FT7811             0x78110401
+#define _FT3D47             0x3D470401
+#define _FT3617             0x36170401
+#define _FT3717             0x37170401
+#define _FT3817B            0x3817B401
+#define _FT3517U            0x3517D401
+
+#define _FT6236U            0x6236D003
+#define _FT6336G            0x6336A003
+#define _FT6336U            0x6336D003
+#define _FT6436U            0x6436D003
+#define _FT6436T            0x6436E003
+
+#define _FT3267             0x32670004
+#define _FT3367             0x33670004
+
+#define _FT3327DQQ_XXX      0x3327D482
+#define _FT5446DQS_XXX      0x5446D482
+
+#define _FT3427_003         0x3427D482
+#define _FT3427G_003        0x3427A482
+#define _FT5446_003         0x5446D482
+#define _FT5446_Q03         0x5446C482
+#define _FT5446_P03         0x5446A481
+#define _FT5426_003         0x5426D482
+#define _FT5526_003         0x5526D482
+
+#define _FT3518             0x35180481
+#define _FT3518U            0x3518D481
+#define _FT3558             0x35580481
+#define _FT3528             0x35280481
+#define _FT5536             0x55360481
+#define _FT5536L            0x5536E481
+#define _FT3418             0x34180481
+
+#define _FT5446U            0x5446D083
+#define _FT5456U            0x5456D083
+#define _FT3417U            0x3417D083
+#define _FT5426U            0x5426D083
+#define _FT3428             0x34280083
+#define _FT3437U            0x3437D083
+
+#define _FT7302             0x73020084
+#define _FT7202             0x72020084
+#define _FT3308             0x33080084
+#define _FT6446             0x64460084
+
+#define _FT6346U            0x6346D085
+#define _FT6346G            0x6346A085
+#define _FT3067             0x30670085
+#define _FT3068             0x30680085
+#define _FT3168             0x31680085
+#define _FT3268             0x32680085
+#define _FT6146             0x61460085
+
+#define _FT5726_003         0x5726D486
+#define _FT5726_V03         0x5726C486
+
+#define _FT3618             0x36180487
+#define _FT5646             0x56460487
+#define _FT3A58             0x3A580487
+#define _FT3B58             0x3B580487
+#define _FT3D58             0x3D580487
+#define _FT5936             0x59360487
+#define _FT5A36             0x5A360487
+#define _FT5B36             0x5B360487
+#define _FT5D36             0x5D360487
+#define _FT5946             0x59460487
+#define _FT5A46             0x5A460487
+#define _FT5B46             0x5B460487
+#define _FT5D46             0x5D460487
+
+#define _FT3658U            0x3658D488
+
+/******************* Enables *********************/
+/*********** 1 to enable, 0 to disable ***********/
+
+/*
+ * show debug log info
+ * enable it for debug, disable it for release
+ */
+#define FTS_DEBUG_EN                            0
+
+/*
+ * Linux MultiTouch Protocol
+ * 1: Protocol B(default), 0: Protocol A
+ */
+#define FTS_MT_PROTOCOL_B_EN                    1
+
+/*
+ * Report Pressure in multitouch
+ * 1:enable(default),0:disable
+*/
+#define FTS_REPORT_PRESSURE_EN                  1
+
+/*
+ * Gesture function enable
+ * default: disable
+ */
+#define FTS_GESTURE_EN                          0
+
+/*
+ * ESD check & protection
+ * default: disable
+ */
+#define FTS_ESDCHECK_EN                         0
+
+/*
+ * Production test enable
+ * 1: enable, 0:disable(default)
+ */
+#define FTS_TEST_EN                             0
+
+/*
+ * Pinctrl enable
+ * default: disable
+ */
+#define FTS_PINCTRL_EN                          1
+
+/*
+ * Customer power enable
+ * enable it when customer need control TP power
+ * default: disable
+ */
+#define FTS_POWER_SOURCE_CUST_EN                1
+
+/****************************************************/
+
+/********************** Upgrade ****************************/
+/*
+ * auto upgrade
+ */
+#define FTS_AUTO_UPGRADE_EN                     1
+
+/*
+ * auto upgrade for lcd cfg
+ */
+#define FTS_AUTO_LIC_UPGRADE_EN                 0
+
+/*
+ * Numbers of modules support
+ */
+#define FTS_GET_MODULE_NUM                      2
+
+/*
+ * module_id: mean vendor_id generally, also maybe gpio or lcm_id...
+ * If means vendor_id, the FTS_MODULE_ID = PANEL_ID << 8 + VENDOR_ID
+ * FTS_GET_MODULE_NUM == 0/1, no check module id, you may ignore them
+ * FTS_GET_MODULE_NUM >= 2, compatible with FTS_MODULE2_ID
+ * FTS_GET_MODULE_NUM >= 3, compatible with FTS_MODULE3_ID
+ */
+#define FTS_MODULE_ID                          0x0000
+#define FTS_MODULE2_ID                         0xd566
+#define FTS_MODULE3_ID                         0x0000
+
+/*
+ * Need set the following when get firmware via firmware_request()
+ * For example: if module'vendor is tianma,
+ * #define FTS_MODULE_NAME                        "tianma"
+ * then file_name will be "focaltech_ts_fw_tianma"
+ * You should rename fw to "focaltech_ts_fw_tianma", and push it into
+ * etc/firmware or by customers
+ */
+#define FTS_MODULE_NAME                        "gvo"
+#define FTS_MODULE2_NAME                       "jdi"
+#define FTS_MODULE3_NAME                       ""
+
+/*
+ * FW.i file for auto upgrade, you must replace it with your own
+ * define your own fw_file, the sample one to be replaced is invalid
+ * NOTE: if FTS_GET_MODULE_NUM > 1, it's the fw corresponding with FTS_VENDOR_ID
+ */
+#define FTS_UPGRADE_FW_FILE                    "include/firmware/fw_sample.i"
+
+/*
+ * if FTS_GET_MODULE_NUM >= 2, fw corrsponding with FTS_VENDOR_ID2
+ * define your own fw_file, the sample one is invalid
+ */
+#define FTS_UPGRADE_FW2_FILE                   "include/firmware/fw_sample.i"
+
+/*
+ * if FTS_GET_MODULE_NUM >= 3, fw corrsponding with FTS_VENDOR_ID3
+ * define your own fw_file, the sample one is invalid
+ */
+#define FTS_UPGRADE_FW3_FILE                   "include/firmware/fw_sample.i"
+
+/*********************************************************/
+
+#endif /* _LINUX_FOCLATECH_CONFIG_H_ */

+ 3423 - 0
qcom/opensource/touch-drivers/focaltech_touch/focaltech_core.c

@@ -0,0 +1,3423 @@
+/*
+ *
+ * FocalTech TouchScreen driver.
+ *
+ * Copyright (c) 2012-2019, FocalTech Systems, Ltd., all rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+/*****************************************************************************
+*
+* File Name: focaltech_core.c
+*
+* Author: Focaltech Driver Team
+*
+* Created: 2016-08-08
+*
+* Abstract: entrance for focaltech ts driver
+*
+* Version: V1.0
+*
+*****************************************************************************/
+
+/*****************************************************************************
+* Included header files
+*****************************************************************************/
+#include <linux/module.h>
+#include <linux/irq.h>
+#include <linux/init.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/of_irq.h>
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+#include <linux/of_irq.h>
+#include <linux/soc/qcom/panel_event_notifier.h>
+#if defined(CONFIG_DRM)
+#include <drm/drm_panel.h>
+#elif defined(CONFIG_FB)
+#include <linux/notifier.h>
+#include <linux/fb.h>
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
+#include <linux/earlysuspend.h>
+#define FTS_SUSPEND_LEVEL 1     /* Early-suspend level */
+#endif
+#include "focaltech_core.h"
+
+#if defined(CONFIG_FTS_TRUSTED_TOUCH)
+#include <linux/atomic.h>
+#include <linux/clk.h>
+#include <linux/pm_runtime.h>
+#include <linux/debugfs.h>
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+#include <linux/kobject.h>
+#include <linux/sysfs.h>
+#include "linux/gunyah/gh_msgq.h"
+#include "linux/gunyah/gh_rm_drv.h"
+#include <linux/sort.h>
+#include <linux/pinctrl/qcom-pinctrl.h>
+#endif
+
+/*****************************************************************************
+* Private constant and macro definitions using #define
+*****************************************************************************/
+#define FTS_DRIVER_NAME                     "fts_ts"
+#define INTERVAL_READ_REG                   200  /* unit:ms */
+#define TIMEOUT_READ_REG                    1000 /* unit:ms */
+#if FTS_POWER_SOURCE_CUST_EN
+#define FTS_VTG_MIN_UV                      3000000
+#define FTS_VTG_MAX_UV                      3300000
+#define FTS_LOAD_MAX_UA                     30000
+#define FTS_LOAD_AVDD_UA                    10000
+#define FTS_LOAD_DISABLE_UA                 0
+#define FTS_I2C_VTG_MIN_UV                  1800000
+#define FTS_I2C_VTG_MAX_UV                  1800000
+#endif
+
+/*****************************************************************************
+* Global variable or extern global variabls/functions
+*****************************************************************************/
+struct fts_ts_data *fts_data;
+
+#if defined(CONFIG_DRM)
+static struct drm_panel *active_panel;
+static void fts_ts_panel_notifier_callback(enum panel_event_notifier_tag tag,
+		 struct panel_event_notification *event, void *client_data);
+#endif
+
+static struct ft_chip_t ctype[] = {
+	{0x88, 0x56, 0x52, 0x00, 0x00, 0x00, 0x00, 0x56, 0xB2},
+	{0x81, 0x54, 0x52, 0x54, 0x52, 0x00, 0x00, 0x54, 0x5C},
+	{0x1C, 0x87, 0x26, 0x87, 0x20, 0x87, 0xA0, 0x00, 0x00},
+};
+
+/*****************************************************************************
+* Static function prototypes
+*****************************************************************************/
+static int fts_ts_suspend(struct device *dev);
+static int fts_ts_resume(struct device *dev);
+static irqreturn_t fts_irq_handler(int irq, void *data);
+static int fts_ts_probe_delayed(struct fts_ts_data *fts_data);
+static int fts_ts_enable_reg(struct fts_ts_data *ts_data, bool enable);
+
+static void fts_ts_register_for_panel_events(struct device_node *dp,
+					struct fts_ts_data *ts_data)
+{
+	const char *touch_type;
+	int rc = 0;
+	void *cookie = NULL;
+
+	rc = of_property_read_string(dp, "focaltech,touch-type",
+						&touch_type);
+	if (rc) {
+		dev_warn(&fts_data->client->dev,
+			"%s: No touch type\n", __func__);
+		return;
+	}
+	if (strcmp(touch_type, "primary")) {
+		pr_err("Invalid touch type\n");
+		return;
+	}
+
+	cookie = panel_event_notifier_register(PANEL_EVENT_NOTIFICATION_PRIMARY,
+			PANEL_EVENT_NOTIFIER_CLIENT_PRIMARY_TOUCH, active_panel,
+			&fts_ts_panel_notifier_callback, ts_data);
+	if (!cookie) {
+		pr_err("Failed to register for panel events\n");
+		return;
+	}
+
+	FTS_DEBUG("registered for panel notifications panel: 0x%x\n",
+			active_panel);
+
+	ts_data->notifier_cookie = cookie;
+}
+
+#ifdef CONFIG_FTS_TRUSTED_TOUCH
+
+static void fts_ts_trusted_touch_abort_handler(struct fts_ts_data *fts_data,
+						int error);
+static struct gh_acl_desc *fts_ts_vm_get_acl(enum gh_vm_names vm_name)
+{
+	struct gh_acl_desc *acl_desc;
+	gh_vmid_t vmid;
+
+	gh_rm_get_vmid(vm_name, &vmid);
+
+	acl_desc = kzalloc(offsetof(struct gh_acl_desc, acl_entries[1]),
+			GFP_KERNEL);
+	if (!acl_desc)
+		return ERR_PTR(ENOMEM);
+
+	acl_desc->n_acl_entries = 1;
+	acl_desc->acl_entries[0].vmid = vmid;
+	acl_desc->acl_entries[0].perms = GH_RM_ACL_R | GH_RM_ACL_W;
+
+	return acl_desc;
+}
+
+static struct gh_sgl_desc *fts_ts_vm_get_sgl(
+				struct trusted_touch_vm_info *vm_info)
+{
+	struct gh_sgl_desc *sgl_desc;
+	int i;
+
+	sgl_desc = kzalloc(offsetof(struct gh_sgl_desc,
+			sgl_entries[vm_info->iomem_list_size]), GFP_KERNEL);
+	if (!sgl_desc)
+		return ERR_PTR(ENOMEM);
+
+	sgl_desc->n_sgl_entries = vm_info->iomem_list_size;
+
+	for (i = 0; i < vm_info->iomem_list_size; i++) {
+		sgl_desc->sgl_entries[i].ipa_base = vm_info->iomem_bases[i];
+		sgl_desc->sgl_entries[i].size = vm_info->iomem_sizes[i];
+	}
+
+	return sgl_desc;
+}
+
+static int fts_ts_populate_vm_info_iomem(struct fts_ts_data *fts_data)
+{
+	int i, gpio, rc = 0;
+	int num_regs, num_sizes, num_gpios, list_size;
+	struct resource res;
+	struct device_node *np = fts_data->dev->of_node;
+	struct trusted_touch_vm_info *vm_info = fts_data->vm_info;
+
+	num_regs = of_property_count_u32_elems(np, "focaltech,trusted-touch-io-bases");
+	if (num_regs < 0) {
+		FTS_ERROR("Invalid number of IO regions specified\n");
+		return -EINVAL;
+	}
+
+	num_sizes = of_property_count_u32_elems(np, "focaltech,trusted-touch-io-sizes");
+	if (num_sizes < 0) {
+		FTS_ERROR("Invalid number of IO regions specified\n");
+		return -EINVAL;
+	}
+
+	if (num_regs != num_sizes) {
+		FTS_ERROR("IO bases and sizes array lengths mismatch\n");
+		return -EINVAL;
+	}
+
+	num_gpios = of_gpio_named_count(np, "focaltech,trusted-touch-vm-gpio-list");
+	if (num_gpios < 0) {
+		dev_warn(fts_data->dev, "Ignoring invalid trusted gpio list: %d\n", num_gpios);
+		num_gpios = 0;
+	}
+
+	list_size = num_regs + num_gpios;
+	vm_info->iomem_list_size = list_size;
+	vm_info->iomem_bases = devm_kcalloc(fts_data->dev, list_size, sizeof(*vm_info->iomem_bases),
+			GFP_KERNEL);
+	if (!vm_info->iomem_bases)
+		return -ENOMEM;
+
+	vm_info->iomem_sizes = devm_kcalloc(fts_data->dev, list_size, sizeof(*vm_info->iomem_sizes),
+			GFP_KERNEL);
+	if (!vm_info->iomem_sizes)
+		return -ENOMEM;
+
+	for (i = 0; i < num_gpios; ++i) {
+		gpio = of_get_named_gpio(np, "focaltech,trusted-touch-vm-gpio-list", i);
+		if (gpio < 0 || !gpio_is_valid(gpio)) {
+			FTS_ERROR("Invalid gpio %d at position %d\n", gpio, i);
+			return gpio;
+		}
+
+		if (!msm_gpio_get_pin_address(gpio, &res)) {
+			FTS_ERROR("Failed to retrieve gpio-%d resource\n", gpio);
+			return -ENODATA;
+		}
+
+		vm_info->iomem_bases[i] = res.start;
+		vm_info->iomem_sizes[i] = resource_size(&res);
+	}
+
+	rc = of_property_read_u32_array(np, "focaltech,trusted-touch-io-bases",
+			&vm_info->iomem_bases[i], list_size - i);
+	if (rc) {
+		FTS_ERROR("Failed to read trusted touch io bases:%d\n", rc);
+		return rc;
+	}
+
+	rc = of_property_read_u32_array(np, "focaltech,trusted-touch-io-sizes",
+			&vm_info->iomem_sizes[i], list_size - i);
+	if (rc) {
+		FTS_ERROR("Failed to read trusted touch io sizes:%d\n", rc);
+		return rc;
+	}
+
+	return 0;
+}
+
+static int fts_ts_populate_vm_info(struct fts_ts_data *fts_data)
+{
+	int rc;
+	struct trusted_touch_vm_info *vm_info;
+	struct device_node *np = fts_data->dev->of_node;
+
+	vm_info = devm_kzalloc(fts_data->dev, sizeof(struct trusted_touch_vm_info), GFP_KERNEL);
+	if (!vm_info)
+		return -ENOMEM;
+
+	fts_data->vm_info = vm_info;
+	vm_info->vm_name = GH_TRUSTED_VM;
+	rc = of_property_read_u32(np, "focaltech,trusted-touch-spi-irq", &vm_info->hw_irq);
+	if (rc) {
+		pr_err("Failed to read trusted touch SPI irq:%d\n", rc);
+		return rc;
+	}
+
+	rc = fts_ts_populate_vm_info_iomem(fts_data);
+	if (rc) {
+		pr_err("Failed to read trusted touch mmio ranges:%d\n", rc);
+		return rc;
+	}
+
+	rc = of_property_read_string(np, "focaltech,trusted-touch-type",
+						&vm_info->trusted_touch_type);
+	if (rc) {
+		pr_warn("%s: No trusted touch type selection made\n", __func__);
+		vm_info->mem_tag = GH_MEM_NOTIFIER_TAG_TOUCH_PRIMARY;
+		vm_info->irq_label = GH_IRQ_LABEL_TRUSTED_TOUCH_PRIMARY;
+		rc = 0;
+	} else if (!strcmp(vm_info->trusted_touch_type, "primary")) {
+		vm_info->mem_tag = GH_MEM_NOTIFIER_TAG_TOUCH_PRIMARY;
+		vm_info->irq_label = GH_IRQ_LABEL_TRUSTED_TOUCH_PRIMARY;
+	} else if (!strcmp(vm_info->trusted_touch_type, "secondary")) {
+		vm_info->mem_tag = GH_MEM_NOTIFIER_TAG_TOUCH_SECONDARY;
+		vm_info->irq_label = GH_IRQ_LABEL_TRUSTED_TOUCH_SECONDARY;
+	}
+
+	return 0;
+}
+
+static void fts_ts_destroy_vm_info(struct fts_ts_data *fts_data)
+{
+	kfree(fts_data->vm_info->iomem_sizes);
+	kfree(fts_data->vm_info->iomem_bases);
+	kfree(fts_data->vm_info);
+}
+
+static void fts_ts_vm_deinit(struct fts_ts_data *fts_data)
+{
+	if (fts_data->vm_info->mem_cookie)
+		gh_mem_notifier_unregister(fts_data->vm_info->mem_cookie);
+	fts_ts_destroy_vm_info(fts_data);
+}
+
+static int fts_ts_trusted_touch_get_vm_state(struct fts_ts_data *fts_data)
+{
+	return atomic_read(&fts_data->vm_info->vm_state);
+}
+
+static void fts_ts_trusted_touch_set_vm_state(struct fts_ts_data *fts_data,
+							int state)
+{
+	atomic_set(&fts_data->vm_info->vm_state, state);
+}
+
+#ifdef CONFIG_ARCH_QTI_VM
+static int fts_ts_vm_mem_release(struct fts_ts_data *fts_data);
+static void fts_ts_trusted_touch_tvm_vm_mode_disable(struct fts_ts_data *fts_data);
+static void fts_ts_trusted_touch_abort_tvm(struct fts_ts_data *fts_data);
+static void fts_ts_trusted_touch_event_notify(struct fts_ts_data *fts_data, int event);
+
+void fts_ts_trusted_touch_tvm_i2c_failure_report(struct fts_ts_data *fts_data)
+{
+	pr_err("initiating trusted touch abort due to i2c failure\n");
+	fts_ts_trusted_touch_abort_handler(fts_data,
+			TRUSTED_TOUCH_EVENT_I2C_FAILURE);
+}
+
+static void fts_ts_trusted_touch_reset_gpio_toggle(struct fts_ts_data *fts_data)
+{
+	void __iomem *base;
+
+	if (fts_data->bus_type != BUS_TYPE_I2C)
+		return;
+
+	base = ioremap(TOUCH_RESET_GPIO_BASE, TOUCH_RESET_GPIO_SIZE);
+	writel_relaxed(0x1, base + TOUCH_RESET_GPIO_OFFSET);
+	/* wait until toggle to finish*/
+	wmb();
+	writel_relaxed(0x0, base + TOUCH_RESET_GPIO_OFFSET);
+	/* wait until toggle to finish*/
+	wmb();
+	iounmap(base);
+}
+
+static void fts_trusted_touch_intr_gpio_toggle(struct fts_ts_data *fts_data,
+		bool enable)
+{
+	void __iomem *base;
+	u32 val;
+
+	if (fts_data->bus_type != BUS_TYPE_I2C)
+		return;
+
+	base = ioremap(TOUCH_INTR_GPIO_BASE, TOUCH_INTR_GPIO_SIZE);
+	val = readl_relaxed(base + TOUCH_RESET_GPIO_OFFSET);
+	if (enable) {
+		val |= BIT(0);
+		writel_relaxed(val, base + TOUCH_INTR_GPIO_OFFSET);
+		/* wait until toggle to finish*/
+		wmb();
+	} else {
+		val &= ~BIT(0);
+		writel_relaxed(val, base + TOUCH_INTR_GPIO_OFFSET);
+		/* wait until toggle to finish*/
+		wmb();
+	}
+	iounmap(base);
+}
+
+static int fts_ts_sgl_cmp(const void *a, const void *b)
+{
+	struct gh_sgl_entry *left = (struct gh_sgl_entry *)a;
+	struct gh_sgl_entry *right = (struct gh_sgl_entry *)b;
+
+	return (left->ipa_base - right->ipa_base);
+}
+
+static int fts_ts_vm_compare_sgl_desc(struct gh_sgl_desc *expected,
+		struct gh_sgl_desc *received)
+{
+	int idx;
+
+	if (expected->n_sgl_entries != received->n_sgl_entries)
+		return -E2BIG;
+	sort(received->sgl_entries, received->n_sgl_entries,
+			sizeof(received->sgl_entries[0]), fts_ts_sgl_cmp, NULL);
+	sort(expected->sgl_entries, expected->n_sgl_entries,
+			sizeof(expected->sgl_entries[0]), fts_ts_sgl_cmp, NULL);
+
+	for (idx = 0; idx < expected->n_sgl_entries; idx++) {
+		struct gh_sgl_entry *left = &expected->sgl_entries[idx];
+		struct gh_sgl_entry *right = &received->sgl_entries[idx];
+
+		if ((left->ipa_base != right->ipa_base) ||
+				(left->size != right->size)) {
+			pr_err("sgl mismatch: left_base:%d right base:%d left size:%d right size:%d\n",
+					left->ipa_base, right->ipa_base,
+					left->size, right->size);
+			return -EINVAL;
+		}
+	}
+	return 0;
+}
+
+static int fts_ts_vm_handle_vm_hardware(struct fts_ts_data *fts_data)
+{
+	int rc = 0;
+
+	if (atomic_read(&fts_data->delayed_vm_probe_pending)) {
+		rc = fts_ts_probe_delayed(fts_data);
+		if (rc) {
+			pr_err(" Delayed probe failure on VM!\n");
+			return rc;
+		}
+		atomic_set(&fts_data->delayed_vm_probe_pending, 0);
+		return rc;
+	}
+
+	fts_irq_enable();
+	fts_ts_trusted_touch_set_vm_state(fts_data, TVM_INTERRUPT_ENABLED);
+	return rc;
+}
+
+static void fts_ts_trusted_touch_tvm_vm_mode_enable(struct fts_ts_data *fts_data)
+{
+
+	struct gh_sgl_desc *sgl_desc, *expected_sgl_desc;
+	struct gh_acl_desc *acl_desc;
+	struct irq_data *irq_data;
+	int rc = 0;
+	int irq = 0;
+
+	if (fts_ts_trusted_touch_get_vm_state(fts_data) != TVM_ALL_RESOURCES_LENT_NOTIFIED) {
+		pr_err("All lend notifications not received\n");
+		fts_ts_trusted_touch_event_notify(fts_data,
+				TRUSTED_TOUCH_EVENT_NOTIFICATIONS_PENDING);
+		return;
+	}
+
+	acl_desc = fts_ts_vm_get_acl(GH_TRUSTED_VM);
+	if (IS_ERR(acl_desc)) {
+		pr_err("failed to populated acl data:rc=%d\n",
+				PTR_ERR(acl_desc));
+		goto accept_fail;
+	}
+
+	sgl_desc = gh_rm_mem_accept(fts_data->vm_info->vm_mem_handle,
+			GH_RM_MEM_TYPE_IO,
+			GH_RM_TRANS_TYPE_LEND,
+			GH_RM_MEM_ACCEPT_VALIDATE_ACL_ATTRS |
+			GH_RM_MEM_ACCEPT_VALIDATE_LABEL |
+			GH_RM_MEM_ACCEPT_DONE,  TRUSTED_TOUCH_MEM_LABEL,
+			acl_desc, NULL, NULL, 0);
+	if (IS_ERR_OR_NULL(sgl_desc)) {
+		pr_err("failed to do mem accept :rc=%d\n",
+				PTR_ERR(sgl_desc));
+		goto acl_fail;
+	}
+	fts_ts_trusted_touch_set_vm_state(fts_data, TVM_IOMEM_ACCEPTED);
+
+	/* Initiate session on tvm */
+	if (fts_data->bus_type == BUS_TYPE_I2C)
+		rc = pm_runtime_get_sync(fts_data->client->adapter->dev.parent);
+	else
+		rc = pm_runtime_get_sync(fts_data->spi->master->dev.parent);
+
+	if (rc < 0) {
+		pr_err("failed to get sync rc:%d\n", rc);
+		goto acl_fail;
+	}
+	fts_ts_trusted_touch_set_vm_state(fts_data, TVM_I2C_SESSION_ACQUIRED);
+
+	expected_sgl_desc = fts_ts_vm_get_sgl(fts_data->vm_info);
+	if (fts_ts_vm_compare_sgl_desc(expected_sgl_desc, sgl_desc)) {
+		pr_err("IO sg list does not match\n");
+		goto sgl_cmp_fail;
+	}
+
+	kfree(expected_sgl_desc);
+	kfree(acl_desc);
+
+	irq = gh_irq_accept(fts_data->vm_info->irq_label, -1, IRQ_TYPE_EDGE_RISING);
+	fts_trusted_touch_intr_gpio_toggle(fts_data, false);
+	if (irq < 0) {
+		pr_err("failed to accept irq\n");
+		goto accept_fail;
+	}
+	fts_ts_trusted_touch_set_vm_state(fts_data, TVM_IRQ_ACCEPTED);
+
+
+	irq_data = irq_get_irq_data(irq);
+	if (!irq_data) {
+		pr_err("Invalid irq data for trusted touch\n");
+		goto accept_fail;
+	}
+	if (!irq_data->hwirq) {
+		pr_err("Invalid irq in irq data\n");
+		goto accept_fail;
+	}
+	if (irq_data->hwirq != fts_data->vm_info->hw_irq) {
+		pr_err("Invalid irq lent\n");
+		goto accept_fail;
+	}
+
+	pr_debug("irq:returned from accept:%d\n", irq);
+	fts_data->irq = irq;
+
+	rc = fts_ts_vm_handle_vm_hardware(fts_data);
+	if (rc) {
+		pr_err(" Delayed probe failure on VM!\n");
+		goto accept_fail;
+	}
+	atomic_set(&fts_data->trusted_touch_enabled, 1);
+	pr_info("trusted touch enabled\n");
+
+	return;
+sgl_cmp_fail:
+	kfree(expected_sgl_desc);
+acl_fail:
+	kfree(acl_desc);
+accept_fail:
+	fts_ts_trusted_touch_abort_handler(fts_data,
+			TRUSTED_TOUCH_EVENT_ACCEPT_FAILURE);
+}
+static void fts_ts_vm_irq_on_lend_callback(void *data,
+					unsigned long notif_type,
+					enum gh_irq_label label)
+{
+	struct fts_ts_data *fts_data = data;
+
+	pr_debug("received irq lend request for label:%d\n", label);
+	if (fts_ts_trusted_touch_get_vm_state(fts_data) == TVM_IOMEM_LENT_NOTIFIED)
+		fts_ts_trusted_touch_set_vm_state(fts_data, TVM_ALL_RESOURCES_LENT_NOTIFIED);
+	else
+		fts_ts_trusted_touch_set_vm_state(fts_data, TVM_IRQ_LENT_NOTIFIED);
+}
+
+static void fts_ts_vm_mem_on_lend_handler(enum gh_mem_notifier_tag tag,
+		unsigned long notif_type, void *entry_data, void *notif_msg)
+{
+	struct gh_rm_notif_mem_shared_payload *payload;
+	struct trusted_touch_vm_info *vm_info;
+	struct fts_ts_data *fts_data;
+
+	fts_data = (struct fts_ts_data *)entry_data;
+	vm_info = fts_data->vm_info;
+	if (!vm_info) {
+		pr_err("Invalid vm_info\n");
+		return;
+	}
+
+	if (notif_type != GH_RM_NOTIF_MEM_SHARED ||
+			tag != vm_info->mem_tag) {
+		pr_err("Invalid command passed from rm\n");
+		return;
+	}
+
+	if (!entry_data || !notif_msg) {
+		pr_err("Invalid entry data passed from rm\n");
+		return;
+	}
+
+
+	payload = (struct gh_rm_notif_mem_shared_payload  *)notif_msg;
+	if (payload->trans_type != GH_RM_TRANS_TYPE_LEND ||
+			payload->label != TRUSTED_TOUCH_MEM_LABEL) {
+		pr_err("Invalid label or transaction type\n");
+		return;
+	}
+
+	vm_info->vm_mem_handle = payload->mem_handle;
+	pr_debug("received mem lend request with handle:%d\n",
+		vm_info->vm_mem_handle);
+	if (fts_ts_trusted_touch_get_vm_state(fts_data) == TVM_IRQ_LENT_NOTIFIED)
+		fts_ts_trusted_touch_set_vm_state(fts_data, TVM_ALL_RESOURCES_LENT_NOTIFIED);
+	else
+		fts_ts_trusted_touch_set_vm_state(fts_data, TVM_IOMEM_LENT_NOTIFIED);
+}
+
+static int fts_ts_vm_mem_release(struct fts_ts_data *fts_data)
+{
+	int rc = 0;
+
+	if (!fts_data->vm_info->vm_mem_handle) {
+		pr_err("Invalid memory handle\n");
+		return -EINVAL;
+	}
+
+	rc = gh_rm_mem_release(fts_data->vm_info->vm_mem_handle, 0);
+	if (rc)
+		pr_err("VM mem release failed: rc=%d\n", rc);
+
+	rc = gh_rm_mem_notify(fts_data->vm_info->vm_mem_handle,
+				GH_RM_MEM_NOTIFY_OWNER_RELEASED,
+				fts_data->vm_info->mem_tag, 0);
+	if (rc)
+		pr_err("Failed to notify mem release to PVM: rc=%d\n");
+	pr_debug("vm mem release succeded\n");
+
+	fts_data->vm_info->vm_mem_handle = 0;
+	return rc;
+}
+
+static void fts_ts_trusted_touch_tvm_vm_mode_disable(struct fts_ts_data *fts_data)
+{
+	int rc = 0;
+
+	if (atomic_read(&fts_data->trusted_touch_abort_status)) {
+		fts_ts_trusted_touch_abort_tvm(fts_data);
+		return;
+	}
+
+	fts_irq_disable();
+	fts_ts_trusted_touch_set_vm_state(fts_data, TVM_INTERRUPT_DISABLED);
+
+	rc = gh_irq_release(fts_data->vm_info->irq_label);
+	if (rc) {
+		pr_err("Failed to release irq rc:%d\n", rc);
+		goto error;
+	} else {
+		fts_ts_trusted_touch_set_vm_state(fts_data, TVM_IRQ_RELEASED);
+	}
+	rc = gh_irq_release_notify(fts_data->vm_info->irq_label);
+	if (rc)
+		pr_err("Failed to notify release irq rc:%d\n", rc);
+
+	pr_debug("vm irq release succeded\n");
+
+	fts_release_all_finger();
+
+	if (fts_data->bus_type == BUS_TYPE_I2C)
+		pm_runtime_put_sync(fts_data->client->adapter->dev.parent);
+	else
+		pm_runtime_put_sync(fts_data->spi->master->dev.parent);
+
+	fts_ts_trusted_touch_set_vm_state(fts_data, TVM_I2C_SESSION_RELEASED);
+	rc = fts_ts_vm_mem_release(fts_data);
+	if (rc) {
+		pr_err("Failed to release mem rc:%d\n", rc);
+		goto error;
+	} else {
+		fts_ts_trusted_touch_set_vm_state(fts_data, TVM_IOMEM_RELEASED);
+	}
+	fts_ts_trusted_touch_set_vm_state(fts_data, TRUSTED_TOUCH_TVM_INIT);
+	atomic_set(&fts_data->trusted_touch_enabled, 0);
+	pr_info("trusted touch disabled\n");
+	return;
+error:
+	fts_ts_trusted_touch_abort_handler(fts_data,
+			TRUSTED_TOUCH_EVENT_RELEASE_FAILURE);
+}
+
+int fts_ts_handle_trusted_touch_tvm(struct fts_ts_data *fts_data, int value)
+{
+	int err = 0;
+
+	switch (value) {
+	case 0:
+		if ((atomic_read(&fts_data->trusted_touch_enabled) == 0) &&
+			(atomic_read(&fts_data->trusted_touch_abort_status) == 0)) {
+			pr_err("Trusted touch is already disabled\n");
+			break;
+		}
+		if (atomic_read(&fts_data->trusted_touch_mode) ==
+				TRUSTED_TOUCH_VM_MODE) {
+			fts_ts_trusted_touch_tvm_vm_mode_disable(fts_data);
+		} else {
+			pr_err("Unsupported trusted touch mode\n");
+		}
+		break;
+
+	case 1:
+		if (atomic_read(&fts_data->trusted_touch_enabled)) {
+			pr_err("Trusted touch usecase underway\n");
+			err = -EBUSY;
+			break;
+		}
+		if (atomic_read(&fts_data->trusted_touch_mode) ==
+				TRUSTED_TOUCH_VM_MODE) {
+			fts_ts_trusted_touch_tvm_vm_mode_enable(fts_data);
+		} else {
+			pr_err("Unsupported trusted touch mode\n");
+		}
+		break;
+
+	default:
+		FTS_ERROR("unsupported value: %lu\n", value);
+		err = -EINVAL;
+		break;
+	}
+
+	return err;
+}
+
+static void fts_ts_trusted_touch_abort_tvm(struct fts_ts_data *fts_data)
+{
+	int rc = 0;
+	int vm_state = fts_ts_trusted_touch_get_vm_state(fts_data);
+
+	if (vm_state >= TRUSTED_TOUCH_TVM_STATE_MAX) {
+		pr_err("invalid tvm driver state: %d\n", vm_state);
+		return;
+	}
+
+	switch (vm_state) {
+	case TVM_INTERRUPT_ENABLED:
+		fts_irq_disable();
+	case TVM_IRQ_ACCEPTED:
+	case TVM_INTERRUPT_DISABLED:
+		rc = gh_irq_release(fts_data->vm_info->irq_label);
+		if (rc)
+			pr_err("Failed to release irq rc:%d\n", rc);
+		rc = gh_irq_release_notify(fts_data->vm_info->irq_label);
+		if (rc)
+			pr_err("Failed to notify irq release rc:%d\n", rc);
+	case TVM_I2C_SESSION_ACQUIRED:
+	case TVM_IOMEM_ACCEPTED:
+	case TVM_IRQ_RELEASED:
+		fts_release_all_finger();
+		if (fts_data->bus_type == BUS_TYPE_I2C)
+			pm_runtime_put_sync(fts_data->client->adapter->dev.parent);
+		else
+			pm_runtime_put_sync(fts_data->spi->master->dev.parent);
+	case TVM_I2C_SESSION_RELEASED:
+		rc = fts_ts_vm_mem_release(fts_data);
+		if (rc)
+			pr_err("Failed to release mem rc:%d\n", rc);
+	case TVM_IOMEM_RELEASED:
+	case TVM_ALL_RESOURCES_LENT_NOTIFIED:
+	case TRUSTED_TOUCH_TVM_INIT:
+	case TVM_IRQ_LENT_NOTIFIED:
+	case TVM_IOMEM_LENT_NOTIFIED:
+		atomic_set(&fts_data->trusted_touch_enabled, 0);
+	}
+
+	atomic_set(&fts_data->trusted_touch_abort_status, 0);
+	fts_ts_trusted_touch_set_vm_state(fts_data, TRUSTED_TOUCH_TVM_INIT);
+}
+
+#else
+
+static void fts_ts_bus_put(struct fts_ts_data *fts_data);
+
+static void fts_ts_trusted_touch_abort_pvm(struct fts_ts_data *fts_data)
+{
+	int rc = 0;
+	int vm_state = fts_ts_trusted_touch_get_vm_state(fts_data);
+
+	if (vm_state >= TRUSTED_TOUCH_PVM_STATE_MAX) {
+		pr_err("Invalid driver state: %d\n", vm_state);
+		return;
+	}
+
+	switch (vm_state) {
+	case PVM_IRQ_RELEASE_NOTIFIED:
+	case PVM_ALL_RESOURCES_RELEASE_NOTIFIED:
+	case PVM_IRQ_LENT:
+	case PVM_IRQ_LENT_NOTIFIED:
+		rc = gh_irq_reclaim(fts_data->vm_info->irq_label);
+		if (rc)
+			pr_err("failed to reclaim irq on pvm rc:%d\n", rc);
+	case PVM_IRQ_RECLAIMED:
+	case PVM_IOMEM_LENT:
+	case PVM_IOMEM_LENT_NOTIFIED:
+	case PVM_IOMEM_RELEASE_NOTIFIED:
+		rc = gh_rm_mem_reclaim(fts_data->vm_info->vm_mem_handle, 0);
+		if (rc)
+			pr_err("failed to reclaim iomem on pvm rc:%d\n", rc);
+		fts_data->vm_info->vm_mem_handle = 0;
+	case PVM_IOMEM_RECLAIMED:
+	case PVM_INTERRUPT_DISABLED:
+		fts_irq_enable();
+	case PVM_I2C_RESOURCE_ACQUIRED:
+	case PVM_INTERRUPT_ENABLED:
+		fts_ts_bus_put(fts_data);
+	case TRUSTED_TOUCH_PVM_INIT:
+	case PVM_I2C_RESOURCE_RELEASED:
+		atomic_set(&fts_data->trusted_touch_enabled, 0);
+		atomic_set(&fts_data->trusted_touch_transition, 0);
+	}
+
+	atomic_set(&fts_data->trusted_touch_abort_status, 0);
+
+	fts_ts_trusted_touch_set_vm_state(fts_data, TRUSTED_TOUCH_PVM_INIT);
+}
+
+static int fts_ts_clk_prepare_enable(struct fts_ts_data *fts_data)
+{
+	int ret;
+
+	ret = clk_prepare_enable(fts_data->iface_clk);
+	if (ret) {
+		FTS_ERROR("error on clk_prepare_enable(iface_clk):%d\n", ret);
+		return ret;
+	}
+
+	ret = clk_prepare_enable(fts_data->core_clk);
+	if (ret) {
+		clk_disable_unprepare(fts_data->iface_clk);
+		FTS_ERROR("error clk_prepare_enable(core_clk):%d\n", ret);
+	}
+	return ret;
+}
+
+static void fts_ts_clk_disable_unprepare(struct fts_ts_data *fts_data)
+{
+	clk_disable_unprepare(fts_data->core_clk);
+	clk_disable_unprepare(fts_data->iface_clk);
+}
+
+static int fts_ts_bus_get(struct fts_ts_data *fts_data)
+{
+	int rc = 0;
+	struct device *dev = NULL;
+
+	cancel_work_sync(&fts_data->resume_work);
+	reinit_completion(&fts_data->trusted_touch_powerdown);
+	fts_ts_enable_reg(fts_data, true);
+
+	if (fts_data->bus_type == BUS_TYPE_I2C)
+		dev = fts_data->client->adapter->dev.parent;
+	else
+		dev = fts_data->spi->master->dev.parent;
+
+	mutex_lock(&fts_data->fts_clk_io_ctrl_mutex);
+	rc = pm_runtime_get_sync(dev);
+	if (rc >= 0 &&  fts_data->core_clk != NULL &&
+				fts_data->iface_clk != NULL) {
+		rc = fts_ts_clk_prepare_enable(fts_data);
+		if (rc)
+			pm_runtime_put_sync(dev);
+	}
+
+	mutex_unlock(&fts_data->fts_clk_io_ctrl_mutex);
+	return rc;
+}
+
+static void fts_ts_bus_put(struct fts_ts_data *fts_data)
+{
+	struct device *dev = NULL;
+
+	if (fts_data->bus_type == BUS_TYPE_I2C)
+		dev = fts_data->client->adapter->dev.parent;
+	else
+		dev = fts_data->spi->master->dev.parent;
+
+	mutex_lock(&fts_data->fts_clk_io_ctrl_mutex);
+	if (fts_data->core_clk != NULL && fts_data->iface_clk != NULL)
+		fts_ts_clk_disable_unprepare(fts_data);
+	pm_runtime_put_sync(dev);
+	mutex_unlock(&fts_data->fts_clk_io_ctrl_mutex);
+	complete(&fts_data->trusted_touch_powerdown);
+	fts_ts_enable_reg(fts_data, false);
+}
+
+static struct gh_notify_vmid_desc *fts_ts_vm_get_vmid(gh_vmid_t vmid)
+{
+	struct gh_notify_vmid_desc *vmid_desc;
+
+	vmid_desc = kzalloc(offsetof(struct gh_notify_vmid_desc,
+				vmid_entries[1]), GFP_KERNEL);
+	if (!vmid_desc)
+		return ERR_PTR(ENOMEM);
+
+	vmid_desc->n_vmid_entries = 1;
+	vmid_desc->vmid_entries[0].vmid = vmid;
+	return vmid_desc;
+}
+
+static void fts_trusted_touch_pvm_vm_mode_disable(struct fts_ts_data *fts_data)
+{
+	int rc = 0;
+
+	atomic_set(&fts_data->trusted_touch_transition, 1);
+
+	if (atomic_read(&fts_data->trusted_touch_abort_status)) {
+		fts_ts_trusted_touch_abort_pvm(fts_data);
+		return;
+	}
+
+	if (fts_ts_trusted_touch_get_vm_state(fts_data) != PVM_ALL_RESOURCES_RELEASE_NOTIFIED)
+		pr_err("all release notifications are not received yet\n");
+
+	rc = gh_rm_mem_reclaim(fts_data->vm_info->vm_mem_handle, 0);
+	if (rc) {
+		pr_err("Trusted touch VM mem reclaim failed rc:%d\n", rc);
+		goto error;
+	}
+	fts_ts_trusted_touch_set_vm_state(fts_data, PVM_IOMEM_RECLAIMED);
+	fts_data->vm_info->vm_mem_handle = 0;
+	pr_debug("vm mem reclaim succeded!\n");
+
+	rc = gh_irq_reclaim(fts_data->vm_info->irq_label);
+	if (rc) {
+		pr_err("failed to reclaim irq on pvm rc:%d\n", rc);
+		goto error;
+	}
+	fts_ts_trusted_touch_set_vm_state(fts_data, PVM_IRQ_RECLAIMED);
+	pr_debug("vm irq reclaim succeded!\n");
+
+	fts_irq_enable();
+	fts_ts_trusted_touch_set_vm_state(fts_data, PVM_INTERRUPT_ENABLED);
+	fts_ts_bus_put(fts_data);
+	atomic_set(&fts_data->trusted_touch_transition, 0);
+	fts_ts_trusted_touch_set_vm_state(fts_data, PVM_I2C_RESOURCE_RELEASED);
+	fts_ts_trusted_touch_set_vm_state(fts_data, TRUSTED_TOUCH_PVM_INIT);
+	atomic_set(&fts_data->trusted_touch_enabled, 0);
+	pr_info("trusted touch disabled\n");
+	return;
+error:
+	fts_ts_trusted_touch_abort_handler(fts_data,
+			TRUSTED_TOUCH_EVENT_RECLAIM_FAILURE);
+}
+
+static void fts_ts_vm_irq_on_release_callback(void *data,
+					unsigned long notif_type,
+					enum gh_irq_label label)
+{
+	struct fts_ts_data *fts_data = data;
+
+	if (notif_type != GH_RM_NOTIF_VM_IRQ_RELEASED) {
+		pr_err("invalid notification type\n");
+		return;
+	}
+
+	if (fts_ts_trusted_touch_get_vm_state(fts_data) == PVM_IOMEM_RELEASE_NOTIFIED)
+		fts_ts_trusted_touch_set_vm_state(fts_data, PVM_ALL_RESOURCES_RELEASE_NOTIFIED);
+	else
+		fts_ts_trusted_touch_set_vm_state(fts_data, PVM_IRQ_RELEASE_NOTIFIED);
+}
+
+static void fts_ts_vm_mem_on_release_handler(enum gh_mem_notifier_tag tag,
+		unsigned long notif_type, void *entry_data, void *notif_msg)
+{
+	struct gh_rm_notif_mem_released_payload *release_payload;
+	struct trusted_touch_vm_info *vm_info;
+	struct fts_ts_data *fts_data;
+
+	fts_data = (struct fts_ts_data *)entry_data;
+	vm_info = fts_data->vm_info;
+	if (!vm_info) {
+		pr_err(" Invalid vm_info\n");
+		return;
+	}
+
+	if (notif_type != GH_RM_NOTIF_MEM_RELEASED) {
+		pr_err(" Invalid notification type\n");
+		return;
+	}
+
+	if (tag != vm_info->mem_tag) {
+		pr_err(" Invalid tag\n");
+		return;
+	}
+
+	if (!entry_data || !notif_msg) {
+		pr_err(" Invalid data or notification message\n");
+		return;
+	}
+
+	release_payload = (struct gh_rm_notif_mem_released_payload  *)notif_msg;
+	if (release_payload->mem_handle != vm_info->vm_mem_handle) {
+		pr_err("Invalid mem handle detected\n");
+		return;
+	}
+
+	if (fts_ts_trusted_touch_get_vm_state(fts_data) == PVM_IRQ_RELEASE_NOTIFIED)
+		fts_ts_trusted_touch_set_vm_state(fts_data, PVM_ALL_RESOURCES_RELEASE_NOTIFIED);
+	else
+		fts_ts_trusted_touch_set_vm_state(fts_data, PVM_IOMEM_RELEASE_NOTIFIED);
+}
+
+static int fts_ts_vm_mem_lend(struct fts_ts_data *fts_data)
+{
+	struct gh_acl_desc *acl_desc;
+	struct gh_sgl_desc *sgl_desc;
+	struct gh_notify_vmid_desc *vmid_desc;
+	gh_memparcel_handle_t mem_handle;
+	gh_vmid_t trusted_vmid;
+	int rc = 0;
+
+	acl_desc = fts_ts_vm_get_acl(GH_TRUSTED_VM);
+	if (IS_ERR(acl_desc)) {
+		pr_err("Failed to get acl of IO memories for Trusted touch\n");
+		rc = PTR_ERR(acl_desc);
+		return rc;
+	}
+
+	sgl_desc = fts_ts_vm_get_sgl(fts_data->vm_info);
+	if (IS_ERR(sgl_desc)) {
+		pr_err("Failed to get sgl of IO memories for Trusted touch\n");
+		rc = PTR_ERR(sgl_desc);
+		goto sgl_error;
+	}
+
+	rc = gh_rm_mem_lend(GH_RM_MEM_TYPE_IO, 0, TRUSTED_TOUCH_MEM_LABEL,
+			acl_desc, sgl_desc, NULL, &mem_handle);
+	if (rc) {
+		pr_err("Failed to lend IO memories for Trusted touch rc:%d\n",
+							rc);
+		goto error;
+	}
+
+	pr_info("vm mem lend succeded\n");
+
+	fts_ts_trusted_touch_set_vm_state(fts_data, PVM_IOMEM_LENT);
+
+	gh_rm_get_vmid(GH_TRUSTED_VM, &trusted_vmid);
+
+	vmid_desc = fts_ts_vm_get_vmid(trusted_vmid);
+
+	rc = gh_rm_mem_notify(mem_handle, GH_RM_MEM_NOTIFY_RECIPIENT_SHARED,
+			fts_data->vm_info->mem_tag, vmid_desc);
+	if (rc) {
+		pr_err("Failed to notify mem lend to hypervisor rc:%d\n", rc);
+		goto vmid_error;
+	}
+
+	fts_ts_trusted_touch_set_vm_state(fts_data, PVM_IOMEM_LENT_NOTIFIED);
+
+	fts_data->vm_info->vm_mem_handle = mem_handle;
+vmid_error:
+	kfree(vmid_desc);
+error:
+	kfree(sgl_desc);
+sgl_error:
+	kfree(acl_desc);
+
+	return rc;
+}
+
+static int fts_ts_trusted_touch_pvm_vm_mode_enable(struct fts_ts_data *fts_data)
+{
+	int rc = 0;
+	struct trusted_touch_vm_info *vm_info = fts_data->vm_info;
+
+	atomic_set(&fts_data->trusted_touch_transition, 1);
+	mutex_lock(&fts_data->transition_lock);
+
+	if (fts_data->suspended) {
+		FTS_ERROR("Invalid power state for operation\n");
+		atomic_set(&fts_data->trusted_touch_transition, 0);
+		rc =  -EPERM;
+		goto error;
+	}
+
+	/* i2c session start and resource acquire */
+	if (fts_ts_bus_get(fts_data) < 0) {
+		FTS_ERROR("fts_ts_bus_get failed\n");
+		rc = -EIO;
+		goto error;
+	}
+
+	fts_ts_trusted_touch_set_vm_state(fts_data, PVM_I2C_RESOURCE_ACQUIRED);
+	/* flush pending interurpts from FIFO */
+	fts_irq_disable();
+	fts_ts_trusted_touch_set_vm_state(fts_data, PVM_INTERRUPT_DISABLED);
+	fts_release_all_finger();
+
+	rc = fts_ts_vm_mem_lend(fts_data);
+	if (rc) {
+		pr_err("Failed to lend memory\n");
+		goto abort_handler;
+	}
+	pr_debug("vm mem lend succeded\n");
+	rc = gh_irq_lend_v2(vm_info->irq_label, vm_info->vm_name,
+		fts_data->irq, &fts_ts_vm_irq_on_release_callback, fts_data);
+	if (rc) {
+		pr_err("Failed to lend irq\n");
+		goto abort_handler;
+	}
+
+	pr_debug("vm irq lend succeded for irq:%d\n", fts_data->irq);
+	fts_ts_trusted_touch_set_vm_state(fts_data, PVM_IRQ_LENT);
+
+	rc = gh_irq_lend_notify(vm_info->irq_label);
+	if (rc) {
+		pr_err("Failed to notify irq\n");
+		goto abort_handler;
+	}
+	fts_ts_trusted_touch_set_vm_state(fts_data, PVM_IRQ_LENT_NOTIFIED);
+
+	mutex_unlock(&fts_data->transition_lock);
+	atomic_set(&fts_data->trusted_touch_transition, 0);
+	atomic_set(&fts_data->trusted_touch_enabled, 1);
+	pr_info("trusted touch enabled\n");
+	return rc;
+
+abort_handler:
+	fts_ts_trusted_touch_abort_handler(fts_data, TRUSTED_TOUCH_EVENT_LEND_FAILURE);
+
+error:
+	mutex_unlock(&fts_data->transition_lock);
+	return rc;
+}
+
+int fts_ts_handle_trusted_touch_pvm(struct fts_ts_data *fts_data, int value)
+{
+	int err = 0;
+
+	switch (value) {
+	case 0:
+		if (atomic_read(&fts_data->trusted_touch_enabled) == 0 &&
+			(atomic_read(&fts_data->trusted_touch_abort_status) == 0)) {
+			pr_err("Trusted touch is already disabled\n");
+			break;
+		}
+		if (atomic_read(&fts_data->trusted_touch_mode) ==
+				TRUSTED_TOUCH_VM_MODE) {
+			fts_trusted_touch_pvm_vm_mode_disable(fts_data);
+		} else {
+			pr_err("Unsupported trusted touch mode\n");
+		}
+		break;
+
+	case 1:
+		if (atomic_read(&fts_data->trusted_touch_enabled)) {
+			pr_err("Trusted touch usecase underway\n");
+			err = -EBUSY;
+			break;
+		}
+		if (atomic_read(&fts_data->trusted_touch_mode) ==
+				TRUSTED_TOUCH_VM_MODE) {
+			err = fts_ts_trusted_touch_pvm_vm_mode_enable(fts_data);
+		} else {
+			pr_err("Unsupported trusted touch mode\n");
+		}
+		break;
+
+	default:
+		FTS_ERROR("unsupported value: %lu\n", value);
+		err = -EINVAL;
+		break;
+	}
+	return err;
+}
+
+#endif
+
+static void fts_ts_trusted_touch_event_notify(struct fts_ts_data *fts_data, int event)
+{
+	atomic_set(&fts_data->trusted_touch_event, event);
+	sysfs_notify(&fts_data->dev->kobj, NULL, "trusted_touch_event");
+}
+
+static void fts_ts_trusted_touch_abort_handler(struct fts_ts_data *fts_data, int error)
+{
+	atomic_set(&fts_data->trusted_touch_abort_status, error);
+	pr_err("TUI session aborted with failure:%d\n", error);
+	fts_ts_trusted_touch_event_notify(fts_data, error);
+#ifdef CONFIG_ARCH_QTI_VM
+	pr_err("Resetting touch controller\n");
+	if (fts_ts_trusted_touch_get_vm_state(fts_data) >= TVM_IOMEM_ACCEPTED &&
+			error == TRUSTED_TOUCH_EVENT_I2C_FAILURE) {
+		pr_err("Resetting touch controller\n");
+		fts_ts_trusted_touch_reset_gpio_toggle(fts_data);
+	}
+#endif
+}
+
+static int fts_ts_vm_init(struct fts_ts_data *fts_data)
+{
+	int rc = 0;
+	struct trusted_touch_vm_info *vm_info;
+	void *mem_cookie;
+
+	rc = fts_ts_populate_vm_info(fts_data);
+	if (rc) {
+		pr_err("Cannot setup vm pipeline\n");
+		rc = -EINVAL;
+		goto fail;
+	}
+
+	vm_info = fts_data->vm_info;
+#ifdef CONFIG_ARCH_QTI_VM
+	mem_cookie = gh_mem_notifier_register(vm_info->mem_tag,
+			fts_ts_vm_mem_on_lend_handler, fts_data);
+	if (!mem_cookie) {
+		pr_err("Failed to register on lend mem notifier\n");
+		rc = -EINVAL;
+		goto init_fail;
+	}
+	vm_info->mem_cookie = mem_cookie;
+	rc = gh_irq_wait_for_lend_v2(vm_info->irq_label, GH_PRIMARY_VM,
+			&fts_ts_vm_irq_on_lend_callback, fts_data);
+	fts_ts_trusted_touch_set_vm_state(fts_data, TRUSTED_TOUCH_TVM_INIT);
+#else
+	mem_cookie = gh_mem_notifier_register(vm_info->mem_tag,
+			fts_ts_vm_mem_on_release_handler, fts_data);
+	if (!mem_cookie) {
+		pr_err("Failed to register on release mem notifier\n");
+		rc = -EINVAL;
+		goto init_fail;
+	}
+	vm_info->mem_cookie = mem_cookie;
+	fts_ts_trusted_touch_set_vm_state(fts_data, TRUSTED_TOUCH_PVM_INIT);
+#endif
+	return rc;
+init_fail:
+	fts_ts_vm_deinit(fts_data);
+fail:
+	return rc;
+}
+
+static void fts_ts_dt_parse_trusted_touch_info(struct fts_ts_data *fts_data)
+{
+	struct device_node *np = fts_data->dev->of_node;
+	int rc = 0;
+	const char *selection;
+	const char *environment;
+
+	rc = of_property_read_string(np, "focaltech,trusted-touch-mode",
+								&selection);
+	if (rc) {
+		dev_warn(fts_data->dev,
+			"%s: No trusted touch mode selection made\n", __func__);
+		atomic_set(&fts_data->trusted_touch_mode,
+						TRUSTED_TOUCH_MODE_NONE);
+		return;
+	}
+
+	if (!strcmp(selection, "vm_mode")) {
+		atomic_set(&fts_data->trusted_touch_mode,
+						TRUSTED_TOUCH_VM_MODE);
+		pr_err("Selected trusted touch mode to VM mode\n");
+	} else {
+		atomic_set(&fts_data->trusted_touch_mode,
+						TRUSTED_TOUCH_MODE_NONE);
+		pr_err("Invalid trusted_touch mode\n");
+	}
+
+	rc = of_property_read_string(np, "focaltech,touch-environment",
+						&environment);
+	if (rc) {
+		dev_warn(fts_data->dev,
+			"%s: No trusted touch mode environment\n", __func__);
+	}
+	fts_data->touch_environment = environment;
+	pr_err("Trusted touch environment:%s\n",
+			fts_data->touch_environment);
+}
+
+static void fts_ts_trusted_touch_init(struct fts_ts_data *fts_data)
+{
+	int rc = 0;
+
+	atomic_set(&fts_data->trusted_touch_initialized, 0);
+	fts_ts_dt_parse_trusted_touch_info(fts_data);
+
+	if (atomic_read(&fts_data->trusted_touch_mode) ==
+						TRUSTED_TOUCH_MODE_NONE)
+		return;
+
+	init_completion(&fts_data->trusted_touch_powerdown);
+
+	/* Get clocks */
+	fts_data->core_clk = devm_clk_get(fts_data->dev->parent,
+						"m-ahb");
+	if (IS_ERR(fts_data->core_clk)) {
+		fts_data->core_clk = NULL;
+		dev_warn(fts_data->dev,
+				"%s: core_clk is not defined\n", __func__);
+	}
+
+	fts_data->iface_clk = devm_clk_get(fts_data->dev->parent,
+						"se-clk");
+	if (IS_ERR(fts_data->iface_clk)) {
+		fts_data->iface_clk = NULL;
+		dev_warn(fts_data->dev,
+			"%s: iface_clk is not defined\n", __func__);
+	}
+
+	if (atomic_read(&fts_data->trusted_touch_mode) ==
+						TRUSTED_TOUCH_VM_MODE) {
+		rc = fts_ts_vm_init(fts_data);
+		if (rc)
+			pr_err("Failed to init VM\n");
+	}
+	atomic_set(&fts_data->trusted_touch_initialized, 1);
+}
+
+#endif
+
+/*****************************************************************************
+*  Name: fts_wait_tp_to_valid
+*  Brief: Read chip id until TP FW become valid(Timeout: TIMEOUT_READ_REG),
+*         need call when reset/power on/resume...
+*  Input:
+*  Output:
+*  Return: return 0 if tp valid, otherwise return error code
+*****************************************************************************/
+int fts_wait_tp_to_valid(void)
+{
+	int ret = 0;
+	int cnt = 0;
+	u8 idh = 0;
+	u8 idl = 0;
+	u8 chip_idh = fts_data->ic_info.ids.chip_idh;
+	u8 chip_idl = fts_data->ic_info.ids.chip_idl;
+
+	do {
+		ret = fts_read_reg(FTS_REG_CHIP_ID, &idh);
+		ret = fts_read_reg(FTS_REG_CHIP_ID2, &idl);
+		if ((ret < 0) || (idh != chip_idh) || (idl != chip_idl)) {
+			FTS_DEBUG("TP Not Ready,ReadData:0x%02x%02x", idh, idl);
+		} else if ((idh == chip_idh) && (idl == chip_idl)) {
+			FTS_INFO("TP Ready,Device ID:0x%02x%02x", idh, idl);
+			return 0;
+		}
+		cnt++;
+		msleep(INTERVAL_READ_REG);
+	} while ((cnt * INTERVAL_READ_REG) < TIMEOUT_READ_REG);
+
+	return -EIO;
+}
+
+/*****************************************************************************
+*  Name: fts_tp_state_recovery
+*  Brief: Need execute this function when reset
+*  Input:
+*  Output:
+*  Return:
+*****************************************************************************/
+void fts_tp_state_recovery(struct fts_ts_data *ts_data)
+{
+	FTS_FUNC_ENTER();
+	/* wait tp stable */
+	fts_wait_tp_to_valid();
+	/* recover TP charger state 0x8B */
+	/* recover TP glove state 0xC0 */
+	/* recover TP cover state 0xC1 */
+	fts_ex_mode_recovery(ts_data);
+	/* recover TP gesture state 0xD0 */
+	fts_gesture_recovery(ts_data);
+	FTS_FUNC_EXIT();
+}
+
+int fts_reset_proc(int hdelayms)
+{
+	FTS_DEBUG("tp reset");
+	gpio_direction_output(fts_data->pdata->reset_gpio, 0);
+	msleep(1);
+	gpio_direction_output(fts_data->pdata->reset_gpio, 1);
+	if (hdelayms) {
+		msleep(hdelayms);
+	}
+
+	return 0;
+}
+
+void fts_irq_disable(void)
+{
+	unsigned long irqflags;
+
+	FTS_FUNC_ENTER();
+	spin_lock_irqsave(&fts_data->irq_lock, irqflags);
+
+	if (!fts_data->irq_disabled) {
+#ifdef CONFIG_FTS_TRUSTED_TOUCH
+		if (atomic_read(&fts_data->trusted_touch_transition))
+			disable_irq_wake(fts_data->irq);
+		else
+			disable_irq_nosync(fts_data->irq);
+#else
+		disable_irq_nosync(fts_data->irq);
+#endif
+		fts_data->irq_disabled = true;
+	}
+
+	spin_unlock_irqrestore(&fts_data->irq_lock, irqflags);
+	FTS_FUNC_EXIT();
+}
+
+void fts_irq_enable(void)
+{
+	unsigned long irqflags = 0;
+
+	FTS_FUNC_ENTER();
+	spin_lock_irqsave(&fts_data->irq_lock, irqflags);
+
+	if (fts_data->irq_disabled) {
+#ifdef CONFIG_FTS_TRUSTED_TOUCH
+		if (atomic_read(&fts_data->trusted_touch_transition))
+			enable_irq_wake(fts_data->irq);
+		else
+			enable_irq(fts_data->irq);
+#else
+		enable_irq(fts_data->irq);
+#endif
+		fts_data->irq_disabled = false;
+	}
+
+	spin_unlock_irqrestore(&fts_data->irq_lock, irqflags);
+	FTS_FUNC_EXIT();
+}
+
+void fts_hid2std(void)
+{
+	int ret = 0;
+	u8 buf[3] = {0xEB, 0xAA, 0x09};
+
+	if (fts_data->bus_type != BUS_TYPE_I2C)
+		return;
+
+	ret = fts_write(buf, 3);
+	if (ret < 0) {
+		FTS_ERROR("hid2std cmd write fail");
+		return;
+	}
+
+	msleep(20);
+	buf[0] = buf[1] = buf[2] = 0;
+	ret = fts_read(NULL, 0, buf, 3);
+	if (ret < 0)
+		FTS_ERROR("hid2std cmd read fail");
+	else if ((buf[0] == 0xEB) && (buf[1] == 0xAA) && (buf[2] == 0x08))
+		FTS_DEBUG("hidi2c change to stdi2c successful");
+	else
+		FTS_DEBUG("hidi2c change to stdi2c not support or fail");
+
+}
+
+static int fts_get_chip_types(
+	struct fts_ts_data *ts_data,
+	u8 id_h, u8 id_l, bool fw_valid)
+{
+	int i = 0;
+	u32 ctype_entries = sizeof(ctype) / sizeof(struct ft_chip_t);
+
+	if ((0x0 == id_h) || (0x0 == id_l)) {
+		FTS_ERROR("id_h/id_l is 0");
+		return -EINVAL;
+	}
+
+	FTS_DEBUG("verify id:0x%02x%02x", id_h, id_l);
+	for (i = 0; i < ctype_entries; i++) {
+		if (VALID == fw_valid) {
+			if ((id_h == ctype[i].chip_idh) && (id_l == ctype[i].chip_idl))
+				break;
+		} else {
+			if (((id_h == ctype[i].rom_idh) && (id_l == ctype[i].rom_idl))
+				|| ((id_h == ctype[i].pb_idh) && (id_l == ctype[i].pb_idl))
+				|| ((id_h == ctype[i].bl_idh) && (id_l == ctype[i].bl_idl)))
+			break;
+		}
+	}
+
+	if (i >= ctype_entries)
+		return -ENODATA;
+
+	ts_data->ic_info.ids = ctype[i];
+	return 0;
+}
+
+static int fts_read_bootid(struct fts_ts_data *ts_data, u8 *id)
+{
+	int ret = 0;
+	u8 chip_id[2] = { 0 };
+	u8 id_cmd[4] = { 0 };
+	u32 id_cmd_len = 0;
+
+	id_cmd[0] = FTS_CMD_START1;
+	id_cmd[1] = FTS_CMD_START2;
+	ret = fts_write(id_cmd, 2);
+	if (ret < 0) {
+		FTS_ERROR("start cmd write fail");
+		return ret;
+	}
+
+	msleep(FTS_CMD_START_DELAY);
+	id_cmd[0] = FTS_CMD_READ_ID;
+	id_cmd[1] = id_cmd[2] = id_cmd[3] = 0x00;
+	if (ts_data->ic_info.is_incell)
+		id_cmd_len = FTS_CMD_READ_ID_LEN_INCELL;
+	else
+		id_cmd_len = FTS_CMD_READ_ID_LEN;
+	ret = fts_read(id_cmd, id_cmd_len, chip_id, 2);
+	if ((ret < 0) || (0x0 == chip_id[0]) || (0x0 == chip_id[1])) {
+		FTS_ERROR("read boot id fail,read:0x%02x%02x", chip_id[0], chip_id[1]);
+		return -EIO;
+	}
+
+	id[0] = chip_id[0];
+	id[1] = chip_id[1];
+	return 0;
+}
+
+/*****************************************************************************
+* Name: fts_get_ic_information
+* Brief: read chip id to get ic information, after run the function, driver w-
+*        ill know which IC is it.
+*        If cant get the ic information, maybe not focaltech's touch IC, need
+*        unregister the driver
+* Input:
+* Output:
+* Return: return 0 if get correct ic information, otherwise return error code
+*****************************************************************************/
+static int fts_get_ic_information(struct fts_ts_data *ts_data)
+{
+	int ret = 0;
+	int cnt = 0;
+	u8 chip_id[2] = { 0 };
+	u32 type = ts_data->pdata->type;
+
+	ts_data->ic_info.is_incell = FTS_CHIP_IDC(type);
+	ts_data->ic_info.hid_supported = FTS_HID_SUPPORTTED(type);
+
+	do {
+		ret = fts_read_reg(FTS_REG_CHIP_ID, &chip_id[0]);
+		ret = fts_read_reg(FTS_REG_CHIP_ID2, &chip_id[1]);
+		if ((ret < 0) || (0x0 == chip_id[0]) || (0x0 == chip_id[1])) {
+			FTS_DEBUG("i2c read invalid, read:0x%02x%02x",
+				chip_id[0], chip_id[1]);
+		} else {
+			ret = fts_get_chip_types(ts_data, chip_id[0], chip_id[1], VALID);
+			if (!ret)
+				break;
+			else
+				FTS_DEBUG("TP not ready, read:0x%02x%02x",
+						chip_id[0], chip_id[1]);
+		}
+
+		cnt++;
+		msleep(INTERVAL_READ_REG);
+	} while ((cnt * INTERVAL_READ_REG) < TIMEOUT_READ_REG);
+
+	if ((cnt * INTERVAL_READ_REG) >= TIMEOUT_READ_REG) {
+		FTS_INFO("fw is invalid, need read boot id");
+		if (ts_data->ic_info.hid_supported) {
+			fts_hid2std();
+		}
+
+		ret = fts_read_bootid(ts_data, &chip_id[0]);
+		if (ret <  0) {
+			FTS_ERROR("read boot id fail");
+			return ret;
+		}
+
+		ret = fts_get_chip_types(ts_data, chip_id[0], chip_id[1], INVALID);
+		if (ret < 0) {
+			FTS_ERROR("can't get ic informaton");
+			return ret;
+		}
+	}
+
+	FTS_INFO("get ic information, chip id = 0x%02x%02x",
+		ts_data->ic_info.ids.chip_idh, ts_data->ic_info.ids.chip_idl);
+
+	return 0;
+}
+
+/*****************************************************************************
+*  Reprot related
+*****************************************************************************/
+static void fts_show_touch_buffer(u8 *data, int datalen)
+{
+	int i = 0;
+	int count = 0;
+	char *tmpbuf = NULL;
+
+	tmpbuf = kzalloc(1024, GFP_KERNEL);
+	if (!tmpbuf) {
+		FTS_ERROR("tmpbuf zalloc fail");
+		return;
+	}
+
+	for (i = 0; i < datalen; i++) {
+		count += snprintf(tmpbuf + count, 1024 - count, "%02X,", data[i]);
+		if (count >= 1024)
+			break;
+	}
+	FTS_DEBUG("point buffer:%s", tmpbuf);
+
+	if (tmpbuf) {
+		kfree(tmpbuf);
+		tmpbuf = NULL;
+	}
+}
+
+void fts_release_all_finger(void)
+{
+	struct input_dev *input_dev = fts_data->input_dev;
+#if FTS_MT_PROTOCOL_B_EN
+	u32 finger_count = 0;
+	u32 max_touches = fts_data->pdata->max_touch_number;
+#endif
+
+	FTS_FUNC_ENTER();
+	mutex_lock(&fts_data->report_mutex);
+#if FTS_MT_PROTOCOL_B_EN
+	for (finger_count = 0; finger_count < max_touches; finger_count++) {
+		input_mt_slot(input_dev, finger_count);
+		input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, false);
+	}
+#else
+	input_mt_sync(input_dev);
+#endif
+	input_report_key(input_dev, BTN_TOUCH, 0);
+	input_sync(input_dev);
+
+	fts_data->touchs = 0;
+	fts_data->key_state = 0;
+	mutex_unlock(&fts_data->report_mutex);
+	FTS_FUNC_EXIT();
+}
+
+/*****************************************************************************
+* Name: fts_input_report_key
+* Brief: process key events,need report key-event if key enable.
+*        if point's coordinate is in (x_dim-50,y_dim-50) ~ (x_dim+50,y_dim+50),
+*        need report it to key event.
+*        x_dim: parse from dts, means key x_coordinate, dimension:+-50
+*        y_dim: parse from dts, means key y_coordinate, dimension:+-50
+* Input:
+* Output:
+* Return: return 0 if it's key event, otherwise return error code
+*****************************************************************************/
+static int fts_input_report_key(struct fts_ts_data *data, int index)
+{
+	int i = 0;
+	int x = data->events[index].x;
+	int y = data->events[index].y;
+	int *x_dim = &data->pdata->key_x_coords[0];
+	int *y_dim = &data->pdata->key_y_coords[0];
+
+	if (!data->pdata->have_key) {
+		return -EINVAL;
+	}
+
+	for (i = 0; i < data->pdata->key_number; i++) {
+		if ((x >= x_dim[i] - FTS_KEY_DIM) && (x <= x_dim[i] + FTS_KEY_DIM) &&
+			(y >= y_dim[i] - FTS_KEY_DIM) && (y <= y_dim[i] + FTS_KEY_DIM)) {
+			if (EVENT_DOWN(data->events[index].flag)
+				&& !(data->key_state & (1 << i))) {
+				input_report_key(data->input_dev, data->pdata->keys[i], 1);
+				data->key_state |= (1 << i);
+				FTS_DEBUG("Key%d(%d,%d) DOWN!", i, x, y);
+			} else if (EVENT_UP(data->events[index].flag)
+				&& (data->key_state & (1 << i))) {
+				input_report_key(data->input_dev, data->pdata->keys[i], 0);
+				data->key_state &= ~(1 << i);
+				FTS_DEBUG("Key%d(%d,%d) Up!", i, x, y);
+			}
+			return 0;
+		}
+	}
+	return -EINVAL;
+}
+
+#if FTS_MT_PROTOCOL_B_EN
+static int fts_input_report_b(struct fts_ts_data *data)
+{
+	int i = 0;
+	int uppoint = 0;
+	int touchs = 0;
+	bool va_reported = false;
+	u32 max_touch_num = data->pdata->max_touch_number;
+	struct ts_event *events = data->events;
+
+	for (i = 0; i < data->touch_point; i++) {
+		if (fts_input_report_key(data, i) == 0)
+			continue;
+
+		va_reported = true;
+		input_mt_slot(data->input_dev, events[i].id);
+
+		if (EVENT_DOWN(events[i].flag)) {
+			input_mt_report_slot_state(data->input_dev, MT_TOOL_FINGER, true);
+
+#if FTS_REPORT_PRESSURE_EN
+			if (events[i].p <= 0) {
+				events[i].p = 0x3f;
+			}
+			input_report_abs(data->input_dev, ABS_MT_PRESSURE, events[i].p);
+#endif
+			if (events[i].area <= 0) {
+				events[i].area = 0x09;
+			}
+			input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR, events[i].area);
+			input_report_abs(data->input_dev, ABS_MT_POSITION_X, events[i].x);
+			input_report_abs(data->input_dev, ABS_MT_POSITION_Y, events[i].y);
+
+			touchs |= BIT(events[i].id);
+			data->touchs |= BIT(events[i].id);
+
+			if ((data->log_level >= 2) ||
+				((1 == data->log_level) && (FTS_TOUCH_DOWN == events[i].flag))) {
+				FTS_DEBUG("[B]P%d(%d, %d)[p:%d,tm:%d] DOWN!",
+					events[i].id,
+					events[i].x, events[i].y,
+					events[i].p, events[i].area);
+			}
+		} else {
+			uppoint++;
+			input_mt_report_slot_state(data->input_dev, MT_TOOL_FINGER, false);
+			data->touchs &= ~BIT(events[i].id);
+			if (data->log_level >= 1) {
+				FTS_DEBUG("[B]P%d UP!", events[i].id);
+			}
+		}
+	}
+
+	if (unlikely(data->touchs ^ touchs)) {
+		for (i = 0; i < max_touch_num; i++)  {
+			if (BIT(i) & (data->touchs ^ touchs)) {
+				if (data->log_level >= 1) {
+					FTS_DEBUG("[B]P%d UP!", i);
+				}
+			va_reported = true;
+			input_mt_slot(data->input_dev, i);
+			input_mt_report_slot_state(data->input_dev, MT_TOOL_FINGER, false);
+			}
+		}
+	}
+	data->touchs = touchs;
+
+	if (va_reported) {
+		/* touchs==0, there's no point but key */
+		if (EVENT_NO_DOWN(data) || (!touchs)) {
+			if (data->log_level >= 1) {
+				FTS_DEBUG("[B]Points All Up!");
+			}
+			input_report_key(data->input_dev, BTN_TOUCH, 0);
+		} else {
+			input_report_key(data->input_dev, BTN_TOUCH, 1);
+		}
+	}
+
+	input_sync(data->input_dev);
+	return 0;
+}
+
+#else
+static int fts_input_report_a(struct fts_ts_data *data)
+{
+	int i = 0;
+	int touchs = 0;
+	bool va_reported = false;
+	struct ts_event *events = data->events;
+
+	for (i = 0; i < data->touch_point; i++) {
+		if (fts_input_report_key(data, i) == 0) {
+			continue;
+		}
+
+		va_reported = true;
+		if (EVENT_DOWN(events[i].flag)) {
+			input_report_abs(data->input_dev, ABS_MT_TRACKING_ID, events[i].id);
+#if FTS_REPORT_PRESSURE_EN
+			if (events[i].p <= 0) {
+				events[i].p = 0x3f;
+			}
+			input_report_abs(data->input_dev, ABS_MT_PRESSURE, events[i].p);
+#endif
+			if (events[i].area <= 0) {
+				events[i].area = 0x09;
+			}
+			input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR, events[i].area);
+
+			input_report_abs(data->input_dev, ABS_MT_POSITION_X, events[i].x);
+			input_report_abs(data->input_dev, ABS_MT_POSITION_Y, events[i].y);
+
+			input_mt_sync(data->input_dev);
+
+			if ((data->log_level >= 2) ||
+				((1 == data->log_level) && (FTS_TOUCH_DOWN == events[i].flag))) {
+				FTS_DEBUG("[A]P%d(%d, %d)[p:%d,tm:%d] DOWN!",
+					events[i].id,
+					events[i].x, events[i].y,
+					events[i].p, events[i].area);
+			}
+			touchs++;
+		}
+	}
+
+	/* last point down, current no point but key */
+	if (data->touchs && !touchs) {
+		va_reported = true;
+	}
+	data->touchs = touchs;
+
+	if (va_reported) {
+		if (EVENT_NO_DOWN(data)) {
+			if (data->log_level >= 1) {
+				FTS_DEBUG("[A]Points All Up!");
+			}
+			input_report_key(data->input_dev, BTN_TOUCH, 0);
+			input_mt_sync(data->input_dev);
+		} else {
+			input_report_key(data->input_dev, BTN_TOUCH, 1);
+		}
+	}
+
+	input_sync(data->input_dev);
+	return 0;
+}
+#endif
+
+static int fts_read_touchdata(struct fts_ts_data *data)
+{
+	int ret = 0;
+	u8 *buf = data->point_buf;
+
+	memset(buf, 0xFF, data->pnt_buf_size);
+	buf[0] = 0x01;
+
+	if (data->gesture_mode) {
+		if (0 == fts_gesture_readdata(data, NULL)) {
+			FTS_INFO("succuss to get gesture data in irq handler");
+			return 1;
+		}
+	}
+
+	ret = fts_read(buf, 1, buf + 1, data->pnt_buf_size - 1);
+	if (ret < 0) {
+		FTS_ERROR("read touchdata failed, ret:%d", ret);
+		return ret;
+	}
+
+	if (data->log_level >= 3) {
+		fts_show_touch_buffer(buf, data->pnt_buf_size);
+	}
+
+	return 0;
+}
+
+static int fts_read_parse_touchdata(struct fts_ts_data *data)
+{
+	int ret = 0;
+	int i = 0;
+	u8 pointid = 0;
+	int base = 0;
+	struct ts_event *events = data->events;
+	int max_touch_num = data->pdata->max_touch_number;
+	u8 *buf = data->point_buf;
+
+	ret = fts_read_touchdata(data);
+	if (ret) {
+		return ret;
+	}
+
+	data->point_num = buf[FTS_TOUCH_POINT_NUM] & 0x0F;
+	data->touch_point = 0;
+
+	if (data->ic_info.is_incell) {
+		if ((data->point_num == 0x0F) && (buf[2] == 0xFF) && (buf[3] == 0xFF)
+			&& (buf[4] == 0xFF) && (buf[5] == 0xFF) && (buf[6] == 0xFF)) {
+			FTS_DEBUG("touch buff is 0xff, need recovery state");
+			fts_release_all_finger();
+			fts_tp_state_recovery(data);
+			return -EIO;
+		}
+	}
+
+	if (data->point_num > max_touch_num) {
+		FTS_INFO("invalid point_num(%d)", data->point_num);
+		return -EIO;
+	}
+
+	for (i = 0; i < max_touch_num; i++) {
+		base = FTS_ONE_TCH_LEN * i;
+		pointid = (buf[FTS_TOUCH_ID_POS + base]) >> 4;
+		if (pointid >= FTS_MAX_ID)
+			break;
+		else if (pointid >= max_touch_num) {
+			FTS_ERROR("ID(%d) beyond max_touch_number", pointid);
+			return -EINVAL;
+		}
+
+		data->touch_point++;
+		events[i].x = ((buf[FTS_TOUCH_X_H_POS + base] & 0x0F) << 8) +
+				(buf[FTS_TOUCH_X_L_POS + base] & 0xFF);
+		events[i].y = ((buf[FTS_TOUCH_Y_H_POS + base] & 0x0F) << 8) +
+				(buf[FTS_TOUCH_Y_L_POS + base] & 0xFF);
+		events[i].flag = buf[FTS_TOUCH_EVENT_POS + base] >> 6;
+		events[i].id = buf[FTS_TOUCH_ID_POS + base] >> 4;
+		events[i].area = buf[FTS_TOUCH_AREA_POS + base] >> 4;
+		events[i].p =  buf[FTS_TOUCH_PRE_POS + base];
+
+		if (EVENT_DOWN(events[i].flag) && (data->point_num == 0)) {
+			FTS_INFO("abnormal touch data from fw");
+			return -EIO;
+		}
+	}
+
+	if (data->touch_point == 0) {
+		FTS_INFO("no touch point information");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static void fts_irq_read_report(void)
+{
+	int ret = 0;
+	struct fts_ts_data *ts_data = fts_data;
+
+#if FTS_ESDCHECK_EN
+	fts_esdcheck_set_intr(1);
+#endif
+
+#if FTS_POINT_REPORT_CHECK_EN
+	fts_prc_queue_work(ts_data);
+#endif
+
+	ret = fts_read_parse_touchdata(ts_data);
+	if (ret == 0) {
+		mutex_lock(&ts_data->report_mutex);
+#if FTS_MT_PROTOCOL_B_EN
+		fts_input_report_b(ts_data);
+#else
+		fts_input_report_a(ts_data);
+#endif
+		mutex_unlock(&ts_data->report_mutex);
+	}
+
+#if FTS_ESDCHECK_EN
+	fts_esdcheck_set_intr(0);
+#endif
+}
+
+static irqreturn_t fts_irq_handler(int irq, void *data)
+{
+	struct fts_ts_data *fts_data = data;
+
+	if (!fts_data) {
+		pr_err("%s: Invalid fts_data\n", __func__);
+		return IRQ_HANDLED;
+	}
+
+	if (!mutex_trylock(&fts_data->transition_lock))
+		return IRQ_HANDLED;
+
+	fts_irq_read_report();
+	mutex_unlock(&fts_data->transition_lock);
+
+	return IRQ_HANDLED;
+}
+
+static int fts_irq_registration(struct fts_ts_data *ts_data)
+{
+	int ret = 0;
+	struct fts_ts_platform_data *pdata = ts_data->pdata;
+
+#ifdef CONFIG_ARCH_QTI_VM
+	pdata->irq_gpio_flags = IRQF_TRIGGER_RISING | IRQF_ONESHOT;
+	FTS_INFO("irq:%d, flag:%x", ts_data->irq, pdata->irq_gpio_flags);
+	ret = request_threaded_irq(ts_data->irq, NULL, fts_irq_handler,
+				pdata->irq_gpio_flags,
+				FTS_DRIVER_NAME, ts_data);
+#else
+	ts_data->irq = gpio_to_irq(pdata->irq_gpio);
+	pdata->irq_gpio_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT;
+	FTS_INFO("irq:%d, flag:%x", ts_data->irq, pdata->irq_gpio_flags);
+	ret = request_threaded_irq(ts_data->irq, NULL, fts_irq_handler,
+				pdata->irq_gpio_flags,
+				FTS_DRIVER_NAME, ts_data);
+#endif
+	return ret;
+}
+
+static int fts_input_init(struct fts_ts_data *ts_data)
+{
+	int ret = 0;
+	int key_num = 0;
+	struct fts_ts_platform_data *pdata = ts_data->pdata;
+	struct input_dev *input_dev;
+
+	FTS_FUNC_ENTER();
+	input_dev = input_allocate_device();
+	if (!input_dev) {
+		FTS_ERROR("Failed to allocate memory for input device");
+		return -ENOMEM;
+	}
+
+	/* Init and register Input device */
+	input_dev->name = FTS_DRIVER_NAME;
+	if (ts_data->bus_type == BUS_TYPE_I2C)
+		input_dev->id.bustype = BUS_I2C;
+	else
+		input_dev->id.bustype = BUS_SPI;
+	input_dev->dev.parent = ts_data->dev;
+
+	input_set_drvdata(input_dev, ts_data);
+
+	__set_bit(EV_SYN, input_dev->evbit);
+	__set_bit(EV_ABS, input_dev->evbit);
+	__set_bit(EV_KEY, input_dev->evbit);
+	__set_bit(BTN_TOUCH, input_dev->keybit);
+	__set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
+
+	if (pdata->have_key) {
+		FTS_INFO("set key capabilities");
+		for (key_num = 0; key_num < pdata->key_number; key_num++)
+			input_set_capability(input_dev, EV_KEY, pdata->keys[key_num]);
+	}
+
+#if FTS_MT_PROTOCOL_B_EN
+	input_mt_init_slots(input_dev, pdata->max_touch_number, INPUT_MT_DIRECT);
+#else
+	input_set_abs_params(input_dev, ABS_MT_TRACKING_ID, 0, 0x0F, 0, 0);
+#endif
+	input_set_abs_params(input_dev, ABS_MT_POSITION_X, pdata->x_min, pdata->x_max, 0, 0);
+	input_set_abs_params(input_dev, ABS_MT_POSITION_Y, pdata->y_min, pdata->y_max, 0, 0);
+	input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, 0xFF, 0, 0);
+#if FTS_REPORT_PRESSURE_EN
+	input_set_abs_params(input_dev, ABS_MT_PRESSURE, 0, 0xFF, 0, 0);
+#endif
+
+	ret = input_register_device(input_dev);
+	if (ret) {
+		FTS_ERROR("Input device registration failed");
+		input_set_drvdata(input_dev, NULL);
+		input_free_device(input_dev);
+		input_dev = NULL;
+		return ret;
+	}
+
+	ts_data->input_dev = input_dev;
+
+	FTS_FUNC_EXIT();
+	return 0;
+}
+
+static int fts_report_buffer_init(struct fts_ts_data *ts_data)
+{
+	int point_num = 0;
+	int events_num = 0;
+
+	point_num = FTS_MAX_POINTS_SUPPORT;
+	ts_data->pnt_buf_size = FTS_TOUCH_DATA_LEN + FTS_GESTURE_DATA_LEN;
+	ts_data->point_buf = (u8 *)kzalloc(ts_data->pnt_buf_size + 1, GFP_KERNEL);
+	if (!ts_data->point_buf) {
+		FTS_ERROR("failed to alloc memory for point buf");
+		return -ENOMEM;
+	}
+
+	events_num = point_num * sizeof(struct ts_event);
+	ts_data->events = (struct ts_event *)kzalloc(events_num, GFP_KERNEL);
+	if (!ts_data->events) {
+		FTS_ERROR("failed to alloc memory for point events");
+		kfree_safe(ts_data->point_buf);
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+#if FTS_POWER_SOURCE_CUST_EN
+/*****************************************************************************
+* Power Control
+*****************************************************************************/
+#if FTS_PINCTRL_EN
+static int fts_pinctrl_init(struct fts_ts_data *ts)
+{
+	int ret = 0;
+
+	ts->pinctrl = devm_pinctrl_get(ts->dev);
+	if (IS_ERR_OR_NULL(ts->pinctrl)) {
+		FTS_ERROR("Failed to get pinctrl, please check dts");
+		ret = PTR_ERR(ts->pinctrl);
+		goto err_pinctrl_get;
+	}
+
+	ts->pins_active = pinctrl_lookup_state(ts->pinctrl, "pmx_ts_active");
+	if (IS_ERR_OR_NULL(ts->pins_active)) {
+		FTS_ERROR("Pin state[active] not found");
+		ret = PTR_ERR(ts->pins_active);
+		goto err_pinctrl_lookup;
+	}
+
+	ts->pins_suspend = pinctrl_lookup_state(ts->pinctrl, "pmx_ts_suspend");
+	if (IS_ERR_OR_NULL(ts->pins_suspend)) {
+		FTS_ERROR("Pin state[suspend] not found");
+		ret = PTR_ERR(ts->pins_suspend);
+		goto err_pinctrl_lookup;
+	}
+
+	ts->pins_release = pinctrl_lookup_state(ts->pinctrl, "pmx_ts_release");
+	if (IS_ERR_OR_NULL(ts->pins_release)) {
+		FTS_ERROR("Pin state[release] not found");
+		ret = PTR_ERR(ts->pins_release);
+	}
+
+	return 0;
+err_pinctrl_lookup:
+	if (ts->pinctrl) {
+		devm_pinctrl_put(ts->pinctrl);
+	}
+err_pinctrl_get:
+	ts->pinctrl = NULL;
+	ts->pins_release = NULL;
+	ts->pins_suspend = NULL;
+	ts->pins_active = NULL;
+	return ret;
+}
+
+static int fts_pinctrl_select_normal(struct fts_ts_data *ts)
+{
+	int ret = 0;
+
+	if (ts->pinctrl && ts->pins_active) {
+		ret = pinctrl_select_state(ts->pinctrl, ts->pins_active);
+		if (ret < 0) {
+			FTS_ERROR("Set normal pin state error:%d", ret);
+		}
+	}
+
+	return ret;
+}
+
+static int fts_pinctrl_select_suspend(struct fts_ts_data *ts)
+{
+	int ret = 0;
+
+	if (ts->pinctrl && ts->pins_suspend) {
+		ret = pinctrl_select_state(ts->pinctrl, ts->pins_suspend);
+		if (ret < 0) {
+			FTS_ERROR("Set suspend pin state error:%d", ret);
+		}
+	}
+
+	return ret;
+}
+
+static int fts_pinctrl_select_release(struct fts_ts_data *ts)
+{
+	int ret = 0;
+
+	if (ts->pinctrl) {
+		if (IS_ERR_OR_NULL(ts->pins_release)) {
+			devm_pinctrl_put(ts->pinctrl);
+			ts->pinctrl = NULL;
+		} else {
+			ret = pinctrl_select_state(ts->pinctrl, ts->pins_release);
+			if (ret < 0)
+				FTS_ERROR("Set gesture pin state error:%d", ret);
+		}
+	}
+
+	return ret;
+}
+#endif /* FTS_PINCTRL_EN */
+
+static int fts_power_configure(struct fts_ts_data *ts_data, bool enable)
+{
+	int ret = 0;
+
+	FTS_FUNC_ENTER();
+
+	if (enable) {
+		if (regulator_count_voltages(ts_data->vdd) > 0) {
+			ret = regulator_set_load(ts_data->vdd, FTS_LOAD_MAX_UA);
+			if (ret) {
+				FTS_ERROR("vdd regulator set_load failed ret=%d", ret);
+				return ret;
+			}
+
+			ret = regulator_set_voltage(ts_data->vdd, FTS_VTG_MIN_UV,
+						FTS_VTG_MAX_UV);
+			if (ret) {
+				FTS_ERROR("vdd regulator set_vtg failed ret=%d", ret);
+				goto err_vdd_load;
+			}
+		}
+
+		if (!IS_ERR_OR_NULL(ts_data->vcc_i2c)) {
+			if (regulator_count_voltages(ts_data->vcc_i2c) > 0) {
+				ret = regulator_set_load(ts_data->vcc_i2c, FTS_LOAD_AVDD_UA);
+				if (ret) {
+					FTS_ERROR("vcc_i2c regulator set_load failed ret=%d", ret);
+					goto err_vdd_load;
+				}
+
+				ret = regulator_set_voltage(ts_data->vcc_i2c,
+							FTS_I2C_VTG_MIN_UV,
+							FTS_I2C_VTG_MAX_UV);
+				if (ret) {
+					FTS_ERROR("vcc_i2c regulator set_vtg failed,ret=%d", ret);
+					goto err_vcc_load;
+				}
+			}
+		}
+	} else {
+		if (regulator_count_voltages(ts_data->vdd) > 0) {
+			ret = regulator_set_load(ts_data->vdd, FTS_LOAD_DISABLE_UA);
+			if (ret) {
+				FTS_ERROR("vdd regulator set_load failed ret=%d", ret);
+				return ret;
+			}
+		}
+
+		if (!IS_ERR_OR_NULL(ts_data->vcc_i2c)) {
+			if (regulator_count_voltages(ts_data->vcc_i2c) > 0) {
+				ret = regulator_set_load(ts_data->vcc_i2c, FTS_LOAD_DISABLE_UA);
+				if (ret) {
+					FTS_ERROR("vcc_i2c regulator set_load failed ret=%d", ret);
+					return ret;
+				}
+			}
+		}
+	}
+
+	FTS_FUNC_EXIT();
+	return ret;
+
+err_vcc_load:
+	regulator_set_load(ts_data->vcc_i2c, FTS_LOAD_DISABLE_UA);
+err_vdd_load:
+	regulator_set_load(ts_data->vdd, FTS_LOAD_DISABLE_UA);
+	return ret;
+}
+
+static int fts_ts_enable_reg(struct fts_ts_data *ts_data, bool enable)
+{
+	int ret = 0;
+
+	if (IS_ERR_OR_NULL(ts_data->vdd)) {
+		FTS_ERROR("vdd is invalid");
+		return -EINVAL;
+	}
+
+	if (enable) {
+		fts_power_configure(ts_data, true);
+		ret = regulator_enable(ts_data->vdd);
+		if (ret)
+			FTS_ERROR("enable vdd regulator failed,ret=%d", ret);
+
+		if (!IS_ERR_OR_NULL(ts_data->vcc_i2c)) {
+			ret = regulator_enable(ts_data->vcc_i2c);
+			if (ret)
+				FTS_ERROR("enable vcc_i2c regulator failed,ret=%d", ret);
+		}
+	} else {
+		ret = regulator_disable(ts_data->vdd);
+		if (ret)
+			FTS_ERROR("disable vdd regulator failed,ret=%d", ret);
+		if (!IS_ERR_OR_NULL(ts_data->vcc_i2c)) {
+			ret = regulator_disable(ts_data->vcc_i2c);
+			if (ret)
+				FTS_ERROR("disable vcc_i2c regulator failed,ret=%d", ret);
+		}
+		fts_power_configure(ts_data, false);
+	}
+
+	return ret;
+}
+
+static int fts_power_source_ctrl(struct fts_ts_data *ts_data, int enable)
+{
+	int ret = 0;
+
+	if (IS_ERR_OR_NULL(ts_data->vdd)) {
+		FTS_ERROR("vdd is invalid");
+		return -EINVAL;
+	}
+
+	FTS_FUNC_ENTER();
+	if (enable) {
+		if (ts_data->power_disabled) {
+			FTS_DEBUG("regulator enable !");
+			gpio_direction_output(ts_data->pdata->reset_gpio, 0);
+			msleep(1);
+			ret = fts_ts_enable_reg(ts_data, true);
+			if (ret)
+				FTS_ERROR("Touch reg enable failed\n");
+			ts_data->power_disabled = false;
+		}
+	} else {
+		if (!ts_data->power_disabled) {
+			FTS_DEBUG("regulator disable !");
+			gpio_direction_output(ts_data->pdata->reset_gpio, 0);
+			msleep(1);
+			ret = fts_ts_enable_reg(ts_data, false);
+			if (ret)
+				FTS_ERROR("Touch reg disable failed");
+			ts_data->power_disabled = true;
+		}
+	}
+
+	FTS_FUNC_EXIT();
+	return ret;
+}
+
+/*****************************************************************************
+* Name: fts_power_source_init
+* Brief: Init regulator power:vdd/vcc_io(if have), generally, no vcc_io
+*        vdd---->vdd-supply in dts, kernel will auto add "-supply" to parse
+*        Must be call after fts_gpio_configure() execute,because this function
+*        will operate reset-gpio which request gpio in fts_gpio_configure()
+* Input:
+* Output:
+* Return: return 0 if init power successfully, otherwise return error code
+*****************************************************************************/
+static int fts_power_source_init(struct fts_ts_data *ts_data)
+{
+	int ret = 0;
+
+	FTS_FUNC_ENTER();
+	ts_data->vdd = regulator_get(ts_data->dev, "vdd");
+	if (IS_ERR_OR_NULL(ts_data->vdd)) {
+		ret = PTR_ERR(ts_data->vdd);
+		FTS_ERROR("get vdd regulator failed,ret=%d", ret);
+		return ret;
+	}
+
+	ts_data->vcc_i2c = regulator_get(ts_data->dev, "vcc_i2c");
+	if (IS_ERR_OR_NULL(ts_data->vcc_i2c))
+		FTS_INFO("get vcc_i2c regulator failed");
+
+#if FTS_PINCTRL_EN
+	fts_pinctrl_init(ts_data);
+	fts_pinctrl_select_normal(ts_data);
+#endif
+
+	ts_data->power_disabled = true;
+	ret = fts_power_source_ctrl(ts_data, ENABLE);
+	if (ret) {
+		FTS_ERROR("fail to enable power(regulator)");
+	}
+
+	FTS_FUNC_EXIT();
+	return ret;
+}
+
+static int fts_power_source_exit(struct fts_ts_data *ts_data)
+{
+#if FTS_PINCTRL_EN
+	fts_pinctrl_select_release(ts_data);
+#endif
+
+	fts_power_source_ctrl(ts_data, DISABLE);
+
+	if (!IS_ERR_OR_NULL(ts_data->vdd)) {
+		if (regulator_count_voltages(ts_data->vdd) > 0)
+			regulator_set_voltage(ts_data->vdd, 0, FTS_VTG_MAX_UV);
+		regulator_put(ts_data->vdd);
+	}
+
+	if (!IS_ERR_OR_NULL(ts_data->vcc_i2c)) {
+		if (regulator_count_voltages(ts_data->vcc_i2c) > 0)
+			regulator_set_voltage(ts_data->vcc_i2c, 0, FTS_I2C_VTG_MAX_UV);
+		regulator_put(ts_data->vcc_i2c);
+	}
+
+	return 0;
+}
+
+static int fts_power_source_suspend(struct fts_ts_data *ts_data)
+{
+	int ret = 0;
+
+#if FTS_PINCTRL_EN
+	fts_pinctrl_select_suspend(ts_data);
+#endif
+
+	ret = fts_power_source_ctrl(ts_data, DISABLE);
+	if (ret < 0) {
+		FTS_ERROR("power off fail, ret=%d", ret);
+	}
+
+	return ret;
+}
+
+static int fts_power_source_resume(struct fts_ts_data *ts_data)
+{
+	int ret = 0;
+
+#if FTS_PINCTRL_EN
+	fts_pinctrl_select_normal(ts_data);
+#endif
+
+	ret = fts_power_source_ctrl(ts_data, ENABLE);
+	if (ret < 0) {
+		FTS_ERROR("power on fail, ret=%d", ret);
+	}
+
+	return ret;
+}
+#endif /* FTS_POWER_SOURCE_CUST_EN */
+
+static int fts_gpio_configure(struct fts_ts_data *data)
+{
+	int ret = 0;
+
+	FTS_FUNC_ENTER();
+	/* request irq gpio */
+	if (gpio_is_valid(data->pdata->irq_gpio)) {
+		ret = gpio_request(data->pdata->irq_gpio, "fts_irq_gpio");
+		if (ret) {
+			FTS_ERROR("[GPIO]irq gpio request failed");
+			goto err_irq_gpio_req;
+		}
+
+		ret = gpio_direction_input(data->pdata->irq_gpio);
+		if (ret) {
+			FTS_ERROR("[GPIO]set_direction for irq gpio failed");
+			goto err_irq_gpio_dir;
+		}
+	}
+
+	/* request reset gpio */
+	if (gpio_is_valid(data->pdata->reset_gpio)) {
+		ret = gpio_request(data->pdata->reset_gpio, "fts_reset_gpio");
+		if (ret) {
+			FTS_ERROR("[GPIO]reset gpio request failed");
+			goto err_irq_gpio_dir;
+		}
+
+		ret = gpio_direction_output(data->pdata->reset_gpio, 1);
+		if (ret) {
+			FTS_ERROR("[GPIO]set_direction for reset gpio failed");
+			goto err_reset_gpio_dir;
+		}
+	}
+
+	FTS_FUNC_EXIT();
+	return 0;
+
+err_reset_gpio_dir:
+	if (gpio_is_valid(data->pdata->reset_gpio))
+		gpio_free(data->pdata->reset_gpio);
+err_irq_gpio_dir:
+	if (gpio_is_valid(data->pdata->irq_gpio))
+		gpio_free(data->pdata->irq_gpio);
+err_irq_gpio_req:
+	FTS_FUNC_EXIT();
+	return ret;
+}
+
+static int fts_get_dt_coords(struct device *dev, char *name,
+				struct fts_ts_platform_data *pdata)
+{
+	int ret = 0;
+	u32 coords[FTS_COORDS_ARR_SIZE] = { 0 };
+	struct property *prop;
+	struct device_node *np = dev->of_node;
+	int coords_size;
+
+	prop = of_find_property(np, name, NULL);
+	if (!prop)
+		return -EINVAL;
+	if (!prop->value)
+		return -ENODATA;
+
+	coords_size = prop->length / sizeof(u32);
+	if (coords_size != FTS_COORDS_ARR_SIZE) {
+		FTS_ERROR("invalid:%s, size:%d", name, coords_size);
+		return -EINVAL;
+	}
+
+	ret = of_property_read_u32_array(np, name, coords, coords_size);
+	if (ret < 0) {
+		FTS_ERROR("Unable to read %s, please check dts", name);
+		pdata->x_min = FTS_X_MIN_DISPLAY_DEFAULT;
+		pdata->y_min = FTS_Y_MIN_DISPLAY_DEFAULT;
+		pdata->x_max = FTS_X_MAX_DISPLAY_DEFAULT;
+		pdata->y_max = FTS_Y_MAX_DISPLAY_DEFAULT;
+		return -ENODATA;
+	} else {
+		pdata->x_min = coords[0];
+		pdata->y_min = coords[1];
+		pdata->x_max = coords[2];
+		pdata->y_max = coords[3];
+	}
+
+	FTS_INFO("display x(%d %d) y(%d %d)", pdata->x_min, pdata->x_max,
+		pdata->y_min, pdata->y_max);
+	return 0;
+}
+
+static int fts_parse_dt(struct device *dev, struct fts_ts_platform_data *pdata)
+{
+	int ret = 0;
+	struct device_node *np = dev->of_node;
+	u32 temp_val = 0;
+
+	FTS_FUNC_ENTER();
+
+	ret = fts_get_dt_coords(dev, "focaltech,display-coords", pdata);
+	if (ret < 0)
+		FTS_ERROR("Unable to get display-coords");
+
+	/* key */
+	pdata->have_key = of_property_read_bool(np, "focaltech,have-key");
+	if (pdata->have_key) {
+		ret = of_property_read_u32(np, "focaltech,key-number", &pdata->key_number);
+		if (ret < 0)
+			FTS_ERROR("Key number undefined!");
+
+		ret = of_property_read_u32_array(np, "focaltech,keys",
+						pdata->keys, pdata->key_number);
+		if (ret < 0)
+			FTS_ERROR("Keys undefined!");
+		else if (pdata->key_number > FTS_MAX_KEYS)
+			pdata->key_number = FTS_MAX_KEYS;
+
+		ret = of_property_read_u32_array(np, "focaltech,key-x-coords",
+						pdata->key_x_coords,
+						pdata->key_number);
+		if (ret < 0)
+			FTS_ERROR("Key Y Coords undefined!");
+
+		ret = of_property_read_u32_array(np, "focaltech,key-y-coords",
+						pdata->key_y_coords,
+						pdata->key_number);
+		if (ret < 0)
+			FTS_ERROR("Key X Coords undefined!");
+
+		FTS_INFO("VK Number:%d, key:(%d,%d,%d), "
+			"coords:(%d,%d),(%d,%d),(%d,%d)",
+			pdata->key_number,
+			pdata->keys[0], pdata->keys[1], pdata->keys[2],
+			pdata->key_x_coords[0], pdata->key_y_coords[0],
+			pdata->key_x_coords[1], pdata->key_y_coords[1],
+			pdata->key_x_coords[2], pdata->key_y_coords[2]);
+	}
+
+	/* reset, irq gpio info */
+	pdata->reset_gpio = of_get_named_gpio_flags(np, "focaltech,reset-gpio",
+			0, &pdata->reset_gpio_flags);
+	if (pdata->reset_gpio < 0)
+		FTS_ERROR("Unable to get reset_gpio");
+
+	pdata->irq_gpio = of_get_named_gpio_flags(np, "focaltech,irq-gpio",
+			0, &pdata->irq_gpio_flags);
+	if (pdata->irq_gpio < 0)
+		FTS_ERROR("Unable to get irq_gpio");
+
+	ret = of_property_read_u32(np, "focaltech,max-touch-number", &temp_val);
+	if (ret < 0) {
+		FTS_ERROR("Unable to get max-touch-number, please check dts");
+		pdata->max_touch_number = FTS_MAX_POINTS_SUPPORT;
+	} else {
+		if (temp_val < 2)
+			pdata->max_touch_number = 2; /* max_touch_number must >= 2 */
+		else if (temp_val > FTS_MAX_POINTS_SUPPORT)
+			pdata->max_touch_number = FTS_MAX_POINTS_SUPPORT;
+		else
+			pdata->max_touch_number = temp_val;
+	}
+
+	FTS_INFO("max touch number:%d, irq gpio:%d, reset gpio:%d",
+		pdata->max_touch_number, pdata->irq_gpio, pdata->reset_gpio);
+
+	ret = of_property_read_u32(np, "focaltech,ic-type", &temp_val);
+	if (ret < 0)
+		pdata->type = _FT3518;
+	else
+		pdata->type = temp_val;
+
+	FTS_FUNC_EXIT();
+	return 0;
+}
+
+#if defined(CONFIG_DRM)
+static void fts_resume_work(struct work_struct *work)
+{
+	struct fts_ts_data *ts_data = container_of(work, struct fts_ts_data,
+					resume_work);
+
+	fts_ts_resume(ts_data->dev);
+}
+
+static void fts_ts_panel_notifier_callback(enum panel_event_notifier_tag tag,
+		 struct panel_event_notification *notification, void *client_data)
+{
+	struct fts_ts_data *ts_data = client_data;
+
+	if (!notification) {
+		pr_err("Invalid notification\n");
+		return;
+	}
+
+	FTS_DEBUG("Notification type:%d, early_trigger:%d",
+			notification->notif_type,
+			notification->notif_data.early_trigger);
+	switch (notification->notif_type) {
+	case DRM_PANEL_EVENT_UNBLANK:
+		if (notification->notif_data.early_trigger)
+			FTS_DEBUG("resume notification pre commit\n");
+		else
+			queue_work(fts_data->ts_workqueue, &fts_data->resume_work);
+		break;
+	case DRM_PANEL_EVENT_BLANK:
+		if (notification->notif_data.early_trigger) {
+			cancel_work_sync(&fts_data->resume_work);
+			fts_ts_suspend(ts_data->dev);
+		} else {
+			FTS_DEBUG("suspend notification post commit\n");
+		}
+		break;
+	case DRM_PANEL_EVENT_BLANK_LP:
+		FTS_DEBUG("received lp event\n");
+		break;
+	case DRM_PANEL_EVENT_FPS_CHANGE:
+		FTS_DEBUG("shashank:Received fps change old fps:%d new fps:%d\n",
+				notification->notif_data.old_fps,
+				notification->notif_data.new_fps);
+		break;
+	default:
+		FTS_DEBUG("notification serviced :%d\n",
+				notification->notif_type);
+		break;
+	}
+}
+
+#elif defined(CONFIG_FB)
+static void fts_resume_work(struct work_struct *work)
+{
+	struct fts_ts_data *ts_data = container_of(work, struct fts_ts_data,
+					resume_work);
+
+	fts_ts_resume(ts_data->dev);
+}
+
+static int fb_notifier_callback(struct notifier_block *self,
+				unsigned long event, void *data)
+{
+	struct fb_event *evdata = data;
+	int *blank = NULL;
+	struct fts_ts_data *ts_data = container_of(self, struct fts_ts_data,
+					fb_notif);
+
+	if (!(event == FB_EARLY_EVENT_BLANK || event == FB_EVENT_BLANK)) {
+		FTS_INFO("event(%lu) do not need process\n", event);
+		return 0;
+	}
+
+	blank = evdata->data;
+	FTS_INFO("FB event:%lu,blank:%d", event, *blank);
+	switch (*blank) {
+	case FB_BLANK_UNBLANK:
+		if (FB_EARLY_EVENT_BLANK == event) {
+			FTS_INFO("resume: event = %lu, not care\n", event);
+		} else if (FB_EVENT_BLANK == event) {
+			queue_work(fts_data->ts_workqueue, &fts_data->resume_work);
+		}
+		break;
+
+	case FB_BLANK_POWERDOWN:
+		if (FB_EARLY_EVENT_BLANK == event) {
+			cancel_work_sync(&fts_data->resume_work);
+			fts_ts_suspend(ts_data->dev);
+		} else if (FB_EVENT_BLANK == event) {
+			FTS_INFO("suspend: event = %lu, not care\n", event);
+		}
+		break;
+
+	default:
+		FTS_INFO("FB BLANK(%d) do not need process\n", *blank);
+		break;
+	}
+
+	return 0;
+}
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
+static void fts_ts_early_suspend(struct early_suspend *handler)
+{
+	struct fts_ts_data *ts_data = container_of(handler, struct fts_ts_data,
+					early_suspend);
+
+	fts_ts_suspend(ts_data->dev);
+}
+
+static void fts_ts_late_resume(struct early_suspend *handler)
+{
+	struct fts_ts_data *ts_data = container_of(handler, struct fts_ts_data,
+					early_suspend);
+
+	fts_ts_resume(ts_data->dev);
+}
+#endif
+
+static int fts_ts_probe_delayed(struct fts_ts_data *fts_data)
+{
+	int ret = 0;
+
+/* Avoid setting up hardware for TVM during probe */
+#ifdef CONFIG_FTS_TRUSTED_TOUCH
+#ifdef CONFIG_ARCH_QTI_VM
+	if (!atomic_read(&fts_data->delayed_vm_probe_pending)) {
+		atomic_set(&fts_data->delayed_vm_probe_pending, 1);
+		return 0;
+	}
+	goto tvm_setup;
+#endif
+#endif
+	ret = fts_gpio_configure(fts_data);
+	if (ret) {
+		FTS_ERROR("configure the gpios fail");
+		goto err_gpio_config;
+	}
+
+#if FTS_POWER_SOURCE_CUST_EN
+	ret = fts_power_source_init(fts_data);
+	if (ret) {
+		FTS_ERROR("fail to get power(regulator)");
+		goto err_power_init;
+	}
+#endif
+	fts_reset_proc(200);
+
+	ret = fts_get_ic_information(fts_data);
+	if (ret) {
+		FTS_ERROR("not focal IC, unregister driver");
+		goto err_irq_req;
+	}
+
+#ifdef CONFIG_ARCH_QTI_VM
+tvm_setup:
+#endif
+	ret = fts_irq_registration(fts_data);
+	if (ret) {
+		FTS_ERROR("request irq failed");
+#ifdef CONFIG_ARCH_QTI_VM
+		return ret;
+#endif
+		goto err_irq_req;
+	}
+
+#ifdef CONFIG_ARCH_QTI_VM
+	return ret;
+#endif
+
+	ret = fts_fwupg_init(fts_data);
+	if (ret)
+		FTS_ERROR("init fw upgrade fail");
+
+	return 0;
+
+err_irq_req:
+	if (gpio_is_valid(fts_data->pdata->reset_gpio))
+		gpio_free(fts_data->pdata->reset_gpio);
+	if (gpio_is_valid(fts_data->pdata->irq_gpio))
+		gpio_free(fts_data->pdata->irq_gpio);
+#if FTS_POWER_SOURCE_CUST_EN
+err_power_init:
+	fts_power_source_exit(fts_data);
+#endif
+err_gpio_config:
+	return ret;
+}
+
+static int fts_ts_probe_entry(struct fts_ts_data *ts_data)
+{
+	int ret = 0;
+	int pdata_size = sizeof(struct fts_ts_platform_data);
+
+	FTS_FUNC_ENTER();
+	FTS_INFO("%s", FTS_DRIVER_VERSION);
+	ts_data->pdata = kzalloc(pdata_size, GFP_KERNEL);
+	if (!ts_data->pdata) {
+		FTS_ERROR("allocate memory for platform_data fail");
+		return -ENOMEM;
+	}
+
+	if (ts_data->dev->of_node) {
+		ret = fts_parse_dt(ts_data->dev, ts_data->pdata);
+		if (ret)
+			FTS_ERROR("device-tree parse fail");
+	} else {
+		if (ts_data->dev->platform_data) {
+			memcpy(ts_data->pdata, ts_data->dev->platform_data, pdata_size);
+		} else {
+			FTS_ERROR("platform_data is null");
+			return -ENODEV;
+		}
+	}
+
+	ts_data->ts_workqueue = create_singlethread_workqueue("fts_wq");
+	if (!ts_data->ts_workqueue) {
+		FTS_ERROR("create fts workqueue fail");
+	}
+
+	spin_lock_init(&ts_data->irq_lock);
+	mutex_init(&ts_data->report_mutex);
+	mutex_init(&ts_data->bus_lock);
+	mutex_init(&ts_data->transition_lock);
+
+	/* Init communication interface */
+	ret = fts_bus_init(ts_data);
+	if (ret) {
+		FTS_ERROR("bus initialize fail");
+		goto err_bus_init;
+	}
+
+	ret = fts_input_init(ts_data);
+	if (ret) {
+		FTS_ERROR("input initialize fail");
+		goto err_input_init;
+	}
+
+	ret = fts_report_buffer_init(ts_data);
+	if (ret) {
+		FTS_ERROR("report buffer init fail");
+		goto err_report_buffer;
+	}
+
+	ret = fts_create_apk_debug_channel(ts_data);
+	if (ret) {
+		FTS_ERROR("create apk debug node fail");
+	}
+
+	ret = fts_create_sysfs(ts_data);
+	if (ret) {
+		FTS_ERROR("create sysfs node fail");
+	}
+
+#if FTS_POINT_REPORT_CHECK_EN
+	ret = fts_point_report_check_init(ts_data);
+	if (ret) {
+		FTS_ERROR("init point report check fail");
+	}
+#endif
+
+	ret = fts_ex_mode_init(ts_data);
+	if (ret) {
+		FTS_ERROR("init glove/cover/charger fail");
+	}
+
+	ret = fts_gesture_init(ts_data);
+	if (ret) {
+		FTS_ERROR("init gesture fail");
+	}
+
+
+#if FTS_ESDCHECK_EN
+	ret = fts_esdcheck_init(ts_data);
+	if (ret) {
+		FTS_ERROR("init esd check fail");
+	}
+#endif
+
+#ifdef CONFIG_FTS_TRUSTED_TOUCH
+	fts_ts_trusted_touch_init(ts_data);
+	mutex_init(&(ts_data->fts_clk_io_ctrl_mutex));
+#endif
+
+#ifndef CONFIG_ARCH_QTI_VM
+	if (ts_data->pdata->type == _FT8726) {
+		atomic_set(&ts_data->delayed_vm_probe_pending, 1);
+		ts_data->suspended = true;
+	} else {
+		ret = fts_ts_probe_delayed(ts_data);
+		if (ret) {
+			FTS_ERROR("Failed to enable resources\n");
+			goto err_probe_delayed;
+		}
+	}
+#else
+	ret = fts_ts_probe_delayed(ts_data);
+	if (ret) {
+		FTS_ERROR("Failed to enable resources\n");
+		goto err_probe_delayed;
+	}
+#endif
+
+#if defined(CONFIG_DRM)
+	if (ts_data->ts_workqueue)
+		INIT_WORK(&ts_data->resume_work, fts_resume_work);
+
+#ifdef CONFIG_FTS_TRUSTED_TOUCH
+	if (!strcmp(fts_data->touch_environment, "pvm"))
+#else
+	if (active_panel)
+#endif
+		fts_ts_register_for_panel_events(ts_data->dev->of_node, ts_data);
+#elif defined(CONFIG_FB)
+	if (ts_data->ts_workqueue) {
+		INIT_WORK(&ts_data->resume_work, fts_resume_work);
+	}
+	ts_data->fb_notif.notifier_call = fb_notifier_callback;
+	ret = fb_register_client(&ts_data->fb_notif);
+	if (ret) {
+		FTS_ERROR("[FB]Unable to register fb_notifier: %d", ret);
+	}
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
+	ts_data->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + FTS_SUSPEND_LEVEL;
+	ts_data->early_suspend.suspend = fts_ts_early_suspend;
+	ts_data->early_suspend.resume = fts_ts_late_resume;
+	register_early_suspend(&ts_data->early_suspend);
+#endif
+
+	FTS_FUNC_EXIT();
+	return 0;
+
+err_probe_delayed:
+	kfree_safe(ts_data->point_buf);
+	kfree_safe(ts_data->events);
+err_report_buffer:
+	input_unregister_device(ts_data->input_dev);
+err_input_init:
+	if (ts_data->ts_workqueue)
+		destroy_workqueue(ts_data->ts_workqueue);
+err_bus_init:
+	kfree_safe(ts_data->bus_tx_buf);
+	kfree_safe(ts_data->bus_rx_buf);
+	kfree_safe(ts_data->pdata);
+
+	FTS_FUNC_EXIT();
+	return ret;
+}
+
+static int fts_ts_remove_entry(struct fts_ts_data *ts_data)
+{
+	FTS_FUNC_ENTER();
+
+#if FTS_POINT_REPORT_CHECK_EN
+	fts_point_report_check_exit(ts_data);
+#endif
+
+	fts_release_apk_debug_channel(ts_data);
+	fts_remove_sysfs(ts_data);
+	fts_ex_mode_exit(ts_data);
+
+	fts_fwupg_exit(ts_data);
+
+
+#if FTS_ESDCHECK_EN
+	fts_esdcheck_exit(ts_data);
+#endif
+
+	fts_gesture_exit(ts_data);
+	fts_bus_exit(ts_data);
+
+	free_irq(ts_data->irq, ts_data);
+	input_unregister_device(ts_data->input_dev);
+
+	if (ts_data->ts_workqueue)
+		destroy_workqueue(ts_data->ts_workqueue);
+
+#if defined(CONFIG_DRM)
+	if (active_panel && ts_data->notifier_cookie)
+		panel_event_notifier_unregister(ts_data->notifier_cookie);
+
+#elif defined(CONFIG_FB)
+	if (fb_unregister_client(&ts_data->fb_notif))
+		FTS_ERROR("Error occurred while unregistering fb_notifier.");
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
+	unregister_early_suspend(&ts_data->early_suspend);
+#endif
+
+	if (gpio_is_valid(ts_data->pdata->reset_gpio))
+		gpio_free(ts_data->pdata->reset_gpio);
+
+	if (gpio_is_valid(ts_data->pdata->irq_gpio))
+		gpio_free(ts_data->pdata->irq_gpio);
+
+#if FTS_POWER_SOURCE_CUST_EN
+	fts_power_source_exit(ts_data);
+#endif
+
+	kfree_safe(ts_data->point_buf);
+	kfree_safe(ts_data->events);
+
+	kfree_safe(ts_data->pdata);
+	kfree_safe(ts_data);
+
+	FTS_FUNC_EXIT();
+
+	return 0;
+}
+
+static int fts_ts_suspend(struct device *dev)
+{
+	int ret = 0;
+	struct fts_ts_data *ts_data = fts_data;
+
+	FTS_FUNC_ENTER();
+	if (ts_data->suspended) {
+		FTS_INFO("Already in suspend state");
+		return 0;
+	}
+
+	if (ts_data->fw_loading) {
+		FTS_INFO("fw upgrade in process, can't suspend");
+		return 0;
+	}
+
+#ifdef CONFIG_FTS_TRUSTED_TOUCH
+	if (atomic_read(&fts_data->trusted_touch_transition)
+			|| atomic_read(&fts_data->trusted_touch_enabled))
+		wait_for_completion_interruptible(
+			&fts_data->trusted_touch_powerdown);
+#endif
+
+	mutex_lock(&ts_data->transition_lock);
+
+#if FTS_ESDCHECK_EN
+	fts_esdcheck_suspend();
+#endif
+
+	if (ts_data->gesture_mode) {
+		fts_gesture_suspend(ts_data);
+	} else {
+		fts_irq_disable();
+
+		FTS_INFO("make TP enter into sleep mode");
+		ret = fts_write_reg(FTS_REG_POWER_MODE, FTS_REG_POWER_MODE_SLEEP);
+		if (ret < 0)
+			FTS_ERROR("set TP to sleep mode fail, ret=%d", ret);
+
+		if (!ts_data->ic_info.is_incell) {
+#if FTS_POWER_SOURCE_CUST_EN
+			ret = fts_power_source_suspend(ts_data);
+			if (ret < 0) {
+				FTS_ERROR("power enter suspend fail");
+			}
+#endif
+		} else {
+#if FTS_PINCTRL_EN
+			fts_pinctrl_select_suspend(ts_data);
+#endif
+			gpio_direction_output(ts_data->pdata->reset_gpio, 0);
+		}
+	}
+
+	fts_release_all_finger();
+	ts_data->suspended = true;
+	mutex_unlock(&ts_data->transition_lock);
+	FTS_FUNC_EXIT();
+	return 0;
+}
+
+static int fts_ts_resume(struct device *dev)
+{
+	struct fts_ts_data *ts_data = fts_data;
+	int ret = 0;
+
+	FTS_FUNC_ENTER();
+	if (!ts_data->suspended) {
+		FTS_DEBUG("Already in awake state");
+		return 0;
+	}
+
+#ifdef CONFIG_FTS_TRUSTED_TOUCH
+
+	if (atomic_read(&ts_data->trusted_touch_transition))
+		wait_for_completion_interruptible(
+			&ts_data->trusted_touch_powerdown);
+#endif
+	if (ts_data->pdata->type == _FT8726 &&
+			atomic_read(&ts_data->delayed_vm_probe_pending)) {
+		ret = fts_ts_probe_delayed(ts_data);
+		if (ret) {
+			FTS_ERROR("Failed to enable resources\n");
+			return ret;
+		}
+		ts_data->suspended = false;
+		atomic_set(&ts_data->delayed_vm_probe_pending, 0);
+		return ret;
+	}
+
+	mutex_lock(&ts_data->transition_lock);
+
+	fts_release_all_finger();
+
+	if (!ts_data->ic_info.is_incell) {
+#if FTS_POWER_SOURCE_CUST_EN
+		fts_power_source_resume(ts_data);
+#endif
+	} else {
+#if FTS_PINCTRL_EN
+		fts_pinctrl_select_normal(ts_data);
+#endif
+	}
+
+	fts_reset_proc(200);
+
+	fts_wait_tp_to_valid();
+	fts_ex_mode_recovery(ts_data);
+
+#if FTS_ESDCHECK_EN
+	fts_esdcheck_resume();
+#endif
+
+	if (ts_data->gesture_mode) {
+		fts_gesture_resume(ts_data);
+	} else {
+		fts_irq_enable();
+	}
+
+	ts_data->suspended = false;
+	mutex_unlock(&ts_data->transition_lock);
+	FTS_FUNC_EXIT();
+	return 0;
+}
+
+/*****************************************************************************
+* TP Driver
+*****************************************************************************/
+static int fts_ts_check_dt(struct device_node *np)
+{
+	int i;
+	int count;
+	struct device_node *node;
+	struct drm_panel *panel;
+
+	count = of_count_phandle_with_args(np, "panel", NULL);
+	if (count <= 0)
+		return 0;
+
+	for (i = 0; i < count; i++) {
+		node = of_parse_phandle(np, "panel", i);
+		panel = of_drm_find_panel(node);
+		of_node_put(node);
+		if (!IS_ERR(panel)) {
+			active_panel = panel;
+			return 0;
+		}
+	}
+
+	return PTR_ERR(panel);
+}
+
+static int fts_ts_check_default_tp(struct device_node *dt, const char *prop)
+{
+	const char **active_tp = NULL;
+	int count, tmp, score = 0;
+	const char *active;
+	int ret, i;
+
+	count = of_property_count_strings(dt->parent, prop);
+	if (count <= 0 || count > 3)
+		return -ENODEV;
+
+	active_tp = kcalloc(count, sizeof(char *),  GFP_KERNEL);
+	if (!active_tp) {
+		FTS_ERROR("FTS alloc failed\n");
+		return -ENOMEM;
+	}
+
+	ret = of_property_read_string_array(dt->parent, prop,
+			active_tp, count);
+	if (ret < 0) {
+		FTS_ERROR("fail to read %s %d\n", prop, ret);
+		ret = -ENODEV;
+		goto out;
+	}
+
+	for (i = 0; i < count; i++) {
+		active = active_tp[i];
+		if (active != NULL) {
+			tmp = of_device_is_compatible(dt, active);
+			if (tmp > 0)
+				score++;
+		}
+	}
+
+	if (score <= 0) {
+		FTS_INFO("not match this driver\n");
+		ret = -ENODEV;
+		goto out;
+	}
+	ret = 0;
+out:
+	kfree(active_tp);
+	return ret;
+}
+
+static int fts_ts_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+	int ret = 0;
+	struct fts_ts_data *ts_data = NULL;
+	struct device_node *dp = client->dev.of_node;
+
+	FTS_INFO("Touch Screen(I2C BUS) driver prboe...");
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+		FTS_ERROR("I2C not supported");
+		return -ENODEV;
+	}
+
+	ret = fts_ts_check_dt(dp);
+	if (ret == -EPROBE_DEFER)
+		return ret;
+
+	if (ret) {
+		if (!fts_ts_check_default_tp(dp, "qcom,i2c-touch-active"))
+			ret = -EPROBE_DEFER;
+		else
+			ret = -ENODEV;
+
+		return ret;
+	}
+
+	/* malloc memory for global struct variable */
+	ts_data = (struct fts_ts_data *)kzalloc(sizeof(*ts_data), GFP_KERNEL);
+	if (!ts_data) {
+		FTS_ERROR("allocate memory for fts_data fail");
+		return -ENOMEM;
+	}
+
+	fts_data = ts_data;
+	ts_data->client = client;
+	ts_data->dev = &client->dev;
+	ts_data->log_level = 1;
+	ts_data->fw_is_running = 0;
+	ts_data->bus_type = BUS_TYPE_I2C;
+	i2c_set_clientdata(client, ts_data);
+
+	ret = fts_ts_probe_entry(ts_data);
+	if (ret) {
+		FTS_ERROR("Touch Screen(I2C BUS) driver probe fail");
+		kfree_safe(ts_data);
+		return ret;
+	}
+
+	FTS_INFO("Touch Screen(I2C BUS) driver prboe successfully");
+	return 0;
+}
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0))
+static void fts_ts_i2c_remove(struct i2c_client *client)
+#else
+static int fts_ts_i2c_remove(struct i2c_client *client)
+#endif
+{
+	int rc = 0;
+
+	rc = fts_ts_remove_entry(i2c_get_clientdata(client));
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0))
+	return rc;
+#endif
+}
+
+static const struct i2c_device_id fts_ts_i2c_id[] = {
+	{FTS_DRIVER_NAME, 0},
+	{},
+};
+static const struct of_device_id fts_dt_match[] = {
+	{.compatible = "focaltech,fts_ts", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, fts_dt_match);
+
+static struct i2c_driver fts_ts_i2c_driver = {
+	.probe = fts_ts_i2c_probe,
+	.remove = fts_ts_i2c_remove,
+	.driver = {
+		.name = FTS_DRIVER_NAME,
+		.owner = THIS_MODULE,
+		.of_match_table = of_match_ptr(fts_dt_match),
+	},
+	.id_table = fts_ts_i2c_id,
+};
+
+static int __init fts_ts_i2c_init(void)
+{
+	int ret = 0;
+
+	FTS_FUNC_ENTER();
+	ret = i2c_add_driver(&fts_ts_i2c_driver);
+	if (ret != 0)
+		FTS_ERROR("Focaltech touch screen driver init failed!");
+
+	FTS_FUNC_EXIT();
+	return ret;
+}
+
+static void __exit fts_ts_i2c_exit(void)
+{
+	i2c_del_driver(&fts_ts_i2c_driver);
+}
+
+static int fts_ts_spi_probe(struct spi_device *spi)
+{
+	int ret = 0;
+	struct fts_ts_data *ts_data = NULL;
+	struct device_node *dp = spi->dev.of_node;
+
+	FTS_INFO("Touch Screen(SPI BUS) driver prboe...");
+
+	ret = fts_ts_check_dt(dp);
+	if (ret == -EPROBE_DEFER)
+		return ret;
+
+	if (ret) {
+		if (!fts_ts_check_default_tp(dp, "qcom,spi-touch-active"))
+			ret = -EPROBE_DEFER;
+		else
+			ret = -ENODEV;
+
+		return ret;
+	}
+
+	spi->mode = SPI_MODE_0;
+	spi->bits_per_word = 8;
+	ret = spi_setup(spi);
+	if (ret) {
+		FTS_ERROR("spi setup fail");
+		return ret;
+	}
+
+	/* malloc memory for global struct variable */
+	ts_data = kzalloc(sizeof(*ts_data), GFP_KERNEL);
+	if (!ts_data) {
+		FTS_ERROR("allocate memory for fts_data fail");
+		return -ENOMEM;
+	}
+
+	fts_data = ts_data;
+	ts_data->spi = spi;
+	ts_data->dev = &spi->dev;
+	ts_data->log_level = 1;
+
+	ts_data->bus_type = BUS_TYPE_SPI_V2;
+	spi_set_drvdata(spi, ts_data);
+
+	ret = fts_ts_probe_entry(ts_data);
+	if (ret) {
+		FTS_ERROR("Touch Screen(SPI BUS) driver probe fail");
+		kfree_safe(ts_data);
+		return ret;
+	}
+
+	FTS_INFO("Touch Screen(SPI BUS) driver prboe successfully");
+	return 0;
+}
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0))
+static void fts_ts_spi_remove(struct spi_device *spi)
+#else
+static int fts_ts_spi_remove(struct spi_device *spi)
+#endif
+{
+	int rc = 0;
+
+	rc = fts_ts_remove_entry(spi_get_drvdata(spi));
+
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0))
+	return rc;
+#endif
+}
+
+static const struct spi_device_id fts_ts_spi_id[] = {
+	{FTS_DRIVER_NAME, 0},
+	{},
+};
+
+static struct spi_driver fts_ts_spi_driver = {
+	.probe = fts_ts_spi_probe,
+	.remove = fts_ts_spi_remove,
+	.driver = {
+		.name = FTS_DRIVER_NAME,
+		.owner = THIS_MODULE,
+#if defined(CONFIG_PM) && FTS_PATCH_COMERR_PM
+		.pm = &fts_dev_pm_ops,
+#endif
+		.of_match_table = of_match_ptr(fts_dt_match),
+	},
+	.id_table = fts_ts_spi_id,
+};
+
+static int __init fts_ts_spi_init(void)
+{
+	int ret = 0;
+
+	FTS_FUNC_ENTER();
+	ret = spi_register_driver(&fts_ts_spi_driver);
+	if (ret != 0)
+		FTS_ERROR("Focaltech touch screen driver init failed!");
+
+	FTS_FUNC_EXIT();
+	return ret;
+}
+
+static void __exit fts_ts_spi_exit(void)
+{
+	spi_unregister_driver(&fts_ts_spi_driver);
+}
+
+static int __init fts_ts_init(void)
+{
+	int ret = 0;
+
+	ret = fts_ts_i2c_init();
+	if (ret)
+		FTS_ERROR("Focaltech I2C driver init failed!");
+
+	ret = fts_ts_spi_init();
+	if (ret)
+		FTS_ERROR("Focaltech SPI driver init failed!");
+
+	return ret;
+}
+
+static void __exit fts_ts_exit(void)
+{
+	fts_ts_i2c_exit();
+	fts_ts_spi_exit();
+}
+
+#ifdef CONFIG_ARCH_QTI_VM
+module_init(fts_ts_init);
+#else
+late_initcall(fts_ts_init);
+#endif
+module_exit(fts_ts_exit);
+
+MODULE_AUTHOR("FocalTech Driver Team");
+MODULE_DESCRIPTION("FocalTech Touchscreen Driver");
+MODULE_LICENSE("GPL v2");

+ 391 - 0
qcom/opensource/touch-drivers/focaltech_touch/focaltech_core.h

@@ -0,0 +1,391 @@
+/*
+ *
+ * FocalTech TouchScreen driver.
+ *
+ * Copyright (c) 2012-2019, Focaltech Ltd. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+/*****************************************************************************
+*
+* File Name: focaltech_core.h
+
+* Author: Focaltech Driver Team
+*
+* Created: 2016-08-08
+*
+* Abstract:
+*
+* Reference:
+*
+*****************************************************************************/
+
+#ifndef __LINUX_FOCALTECH_CORE_H__
+#define __LINUX_FOCALTECH_CORE_H__
+/*****************************************************************************
+* Included header files
+*****************************************************************************/
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/spi/spi.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
+#include <asm/uaccess.h>
+#include <linux/firmware.h>
+#include <linux/debugfs.h>
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+#include <linux/wait.h>
+#include <linux/time.h>
+#include <linux/jiffies.h>
+#include <linux/fs.h>
+#include <linux/proc_fs.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/kthread.h>
+#include <linux/dma-mapping.h>
+#if defined(CONFIG_FTS_TRUSTED_TOUCH)
+#include <linux/gunyah/gh_irq_lend.h>
+#include <linux/gunyah/gh_mem_notifier.h>
+#endif
+#include "focaltech_common.h"
+
+/*****************************************************************************
+* Private constant and macro definitions using #define
+*****************************************************************************/
+#define FTS_MAX_POINTS_SUPPORT              10 /* constant value, can't be changed */
+#define FTS_MAX_KEYS                        4
+#define FTS_KEY_DIM                         10
+#define FTS_ONE_TCH_LEN                     6
+#define FTS_TOUCH_DATA_LEN  (FTS_MAX_POINTS_SUPPORT * FTS_ONE_TCH_LEN + 3)
+
+#define FTS_GESTURE_POINTS_MAX              6
+#define FTS_GESTURE_DATA_LEN               (FTS_GESTURE_POINTS_MAX * 4 + 4)
+
+#define FTS_MAX_ID                          0x0A
+#define FTS_TOUCH_X_H_POS                   3
+#define FTS_TOUCH_X_L_POS                   4
+#define FTS_TOUCH_Y_H_POS                   5
+#define FTS_TOUCH_Y_L_POS                   6
+#define FTS_TOUCH_PRE_POS                   7
+#define FTS_TOUCH_AREA_POS                  8
+#define FTS_TOUCH_POINT_NUM                 2
+#define FTS_TOUCH_EVENT_POS                 3
+#define FTS_TOUCH_ID_POS                    5
+#define FTS_COORDS_ARR_SIZE                 4
+#define FTS_X_MIN_DISPLAY_DEFAULT           0
+#define FTS_Y_MIN_DISPLAY_DEFAULT           0
+#define FTS_X_MAX_DISPLAY_DEFAULT           720
+#define FTS_Y_MAX_DISPLAY_DEFAULT           1280
+
+#define FTS_TOUCH_DOWN                      0
+#define FTS_TOUCH_UP                        1
+#define FTS_TOUCH_CONTACT                   2
+#define EVENT_DOWN(flag)                    ((FTS_TOUCH_DOWN == flag) || (FTS_TOUCH_CONTACT == flag))
+#define EVENT_UP(flag)                      (FTS_TOUCH_UP == flag)
+#define EVENT_NO_DOWN(data)                 (!data->point_num)
+
+#define FTX_MAX_COMPATIBLE_TYPE             4
+#define FTX_MAX_COMMMAND_LENGTH             16
+
+
+/*****************************************************************************
+*  Alternative mode (When something goes wrong, the modules may be able to solve the problem.)
+*****************************************************************************/
+/*
+ * For commnication error in PM(deep sleep) state
+ */
+#define FTS_PATCH_COMERR_PM                     0
+#define FTS_TIMEOUT_COMERR_PM                   700
+
+
+/*****************************************************************************
+* Private enumerations, structures and unions using typedef
+*****************************************************************************/
+struct ftxxxx_proc {
+	struct proc_dir_entry *proc_entry;
+	u8 opmode;
+	u8 cmd_len;
+	u8 cmd[FTX_MAX_COMMMAND_LENGTH];
+};
+
+struct fts_ts_platform_data {
+	u32 type;
+	u32 irq_gpio;
+	u32 irq_gpio_flags;
+	u32 reset_gpio;
+	u32 reset_gpio_flags;
+	bool have_key;
+	u32 key_number;
+	u32 keys[FTS_MAX_KEYS];
+	u32 key_y_coords[FTS_MAX_KEYS];
+	u32 key_x_coords[FTS_MAX_KEYS];
+	u32 x_max;
+	u32 y_max;
+	u32 x_min;
+	u32 y_min;
+	u32 max_touch_number;
+};
+
+struct ts_event {
+	int x;      /*x coordinate */
+	int y;      /*y coordinate */
+	int p;      /* pressure */
+	int flag;   /* touch event flag: 0 -- down; 1-- up; 2 -- contact */
+	int id;     /*touch ID */
+	int area;
+};
+
+enum trusted_touch_mode_config {
+	TRUSTED_TOUCH_VM_MODE,
+	TRUSTED_TOUCH_MODE_NONE
+};
+
+enum trusted_touch_pvm_states {
+	TRUSTED_TOUCH_PVM_INIT,
+	PVM_I2C_RESOURCE_ACQUIRED,
+	PVM_INTERRUPT_DISABLED,
+	PVM_IOMEM_LENT,
+	PVM_IOMEM_LENT_NOTIFIED,
+	PVM_IRQ_LENT,
+	PVM_IRQ_LENT_NOTIFIED,
+	PVM_IOMEM_RELEASE_NOTIFIED,
+	PVM_IRQ_RELEASE_NOTIFIED,
+	PVM_ALL_RESOURCES_RELEASE_NOTIFIED,
+	PVM_IRQ_RECLAIMED,
+	PVM_IOMEM_RECLAIMED,
+	PVM_INTERRUPT_ENABLED,
+	PVM_I2C_RESOURCE_RELEASED,
+	TRUSTED_TOUCH_PVM_STATE_MAX
+};
+
+enum trusted_touch_tvm_states {
+	TRUSTED_TOUCH_TVM_INIT,
+	TVM_IOMEM_LENT_NOTIFIED,
+	TVM_IRQ_LENT_NOTIFIED,
+	TVM_ALL_RESOURCES_LENT_NOTIFIED,
+	TVM_IOMEM_ACCEPTED,
+	TVM_I2C_SESSION_ACQUIRED,
+	TVM_IRQ_ACCEPTED,
+	TVM_INTERRUPT_ENABLED,
+	TVM_INTERRUPT_DISABLED,
+	TVM_IRQ_RELEASED,
+	TVM_I2C_SESSION_RELEASED,
+	TVM_IOMEM_RELEASED,
+	TRUSTED_TOUCH_TVM_STATE_MAX
+};
+
+#ifdef CONFIG_FTS_TRUSTED_TOUCH
+#define TRUSTED_TOUCH_MEM_LABEL 0x7
+
+#define TOUCH_RESET_GPIO_BASE 0xF114000
+#define TOUCH_RESET_GPIO_SIZE 0x1000
+#define TOUCH_RESET_GPIO_OFFSET 0x4
+#define TOUCH_INTR_GPIO_BASE 0xF115000
+#define TOUCH_INTR_GPIO_SIZE 0x1000
+#define TOUCH_INTR_GPIO_OFFSET 0x8
+
+#define TRUSTED_TOUCH_EVENT_LEND_FAILURE -1
+#define TRUSTED_TOUCH_EVENT_LEND_NOTIFICATION_FAILURE -2
+#define TRUSTED_TOUCH_EVENT_ACCEPT_FAILURE -3
+#define	TRUSTED_TOUCH_EVENT_FUNCTIONAL_FAILURE -4
+#define	TRUSTED_TOUCH_EVENT_RELEASE_FAILURE -5
+#define	TRUSTED_TOUCH_EVENT_RECLAIM_FAILURE -6
+#define	TRUSTED_TOUCH_EVENT_I2C_FAILURE -7
+#define	TRUSTED_TOUCH_EVENT_NOTIFICATIONS_PENDING 5
+
+struct trusted_touch_vm_info {
+	enum gh_irq_label irq_label;
+	enum gh_mem_notifier_tag mem_tag;
+	enum gh_vm_names vm_name;
+	const char *trusted_touch_type;
+	u32 hw_irq;
+	gh_memparcel_handle_t vm_mem_handle;
+	u32 *iomem_bases;
+	u32 *iomem_sizes;
+	u32 iomem_list_size;
+	void *mem_cookie;
+	atomic_t vm_state;
+};
+#endif
+
+struct fts_ts_data {
+	struct i2c_client *client;
+	struct spi_device *spi;
+	struct device *dev;
+	struct input_dev *input_dev;
+	struct fts_ts_platform_data *pdata;
+	struct ts_ic_info ic_info;
+	struct workqueue_struct *ts_workqueue;
+	struct work_struct fwupg_work;
+	struct delayed_work esdcheck_work;
+	struct delayed_work prc_work;
+	struct work_struct resume_work;
+	struct work_struct suspend_work;
+	struct ftxxxx_proc proc;
+	spinlock_t irq_lock;
+	struct mutex report_mutex;
+	struct mutex bus_lock;
+	struct mutex transition_lock;
+	int irq;
+	int log_level;
+	int fw_is_running;      /* confirm fw is running when using spi:default 0 */
+	int dummy_byte;
+#if defined(CONFIG_PM) && FTS_PATCH_COMERR_PM
+	struct completion pm_completion;
+	bool pm_suspend;
+#endif
+	bool suspended;
+	bool fw_loading;
+	bool irq_disabled;
+	bool power_disabled;
+	bool glove_mode;
+	bool cover_mode;
+	bool charger_mode;
+	bool gesture_mode;      /* gesture enable or disable, default: disable */
+	int report_rate;
+	/* multi-touch */
+	struct ts_event *events;
+	u8 *bus_tx_buf;
+	u8 *bus_rx_buf;
+	int bus_type;
+	u8 *point_buf;
+	void *notifier_cookie;
+	int pnt_buf_size;
+	int touchs;
+	int key_state;
+	int touch_point;
+	int point_num;
+	struct regulator *vdd;
+	struct regulator *vcc_i2c;
+#if FTS_PINCTRL_EN
+	struct pinctrl *pinctrl;
+	struct pinctrl_state *pins_active;
+	struct pinctrl_state *pins_suspend;
+	struct pinctrl_state *pins_release;
+#endif
+#if defined(CONFIG_FB) || defined(CONFIG_DRM)
+	struct notifier_block fb_notif;
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
+	struct early_suspend early_suspend;
+#endif
+
+#ifdef CONFIG_FTS_TRUSTED_TOUCH
+	struct trusted_touch_vm_info *vm_info;
+	struct mutex fts_clk_io_ctrl_mutex;
+	const char *touch_environment;
+	struct completion trusted_touch_powerdown;
+	struct clk *core_clk;
+	struct clk *iface_clk;
+	atomic_t trusted_touch_initialized;
+	atomic_t trusted_touch_enabled;
+	atomic_t trusted_touch_transition;
+	atomic_t trusted_touch_event;
+	atomic_t trusted_touch_abort_status;
+	atomic_t trusted_touch_mode;
+#endif
+	atomic_t delayed_vm_probe_pending;
+};
+
+enum _FTS_BUS_TYPE {
+	BUS_TYPE_NONE,
+	BUS_TYPE_I2C,
+	BUS_TYPE_SPI,
+	BUS_TYPE_SPI_V2,
+};
+
+/*****************************************************************************
+* Global variable or extern global variabls/functions
+*****************************************************************************/
+extern struct fts_ts_data *fts_data;
+
+/* communication interface */
+int fts_read(u8 *cmd, u32 cmdlen, u8 *data, u32 datalen);
+int fts_read_reg(u8 addr, u8 *value);
+int fts_write(u8 *writebuf, u32 writelen);
+int fts_write_reg(u8 addr, u8 value);
+void fts_hid2std(void);
+int fts_bus_init(struct fts_ts_data *ts_data);
+int fts_bus_exit(struct fts_ts_data *ts_data);
+
+/* Gesture functions */
+int fts_gesture_init(struct fts_ts_data *ts_data);
+int fts_gesture_exit(struct fts_ts_data *ts_data);
+void fts_gesture_recovery(struct fts_ts_data *ts_data);
+int fts_gesture_readdata(struct fts_ts_data *ts_data, u8 *data);
+int fts_gesture_suspend(struct fts_ts_data *ts_data);
+int fts_gesture_resume(struct fts_ts_data *ts_data);
+
+/* Apk and functions */
+int fts_create_apk_debug_channel(struct fts_ts_data *);
+void fts_release_apk_debug_channel(struct fts_ts_data *);
+
+/* ADB functions */
+int fts_create_sysfs(struct fts_ts_data *ts_data);
+int fts_remove_sysfs(struct fts_ts_data *ts_data);
+
+/* ESD */
+#if FTS_ESDCHECK_EN
+int fts_esdcheck_init(struct fts_ts_data *ts_data);
+int fts_esdcheck_exit(struct fts_ts_data *ts_data);
+int fts_esdcheck_switch(bool enable);
+int fts_esdcheck_proc_busy(bool proc_debug);
+int fts_esdcheck_set_intr(bool intr);
+int fts_esdcheck_suspend(void);
+int fts_esdcheck_resume(void);
+#endif
+
+/* Production test */
+#if FTS_TEST_EN
+int fts_test_init(struct fts_ts_data *ts_data);
+int fts_test_exit(struct fts_ts_data *ts_data);
+#endif
+
+/* Point Report Check*/
+#if FTS_POINT_REPORT_CHECK_EN
+int fts_point_report_check_init(struct fts_ts_data *ts_data);
+int fts_point_report_check_exit(struct fts_ts_data *ts_data);
+void fts_prc_queue_work(struct fts_ts_data *ts_data);
+#endif
+
+/* FW upgrade */
+int fts_fwupg_init(struct fts_ts_data *ts_data);
+int fts_fwupg_exit(struct fts_ts_data *ts_data);
+int fts_enter_test_environment(bool test_state);
+
+/* Other */
+int fts_reset_proc(int hdelayms);
+int fts_wait_tp_to_valid(void);
+void fts_release_all_finger(void);
+void fts_tp_state_recovery(struct fts_ts_data *ts_data);
+int fts_ex_mode_init(struct fts_ts_data *ts_data);
+int fts_ex_mode_exit(struct fts_ts_data *ts_data);
+int fts_ex_mode_recovery(struct fts_ts_data *ts_data);
+
+void fts_irq_disable(void);
+void fts_irq_enable(void);
+int fts_ts_handle_trusted_touch_pvm(struct fts_ts_data *ts_data, int value);
+int fts_ts_handle_trusted_touch_tvm(struct fts_ts_data *ts_data, int value);
+#ifdef CONFIG_FTS_TRUSTED_TOUCH
+#ifdef CONFIG_ARCH_QTI_VM
+void fts_ts_trusted_touch_tvm_i2c_failure_report(struct fts_ts_data *fts_data);
+#endif
+#endif
+#endif /* __LINUX_FOCALTECH_CORE_H__ */

+ 465 - 0
qcom/opensource/touch-drivers/focaltech_touch/focaltech_esdcheck.c

@@ -0,0 +1,465 @@
+/*
+ *
+ * FocalTech TouchScreen driver.
+ *
+ * Copyright (c) 2012-2019, FocalTech Systems, Ltd., all rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+/*****************************************************************************
+*
+* File Name: focaltech_esdcheck.c
+*
+*    Author: Focaltech Driver Team
+*
+*   Created: 2016-08-03
+*
+*  Abstract: ESD check function
+*
+*   Version: v1.0
+*
+* Revision History:
+*        v1.0:
+*            First release. By luougojin 2016-08-03
+*        v1.1: By luougojin 2017-02-15
+*            1. Add LCD_ESD_PATCH to control idc_esdcheck_lcderror
+*****************************************************************************/
+
+/*****************************************************************************
+* Included header files
+*****************************************************************************/
+#include "focaltech_core.h"
+
+#if FTS_ESDCHECK_EN
+/*****************************************************************************
+* Private constant and macro definitions using #define
+*****************************************************************************/
+#define ESDCHECK_WAIT_TIME              1000    /* ms */
+#define LCD_ESD_PATCH                   0
+#define ESDCHECK_INTRCNT_MAX            2
+
+/*****************************************************************************
+* Private enumerations, structures and unions using typedef
+*****************************************************************************/
+struct fts_esdcheck_st {
+	u8  mode                : 1;    /* 1- need check esd 0- no esd check */
+	u8  suspend             : 1;
+	u8  proc_debug          : 1;    /* apk or adb use */
+	u8  intr                : 1;    /* 1- Interrupt trigger */
+	u8  unused              : 4;
+	u8  intr_cnt;
+	u8  flow_work_hold_cnt;         /* Flow Work Cnt(reg0x91) keep a same value for x times. >=5 times is ESD, need reset */
+	u8  flow_work_cnt_last;         /* Save Flow Work Cnt(reg0x91) value */
+	u32 hardware_reset_cnt;
+	u32 nack_cnt;
+	u32 dataerror_cnt;
+};
+
+/*****************************************************************************
+* Static variables
+*****************************************************************************/
+static struct fts_esdcheck_st fts_esdcheck_data;
+
+/*****************************************************************************
+* Global variable or extern global variabls/functions
+*****************************************************************************/
+
+/*****************************************************************************
+* Static function prototypes
+*****************************************************************************/
+
+/*****************************************************************************
+* functions body
+*****************************************************************************/
+#if LCD_ESD_PATCH
+int lcd_need_reset;
+static int tp_need_recovery; /* LCD reset cause Tp reset */
+int idc_esdcheck_lcderror(struct fts_ts_data *ts_data)
+{
+	int ret = 0;
+	u8 val = 0;
+
+	FTS_DEBUG("check LCD ESD");
+	if ( (tp_need_recovery == 1) && (lcd_need_reset == 0) ) {
+		tp_need_recovery = 0;
+		/* LCD reset, need recover TP state */
+		fts_release_all_finger();
+		fts_tp_state_recovery(ts_data);
+	}
+
+	ret = fts_read_reg(FTS_REG_ESD_SATURATE, &val);
+	if ( ret < 0) {
+		FTS_ERROR("read reg0xED fail,ret:%d", ret);
+		return -EIO;
+	}
+
+	if (val == 0xAA) {
+		/*
+		* 1. Set flag lcd_need_reset = 1;
+		* 2. LCD driver need reset(recovery) LCD and set lcd_need_reset to 0
+		* 3. recover TP state
+		*/
+		FTS_INFO("LCD ESD, need execute LCD reset");
+		lcd_need_reset = 1;
+		tp_need_recovery = 1;
+	}
+
+	return 0;
+}
+#endif
+
+static int fts_esdcheck_tp_reset(struct fts_ts_data *ts_data)
+{
+	FTS_FUNC_ENTER();
+
+	fts_esdcheck_data.flow_work_hold_cnt = 0;
+	fts_esdcheck_data.hardware_reset_cnt++;
+
+	fts_reset_proc(200);
+	fts_release_all_finger();
+	fts_tp_state_recovery(ts_data);
+
+	FTS_FUNC_EXIT();
+	return 0;
+}
+
+static bool get_chip_id(struct fts_ts_data *ts_data)
+{
+	int ret = 0;
+	int i = 0;
+	u8 reg_value = 0;
+	u8 reg_addr = 0;
+	u8 chip_id = ts_data->ic_info.ids.chip_idh;
+
+	for (i = 0; i < 3; i++) {
+		reg_addr = FTS_REG_CHIP_ID;
+		ret = fts_read(&reg_addr, 1, &reg_value, 1);
+		if (ret < 0) {
+			FTS_ERROR("read chip id fail,ret:%d", ret);
+			fts_esdcheck_data.nack_cnt++;
+		} else {
+			if (reg_value == chip_id) {
+				break;
+			} else {
+				FTS_DEBUG("read chip_id:%x,retry:%d", reg_value, i);
+				fts_esdcheck_data.dataerror_cnt++;
+			}
+		}
+		msleep(10);
+	}
+
+	/* if can't get correct data in 3 times, then need hardware reset */
+	if (i >= 3) {
+		FTS_ERROR("read chip id 3 times fail, need execute TP reset");
+		return true;
+	}
+
+	return false;
+}
+
+/*****************************************************************************
+*  Name: get_flow_cnt
+*  Brief: Read flow cnt(0x91)
+*  Input:
+*  Output:
+*  Return:  1(true) - Reg 0x91(flow cnt) abnormal: hold a value for 5 times
+*           0(false) - Reg 0x91(flow cnt) normal
+*****************************************************************************/
+static bool get_flow_cnt(struct fts_ts_data *ts_data)
+{
+	int ret = 0;
+	u8  reg_value = 0;
+	u8  reg_addr = 0;
+
+	reg_addr = FTS_REG_FLOW_WORK_CNT;
+	ret = fts_read(&reg_addr, 1, &reg_value, 1);
+	if (ret < 0) {
+		FTS_ERROR("read reg0x91 fail,ret:%d", ret);
+		fts_esdcheck_data.nack_cnt++;
+	} else {
+		if ( reg_value == fts_esdcheck_data.flow_work_cnt_last ) {
+			FTS_DEBUG("reg0x91,val:%x,last:%x", reg_value,
+				fts_esdcheck_data.flow_work_cnt_last);
+			fts_esdcheck_data.flow_work_hold_cnt++;
+		} else {
+			fts_esdcheck_data.flow_work_hold_cnt = 0;
+		}
+
+		fts_esdcheck_data.flow_work_cnt_last = reg_value;
+	}
+
+		/* Flow Work Cnt keep a value for 5 times, need execute TP reset */
+	if (fts_esdcheck_data.flow_work_hold_cnt >= 5) {
+		FTS_DEBUG("reg0x91 keep a value for 5 times, need execute TP reset");
+		return true;
+	}
+
+	return false;
+}
+
+static int esdcheck_algorithm(struct fts_ts_data *ts_data)
+{
+	int  ret = 0;
+	u8   reg_value = 0;
+	u8   reg_addr = 0;
+	bool hardware_reset = 0;
+
+	/* 1. esdcheck is interrupt, then return */
+	if (fts_esdcheck_data.intr == 1) {
+		fts_esdcheck_data.intr_cnt++;
+		if (fts_esdcheck_data.intr_cnt > ESDCHECK_INTRCNT_MAX)
+			fts_esdcheck_data.intr = 0;
+		else
+			return 0;
+	}
+
+	/* 2. check power state, if suspend, no need check esd */
+	if (fts_esdcheck_data.suspend == 1) {
+		FTS_DEBUG("In suspend, not check esd");
+		/* because in suspend state, adb can be used, when upgrade FW, will
+		 * active ESD check(active = 1); But in suspend, then will don't
+		 * queue_delayed_work, when resume, don't check ESD again
+		 */
+		return 0;
+	}
+
+	/* 3. check fts_esdcheck_data.proc_debug state, if 1-proc busy, no need check esd*/
+	if (fts_esdcheck_data.proc_debug == 1) {
+		FTS_INFO("In apk/adb command mode, not check esd");
+		return 0;
+	}
+
+	/* 4. In factory mode, can't check esd */
+	reg_addr = FTS_REG_WORKMODE;
+	ret = fts_read_reg(reg_addr, &reg_value);
+	if ( ret < 0 ) {
+		fts_esdcheck_data.nack_cnt++;
+	} else if ( (reg_value & 0x70) !=  FTS_REG_WORKMODE_WORK_VALUE) {
+		FTS_DEBUG("not in work mode(%x), no check esd", reg_value);
+		return 0;
+	}
+
+	/* 5. IDC esd check lcd  default:close */
+#if LCD_ESD_PATCH
+	idc_esdcheck_lcderror(ts_data);
+#endif
+
+	/* 6. Get Chip ID */
+	hardware_reset = get_chip_id(ts_data);
+
+	/* 7. get Flow work cnt: 0x91 If no change for 5 times, then ESD and reset */
+	if (!hardware_reset) {
+		hardware_reset = get_flow_cnt(ts_data);
+	}
+
+	/* 8. If need hardware reset, then handle it here */
+	if (hardware_reset == 1) {
+		FTS_DEBUG("NoACK=%d, Error Data=%d, Hardware Reset=%d",
+				fts_esdcheck_data.nack_cnt,
+				fts_esdcheck_data.dataerror_cnt,
+				fts_esdcheck_data.hardware_reset_cnt);
+		fts_esdcheck_tp_reset(ts_data);
+	}
+
+	return 0;
+}
+
+static void esdcheck_func(struct work_struct *work)
+{
+	struct fts_ts_data *ts_data = container_of(work,
+					struct fts_ts_data, esdcheck_work.work);
+
+	if (ENABLE == fts_esdcheck_data.mode) {
+		esdcheck_algorithm(ts_data);
+		queue_delayed_work(ts_data->ts_workqueue,
+				&ts_data->esdcheck_work,
+				msecs_to_jiffies(ESDCHECK_WAIT_TIME));
+	}
+}
+
+int fts_esdcheck_set_intr(bool intr)
+{
+	/* interrupt don't add debug message */
+	fts_esdcheck_data.intr = intr;
+	fts_esdcheck_data.intr_cnt = (u8)intr;
+	return 0;
+}
+
+static int fts_esdcheck_get_status(void)
+{
+	/* interrupt don't add debug message */
+	return fts_esdcheck_data.mode;
+}
+
+/*****************************************************************************
+*  Name: fts_esdcheck_proc_busy
+*  Brief: When APK or ADB command access TP via driver, then need set proc_debug,
+*         then will not check ESD.
+*  Input:
+*  Output:
+*  Return:
+*****************************************************************************/
+int fts_esdcheck_proc_busy(bool proc_debug)
+{
+	fts_esdcheck_data.proc_debug = proc_debug;
+	return 0;
+}
+
+/*****************************************************************************
+*  Name: fts_esdcheck_switch
+*  Brief: FTS esd check function switch.
+*  Input:   enable:  1 - Enable esd check
+*                    0 - Disable esd check
+*  Output:
+*  Return:
+*****************************************************************************/
+int fts_esdcheck_switch(bool enable)
+{
+	struct fts_ts_data *ts_data = fts_data;
+	FTS_FUNC_ENTER();
+	if (fts_esdcheck_data.mode == ENABLE) {
+		if (enable) {
+			FTS_DEBUG("ESD check start");
+			fts_esdcheck_data.flow_work_hold_cnt = 0;
+			fts_esdcheck_data.flow_work_cnt_last = 0;
+			fts_esdcheck_data.intr = 0;
+			fts_esdcheck_data.intr_cnt = 0;
+			queue_delayed_work(ts_data->ts_workqueue,
+					&ts_data->esdcheck_work,
+					msecs_to_jiffies(ESDCHECK_WAIT_TIME));
+		} else {
+			FTS_DEBUG("ESD check stop");
+			cancel_delayed_work_sync(&ts_data->esdcheck_work);
+		}
+	}
+
+	FTS_FUNC_EXIT();
+	return 0;
+}
+
+int fts_esdcheck_suspend(void)
+{
+	FTS_FUNC_ENTER();
+	fts_esdcheck_switch(DISABLE);
+	fts_esdcheck_data.suspend = 1;
+	fts_esdcheck_data.intr = 0;
+	fts_esdcheck_data.intr_cnt = 0;
+	FTS_FUNC_EXIT();
+	return 0;
+}
+
+int fts_esdcheck_resume( void )
+{
+	FTS_FUNC_ENTER();
+	fts_esdcheck_switch(ENABLE);
+	fts_esdcheck_data.suspend = 0;
+	fts_esdcheck_data.intr = 0;
+	fts_esdcheck_data.intr_cnt = 0;
+	FTS_FUNC_EXIT();
+	return 0;
+}
+
+static ssize_t fts_esdcheck_store(
+	struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct input_dev *input_dev = fts_data->input_dev;
+
+	mutex_lock(&input_dev->mutex);
+	if (FTS_SYSFS_ECHO_ON(buf)) {
+		FTS_DEBUG("enable esdcheck");
+		fts_esdcheck_data.mode = ENABLE;
+		fts_esdcheck_switch(ENABLE);
+	} else if (FTS_SYSFS_ECHO_OFF(buf)) {
+		FTS_DEBUG("disable esdcheck");
+		fts_esdcheck_switch(DISABLE);
+		fts_esdcheck_data.mode = DISABLE;
+	}
+	mutex_unlock(&input_dev->mutex);
+
+	return count;
+}
+
+static ssize_t fts_esdcheck_show(
+	struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int count;
+	struct input_dev *input_dev = fts_data->input_dev;
+
+	mutex_lock(&input_dev->mutex);
+	count = snprintf(buf, PAGE_SIZE, "Esd check: %s\n", \
+			fts_esdcheck_get_status() ? "On" : "Off");
+	mutex_unlock(&input_dev->mutex);
+
+	return count;
+}
+
+/* sysfs esd node
+ *   read example: cat  fts_esd_mode        ---read esd mode
+ *   write example:echo 01 > fts_esd_mode   ---make esdcheck enable
+ *
+ */
+static DEVICE_ATTR (fts_esd_mode, S_IRUGO | S_IWUSR, fts_esdcheck_show, fts_esdcheck_store);
+
+static struct attribute *fts_esd_mode_attrs[] = {
+
+	&dev_attr_fts_esd_mode.attr,
+	NULL,
+};
+
+static struct attribute_group fts_esd_group = {
+	.attrs = fts_esd_mode_attrs,
+};
+
+int fts_create_esd_sysfs(struct device *dev)
+{
+	int ret = 0;
+
+	ret = sysfs_create_group(&dev->kobj, &fts_esd_group);
+	if ( ret != 0) {
+		FTS_ERROR("fts_create_esd_sysfs(sysfs) create fail");
+		sysfs_remove_group(&dev->kobj, &fts_esd_group);
+		return ret;
+	}
+	return 0;
+}
+
+int fts_esdcheck_init(struct fts_ts_data *ts_data)
+{
+	FTS_FUNC_ENTER();
+
+	if (ts_data->ts_workqueue) {
+		INIT_DELAYED_WORK(&ts_data->esdcheck_work, esdcheck_func);
+	} else {
+		FTS_ERROR("fts workqueue is NULL, can't run esd check func!");
+		return -EINVAL;
+	}
+
+	memset((u8 *)&fts_esdcheck_data, 0, sizeof(struct fts_esdcheck_st));
+
+	fts_esdcheck_data.mode = ENABLE;
+	fts_esdcheck_data.intr = 0;
+	fts_esdcheck_data.intr_cnt = 0;
+	fts_esdcheck_switch(ENABLE);
+	fts_create_esd_sysfs(ts_data->dev);
+	FTS_FUNC_EXIT();
+	return 0;
+}
+
+int fts_esdcheck_exit(struct fts_ts_data *ts_data)
+{
+	sysfs_remove_group(&ts_data->dev->kobj, &fts_esd_group);
+	return 0;
+}
+
+#endif /* FTS_ESDCHECK_EN */
+

+ 1229 - 0
qcom/opensource/touch-drivers/focaltech_touch/focaltech_ex_fun.c

@@ -0,0 +1,1229 @@
+/*
+ *
+ * FocalTech TouchScreen driver.
+ *
+ * Copyright (c) 2012-2019, Focaltech Ltd. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+/*****************************************************************************
+*
+* File Name: Focaltech_ex_fun.c
+*
+* Author: Focaltech Driver Team
+*
+* Created: 2016-08-08
+*
+* Abstract:
+*
+* Reference:
+*
+*****************************************************************************/
+
+/*****************************************************************************
+* 1.Included header files
+*****************************************************************************/
+#include <linux/uaccess.h>
+#include "focaltech_core.h"
+
+/*****************************************************************************
+* Private constant and macro definitions using #define
+*****************************************************************************/
+#define PROC_UPGRADE                            0
+#define PROC_READ_REGISTER                      1
+#define PROC_WRITE_REGISTER                     2
+#define PROC_AUTOCLB                            4
+#define PROC_UPGRADE_INFO                       5
+#define PROC_WRITE_DATA                         6
+#define PROC_READ_DATA                          7
+#define PROC_SET_TEST_FLAG                      8
+#define PROC_SET_SLAVE_ADDR                     10
+#define PROC_HW_RESET                           11
+#define PROC_READ_STATUS                        12
+#define PROC_SET_BOOT_MODE                      13
+#define PROC_ENTER_TEST_ENVIRONMENT             14
+#define PROC_NAME                               "ftxxxx-debug"
+#define PROC_BUF_SIZE                           256
+
+/*****************************************************************************
+* Private enumerations, structures and unions using typedef
+*****************************************************************************/
+enum {
+	RWREG_OP_READ = 0,
+	RWREG_OP_WRITE = 1,
+};
+
+/*****************************************************************************
+* Static variables
+*****************************************************************************/
+static struct rwreg_operation_t {
+	int type;        /*  0: read, 1: write */
+	int reg;         /*  register */
+	int len;         /*  read/write length */
+	int val;         /*  length = 1; read: return value, write: op return */
+	int res;         /*  0: success, otherwise: fail */
+	char *opbuf;     /*  length >= 1, read return value, write: op return */
+} rw_op;
+
+/*****************************************************************************
+* Global variable or extern global variabls/functions
+*****************************************************************************/
+
+/*****************************************************************************
+* Static function prototypes
+*****************************************************************************/
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0))
+static ssize_t fts_debug_write(
+	struct file *filp, const char __user *buff, size_t count, loff_t *ppos)
+{
+	u8 *writebuf = NULL;
+	u8 tmpbuf[PROC_BUF_SIZE] = { 0 };
+	int buflen = count;
+	int writelen = 0;
+	int ret = 0;
+	char tmp[PROC_BUF_SIZE];
+	struct fts_ts_data *ts_data = fts_data;
+	struct ftxxxx_proc *proc = &ts_data->proc;
+
+	if ((buflen <= 1) || (buflen > PAGE_SIZE)) {
+		FTS_ERROR("apk proc wirte count(%d>%d) fail", buflen, (int)PAGE_SIZE);
+		return -EINVAL;
+	}
+
+	if (buflen > PROC_BUF_SIZE) {
+		writebuf = (u8 *)kzalloc(buflen * sizeof(u8), GFP_KERNEL);
+		if (NULL == writebuf) {
+			FTS_ERROR("apk proc wirte buf zalloc fail");
+			return -ENOMEM;
+		}
+	} else {
+		writebuf = tmpbuf;
+	}
+
+	if (copy_from_user(writebuf, buff, buflen)) {
+		FTS_ERROR("[APK]: copy from user error!!");
+		ret = -EFAULT;
+		goto proc_write_err;
+	}
+
+	proc->opmode = writebuf[0];
+	switch (proc->opmode) {
+	case PROC_SET_TEST_FLAG:
+		FTS_DEBUG("[APK]: PROC_SET_TEST_FLAG = %x", writebuf[1]);
+		if (writebuf[1] == 0) {
+#if FTS_ESDCHECK_EN
+			fts_esdcheck_switch(ENABLE);
+#endif
+		} else {
+#if FTS_ESDCHECK_EN
+			fts_esdcheck_switch(DISABLE);
+#endif
+		}
+		break;
+
+	case PROC_READ_REGISTER:
+		proc->cmd[0] = writebuf[1];
+		break;
+
+	case PROC_WRITE_REGISTER:
+		ret = fts_write_reg(writebuf[1], writebuf[2]);
+		if (ret < 0) {
+			FTS_ERROR("PROC_WRITE_REGISTER write error");
+			goto proc_write_err;
+		}
+		break;
+
+	case PROC_READ_DATA:
+		writelen = buflen - 1;
+		if (writelen >= FTX_MAX_COMMMAND_LENGTH) {
+			FTS_ERROR("cmd(PROC_READ_DATA) len(%d) fail", writelen);
+			goto proc_write_err;
+		}
+		memcpy(proc->cmd, writebuf + 1, writelen);
+		proc->cmd_len = writelen;
+		ret = fts_write(writebuf + 1, writelen);
+		if (ret < 0) {
+			FTS_ERROR("PROC_READ_DATA write error");
+			goto proc_write_err;
+		}
+		break;
+
+	case PROC_WRITE_DATA:
+		writelen = buflen - 1;
+		ret = fts_write(writebuf + 1, writelen);
+		if (ret < 0) {
+			FTS_ERROR("PROC_WRITE_DATA write error");
+			goto proc_write_err;
+		}
+		break;
+
+	case PROC_SET_SLAVE_ADDR:
+		break;
+
+	case PROC_HW_RESET:
+		snprintf(tmp, PROC_BUF_SIZE, "%s", writebuf + 1);
+		tmp[buflen - 1] = '\0';
+		if (strncmp(tmp, "focal_driver", 12) == 0) {
+			FTS_INFO("APK execute HW Reset");
+			fts_reset_proc(0);
+		}
+		break;
+
+	case PROC_SET_BOOT_MODE:
+		FTS_DEBUG("[APK]: PROC_SET_BOOT_MODE = %x", writebuf[1]);
+		if (0 == writebuf[1]) {
+			ts_data->fw_is_running = true;
+		} else {
+			ts_data->fw_is_running = false;
+		}
+		break;
+
+	case PROC_ENTER_TEST_ENVIRONMENT:
+		FTS_DEBUG("[APK]: PROC_ENTER_TEST_ENVIRONMENT = %x", writebuf[1]);
+		if (0 == writebuf[1]) {
+			fts_enter_test_environment(0);
+		} else {
+			fts_enter_test_environment(1);
+		}
+		break;
+
+	default:
+		break;
+	}
+
+	ret = buflen;
+proc_write_err:
+	if ((buflen > PROC_BUF_SIZE) && writebuf) {
+		kfree(writebuf);
+		writebuf = NULL;
+	}
+	return ret;
+}
+
+static ssize_t fts_debug_read(
+	struct file *filp, char __user *buff, size_t count, loff_t *ppos)
+{
+	int ret = 0;
+	int num_read_chars = 0;
+	int buflen = count;
+	u8 *readbuf = NULL;
+	u8 tmpbuf[PROC_BUF_SIZE] = { 0 };
+	struct fts_ts_data *ts_data = fts_data;
+	struct ftxxxx_proc *proc = &ts_data->proc;
+
+	if ((buflen <= 0) || (buflen > PAGE_SIZE)) {
+		FTS_ERROR("apk proc read count(%d>%d) fail", buflen, (int)PAGE_SIZE);
+		return -EINVAL;
+	}
+
+	if (buflen > PROC_BUF_SIZE) {
+		readbuf = (u8 *)kzalloc(buflen * sizeof(u8), GFP_KERNEL);
+		if (NULL == readbuf) {
+			FTS_ERROR("apk proc wirte buf zalloc fail");
+			return -ENOMEM;
+		}
+	} else {
+		readbuf = tmpbuf;
+	}
+
+#if FTS_ESDCHECK_EN
+	fts_esdcheck_proc_busy(1);
+#endif
+
+	switch (proc->opmode) {
+	case PROC_READ_REGISTER:
+		num_read_chars = 1;
+		ret = fts_read_reg(proc->cmd[0], &readbuf[0]);
+		if (ret < 0) {
+			FTS_ERROR("PROC_READ_REGISTER read error");
+			goto proc_read_err;
+		}
+		break;
+
+	case PROC_WRITE_REGISTER:
+		break;
+
+	case PROC_READ_DATA:
+		num_read_chars = buflen;
+		ret = fts_read(NULL, 0, readbuf, num_read_chars);
+		if (ret < 0) {
+			FTS_ERROR("PROC_READ_DATA read error");
+			goto proc_read_err;
+		}
+		break;
+
+	case PROC_WRITE_DATA:
+		break;
+
+	default:
+		break;
+	}
+
+#if FTS_ESDCHECK_EN
+	fts_esdcheck_proc_busy(0);
+#endif
+
+	if (copy_to_user(buff, readbuf, num_read_chars)) {
+		FTS_ERROR("copy to user error");
+		ret = -EFAULT;
+		goto proc_read_err;
+	}
+
+	ret = num_read_chars;
+proc_read_err:
+	if ((buflen > PROC_BUF_SIZE) && readbuf) {
+		kfree(readbuf);
+		readbuf = NULL;
+	}
+	return ret;
+}
+
+static const struct proc_ops fts_proc_fops = {
+	.proc_read  = fts_debug_read,
+	.proc_write = fts_debug_write,
+};
+#else
+static int fts_debug_write(struct file *filp,
+	const char __user *buff, unsigned long len, void *data)
+{
+	u8 *writebuf = NULL;
+	u8 tmpbuf[PROC_BUF_SIZE] = { 0 };
+	int buflen = count;
+	int writelen = 0;
+	int ret = 0;
+	char tmp[PROC_BUF_SIZE];
+	struct fts_ts_data *ts_data = fts_data;
+	struct ftxxxx_proc *proc = &ts_data->proc;
+
+	if ((buflen <= 1) || (buflen > PAGE_SIZE)) {
+		FTS_ERROR("apk proc wirte count(%d>%d) fail", buflen, (int)PAGE_SIZE);
+		return -EINVAL;
+	}
+
+	if (buflen > PROC_BUF_SIZE) {
+		writebuf = (u8 *)kzalloc(buflen * sizeof(u8), GFP_KERNEL);
+		if (NULL == writebuf) {
+			FTS_ERROR("apk proc wirte buf zalloc fail");
+			return -ENOMEM;
+		}
+	} else {
+		writebuf = tmpbuf;
+	}
+
+	if (copy_from_user(writebuf, buff, buflen)) {
+		FTS_ERROR("[APK]: copy from user error!!");
+		ret = -EFAULT;
+		goto proc_write_err;
+	}
+
+	proc->opmode = writebuf[0];
+	switch (proc->opmode) {
+	case PROC_SET_TEST_FLAG:
+		FTS_DEBUG("[APK]: PROC_SET_TEST_FLAG = %x", writebuf[1]);
+		if (writebuf[1] == 0) {
+#if FTS_ESDCHECK_EN
+			fts_esdcheck_switch(ENABLE);
+#endif
+		} else {
+#if FTS_ESDCHECK_EN
+			fts_esdcheck_switch(DISABLE);
+#endif
+		}
+		break;
+
+	case PROC_READ_REGISTER:
+		proc->cmd[0] = writebuf[1];
+		break;
+
+	case PROC_WRITE_REGISTER:
+		ret = fts_write_reg(writebuf[1], writebuf[2]);
+		if (ret < 0) {
+			FTS_ERROR("PROC_WRITE_REGISTER write error");
+			goto proc_write_err;
+		}
+		break;
+
+	case PROC_READ_DATA:
+		writelen = buflen - 1;
+		if (writelen >= FTX_MAX_COMMMAND_LENGTH) {
+			FTS_ERROR("cmd(PROC_READ_DATA) length(%d) fail", writelen);
+			goto proc_write_err;
+		}
+		memcpy(proc->cmd, writebuf + 1, writelen);
+		proc->cmd_len = writelen;
+		ret = fts_write(writebuf + 1, writelen);
+		if (ret < 0) {
+			FTS_ERROR("PROC_READ_DATA write error");
+			goto proc_write_err;
+		}
+		break;
+
+	case PROC_WRITE_DATA:
+		writelen = buflen - 1;
+		ret = fts_write(writebuf + 1, writelen);
+		if (ret < 0) {
+			FTS_ERROR("PROC_WRITE_DATA write error");
+			goto proc_write_err;
+		}
+		break;
+
+	case PROC_SET_SLAVE_ADDR:
+		break;
+
+	case PROC_HW_RESET:
+		snprintf(tmp, PROC_BUF_SIZE, "%s", writebuf + 1);
+		tmp[buflen - 1] = '\0';
+		if (strncmp(tmp, "focal_driver", 12) == 0) {
+			FTS_INFO("APK execute HW Reset");
+			fts_reset_proc(0);
+		}
+		break;
+
+	case PROC_SET_BOOT_MODE:
+		FTS_DEBUG("[APK]: PROC_SET_BOOT_MODE = %x", writebuf[1]);
+		if (0 == writebuf[1]) {
+			ts_data->fw_is_running = true;
+		} else {
+			ts_data->fw_is_running = false;
+		}
+		break;
+
+	case PROC_ENTER_TEST_ENVIRONMENT:
+		FTS_DEBUG("[APK]: PROC_ENTER_TEST_ENVIRONMENT = %x", writebuf[1]);
+		if (0 == writebuf[1]) {
+			fts_enter_test_environment(0);
+		} else {
+			fts_enter_test_environment(1);
+		}
+		break;
+
+	default:
+		break;
+	}
+
+	ret = buflen;
+proc_write_err:
+	if ((buflen > PROC_BUF_SIZE) && writebuf) {
+		kfree(writebuf);
+		writebuf = NULL;
+	}
+	return ret;
+}
+
+static int fts_debug_read(
+	char *page, char **start, off_t off, int count, int *eof, void *data )
+{
+	int ret = 0;
+	int num_read_chars = 0;
+	int buflen = count;
+	u8 *readbuf = NULL;
+	u8 tmpbuf[PROC_BUF_SIZE] = { 0 };
+	struct fts_ts_data *ts_data = fts_data;
+	struct ftxxxx_proc *proc = &ts_data->proc;
+
+	if ((buflen <= 0) || (buflen > PAGE_SIZE)) {
+		FTS_ERROR("apk proc read count(%d>%d) fail", buflen, (int)PAGE_SIZE);
+		return -EINVAL;
+	}
+
+	if (buflen > PROC_BUF_SIZE) {
+		readbuf = (u8 *)kzalloc(buflen * sizeof(u8), GFP_KERNEL);
+		if (NULL == readbuf) {
+			FTS_ERROR("apk proc wirte buf zalloc fail");
+			return -ENOMEM;
+		}
+	} else {
+		readbuf = tmpbuf;
+	}
+
+#if FTS_ESDCHECK_EN
+	fts_esdcheck_proc_busy(1);
+#endif
+
+	switch (proc->opmode) {
+	case PROC_READ_REGISTER:
+		num_read_chars = 1;
+		ret = fts_read_reg(proc->cmd[0], &readbuf[0]);
+		if (ret < 0) {
+			FTS_ERROR("PROC_READ_REGISTER read error");
+			goto proc_read_err;
+		}
+		break;
+
+	case PROC_WRITE_REGISTER:
+		break;
+
+	case PROC_READ_DATA:
+		num_read_chars = buflen;
+		ret = fts_read(NULL, 0, readbuf, num_read_chars);
+		if (ret < 0) {
+			FTS_ERROR("PROC_READ_DATA read error");
+			goto proc_read_err;
+		}
+		break;
+
+	case PROC_WRITE_DATA:
+		break;
+
+	default:
+		break;
+	}
+
+#if FTS_ESDCHECK_EN
+	fts_esdcheck_proc_busy(0);
+#endif
+
+	if (copy_to_user(buff, readbuf, num_read_chars)) {
+		FTS_ERROR("copy to user error");
+		ret = -EFAULT;
+		goto proc_read_err;
+	}
+
+	ret = num_read_chars;
+proc_read_err:
+	if ((buflen > PROC_BUF_SIZE) && readbuf) {
+		kfree(readbuf);
+		readbuf = NULL;
+	}
+	return ret;
+}
+#endif
+
+int fts_create_apk_debug_channel(struct fts_ts_data *ts_data)
+{
+	struct ftxxxx_proc *proc = &ts_data->proc;
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0))
+	proc->proc_entry = proc_create(PROC_NAME, 0777, NULL, &fts_proc_fops);
+	if (NULL == proc->proc_entry) {
+		FTS_ERROR("create proc entry fail");
+		return -ENOMEM;
+	}
+#else
+	proc->proc_entry = create_proc_entry(PROC_NAME, 0777, NULL);
+	if (NULL == proc->proc_entry) {
+		FTS_ERROR("create proc entry fail");
+		return -ENOMEM;
+	}
+	proc->proc_entry->write_proc = fts_debug_write;
+	proc->proc_entry->read_proc = fts_debug_read;
+#endif
+
+	FTS_INFO("Create proc entry success!");
+	return 0;
+}
+
+void fts_release_apk_debug_channel(struct fts_ts_data *ts_data)
+{
+	struct ftxxxx_proc *proc = &ts_data->proc;
+
+	if (proc->proc_entry) {
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0))
+		proc_remove(proc->proc_entry);
+#else
+		remove_proc_entry(PROC_NAME, NULL);
+#endif
+	}
+}
+
+/************************************************************************
+ * sysfs interface
+ ***********************************************************************/
+/* fts_hw_reset interface */
+static ssize_t fts_hw_reset_show(
+	struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct input_dev *input_dev = fts_data->input_dev;
+	ssize_t count = 0;
+
+	mutex_lock(&input_dev->mutex);
+	fts_reset_proc(0);
+	count = snprintf(buf, PAGE_SIZE, "hw reset executed\n");
+	mutex_unlock(&input_dev->mutex);
+
+	return count;
+}
+
+static ssize_t fts_hw_reset_store(
+	struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t count)
+{
+	return -EPERM;
+}
+
+/* fts_irq interface */
+static ssize_t fts_irq_show(
+	struct device *dev, struct device_attribute *attr, char *buf)
+{
+	ssize_t count = 0;
+	struct irq_desc *desc = irq_to_desc(fts_data->irq);
+
+	count = snprintf(buf, PAGE_SIZE, "irq_depth:%d\n", desc->depth);
+
+	return count;
+}
+
+static ssize_t fts_irq_store(
+	struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct input_dev *input_dev = fts_data->input_dev;
+
+	mutex_lock(&input_dev->mutex);
+	if (FTS_SYSFS_ECHO_ON(buf)) {
+		FTS_INFO("enable irq");
+		fts_irq_enable();
+	} else if (FTS_SYSFS_ECHO_OFF(buf)) {
+		FTS_INFO("disable irq");
+		fts_irq_disable();
+	}
+	mutex_unlock(&input_dev->mutex);
+	return count;
+}
+
+/* fts_boot_mode interface */
+static ssize_t fts_bootmode_store(
+	struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct input_dev *input_dev = fts_data->input_dev;
+
+	FTS_FUNC_ENTER();
+	mutex_lock(&input_dev->mutex);
+	if (FTS_SYSFS_ECHO_ON(buf)) {
+		FTS_INFO("[EX-FUN]set to boot mode");
+		fts_data->fw_is_running = false;
+	} else if (FTS_SYSFS_ECHO_OFF(buf)) {
+		FTS_INFO("[EX-FUN]set to fw mode");
+		fts_data->fw_is_running = true;
+	}
+	mutex_unlock(&input_dev->mutex);
+	FTS_FUNC_EXIT();
+
+	return count;
+}
+
+static ssize_t fts_bootmode_show(
+	struct device *dev, struct device_attribute *attr, char *buf)
+{
+	ssize_t count = 0;
+	struct input_dev *input_dev = fts_data->input_dev;
+
+	FTS_FUNC_ENTER();
+	mutex_lock(&input_dev->mutex);
+	if (true == fts_data->fw_is_running) {
+		count = snprintf(buf, PAGE_SIZE, "tp is in fw mode\n");
+	} else {
+		count = snprintf(buf, PAGE_SIZE, "tp is in boot mode\n");
+	}
+	mutex_unlock(&input_dev->mutex);
+	FTS_FUNC_EXIT();
+
+	return count;
+}
+
+/* fts_tpfwver interface */
+static ssize_t fts_fw_version_show(
+	struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct fts_ts_data *ts_data = fts_data;
+	struct input_dev *input_dev = ts_data->input_dev;
+	ssize_t num_read_chars = 0;
+	u8 fwver = 0;
+
+	mutex_lock(&input_dev->mutex);
+
+#if FTS_ESDCHECK_EN
+	fts_esdcheck_proc_busy(1);
+#endif
+	fts_read_reg(FTS_REG_FW_VER, &fwver);
+#if FTS_ESDCHECK_EN
+	fts_esdcheck_proc_busy(0);
+#endif
+	if ((fwver == 0xFF) || (fwver == 0x00))
+		num_read_chars = snprintf(buf, PAGE_SIZE, "get tp fw version fail!\n");
+	else
+		num_read_chars = snprintf(buf, PAGE_SIZE, "%02x\n", fwver);
+
+	mutex_unlock(&input_dev->mutex);
+	return num_read_chars;
+}
+
+static ssize_t fts_fw_version_store(
+	struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t count)
+{
+	return -EPERM;
+}
+
+/* fts_rw_reg */
+static ssize_t fts_tprwreg_show(
+	struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int count;
+	int i;
+	struct input_dev *input_dev = fts_data->input_dev;
+
+	mutex_lock(&input_dev->mutex);
+
+	if (rw_op.len < 0) {
+		count = snprintf(buf, PAGE_SIZE, "Invalid cmd line\n");
+	} else if (rw_op.len == 1) {
+		if (RWREG_OP_READ == rw_op.type) {
+			if (rw_op.res == 0) {
+				count = snprintf(buf, PAGE_SIZE, "Read %02X: %02X\n", rw_op.reg, rw_op.val);
+			} else {
+				count = snprintf(buf, PAGE_SIZE, "Read %02X failed, ret: %d\n", rw_op.reg,  rw_op.res);
+			}
+		} else {
+			if (rw_op.res == 0) {
+				count = snprintf(buf, PAGE_SIZE, "Write %02X, %02X success\n", rw_op.reg,  rw_op.val);
+			} else {
+				count = snprintf(buf, PAGE_SIZE, "Write %02X failed, ret: %d\n", rw_op.reg,  rw_op.res);
+			}
+		}
+	} else {
+		if (RWREG_OP_READ == rw_op.type) {
+			count = snprintf(buf, PAGE_SIZE, "Read Reg: [%02X]-[%02X]\n", rw_op.reg, rw_op.reg + rw_op.len);
+			count += snprintf(buf + count, PAGE_SIZE, "Result: ");
+			if (rw_op.res) {
+				count += snprintf(buf + count, PAGE_SIZE, "failed, ret: %d\n", rw_op.res);
+			} else {
+				if (rw_op.opbuf) {
+					for (i = 0; i < rw_op.len; i++) {
+						count += snprintf(buf + count, PAGE_SIZE, "%02X ", rw_op.opbuf[i]);
+					}
+					count += snprintf(buf + count, PAGE_SIZE, "\n");
+				}
+			}
+		} else {
+			count = snprintf(buf, PAGE_SIZE, "Write Reg: [%02X]-[%02X]\n", rw_op.reg, rw_op.reg + rw_op.len - 1);
+			count += snprintf(buf + count, PAGE_SIZE, "Write Data: ");
+			if (rw_op.opbuf) {
+				for (i = 1; i < rw_op.len; i++) {
+					count += snprintf(buf + count, PAGE_SIZE, "%02X ", rw_op.opbuf[i]);
+				}
+				count += snprintf(buf + count, PAGE_SIZE, "\n");
+			}
+			if (rw_op.res) {
+				count += snprintf(buf + count, PAGE_SIZE, "Result: failed, ret: %d\n", rw_op.res);
+			} else {
+				count += snprintf(buf + count, PAGE_SIZE, "Result: success\n");
+			}
+		}
+		/*if (rw_op.opbuf) {
+		 *  kfree(rw_op.opbuf);
+		 *  rw_op.opbuf = NULL;
+		 *}
+		 */
+	}
+	mutex_unlock(&input_dev->mutex);
+
+	return count;
+}
+
+static int shex_to_int(const char *hex_buf, int size)
+{
+	int i;
+	int base = 1;
+	int value = 0;
+	char single;
+
+	for (i = size - 1; i >= 0; i--) {
+		single = hex_buf[i];
+
+		if ((single >= '0') && (single <= '9')) {
+			value += (single - '0') * base;
+		} else if ((single >= 'a') && (single <= 'z')) {
+			value += (single - 'a' + 10) * base;
+		} else if ((single >= 'A') && (single <= 'Z')) {
+			value += (single - 'A' + 10) * base;
+		} else {
+			return -EINVAL;
+		}
+
+		base *= 16;
+	}
+
+	return value;
+}
+
+
+static u8 shex_to_u8(const char *hex_buf, int size)
+{
+	return (u8)shex_to_int(hex_buf, size);
+}
+/*
+ * Format buf:
+ * [0]: '0' write, '1' read(reserved)
+ * [1-2]: addr, hex
+ * [3-4]: length, hex
+ * [5-6]...[n-(n+1)]: data, hex
+ */
+static int fts_parse_buf(const char *buf, size_t cmd_len)
+{
+	int length;
+	int i;
+	char *tmpbuf;
+
+	rw_op.reg = shex_to_u8(buf + 1, 2);
+	length = shex_to_int(buf + 3, 2);
+
+	if (buf[0] == '1') {
+		rw_op.len = length;
+		rw_op.type = RWREG_OP_READ;
+		FTS_DEBUG("read %02X, %d bytes", rw_op.reg, rw_op.len);
+	} else {
+		if (cmd_len < (length * 2 + 5)) {
+			pr_err("data invalided!\n");
+			return -EINVAL;
+		}
+		FTS_DEBUG("write %02X, %d bytes", rw_op.reg, length);
+
+		/* first byte is the register addr */
+		rw_op.type = RWREG_OP_WRITE;
+		rw_op.len = length + 1;
+	}
+
+	if (rw_op.len > 0) {
+		tmpbuf = (char *)kzalloc(rw_op.len, GFP_KERNEL);
+		if (!tmpbuf) {
+			FTS_ERROR("allocate memory failed!\n");
+			return -ENOMEM;
+		}
+
+		if (RWREG_OP_WRITE == rw_op.type) {
+			tmpbuf[0] = rw_op.reg & 0xFF;
+			FTS_DEBUG("write buffer: ");
+			for (i = 1; i < rw_op.len; i++) {
+				tmpbuf[i] = shex_to_u8(buf + 5 + i * 2 - 2, 2);
+				FTS_DEBUG("buf[%d]: %02X", i, tmpbuf[i] & 0xFF);
+			}
+		}
+		rw_op.opbuf = tmpbuf;
+	}
+
+	return rw_op.len;
+}
+
+static ssize_t fts_tprwreg_store(
+	struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct input_dev *input_dev = fts_data->input_dev;
+	ssize_t cmd_length = 0;
+
+	mutex_lock(&input_dev->mutex);
+	cmd_length = count - 1;
+
+	if (rw_op.opbuf) {
+		kfree(rw_op.opbuf);
+		rw_op.opbuf = NULL;
+	}
+
+	FTS_DEBUG("cmd len: %d, buf: %s", (int)cmd_length, buf);
+	/* compatible old ops */
+	if (2 == cmd_length) {
+		rw_op.type = RWREG_OP_READ;
+		rw_op.len = 1;
+		rw_op.reg = shex_to_int(buf, 2);
+	} else if (4 == cmd_length) {
+		rw_op.type = RWREG_OP_WRITE;
+		rw_op.len = 1;
+		rw_op.reg = shex_to_int(buf, 2);
+		rw_op.val = shex_to_int(buf + 2, 2);
+	} else if (cmd_length < 5) {
+		FTS_ERROR("Invalid cmd buffer");
+		mutex_unlock(&input_dev->mutex);
+		return -EINVAL;
+	} else {
+		rw_op.len = fts_parse_buf(buf, cmd_length);
+	}
+
+#if FTS_ESDCHECK_EN
+	fts_esdcheck_proc_busy(1);
+#endif
+	if (rw_op.len < 0) {
+		FTS_ERROR("cmd buffer error!");
+		goto exit;
+	}
+
+	if (RWREG_OP_READ == rw_op.type) {
+		if (rw_op.len == 1) {
+			u8 reg, val;
+			reg = rw_op.reg & 0xFF;
+			rw_op.res = fts_read_reg(reg, &val);
+			rw_op.val = val;
+		} else {
+			char reg;
+			reg = rw_op.reg & 0xFF;
+
+			rw_op.res = fts_read(&reg, 1, rw_op.opbuf, rw_op.len);
+		}
+
+		if (rw_op.res < 0) {
+			FTS_ERROR("Could not read 0x%02x", rw_op.reg);
+		} else {
+			FTS_INFO("read 0x%02x, %d bytes successful", rw_op.reg, rw_op.len);
+			rw_op.res = 0;
+		}
+
+	} else {
+		if (rw_op.len == 1) {
+			u8 reg, val;
+			reg = rw_op.reg & 0xFF;
+			val = rw_op.val & 0xFF;
+			rw_op.res = fts_write_reg(reg, val);
+		} else {
+			rw_op.res = fts_write(rw_op.opbuf, rw_op.len);
+		}
+		if (rw_op.res < 0) {
+			FTS_ERROR("Could not write 0x%02x", rw_op.reg);
+
+		} else {
+			FTS_INFO("Write 0x%02x, %d bytes successful", rw_op.val, rw_op.len);
+			rw_op.res = 0;
+		}
+	}
+
+exit:
+#if FTS_ESDCHECK_EN
+	fts_esdcheck_proc_busy(0);
+#endif
+	mutex_unlock(&input_dev->mutex);
+
+	return count;
+}
+
+/* fts_driver_info interface */
+static ssize_t fts_driverinfo_show(
+	struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int count = 0;
+	struct fts_ts_data *ts_data = fts_data;
+	struct fts_ts_platform_data *pdata = ts_data->pdata;
+	struct input_dev *input_dev = ts_data->input_dev;
+
+	mutex_lock(&input_dev->mutex);
+	count += scnprintf(buf + count, PAGE_SIZE, "Driver Ver:%s\n", FTS_DRIVER_VERSION);
+
+	count += snprintf(buf + count, PAGE_SIZE, "Resolution:(%d,%d)~(%d,%d)\n",
+			pdata->x_min, pdata->y_min, pdata->x_max, pdata->y_max);
+
+	count += snprintf(buf + count, PAGE_SIZE, "Max Touchs:%d\n", pdata->max_touch_number);
+
+	count += scnprintf(buf + count, PAGE_SIZE, "reset gpio:%d,int gpio:%d,irq:%d\n",
+			pdata->reset_gpio, pdata->irq_gpio, ts_data->irq);
+
+	count += scnprintf(buf + count, PAGE_SIZE, "IC ID:0x%02x%02x\n",
+			ts_data->ic_info.ids.chip_idh, ts_data->ic_info.ids.chip_idl);
+	if (ts_data->bus_type == BUS_TYPE_I2C)
+		count += scnprintf(buf + count, PAGE_SIZE, "BUS:%s,addr:0x%x\n",
+				"I2C", ts_data->client->addr);
+	else
+		count += scnprintf(buf + count, PAGE_SIZE,
+				"BUS:%s,mode:%d,max_freq:%d\n", "SPI",
+				ts_data->spi->mode, ts_data->spi->max_speed_hz);
+
+	mutex_unlock(&input_dev->mutex);
+
+	return count;
+}
+
+static ssize_t fts_driverinfo_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	return -EPERM;
+}
+
+/* fts_dump_reg interface */
+static ssize_t fts_dumpreg_show(
+	struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int count = 0;
+	u8 val = 0;
+	struct input_dev *input_dev = fts_data->input_dev;
+
+	mutex_lock(&input_dev->mutex);
+#if FTS_ESDCHECK_EN
+	fts_esdcheck_proc_busy(1);
+#endif
+	fts_read_reg(FTS_REG_POWER_MODE, &val);
+	count += snprintf(buf + count, PAGE_SIZE, "Power Mode:0x%02x\n", val);
+
+	fts_read_reg(FTS_REG_FW_VER, &val);
+	count += snprintf(buf + count, PAGE_SIZE, "FW Ver:0x%02x\n", val);
+
+	fts_read_reg(FTS_REG_LIC_VER, &val);
+	count += snprintf(buf + count, PAGE_SIZE, "LCD Initcode Ver:0x%02x\n", val);
+
+	fts_read_reg(FTS_REG_IDE_PARA_VER_ID, &val);
+	count += snprintf(buf + count, PAGE_SIZE, "Param Ver:0x%02x\n", val);
+
+	fts_read_reg(FTS_REG_IDE_PARA_STATUS, &val);
+	count += snprintf(buf + count, PAGE_SIZE, "Param status:0x%02x\n", val);
+
+	fts_read_reg(FTS_REG_VENDOR_ID, &val);
+	count += snprintf(buf + count, PAGE_SIZE, "Vendor ID:0x%02x\n", val);
+
+	fts_read_reg(FTS_REG_LCD_BUSY_NUM, &val);
+	count += snprintf(buf + count, PAGE_SIZE, "LCD Busy Number:0x%02x\n", val);
+
+	fts_read_reg(FTS_REG_GESTURE_EN, &val);
+	count += snprintf(buf + count, PAGE_SIZE, "Gesture Mode:0x%02x\n", val);
+
+	fts_read_reg(FTS_REG_CHARGER_MODE_EN, &val);
+	count += snprintf(buf + count, PAGE_SIZE, "charge stat:0x%02x\n", val);
+
+	fts_read_reg(FTS_REG_INT_CNT, &val);
+	count += snprintf(buf + count, PAGE_SIZE, "INT count:0x%02x\n", val);
+
+	fts_read_reg(FTS_REG_FLOW_WORK_CNT, &val);
+	count += snprintf(buf + count, PAGE_SIZE, "ESD count:0x%02x\n", val);
+#if FTS_ESDCHECK_EN
+	fts_esdcheck_proc_busy(0);
+#endif
+
+	mutex_unlock(&input_dev->mutex);
+
+	return count;
+}
+
+static ssize_t fts_dumpreg_store(
+	struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t count)
+{
+	return -EPERM;
+}
+
+/* fts_dump_reg interface */
+static ssize_t fts_tpbuf_show(
+	struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int count = 0;
+	int i = 0;
+	struct input_dev *input_dev = fts_data->input_dev;
+
+	mutex_lock(&input_dev->mutex);
+	count += snprintf(buf + count, PAGE_SIZE, "touch point buffer:\n");
+	for (i = 0; i < fts_data->pnt_buf_size; i++) {
+		count += snprintf(buf + count, PAGE_SIZE, "%02x ",
+			fts_data->point_buf[i]);
+	}
+	count += snprintf(buf + count, PAGE_SIZE, "\n");
+	mutex_unlock(&input_dev->mutex);
+
+	return count;
+}
+
+static ssize_t fts_tpbuf_store(
+	struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t count)
+{
+	return -EPERM;
+}
+
+/* fts_log_level interface */
+static ssize_t fts_log_level_show(
+	struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int count = 0;
+	struct input_dev *input_dev = fts_data->input_dev;
+
+	mutex_lock(&input_dev->mutex);
+	count += snprintf(buf + count, PAGE_SIZE, "log level:%d\n",
+			fts_data->log_level);
+	mutex_unlock(&input_dev->mutex);
+
+	return count;
+}
+
+static ssize_t fts_log_level_store(
+	struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t count)
+{
+	int value = 0;
+	struct input_dev *input_dev = fts_data->input_dev;
+
+	FTS_FUNC_ENTER();
+	mutex_lock(&input_dev->mutex);
+	sscanf(buf, "%d", &value);
+	FTS_DEBUG("log level:%d->%d", fts_data->log_level, value);
+	fts_data->log_level = value;
+	mutex_unlock(&input_dev->mutex);
+	FTS_FUNC_EXIT();
+
+	return count;
+}
+
+#ifdef CONFIG_FTS_TRUSTED_TOUCH
+
+static ssize_t trusted_touch_enable_show(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct fts_ts_data *info = fts_data;
+
+	return scnprintf(buf, PAGE_SIZE, "%d",
+			atomic_read(&info->trusted_touch_enabled));
+}
+
+static ssize_t trusted_touch_enable_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct fts_ts_data *info = fts_data;
+	unsigned long value;
+	int err = 0;
+
+	if (count > 2)
+		return -EINVAL;
+	err = kstrtoul(buf, 10, &value);
+	if (err != 0)
+		return err;
+
+	if (!atomic_read(&info->trusted_touch_initialized))
+		return -EIO;
+
+#ifdef CONFIG_ARCH_QTI_VM
+	err = fts_ts_handle_trusted_touch_tvm(info, value);
+	if (err) {
+		pr_err("Failed to handle trusted touch in tvm\n");
+		return -EINVAL;
+	}
+#else
+	err = fts_ts_handle_trusted_touch_pvm(info, value);
+	if (err) {
+		pr_err("Failed to handle trusted touch in pvm\n");
+		return -EINVAL;
+	}
+#endif
+	err = count;
+	return err;
+}
+
+static ssize_t trusted_touch_event_show(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct fts_ts_data *info = fts_data;
+
+	return scnprintf(buf, PAGE_SIZE, "%d",
+			atomic_read(&info->trusted_touch_event));
+}
+
+static ssize_t trusted_touch_event_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct fts_ts_data *info = fts_data;
+	unsigned long value;
+	int err = 0;
+
+	if (count > 2)
+		return -EINVAL;
+
+	err = kstrtoul(buf, 10, &value);
+	if (err != 0)
+		return err;
+
+	if (!atomic_read(&info->trusted_touch_initialized))
+		return -EIO;
+
+	if (value)
+		return -EIO;
+
+	atomic_set(&info->trusted_touch_event, value);
+
+	return count;
+}
+
+static ssize_t trusted_touch_type_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct fts_ts_data *info = fts_data;
+
+	return scnprintf(buf, PAGE_SIZE, "%s", info->vm_info->trusted_touch_type);
+}
+
+#endif
+
+/* get the fw version  example:cat fw_version */
+static DEVICE_ATTR_RW(fts_fw_version);
+
+/* read and write register(s)
+*   All data type is **HEX**
+*   Single Byte:
+*       read:   echo 88 > rw_reg ---read register 0x88
+*       write:  echo 8807 > rw_reg ---write 0x07 into register 0x88
+*   Multi-bytes:
+*       [0:rw-flag][1-2: reg addr, hex][3-4: length, hex][5-6...n-n+1: write data, hex]
+*       rw-flag: 0, write; 1, read
+*       read:  echo 10005           > rw_reg ---read reg 0x00-0x05
+*       write: echo 000050102030405 > rw_reg ---write reg 0x00-0x05 as 01,02,03,04,05
+*  Get result:
+*       cat rw_reg
+*/
+static DEVICE_ATTR(fts_rw_reg, S_IRUGO | S_IWUSR, fts_tprwreg_show, fts_tprwreg_store);
+static DEVICE_ATTR(fts_driver_info, S_IRUGO | S_IWUSR, fts_driverinfo_show, fts_driverinfo_store);
+static DEVICE_ATTR(fts_dump_reg, S_IRUGO | S_IWUSR, fts_dumpreg_show, fts_dumpreg_store);
+static DEVICE_ATTR(fts_hw_reset, S_IRUGO | S_IWUSR, fts_hw_reset_show, fts_hw_reset_store);
+static DEVICE_ATTR(fts_irq, S_IRUGO | S_IWUSR, fts_irq_show, fts_irq_store);
+static DEVICE_ATTR(fts_boot_mode, S_IRUGO | S_IWUSR, fts_bootmode_show, fts_bootmode_store);
+static DEVICE_ATTR(fts_touch_point, S_IRUGO | S_IWUSR, fts_tpbuf_show, fts_tpbuf_store);
+static DEVICE_ATTR(fts_log_level, S_IRUGO | S_IWUSR, fts_log_level_show, fts_log_level_store);
+#ifdef CONFIG_FTS_TRUSTED_TOUCH
+static DEVICE_ATTR_RW(trusted_touch_enable);
+static DEVICE_ATTR_RW(trusted_touch_event);
+static DEVICE_ATTR_RO(trusted_touch_type);
+#endif
+
+/* add your attr in here*/
+static struct attribute *fts_attributes[] = {
+	&dev_attr_fts_fw_version.attr,
+	&dev_attr_fts_rw_reg.attr,
+	&dev_attr_fts_dump_reg.attr,
+	&dev_attr_fts_driver_info.attr,
+	&dev_attr_fts_hw_reset.attr,
+	&dev_attr_fts_irq.attr,
+	&dev_attr_fts_boot_mode.attr,
+	&dev_attr_fts_touch_point.attr,
+	&dev_attr_fts_log_level.attr,
+#ifdef CONFIG_FTS_TRUSTED_TOUCH
+	&dev_attr_trusted_touch_enable.attr,
+	&dev_attr_trusted_touch_event.attr,
+	&dev_attr_trusted_touch_type.attr,
+#endif
+	NULL
+};
+
+static struct attribute_group fts_attribute_group = {
+	.attrs = fts_attributes
+};
+
+int fts_create_sysfs(struct fts_ts_data *ts_data)
+{
+	int ret = 0;
+
+	ret = sysfs_create_group(&ts_data->dev->kobj, &fts_attribute_group);
+	if (ret) {
+		FTS_ERROR("[EX]: sysfs_create_group() failed!!");
+		sysfs_remove_group(&ts_data->dev->kobj, &fts_attribute_group);
+		return -ENOMEM;
+	} else {
+		FTS_INFO("[EX]: sysfs_create_group() succeeded!!");
+	}
+
+	return ret;
+}
+
+int fts_remove_sysfs(struct fts_ts_data *ts_data)
+{
+	sysfs_remove_group(&ts_data->dev->kobj, &fts_attribute_group);
+	return 0;
+}

+ 359 - 0
qcom/opensource/touch-drivers/focaltech_touch/focaltech_ex_mode.c

@@ -0,0 +1,359 @@
+/*
+ *
+ * FocalTech ftxxxx TouchScreen driver.
+ *
+ * Copyright (c) 2012-2019, Focaltech Ltd. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+/*****************************************************************************
+*
+* File Name: focaltech_ex_mode.c
+*
+* Author: Focaltech Driver Team
+*
+* Created: 2016-08-31
+*
+* Abstract:
+*
+* Reference:
+*
+*****************************************************************************/
+
+/*****************************************************************************
+* 1.Included header files
+*****************************************************************************/
+#include "focaltech_core.h"
+
+/*****************************************************************************
+* 2.Private constant and macro definitions using #define
+*****************************************************************************/
+
+/*****************************************************************************
+* 3.Private enumerations, structures and unions using typedef
+*****************************************************************************/
+enum _ex_mode {
+	MODE_GLOVE = 0,
+	MODE_COVER,
+	MODE_CHARGER,
+	REPORT_RATE,
+};
+
+/*****************************************************************************
+* 4.Static variables
+*****************************************************************************/
+
+/*****************************************************************************
+* 5.Global variable or extern global variabls/functions
+*****************************************************************************/
+
+/*****************************************************************************
+* 6.Static function prototypes
+*******************************************************************************/
+static int fts_ex_mode_switch(enum _ex_mode mode, u8 value)
+{
+	int ret = 0;
+
+	switch (mode) {
+	case MODE_GLOVE:
+		ret = fts_write_reg(FTS_REG_GLOVE_MODE_EN, value > 0 ? 1 : 0);
+		if (ret < 0)
+			FTS_ERROR("MODE_GLOVE switch to %d fail", value);
+		break;
+
+	case MODE_COVER:
+		ret = fts_write_reg(FTS_REG_COVER_MODE_EN, value > 0 ? 1 : 0);
+		if (ret < 0)
+			FTS_ERROR("MODE_COVER switch to %d fail", value);
+		break;
+
+	case MODE_CHARGER:
+		ret = fts_write_reg(FTS_REG_CHARGER_MODE_EN, value > 0 ? 1 : 0);
+		if (ret < 0)
+			FTS_ERROR("MODE_CHARGER switch to %d fail", value);
+		break;
+
+	case REPORT_RATE:
+		ret = fts_write_reg(FTS_REG_REPORT_RATE, value);
+		if (ret < 0)
+			FTS_ERROR("REPORT_RATE switch to %d fail", value);
+		break;
+
+	default:
+		FTS_ERROR("mode(%d) unsupport", mode);
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static ssize_t fts_glove_mode_show(
+	struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int count = 0;
+	u8 val = 0;
+	struct fts_ts_data *ts_data = fts_data;
+	struct input_dev *input_dev = ts_data->input_dev;
+
+	mutex_lock(&input_dev->mutex);
+	fts_read_reg(FTS_REG_GLOVE_MODE_EN, &val);
+	count = scnprintf(buf + count, PAGE_SIZE, "Glove Mode:%s\n",
+			ts_data->glove_mode ? "On" : "Off");
+	count += scnprintf(buf + count, PAGE_SIZE - count,
+			"Glove Reg(0xC0):%d\n", val);
+	mutex_unlock(&input_dev->mutex);
+
+	return count;
+}
+
+static ssize_t fts_glove_mode_store(
+	struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t count)
+{
+	int ret = 0;
+	struct fts_ts_data *ts_data = fts_data;
+
+	if (FTS_SYSFS_ECHO_ON(buf)) {
+		if (!ts_data->glove_mode) {
+			FTS_DEBUG("enter glove mode");
+			ret = fts_ex_mode_switch(MODE_GLOVE, ENABLE);
+			if (ret >= 0) {
+				ts_data->glove_mode = ENABLE;
+			}
+		}
+	} else if (FTS_SYSFS_ECHO_OFF(buf)) {
+		if (ts_data->glove_mode) {
+			FTS_DEBUG("exit glove mode");
+			ret = fts_ex_mode_switch(MODE_GLOVE, DISABLE);
+			if (ret >= 0) {
+				ts_data->glove_mode = DISABLE;
+			}
+		}
+	}
+
+	FTS_DEBUG("glove mode:%d", ts_data->glove_mode);
+	return count;
+}
+
+
+static ssize_t fts_cover_mode_show(
+	struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int count = 0;
+	u8 val = 0;
+	struct fts_ts_data *ts_data = fts_data;
+	struct input_dev *input_dev = ts_data->input_dev;
+
+	mutex_lock(&input_dev->mutex);
+	fts_read_reg(FTS_REG_COVER_MODE_EN, &val);
+	count = scnprintf(buf + count, PAGE_SIZE, "Cover Mode:%s\n",
+			ts_data->cover_mode ? "On" : "Off");
+	count += scnprintf(buf + count, PAGE_SIZE - count,
+			"Cover Reg(0xC1):%d\n", val);
+	mutex_unlock(&input_dev->mutex);
+
+	return count;
+}
+
+static ssize_t fts_cover_mode_store(
+	struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t count)
+{
+	int ret = 0;
+	struct fts_ts_data *ts_data = fts_data;
+
+	if (FTS_SYSFS_ECHO_ON(buf)) {
+		if (!ts_data->cover_mode) {
+			FTS_DEBUG("enter cover mode");
+			ret = fts_ex_mode_switch(MODE_COVER, ENABLE);
+			if (ret >= 0) {
+				ts_data->cover_mode = ENABLE;
+			}
+		}
+	} else if (FTS_SYSFS_ECHO_OFF(buf)) {
+		if (ts_data->cover_mode) {
+			FTS_DEBUG("exit cover mode");
+			ret = fts_ex_mode_switch(MODE_COVER, DISABLE);
+			if (ret >= 0) {
+				ts_data->cover_mode = DISABLE;
+			}
+		}
+	}
+
+	FTS_DEBUG("cover mode:%d", ts_data->cover_mode);
+	return count;
+}
+
+static ssize_t fts_charger_mode_show(
+	struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int count = 0;
+	u8 val = 0;
+	struct fts_ts_data *ts_data = fts_data;
+	struct input_dev *input_dev = ts_data->input_dev;
+
+	mutex_lock(&input_dev->mutex);
+	fts_read_reg(FTS_REG_CHARGER_MODE_EN, &val);
+	count = scnprintf(buf + count, PAGE_SIZE, "Charger Mode:%s\n",
+			ts_data->charger_mode ? "On" : "Off");
+	count += scnprintf(buf + count, PAGE_SIZE - count,
+			"Charger Reg(0x8B):%d\n", val);
+	mutex_unlock(&input_dev->mutex);
+
+	return count;
+}
+
+static ssize_t fts_charger_mode_store(
+	struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t count)
+{
+	int ret = 0;
+	struct fts_ts_data *ts_data = fts_data;
+
+	if (FTS_SYSFS_ECHO_ON(buf)) {
+		if (!ts_data->charger_mode) {
+			FTS_DEBUG("enter charger mode");
+			ret = fts_ex_mode_switch(MODE_CHARGER, ENABLE);
+			if (ret >= 0) {
+				ts_data->charger_mode = ENABLE;
+			}
+		}
+	} else if (FTS_SYSFS_ECHO_OFF(buf)) {
+		if (ts_data->charger_mode) {
+			FTS_DEBUG("exit charger mode");
+			ret = fts_ex_mode_switch(MODE_CHARGER, DISABLE);
+			if (ret >= 0) {
+				ts_data->charger_mode = DISABLE;
+			}
+		}
+	}
+
+	FTS_DEBUG("charger mode:%d", ts_data->glove_mode);
+	return count;
+}
+
+static ssize_t fts_report_rate_show(
+	struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int count = 0;
+	u8 val = 0;
+	struct fts_ts_data *ts_data = fts_data;
+	struct input_dev *input_dev = ts_data->input_dev;
+
+	mutex_lock(&input_dev->mutex);
+	fts_read_reg(FTS_REG_REPORT_RATE, &val);
+	count = scnprintf(buf + count, PAGE_SIZE,
+			"Report Rate:%d\n", ts_data->report_rate);
+	count += scnprintf(buf + count, PAGE_SIZE - count,
+			"Report Rate Reg(0x88):%d\n", val);
+	mutex_unlock(&input_dev->mutex);
+
+	return count;
+}
+
+static ssize_t fts_report_rate_store(
+	struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t count)
+{
+	int ret = 0;
+	struct fts_ts_data *ts_data = fts_data;
+	int rate;
+
+	ret = kstrtoint(buf, 16, &rate);
+	if (ret)
+		return ret;
+
+	if (rate != ts_data->report_rate) {
+		ret = fts_ex_mode_switch(REPORT_RATE, (u8)rate);
+		if (ret >= 0)
+			ts_data->report_rate = rate;
+	}
+
+	FTS_DEBUG("report rate:%d", ts_data->report_rate);
+	return count;
+}
+
+
+/* read and write charger mode
+ * read example: cat fts_glove_mode        ---read  glove mode
+ * write example:echo 1 > fts_glove_mode   ---write glove mode to 01
+ */
+static DEVICE_ATTR(fts_glove_mode, S_IRUGO | S_IWUSR,
+			fts_glove_mode_show, fts_glove_mode_store);
+
+static DEVICE_ATTR(fts_cover_mode, S_IRUGO | S_IWUSR,
+			fts_cover_mode_show, fts_cover_mode_store);
+
+static DEVICE_ATTR(fts_charger_mode, S_IRUGO | S_IWUSR,
+			fts_charger_mode_show, fts_charger_mode_store);
+
+static DEVICE_ATTR_RW(fts_report_rate);
+
+static struct attribute *fts_touch_mode_attrs[] = {
+	&dev_attr_fts_glove_mode.attr,
+	&dev_attr_fts_cover_mode.attr,
+	&dev_attr_fts_charger_mode.attr,
+	&dev_attr_fts_report_rate.attr,
+	NULL,
+};
+
+static struct attribute_group fts_touch_mode_group = {
+	.attrs = fts_touch_mode_attrs,
+};
+
+int fts_ex_mode_recovery(struct fts_ts_data *ts_data)
+{
+	if (ts_data->glove_mode) {
+		fts_ex_mode_switch(MODE_GLOVE, ENABLE);
+	}
+
+	if (ts_data->cover_mode) {
+		fts_ex_mode_switch(MODE_COVER, ENABLE);
+	}
+
+	if (ts_data->charger_mode) {
+		fts_ex_mode_switch(MODE_CHARGER, ENABLE);
+	}
+
+	if (ts_data->report_rate > 0)
+		fts_ex_mode_switch(REPORT_RATE, ts_data->report_rate);
+
+	return 0;
+}
+
+int fts_ex_mode_init(struct fts_ts_data *ts_data)
+{
+	int ret = 0;
+
+	ts_data->glove_mode = DISABLE;
+	ts_data->cover_mode = DISABLE;
+	ts_data->charger_mode = DISABLE;
+	ts_data->report_rate = 0;
+
+	ret = sysfs_create_group(&ts_data->dev->kobj, &fts_touch_mode_group);
+	if (ret < 0) {
+		FTS_ERROR("create sysfs(ex_mode) fail");
+		sysfs_remove_group(&ts_data->dev->kobj, &fts_touch_mode_group);
+		return ret;
+	} else {
+		FTS_DEBUG("create sysfs(ex_mode) succeedfully");
+	}
+
+	return 0;
+}
+
+int fts_ex_mode_exit(struct fts_ts_data *ts_data)
+{
+	sysfs_remove_group(&ts_data->dev->kobj, &fts_touch_mode_group);
+	return 0;
+}

+ 2093 - 0
qcom/opensource/touch-drivers/focaltech_touch/focaltech_flash.c

@@ -0,0 +1,2093 @@
+/*
+ *
+ * FocalTech fts TouchScreen driver.
+ *
+ * Copyright (c) 2012-2019, Focaltech Ltd. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+/*****************************************************************************
+*
+* File Name: focaltech_flash.c
+*
+* Author: Focaltech Driver Team
+*
+* Created: 2016-08-08
+*
+* Abstract:
+*
+* Reference:
+*
+*****************************************************************************/
+
+/*****************************************************************************
+* 1.Included header files
+*****************************************************************************/
+#include "focaltech_core.h"
+#include "focaltech_flash.h"
+
+/*****************************************************************************
+* Private constant and macro definitions using #define
+*****************************************************************************/
+#define FTS_FW_REQUEST_SUPPORT                      1
+/* Example: focaltech_ts_fw_tianma.bin */
+#define FTS_FW_NAME_PREX_WITH_REQUEST               "focaltech_ts_fw_"
+
+/*****************************************************************************
+* Global variable or extern global variabls/functions
+*****************************************************************************/
+u8 fw_file[1] = {
+0,
+};
+
+struct upgrade_module module_list[] = {
+	{FTS_MODULE_ID, FTS_MODULE_NAME, fw_file, sizeof(fw_file)},
+	{FTS_MODULE2_ID, FTS_MODULE2_NAME, fw_file, sizeof(fw_file)},
+};
+
+struct upgrade_func upgrade_func_ft8720 = {
+	.ctype = {0x1C},
+	.fwveroff = 0x210E,
+	.fwcfgoff = 0x1000,
+	.appoff = 0x2000,
+	.licoff = 0x0000,
+	.appoff_handle_in_ic = true,
+	.pramboot_supported = false,
+	.new_return_value_from_ic = true,
+	.hid_supported = false,
+};
+
+struct upgrade_func *upgrade_func_list[] = {
+	&upgrade_func_ft5452,
+	&upgrade_func_ft5652,
+	&upgrade_func_ft8720,
+};
+
+struct fts_upgrade *fwupgrade;
+
+/*****************************************************************************
+* Static function prototypes
+*****************************************************************************/
+static bool fts_fwupg_check_state(
+	struct fts_upgrade *upg, enum FW_STATUS rstate);
+
+/************************************************************************
+* Name: fts_fwupg_get_boot_state
+* Brief: read boot id(rom/pram/bootloader), confirm boot environment
+* Input:
+* Output:
+* Return: return 0 if success, otherwise return error code
+***********************************************************************/
+static int fts_fwupg_get_boot_state(
+	struct fts_upgrade *upg,
+	enum FW_STATUS *fw_sts)
+{
+	int ret = 0;
+	u8 cmd[4] = { 0 };
+	u32 cmd_len = 0;
+	u8 val[2] = { 0 };
+	struct ft_chip_t *ids = NULL;
+
+	FTS_INFO("**********read boot id**********");
+	if ((!upg) || (!upg->func) || (!upg->ts_data) || (!fw_sts)) {
+		FTS_ERROR("upg/func/ts_data/fw_sts is null");
+		return -EINVAL;
+	}
+
+	if (upg->func->hid_supported)
+		fts_hid2std();
+
+	cmd[0] = FTS_CMD_START1;
+	cmd[1] = FTS_CMD_START2;
+	if (upg->func->upgspec_version >= UPGRADE_SPEC_V_1_0)
+		cmd_len = 1;
+	else
+		cmd_len = 2;
+	ret = fts_write(cmd, cmd_len);
+	if (ret < 0) {
+		FTS_ERROR("write 55 cmd fail");
+		return ret;
+	}
+
+	msleep(FTS_CMD_START_DELAY);
+	cmd[0] = FTS_CMD_READ_ID;
+	cmd[1] = cmd[2] = cmd[3] = 0x00;
+	if (fts_data->ic_info.is_incell ||
+		(upg->func->upgspec_version >= UPGRADE_SPEC_V_1_0))
+		cmd_len = FTS_CMD_READ_ID_LEN_INCELL;
+	else
+		cmd_len = FTS_CMD_READ_ID_LEN;
+	ret = fts_read(cmd, cmd_len, val, 2);
+	if (ret < 0) {
+		FTS_ERROR("write 90 cmd fail");
+		return ret;
+	}
+	FTS_INFO("read boot id:0x%02x%02x", val[0], val[1]);
+
+	ids = &upg->ts_data->ic_info.ids;
+	if ((val[0] == ids->rom_idh) && (val[1] == ids->rom_idl)) {
+		FTS_INFO("tp run in romboot");
+		*fw_sts = FTS_RUN_IN_ROM;
+	} else if ((val[0] == ids->pb_idh) && (val[1] == ids->pb_idl)) {
+		FTS_INFO("tp run in pramboot");
+		*fw_sts = FTS_RUN_IN_PRAM;
+	} else if ((val[0] == ids->bl_idh) && (val[1] == ids->bl_idl)) {
+		FTS_INFO("tp run in bootloader");
+		*fw_sts = FTS_RUN_IN_BOOTLOADER;
+	}
+
+	return 0;
+}
+
+static int fts_fwupg_reset_to_boot(struct fts_upgrade *upg)
+{
+	int ret = 0;
+	u8 reg = FTS_REG_UPGRADE;
+
+	FTS_INFO("send 0xAA and 0x55 to FW, reset to boot environment");
+	if (upg && upg->func && upg->func->is_reset_register_BC) {
+		reg = FTS_REG_UPGRADE2;
+	}
+
+	ret = fts_write_reg(reg, FTS_UPGRADE_AA);
+	if (ret < 0) {
+		FTS_ERROR("write FC=0xAA fail");
+		return ret;
+	}
+	msleep(FTS_DELAY_UPGRADE_AA);
+
+	ret = fts_write_reg(reg, FTS_UPGRADE_55);
+	if (ret < 0) {
+		FTS_ERROR("write FC=0x55 fail");
+		return ret;
+	}
+
+	msleep(FTS_DELAY_UPGRADE_RESET);
+	return 0;
+}
+
+/************************************************************************
+* Name: fts_fwupg_reset_to_romboot
+* Brief: reset to romboot, to load pramboot
+* Input:
+* Output:
+* Return: return 0 if success, otherwise return error code
+***********************************************************************/
+static int fts_fwupg_reset_to_romboot(struct fts_upgrade *upg)
+{
+	int ret = 0;
+	int i = 0;
+	u8 cmd = FTS_CMD_RESET;
+	enum FW_STATUS state = FTS_RUN_IN_ERROR;
+
+	ret = fts_write(&cmd, 1);
+	if (ret < 0) {
+		FTS_ERROR("pram/rom/bootloader reset cmd write fail");
+		return ret;
+	}
+	mdelay(10);
+
+	for (i = 0; i < FTS_UPGRADE_LOOP; i++) {
+		ret = fts_fwupg_get_boot_state(upg, &state);
+		if (FTS_RUN_IN_ROM == state)
+			break;
+		mdelay(5);
+	}
+	if (i >= FTS_UPGRADE_LOOP) {
+		FTS_ERROR("reset to romboot fail");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static u16 fts_crc16_calc_host(u8 *pbuf, u32 length)
+{
+	u16 ecc = 0;
+	u32 i = 0;
+	u32 j = 0;
+
+	for ( i = 0; i < length; i += 2 ) {
+		ecc ^= ((pbuf[i] << 8) | (pbuf[i + 1]));
+		for (j = 0; j < 16; j ++) {
+			if (ecc & 0x01)
+				ecc = (u16)((ecc >> 1) ^ AL2_FCS_COEF);
+			else
+				ecc >>= 1;
+		}
+	}
+
+	return ecc;
+}
+
+static u16 fts_pram_ecc_calc_host(u8 *pbuf, u32 length)
+{
+	return fts_crc16_calc_host(pbuf, length);
+}
+
+static int fts_pram_ecc_cal_algo(
+	struct fts_upgrade *upg,
+	u32 start_addr,
+	u32 ecc_length)
+{
+	int ret = 0;
+	int i = 0;
+	int ecc = 0;
+	u8 val[2] = { 0 };
+	u8 tmp = 0;
+	u8 cmd[FTS_ROMBOOT_CMD_ECC_NEW_LEN] = { 0 };
+
+	FTS_INFO("read out pramboot checksum");
+	if ((!upg) || (!upg->func)) {
+		FTS_ERROR("upg/func is null");
+		return -EINVAL;
+	}
+
+	cmd[0] = FTS_ROMBOOT_CMD_ECC;
+	cmd[1] = BYTE_OFF_16(start_addr);
+	cmd[2] = BYTE_OFF_8(start_addr);
+	cmd[3] = BYTE_OFF_0(start_addr);
+	cmd[4] = BYTE_OFF_16(ecc_length);
+	cmd[5] = BYTE_OFF_8(ecc_length);
+	cmd[6] = BYTE_OFF_0(ecc_length);
+	ret = fts_write(cmd, FTS_ROMBOOT_CMD_ECC_NEW_LEN);
+	if (ret < 0) {
+		FTS_ERROR("write pramboot ecc cal cmd fail");
+		return ret;
+	}
+
+	cmd[0] = FTS_ROMBOOT_CMD_ECC_FINISH;
+	for (i = 0; i < FTS_ECC_FINISH_TIMEOUT; i++) {
+		msleep(1);
+		ret = fts_read(cmd, 1, val, 1);
+		if (ret < 0) {
+			FTS_ERROR("ecc_finish read cmd fail");
+			return ret;
+		}
+		if (upg->func->new_return_value_from_ic ||
+			(upg->func->upgspec_version >= UPGRADE_SPEC_V_1_0)) {
+			tmp = FTS_ROMBOOT_CMD_ECC_FINISH_OK_A5;
+		} else {
+			tmp = FTS_ROMBOOT_CMD_ECC_FINISH_OK_00;
+		}
+		if (tmp == val[0])
+			break;
+	}
+	if (i >= FTS_ECC_FINISH_TIMEOUT) {
+		FTS_ERROR("wait ecc finish fail");
+		return -EIO;
+	}
+
+	cmd[0] = FTS_ROMBOOT_CMD_ECC_READ;
+	ret = fts_read(cmd, 1, val, 2);
+	if (ret < 0) {
+		FTS_ERROR("read pramboot ecc fail");
+		return ret;
+	}
+
+	ecc = ((u16)(val[0] << 8) + val[1]) & 0x0000FFFF;
+	return ecc;
+}
+
+static int fts_pram_ecc_cal_xor(void)
+{
+	int ret = 0;
+	u8 reg_val = 0;
+
+	FTS_INFO("read out pramboot checksum");
+
+	ret = fts_read_reg(FTS_ROMBOOT_CMD_ECC, &reg_val);
+	if (ret < 0) {
+		FTS_ERROR("read pramboot ecc fail");
+		return ret;
+	}
+
+	return (int)reg_val;
+}
+
+static int fts_pram_ecc_cal(struct fts_upgrade *upg, u32 saddr, u32 len)
+{
+	if ((!upg) || (!upg->func)) {
+		FTS_ERROR("upg/func is null");
+		return -EINVAL;
+	}
+
+	if ((upg->func->pram_ecc_check_mode == ECC_CHECK_MODE_CRC16) ||
+		(upg->func->upgspec_version >= UPGRADE_SPEC_V_1_0))
+		return fts_pram_ecc_cal_algo(upg, saddr, len);
+
+	return fts_pram_ecc_cal_xor();
+}
+
+static int fts_pram_write_buf(struct fts_upgrade *upg, u8 *buf, u32 len)
+{
+	int ret = 0;
+	u32 i = 0;
+	u32 j = 0;
+	u32 offset = 0;
+	u32 remainder = 0;
+	u32 packet_number;
+	u32 packet_len = 0;
+	u8 packet_buf[FTS_FLASH_PACKET_LENGTH + FTS_CMD_WRITE_LEN] = { 0 };
+	u8 ecc_tmp = 0;
+	int ecc_in_host = 0;
+	u32 cmdlen = 0;
+
+	FTS_INFO("write pramboot to pram");
+	if ((!upg) || (!upg->func) || !buf) {
+		FTS_ERROR("upg/func/buf is null");
+		return -EINVAL;
+	}
+
+	FTS_INFO("pramboot len=%d", len);
+	if ((len < PRAMBOOT_MIN_SIZE) || (len > PRAMBOOT_MAX_SIZE)) {
+		FTS_ERROR("pramboot length(%d) fail", len);
+		return -EINVAL;
+	}
+
+	packet_number = len / FTS_FLASH_PACKET_LENGTH;
+	remainder = len % FTS_FLASH_PACKET_LENGTH;
+	if (remainder > 0)
+		packet_number++;
+	packet_len = FTS_FLASH_PACKET_LENGTH;
+
+	for (i = 0; i < packet_number; i++) {
+		offset = i * FTS_FLASH_PACKET_LENGTH;
+		/* last packet */
+		if ((i == (packet_number - 1)) && remainder)
+			packet_len = remainder;
+
+		if (upg->ts_data->bus_type == BUS_TYPE_SPI_V2) {
+			packet_buf[0] = FTS_ROMBOOT_CMD_SET_PRAM_ADDR;
+			packet_buf[1] = BYTE_OFF_16(offset);
+			packet_buf[2] = BYTE_OFF_8(offset);
+			packet_buf[3] = BYTE_OFF_0(offset);
+
+			ret = fts_write(packet_buf, FTS_ROMBOOT_CMD_SET_PRAM_ADDR_LEN);
+			if (ret < 0) {
+				FTS_ERROR("pramboot set write address(%d) fail", i);
+				return ret;
+			}
+
+			packet_buf[0] = FTS_ROMBOOT_CMD_WRITE;
+			cmdlen = 1;
+		} else {
+			packet_buf[0] = FTS_ROMBOOT_CMD_WRITE;
+			packet_buf[1] = BYTE_OFF_16(offset);
+			packet_buf[2] = BYTE_OFF_8(offset);
+			packet_buf[3] = BYTE_OFF_0(offset);
+
+			packet_buf[4] = BYTE_OFF_8(packet_len);
+			packet_buf[5] = BYTE_OFF_0(packet_len);
+			cmdlen = 6;
+		}
+
+		for (j = 0; j < packet_len; j++) {
+			packet_buf[cmdlen + j] = buf[offset + j];
+			if (upg->func->pram_ecc_check_mode == ECC_CHECK_MODE_XOR)
+				ecc_tmp ^= packet_buf[cmdlen + j];
+		}
+
+		ret = fts_write(packet_buf, packet_len + cmdlen);
+		if (ret < 0) {
+			FTS_ERROR("pramboot write data(%d) fail", i);
+			return ret;
+		}
+	}
+
+	if ((upg->func->pram_ecc_check_mode == ECC_CHECK_MODE_CRC16) ||
+		(upg->func->upgspec_version >= UPGRADE_SPEC_V_1_0))
+		ecc_in_host = (int)fts_pram_ecc_calc_host(buf, len);
+	else
+		ecc_in_host = (int)ecc_tmp;
+
+	return ecc_in_host;
+}
+
+static int fts_pram_start(void)
+{
+	u8 cmd = FTS_ROMBOOT_CMD_START_APP;
+	int ret = 0;
+
+	FTS_INFO("remap to start pramboot");
+
+	ret = fts_write(&cmd, 1);
+	if (ret < 0) {
+		FTS_ERROR("write start pram cmd fail");
+		return ret;
+	}
+	msleep(FTS_DELAY_PRAMBOOT_START);
+
+	return 0;
+}
+
+static int fts_pram_write_remap(struct fts_upgrade *upg)
+{
+	int ret = 0;
+	int ecc_in_host = 0;
+	int ecc_in_tp = 0;
+	u8 *pb_buf = NULL;
+	u32 pb_len = 0;
+
+	FTS_INFO("write pram and remap");
+	if (!upg || !upg->func || !upg->func->pramboot) {
+		FTS_ERROR("upg/func/pramboot is null");
+		return -EINVAL;
+	}
+
+	if (upg->func->pb_length < FTS_MIN_LEN) {
+		FTS_ERROR("pramboot length(%d) fail", upg->func->pb_length);
+		return -EINVAL;
+	}
+
+	pb_buf = upg->func->pramboot;
+	pb_len = upg->func->pb_length;
+
+	/* write pramboot to pram */
+	ecc_in_host = fts_pram_write_buf(upg, pb_buf, pb_len);
+	if (ecc_in_host < 0) {
+		FTS_ERROR( "write pramboot fail");
+		return ecc_in_host;
+	}
+
+	/* read out checksum */
+	ecc_in_tp = fts_pram_ecc_cal(upg, 0, pb_len);
+	if (ecc_in_tp < 0) {
+		FTS_ERROR( "read pramboot ecc fail");
+		return ecc_in_tp;
+	}
+
+	FTS_INFO("pram ecc in tp:%x, host:%x", ecc_in_tp, ecc_in_host);
+	/*  pramboot checksum != fw checksum, upgrade fail */
+	if (ecc_in_host != ecc_in_tp) {
+		FTS_ERROR("pramboot ecc check fail");
+		return -EIO;
+	}
+
+	/*start pram*/
+	ret = fts_pram_start();
+	if (ret < 0) {
+		FTS_ERROR("pram start fail");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int fts_pram_init(void)
+{
+	int ret = 0;
+	u8 reg_val = 0;
+	u8 wbuf[3] = { 0 };
+
+	FTS_INFO("pramboot initialization");
+
+	/* read flash ID */
+	wbuf[0] = FTS_CMD_FLASH_TYPE;
+	ret = fts_read(wbuf, 1, &reg_val, 1);
+	if (ret < 0) {
+		FTS_ERROR("read flash type fail");
+		return ret;
+	}
+
+	/* set flash clk */
+	wbuf[0] = FTS_CMD_FLASH_TYPE;
+	wbuf[1] = reg_val;
+	wbuf[2] = 0x00;
+	ret = fts_write(wbuf, 3);
+	if (ret < 0) {
+		FTS_ERROR("write flash type fail");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int fts_pram_write_init(struct fts_upgrade *upg)
+{
+	int ret = 0;
+	bool state = 0;
+	enum FW_STATUS status = FTS_RUN_IN_ERROR;
+
+	FTS_INFO("**********pram write and init**********");
+	if ((NULL == upg) || (NULL == upg->func)) {
+		FTS_ERROR("upgrade/func is null");
+		return -EINVAL;
+	}
+
+	if (!upg->func->pramboot_supported) {
+		FTS_ERROR("ic not support pram");
+		return -EINVAL;
+	}
+
+	FTS_DEBUG("check whether tp is in romboot or not ");
+	/* need reset to romboot when non-romboot state */
+	ret = fts_fwupg_get_boot_state(upg, &status);
+	if (status != FTS_RUN_IN_ROM) {
+		if (FTS_RUN_IN_PRAM == status) {
+			FTS_INFO("tp is in pramboot, need send reset cmd before upgrade");
+			ret = fts_pram_init();
+			if (ret < 0) {
+				FTS_ERROR("pramboot(before) init fail");
+				return ret;
+			}
+		}
+
+		FTS_INFO("tp isn't in romboot, need send reset to romboot");
+		ret = fts_fwupg_reset_to_romboot(upg);
+		if (ret < 0) {
+			FTS_ERROR("reset to romboot fail");
+			return ret;
+		}
+	}
+
+	/* check the length of the pramboot */
+	ret = fts_pram_write_remap(upg);
+	if (ret < 0) {
+		FTS_ERROR("pram write fail, ret=%d", ret);
+		return ret;
+	}
+
+	FTS_DEBUG("after write pramboot, confirm run in pramboot");
+	state = fts_fwupg_check_state(upg, FTS_RUN_IN_PRAM);
+	if (!state) {
+		FTS_ERROR("not in pramboot");
+		return -EIO;
+	}
+
+	ret = fts_pram_init();
+	if (ret < 0) {
+		FTS_ERROR("pramboot init fail");
+		return ret;
+	}
+
+	return 0;
+}
+
+static bool fts_fwupg_check_fw_valid(void)
+{
+	int ret = 0;
+
+	ret = fts_wait_tp_to_valid();
+	if (ret < 0) {
+		FTS_INFO("tp fw invaild");
+		return false;
+	}
+
+	FTS_INFO("tp fw vaild");
+	return true;
+}
+
+/************************************************************************
+* Name: fts_fwupg_check_state
+* Brief: confirm tp run in which mode: romboot/pramboot/bootloader
+* Input:
+* Output:
+* Return: return true if state is match, otherwise return false
+***********************************************************************/
+static bool fts_fwupg_check_state(
+	struct fts_upgrade *upg, enum FW_STATUS rstate)
+{
+	int ret = 0;
+	int i = 0;
+	enum FW_STATUS cstate = FTS_RUN_IN_ERROR;
+
+	for (i = 0; i < FTS_UPGRADE_LOOP; i++) {
+		ret = fts_fwupg_get_boot_state(upg, &cstate);
+		/* FTS_DEBUG("fw state=%d, retries=%d", cstate, i); */
+		if (cstate == rstate)
+			return true;
+		msleep(FTS_DELAY_READ_ID);
+	}
+
+	return false;
+}
+
+/************************************************************************
+* Name: fts_fwupg_reset_in_boot
+* Brief: RST CMD(07), reset to romboot(bootloader) in boot environment
+* Input:
+* Output:
+* Return: return 0 if success, otherwise return error code
+***********************************************************************/
+int fts_fwupg_reset_in_boot(void)
+{
+	int ret = 0;
+	u8 cmd = FTS_CMD_RESET;
+
+	FTS_INFO("reset in boot environment");
+	ret = fts_write(&cmd, 1);
+	if (ret < 0) {
+		FTS_ERROR("pram/rom/bootloader reset cmd write fail");
+		return ret;
+	}
+
+	msleep(FTS_DELAY_UPGRADE_RESET);
+	return 0;
+}
+
+static int fts_fwupg_enter_into_boot_old(struct fts_upgrade *upg)
+{
+	int ret = 0;
+	bool fwvalid = false;
+	bool state = false;
+
+	FTS_INFO("***********enter into pramboot/bootloader***********");
+	if ((!upg) || (NULL == upg->func)) {
+		FTS_ERROR("upgrade/func is null");
+		return -EINVAL;
+	}
+
+	fwvalid = fts_fwupg_check_fw_valid();
+	if (fwvalid) {
+		ret = fts_fwupg_reset_to_boot(upg);
+		if (ret < 0) {
+			FTS_ERROR("enter into romboot/bootloader fail");
+			return ret;
+		}
+	} else if (upg->func->read_boot_id_need_reset) {
+		ret = fts_fwupg_reset_in_boot();
+		if (ret < 0) {
+			FTS_ERROR("reset before read boot id when fw invalid fail");
+			return ret;
+		}
+	}
+
+	if (upg->func->pramboot_supported) {
+		FTS_INFO("pram supported, write pramboot and init");
+		/* pramboot */
+		ret = fts_pram_write_init(upg);
+		if (ret < 0) {
+			FTS_ERROR("pram write_init fail");
+			return ret;
+		}
+	} else {
+		FTS_DEBUG("pram not supported, confirm in bootloader");
+		/* bootloader */
+		state = fts_fwupg_check_state(upg, FTS_RUN_IN_BOOTLOADER);
+		if (!state) {
+			FTS_ERROR("fw not in bootloader, fail");
+			return -EIO;
+		}
+	}
+
+	return 0;
+}
+
+static int fts_hardware_reset_to_boot(void)
+{
+	u8 cmd[2] = {0};
+	int i = 0;
+	u8 chip_id[2] = { 0 };
+	u8 id_cmd[4] = { 0 };
+	int ret = 0;
+
+	fts_reset_proc(0);
+	FTS_INFO("reset finish!!");
+	usleep_range(10000, 11000);
+
+	for (i = 0; i < 20; i++) {
+		FTS_INFO("start send 55 AA");
+		cmd[0] = 0x55;
+		cmd[1] = 0xAA;
+		ret = fts_write(cmd, 2);
+		if (ret < 0)
+			FTS_ERROR("send 55 AA cmd fail");
+
+		usleep_range(8000, 9000);
+		FTS_INFO("start send 90 00 00 00");
+		id_cmd[0] = 0x90;
+		id_cmd[1] = id_cmd[2] = id_cmd[3] = 0x00;
+		ret = fts_read(id_cmd, 1, chip_id, 2);
+		if (ret < 0)
+			FTS_ERROR("send 90 cmd fail");
+
+		FTS_INFO("read boot id = %x--%x", chip_id[0], chip_id[1]);
+		if (chip_id[1] == 0xB2)
+			break;
+
+		usleep_range(1000, 2000);
+
+		if (i == 10) {
+			fts_reset_proc(0);
+			FTS_INFO("reset again finish!!");
+			usleep_range(10000, 11000);
+		}
+
+	}
+
+	if (i >= 20) {
+		FTS_INFO("enter into romboot fail");
+		return -EIO;
+	}
+	return 0;
+
+}
+
+static int fts_fwupg_enter_into_boot_new(struct fts_upgrade *upg)
+{
+	int ret = 0;
+	bool state = false;
+
+	FTS_INFO("***********enter into pramboot/bootloader***********");
+	if ((!upg) || (upg->func == NULL)) {
+		FTS_ERROR("upgrade/func is null");
+		return -EINVAL;
+	}
+
+	ret = fts_hardware_reset_to_boot();
+	if (!ret) {
+		FTS_INFO("tp run in bootloader success");
+		return ret;
+	}
+
+	if (upg->func->pramboot_supported) {
+		FTS_INFO("pram supported, write pramboot and init");
+		/* pramboot */
+		if (upg->func->write_pramboot_private)
+			ret = upg->func->write_pramboot_private();
+		else
+			ret = fts_pram_write_init(upg);
+
+		if (ret < 0) {
+			FTS_ERROR("pram write_init fail");
+			return ret;
+		}
+	} else {
+		FTS_DEBUG("pram not supported, confirm in bootloader");
+		/* bootloader */
+		state = fts_fwupg_check_state(upg, FTS_RUN_IN_BOOTLOADER);
+		if (!state) {
+			FTS_ERROR("fw not in bootloader, fail");
+			return -EIO;
+		}
+	}
+
+	return 0;
+}
+
+int fts_fwupg_enter_into_boot(void)
+{
+	struct fts_upgrade *upg = fwupgrade;
+
+	if (upg->func->upgspec_version >= UPGRADE_SPEC_V_1_0)
+		return fts_fwupg_enter_into_boot_new(upg);
+
+	return fts_fwupg_enter_into_boot_old(upg);
+}
+
+/************************************************************************
+ * Name: fts_fwupg_check_flash_status
+ * Brief: read status from tp
+ * Input: flash_status: correct value from tp
+ *        retries: read retry times
+ *        retries_delay: retry delay
+ * Output:
+ * Return: return true if flash status check pass, otherwise return false
+ ***********************************************************************/
+static bool fts_fwupg_check_flash_status(
+	u16 flash_status,
+	int retries,
+	int retries_delay)
+{
+	int ret = 0;
+	int i = 0;
+	u8 cmd = 0;
+	u8 val[FTS_CMD_FLASH_STATUS_LEN] = { 0 };
+	u16 read_status = 0;
+
+	for (i = 0; i < retries; i++) {
+		cmd = FTS_CMD_FLASH_STATUS;
+		ret = fts_read(&cmd , 1, val, FTS_CMD_FLASH_STATUS_LEN);
+		read_status = (((u16)val[0]) << 8) + val[1];
+		if (flash_status == read_status) {
+			/* FTS_DEBUG("[UPGRADE]flash status ok"); */
+			return true;
+		}
+		/* FTS_DEBUG("flash status fail,ok:%04x read:%04x, retries:%d", flash_status, read_status, i); */
+		msleep(retries_delay);
+	}
+
+	return false;
+}
+
+/************************************************************************
+ * Name: fts_fwupg_erase
+ * Brief: erase flash area
+ * Input: delay - delay after erase
+ * Output:
+ * Return: return 0 if success, otherwise return error code
+ ***********************************************************************/
+int fts_fwupg_erase(u32 delay)
+{
+	int ret = 0;
+	u8 cmd = 0;
+	bool flag = false;
+
+	FTS_INFO("**********erase now**********");
+
+	/*send to erase flash*/
+	cmd = FTS_CMD_ERASE_APP;
+	ret = fts_write(&cmd, 1);
+	if (ret < 0) {
+		FTS_ERROR("erase cmd fail");
+		return ret;
+	}
+	msleep(delay);
+
+	/* read status 0xF0AA: success */
+	flag = fts_fwupg_check_flash_status(FTS_CMD_FLASH_STATUS_ERASE_OK,
+						FTS_RETRIES_REASE,
+						FTS_RETRIES_DELAY_REASE);
+	if (!flag) {
+		FTS_ERROR("ecc flash status check fail");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+/************************************************************************
+ * Name: fts_fwupg_ecc_cal
+ * Brief: calculate and get ecc from tp
+ * Input: saddr - start address need calculate ecc
+ *        len - length need calculate ecc
+ * Output:
+ * Return: return data ecc of tp if success, otherwise return error code
+ ***********************************************************************/
+int fts_fwupg_ecc_cal(u32 saddr, u32 len)
+{
+	int ret = 0;
+	u32 i = 0;
+	u32 cmdlen = FTS_CMD_ECC_CAL_LEN;
+	u8 wbuf[FTS_CMD_ECC_CAL_LEN] = { 0 };
+	u8 val[FTS_CMD_FLASH_STATUS_LEN] = { 0 };
+	int ecc = 0;
+	int ecc_len = 0;
+	u32 packet_num = 0;
+	u32 packet_len = 0;
+	u32 remainder = 0;
+	u32 addr = 0;
+	u32 offset = 0;
+	bool bflag = false;
+	struct fts_upgrade *upg = fwupgrade;
+
+	FTS_INFO( "**********read out checksum**********");
+	if ((NULL == upg) || (NULL == upg->func)) {
+		FTS_ERROR("upgrade/func is null");
+		return -EINVAL;
+	}
+
+	/* check sum init */
+	wbuf[0] = FTS_CMD_ECC_INIT;
+	ret = fts_write(wbuf, 1);
+	if (ret < 0) {
+		FTS_ERROR("ecc init cmd write fail");
+		return ret;
+	}
+
+	if (upg->func->upgspec_version >= UPGRADE_SPEC_V_1_0) {
+		packet_num = 1;
+		remainder = 0;
+		packet_len = len;
+	} else {
+		packet_num = len / FTS_MAX_LEN_ECC_CALC;
+		remainder = len % FTS_MAX_LEN_ECC_CALC;
+		if (remainder)
+			packet_num++;
+		packet_len = FTS_MAX_LEN_ECC_CALC;
+	}
+	FTS_INFO("ecc calc num:%d, remainder:%d", packet_num, remainder);
+
+	/* send commond to start checksum */
+	wbuf[0] = FTS_CMD_ECC_CAL;
+	for (i = 0; i < packet_num; i++) {
+		offset = FTS_MAX_LEN_ECC_CALC * i;
+		addr = saddr + offset;
+		wbuf[1] = BYTE_OFF_16(addr);
+		wbuf[2] = BYTE_OFF_8(addr);
+		wbuf[3] = BYTE_OFF_0(addr);
+
+		if (upg->func->upgspec_version >= UPGRADE_SPEC_V_1_0) {
+			wbuf[4] = BYTE_OFF_16(packet_len);
+			wbuf[5] = BYTE_OFF_8(packet_len);
+			wbuf[6] = BYTE_OFF_0(packet_len);
+			cmdlen = FTS_CMD_ECC_CAL_LEN;
+		} else {
+			if ((i == (packet_num - 1)) && remainder)
+				packet_len = remainder;
+			wbuf[4] = BYTE_OFF_8(packet_len);
+			wbuf[5] = BYTE_OFF_0(packet_len);
+			cmdlen = FTS_CMD_ECC_CAL_LEN - 1;
+		}
+
+		FTS_DEBUG("ecc calc startaddr:0x%04x, len:%d", addr, packet_len);
+		ret = fts_write(wbuf, cmdlen);
+		if (ret < 0) {
+			FTS_ERROR("ecc calc cmd write fail");
+			return ret;
+		}
+
+		msleep(packet_len / 256);
+
+		/* read status if check sum is finished */
+		bflag = fts_fwupg_check_flash_status(FTS_CMD_FLASH_STATUS_ECC_OK,
+						FTS_RETRIES_ECC_CAL,
+						FTS_RETRIES_DELAY_ECC_CAL);
+		if (!bflag) {
+			FTS_ERROR("ecc flash status read fail");
+			return -EIO;
+		}
+	}
+
+	ecc_len = 1;
+	if ((upg->func->fw_ecc_check_mode == ECC_CHECK_MODE_CRC16) ||
+		(upg->func->upgspec_version >= UPGRADE_SPEC_V_1_0))
+		ecc_len = 2;
+
+	/* read out check sum */
+	wbuf[0] = FTS_CMD_ECC_READ;
+	ret = fts_read(wbuf, 1, val, ecc_len);
+	if (ret < 0) {
+		FTS_ERROR( "ecc read cmd write fail");
+		return ret;
+	}
+
+	if ((upg->func->fw_ecc_check_mode == ECC_CHECK_MODE_CRC16) ||
+		(upg->func->upgspec_version >= UPGRADE_SPEC_V_1_0))
+		ecc = (int)((u16)(val[0] << 8) + val[1]);
+	else
+		ecc = (int)val[0];
+
+	return ecc;
+}
+
+/************************************************************************
+ * Name: fts_flash_write_buf
+ * Brief: write buf data to flash address
+ * Input: saddr - start address data write to flash
+ *        buf - data buffer
+ *        len - data length
+ *        delay - delay after write
+ * Output:
+ * Return: return data ecc of host if success, otherwise return error code
+ ***********************************************************************/
+int fts_flash_write_buf(
+	u32 saddr,
+	u8 *buf,
+	u32 len,
+	u32 delay)
+{
+	int ret = 0;
+	u32 i = 0;
+	u32 j = 0;
+	u32 packet_number = 0;
+	u32 packet_len = 0;
+	u32 addr = 0;
+	u32 offset = 0;
+	u32 remainder = 0;
+	u32 cmdlen = 0;
+	u8 packet_buf[FTS_FLASH_PACKET_LENGTH + FTS_CMD_WRITE_LEN] = { 0 };
+	u8 ecc_tmp = 0;
+	int ecc_in_host = 0;
+	u8 cmd = 0;
+	u8 val[FTS_CMD_FLASH_STATUS_LEN] = { 0 };
+	u16 read_status = 0;
+	u16 wr_ok = 0;
+	struct fts_upgrade *upg = fwupgrade;
+
+	FTS_INFO( "**********write data to flash**********");
+	if ((!upg) || (!upg->func || !buf || !len)) {
+		FTS_ERROR("upgrade/func/buf/len is invalid");
+		return -EINVAL;
+	}
+
+	FTS_INFO("data buf start addr=0x%x, len=0x%x", saddr, len);
+	packet_number = len / FTS_FLASH_PACKET_LENGTH;
+	remainder = len % FTS_FLASH_PACKET_LENGTH;
+	if (remainder > 0)
+		packet_number++;
+	packet_len = FTS_FLASH_PACKET_LENGTH;
+	FTS_INFO("write data, num:%d remainder:%d", packet_number, remainder);
+
+	for (i = 0; i < packet_number; i++) {
+		offset = i * FTS_FLASH_PACKET_LENGTH;
+		addr = saddr + offset;
+
+		/* last packet */
+		if ((i == (packet_number - 1)) && remainder)
+			packet_len = remainder;
+
+		if (upg->ts_data->bus_type == BUS_TYPE_SPI_V2) {
+			packet_buf[0] = FTS_CMD_SET_WFLASH_ADDR;
+			packet_buf[1] = BYTE_OFF_16(addr);
+			packet_buf[2] = BYTE_OFF_8(addr);
+			packet_buf[3] = BYTE_OFF_0(addr);
+			ret = fts_write(packet_buf, FTS_LEN_SET_ADDR);
+			if (ret < 0) {
+				FTS_ERROR("set flash address fail");
+				return ret;
+			}
+
+			packet_buf[0] = FTS_CMD_WRITE;
+			cmdlen = 1;
+		} else {
+			packet_buf[0] = FTS_CMD_WRITE;
+			packet_buf[1] = BYTE_OFF_16(addr);
+			packet_buf[2] = BYTE_OFF_8(addr);
+			packet_buf[3] = BYTE_OFF_0(addr);
+			packet_buf[4] = BYTE_OFF_8(packet_len);
+			packet_buf[5] = BYTE_OFF_0(packet_len);
+			cmdlen = 6;
+		}
+
+		for (j = 0; j < packet_len; j++) {
+			packet_buf[cmdlen + j] = buf[offset + j];
+			ecc_tmp ^= packet_buf[cmdlen + j];
+		}
+
+		ret = fts_write(packet_buf, packet_len + cmdlen);
+		if (ret < 0) {
+			FTS_ERROR("app write fail");
+			return ret;
+		}
+		mdelay(delay);
+
+		/* read status */
+		wr_ok = FTS_CMD_FLASH_STATUS_WRITE_OK + addr / packet_len;
+		for (j = 0; j < FTS_RETRIES_WRITE; j++) {
+			cmd = FTS_CMD_FLASH_STATUS;
+			ret = fts_read(&cmd , 1, val, FTS_CMD_FLASH_STATUS_LEN);
+			read_status = (((u16)val[0]) << 8) + val[1];
+			/*  FTS_INFO("%x %x", wr_ok, read_status); */
+			if (read_status == wr_ok)
+				break;
+
+			mdelay(FTS_RETRIES_DELAY_WRITE);
+		}
+	}
+
+	ecc_in_host = (int)ecc_tmp;
+	if ((upg->func->fw_ecc_check_mode == ECC_CHECK_MODE_CRC16) ||
+		(upg->func->upgspec_version >= UPGRADE_SPEC_V_1_0))
+		ecc_in_host = (int)fts_crc16_calc_host(buf, len);
+
+	return ecc_in_host;
+}
+
+/************************************************************************
+ * Name: fts_flash_read_buf
+ * Brief: read data from flash
+ * Input: saddr - start address data write to flash
+ *        buf - buffer to store data read from flash
+ *        len - read length
+ * Output:
+ * Return: return 0 if success, otherwise return error code
+ *
+ * Warning: can't call this function directly, need call in boot environment
+ ***********************************************************************/
+static int fts_flash_read_buf(u32 saddr, u8 *buf, u32 len)
+{
+	int ret = 0;
+	u32 i = 0;
+	u32 packet_number = 0;
+	u32 packet_len = 0;
+	u32 addr = 0;
+	u32 offset = 0;
+	u32 remainder = 0;
+	u8 wbuf[FTS_CMD_READ_LEN_SPI] = { 0 };
+	struct fts_upgrade *upg = fwupgrade;
+
+	if (!upg || !buf || !len) {
+		FTS_ERROR("upgrade/buf is NULL or len is 0");
+		return -EINVAL;
+	}
+
+	packet_number = len / FTS_FLASH_PACKET_LENGTH;
+	remainder = len % FTS_FLASH_PACKET_LENGTH;
+	if (remainder > 0) {
+		packet_number++;
+	}
+	packet_len = FTS_FLASH_PACKET_LENGTH;
+	FTS_INFO("read packet_number:%d, remainder:%d", packet_number, remainder);
+
+
+	for (i = 0; i < packet_number; i++) {
+		offset = i * FTS_FLASH_PACKET_LENGTH;
+		addr = saddr + offset;
+		/* last packet */
+		if ((i == (packet_number - 1)) && remainder)
+			packet_len = remainder;
+
+		if (upg->ts_data->bus_type == BUS_TYPE_I2C) {
+			wbuf[0] = FTS_CMD_READ;
+			wbuf[1] = BYTE_OFF_16(addr);
+			wbuf[2] = BYTE_OFF_8(addr);
+			wbuf[3] = BYTE_OFF_0(addr);
+			ret = fts_write(wbuf, FTS_CMD_READ_LEN);
+			if (ret < 0) {
+				FTS_ERROR("pram/bootloader write 03 command fail");
+				return ret;
+			}
+
+			msleep(FTS_CMD_READ_DELAY); /* must wait, otherwise read wrong data */
+			ret = fts_read(NULL, 0, buf + offset, packet_len);
+			if (ret < 0) {
+				FTS_ERROR("pram/bootloader read 03 command fail");
+				return ret;
+			}
+		} else if (upg->ts_data->bus_type == BUS_TYPE_SPI_V2) {
+			wbuf[0] = FTS_CMD_SET_RFLASH_ADDR;
+			wbuf[1] = BYTE_OFF_16(addr);
+			wbuf[2] = BYTE_OFF_8(addr);
+			wbuf[3] = BYTE_OFF_0(addr);
+			ret = fts_write(wbuf, FTS_LEN_SET_ADDR);
+			if (ret < 0) {
+				FTS_ERROR("set flash address fail");
+				return ret;
+			}
+
+			msleep(FTS_CMD_READ_DELAY);
+			wbuf[0] = FTS_CMD_READ;
+			ret = fts_read(wbuf, 1, buf + offset, packet_len);
+			if (ret < 0) {
+				FTS_ERROR("pram/bootloader read 03(SPI_V2) command fail");
+				return ret;
+			}
+		} else if (upg->ts_data->bus_type == BUS_TYPE_SPI) {
+			wbuf[0] = FTS_CMD_READ;
+			wbuf[1] = BYTE_OFF_16(addr);
+			wbuf[2] = BYTE_OFF_8(addr);
+			wbuf[3] = BYTE_OFF_0(addr);
+			wbuf[4] = BYTE_OFF_8(packet_len);
+			wbuf[5] = BYTE_OFF_0(packet_len);
+			ret = fts_read(wbuf, FTS_CMD_READ_LEN_SPI,
+						buf + offset, packet_len);
+			if (ret < 0) {
+				FTS_ERROR("pram/bootloader read 03(SPI) command fail");
+				return ret;
+			}
+		}
+	}
+
+	return 0;
+}
+
+/************************************************************************
+ * Name: fts_flash_read
+ * Brief:
+ * Input:  addr  - address of flash
+ *         len   - length of read
+ * Output: buf   - data read from flash
+ * Return: return 0 if success, otherwise return error code
+ ***********************************************************************/
+static int fts_flash_read(u32 addr, u8 *buf, u32 len)
+{
+	int ret = 0;
+
+	FTS_INFO("***********read flash***********");
+	if ((NULL == buf) || (0 == len)) {
+		FTS_ERROR("buf is NULL or len is 0");
+		return -EINVAL;
+	}
+
+	ret = fts_fwupg_enter_into_boot();
+	if (ret < 0) {
+		FTS_ERROR("enter into pramboot/bootloader fail");
+		goto read_flash_err;
+	}
+
+	ret = fts_flash_read_buf(addr, buf, len);
+	if (ret < 0) {
+		FTS_ERROR("read flash fail");
+		goto read_flash_err;
+	}
+
+read_flash_err:
+	/* reset to normal boot */
+	ret = fts_fwupg_reset_in_boot();
+	if (ret < 0)
+		FTS_ERROR("reset to normal boot fail");
+
+	return ret;
+}
+
+int fts_enter_test_environment(bool test_state)
+{
+	return 0;
+}
+#if FTS_AUTO_LIC_UPGRADE_EN
+static int fts_lic_get_vid_in_tp(u16 *vid)
+{
+	int ret = 0;
+	u8 val[2] = { 0 };
+
+	if (NULL == vid) {
+		FTS_ERROR("vid is NULL");
+		return -EINVAL;
+	}
+
+	ret = fts_read_reg(FTS_REG_VENDOR_ID, &val[0]);
+	if (fts_data->ic_info.is_incell)
+		ret = fts_read_reg(FTS_REG_MODULE_ID, &val[1]);
+	if (ret < 0) {
+		FTS_ERROR("read vid from tp fail");
+		return ret;
+	}
+
+	*vid = *(u16 *)val;
+	return 0;
+}
+
+static int fts_lic_get_vid_in_host(struct fts_upgrade *upg, u16 *vid)
+{
+	u8 val[2] = { 0 };
+	u8 *licbuf = NULL;
+	u32 conf_saddr = 0;
+
+	if (!upg || !upg->func || !upg->lic || !vid) {
+		FTS_ERROR("upgrade/func/get_hlic_ver/lic/vid is null");
+		return -EINVAL;
+	}
+
+	if (upg->lic_length < FTS_MAX_LEN_SECTOR) {
+		FTS_ERROR("lic length(%x) fail", upg->lic_length);
+		return -EINVAL;
+	}
+
+	licbuf  = upg->lic;
+	conf_saddr = upg->func->fwcfgoff;
+	val[0] = licbuf[conf_saddr + FTS_CONIFG_VENDORID_OFF];
+	if (fts_data->ic_info.is_incell)
+		val[1] = licbuf[conf_saddr + FTS_CONIFG_MODULEID_OFF];
+
+	*vid = *(u16 *)val;
+	return 0;
+}
+
+static int fts_lic_get_ver_in_tp(u8 *ver)
+{
+	int ret = 0;
+
+	if (NULL == ver) {
+		FTS_ERROR("ver is NULL");
+		return -EINVAL;
+	}
+
+	ret = fts_read_reg(FTS_REG_LIC_VER, ver);
+	if (ret < 0) {
+		FTS_ERROR("read lcd initcode ver from tp fail");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int fts_lic_get_ver_in_host(struct fts_upgrade *upg, u8 *ver)
+{
+	int ret = 0;
+
+	if (!upg || !upg->func || !upg->func->get_hlic_ver || !upg->lic) {
+		FTS_ERROR("upgrade/func/get_hlic_ver/lic is null");
+		return -EINVAL;
+	}
+
+	ret = upg->func->get_hlic_ver(upg->lic);
+	if (ret < 0) {
+		FTS_ERROR("get host lcd initial code version fail");
+		return ret;
+	}
+
+	*ver = (u8)ret;
+	return ret;
+}
+
+static bool fts_lic_need_upgrade(struct fts_upgrade *upg)
+{
+	int ret = 0;
+	u8 initcode_ver_in_tp = 0;
+	u8 initcode_ver_in_host = 0;
+	u16 vid_in_tp = 0;
+	u16 vid_in_host = 0;
+	bool fwvalid = false;
+
+	fwvalid = fts_fwupg_check_fw_valid();
+	if ( !fwvalid) {
+		FTS_INFO("fw is invalid, no upgrade lcd init code");
+		return false;
+	}
+
+	ret = fts_lic_get_vid_in_host(upg, &vid_in_host);
+	if (ret < 0) {
+		FTS_ERROR("vendor id in host invalid");
+		return false;
+	}
+
+	ret = fts_lic_get_vid_in_tp(&vid_in_tp);
+	if (ret < 0) {
+		FTS_ERROR("vendor id in tp invalid");
+		return false;
+	}
+
+	FTS_DEBUG("vid in tp:0x%04x, host:0x%04x", vid_in_tp, vid_in_host);
+	if (vid_in_tp != vid_in_host) {
+		FTS_INFO("vendor id in tp&host are different, no upgrade lic");
+		return false;
+	}
+
+	ret = fts_lic_get_ver_in_host(upg, &initcode_ver_in_host);
+	if (ret < 0) {
+		FTS_ERROR("init code in host invalid");
+		return false;
+	}
+
+	ret = fts_lic_get_ver_in_tp(&initcode_ver_in_tp);
+	if (ret < 0) {
+		FTS_ERROR("read reg0xE4 fail");
+		return false;
+	}
+
+	FTS_DEBUG("lcd initial code version in tp:%x, host:%x",
+		initcode_ver_in_tp, initcode_ver_in_host);
+	if (0xA5 == initcode_ver_in_tp) {
+		FTS_INFO("lcd init code ver is 0xA5, don't upgade init code");
+		return false;
+	} else if (0xFF == initcode_ver_in_tp) {
+		FTS_DEBUG("lcd init code in tp is invalid, need upgrade init code");
+		return true;
+	} else if (initcode_ver_in_tp < initcode_ver_in_host)
+		return true;
+	else
+		return false;
+}
+
+static int fts_lic_upgrade(struct fts_upgrade *upg)
+{
+	int ret = 0;
+	bool hlic_upgrade = false;
+	int upgrade_count = 0;
+	u8 ver = 0;
+
+	FTS_INFO("lcd initial code auto upgrade function");
+	if ((!upg) || (!upg->func) || (!upg->func->lic_upgrade)) {
+		FTS_ERROR("lcd upgrade function is null");
+		return -EINVAL;
+	}
+
+	hlic_upgrade = fts_lic_need_upgrade(upg);
+	FTS_INFO("lcd init code upgrade flag:%d", hlic_upgrade);
+	if (hlic_upgrade) {
+		FTS_INFO("lcd initial code need upgrade, upgrade begin...");
+		do {
+			FTS_INFO("lcd initial code upgrade times:%d", upgrade_count);
+			upgrade_count++;
+
+			ret = upg->func->lic_upgrade(upg->lic, upg->lic_length);
+			if (ret < 0) {
+				fts_fwupg_reset_in_boot();
+			} else {
+				fts_lic_get_ver_in_tp(&ver);
+				FTS_INFO("success upgrade to lcd initcode ver:%02x", ver);
+				break;
+			}
+		} while (upgrade_count < 2);
+	} else {
+		FTS_INFO("lcd initial code don't need upgrade");
+	}
+
+	return ret;
+}
+#endif /* FTS_AUTO_LIC_UPGRADE_EN */
+
+
+static int fts_param_get_ver_in_tp(u8 *ver)
+{
+	int ret = 0;
+
+	if (NULL == ver) {
+		FTS_ERROR("ver is NULL");
+		return -EINVAL;
+	}
+
+	ret = fts_read_reg(FTS_REG_IDE_PARA_VER_ID, ver);
+	if (ret < 0) {
+		FTS_ERROR("read fw param ver from tp fail");
+		return ret;
+	}
+
+	if ((0x00 == *ver) || (0xFF == *ver)) {
+		FTS_INFO("param version in tp invalid");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int fts_param_get_ver_in_host(struct fts_upgrade *upg, u8 *ver)
+{
+	if ((!upg) || (!upg->func) || (!upg->fw) || (!ver)) {
+		FTS_ERROR("fts_data/upgrade/func/fw/ver is NULL");
+		return -EINVAL;
+	}
+
+	if (upg->fw_length < upg->func->paramcfgveroff) {
+		FTS_ERROR("fw len(%x) < paramcfg ver offset(%x)",
+			upg->fw_length, upg->func->paramcfgveroff);
+		return -EINVAL;
+	}
+
+	FTS_INFO("fw paramcfg version offset:%x", upg->func->paramcfgveroff);
+	*ver = upg->fw[upg->func->paramcfgveroff];
+
+	if ((0x00 == *ver) || (0xFF == *ver)) {
+		FTS_INFO("param version in host invalid");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+/*
+ * return: < 0 : error
+ *         == 0: no ide
+ *         == 1: ide
+ */
+static int fts_param_ide_in_host(struct fts_upgrade *upg)
+{
+	u32 off = 0;
+
+	if ((!upg) || (!upg->func) || (!upg->fw)) {
+		FTS_ERROR("fts_data/upgrade/func/fw is NULL");
+		return -EINVAL;
+	}
+
+	if (upg->fw_length < upg->func->paramcfgoff + FTS_FW_IDE_SIG_LEN) {
+		FTS_INFO("fw len(%x) < paramcfg offset(%x), no IDE",
+			upg->fw_length, upg->func->paramcfgoff + FTS_FW_IDE_SIG_LEN);
+		return 0;
+	}
+
+	off = upg->func->paramcfgoff;
+	if (0 == memcmp(&upg->fw[off], FTS_FW_IDE_SIG, FTS_FW_IDE_SIG_LEN)) {
+		FTS_INFO("fw in host is IDE version");
+		return 1;
+	}
+
+	FTS_INFO("fw in host isn't IDE version");
+	return 0;
+}
+
+/*
+ * return: < 0 : error
+ *         0   : no ide
+ *         1   : ide
+ */
+static int fts_param_ide_in_tp(u8 *val)
+{
+	int ret = 0;
+
+	ret = fts_read_reg(FTS_REG_IDE_PARA_STATUS, val);
+	if (ret < 0) {
+		FTS_ERROR("read IDE PARAM STATUS in tp fail");
+		return ret;
+	}
+
+	if ((*val != 0xFF) && ((*val & 0x80) == 0x80)) {
+		FTS_INFO("fw in tp is IDE version");
+		return 1;
+	}
+
+	FTS_INFO("fw in tp isn't IDE version");
+	return 0;
+}
+
+/************************************************************************
+ * fts_param_need_upgrade - check fw paramcfg need upgrade or not
+ *
+ * Return:  < 0 : error if paramcfg need upgrade
+ *          0   : no need upgrade
+ *          1   : need upgrade app + param
+ *          2   : need upgrade param
+ ***********************************************************************/
+static int fts_param_need_upgrade(struct fts_upgrade *upg)
+{
+	int ret = 0;
+	u8 val = 0;
+	int ide_in_host = 0;
+	int ide_in_tp = 0;
+	u8 ver_in_host = 0;
+	u8 ver_in_tp = 0;
+	bool fwvalid = false;
+
+	fwvalid = fts_fwupg_check_fw_valid();
+	if ( !fwvalid) {
+		FTS_INFO("fw is invalid, upgrade app+param");
+		return 1;
+	}
+
+	ide_in_host = fts_param_ide_in_host(upg);
+	if (ide_in_host < 0) {
+		FTS_INFO("fts_param_ide_in_host fail");
+		return ide_in_host;
+	}
+
+	ide_in_tp = fts_param_ide_in_tp(&val);
+	if (ide_in_tp < 0) {
+		FTS_INFO("fts_param_ide_in_tp fail");
+		return ide_in_tp;
+	}
+
+	if ((0 == ide_in_host) && (0 == ide_in_tp)) {
+		FTS_INFO("fw in host&tp are both no ide");
+		return 0;
+	} else if (ide_in_host != ide_in_tp) {
+		FTS_INFO("fw in host&tp not equal, need upgrade app+param");
+		return 1;
+	} else if ((1 == ide_in_host) && (1 == ide_in_tp)) {
+		FTS_INFO("fw in host&tp are both ide");
+		if ((val & 0x7F) != 0x00) {
+			FTS_INFO("param invalid, need upgrade param");
+			return 2;
+		}
+
+		ret = fts_param_get_ver_in_host(upg, &ver_in_host);
+		if (ret < 0) {
+			FTS_ERROR("param version in host invalid");
+		return ret;
+		}
+
+		ret = fts_param_get_ver_in_tp(&ver_in_tp);
+		if (ret < 0) {
+			FTS_ERROR("get IDE param ver in tp fail");
+		return ret;
+		}
+
+		FTS_INFO("fw paramcfg version in tp:%x, host:%x",
+			ver_in_tp, ver_in_host);
+		if (ver_in_tp != ver_in_host) {
+			return 2;
+		}
+	}
+
+	return 0;
+}
+
+static int fts_fwupg_get_ver_in_tp(u8 *ver)
+{
+	int ret = 0;
+
+	if (NULL == ver) {
+		FTS_ERROR("ver is NULL");
+		return -EINVAL;
+	}
+
+	ret = fts_read_reg(FTS_REG_FW_VER, ver);
+	if (ret < 0) {
+		FTS_ERROR("read fw ver from tp fail");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int fts_fwupg_get_ver_in_host(struct fts_upgrade *upg, u8 *ver)
+{
+	if ((!upg) || (!upg->func) || (!upg->fw) || (!ver)) {
+		FTS_ERROR("fts_data/upgrade/func/fw/ver is NULL");
+		return -EINVAL;
+	}
+
+	if (upg->fw_length < upg->func->fwveroff) {
+		FTS_ERROR("fw len(0x%0x) < fw ver offset(0x%x)",
+			upg->fw_length, upg->func->fwveroff);
+		return -EINVAL;
+	}
+
+	FTS_INFO("fw version offset:0x%x", upg->func->fwveroff);
+	*ver = upg->fw[upg->func->fwveroff];
+	return 0;
+}
+
+static bool fts_fwupg_need_upgrade(struct fts_upgrade *upg)
+{
+	int ret = 0;
+	bool fwvalid = false;
+	u8 fw_ver_in_host = 0;
+	u8 fw_ver_in_tp = 0;
+
+	fwvalid = fts_fwupg_check_fw_valid();
+	if (fwvalid) {
+		ret = fts_fwupg_get_ver_in_host(upg, &fw_ver_in_host);
+		if (ret < 0) {
+			FTS_ERROR("get fw ver in host fail");
+			return false;
+		}
+
+		ret = fts_fwupg_get_ver_in_tp(&fw_ver_in_tp);
+		if (ret < 0) {
+			FTS_ERROR("get fw ver in tp fail");
+			return false;
+		}
+
+		FTS_INFO("fw version in tp:%x, host:%x", fw_ver_in_tp, fw_ver_in_host);
+		if (fw_ver_in_tp != fw_ver_in_host) {
+			return true;
+		}
+	} else {
+		FTS_INFO("fw invalid, need upgrade fw");
+		return true;
+	}
+
+	return false;
+}
+
+/************************************************************************
+ * Name: fts_fw_upgrade
+ * Brief: fw upgrade main entry, run in following steps
+ *        1. check fw version(A6), not equal, will upgrade app(+param)
+ *        2. if fw version equal, will check ide, will upgrade app(+param)
+ *        in the follow situation
+ *          a. host&tp IDE's type are not equal, will upgrade app+param
+ *          b. host&tp are both IDE's type, and param's version are not
+ *          equal, will upgrade param
+ * Input:
+ * Output:
+ * Return: return 0 if success, otherwise return error code
+ ***********************************************************************/
+int fts_fwupg_upgrade(struct fts_upgrade *upg)
+{
+	int ret = 0;
+	bool upgrade_flag = false;
+	int upgrade_count = 0;
+	u8 ver = 0;
+
+	FTS_INFO("fw auto upgrade function");
+	if ((NULL == upg) || (NULL == upg->func)) {
+		FTS_ERROR("upg/upg->func is null");
+		return -EINVAL;
+	}
+
+	upgrade_flag = fts_fwupg_need_upgrade(upg);
+	FTS_INFO("fw upgrade flag:%d", upgrade_flag);
+	do {
+		upgrade_count++;
+		if (upgrade_flag) {
+			FTS_INFO("upgrade fw app(times:%d)", upgrade_count);
+			if (upg->func->upgrade) {
+				ret = upg->func->upgrade(upg->fw, upg->fw_length);
+				if (ret < 0) {
+					fts_fwupg_reset_in_boot();
+				} else {
+					fts_fwupg_get_ver_in_tp(&ver);
+					FTS_INFO("success upgrade to fw version %02x", ver);
+					break;
+				}
+			} else {
+				FTS_ERROR("upgrade func/upgrade is null, return immediately");
+				ret = -ENODATA;
+				break;
+			}
+		} else {
+			if (upg->func->param_upgrade) {
+				ret = fts_param_need_upgrade(upg);
+				if (ret <= 0) {
+					FTS_INFO("param don't need upgrade");
+					break;
+				} else if (1 == ret) {
+					FTS_INFO("force upgrade fw app(times:%d)", upgrade_count);
+					if (upg->func->upgrade) {
+						ret = upg->func->upgrade(upg->fw, upg->fw_length);
+						if (ret < 0) {
+							fts_fwupg_reset_in_boot();
+						} else {
+							break;
+						}
+					}
+				} else if (2 == ret) {
+					FTS_INFO("upgrade param area(times:%d)", upgrade_count);
+					ret = upg->func->param_upgrade(upg->fw, upg->fw_length);
+					if (ret < 0) {
+						fts_fwupg_reset_in_boot();
+					} else {
+						fts_param_get_ver_in_tp(&ver);
+						FTS_INFO("success upgrade to fw param version %02x", ver);
+						break;
+					}
+				} else
+					break;
+			} else {
+				break;
+			}
+		}
+	} while (upgrade_count < 2);
+
+	return ret;
+}
+
+/************************************************************************
+ * fts_fwupg_auto_upgrade - upgrade main entry
+ ***********************************************************************/
+static void fts_fwupg_auto_upgrade(struct fts_upgrade *upg)
+{
+	int ret = 0;
+
+	FTS_INFO("********************FTS enter upgrade********************");
+	if (!upg || !upg->ts_data) {
+		FTS_ERROR("upg/ts_data is null");
+		return ;
+	}
+
+	ret = fts_fwupg_upgrade(upg);
+	if (ret < 0)
+		FTS_ERROR("**********tp fw(app/param) upgrade failed**********");
+	else
+		FTS_INFO("**********tp fw(app/param) no upgrade/upgrade success**********");
+
+#if FTS_AUTO_LIC_UPGRADE_EN
+	ret = fts_lic_upgrade(upg);
+	if (ret < 0)
+		FTS_ERROR("**********lcd init code upgrade failed**********");
+	else
+		FTS_INFO("**********lcd init code no upgrade/upgrade success**********");
+#endif
+
+	FTS_INFO("********************FTS exit upgrade********************");
+}
+
+static int fts_fwupg_get_vendorid(struct fts_upgrade *upg, int *vid)
+{
+	int ret = 0;
+	bool fwvalid = false;
+	u8 vendor_id = 0;
+	u8 module_id = 0;
+	u32 fwcfg_addr = 0;
+	u8 cmd = 0;
+	u8 cfgbuf[FTS_HEADER_LEN] = { 0 };
+
+	FTS_INFO("read vendor id from tp");
+	if ((!upg) || (!upg->func) || (!upg->ts_data) || (!vid)) {
+		FTS_ERROR("upgrade/func/ts_data/vid is null");
+		return -EINVAL;
+	}
+
+	fwvalid = fts_fwupg_check_fw_valid();
+	if (fwvalid) {
+		ret = fts_read_reg(FTS_REG_VENDOR_ID, &vendor_id);
+		if (upg->ts_data->ic_info.is_incell)
+			ret = fts_read_reg(FTS_REG_MODULE_ID, &module_id);
+	} else {
+		if (upg->func->upgspec_version >= UPGRADE_SPEC_V_1_0) {
+			cmd = FTS_CMD_READ_FW_CONF;
+			ret = fts_read(&cmd, 1, cfgbuf, FTS_HEADER_LEN);
+		} else {
+			fwcfg_addr =  upg->func->fwcfgoff;
+			ret = fts_flash_read(fwcfg_addr, cfgbuf, FTS_HEADER_LEN);
+		}
+
+		if ((cfgbuf[FTS_CONIFG_VENDORID_OFF] +
+			cfgbuf[FTS_CONIFG_VENDORID_OFF + 1]) == 0xFF)
+			vendor_id = cfgbuf[FTS_CONIFG_VENDORID_OFF];
+		if (upg->ts_data->ic_info.is_incell) {
+			if ((cfgbuf[FTS_CONIFG_MODULEID_OFF] +
+				cfgbuf[FTS_CONIFG_MODULEID_OFF + 1]) == 0xFF)
+				module_id = cfgbuf[FTS_CONIFG_MODULEID_OFF];
+		}
+	}
+
+	if (ret < 0) {
+		FTS_ERROR("fail to get vendor id from tp");
+		return ret;
+	}
+
+	*vid = (int)((module_id << 8) + vendor_id);
+	return 0;
+}
+
+static int fts_fwupg_get_module_info(struct fts_upgrade *upg)
+{
+	int ret = 0;
+	int i = 0;
+	struct upgrade_module *info = &module_list[0];
+
+	if (!upg || !upg->ts_data) {
+		FTS_ERROR("upg/ts_data is null");
+		return -EINVAL;
+	}
+
+	if (FTS_GET_MODULE_NUM > 1) {
+		/* support multi modules, must read correct module id(vendor id) */
+		ret = fts_fwupg_get_vendorid(upg, &upg->module_id);
+		if (ret < 0) {
+			FTS_ERROR("get vendor id failed");
+			return ret;
+		}
+		FTS_INFO("module id:%04x", upg->module_id);
+		for (i = 0; i < FTS_GET_MODULE_NUM; i++) {
+			info = &module_list[i];
+			if (upg->module_id == info->id) {
+				FTS_INFO("module id match, get module info pass");
+				break;
+			}
+		}
+		if (i >= FTS_GET_MODULE_NUM) {
+			info = &module_list[0];
+			FTS_ERROR("no module id match, default to use first module");
+		}
+	}
+
+	upg->module_info = info;
+	return 0;
+}
+
+static int fts_get_fw_file_via_request_firmware(struct fts_upgrade *upg)
+{
+	int ret = 0;
+	const struct firmware *fw = NULL;
+	u8 *tmpbuf = NULL;
+	char fwname[FILE_NAME_LENGTH] = { 0 };
+
+	if (!upg || !upg->ts_data || !upg->ts_data->dev) {
+		FTS_ERROR("upg/ts_data/dev is null");
+		return -EINVAL;
+	}
+
+	if (upg->ts_data->pdata->type == _FT3658U)
+		snprintf(fwname, FILE_NAME_LENGTH, "%s%s_ft3658.bin",
+				FTS_FW_NAME_PREX_WITH_REQUEST,
+				upg->module_info->vendor_name);
+	else
+		snprintf(fwname, FILE_NAME_LENGTH, "%s%s.bin",
+				FTS_FW_NAME_PREX_WITH_REQUEST,
+				upg->module_info->vendor_name);
+
+	ret = request_firmware(&fw, fwname, upg->ts_data->dev);
+	if (ret == 0) {
+		FTS_INFO("firmware(%s) request successfully", fwname);
+		tmpbuf = vmalloc(fw->size);
+		if (NULL == tmpbuf) {
+			FTS_ERROR("fw buffer vmalloc fail");
+			ret = -ENOMEM;
+		} else {
+			memcpy(tmpbuf, fw->data, fw->size);
+			upg->fw = tmpbuf;
+			upg->fw_length = fw->size;
+			upg->fw_from_request = 1;
+		}
+	} else {
+		FTS_INFO("firmware(%s) request fail,ret=%d", fwname, ret);
+	}
+
+	if (fw != NULL) {
+		release_firmware(fw);
+		fw = NULL;
+	}
+
+	return ret;
+}
+
+static int fts_get_fw_file_via_i(struct fts_upgrade *upg)
+{
+	upg->fw = upg->module_info->fw_file;
+	upg->fw_length = upg->module_info->fw_len;
+	upg->fw_from_request = 0;
+
+	return 0;
+}
+
+/*****************************************************************************
+ *  Name: fts_fwupg_get_fw_file
+ *  Brief: get fw image/file,
+ *         If support muitl modules, please set FTS_GET_MODULE_NUM, and FTS_-
+ *         MODULE_ID/FTS_MODULE_NAME;
+ *         If get fw via .i file, please set FTS_FW_REQUEST_SUPPORT=0, and F-
+ *         TS_MODULE_ID; will use module id to distingwish different modules;
+ *         If get fw via reques_firmware(), please set FTS_FW_REQUEST_SUPPORT
+ *         =1, and FTS_MODULE_NAME; fw file name will be composed of "focalt-
+ *         ech_ts_fw_" & FTS_VENDOR_NAME;
+ *
+ *         If have flash, module_id=vendor_id, If non-flash,module_id need
+ *         transfer from LCD driver(gpio or lcm_id or ...);
+ *  Input:
+ *  Output:
+ *  Return: return 0 if success, otherwise return error code
+ *****************************************************************************/
+static int fts_fwupg_get_fw_file(struct fts_upgrade *upg)
+{
+	int ret = 0;
+	bool get_fw_i_flag = false;
+
+	FTS_DEBUG("get upgrade fw file");
+	if (!upg || !upg->ts_data) {
+		FTS_ERROR("upg/ts_data is null");
+		return -EINVAL;
+	}
+
+	ret = fts_fwupg_get_module_info(upg);
+	if ((ret < 0) || (!upg->module_info)) {
+		FTS_ERROR("get module info fail");
+		return ret;
+	}
+
+	if (FTS_FW_REQUEST_SUPPORT) {
+		ret = fts_get_fw_file_via_request_firmware(upg);
+		if (ret != 0) {
+			get_fw_i_flag = true;
+		}
+	} else {
+		get_fw_i_flag = true;
+	}
+
+	if (get_fw_i_flag) {
+		ret = fts_get_fw_file_via_i(upg);
+	}
+
+	upg->lic = upg->fw;
+	upg->lic_length = upg->fw_length;
+
+	FTS_INFO("upgrade fw file len:%d", upg->fw_length);
+	if ((upg->fw_length < FTS_MIN_LEN)
+		|| (upg->fw_length > FTS_MAX_LEN_FILE)) {
+		FTS_ERROR("fw file len(%d) fail", upg->fw_length);
+		return -ENODATA;
+	}
+
+	return ret;
+}
+
+static void fts_fwupg_init_ic_detail(struct fts_upgrade *upg)
+{
+	if (upg && upg->func && upg->func->init) {
+		upg->func->init(upg->fw, upg->fw_length);
+	}
+}
+
+/*****************************************************************************
+ *  Name: fts_fwupg_work
+ *  Brief: 1. get fw image/file
+ *         2. ic init if have
+ *         3. call upgrade main function(fts_fwupg_auto_upgrade)
+ *  Input:
+ *  Output:
+ *  Return:
+ *****************************************************************************/
+static void fts_fwupg_work(struct work_struct *work)
+{
+	int ret = 0;
+	struct fts_upgrade *upg = fwupgrade;
+
+#if !FTS_AUTO_UPGRADE_EN
+	FTS_INFO("FTS_AUTO_UPGRADE_EN is disabled, not upgrade when power on");
+	return ;
+#endif
+
+	FTS_INFO("fw upgrade work function");
+	if (!upg || !upg->ts_data) {
+		FTS_ERROR("upg/ts_data is null");
+		return ;
+	}
+
+	upg->ts_data->fw_loading = 1;
+	fts_irq_disable();
+#if FTS_ESDCHECK_EN
+	fts_esdcheck_switch(DISABLE);
+#endif
+
+	/* get fw */
+	ret = fts_fwupg_get_fw_file(upg);
+	if (ret < 0) {
+		FTS_ERROR("get file fail, can't upgrade");
+	} else {
+		/* ic init if have */
+		fts_fwupg_init_ic_detail(upg);
+		/* run auto upgrade */
+		fts_fwupg_auto_upgrade(upg);
+	}
+
+#if FTS_ESDCHECK_EN
+	fts_esdcheck_switch(ENABLE);
+#endif
+	fts_irq_enable();
+	upg->ts_data->fw_loading = 0;
+}
+
+int fts_fwupg_init(struct fts_ts_data *ts_data)
+{
+	int i = 0;
+	int j = 0;
+	int ic_stype = 0;
+	struct upgrade_func *func = upgrade_func_list[0];
+	int func_count = sizeof(upgrade_func_list) / sizeof(upgrade_func_list[0]);
+
+	FTS_INFO("fw upgrade init function");
+
+	if (!ts_data || !ts_data->ts_workqueue) {
+		FTS_ERROR("ts_data/workqueue is NULL, can't run upgrade function");
+		return -EINVAL;
+	}
+
+	if (0 == func_count) {
+		FTS_ERROR("no upgrade function in tp driver");
+		return -ENODATA;
+	}
+
+	fwupgrade = (struct fts_upgrade *)kzalloc(sizeof(*fwupgrade), GFP_KERNEL);
+	if (NULL == fwupgrade) {
+		FTS_ERROR("malloc memory for upgrade fail");
+		return -ENOMEM;
+	}
+
+	ic_stype = ts_data->ic_info.ids.type;
+	if (1 == func_count) {
+		fwupgrade->func = func;
+	} else {
+		for (i = 0; i < func_count; i++) {
+			func = upgrade_func_list[i];
+			for (j = 0; j < FTX_MAX_COMPATIBLE_TYPE; j++) {
+				if (0 == func->ctype[j])
+					break;
+				else if (func->ctype[j] == ic_stype) {
+					FTS_INFO("match upgrade function,type:%x", (int)func->ctype[j]);
+					fwupgrade->func = func;
+				}
+			}
+		}
+	}
+
+	if (NULL == fwupgrade->func) {
+		FTS_ERROR("no upgrade function match, can't upgrade");
+		kfree(fwupgrade);
+		fwupgrade = NULL;
+		return -ENODATA;
+	}
+
+	fwupgrade->ts_data = ts_data;
+	INIT_WORK(&ts_data->fwupg_work, fts_fwupg_work);
+	queue_work(ts_data->ts_workqueue, &ts_data->fwupg_work);
+
+	return 0;
+}
+
+int fts_fwupg_exit(struct fts_ts_data *ts_data)
+{
+	FTS_FUNC_ENTER();
+	if (fwupgrade) {
+		if (fwupgrade->fw_from_request) {
+			vfree(fwupgrade->fw);
+			fwupgrade->fw = NULL;
+		}
+
+		kfree(fwupgrade);
+		fwupgrade = NULL;
+	}
+	FTS_FUNC_EXIT();
+	return 0;
+}

+ 216 - 0
qcom/opensource/touch-drivers/focaltech_touch/focaltech_flash.h

@@ -0,0 +1,216 @@
+/************************************************************************
+* Copyright (C) 2012-2019, Focaltech Systems (R)£¬All Rights Reserved.
+*
+* File Name: focaltech_flash.h
+*
+* Author: Focaltech Driver Team
+*
+* Created: 2016-08-07
+*
+* Abstract:
+*
+************************************************************************/
+#ifndef __LINUX_FOCALTECH_FLASH_H__
+#define __LINUX_FOCALTECH_FLASH_H__
+
+/*****************************************************************************
+* 1.Included header files
+*****************************************************************************/
+#include "focaltech_core.h"
+
+/*****************************************************************************
+* Private constant and macro definitions using #define
+*****************************************************************************/
+#define FTS_CMD_RESET                               0x07
+#define FTS_ROMBOOT_CMD_SET_PRAM_ADDR               0xAD
+#define FTS_ROMBOOT_CMD_SET_PRAM_ADDR_LEN           4
+#define FTS_ROMBOOT_CMD_WRITE                       0xAE
+#define FTS_ROMBOOT_CMD_START_APP                   0x08
+#define FTS_DELAY_PRAMBOOT_START                    100
+#define FTS_ROMBOOT_CMD_ECC                         0xCC
+#define FTS_PRAM_SADDR                              0x000000
+#define FTS_DRAM_SADDR                              0xD00000
+
+#define FTS_CMD_READ                                0x03
+#define FTS_CMD_READ_DELAY                          1
+#define FTS_CMD_READ_LEN                            4
+#define FTS_CMD_READ_LEN_SPI                        6
+#define FTS_CMD_FLASH_TYPE                          0x05
+#define FTS_CMD_FLASH_MODE                          0x09
+#define FLASH_MODE_WRITE_FLASH_VALUE                0x0A
+#define FLASH_MODE_UPGRADE_VALUE                    0x0B
+#define FLASH_MODE_LIC_VALUE                        0x0C
+#define FLASH_MODE_PARAM_VALUE                      0x0D
+#define FTS_CMD_ERASE_APP                           0x61
+#define FTS_REASE_APP_DELAY                         1350
+#define FTS_ERASE_SECTOR_DELAY                      60
+#define FTS_RETRIES_REASE                           50
+#define FTS_RETRIES_DELAY_REASE                     400
+#define FTS_CMD_FLASH_STATUS                        0x6A
+#define FTS_CMD_FLASH_STATUS_LEN                    2
+#define FTS_CMD_FLASH_STATUS_NOP                    0x0000
+#define FTS_CMD_FLASH_STATUS_ECC_OK                 0xF055
+#define FTS_CMD_FLASH_STATUS_ERASE_OK               0xF0AA
+#define FTS_CMD_FLASH_STATUS_WRITE_OK               0x1000
+#define FTS_CMD_ECC_INIT                            0x64
+#define FTS_CMD_ECC_CAL                             0x65
+#define FTS_CMD_ECC_CAL_LEN                         7
+#define FTS_RETRIES_ECC_CAL                         10
+#define FTS_RETRIES_DELAY_ECC_CAL                   50
+#define FTS_CMD_ECC_READ                            0x66
+#define FTS_CMD_SET_WFLASH_ADDR                     0xAB
+#define FTS_CMD_SET_RFLASH_ADDR                     0xAC
+#define FTS_LEN_SET_ADDR                            4
+#define FTS_CMD_DATA_LEN                            0xB0
+#define FTS_CMD_APP_DATA_LEN_INCELL                 0x7A
+#define FTS_CMD_DATA_LEN_LEN                        4
+#define FTS_CMD_WRITE                               0xBF
+#define FTS_RETRIES_WRITE                           100
+#define FTS_RETRIES_DELAY_WRITE                     1
+#define FTS_CMD_WRITE_LEN                           6
+#define FTS_DELAY_READ_ID                           20
+#define FTS_DELAY_UPGRADE_RESET                     80
+#define PRAMBOOT_MIN_SIZE                           0x120
+#define PRAMBOOT_MAX_SIZE                           (64*1024)
+#define FTS_FLASH_PACKET_LENGTH                     32     /* max=128 */
+#define FTS_MAX_LEN_ECC_CALC                        0xFFFE /* must be even */
+#define FTS_MIN_LEN                                 0x120
+#define FTS_MAX_LEN_FILE                            (128 * 1024)
+#define FTS_MAX_LEN_APP                             (64 * 1024)
+#define FTS_MAX_LEN_SECTOR                          (4 * 1024)
+#define FTS_CONIFG_VENDORID_OFF                     0x04
+#define FTS_CONIFG_MODULEID_OFF                     0x1E
+#define FTS_CONIFG_PROJECTID_OFF                    0x20
+#define FTS_APPINFO_OFF                             0x100
+#define FTS_APPINFO_APPLEN_OFF                      0x00
+#define FTS_APPINFO_APPLEN2_OFF                     0x12
+#define FTS_REG_UPGRADE                             0xFC
+#define FTS_REG_UPGRADE2                            0xBC
+#define FTS_UPGRADE_AA                              0xAA
+#define FTS_UPGRADE_55                              0x55
+#define FTS_DELAY_UPGRADE_AA                        10
+#define FTS_UPGRADE_LOOP                            30
+#define FTS_HEADER_LEN                              32
+#define FTS_FW_BIN_FILEPATH                         "/sdcard/"
+#define FTS_FW_IDE_SIG                              "IDE_"
+#define FTS_FW_IDE_SIG_LEN                          4
+#define MAX_MODULE_VENDOR_NAME_LEN                  16
+
+#define FTS_ROMBOOT_CMD_ECC_NEW_LEN                 7
+#define FTS_ECC_FINISH_TIMEOUT                      100
+#define FTS_ROMBOOT_CMD_ECC_FINISH                  0xCE
+#define FTS_ROMBOOT_CMD_ECC_FINISH_OK_A5            0xA5
+#define FTS_ROMBOOT_CMD_ECC_FINISH_OK_00            0x00
+#define FTS_ROMBOOT_CMD_ECC_READ                    0xCD
+#define AL2_FCS_COEF                ((1 << 15) + (1 << 10) + (1 << 3))
+
+#define FTS_APP_INFO_OFFSET                         0x100
+
+enum FW_STATUS {
+	FTS_RUN_IN_ERROR,
+	FTS_RUN_IN_APP,
+	FTS_RUN_IN_ROM,
+	FTS_RUN_IN_PRAM,
+	FTS_RUN_IN_BOOTLOADER,
+};
+
+enum FW_FLASH_MODE {
+	FLASH_MODE_APP,
+	FLASH_MODE_LIC,
+	FLASH_MODE_PARAM,
+	FLASH_MODE_ALL,
+};
+
+enum ECC_CHECK_MODE {
+	ECC_CHECK_MODE_XOR,
+	ECC_CHECK_MODE_CRC16,
+};
+
+enum UPGRADE_SPEC {
+	UPGRADE_SPEC_V_1_0 = 0x0100,
+};
+
+/*****************************************************************************
+* Private enumerations, structures and unions using typedef
+*****************************************************************************/
+/* IC info */
+struct upgrade_func {
+	u64 ctype[FTX_MAX_COMPATIBLE_TYPE];
+	u32 fwveroff;
+	u32 fwcfgoff;
+	u32 appoff;
+	u32 licoff;
+	u32 paramcfgoff;
+	u32 paramcfgveroff;
+	u32 paramcfg2off;
+	int pram_ecc_check_mode;
+	int fw_ecc_check_mode;
+	int upgspec_version;
+	bool new_return_value_from_ic;
+	bool appoff_handle_in_ic;
+	bool is_reset_register_BC;
+	bool read_boot_id_need_reset;
+	bool hid_supported;
+	bool pramboot_supported;
+	u8 *pramboot;
+	u32 pb_length;
+	int (*init)(u8 *, u32);
+	int (*write_pramboot_private)(void);
+	int (*upgrade)(u8 *, u32);
+	int (*get_hlic_ver)(u8 *);
+	int (*lic_upgrade)(u8 *, u32);
+	int (*param_upgrade)(u8 *, u32);
+	int (*force_upgrade)(u8 *, u32);
+};
+
+struct upgrade_setting_nf {
+	u8 rom_idh;
+	u8 rom_idl;
+	u16 reserved;
+	u32 app2_offset;
+	u32 ecclen_max;
+	u8 eccok_val;
+	u8 upgsts_boot;
+	u8 delay_init;
+	bool spi_pe;
+	bool half_length;
+	bool fd_check;
+	bool drwr_support;
+};
+
+struct upgrade_module {
+	int id;
+	char vendor_name[MAX_MODULE_VENDOR_NAME_LEN];
+	u8 *fw_file;
+	u32 fw_len;
+};
+
+struct fts_upgrade {
+	struct fts_ts_data *ts_data;
+	struct upgrade_module *module_info;
+	struct upgrade_func *func;
+	struct upgrade_setting_nf *setting_nf;
+	int module_id;
+	bool fw_from_request;
+	u8 *fw;
+	u32 fw_length;
+	u8 *lic;
+	u32 lic_length;
+};
+
+/*****************************************************************************
+* Global variable or extern global variabls/functions
+*****************************************************************************/
+extern struct upgrade_func upgrade_func_ft5452;
+extern struct upgrade_func upgrade_func_ft5652;
+
+/*****************************************************************************
+* Static function prototypes
+*****************************************************************************/
+int fts_fwupg_reset_in_boot(void);
+int fts_fwupg_enter_into_boot(void);
+int fts_fwupg_erase(u32 delay);
+int fts_fwupg_ecc_cal(u32 saddr, u32 len);
+int fts_flash_write_buf(u32 saddr, u8 *buf, u32 len, u32 delay);
+int fts_fwupg_upgrade(struct fts_upgrade *upg);
+#endif

+ 292 - 0
qcom/opensource/touch-drivers/focaltech_touch/focaltech_flash/focaltech_upgrade_ft3518.c

@@ -0,0 +1,292 @@
+/*
+ *
+ * FocalTech fts TouchScreen driver.
+ *
+ * Copyright (c) 2012-2019, Focaltech Ltd. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+/*****************************************************************************
+*
+* File Name: focaltech_upgrade_ft5452.c
+*
+* Author: Focaltech Driver Team
+*
+* Created: 2016-08-15
+*
+* Abstract:
+*
+* Reference:
+*
+*****************************************************************************/
+/*****************************************************************************
+* 1.Included header files
+*****************************************************************************/
+#include "../focaltech_flash.h"
+
+/************************************************************************
+* Name: fts_ft5452_upgrade
+* Brief:
+* Input:
+* Output:
+* Return: return 0 if success, otherwise return error code
+***********************************************************************/
+static int fts_ft5452_upgrade(u8 *buf, u32 len)
+{
+	int ret = 0;
+	u32 start_addr = 0;
+	u8 cmd[4] = { 0 };
+	int ecc_in_host = 0;
+	int ecc_in_tp = 0;
+
+	int i = 0;
+	u8 wbuf[7] = { 0 };
+	u8 reg_val[4] = {0};
+
+	if (NULL == buf) {
+		FTS_ERROR("fw buf is null");
+		return -EINVAL;
+	}
+
+	if ((len < FTS_MIN_LEN) || (len > (60 * 1024))) {
+		FTS_ERROR("fw buffer len(%x) fail", len);
+		return -EINVAL;
+	}
+
+	/* enter into upgrade environment */
+	ret = fts_fwupg_enter_into_boot();
+	if (ret < 0) {
+		FTS_ERROR("enter into pramboot/bootloader fail,ret=%d", ret);
+		goto fw_reset;
+	}
+
+	cmd[0] = FTS_CMD_FLASH_MODE;
+	cmd[1] = FLASH_MODE_UPGRADE_VALUE;
+	ret = fts_write(cmd, 2);
+	if (ret < 0) {
+		FTS_ERROR("upgrade mode(09) cmd write fail");
+		goto fw_reset;
+	}
+
+	cmd[0] = FTS_CMD_DATA_LEN;
+	cmd[1] = BYTE_OFF_16(len);
+	cmd[2] = BYTE_OFF_8(len);
+	cmd[3] = BYTE_OFF_0(len);
+	ret = fts_write(cmd, FTS_CMD_DATA_LEN_LEN);
+	if (ret < 0) {
+		FTS_ERROR("data len cmd write fail");
+		goto fw_reset;
+	}
+
+	ret = fts_fwupg_erase(FTS_REASE_APP_DELAY);
+	if (ret < 0) {
+		FTS_ERROR("erase cmd write fail");
+		goto fw_reset;
+	}
+
+	/* write app */
+	start_addr = upgrade_func_ft5452.appoff;
+	ecc_in_host = fts_flash_write_buf(start_addr, buf, len, 1);
+	if (ecc_in_host < 0 ) {
+		FTS_ERROR("lcd initial code write fail");
+		goto fw_reset;
+	}
+
+	FTS_INFO( "**********read out checksum**********");
+
+	/* check sum init */
+	wbuf[0] = FTS_CMD_ECC_INIT;
+	ret = fts_write(wbuf, 1);
+	if (ret < 0) {
+		FTS_ERROR("ecc init cmd write fail");
+		return ret;
+	}
+
+	/* send commond to start checksum */
+	wbuf[0] = FTS_CMD_ECC_CAL;
+	wbuf[1] = BYTE_OFF_16(start_addr);
+	wbuf[2] = BYTE_OFF_8(start_addr);
+	wbuf[3] = BYTE_OFF_0(start_addr);
+
+	wbuf[4] = BYTE_OFF_16(len);
+	wbuf[5] = BYTE_OFF_8(len);
+	wbuf[6] = BYTE_OFF_0(len);
+
+	FTS_DEBUG("ecc calc startaddr:0x%04x, len:%d", start_addr, len);
+	ret = fts_write(wbuf, 7);
+	if (ret < 0) {
+		FTS_ERROR("ecc calc cmd write fail");
+		return ret;
+	}
+
+	msleep(len / 256);
+
+	/* read status if check sum is finished */
+	for (i = 0; i < FTS_RETRIES_ECC_CAL; i++) {
+		wbuf[0] = FTS_CMD_FLASH_STATUS;
+		reg_val[0] = reg_val[1] = 0x00;
+		fts_read(wbuf, 1, reg_val, 2);
+		FTS_DEBUG("[UPGRADE]: reg_val[0]=%02x reg_val[0]=%02x!!", reg_val[0], reg_val[1]);
+		if ((0xF0 == reg_val[0]) && (0x55 == reg_val[1])) {
+			break;
+		}
+		msleep(FTS_RETRIES_DELAY_ECC_CAL);
+	}
+
+	/* read out check sum */
+	wbuf[0] = FTS_CMD_ECC_READ;
+	ret = fts_read(wbuf, 1, reg_val, 1);
+	if (ret < 0) {
+		FTS_ERROR( "ecc read cmd write fail");
+		return ret;
+	}
+	ecc_in_tp = reg_val[0];
+
+	FTS_INFO("ecc in tp:%x, host:%x", ecc_in_tp, ecc_in_host);
+	if (ecc_in_tp != ecc_in_host) {
+		FTS_ERROR("ecc check fail");
+		goto fw_reset;
+	}
+
+	FTS_INFO("upgrade success, reset to normal boot");
+	ret = fts_fwupg_reset_in_boot();
+	if (ret < 0) {
+		FTS_ERROR("reset to normal boot fail");
+	}
+
+	msleep(200);
+	return 0;
+
+fw_reset:
+	FTS_INFO("upgrade fail, reset to normal boot");
+	ret = fts_fwupg_reset_in_boot();
+	if (ret < 0) {
+		FTS_ERROR("reset to normal boot fail");
+	}
+	return -EIO;
+}
+
+struct upgrade_func upgrade_func_ft5452 = {
+	.ctype = {0x81},
+	.fwveroff = 0x010E,
+	.fwcfgoff = 0x1FFB0,
+	.appoff = 0x0000,
+	.pramboot_supported = false,
+	.hid_supported = true,
+	.upgrade = fts_ft5452_upgrade,
+};
+
+
+#define FTS_DELAY_ERASE_PAGE_2K         80
+#define FTS_SIZE_PAGE_2K                2048
+
+/************************************************************************
+ * Name: fts_ft5652_upgrade
+ * Brief:
+ * Input:
+ * Output:
+ * Return: return 0 if success, otherwise return error code
+ **********************************************************************/
+static int fts_ft5652_upgrade(u8 *buf, u32 len)
+{
+	int ret = 0;
+	u32 start_addr = 0;
+	u8 cmd[4] = { 0 };
+	u32 delay = 0;
+	int ecc_in_host = 0;
+	int ecc_in_tp = 0;
+
+	if ((buf == NULL) || (len < FTS_MIN_LEN)) {
+		FTS_ERROR("buffer/len(%x) is invalid", len);
+		return -EINVAL;
+	}
+
+	/* enter into upgrade environment */
+	ret = fts_fwupg_enter_into_boot();
+	if (ret < 0) {
+		FTS_ERROR("enter into pramboot/bootloader fail,ret=%d", ret);
+		goto fw_reset;
+	}
+
+	cmd[0] = FTS_CMD_APP_DATA_LEN_INCELL;
+	cmd[1] = BYTE_OFF_16(len);
+	cmd[2] = BYTE_OFF_8(len);
+	cmd[3] = BYTE_OFF_0(len);
+	ret = fts_write(cmd, FTS_CMD_DATA_LEN_LEN);
+	if (ret < 0) {
+		FTS_ERROR("data len cmd write fail");
+		goto fw_reset;
+	}
+
+	cmd[0] = FTS_CMD_FLASH_MODE;
+	cmd[1] = FLASH_MODE_UPGRADE_VALUE;
+	ret = fts_write(cmd, 2);
+	if (ret < 0) {
+		FTS_ERROR("upgrade mode(09) cmd write fail");
+		goto fw_reset;
+	}
+
+	delay = FTS_DELAY_ERASE_PAGE_2K * (len / FTS_SIZE_PAGE_2K);
+	ret = fts_fwupg_erase(delay);
+	if (ret < 0) {
+		FTS_ERROR("erase cmd write fail");
+		goto fw_reset;
+	}
+
+	/* write app */
+	start_addr = upgrade_func_ft5652.appoff;
+	ecc_in_host = fts_flash_write_buf(start_addr, buf, len, 1);
+	if (ecc_in_host < 0) {
+		FTS_ERROR("flash write fail");
+		goto fw_reset;
+	}
+
+	/* ecc */
+	ecc_in_tp = fts_fwupg_ecc_cal(start_addr, len);
+	if (ecc_in_tp < 0) {
+		FTS_ERROR("ecc read fail");
+		goto fw_reset;
+	}
+
+	FTS_INFO("ecc in tp:%x, host:%x", ecc_in_tp, ecc_in_host);
+	if (ecc_in_tp != ecc_in_host) {
+		FTS_ERROR("ecc check fail");
+		goto fw_reset;
+	}
+
+	FTS_INFO("upgrade success, reset to normal boot");
+	ret = fts_fwupg_reset_in_boot();
+	if (ret < 0)
+		FTS_ERROR("reset to normal boot fail");
+
+	msleep(200);
+	return 0;
+
+fw_reset:
+	FTS_INFO("upgrade fail, reset to normal boot");
+	ret = fts_fwupg_reset_in_boot();
+	if (ret < 0)
+		FTS_ERROR("reset to normal boot fail");
+
+	return -EIO;
+}
+
+struct upgrade_func upgrade_func_ft5652 = {
+	.ctype = {0x88},
+	.fwveroff = 0x010E,
+	.fwcfgoff = 0x1F80,
+	.appoff = 0x0000,
+	.upgspec_version = UPGRADE_SPEC_V_1_0,
+	.pramboot_supported = false,
+	.hid_supported = true,
+	.upgrade = fts_ft5652_upgrade,
+};

+ 477 - 0
qcom/opensource/touch-drivers/focaltech_touch/focaltech_gesture.c

@@ -0,0 +1,477 @@
+/*
+ *
+ * FocalTech TouchScreen driver.
+ *
+ * Copyright (c) 2012-2019, Focaltech Ltd. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+/*****************************************************************************
+*
+* File Name: focaltech_gestrue.c
+*
+* Author: Focaltech Driver Team
+*
+* Created: 2016-08-08
+*
+* Abstract:
+*
+* Reference:
+*
+*****************************************************************************/
+
+/*****************************************************************************
+* 1.Included header files
+*****************************************************************************/
+#include "focaltech_core.h"
+
+/******************************************************************************
+* Private constant and macro definitions using #define
+*****************************************************************************/
+#define KEY_GESTURE_U                           KEY_U
+#define KEY_GESTURE_UP                          KEY_UP
+#define KEY_GESTURE_DOWN                        KEY_DOWN
+#define KEY_GESTURE_LEFT                        KEY_LEFT
+#define KEY_GESTURE_RIGHT                       KEY_RIGHT
+#define KEY_GESTURE_O                           KEY_O
+#define KEY_GESTURE_E                           KEY_E
+#define KEY_GESTURE_M                           KEY_M
+#define KEY_GESTURE_L                           KEY_L
+#define KEY_GESTURE_W                           KEY_W
+#define KEY_GESTURE_S                           KEY_S
+#define KEY_GESTURE_V                           KEY_V
+#define KEY_GESTURE_C                           KEY_C
+#define KEY_GESTURE_Z                           KEY_Z
+
+#define GESTURE_LEFT                            0x20
+#define GESTURE_RIGHT                           0x21
+#define GESTURE_UP                              0x22
+#define GESTURE_DOWN                            0x23
+#define GESTURE_DOUBLECLICK                     0x24
+#define GESTURE_O                               0x30
+#define GESTURE_W                               0x31
+#define GESTURE_M                               0x32
+#define GESTURE_E                               0x33
+#define GESTURE_L                               0x44
+#define GESTURE_S                               0x46
+#define GESTURE_V                               0x54
+#define GESTURE_Z                               0x41
+#define GESTURE_C                               0x34
+
+/*****************************************************************************
+* Private enumerations, structures and unions using typedef
+*****************************************************************************/
+/*
+* gesture_id    - mean which gesture is recognised
+* point_num     - points number of this gesture
+* coordinate_x  - All gesture point x coordinate
+* coordinate_y  - All gesture point y coordinate
+* mode          - gesture enable/disable, need enable by host
+*               - 1:enable gesture function(default)  0:disable
+* active        - gesture work flag,
+*                 always set 1 when suspend, set 0 when resume
+*/
+struct fts_gesture_st {
+	u8 gesture_id;
+	u8 point_num;
+	u16 coordinate_x[FTS_GESTURE_POINTS_MAX];
+	u16 coordinate_y[FTS_GESTURE_POINTS_MAX];
+};
+
+/*****************************************************************************
+* Static variables
+*****************************************************************************/
+static struct fts_gesture_st fts_gesture_data;
+
+/*****************************************************************************
+* Global variable or extern global variabls/functions
+*****************************************************************************/
+
+/*****************************************************************************
+* Static function prototypes
+*****************************************************************************/
+static ssize_t fts_gesture_show(
+	struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int count = 0;
+	u8 val = 0;
+	struct fts_ts_data *ts_data = fts_data;
+
+	mutex_lock(&ts_data->input_dev->mutex);
+	fts_read_reg(FTS_REG_GESTURE_EN, &val);
+	count = snprintf(buf, PAGE_SIZE, "Gesture Mode:%s\n",
+			ts_data->gesture_mode ? "On" : "Off");
+	count += snprintf(buf + count, PAGE_SIZE, "Reg(0xD0)=%d\n", val);
+	mutex_unlock(&ts_data->input_dev->mutex);
+
+	return count;
+}
+
+static ssize_t fts_gesture_store(
+	struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct fts_ts_data *ts_data = fts_data;
+
+	mutex_lock(&ts_data->input_dev->mutex);
+	if (FTS_SYSFS_ECHO_ON(buf)) {
+		FTS_DEBUG("enable gesture");
+		ts_data->gesture_mode = ENABLE;
+	} else if (FTS_SYSFS_ECHO_OFF(buf)) {
+		FTS_DEBUG("disable gesture");
+		ts_data->gesture_mode = DISABLE;
+	}
+	mutex_unlock(&ts_data->input_dev->mutex);
+
+	return count;
+}
+
+static ssize_t fts_gesture_buf_show(
+	struct device *dev, struct device_attribute *attr, char *buf)
+{
+	int count = 0;
+	int i = 0;
+	struct input_dev *input_dev = fts_data->input_dev;
+	struct fts_gesture_st *gesture = &fts_gesture_data;
+
+	mutex_lock(&input_dev->mutex);
+	count = snprintf(buf, PAGE_SIZE, "Gesture ID:%d\n", gesture->gesture_id);
+	count += snprintf(buf + count, PAGE_SIZE, "Gesture PointNum:%d\n",
+			gesture->point_num);
+	count += snprintf(buf + count, PAGE_SIZE, "Gesture Points Buffer:\n");
+
+	/* save point data,max:6 */
+	for (i = 0; i < FTS_GESTURE_POINTS_MAX; i++) {
+		count += snprintf(buf + count, PAGE_SIZE, "%3d(%4d,%4d) ", i,
+			gesture->coordinate_x[i], gesture->coordinate_y[i]);
+
+		if ((i + 1) % 4 == 0)
+			count += snprintf(buf + count, PAGE_SIZE, "\n");
+	}
+	count += snprintf(buf + count, PAGE_SIZE, "\n");
+	mutex_unlock(&input_dev->mutex);
+
+	return count;
+}
+
+static ssize_t fts_gesture_buf_store(
+	struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t count)
+{
+	return -EPERM;
+}
+
+
+/* sysfs gesture node
+ *   read example: cat  fts_gesture_mode       ---read gesture mode
+ *   write example:echo 1 > fts_gesture_mode   --- write gesture mode to 1
+ *
+ */
+static DEVICE_ATTR(fts_gesture_mode, S_IRUGO | S_IWUSR, fts_gesture_show,
+			fts_gesture_store);
+/*
+ *   read example: cat fts_gesture_buf        --- read gesture buf
+ */
+static DEVICE_ATTR(fts_gesture_buf, S_IRUGO | S_IWUSR,
+			fts_gesture_buf_show, fts_gesture_buf_store);
+
+static struct attribute *fts_gesture_mode_attrs[] = {
+	&dev_attr_fts_gesture_mode.attr,
+	&dev_attr_fts_gesture_buf.attr,
+	NULL,
+};
+
+static struct attribute_group fts_gesture_group = {
+	.attrs = fts_gesture_mode_attrs,
+};
+
+static int fts_create_gesture_sysfs(struct device *dev)
+{
+	int ret = 0;
+
+	ret = sysfs_create_group(&dev->kobj, &fts_gesture_group);
+	if (ret) {
+		FTS_ERROR("gesture sys node create fail");
+		sysfs_remove_group(&dev->kobj, &fts_gesture_group);
+		return ret;
+	}
+
+	return 0;
+}
+
+static void fts_gesture_report(struct input_dev *input_dev, int gesture_id)
+{
+	int gesture;
+
+	FTS_DEBUG("gesture_id:0x%x", gesture_id);
+	switch (gesture_id) {
+	case GESTURE_LEFT:
+		gesture = KEY_GESTURE_LEFT;
+		break;
+
+	case GESTURE_RIGHT:
+		gesture = KEY_GESTURE_RIGHT;
+		break;
+
+	case GESTURE_UP:
+		gesture = KEY_GESTURE_UP;
+		break;
+
+	case GESTURE_DOWN:
+		gesture = KEY_GESTURE_DOWN;
+		break;
+
+	case GESTURE_DOUBLECLICK:
+		gesture = KEY_POWER;
+
+		break;
+
+	case GESTURE_O:
+		gesture = KEY_GESTURE_O;
+		break;
+
+	case GESTURE_W:
+		gesture = KEY_GESTURE_W;
+		break;
+
+	case GESTURE_M:
+		gesture = KEY_GESTURE_M;
+		break;
+
+	case GESTURE_E:
+		gesture = KEY_GESTURE_E;
+		break;
+
+	case GESTURE_L:
+		gesture = KEY_GESTURE_L;
+		break;
+
+	case GESTURE_S:
+		gesture = KEY_GESTURE_S;
+		break;
+
+	case GESTURE_V:
+		gesture = KEY_GESTURE_V;
+		break;
+
+	case GESTURE_Z:
+		gesture = KEY_GESTURE_Z;
+		break;
+
+	case GESTURE_C:
+		gesture = KEY_GESTURE_C;
+		break;
+
+	default:
+		gesture = -1;
+		break;
+	}
+
+	/* report event key */
+	if (gesture != -1) {
+		FTS_DEBUG("Gesture Code=%d", gesture);
+		input_report_key(input_dev, gesture, 1);
+		input_sync(input_dev);
+		input_report_key(input_dev, gesture, 0);
+		input_sync(input_dev);
+	}
+}
+
+/*****************************************************************************
+* Name: fts_gesture_readdata
+* Brief: Read information about gesture: enable flag/gesture points..., if ges-
+*        ture enable, save gesture points' information, and report to OS.
+*        It will be called this function every intrrupt when FTS_GESTURE_EN = 1
+*
+*        gesture data length: 1(enable) + 1(reserve) + 2(header) + 6 * 4
+* Input: ts_data - global struct data
+*        data    - gesture data buffer if non-flash, else NULL
+* Output:
+* Return: 0 - read gesture data successfully, the report data is gesture data
+*         1 - tp not in suspend/gesture not enable in TP FW
+*         -Exx - error
+*****************************************************************************/
+int fts_gesture_readdata(struct fts_ts_data *ts_data, u8 *data)
+{
+	int ret = 0;
+	int i = 0;
+	int index = 0;
+	u8 buf[FTS_GESTURE_DATA_LEN] = { 0 };
+	struct input_dev *input_dev = ts_data->input_dev;
+	struct fts_gesture_st *gesture = &fts_gesture_data;
+
+	if (!ts_data->suspended || !ts_data->gesture_mode) {
+		return 1;
+	}
+
+
+	ret = fts_read_reg(FTS_REG_GESTURE_EN, &buf[0]);
+	if ((ret < 0) || (buf[0] != ENABLE)) {
+		FTS_DEBUG("gesture not enable in fw, don't process gesture");
+		return 1;
+	}
+
+	buf[2] = FTS_REG_GESTURE_OUTPUT_ADDRESS;
+	ret = fts_read(&buf[2], 1, &buf[2], FTS_GESTURE_DATA_LEN - 2);
+	if (ret < 0) {
+		FTS_ERROR("read gesture header data fail");
+		return ret;
+	}
+
+	/* init variable before read gesture point */
+	memset(gesture->coordinate_x, 0, FTS_GESTURE_POINTS_MAX * sizeof(u16));
+	memset(gesture->coordinate_y, 0, FTS_GESTURE_POINTS_MAX * sizeof(u16));
+	gesture->gesture_id = buf[2];
+	gesture->point_num = buf[3];
+	FTS_DEBUG("gesture_id=%d, point_num=%d",
+		gesture->gesture_id, gesture->point_num);
+
+	/* save point data,max:6 */
+	for (i = 0; i < FTS_GESTURE_POINTS_MAX; i++) {
+		index = 4 * i + 4;
+		gesture->coordinate_x[i] = (u16)(((buf[0 + index] & 0x0F) << 8)
+						+ buf[1 + index]);
+		gesture->coordinate_y[i] = (u16)(((buf[2 + index] & 0x0F) << 8)
+						+ buf[3 + index]);
+	}
+
+	/* report gesture to OS */
+	fts_gesture_report(input_dev, gesture->gesture_id);
+	return 0;
+}
+
+void fts_gesture_recovery(struct fts_ts_data *ts_data)
+{
+	if (ts_data->gesture_mode && ts_data->suspended) {
+		FTS_DEBUG("gesture recovery...");
+		fts_write_reg(0xD1, 0xFF);
+		fts_write_reg(0xD2, 0xFF);
+		fts_write_reg(0xD5, 0xFF);
+		fts_write_reg(0xD6, 0xFF);
+		fts_write_reg(0xD7, 0xFF);
+		fts_write_reg(0xD8, 0xFF);
+		fts_write_reg(FTS_REG_GESTURE_EN, ENABLE);
+	}
+}
+
+int fts_gesture_suspend(struct fts_ts_data *ts_data)
+{
+	int i = 0;
+	u8 state = 0xFF;
+
+	FTS_FUNC_ENTER();
+	if (enable_irq_wake(ts_data->irq)) {
+		FTS_DEBUG("enable_irq_wake(irq:%d) fail", ts_data->irq);
+	}
+
+	for (i = 0; i < 5; i++) {
+		fts_write_reg(0xD1, 0xFF);
+		fts_write_reg(0xD2, 0xFF);
+		fts_write_reg(0xD5, 0xFF);
+		fts_write_reg(0xD6, 0xFF);
+		fts_write_reg(0xD7, 0xFF);
+		fts_write_reg(0xD8, 0xFF);
+		fts_write_reg(FTS_REG_GESTURE_EN, ENABLE);
+		msleep(1);
+		fts_read_reg(FTS_REG_GESTURE_EN, &state);
+		if (state == ENABLE)
+			break;
+	}
+
+	if (i >= 5)
+		FTS_ERROR("make IC enter into gesture(suspend) fail,state:%x", state);
+	else
+		FTS_INFO("Enter into gesture(suspend) successfully");
+
+	FTS_FUNC_EXIT();
+	return 0;
+}
+
+int fts_gesture_resume(struct fts_ts_data *ts_data)
+{
+	int i = 0;
+	u8 state = 0xFF;
+
+	FTS_FUNC_ENTER();
+	if (disable_irq_wake(ts_data->irq)) {
+		FTS_DEBUG("disable_irq_wake(irq:%d) fail", ts_data->irq);
+	}
+
+	for (i = 0; i < 5; i++) {
+		fts_write_reg(FTS_REG_GESTURE_EN, DISABLE);
+		msleep(1);
+		fts_read_reg(FTS_REG_GESTURE_EN, &state);
+		if (state == DISABLE)
+			break;
+	}
+
+	if (i >= 5)
+		FTS_ERROR("make IC exit gesture(resume) fail,state:%x", state);
+	else
+		FTS_INFO("resume from gesture successfully");
+
+	FTS_FUNC_EXIT();
+	return 0;
+}
+
+int fts_gesture_init(struct fts_ts_data *ts_data)
+{
+	struct input_dev *input_dev = ts_data->input_dev;
+
+	FTS_FUNC_ENTER();
+	input_set_capability(input_dev, EV_KEY, KEY_POWER);
+	input_set_capability(input_dev, EV_KEY, KEY_GESTURE_U);
+	input_set_capability(input_dev, EV_KEY, KEY_GESTURE_UP);
+	input_set_capability(input_dev, EV_KEY, KEY_GESTURE_DOWN);
+	input_set_capability(input_dev, EV_KEY, KEY_GESTURE_LEFT);
+	input_set_capability(input_dev, EV_KEY, KEY_GESTURE_RIGHT);
+	input_set_capability(input_dev, EV_KEY, KEY_GESTURE_O);
+	input_set_capability(input_dev, EV_KEY, KEY_GESTURE_E);
+	input_set_capability(input_dev, EV_KEY, KEY_GESTURE_M);
+	input_set_capability(input_dev, EV_KEY, KEY_GESTURE_L);
+	input_set_capability(input_dev, EV_KEY, KEY_GESTURE_W);
+	input_set_capability(input_dev, EV_KEY, KEY_GESTURE_S);
+	input_set_capability(input_dev, EV_KEY, KEY_GESTURE_V);
+	input_set_capability(input_dev, EV_KEY, KEY_GESTURE_Z);
+	input_set_capability(input_dev, EV_KEY, KEY_GESTURE_C);
+
+	__set_bit(KEY_GESTURE_RIGHT, input_dev->keybit);
+	__set_bit(KEY_GESTURE_LEFT, input_dev->keybit);
+	__set_bit(KEY_GESTURE_UP, input_dev->keybit);
+	__set_bit(KEY_GESTURE_DOWN, input_dev->keybit);
+	__set_bit(KEY_GESTURE_U, input_dev->keybit);
+	__set_bit(KEY_GESTURE_O, input_dev->keybit);
+	__set_bit(KEY_GESTURE_E, input_dev->keybit);
+	__set_bit(KEY_GESTURE_M, input_dev->keybit);
+	__set_bit(KEY_GESTURE_W, input_dev->keybit);
+	__set_bit(KEY_GESTURE_L, input_dev->keybit);
+	__set_bit(KEY_GESTURE_S, input_dev->keybit);
+	__set_bit(KEY_GESTURE_V, input_dev->keybit);
+	__set_bit(KEY_GESTURE_C, input_dev->keybit);
+	__set_bit(KEY_GESTURE_Z, input_dev->keybit);
+
+	fts_create_gesture_sysfs(ts_data->dev);
+
+	memset(&fts_gesture_data, 0, sizeof(struct fts_gesture_st));
+	ts_data->gesture_mode = FTS_GESTURE_EN;
+
+	FTS_FUNC_EXIT();
+	return 0;
+}
+
+int fts_gesture_exit(struct fts_ts_data *ts_data)
+{
+	FTS_FUNC_ENTER();
+	sysfs_remove_group(&ts_data->dev->kobj, &fts_gesture_group);
+	FTS_FUNC_EXIT();
+	return 0;
+}

+ 548 - 0
qcom/opensource/touch-drivers/focaltech_touch/focaltech_i2c.c

@@ -0,0 +1,548 @@
+/*
+ *
+ * FocalTech TouchScreen driver.
+ *
+ * Copyright (c) 2012-2019, FocalTech Systems, Ltd., all rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+/************************************************************************
+*
+* File Name: focaltech_i2c.c
+*
+* Author: Focaltech Driver Team
+*
+* Created: 2016-08-04
+*
+* Abstract: i2c communication with TP
+*
+* Version: v1.0
+*
+* Revision History:
+*
+************************************************************************/
+
+/*****************************************************************************
+* Included header files
+*****************************************************************************/
+#include "focaltech_core.h"
+#include <linux/pm_runtime.h>
+
+/*****************************************************************************
+* Private constant and macro definitions using #define
+*****************************************************************************/
+#define I2C_RETRY_NUMBER                    3
+#define I2C_BUF_LENGTH                      256
+
+/*****************************************************************************
+* Private enumerations, structures and unions using typedef
+*****************************************************************************/
+
+/*****************************************************************************
+* Static variables
+*****************************************************************************/
+static struct fts_ts_data *ts_data;
+
+/*****************************************************************************
+* Global variable or extern global variabls/functions
+*****************************************************************************/
+
+/*****************************************************************************
+* Static function prototypes
+*****************************************************************************/
+
+/*****************************************************************************
+* functions body
+*****************************************************************************/
+static int fts_i2c_read(u8 *cmd, u32 cmdlen, u8 *data, u32 datalen)
+{
+	int ret = 0;
+	int i = 0;
+	struct i2c_msg msg_list[2];
+	struct i2c_msg *msg = NULL;
+	int msg_num = 0;
+
+	/* must have data when read */
+	if (!ts_data || !ts_data->client || !data || !datalen
+		|| (datalen >= I2C_BUF_LENGTH) || (cmdlen >= I2C_BUF_LENGTH)) {
+		FTS_ERROR("fts_data/client/cmdlen(%d)/data/datalen(%d) is invalid",
+			cmdlen, datalen);
+		return -EINVAL;
+	}
+
+	mutex_lock(&ts_data->bus_lock);
+	memset(&msg_list[0], 0, sizeof(struct i2c_msg));
+	memset(&msg_list[1], 0, sizeof(struct i2c_msg));
+	memcpy(ts_data->bus_tx_buf, cmd, cmdlen);
+	msg_list[0].addr = ts_data->client->addr;
+	msg_list[0].flags = 0;
+	msg_list[0].len = cmdlen;
+	msg_list[0].buf = ts_data->bus_tx_buf;
+	msg_list[1].addr = ts_data->client->addr;
+	msg_list[1].flags = I2C_M_RD;
+	msg_list[1].len = datalen;
+	msg_list[1].buf = ts_data->bus_rx_buf;
+	if (cmd && cmdlen) {
+		msg = &msg_list[0];
+		msg_num = 2;
+	} else {
+		msg = &msg_list[1];
+		msg_num = 1;
+	}
+
+	for (i = 0; i < I2C_RETRY_NUMBER; i++) {
+		ret = i2c_transfer(ts_data->client->adapter, msg, msg_num);
+		if (ret < 0) {
+#ifdef CONFIG_FTS_TRUSTED_TOUCH
+#ifdef CONFIG_ARCH_QTI_VM
+			if (atomic_read(&ts_data->trusted_touch_enabled) &&
+					ret == -ECONNRESET) {
+				pr_err("failed i2c read reacquiring session\n");
+				pm_runtime_put_sync(
+					ts_data->client->adapter->dev.parent);
+				pm_runtime_get_sync(
+					ts_data->client->adapter->dev.parent);
+			}
+#endif
+#endif
+			FTS_ERROR("i2c_transfer(read) fail,ret:%d", ret);
+		} else {
+			memcpy(data, ts_data->bus_rx_buf, datalen);
+			break;
+		}
+	}
+
+	if (ret < 0) {
+#ifdef CONFIG_FTS_TRUSTED_TOUCH
+#ifdef CONFIG_ARCH_QTI_VM
+		pr_err("initiating abort due to i2c xfer failure\n");
+		fts_ts_trusted_touch_tvm_i2c_failure_report(ts_data);
+#endif
+#endif
+	}
+
+	mutex_unlock(&ts_data->bus_lock);
+	return ret;
+}
+
+static int fts_i2c_write(u8 *writebuf, u32 writelen)
+{
+	int ret = 0;
+	int i = 0;
+	struct i2c_msg msgs;
+
+	if (!ts_data || !ts_data->client || !writebuf || !writelen
+		|| (writelen >= I2C_BUF_LENGTH)) {
+		FTS_ERROR("fts_data/client/data/datalen(%d) is invalid", writelen);
+		return -EINVAL;
+	}
+
+	mutex_lock(&ts_data->bus_lock);
+	memset(&msgs, 0, sizeof(struct i2c_msg));
+	memcpy(ts_data->bus_tx_buf, writebuf, writelen);
+	msgs.addr = ts_data->client->addr;
+	msgs.flags = 0;
+	msgs.len = writelen;
+	msgs.buf = ts_data->bus_tx_buf;
+	for (i = 0; i < I2C_RETRY_NUMBER; i++) {
+		ret = i2c_transfer(ts_data->client->adapter, &msgs, 1);
+		if (ret < 0) {
+#ifdef CONFIG_FTS_TRUSTED_TOUCH
+#ifdef CONFIG_ARCH_QTI_VM
+			if (atomic_read(&ts_data->trusted_touch_enabled) &&
+				ret == -ECONNRESET){
+				pr_err("failed i2c write reacquiring session\n");
+				pm_runtime_put_sync(
+					ts_data->client->adapter->dev.parent);
+				pm_runtime_get_sync(
+					ts_data->client->adapter->dev.parent);
+			}
+#endif
+#endif
+			FTS_ERROR("i2c_transfer(write) fail,ret:%d", ret);
+		} else {
+			break;
+		}
+	}
+
+	if (ret < 0) {
+#ifdef CONFIG_FTS_TRUSTED_TOUCH
+#ifdef CONFIG_ARCH_QTI_VM
+		pr_err("initiating abort due to i2c xfer failure\n");
+		fts_ts_trusted_touch_tvm_i2c_failure_report(ts_data);
+#endif
+#endif
+	}
+	mutex_unlock(&ts_data->bus_lock);
+	return ret;
+}
+
+static int fts_i2c_init(struct fts_ts_data *ts_data)
+{
+	FTS_FUNC_ENTER();
+	ts_data->bus_tx_buf = kzalloc(I2C_BUF_LENGTH, GFP_KERNEL);
+	if (ts_data->bus_tx_buf == NULL) {
+		FTS_ERROR("failed to allocate memory for bus_tx_buf");
+		return -ENOMEM;
+	}
+
+	ts_data->bus_rx_buf = kzalloc(I2C_BUF_LENGTH, GFP_KERNEL);
+	if (ts_data->bus_rx_buf == NULL) {
+		FTS_ERROR("failed to allocate memory for bus_rx_buf");
+		return -ENOMEM;
+	}
+	FTS_FUNC_EXIT();
+	return 0;
+}
+
+static int fts_i2c_exit(struct fts_ts_data *ts_data)
+{
+	FTS_FUNC_ENTER();
+	if (ts_data && ts_data->bus_tx_buf) {
+		kfree(ts_data->bus_tx_buf);
+		ts_data->bus_tx_buf = NULL;
+	}
+
+	if (ts_data && ts_data->bus_rx_buf) {
+		kfree(ts_data->bus_rx_buf);
+		ts_data->bus_rx_buf = NULL;
+	}
+	FTS_FUNC_EXIT();
+	return 0;
+}
+
+/*****************************************************************************
+ * Private constant and macro definitions using #define
+ ****************************************************************************/
+#define SPI_RETRY_NUMBER            3
+#define CS_HIGH_DELAY               150 /* unit: us */
+#define SPI_BUF_LENGTH              256
+
+#define DATA_CRC_EN                 0x20
+#define WRITE_CMD                   0x00
+#define READ_CMD                    (0x80 | DATA_CRC_EN)
+
+#define SPI_DUMMY_BYTE              3
+#define SPI_HEADER_LENGTH           6   /*CRC*/
+
+/*****************************************************************************
+ * functions body
+ ****************************************************************************/
+/* spi interface */
+static int fts_spi_transfer(u8 *tx_buf, u8 *rx_buf, u32 len)
+{
+	int ret = 0;
+	struct spi_device *spi = fts_data->spi;
+	struct spi_message msg;
+	struct spi_transfer xfer = {
+		.tx_buf = tx_buf,
+		.rx_buf = rx_buf,
+		.len    = len,
+	};
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfer, &msg);
+
+	ret = spi_sync(spi, &msg);
+	if (ret) {
+		FTS_ERROR("spi_sync fail,ret:%d", ret);
+		return ret;
+	}
+
+	return ret;
+}
+
+static void crckermit(u8 *data, u32 len, u16 *crc_out)
+{
+	u32 i = 0;
+	u32 j = 0;
+	u16 crc = 0xFFFF;
+
+	for (i = 0; i < len; i++) {
+		crc ^= data[i];
+		for (j = 0; j < 8; j++) {
+			if (crc & 0x01)
+				crc = (crc >> 1) ^ 0x8408;
+			else
+				crc = (crc >> 1);
+		}
+	}
+
+	*crc_out = crc;
+}
+
+static int rdata_check(u8 *rdata, u32 rlen)
+{
+	u16 crc_calc = 0;
+	u16 crc_read = 0;
+
+	crckermit(rdata, rlen - 2, &crc_calc);
+	crc_read = (u16)(rdata[rlen - 1] << 8) + rdata[rlen - 2];
+	if (crc_calc != crc_read)
+		return -EIO;
+
+	return 0;
+}
+
+static int fts_spi_write(u8 *writebuf, u32 writelen)
+{
+	int ret = 0;
+	int i = 0;
+	struct fts_ts_data *ts_data = fts_data;
+	u8 *txbuf = NULL;
+	u8 *rxbuf = NULL;
+	u32 txlen = 0;
+	u32 txlen_need = writelen + SPI_HEADER_LENGTH + ts_data->dummy_byte;
+	u32 datalen = writelen - 1;
+
+	if (!writebuf || !writelen) {
+		FTS_ERROR("writebuf/len is invalid");
+		return -EINVAL;
+	}
+
+	mutex_lock(&ts_data->bus_lock);
+	if (txlen_need > SPI_BUF_LENGTH) {
+		txbuf = kzalloc(txlen_need, GFP_KERNEL);
+		if (txbuf == NULL) {
+			FTS_ERROR("txbuf malloc fail");
+			ret = -ENOMEM;
+			goto err_write;
+		}
+
+		rxbuf = kzalloc(txlen_need, GFP_KERNEL);
+		if (rxbuf == NULL) {
+			FTS_ERROR("rxbuf malloc fail");
+			ret = -ENOMEM;
+			goto err_write;
+		}
+	} else {
+		txbuf = ts_data->bus_tx_buf;
+		rxbuf = ts_data->bus_rx_buf;
+		memset(txbuf, 0x0, SPI_BUF_LENGTH);
+		memset(rxbuf, 0x0, SPI_BUF_LENGTH);
+	}
+
+	txbuf[txlen++] = writebuf[0];
+	txbuf[txlen++] = WRITE_CMD;
+	txbuf[txlen++] = (datalen >> 8) & 0xFF;
+	txbuf[txlen++] = datalen & 0xFF;
+	if (datalen > 0) {
+		txlen = txlen + SPI_DUMMY_BYTE;
+		memcpy(&txbuf[txlen], &writebuf[1], datalen);
+		txlen = txlen + datalen;
+	}
+
+	for (i = 0; i < SPI_RETRY_NUMBER; i++) {
+		ret = fts_spi_transfer(txbuf, rxbuf, txlen);
+		if ((ret == 0) && ((rxbuf[3] & 0xA0) == 0))
+			break;
+
+		FTS_DEBUG("data write(addr:%x),status:%x,retry:%d,ret:%d",
+				writebuf[0], rxbuf[3], i, ret);
+		ret = -EIO;
+		udelay(CS_HIGH_DELAY);
+	}
+
+	if (ret < 0) {
+		FTS_ERROR("data write(addr:%x) fail,status:%x,ret:%d",
+				writebuf[0], rxbuf[3], ret);
+	}
+
+err_write:
+	if (txlen_need > SPI_BUF_LENGTH) {
+		kfree(txbuf);
+		kfree(rxbuf);
+	}
+
+	udelay(CS_HIGH_DELAY);
+	mutex_unlock(&ts_data->bus_lock);
+	return ret;
+}
+
+static int fts_spi_read(u8 *cmd, u32 cmdlen, u8 *data, u32 datalen)
+{
+	int ret = 0;
+	int i = 0;
+	u8 *txbuf = NULL;
+	u8 *rxbuf = NULL;
+	u32 txlen = 0;
+	u32 txlen_need = datalen + SPI_HEADER_LENGTH + ts_data->dummy_byte;
+	u8 ctrl = READ_CMD;
+	u32 dp = 0;
+
+	if (!cmd || !cmdlen || !data || !datalen) {
+		FTS_ERROR("cmd/cmdlen/data/datalen is invalid");
+		return -EINVAL;
+	}
+
+	mutex_lock(&ts_data->bus_lock);
+	if (txlen_need > SPI_BUF_LENGTH) {
+		txbuf = kzalloc(txlen_need, GFP_KERNEL);
+		if (txbuf == NULL) {
+			FTS_ERROR("txbuf malloc fail");
+			ret = -ENOMEM;
+			goto err_read;
+		}
+
+		rxbuf = kzalloc(txlen_need, GFP_KERNEL);
+		if (rxbuf == NULL) {
+			FTS_ERROR("rxbuf malloc fail");
+			ret = -ENOMEM;
+			goto err_read;
+		}
+	} else {
+		txbuf = ts_data->bus_tx_buf;
+		rxbuf = ts_data->bus_rx_buf;
+		memset(txbuf, 0x0, SPI_BUF_LENGTH);
+		memset(rxbuf, 0x0, SPI_BUF_LENGTH);
+	}
+
+	txbuf[txlen++] = cmd[0];
+	txbuf[txlen++] = ctrl;
+	txbuf[txlen++] = (datalen >> 8) & 0xFF;
+	txbuf[txlen++] = datalen & 0xFF;
+	dp = txlen + SPI_DUMMY_BYTE;
+	txlen = dp + datalen;
+	if (ctrl & DATA_CRC_EN)
+		txlen = txlen + 2;
+
+	for (i = 0; i < SPI_RETRY_NUMBER; i++) {
+		ret = fts_spi_transfer(txbuf, rxbuf, txlen);
+		if ((ret == 0) && ((rxbuf[3] & 0xA0) == 0)) {
+			memcpy(data, &rxbuf[dp], datalen);
+			/* crc check */
+			if (ctrl & DATA_CRC_EN) {
+				ret = rdata_check(&rxbuf[dp], txlen - dp);
+				if (ret < 0) {
+					FTS_DEBUG("data read(addr:%x) crc abnormal,retry:%d",
+							cmd[0], i);
+					udelay(CS_HIGH_DELAY);
+					continue;
+				}
+			}
+			break;
+		}
+
+		FTS_DEBUG("data read(addr:%x) status:%x,retry:%d,ret:%d",
+				cmd[0], rxbuf[3], i, ret);
+		ret = -EIO;
+		udelay(CS_HIGH_DELAY);
+	}
+
+	if (ret < 0) {
+		FTS_ERROR("data read(addr:%x) %s,status:%x,ret:%d", cmd[0],
+				(i >= SPI_RETRY_NUMBER) ? "crc abnormal" : "fail",
+				rxbuf[3], ret);
+	}
+
+err_read:
+	if (txlen_need > SPI_BUF_LENGTH) {
+		kfree(txbuf);
+		kfree(rxbuf);
+	}
+
+	udelay(CS_HIGH_DELAY);
+	mutex_unlock(&ts_data->bus_lock);
+	return ret;
+}
+
+static int fts_spi_init(struct fts_ts_data *ts_data)
+{
+	FTS_FUNC_ENTER();
+	ts_data->bus_tx_buf = kzalloc(SPI_BUF_LENGTH, GFP_KERNEL);
+	if (ts_data->bus_tx_buf == NULL) {
+		FTS_ERROR("failed to allocate memory for bus_tx_buf");
+		return -ENOMEM;
+	}
+
+	ts_data->bus_rx_buf = kzalloc(SPI_BUF_LENGTH, GFP_KERNEL);
+	if (ts_data->bus_rx_buf == NULL) {
+		FTS_ERROR("failed to allocate memory for bus_rx_buf");
+		return -ENOMEM;
+	}
+
+	ts_data->dummy_byte = SPI_DUMMY_BYTE;
+	FTS_FUNC_EXIT();
+	return 0;
+}
+
+static int fts_spi_exit(struct fts_ts_data *ts_data)
+{
+	FTS_FUNC_ENTER();
+	if (ts_data && ts_data->bus_tx_buf) {
+		kfree(ts_data->bus_tx_buf);
+		ts_data->bus_tx_buf = NULL;
+	}
+
+	if (ts_data && ts_data->bus_rx_buf) {
+		kfree(ts_data->bus_rx_buf);
+		ts_data->bus_rx_buf = NULL;
+	}
+	FTS_FUNC_EXIT();
+	return 0;
+}
+
+int fts_read(u8 *cmd, u32 cmdlen, u8 *data, u32 datalen)
+{
+	int ret = 0;
+
+	if (ts_data->bus_type == BUS_TYPE_I2C)
+		ret = fts_i2c_read(cmd, cmdlen, data, datalen);
+	else
+		ret = fts_spi_read(cmd, cmdlen, data, datalen);
+
+	return ret;
+}
+
+int fts_write(u8 *writebuf, u32 writelen)
+{
+	int ret = 0;
+
+	if (ts_data->bus_type == BUS_TYPE_I2C)
+		ret = fts_i2c_write(writebuf, writelen);
+	else
+		ret = fts_spi_write(writebuf, writelen);
+
+	return ret;
+}
+
+int fts_read_reg(u8 addr, u8 *value)
+{
+	return fts_read(&addr, 1, value, 1);
+}
+
+int fts_write_reg(u8 addr, u8 value)
+{
+	u8 buf[2] = { 0 };
+
+	buf[0] = addr;
+	buf[1] = value;
+	return fts_write(buf, sizeof(buf));
+}
+
+int fts_bus_init(struct fts_ts_data *_ts_data)
+{
+	ts_data = _ts_data;
+
+	if (ts_data->bus_type == BUS_TYPE_I2C)
+		return fts_i2c_init(ts_data);
+
+	return fts_spi_init(ts_data);
+}
+
+int fts_bus_exit(struct fts_ts_data *ts_data)
+{
+	if (ts_data->bus_type == BUS_TYPE_I2C)
+		return fts_i2c_exit(ts_data);
+
+	return fts_spi_exit(ts_data);
+}

+ 135 - 0
qcom/opensource/touch-drivers/focaltech_touch/focaltech_point_report_check.c

@@ -0,0 +1,135 @@
+/*
+ *
+ * FocalTech TouchScreen driver.
+ *
+ * Copyright (c) 2012-2019, FocalTech Systems, Ltd., all rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+/*****************************************************************************
+*
+* File Name: focaltech_point_report_check.c
+*
+* Author: Focaltech Driver Team
+*
+* Created: 2016-11-16
+*
+* Abstract: point report check function
+*
+* Version: v1.0
+*
+* Revision History:
+*
+*****************************************************************************/
+
+/*****************************************************************************
+* Included header files
+*****************************************************************************/
+#include "focaltech_core.h"
+
+#if FTS_POINT_REPORT_CHECK_EN
+/*****************************************************************************
+* Private constant and macro definitions using #define
+*****************************************************************************/
+#define POINT_REPORT_CHECK_WAIT_TIME              200    /* unit:ms */
+
+/*****************************************************************************
+* functions body
+*****************************************************************************/
+/*****************************************************************************
+*  Name: fts_prc_func
+*  Brief: fts point report check work func, report whole up of points
+*  Input:
+*  Output:
+*  Return:
+*****************************************************************************/
+static void fts_prc_func(struct work_struct *work)
+{
+	struct fts_ts_data *ts_data = container_of(work,
+					struct fts_ts_data, prc_work.work);
+	struct input_dev *input_dev = ts_data->input_dev;
+#if FTS_MT_PROTOCOL_B_EN
+	u32 finger_count = 0;
+	u32 max_touches = fts_data->pdata->max_touch_number;
+#endif
+
+	FTS_FUNC_ENTER();
+	mutex_lock(&ts_data->report_mutex);
+
+#if FTS_MT_PROTOCOL_B_EN
+	for (finger_count = 0; finger_count < max_touches; finger_count++) {
+		input_mt_slot(input_dev, finger_count);
+		input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, false);
+	}
+#else
+	input_mt_sync(input_dev);
+#endif
+	input_report_key(input_dev, BTN_TOUCH, 0);
+	input_sync(input_dev);
+
+	mutex_unlock(&ts_data->report_mutex);
+
+	FTS_FUNC_EXIT();
+}
+
+/*****************************************************************************
+*  Name: fts_prc_queue_work
+*  Brief: fts point report check queue work, call it when interrupt comes
+*  Input:
+*  Output:
+*  Return:
+*****************************************************************************/
+void fts_prc_queue_work(struct fts_ts_data *ts_data)
+{
+	cancel_delayed_work_sync(&ts_data->prc_work);
+	queue_delayed_work(ts_data->ts_workqueue, &ts_data->prc_work,
+			msecs_to_jiffies(POINT_REPORT_CHECK_WAIT_TIME));
+}
+
+/*****************************************************************************
+*  Name: fts_point_report_check_init
+*  Brief:
+*  Input:
+*  Output:
+*  Return: < 0: Fail to create esd check queue
+*****************************************************************************/
+int fts_point_report_check_init(struct fts_ts_data *ts_data)
+{
+	FTS_FUNC_ENTER();
+
+	if (ts_data->ts_workqueue) {
+		INIT_DELAYED_WORK(&ts_data->prc_work, fts_prc_func);
+	} else {
+		FTS_ERROR("fts workqueue is NULL, can't run point report check function");
+		return -EINVAL;
+	}
+
+	FTS_FUNC_EXIT();
+	return 0;
+}
+
+/*****************************************************************************
+*  Name: fts_point_report_check_exit
+*  Brief:
+*  Input:
+*  Output:
+*  Return:
+*****************************************************************************/
+int fts_point_report_check_exit(struct fts_ts_data *ts_data)
+{
+	FTS_FUNC_ENTER();
+
+	FTS_FUNC_EXIT();
+	return 0;
+}
+#endif /* FTS_POINT_REPORT_CHECK_EN */
+

+ 126 - 0
qcom/opensource/touch-drivers/glink_interface_ts/glink_interface.c

@@ -0,0 +1,126 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2021, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <glink_interface.h>
+
+static struct glink_touch_dev *touch_pdev = NULL;
+struct touch_channel_ops touch_ops;
+
+
+void glink_touch_channel_init(void (*fn1)(bool), void (*fn2)(void *data, int len))
+{
+	touch_ops.glink_channel_state = fn1;
+	touch_ops.rx_msg = fn2;
+}
+EXPORT_SYMBOL(glink_touch_channel_init);
+
+int glink_touch_tx_msg(void  *msg, size_t len)
+{
+	int ret = 0;
+
+	if (touch_pdev == NULL || !touch_pdev->chnl_state) {
+		pr_err("pmsg_device is null, channel is closed\n");
+		return -ENETRESET;
+	}
+
+	touch_pdev->message = msg;
+	touch_pdev->message_length = len;
+	if (touch_pdev->message) {
+		ret = rpmsg_send(touch_pdev->channel,
+			touch_pdev->message, touch_pdev->message_length);
+		if (ret)
+			pr_err("rpmsg_send failed: %d\n", ret);
+
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(glink_touch_tx_msg);
+
+static int glink_touch_probe(struct rpmsg_device  *touch_rpdev)
+{
+	int ret = 0;
+	void *msg = NULL;
+
+	if (!touch_rpdev) {
+		pr_err("rpmsg_device is null\n");
+		return -ENODEV;
+	}
+
+	pr_info("%s Start of glink_touch_probe\n", __func__);
+	touch_pdev = devm_kzalloc(&touch_rpdev->dev, sizeof(*touch_pdev), GFP_KERNEL);
+	if (!touch_pdev)
+		return -ENOMEM;
+
+	touch_pdev->channel = touch_rpdev->ept;
+	touch_pdev->dev = &touch_rpdev->dev;
+	if (touch_pdev->channel == NULL)
+		return -ENOMEM;
+		
+	touch_pdev->chnl_state = true;
+	dev_set_drvdata(&touch_rpdev->dev, touch_pdev);
+	
+	/* send a callback to slate-MSM touch driver*/
+	touch_ops.glink_channel_state(true);
+	if (touch_pdev->message == NULL)
+		ret = glink_touch_tx_msg(msg, 0);
+	
+	pr_info("%s End of glink_touch_probe\n", __func__);
+	return 0;
+}
+
+static void glink_touch_remove(struct rpmsg_device *touch_rpdev)
+{
+	touch_pdev->chnl_state = false;
+	touch_pdev->message = NULL;
+	dev_dbg(&touch_rpdev->dev, "rpmsg client driver is removed\n");
+	touch_ops.glink_channel_state(false);
+	dev_set_drvdata(&touch_rpdev->dev, NULL);
+	
+}
+
+static int glink_touch_cb(struct rpmsg_device *touch_rpdev,
+				void *data, int len, void *priv, u32 src)
+{
+	struct glink_touch_dev *touch_dev =
+			dev_get_drvdata(&touch_rpdev->dev);
+
+	if (!touch_dev)
+		return -ENODEV;
+	touch_ops.rx_msg(data, len);
+
+	return 0;
+}
+
+static const struct rpmsg_device_id glink_touch_driver_id_table[] = {
+	{ "touch-ctrl" },
+	{},
+};
+MODULE_DEVICE_TABLE(rpmsg, glink_touch_driver_id_table);
+
+static const struct of_device_id glink_touch_driver_of_match[] = {
+	{ .compatible = "qcom,slatetouch-rpmsg" },
+	{},
+};
+
+static struct rpmsg_driver glink_touch_client = {
+	.id_table = glink_touch_driver_id_table,
+	.probe = glink_touch_probe,
+	.callback = glink_touch_cb,
+	.remove = glink_touch_remove,
+	.drv = {
+		.name = "glink_interface",
+		.of_match_table = glink_touch_driver_of_match,
+	},
+};
+module_rpmsg_driver(glink_touch_client);
+
+MODULE_DESCRIPTION("Interface Driver for MSM TOUCH and RPMSG");
+MODULE_LICENSE("GPL v2");

+ 74 - 0
qcom/opensource/touch-drivers/glink_interface_ts/glink_interface.h

@@ -0,0 +1,74 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2021, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef GLINKINTERFACE_H
+#define GLINKINTERFACE_H
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+#include <linux/rpmsg.h>
+
+
+#define	TOUCH_GLINK_INTENT_SIZE	0x0c
+#define TOUCH_MSG_SIZE 0x08
+#define TIMEOUT_MS 2000
+
+struct glink_touch_priv {
+ 	void *handle;
+ 	struct mutex glink_mutex;
+ 	struct mutex touch_state_mutex;
+ 	void *lhndl;
+ 	char rx_buf[TOUCH_GLINK_INTENT_SIZE];
+ 	bool glink_touch_cmplt;
+	wait_queue_head_t link_state_wait;
+ 	bool msm_touch_rpmsg;
+};
+
+static void *glink_touch_drv;
+
+enum touch_slate_cmds {
+	TOUCH_ENTER_PREPARE = 0x1100,
+	TOUCH_ENTER,
+	TOUCH_EXIT_PREPARE,
+	TOUCH_EXIT
+};
+
+struct glink_touch_dev {
+	struct rpmsg_endpoint *channel;
+	struct device *dev;
+	bool chnl_state;
+	void *message;
+	size_t message_length;
+};
+
+struct touch_channel_ops {
+	void (*glink_channel_state)(bool state);
+	void (*rx_msg)(void *data, int len);
+};
+
+void glink_touch_channel_init(void (*fn1)(bool), void (*fn2)(void *, int));
+
+
+
+#if IS_ENABLED(CONFIG_MSM_SLATERSB_RPMSG)
+int glink_touch_tx_msg(void  *msg, size_t len);
+#else
+static inline int glink_touch_tx_msg(void  *msg, size_t len)
+{
+	return -EIO;
+}
+#endif
+
+#endif

+ 1360 - 0
qcom/opensource/touch-drivers/goodix_berlin_driver/goodix_brl_fwupdate.c

@@ -0,0 +1,1360 @@
+/*
+ * Goodix Touchscreen Driver
+ * Copyright (C) 2020 - 2021 Goodix, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be a reference
+ * to you, when you are integrating the GOODiX's CTP IC into your system,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ */
+#include "goodix_ts_core.h"
+
+#define BUS_TYPE_SPI					1
+#define BUS_TYPE_I2C					0
+
+#define GOODIX_BUS_RETRY_TIMES			3
+
+#define FW_HEADER_SIZE_BRA				256
+#define FW_HEADER_SIZE					512
+#define FW_SUBSYS_INFO_SIZE				10
+#define FW_SUBSYS_INFO_OFFSET_BRA		36
+#define FW_SUBSYS_INFO_OFFSET			42
+#define FW_SUBSYS_MAX_NUM				47
+
+#define ISP_MAX_BUFFERSIZE				4096
+
+#define FW_PID_LEN						8
+#define FW_VID_LEN						4
+#define FLASH_CMD_LEN					11
+
+#define FW_FILE_CHECKSUM_OFFSET			8
+#define CONFIG_DATA_TYPE				4
+
+#define ISP_RAM_ADDR_BRA				0x18400
+#define ISP_RAM_ADDR_BRB				0x57000
+#define ISP_RAM_ADDR_BRD				0x23800
+#define HW_REG_CPU_RUN_FROM				0x10000
+#define FLASH_CMD_REG_BRA				0x10400
+#define FLASH_CMD_REG_BRB				0x13400
+#define FLASH_CMD_REG_BRD				0x12400
+#define HW_REG_ISP_BUFFER_BRA			0x10410
+#define HW_REG_ISP_BUFFER_BRB			0x13410
+#define HW_REG_ISP_BUFFER_BRD			0x12410
+#define CONFIG_DATA_ADDR_BRA			0x3E000
+#define CONFIG_DATA_ADDR_BRB			0x40000
+#define CONFIG_DATA_ADDR_BRD			0x3E000
+#define GOODIX_CFG_ID_ADDR_BRA			0x1006E
+#define GOODIX_CFG_ID_ADDR_BRB			0x10076
+#define GOODIX_CFG_ID_ADDR_BRD			0x10076
+
+#define HOLD_CPU_REG_W					0x0002
+#define HOLD_CPU_REG_R					0x2000
+#define MISCTL_REG_BRA					0xD807
+#define MISCTL_REG_BRB					0xD80B
+#define MISCTL_REG_BRD					0xD804
+#define ENABLE_MISCTL_BRA				0x08
+#define ENABLE_MISCTL_BRB				0x40
+#define ENABLE_MISCTL_BRD				0x20700000
+#define ESD_KEY_REG						0xCC58
+#define WATCH_DOG_REG_BRA				0xCC54
+#define WATCH_DOG_REG_BRB				0xD054
+#define WATCH_DOG_REG_BRD				0xD040
+
+#define FLASH_CMD_TYPE_READ				0xAA
+#define FLASH_CMD_TYPE_WRITE			0xBB
+#define FLASH_CMD_ACK_CHK_PASS			0xEE
+#define FLASH_CMD_ACK_CHK_ERROR			0x33
+#define FLASH_CMD_ACK_IDLE				0x11
+#define FLASH_CMD_W_STATUS_CHK_PASS		0x22
+#define FLASH_CMD_W_STATUS_CHK_FAIL		0x33
+#define FLASH_CMD_W_STATUS_ADDR_ERR		0x44
+#define FLASH_CMD_W_STATUS_WRITE_ERR	0x55
+#define FLASH_CMD_W_STATUS_WRITE_OK		0xEE
+
+#define CHIP_TYPE_BRA					0x96
+#define CHIP_TYPE_BRB					0x97
+#define CHIP_TYPE_BRD					0x98
+
+
+struct update_info_t {
+	int header_size;
+	int subsys_info_offset;
+	u32 isp_ram_reg;
+	u32 flash_cmd_reg;
+	u32 isp_buffer_reg;
+	u32 config_data_reg;
+	u32 misctl_reg;
+	u32 watch_dog_reg;
+	u32 config_id_reg;
+	u32 enable_misctl_val;
+};
+
+/* berlinA update into */
+struct update_info_t update_bra = {
+	FW_HEADER_SIZE_BRA,
+	FW_SUBSYS_INFO_OFFSET_BRA,
+	ISP_RAM_ADDR_BRA,
+	FLASH_CMD_REG_BRA,
+	HW_REG_ISP_BUFFER_BRA,
+	CONFIG_DATA_ADDR_BRA,
+	MISCTL_REG_BRA,
+	WATCH_DOG_REG_BRA,
+	GOODIX_CFG_ID_ADDR_BRA,
+	ENABLE_MISCTL_BRA,
+};
+
+/* berlinB update info */
+struct update_info_t update_brb = {
+	FW_HEADER_SIZE,
+	FW_SUBSYS_INFO_OFFSET,
+	ISP_RAM_ADDR_BRB,
+	FLASH_CMD_REG_BRB,
+	HW_REG_ISP_BUFFER_BRB,
+	CONFIG_DATA_ADDR_BRB,
+	MISCTL_REG_BRB,
+	WATCH_DOG_REG_BRB,
+	GOODIX_CFG_ID_ADDR_BRB,
+	ENABLE_MISCTL_BRB,
+};
+
+/* berlinD update info */
+struct update_info_t update_brd = {
+	FW_HEADER_SIZE,
+	FW_SUBSYS_INFO_OFFSET,
+	ISP_RAM_ADDR_BRD,
+	FLASH_CMD_REG_BRD,
+	HW_REG_ISP_BUFFER_BRD,
+	CONFIG_DATA_ADDR_BRD,
+	MISCTL_REG_BRD,
+	WATCH_DOG_REG_BRD,
+	GOODIX_CFG_ID_ADDR_BRD,
+	ENABLE_MISCTL_BRD,
+};
+
+/**
+ * fw_subsys_info - subsytem firmware information
+ * @type: sybsystem type
+ * @size: firmware size
+ * @flash_addr: flash address
+ * @data: firmware data
+ */
+struct fw_subsys_info {
+	u8 type;
+	u32 size;
+	u32 flash_addr;
+	const u8 *data;
+};
+
+/**
+ *  firmware_summary
+ * @size: fw total length
+ * @checksum: checksum of fw
+ * @hw_pid: mask pid string
+ * @hw_pid: mask vid code
+ * @fw_pid: fw pid string
+ * @fw_vid: fw vid code
+ * @subsys_num: number of fw subsystem
+ * @chip_type: chip type
+ * @protocol_ver: firmware packing
+ *   protocol version
+ * @bus_type: 0 represent I2C, 1 for SPI
+ * @subsys: sybsystem info
+ */
+#pragma pack(1)
+struct  firmware_summary {
+	u32 size;
+	u32 checksum;
+	u8 hw_pid[6];
+	u8 hw_vid[3];
+	u8 fw_pid[FW_PID_LEN];
+	u8 fw_vid[FW_VID_LEN];
+	u8 subsys_num;
+	u8 chip_type;
+	u8 protocol_ver;
+	u8 bus_type;
+	u8 flash_protect;
+	// u8 reserved[8];
+	struct fw_subsys_info subsys[FW_SUBSYS_MAX_NUM];
+};
+#pragma pack()
+
+/**
+ * firmware_data - firmware data structure
+ * @fw_summary: firmware information
+ * @firmware: firmware data structure
+ */
+struct firmware_data {
+	struct firmware_summary fw_summary;
+	const struct firmware *firmware;
+	struct firmware *fw_sysfs;
+};
+
+struct config_data {
+	u8 *data;
+	int size;
+};
+
+#pragma pack(1)
+struct goodix_flash_cmd {
+	union {
+		struct {
+			u8 status;
+			u8 ack;
+			u8 len;
+			u8 cmd;
+			u8 fw_type;
+			u16 fw_len;
+			u32 fw_addr;
+			//u16 checksum;
+		};
+		u8 buf[16];
+	};
+};
+#pragma pack()
+
+enum update_status {
+	UPSTA_NOTWORK = 0,
+	UPSTA_PREPARING,
+	UPSTA_UPDATING,
+	UPSTA_SUCCESS,
+	UPSTA_FAILED
+};
+
+enum compare_status {
+	COMPARE_EQUAL = 0,
+	COMPARE_NOCODE,
+	COMPARE_PIDMISMATCH,
+	COMPARE_FW_NOTEQUAL,
+	COMPARE_CFG_NOTEQUAL,
+};
+
+/**
+ * fw_update_ctrl - structure used to control the
+ *  firmware update process
+ * @initialized: struct init state
+ * @mode: indicate weather reflash config or not, fw data source,
+ *        and run on block mode or not.
+ * @status: update status
+ * @progress: indicate the progress of update
+ * @fw_data: firmware data
+ * @fw_name: firmware name
+ * @attr_fwimage: sysfs bin attrs, for storing fw image
+ * @fw_data_src: firmware data source form sysfs, request or head file
+ * @kobj: pointer to the sysfs kobject
+ */
+struct fw_update_ctrl {
+	struct mutex mutex;
+	int initialized;
+	char fw_name[GOODIX_MAX_STR_LABLE_LEN];
+	int mode;
+	enum update_status status;
+	int spend_time;
+
+	struct firmware_data fw_data;
+	struct goodix_ic_config *ic_config;
+	struct goodix_ts_core *core_data;
+	struct update_info_t *update_info;
+
+	struct bin_attribute attr_fwimage;
+	struct kobject *kobj;
+};
+static struct fw_update_ctrl goodix_fw_update_ctrl;
+
+static int goodix_fw_update_reset(int delay)
+{
+	struct goodix_ts_hw_ops *hw_ops;
+
+	hw_ops = goodix_fw_update_ctrl.core_data->hw_ops;
+	return hw_ops->reset(goodix_fw_update_ctrl.core_data, delay);
+}
+
+static int get_fw_version_info(struct goodix_fw_version *fw_version)
+{
+	struct goodix_ts_hw_ops *hw_ops =
+		goodix_fw_update_ctrl.core_data->hw_ops;
+
+	return hw_ops->read_version(goodix_fw_update_ctrl.core_data,
+				fw_version);
+}
+
+static int goodix_reg_write(unsigned int addr,
+			unsigned char *data, unsigned int len)
+{
+	struct goodix_ts_hw_ops *hw_ops =
+			goodix_fw_update_ctrl.core_data->hw_ops;
+
+	return hw_ops->write(goodix_fw_update_ctrl.core_data,
+			addr, data, len);
+}
+
+static int goodix_reg_read(unsigned int addr,
+			unsigned char *data, unsigned int len)
+{
+	struct goodix_ts_hw_ops *hw_ops =
+			goodix_fw_update_ctrl.core_data->hw_ops;
+
+	return hw_ops->read(goodix_fw_update_ctrl.core_data,
+			addr, data, len);
+}
+
+/**
+ * goodix_parse_firmware - parse firmware header information
+ *	and subsystem information from firmware data buffer
+ *
+ * @fw_data: firmware struct, contains firmware header info
+ *	and firmware data.
+ * return: 0 - OK, < 0 - error
+ */
+/* sizeof(length) + sizeof(checksum) */
+
+static int goodix_parse_firmware(struct firmware_data *fw_data)
+{
+	const struct firmware *firmware;
+	struct  firmware_summary *fw_summary;
+	unsigned int i, fw_offset, info_offset;
+	u32 checksum;
+	int ic_type =
+		goodix_fw_update_ctrl.core_data->bus->ic_type;
+	int subsys_info_offset =
+		goodix_fw_update_ctrl.update_info->subsys_info_offset;
+	int header_size =
+		goodix_fw_update_ctrl.update_info->header_size;
+	int r = 0;
+
+	fw_summary = &fw_data->fw_summary;
+
+	/* copy firmware head info */
+	if (goodix_fw_update_ctrl.mode & UPDATE_MODE_SRC_SYSFS)
+		firmware = fw_data->fw_sysfs;
+	else
+		firmware = fw_data->firmware;
+
+	if (firmware->size < subsys_info_offset) {
+		ts_err("Invalid firmware size:%zu", firmware->size);
+		r = -EINVAL;
+		goto err_size;
+	}
+	memcpy(fw_summary, firmware->data, sizeof(*fw_summary));
+
+	/* check firmware size */
+	fw_summary->size = le32_to_cpu(fw_summary->size);
+	if (firmware->size != fw_summary->size + FW_FILE_CHECKSUM_OFFSET) {
+		ts_err("Bad firmware, size not match, %zu != %d",
+				firmware->size,
+				fw_summary->size + FW_FILE_CHECKSUM_OFFSET);
+		r = -EINVAL;
+		goto err_size;
+	}
+
+	for (i = FW_FILE_CHECKSUM_OFFSET, checksum = 0;
+	     i < firmware->size; i += 2)
+		checksum += firmware->data[i] + (firmware->data[i+1] << 8);
+
+	/* byte order change, and check */
+	fw_summary->checksum = le32_to_cpu(fw_summary->checksum);
+	if (checksum != fw_summary->checksum) {
+		ts_err("Bad firmware, cheksum error");
+		r = -EINVAL;
+		goto err_size;
+	}
+
+	if (fw_summary->subsys_num > FW_SUBSYS_MAX_NUM) {
+		ts_err("Bad firmware, invalid subsys num: %d",
+			fw_summary->subsys_num);
+		r = -EINVAL;
+		goto err_size;
+	}
+
+	/* parse subsystem info */
+	fw_offset = header_size;
+	for (i = 0; i < fw_summary->subsys_num; i++) {
+		info_offset = subsys_info_offset +
+					i * FW_SUBSYS_INFO_SIZE;
+
+		fw_summary->subsys[i].type = firmware->data[info_offset];
+		fw_summary->subsys[i].size =
+			le32_to_cpup((__le32 *)&firmware->data[info_offset + 1]);
+
+		fw_summary->subsys[i].flash_addr =
+			le32_to_cpup((__le32 *)&firmware->data[info_offset + 5]);
+		if (fw_offset > firmware->size) {
+			ts_err("Sybsys offset exceed Firmware size");
+			goto err_size;
+		}
+
+		fw_summary->subsys[i].data = firmware->data + fw_offset;
+		fw_offset += fw_summary->subsys[i].size;
+	}
+
+	ts_info("Firmware package protocol: V%u", fw_summary->protocol_ver);
+	ts_info("Firmware PID:GT%s", fw_summary->fw_pid);
+	ts_info("Firmware VID:%*ph", 4, fw_summary->fw_vid);
+	ts_info("Firmware chip type:0x%02X", fw_summary->chip_type);
+	ts_info("Firmware bus type:%s",
+		(fw_summary->bus_type & BUS_TYPE_SPI) ? "SPI" : "I2C");
+	ts_info("Firmware size:%u", fw_summary->size);
+	ts_info("Firmware subsystem num:%u", fw_summary->subsys_num);
+
+	for (i = 0; i < fw_summary->subsys_num; i++) {
+		ts_debug("------------------------------------------");
+		ts_debug("Index:%d", i);
+		ts_debug("Subsystem type:%02X", fw_summary->subsys[i].type);
+		ts_debug("Subsystem size:%u", fw_summary->subsys[i].size);
+		ts_debug("Subsystem flash_addr:%08X",
+				fw_summary->subsys[i].flash_addr);
+		ts_debug("Subsystem Ptr:%p", fw_summary->subsys[i].data);
+	}
+
+	if (fw_summary->chip_type == CHIP_TYPE_BRA &&
+		ic_type != IC_TYPE_BERLIN_A) {
+		ts_err("ic type mismatch!");
+		r = -EINVAL;
+	} else if (fw_summary->chip_type == CHIP_TYPE_BRB &&
+		ic_type != IC_TYPE_BERLIN_B) {
+		ts_err("ic type mismatch!");
+		r = -EINVAL;
+	} else if (fw_summary->chip_type == CHIP_TYPE_BRD &&
+		ic_type != IC_TYPE_BERLIN_D) {
+		ts_err("ic type mismatch!");
+		r = -EINVAL;
+	}
+
+err_size:
+	return r;
+}
+
+/**
+ * goodix_fw_version_compare - compare the active version with
+ * firmware file version.
+ * @fwu_ctrl: firmware information to be compared
+ * return: 0 equal, < 0 unequal
+ */
+#define GOODIX_NOCODE "NOCODE"
+static int goodix_fw_version_compare(struct fw_update_ctrl *fwu_ctrl)
+{
+	int ret = 0;
+	struct goodix_fw_version fw_version;
+	struct firmware_summary *fw_summary = &fwu_ctrl->fw_data.fw_summary;
+	u32 config_id_reg = goodix_fw_update_ctrl.update_info->config_id_reg;
+	u32 file_cfg_id;
+	u32 ic_cfg_id;
+
+	/* compare fw_version */
+	ret = get_fw_version_info(&fw_version);
+	if (ret)
+		return -EINVAL;
+
+	if (!memcmp(fw_version.rom_pid, GOODIX_NOCODE, 6) ||
+		!memcmp(fw_version.patch_pid, GOODIX_NOCODE, 6)) {
+		ts_info("there is no code in the chip");
+		return COMPARE_NOCODE;
+	}
+
+	if (memcmp(fw_version.patch_pid, fw_summary->fw_pid, FW_PID_LEN)) {
+		ts_err("Product ID mismatch:%s != %s",
+			fw_version.patch_pid, fw_summary->fw_pid);
+		return COMPARE_PIDMISMATCH;
+	}
+
+	ret = memcmp(fw_version.patch_vid, fw_summary->fw_vid, FW_VID_LEN);
+	if (ret) {
+		ts_info("active firmware version:%*ph", FW_VID_LEN,
+				fw_version.patch_vid);
+		ts_info("firmware file version: %*ph", FW_VID_LEN,
+				fw_summary->fw_vid);
+		return COMPARE_FW_NOTEQUAL;
+	}
+	ts_info("fw_version equal");
+
+	/* compare config id */
+	if (fwu_ctrl->ic_config && fwu_ctrl->ic_config->len > 0) {
+		file_cfg_id =
+			goodix_get_file_config_id(fwu_ctrl->ic_config->data);
+		goodix_reg_read(config_id_reg,
+			(u8 *)&ic_cfg_id, sizeof(ic_cfg_id));
+		if (ic_cfg_id != file_cfg_id) {
+			ts_info("ic_cfg_id:0x%x != file_cfg_id:0x%x",
+				ic_cfg_id, file_cfg_id);
+			return COMPARE_CFG_NOTEQUAL;
+		}
+		ts_info("config_id equal");
+	}
+
+	return COMPARE_EQUAL;
+}
+
+/**
+ * goodix_reg_write_confirm - write register and confirm the value
+ *  in the register.
+ * @dev: pointer to touch device
+ * @addr: register address
+ * @data: pointer to data buffer
+ * @len: data length
+ * return: 0 write success and confirm ok
+ *		   < 0 failed
+ */
+static int goodix_reg_write_confirm(unsigned int addr,
+		unsigned char *data, unsigned int len)
+{
+	u8 *cfm = NULL;
+	u8 cfm_buf[32];
+	int r, i;
+
+	if (len > sizeof(cfm_buf)) {
+		cfm = kzalloc(len, GFP_KERNEL);
+		if (!cfm)
+			return -ENOMEM;
+	} else {
+		cfm = &cfm_buf[0];
+	}
+
+	for (i = 0; i < GOODIX_BUS_RETRY_TIMES; i++) {
+		r = goodix_reg_write(addr, data, len);
+		if (r < 0)
+			goto exit;
+
+		r = goodix_reg_read(addr, cfm, len);
+		if (r < 0)
+			goto exit;
+
+		if (memcmp(data, cfm, len)) {
+			r = -EINVAL;
+			continue;
+		} else {
+			r = 0;
+			break;
+		}
+	}
+
+exit:
+	if (cfm != &cfm_buf[0])
+		kfree(cfm);
+	return r;
+}
+
+
+/**
+ * goodix_load_isp - load ISP program to device ram
+ * @dev: pointer to touch device
+ * @fw_data: firmware data
+ * return 0 ok, <0 error
+ */
+static int goodix_load_isp(struct firmware_data *fw_data)
+{
+	struct goodix_fw_version isp_fw_version;
+	struct fw_subsys_info *fw_isp;
+	u32 isp_ram_reg = goodix_fw_update_ctrl.update_info->isp_ram_reg;
+	u8 reg_val[8] = {0x00};
+	int r;
+
+	memset(&isp_fw_version, 0, sizeof(isp_fw_version));
+	fw_isp = &fw_data->fw_summary.subsys[0];
+
+	ts_info("Loading ISP start");
+	r = goodix_reg_write_confirm(isp_ram_reg,
+					(u8 *)fw_isp->data, fw_isp->size);
+	if (r < 0) {
+		ts_err("Loading ISP error");
+		return r;
+	}
+
+	ts_info("Success send ISP data");
+
+	/* SET BOOT OPTION TO 0X55 */
+	memset(reg_val, 0x55, 8);
+	r = goodix_reg_write_confirm(HW_REG_CPU_RUN_FROM, reg_val, 8);
+	if (r < 0) {
+		ts_err("Failed set REG_CPU_RUN_FROM flag");
+		return r;
+	}
+	ts_info("Success write [8]0x55 to 0x%x", HW_REG_CPU_RUN_FROM);
+
+	if (goodix_fw_update_reset(100))
+		ts_err("reset abnormal");
+	/*check isp state */
+	if (get_fw_version_info(&isp_fw_version)) {
+		ts_err("failed read isp version");
+		return -2;
+	}
+	if (memcmp(&isp_fw_version.patch_pid[3], "ISP", 3)) {
+		ts_err("patch id error %c%c%c != %s",
+		isp_fw_version.patch_pid[3], isp_fw_version.patch_pid[4],
+		isp_fw_version.patch_pid[5], "ISP");
+		return -3;
+	}
+	ts_info("ISP running successfully");
+	return 0;
+}
+
+/**
+ * goodix_update_prepare - update prepare, loading ISP program
+ *  and make sure the ISP is running.
+ * @fwu_ctrl: pointer to fimrware control structure
+ * return: 0 ok, <0 error
+ */
+static int goodix_update_prepare(struct fw_update_ctrl *fwu_ctrl)
+{
+	u32 misctl_reg = fwu_ctrl->update_info->misctl_reg;
+	u32 watch_dog_reg = fwu_ctrl->update_info->watch_dog_reg;
+	u32 enable_misctl_val = fwu_ctrl->update_info->enable_misctl_val;
+	u8 reg_val[4] = {0};
+	u8 temp_buf[64] = {0};
+	int retry = 20;
+	int r;
+
+	/*reset IC*/
+	ts_info("firmware update, reset");
+	if (goodix_fw_update_reset(5))
+		ts_err("reset abnormal");
+
+	retry = 100;
+	/* Hold cpu*/
+	do {
+		reg_val[0] = 0x01;
+		reg_val[1] = 0x00;
+		r = goodix_reg_write(HOLD_CPU_REG_W, reg_val, 2);
+		r |= goodix_reg_read(HOLD_CPU_REG_R, &temp_buf[0], 4);
+		r |= goodix_reg_read(HOLD_CPU_REG_R, &temp_buf[4], 4);
+		r |= goodix_reg_read(HOLD_CPU_REG_R, &temp_buf[8], 4);
+		if (!r && !memcmp(&temp_buf[0], &temp_buf[4], 4) &&
+			!memcmp(&temp_buf[4], &temp_buf[8], 4) &&
+			!memcmp(&temp_buf[0], &temp_buf[8], 4)) {
+			break;
+		}
+		usleep_range(1000, 1100);
+		ts_info("retry hold cpu %d", retry);
+		ts_debug("data:%*ph", 12, temp_buf);
+	} while (--retry);
+	if (!retry) {
+		ts_err("Failed to hold CPU, return =%d", r);
+		return -1;
+	}
+	ts_info("Success hold CPU");
+
+	/* enable misctl clock */
+	if (fwu_ctrl->core_data->bus->ic_type == IC_TYPE_BERLIN_D)
+		goodix_reg_write(misctl_reg, (u8 *)&enable_misctl_val, 4);
+	else
+		goodix_reg_write(misctl_reg, (u8 *)&enable_misctl_val, 1);
+	ts_info("enbale misctl clock");
+
+	if (fwu_ctrl->core_data->bus->ic_type == IC_TYPE_BERLIN_A) {
+		/* open ESD_KEY */
+		retry = 20;
+		do {
+			reg_val[0] = 0x95;
+			r = goodix_reg_write(ESD_KEY_REG, reg_val, 1);
+			r |= goodix_reg_read(ESD_KEY_REG, temp_buf, 1);
+			if (!r && temp_buf[0] == 0x01)
+				break;
+			usleep_range(1000, 1100);
+			ts_info("retry %d enable esd key, 0x%x",
+				retry, temp_buf[0]);
+		} while (--retry);
+		if (!retry) {
+			ts_err("Failed to enable esd key, return =%d", r);
+			return -2;
+		}
+		ts_info("success enable esd key");
+	}
+
+	/* disable watch dog */
+	reg_val[0] = 0x00;
+	r = goodix_reg_write(watch_dog_reg, reg_val, 1);
+	ts_info("disable watch dog");
+
+	/* load ISP code and run form isp */
+	r = goodix_load_isp(&fwu_ctrl->fw_data);
+	if (r < 0)
+		ts_err("Failed load and run isp");
+
+	return r;
+}
+
+/*	goodix_send_flash_cmd: send command to read or write flash data
+ *	@flash_cmd: command need to send.
+ */
+static int goodix_send_flash_cmd(struct goodix_flash_cmd *flash_cmd)
+{
+	int i, ret, retry;
+	struct goodix_flash_cmd tmp_cmd;
+	u32 flash_cmd_reg = goodix_fw_update_ctrl.update_info->flash_cmd_reg;
+
+	ts_info("try send flash cmd:%*ph", (int)sizeof(flash_cmd->buf),
+		flash_cmd->buf);
+	memset(tmp_cmd.buf, 0, sizeof(tmp_cmd));
+	ret = goodix_reg_write(flash_cmd_reg,
+		flash_cmd->buf, sizeof(flash_cmd->buf));
+	if (ret) {
+		ts_err("failed send flash cmd %d", ret);
+		return ret;
+	}
+
+	retry = 5;
+	for (i = 0; i < retry; i++) {
+		ret = goodix_reg_read(flash_cmd_reg,
+			tmp_cmd.buf, sizeof(tmp_cmd.buf));
+		if (!ret && tmp_cmd.ack == FLASH_CMD_ACK_CHK_PASS)
+			break;
+		usleep_range(5000, 5100);
+		ts_info("flash cmd ack error retry %d, ack 0x%x, ret %d",
+			i, tmp_cmd.ack, ret);
+	}
+	if (tmp_cmd.ack != FLASH_CMD_ACK_CHK_PASS) {
+		ts_err("flash cmd ack error, ack 0x%x, ret %d",
+			tmp_cmd.ack, ret);
+		ts_err("data:%*ph", (int)sizeof(tmp_cmd.buf), tmp_cmd.buf);
+		return -EINVAL;
+	}
+	ts_info("flash cmd ack check pass");
+
+	msleep(50);
+	retry = 20;
+	for (i = 0; i < retry; i++) {
+		ret = goodix_reg_read(flash_cmd_reg,
+			tmp_cmd.buf, sizeof(tmp_cmd.buf));
+		if (!ret && tmp_cmd.ack == FLASH_CMD_ACK_CHK_PASS &&
+			tmp_cmd.status == FLASH_CMD_W_STATUS_WRITE_OK) {
+			ts_info("flash status check pass");
+			return 0;
+		}
+
+		ts_info("flash cmd status not ready, retry %d, ack 0x%x, status 0x%x, ret %d",
+				i, tmp_cmd.ack, tmp_cmd.status, ret);
+		usleep_range(10000, 11000);
+	}
+
+	ts_err("flash cmd status error %d, ack 0x%x, status 0x%x, ret %d",
+		i, tmp_cmd.ack, tmp_cmd.status, ret);
+	if (ret) {
+		ts_info("reason: bus or paltform error");
+		return -EINVAL;
+	}
+
+	switch (tmp_cmd.status) {
+	case FLASH_CMD_W_STATUS_CHK_PASS:
+		ts_err("data check pass, but failed get follow-up results");
+		return -EFAULT;
+	case FLASH_CMD_W_STATUS_CHK_FAIL:
+		ts_err("data check failed, please retry");
+		return -EAGAIN;
+	case FLASH_CMD_W_STATUS_ADDR_ERR:
+		ts_err("flash target addr error, please check");
+		return -EFAULT;
+	case FLASH_CMD_W_STATUS_WRITE_ERR:
+		ts_err("flash data write err, please retry");
+		return -EAGAIN;
+	default:
+		ts_err("unknown status");
+		return -EFAULT;
+	}
+}
+
+static int goodix_flash_package(u8 subsys_type, u8 *pkg,
+	u32 flash_addr, u16 pkg_len)
+{
+	int ret, retry;
+	struct goodix_flash_cmd flash_cmd;
+	u32 isp_buffer_reg = goodix_fw_update_ctrl.update_info->isp_buffer_reg;
+
+	retry = 2;
+	do {
+		ret = goodix_reg_write(isp_buffer_reg, pkg, pkg_len);
+		if (ret < 0) {
+			ts_err("Failed to write firmware packet");
+			return ret;
+		}
+
+		flash_cmd.status = 0;
+		flash_cmd.ack = 0;
+		flash_cmd.len = FLASH_CMD_LEN;
+		flash_cmd.cmd = FLASH_CMD_TYPE_WRITE;
+		flash_cmd.fw_type = subsys_type;
+		flash_cmd.fw_len = cpu_to_le16(pkg_len);
+		flash_cmd.fw_addr = cpu_to_le32(flash_addr);
+
+		goodix_append_checksum(&(flash_cmd.buf[2]),
+				9, CHECKSUM_MODE_U8_LE);
+
+		ret = goodix_send_flash_cmd(&flash_cmd);
+		if (!ret) {
+			ts_info("success write package to 0x%x, len %d",
+				flash_addr, pkg_len - 4);
+			return 0;
+		}
+	} while (ret == -EAGAIN && --retry);
+
+	return ret;
+}
+
+/**
+ * goodix_flash_subsystem - flash subsystem firmware,
+ *  Main flow of flashing firmware.
+ *	Each firmware subsystem is divided into several
+ *	packets, the max size of packet is limited to
+ *	@{ISP_MAX_BUFFERSIZE}
+ * @dev: pointer to touch device
+ * @subsys: subsystem information
+ * return: 0 ok, < 0 error
+ */
+static int goodix_flash_subsystem(struct fw_subsys_info *subsys)
+{
+	u32 data_size, offset;
+	u32 total_size;
+	//TODO: confirm flash addr ,<< 8??
+	u32 subsys_base_addr = subsys->flash_addr;
+	u8 *fw_packet = NULL;
+	int r = 0;
+
+	/*
+	 * if bus(i2c/spi) error occued, then exit, we will do
+	 * hardware reset and re-prepare ISP and then retry
+	 * flashing
+	 */
+	total_size = subsys->size;
+	fw_packet = kzalloc(ISP_MAX_BUFFERSIZE + 4, GFP_KERNEL);
+	if (!fw_packet) {
+		ts_err("Failed alloc memory");
+		return -EINVAL;
+	}
+
+	offset = 0;
+	while (total_size > 0) {
+		data_size = total_size > ISP_MAX_BUFFERSIZE ?
+				ISP_MAX_BUFFERSIZE : total_size;
+		ts_info("Flash firmware to %08x,size:%u bytes",
+			subsys_base_addr + offset, data_size);
+
+		memcpy(fw_packet, &subsys->data[offset], data_size);
+		/* set checksum for package data */
+		goodix_append_checksum(fw_packet,
+				data_size, CHECKSUM_MODE_U16_LE);
+
+		r = goodix_flash_package(subsys->type, fw_packet,
+				subsys_base_addr + offset, data_size + 4);
+		if (r) {
+			ts_err("failed flash to %08x,size:%u bytes",
+			subsys_base_addr + offset, data_size);
+			break;
+		}
+		offset += data_size;
+		total_size -= data_size;
+	} /* end while */
+
+	kfree(fw_packet);
+	return r;
+}
+
+/**
+ * goodix_flash_firmware - flash firmware
+ * @dev: pointer to touch device
+ * @fw_data: firmware data
+ * return: 0 ok, < 0 error
+ */
+static int goodix_flash_firmware(struct fw_update_ctrl *fw_ctrl)
+{
+	struct firmware_data *fw_data = &fw_ctrl->fw_data;
+	struct  firmware_summary  *fw_summary;
+	struct fw_subsys_info *fw_x;
+	struct fw_subsys_info subsys_cfg = {0};
+	u32 config_data_reg = fw_ctrl->update_info->config_data_reg;
+	int retry = GOODIX_BUS_RETRY_TIMES;
+	int i, r = 0, fw_num;
+
+	/*	start from subsystem 1,
+	 *	subsystem 0 is the ISP program
+	 */
+
+	fw_summary = &fw_data->fw_summary;
+	fw_num = fw_summary->subsys_num;
+
+	/* flash config data first if we have */
+	if (fw_ctrl->ic_config && fw_ctrl->ic_config->len) {
+		subsys_cfg.data = fw_ctrl->ic_config->data;
+		subsys_cfg.size = fw_ctrl->ic_config->len;
+		subsys_cfg.flash_addr = config_data_reg;
+		subsys_cfg.type = CONFIG_DATA_TYPE;
+		r = goodix_flash_subsystem(&subsys_cfg);
+		if (r) {
+			ts_err("failed flash config with ISP, %d", r);
+			return r;
+		}
+		ts_info("success flash config with ISP");
+	}
+
+	for (i = 1; i < fw_num && retry;) {
+		ts_info("--- Start to flash subsystem[%d] ---", i);
+		fw_x = &fw_summary->subsys[i];
+		r = goodix_flash_subsystem(fw_x);
+		if (r == 0) {
+			ts_info("--- End flash subsystem[%d]: OK ---", i);
+			i++;
+		} else if (r == -EAGAIN) {
+			retry--;
+			ts_err("--- End flash subsystem%d: Fail, errno:%d, retry:%d ---",
+				i, r, GOODIX_BUS_RETRY_TIMES - retry);
+		} else if (r < 0) { /* bus error */
+			ts_err("--- End flash subsystem%d: Fatal error:%d exit ---",
+				i, r);
+			goto exit_flash;
+		}
+	}
+
+exit_flash:
+	return r;
+}
+
+/**
+ * goodix_update_finish - update finished, FREE resource
+ *  and reset flags---
+ * @fwu_ctrl: pointer to fw_update_ctrl structrue
+ * return: 0 ok, < 0 error
+ */
+static int goodix_update_finish(struct fw_update_ctrl *fwu_ctrl)
+{
+	int ret;
+
+	if (goodix_fw_update_reset(100))
+		ts_err("reset abnormal");
+	ret = goodix_fw_version_compare(fwu_ctrl);
+	if (ret == COMPARE_EQUAL || ret == COMPARE_CFG_NOTEQUAL)
+		return 0;
+
+	return -EINVAL;
+}
+
+/**
+ * goodix_fw_update_proc - firmware update process, the entry of
+ *  firmware update flow
+ * @fwu_ctrl: firmware control
+ * return: = 0 update ok, < 0 error or NO_NEED_UPDATE
+ */
+int goodix_fw_update_proc(struct fw_update_ctrl *fwu_ctrl)
+{
+#define FW_UPDATE_RETRY		2
+	int retry0 = FW_UPDATE_RETRY;
+	int retry1 = FW_UPDATE_RETRY;
+	int ret = 0;
+
+	ret = goodix_parse_firmware(&fwu_ctrl->fw_data);
+	if (ret < 0)
+		return ret;
+
+	if (!(fwu_ctrl->mode & UPDATE_MODE_FORCE)) {
+		ret = goodix_fw_version_compare(fwu_ctrl);
+		ts_info("need to upgrade");
+	}
+
+start_update:
+	fwu_ctrl->status = UPSTA_PREPARING;
+	do {
+		ret = goodix_update_prepare(fwu_ctrl);
+		if (ret) {
+			ts_err("failed prepare ISP, retry %d",
+				FW_UPDATE_RETRY - retry0);
+		}
+	} while (ret && --retry0 > 0);
+	if (ret) {
+		ts_err("Failed to prepare ISP, exit update:%d", ret);
+		goto err_fw_prepare;
+	}
+
+	/* progress: 20%~100% */
+	fwu_ctrl->status = UPSTA_UPDATING;
+	ret = goodix_flash_firmware(fwu_ctrl);
+	if (ret < 0 && --retry1 > 0) {
+		ts_err("Bus error, retry firmware update:%d",
+				FW_UPDATE_RETRY - retry1);
+		goto start_update;
+	}
+	if (ret)
+		ts_err("flash fw data enter error, ret:%d", ret);
+	else
+		ts_info("flash fw data success, need check version");
+
+err_fw_prepare:
+	ret = goodix_update_finish(fwu_ctrl);
+	if (!ret)
+		ts_info("Firmware update successfully");
+	else
+		ts_err("Firmware update failed, ret:%d", ret);
+
+	return ret;
+}
+
+/*
+ * goodix_sysfs_update_en_store: start fw update manually
+ * @buf: '1'[001] update in blocking mode with fwdata from sysfs
+ *       '2'[010] update in blocking mode with fwdata from request
+ *       '5'[101] update in unblocking mode with fwdata from sysfs
+ *       '6'[110] update in unblocking mode with fwdata from request
+ */
+static ssize_t goodix_sysfs_update_en_store(
+		struct kobject *kobj, struct kobj_attribute *attr,
+		const char *buf, size_t count)
+{
+	int ret = 0;
+	int mode = 0;
+	struct fw_update_ctrl *fw_ctrl = &goodix_fw_update_ctrl;
+
+	if (!buf || count <= 0) {
+		ts_err("invalid params");
+		return -EINVAL;
+	}
+	if (!fw_ctrl || !fw_ctrl->initialized) {
+		ts_err("fw module uninit");
+		return -EINVAL;
+	}
+
+	ts_info("set update mode:0x%x", buf[0]);
+	if (buf[0] == '1') {
+		mode = UPDATE_MODE_FORCE | UPDATE_MODE_BLOCK |
+			UPDATE_MODE_SRC_SYSFS;
+	} else if (buf[0] == '2') {
+		mode = UPDATE_MODE_FORCE | UPDATE_MODE_BLOCK |
+			UPDATE_MODE_SRC_REQUEST;
+	} else if (buf[0] == '5') {
+		mode = UPDATE_MODE_FORCE | UPDATE_MODE_SRC_SYSFS;
+	} else if (buf[0] == '6') {
+		mode = UPDATE_MODE_FORCE | UPDATE_MODE_SRC_REQUEST;
+	} else {
+		ts_err("invalid update mode:0x%x", buf[0]);
+		return -EINVAL;
+	}
+
+	ret = goodix_do_fw_update(NULL, mode);
+	if (!ret) {
+		ts_info("success do update work");
+		return count;
+	}
+	ts_err("failed do fw update work");
+	return -EINVAL;
+}
+
+static ssize_t goodix_sysfs_fwimage_store(struct file *file,
+		struct kobject *kobj, struct bin_attribute *attr,
+		char *buf, loff_t pos, size_t count)
+{
+	struct firmware **fw = &goodix_fw_update_ctrl.fw_data.fw_sysfs;
+
+	if (*fw == NULL) {
+		*fw = kzalloc(sizeof(**fw), GFP_KERNEL);
+		if (*fw == NULL)
+			return -ENOMEM;
+		(*fw)->data = vmalloc(GOODIX_FW_MAX_SIEZE);
+		if ((*fw)->data == NULL) {
+			kfree(*fw);
+			*fw = NULL;
+			return -ENOMEM;
+		}
+	}
+
+	if (pos + count > GOODIX_FW_MAX_SIEZE)
+		return -EFAULT;
+	memcpy((u8 *)&(*fw)->data[pos], buf, count);
+	(*fw)->size = pos + count;
+
+	return count;
+}
+
+/* return fw_update result */
+static ssize_t goodix_sysfs_result_show(
+		struct kobject *kobj, struct kobj_attribute *attr,
+		char *buf)
+{
+	struct fw_update_ctrl *fw_ctrl = &goodix_fw_update_ctrl;
+	char str[GOODIX_MAX_STR_LABLE_LEN] = {0};
+	int r = -EINVAL;
+
+	if (!fw_ctrl)
+		return r;
+
+	switch (fw_ctrl->status) {
+	case UPSTA_PREPARING:
+		scnprintf(str, ARRAY_SIZE(str), "preparing");
+		break;
+	case UPSTA_UPDATING:
+		scnprintf(str, ARRAY_SIZE(str), "updating");
+		break;
+	case UPSTA_SUCCESS:
+		scnprintf(str, ARRAY_SIZE(str), "success");
+		break;
+	case UPSTA_FAILED:
+		scnprintf(str, ARRAY_SIZE(str), "failed");
+		break;
+	case UPSTA_NOTWORK:
+	default:
+		scnprintf(str, ARRAY_SIZE(str), "notwork");
+		break;
+	}
+
+	r = snprintf(buf, PAGE_SIZE, "result:%s  spend_time:%dms\n",
+			str, fw_ctrl->spend_time);
+
+	return r;
+}
+
+static struct kobj_attribute goodix_sysfs_update =
+	__ATTR(update_en, 0220, NULL, goodix_sysfs_update_en_store);
+static struct kobj_attribute goodix_sysfs_result =
+	__ATTR(result, 0664, goodix_sysfs_result_show, NULL);
+
+static struct attribute *goodix_fwu_attrs[] = {
+	&goodix_sysfs_update.attr,
+	&goodix_sysfs_result.attr
+};
+
+static int goodix_fw_sysfs_init(struct goodix_ts_core *core_data,
+		struct fw_update_ctrl *fw_ctrl)
+{
+	int ret = 0, i;
+
+	fw_ctrl->kobj = kobject_create_and_add("fwupdate",
+					&core_data->pdev->dev.kobj);
+	if (!fw_ctrl->kobj) {
+		ts_err("failed create sub dir for fwupdate");
+		return -EINVAL;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(goodix_fwu_attrs) && !ret; i++)
+		ret = sysfs_create_file(fw_ctrl->kobj, goodix_fwu_attrs[i]);
+
+	if (ret) {
+		ts_err("failed create fwu sysfs files");
+		while (--i >= 0)
+			sysfs_remove_file(fw_ctrl->kobj, goodix_fwu_attrs[i]);
+
+		kobject_put(fw_ctrl->kobj);
+		return -EINVAL;
+	}
+
+	fw_ctrl->attr_fwimage.attr.name = "fwimage";
+	fw_ctrl->attr_fwimage.attr.mode = 0664;
+	fw_ctrl->attr_fwimage.size = 0;
+	fw_ctrl->attr_fwimage.write = goodix_sysfs_fwimage_store;
+	ret = sysfs_create_bin_file(fw_ctrl->kobj, &fw_ctrl->attr_fwimage);
+	if (ret) {
+		ts_err("failed create fwimage bin node, %d", ret);
+		for (i = 0; i < ARRAY_SIZE(goodix_fwu_attrs); i++)
+			sysfs_remove_file(fw_ctrl->kobj, goodix_fwu_attrs[i]);
+		kobject_put(fw_ctrl->kobj);
+	}
+
+	return ret;
+}
+
+static void goodix_fw_sysfs_remove(void)
+{
+	struct fw_update_ctrl *fw_ctrl = &goodix_fw_update_ctrl;
+	int i;
+
+	sysfs_remove_bin_file(fw_ctrl->kobj, &fw_ctrl->attr_fwimage);
+
+	for (i = 0; i < ARRAY_SIZE(goodix_fwu_attrs); i++)
+		sysfs_remove_file(fw_ctrl->kobj,
+				goodix_fwu_attrs[i]);
+
+	kobject_put(fw_ctrl->kobj);
+}
+
+
+/**
+ * goodix_request_firmware - request firmware data from user space
+ *
+ * @fw_data: firmware struct, contains firmware header info
+ *	and firmware data pointer.
+ * return: 0 - OK, < 0 - error
+ */
+static int goodix_request_firmware(struct firmware_data *fw_data,
+				const char *name)
+{
+	struct fw_update_ctrl *fw_ctrl =
+		container_of(fw_data, struct fw_update_ctrl, fw_data);
+	struct device *dev = &(fw_ctrl->core_data->pdev->dev);
+	int r;
+	int retry = GOODIX_RETRY_3;
+
+	ts_info("Request firmware image [%s]", name);
+
+	while (retry--) {
+		r = request_firmware(&fw_data->firmware, name, dev);
+		if (!r)
+			break;
+		ts_info("get fw bin retry:[%d]", GOODIX_RETRY_3 - retry);
+		msleep(200);
+	}
+	if (retry < 0) {
+		ts_err("Firmware image [%s] not available,errno:%d", name, r);
+		return r;
+	}
+
+	ts_info("Firmware image [%s] is ready", name);
+	return 0;
+}
+
+/**
+ * relase firmware resources
+ *
+ */
+static inline void goodix_release_firmware(struct firmware_data *fw_data)
+{
+	if (fw_data->firmware) {
+		release_firmware(fw_data->firmware);
+		fw_data->firmware = NULL;
+	}
+}
+
+static int goodix_fw_update_thread(void *data)
+{
+	struct fw_update_ctrl *fwu_ctrl = data;
+	ktime_t start, end;
+	int r = -EINVAL;
+
+	start = ktime_get();
+	fwu_ctrl->spend_time = 0;
+	fwu_ctrl->status = UPSTA_NOTWORK;
+	mutex_lock(&fwu_ctrl->mutex);
+
+	ts_debug("notify update start");
+	goodix_ts_blocking_notify(NOTIFY_FWUPDATE_START, NULL);
+
+	if (fwu_ctrl->mode & UPDATE_MODE_SRC_REQUEST) {
+		ts_info("Firmware request update starts");
+		r = goodix_request_firmware(&fwu_ctrl->fw_data,
+						fwu_ctrl->fw_name);
+		if (r < 0)
+			goto out;
+	} else if (fwu_ctrl->mode & UPDATE_MODE_SRC_SYSFS) {
+		if (!fwu_ctrl->fw_data.fw_sysfs) {
+			ts_err("Invalid firmware from sysfs");
+			r = -EINVAL;
+			goto out;
+		}
+		if (fwu_ctrl->fw_data.fw_sysfs->size < 4096) {
+			ts_err("Invalid firmware size[%ld] from sysfs",
+					fwu_ctrl->fw_data.fw_sysfs->size);
+			vfree(fwu_ctrl->fw_data.fw_sysfs->data);
+			kfree(fwu_ctrl->fw_data.fw_sysfs);
+			fwu_ctrl->fw_data.fw_sysfs = NULL;
+			r = -EINVAL;
+			goto out;
+		}
+	} else {
+		ts_err("unknown update mode 0x%x", fwu_ctrl->mode);
+		r = -EINVAL;
+		goto out;
+	}
+
+	/* ready to update */
+	ts_debug("start update proc");
+	r = goodix_fw_update_proc(fwu_ctrl);
+
+	/* clean */
+	if (fwu_ctrl->mode & UPDATE_MODE_SRC_SYSFS) {
+		vfree(fwu_ctrl->fw_data.fw_sysfs->data);
+		kfree(fwu_ctrl->fw_data.fw_sysfs);
+		fwu_ctrl->fw_data.fw_sysfs = NULL;
+	} else if (fwu_ctrl->mode & UPDATE_MODE_SRC_REQUEST) {
+		goodix_release_firmware(&fwu_ctrl->fw_data);
+	}
+out:
+	fwu_ctrl->mode = UPDATE_MODE_DEFAULT;
+	mutex_unlock(&fwu_ctrl->mutex);
+
+	if (r) {
+		ts_err("fw update failed, %d", r);
+		fwu_ctrl->status = UPSTA_FAILED;
+		goodix_ts_blocking_notify(NOTIFY_FWUPDATE_FAILED, NULL);
+	} else {
+		ts_info("fw update success");
+		fwu_ctrl->status = UPSTA_SUCCESS;
+		goodix_ts_blocking_notify(NOTIFY_FWUPDATE_SUCCESS, NULL);
+	}
+
+	end = ktime_get();
+	fwu_ctrl->spend_time = ktime_to_ms(ktime_sub(end, start));
+
+	return r;
+}
+
+int goodix_do_fw_update(struct goodix_ic_config *ic_config, int mode)
+{
+	struct task_struct *fwu_thrd;
+	struct fw_update_ctrl *fwu_ctrl = &goodix_fw_update_ctrl;
+	int ret;
+
+	if (!fwu_ctrl->initialized) {
+		ts_err("fw mode uninit");
+		return -EINVAL;
+	}
+
+	fwu_ctrl->mode = mode;
+	fwu_ctrl->ic_config = ic_config;
+	ts_debug("fw update mode 0x%x", mode);
+	if (fwu_ctrl->mode & UPDATE_MODE_BLOCK) {
+		ret = goodix_fw_update_thread(fwu_ctrl);
+		ts_info("fw update return %d", ret);
+		return ret;
+	}
+	/* create and run update thread */
+	fwu_thrd = kthread_run(goodix_fw_update_thread,
+			fwu_ctrl, "goodix-fwu");
+	if (IS_ERR_OR_NULL(fwu_thrd)) {
+		ts_err("Failed to create update thread:%ld",
+				PTR_ERR(fwu_thrd));
+		return -EFAULT;
+	}
+	ts_info("success create fw update thread");
+	return 0;
+}
+
+int goodix_fw_update_init(struct goodix_ts_core *core_data)
+{
+	int ret;
+
+	if (!core_data || !core_data->hw_ops) {
+		ts_err("core_data && hw_ops cann't be null");
+		return -ENODEV;
+	}
+
+	mutex_init(&goodix_fw_update_ctrl.mutex);
+	goodix_fw_update_ctrl.core_data = core_data;
+	goodix_fw_update_ctrl.mode = 0;
+
+	strlcpy(goodix_fw_update_ctrl.fw_name, core_data->board_data.fw_name,
+		sizeof(goodix_fw_update_ctrl.fw_name));
+
+	ret = goodix_fw_sysfs_init(core_data, &goodix_fw_update_ctrl);
+	if (ret) {
+		ts_err("failed create fwupate sysfs node");
+		return ret;
+	}
+	if (core_data->bus->ic_type == IC_TYPE_BERLIN_A)
+		goodix_fw_update_ctrl.update_info = &update_bra;
+	else if (core_data->bus->ic_type == IC_TYPE_BERLIN_B)
+		goodix_fw_update_ctrl.update_info = &update_brb;
+	else
+		goodix_fw_update_ctrl.update_info = &update_brd;
+
+	goodix_fw_update_ctrl.initialized = 1;
+	return 0;
+}
+
+void goodix_fw_update_uninit(void)
+{
+	if (!goodix_fw_update_ctrl.initialized)
+		return;
+
+	mutex_lock(&goodix_fw_update_ctrl.mutex);
+	goodix_fw_sysfs_remove();
+	goodix_fw_update_ctrl.initialized = 0;
+	mutex_unlock(&goodix_fw_update_ctrl.mutex);
+	mutex_destroy(&goodix_fw_update_ctrl.mutex);
+}

+ 1526 - 0
qcom/opensource/touch-drivers/goodix_berlin_driver/goodix_brl_hw.c

@@ -0,0 +1,1526 @@
+ /*
+  * Goodix Touchscreen Driver
+  * Copyright (C) 2020 - 2021 Goodix, Inc.
+  * Copyright (c) 2022 - 2023 Qualcomm Innovation Center, Inc. All rights reserved.
+  *
+  * This program is free software; you can redistribute it and/or modify
+  * it under the terms of the GNU General Public License as published by
+  * the Free Software Foundation; either version 2 of the License, or
+  * (at your option) any later version.
+  *
+  * This program is distributed in the hope that it will be a reference
+  * to you, when you are integrating the GOODiX's CTP IC into your system,
+  * but WITHOUT ANY WARRANTY; without even the implied warranty of
+  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  * General Public License for more details.
+  *
+  */
+#include "goodix_ts_core.h"
+
+/* berlin_A SPI mode setting */
+#define GOODIX_SPI_MODE_REG			0xC900
+#define GOODIX_SPI_NORMAL_MODE_0	0x01
+
+/* berlin_A D12 setting */
+#define GOODIX_REG_CLK_STA0			0xD807
+#define GOODIX_CLK_STA0_ENABLE		0xFF
+#define GOODIX_REG_CLK_STA1			0xD806
+#define GOODIX_CLK_STA1_ENABLE		0x77
+#define GOODIX_REG_TRIM_D12			0xD006
+#define GOODIX_TRIM_D12_LEVEL		0x3C
+#define GOODIX_REG_RESET			0xD808
+#define GOODIX_RESET_EN				0xFA
+#define HOLD_CPU_REG_W				0x0002
+#define HOLD_CPU_REG_R				0x2000
+
+#define DEV_CONFIRM_VAL				0xAA
+#define BOOTOPTION_ADDR				0x10000
+#define FW_VERSION_INFO_ADDR_BRA	0x1000C
+#define FW_VERSION_INFO_ADDR		0x10014
+
+#define GOODIX_IC_INFO_MAX_LEN		1024
+#define GOODIX_IC_INFO_ADDR_BRA		0x10068
+#define GOODIX_IC_INFO_ADDR			0x10070
+
+
+enum brl_request_code {
+	BRL_REQUEST_CODE_CONFIG = 0x01,
+	BRL_REQUEST_CODE_REF_ERR = 0x02,
+	BRL_REQUEST_CODE_RESET = 0x03,
+	BRL_REQUEST_CODE_CLOCK = 0x04,
+};
+
+static int brl_select_spi_mode(struct goodix_ts_core *cd)
+{
+	int ret;
+	int i;
+	u8 w_value = GOODIX_SPI_NORMAL_MODE_0;
+	u8 r_value;
+
+	if (cd->bus->bus_type == GOODIX_BUS_TYPE_I2C ||
+			cd->bus->ic_type != IC_TYPE_BERLIN_A)
+		return 0;
+
+	for (i = 0; i < GOODIX_RETRY_5; i++) {
+		cd->hw_ops->write(cd, GOODIX_SPI_MODE_REG,
+				&w_value, 1);
+		ret = cd->hw_ops->read(cd, GOODIX_SPI_MODE_REG,
+				&r_value, 1);
+		if (!ret && r_value == w_value)
+			return 0;
+	}
+	ts_err("failed switch SPI mode, ret:%d r_value:%02x", ret, r_value);
+	return -EINVAL;
+}
+
+static int brl_dev_confirm(struct goodix_ts_core *cd)
+{
+	struct goodix_ts_hw_ops *hw_ops = cd->hw_ops;
+	int ret = 0;
+	int retry = GOODIX_RETRY_3;
+	u8 tx_buf[8] = {0};
+	u8 rx_buf[8] = {0};
+
+	memset(tx_buf, DEV_CONFIRM_VAL, sizeof(tx_buf));
+	while (retry--) {
+		ret = hw_ops->write(cd, BOOTOPTION_ADDR,
+			tx_buf, sizeof(tx_buf));
+		if (ret < 0)
+			return ret;
+		ret = hw_ops->read(cd, BOOTOPTION_ADDR,
+			rx_buf, sizeof(rx_buf));
+		if (ret < 0)
+			return ret;
+		if (!memcmp(tx_buf, rx_buf, sizeof(tx_buf)))
+			break;
+		usleep_range(5000, 5100);
+	}
+
+	if (retry < 0) {
+		ret = -EINVAL;
+		ts_err("device confirm failed, rx_buf:%*ph", 8, rx_buf);
+	}
+
+	ts_info("device connected");
+	return ret;
+}
+
+static int brl_reset_after(struct goodix_ts_core *cd)
+{
+	u8 reg_val[2] = {0};
+	u8 temp_buf[12] = {0};
+	int ret;
+	int retry;
+
+	if (cd->bus->ic_type != IC_TYPE_BERLIN_A)
+		return 0;
+
+	ts_info("IN");
+
+	/* select spi mode */
+	ret = brl_select_spi_mode(cd);
+	if (ret < 0)
+		return ret;
+
+	/* hold cpu */
+	retry = GOODIX_RETRY_10;
+	while (retry--) {
+		reg_val[0] = 0x01;
+		reg_val[1] = 0x00;
+		ret = cd->hw_ops->write(cd, HOLD_CPU_REG_W, reg_val, 2);
+		ret |= cd->hw_ops->read(cd, HOLD_CPU_REG_R, &temp_buf[0], 4);
+		ret |= cd->hw_ops->read(cd, HOLD_CPU_REG_R, &temp_buf[4], 4);
+		ret |= cd->hw_ops->read(cd, HOLD_CPU_REG_R, &temp_buf[8], 4);
+		if (!ret && !memcmp(&temp_buf[0], &temp_buf[4], 4) &&
+			!memcmp(&temp_buf[4], &temp_buf[8], 4) &&
+			!memcmp(&temp_buf[0], &temp_buf[8], 4)) {
+			break;
+		}
+	}
+	if (retry < 0) {
+		ts_err("failed to hold cpu, status:%*ph", 12, temp_buf);
+		return -EINVAL;
+	}
+
+	/* enable sta0 clk */
+	retry = GOODIX_RETRY_5;
+	while (retry--) {
+		reg_val[0] = GOODIX_CLK_STA0_ENABLE;
+		ret = cd->hw_ops->write(cd, GOODIX_REG_CLK_STA0, reg_val, 1);
+		ret |= cd->hw_ops->read(cd, GOODIX_REG_CLK_STA0, temp_buf, 1);
+		if (!ret && temp_buf[0] == GOODIX_CLK_STA0_ENABLE)
+			break;
+	}
+	if (retry < 0) {
+		ts_err("failed to enable group0 clock, ret:%d status:%02x",
+				ret, temp_buf[0]);
+		return -EINVAL;
+	}
+
+	/* enable sta1 clk */
+	retry = GOODIX_RETRY_5;
+	while (retry--) {
+		reg_val[0] = GOODIX_CLK_STA1_ENABLE;
+		ret = cd->hw_ops->write(cd, GOODIX_REG_CLK_STA1, reg_val, 1);
+		ret |= cd->hw_ops->read(cd, GOODIX_REG_CLK_STA1, temp_buf, 1);
+		if (!ret && temp_buf[0] == GOODIX_CLK_STA1_ENABLE)
+			break;
+	}
+	if (retry < 0) {
+		ts_err("failed to enable group1 clock, ret:%d status:%02x",
+				ret, temp_buf[0]);
+		return -EINVAL;
+	}
+
+	/* set D12 level */
+	retry = GOODIX_RETRY_5;
+	while (retry--) {
+		reg_val[0] = GOODIX_TRIM_D12_LEVEL;
+		ret = cd->hw_ops->write(cd, GOODIX_REG_TRIM_D12, reg_val, 1);
+		ret |= cd->hw_ops->read(cd, GOODIX_REG_TRIM_D12, temp_buf, 1);
+		if (!ret && temp_buf[0] == GOODIX_TRIM_D12_LEVEL)
+			break;
+	}
+	if (retry < 0) {
+		ts_err("failed to set D12, ret:%d status:%02x",
+				ret, temp_buf[0]);
+		return -EINVAL;
+	}
+
+	usleep_range(5000, 5100);
+	/* soft reset */
+	reg_val[0] = GOODIX_RESET_EN;
+	ret = cd->hw_ops->write(cd, GOODIX_REG_RESET, reg_val, 1);
+	if (ret < 0)
+		return ret;
+
+	/* select spi mode */
+	ret = brl_select_spi_mode(cd);
+	if (ret < 0)
+		return ret;
+
+	ts_info("OUT");
+
+	return 0;
+}
+
+#define REG_SUSPEND_CURRENT 20
+#define REG_RESUME_CURRENT 30000
+#define REG_RESUME_MIN_VOLTAGE 3200000
+#define REG_RESUME_MAX_VOLTAGE 3200000
+
+static int brl_power_on(struct goodix_ts_core *cd, bool on)
+{
+	int ret = 0;
+	int iovdd_gpio = cd->board_data.iovdd_gpio;
+	int avdd_gpio = cd->board_data.avdd_gpio;
+	int reset_gpio = cd->board_data.reset_gpio;
+
+	if (on) {
+		if (iovdd_gpio > 0) {
+			gpio_direction_output(iovdd_gpio, 1);
+		} else if (cd->iovdd) {
+			if (regulator_count_voltages(cd->iovdd) > 0) {
+				ret = regulator_set_load(cd->iovdd, REG_RESUME_CURRENT);
+				if (ret) {
+					ts_err("Setting regulator load failed:%d", ret);
+					goto power_off;
+				}
+			}
+			ret = regulator_enable(cd->iovdd);
+			if (ret < 0) {
+				ts_err("Failed to enable iovdd:%d", ret);
+				goto power_off;
+			}
+		}
+		usleep_range(3000, 3100);
+		if (avdd_gpio > 0) {
+			gpio_direction_output(avdd_gpio, 1);
+		} else if (cd->avdd) {
+			if (regulator_count_voltages(cd->avdd) > 0) {
+				ret = regulator_set_load(cd->avdd, REG_RESUME_CURRENT);
+				if (ret) {
+					ts_err("vdd regulator set_load failed ret=%d", ret);
+					return ret;
+				}
+				ret = regulator_set_voltage(cd->avdd, REG_RESUME_MIN_VOLTAGE,
+							REG_RESUME_MAX_VOLTAGE);
+				if (ret) {
+					ts_err("vdd regulator set_vtg failed ret=%d", ret);
+					return ret;
+				}
+			}
+			ret = regulator_enable(cd->avdd);
+			if (ret < 0) {
+				ts_err("Failed to enable avdd:%d", ret);
+				goto power_off;
+			}
+		}
+
+		gpio_direction_output(cd->board_data.reset_gpio, 0);
+		usleep_range(15000, 15100);
+		gpio_direction_output(cd->board_data.reset_gpio, 1);
+		msleep(GOODIX_NORMAL_RESET_DELAY_MS);
+
+		ret = brl_dev_confirm(cd);
+		if (ret < 0)
+			goto power_off;
+		ret = brl_reset_after(cd);
+		if (ret < 0)
+			goto power_off;
+
+		return 0;
+	}
+
+power_off:
+	gpio_direction_output(reset_gpio, 0);
+	if (iovdd_gpio > 0)
+		gpio_direction_output(iovdd_gpio, 0);
+	else if (cd->iovdd) {
+		regulator_disable(cd->iovdd);
+		if (regulator_count_voltages(cd->iovdd) > 0)
+			regulator_set_load(cd->iovdd, REG_SUSPEND_CURRENT);
+	}
+	if (avdd_gpio > 0)
+		gpio_direction_output(avdd_gpio, 0);
+	else if (cd->avdd)
+		regulator_disable(cd->avdd);
+	return ret;
+}
+
+#define GOODIX_SLEEP_CMD	0x84
+int brl_suspend(struct goodix_ts_core *cd)
+{
+#ifdef GOODIX_SUSPEND_GESTURE_ENABLE
+	struct goodix_ts_cmd sleep_cmd;
+
+	sleep_cmd.cmd = GOODIX_SLEEP_CMD;
+	sleep_cmd.len = 4;
+	if (cd->hw_ops->send_cmd(cd, &sleep_cmd))
+		ts_err("failed send sleep cmd");
+#else
+	if (cd->hw_ops->power_on(cd, 0))
+		ts_err("failed power off");
+#endif
+	return 0;
+}
+
+int brl_resume(struct goodix_ts_core *cd)
+{
+	int ret = 0;
+
+#ifdef GOODIX_SUSPEND_GESTURE_ENABLE
+	ret = cd->hw_ops->reset(cd, GOODIX_NORMAL_RESET_DELAY_MS);
+#else
+	ret = cd->hw_ops->power_on(cd, 1);
+	if (ret) {
+		ts_err("failed power on");
+		return ret;
+	}
+#endif
+
+	return ret;
+}
+
+#define GOODIX_GESTURE_CMD_BA	0x12
+#define GOODIX_GESTURE_CMD		0xA6
+int brl_gesture(struct goodix_ts_core *cd, int gesture_type)
+{
+	struct goodix_ts_cmd cmd;
+
+	if (cd->bus->ic_type == IC_TYPE_BERLIN_A)
+		cmd.cmd = GOODIX_GESTURE_CMD_BA;
+	else
+		cmd.cmd = GOODIX_GESTURE_CMD;
+	cmd.len = 5;
+	cmd.data[0] = gesture_type;
+	if (cd->hw_ops->send_cmd(cd, &cmd))
+		ts_err("failed send gesture cmd");
+
+	return 0;
+}
+
+static int brl_reset(struct goodix_ts_core *cd, int delay)
+{
+	ts_info("chip_reset");
+
+	gpio_direction_output(cd->board_data.reset_gpio, 0);
+	usleep_range(2000, 2100);
+	gpio_direction_output(cd->board_data.reset_gpio, 1);
+	if (delay < 20)
+		usleep_range(delay * 1000, delay * 1000 + 100);
+	else
+		msleep(delay);
+
+	return brl_select_spi_mode(cd);
+}
+
+static int brl_irq_enbale(struct goodix_ts_core *cd, bool enable)
+{
+	if (enable && !atomic_cmpxchg(&cd->irq_enabled, 0, 1)) {
+		enable_irq(cd->irq);
+		ts_debug("Irq enabled");
+		return 0;
+	}
+
+	if (!enable && atomic_cmpxchg(&cd->irq_enabled, 1, 0)) {
+		disable_irq_nosync(cd->irq);
+		ts_debug("Irq disabled");
+		return 0;
+	}
+	ts_info("warning: irq depth imbalance!");
+	return 0;
+}
+
+static int brl_read(struct goodix_ts_core *cd, unsigned int addr,
+		unsigned char *data, unsigned int len)
+{
+	struct goodix_bus_interface *bus = cd->bus;
+
+	return bus->read(bus->dev, addr, data, len);
+}
+
+static int brl_write(struct goodix_ts_core *cd, unsigned int addr,
+		 unsigned char *data, unsigned int len)
+{
+	struct goodix_bus_interface *bus = cd->bus;
+
+	return bus->write(bus->dev, addr, data, len);
+}
+
+/* command ack info */
+#define CMD_ACK_IDLE             0x01
+#define CMD_ACK_BUSY             0x02
+#define CMD_ACK_BUFFER_OVERFLOW  0x03
+#define CMD_ACK_CHECKSUM_ERROR   0x04
+#define CMD_ACK_OK               0x80
+
+#define GOODIX_CMD_RETRY 6
+static int brl_send_cmd(struct goodix_ts_core *cd,
+	struct goodix_ts_cmd *cmd)
+{
+	int ret, retry, i;
+	struct goodix_ts_cmd cmd_ack;
+	struct goodix_ic_info_misc *misc = &cd->ic_info.misc;
+	struct goodix_ts_hw_ops *hw_ops = cd->hw_ops;
+
+	cmd->state = 0;
+	cmd->ack = 0;
+	goodix_append_checksum(&(cmd->buf[2]), cmd->len - 2,
+		CHECKSUM_MODE_U8_LE);
+	ts_debug("cmd data %*ph", cmd->len, &(cmd->buf[2]));
+
+	retry = 0;
+	while (retry++ < GOODIX_CMD_RETRY) {
+		ret = hw_ops->write(cd, misc->cmd_addr,
+				    cmd->buf, sizeof(*cmd));
+		if (ret < 0) {
+			ts_err("failed write command");
+			return ret;
+		}
+		for (i = 0; i < GOODIX_CMD_RETRY; i++) {
+			/* check command result */
+			ret = hw_ops->read(cd, misc->cmd_addr,
+				cmd_ack.buf, sizeof(cmd_ack));
+			if (ret < 0) {
+				ts_err("failed read command ack, %d", ret);
+				return ret;
+			}
+			ts_debug("cmd ack data %*ph",
+				 (int)sizeof(cmd_ack), cmd_ack.buf);
+			if (cmd_ack.ack == CMD_ACK_OK) {
+				msleep(40); // wait for cmd response
+				return 0;
+			}
+			if (cmd_ack.ack == CMD_ACK_BUSY ||
+				cmd_ack.ack == 0x00) {
+				usleep_range(1000, 1100);
+				continue;
+			}
+			if (cmd_ack.ack == CMD_ACK_BUFFER_OVERFLOW)
+				usleep_range(10000, 11000);
+			usleep_range(1000, 1100);
+			break;
+		}
+	}
+	ts_err("failed get valid cmd ack");
+	return -EINVAL;
+}
+
+#pragma  pack(1)
+struct goodix_config_head {
+	union {
+		struct {
+			u8 panel_name[8];
+			u8 fw_pid[8];
+			u8 fw_vid[4];
+			u8 project_name[8];
+			u8 file_ver[2];
+			u32 cfg_id;
+			u8 cfg_ver;
+			u8 cfg_time[8];
+			u8 reserved[15];
+			u8 flag;
+			u16 cfg_len;
+			u8 cfg_num;
+			u16 checksum;
+		};
+		u8 buf[64];
+	};
+};
+#pragma pack()
+
+#define CONFIG_CND_LEN			4
+#define CONFIG_CMD_START		0x04
+#define CONFIG_CMD_WRITE		0x05
+#define CONFIG_CMD_EXIT			0x06
+#define CONFIG_CMD_READ_START	0x07
+#define CONFIG_CMD_READ_EXIT	0x08
+
+#define CONFIG_CMD_STATUS_PASS	0x80
+#define CONFIG_CMD_WAIT_RETRY	20
+
+static int wait_cmd_status(struct goodix_ts_core *cd,
+	u8 target_status, int retry)
+{
+	struct goodix_ts_cmd cmd_ack;
+	struct goodix_ic_info_misc *misc = &cd->ic_info.misc;
+	struct goodix_ts_hw_ops *hw_ops = cd->hw_ops;
+	int i, ret;
+
+	for (i = 0; i < retry; i++) {
+		ret = hw_ops->read(cd, misc->cmd_addr, cmd_ack.buf,
+			sizeof(cmd_ack));
+		if (!ret && cmd_ack.state == target_status) {
+			ts_debug("status check pass");
+			return 0;
+		}
+		ts_debug("cmd buf %*ph", (int)sizeof(cmd_ack), cmd_ack.buf);
+		msleep(20);
+	}
+
+	ts_err("cmd status not ready, retry %d, ack 0x%x, status 0x%x, ret %d",
+			i, cmd_ack.ack, cmd_ack.state, ret);
+	return -EINVAL;
+}
+
+static int send_cfg_cmd(struct goodix_ts_core *cd,
+	struct goodix_ts_cmd *cfg_cmd)
+{
+	int ret;
+
+	ret = cd->hw_ops->send_cmd(cd, cfg_cmd);
+	if (ret) {
+		ts_err("failed write cfg prepare cmd %d", ret);
+		return ret;
+	}
+	ret = wait_cmd_status(cd, CONFIG_CMD_STATUS_PASS,
+		CONFIG_CMD_WAIT_RETRY);
+	if (ret) {
+		ts_err("failed wait for fw ready for config, %d", ret);
+		return ret;
+	}
+	return 0;
+}
+
+static int brl_send_config(struct goodix_ts_core *cd, u8 *cfg, int len)
+{
+	int ret;
+	u8 *tmp_buf;
+	u16 cfg_head_len = sizeof(struct goodix_config_head) / sizeof(u8);
+	struct goodix_ts_cmd cfg_cmd;
+	struct goodix_ic_info_misc *misc = &cd->ic_info.misc;
+	struct goodix_ts_hw_ops *hw_ops = cd->hw_ops;
+	struct goodix_config_head *cfg_head = (struct goodix_config_head *)cfg;
+
+	if (!cd || !cfg) {
+		ts_err("input parameter is NULL");
+		return -EINVAL;
+	} else if (len > misc->fw_buffer_max_len) {
+		ts_err("config len exceed limit %d > %d",
+			len, misc->fw_buffer_max_len);
+		return -EINVAL;
+	} else if (len < cfg_head_len) {
+		ts_err("config buffer size %d smaller than header size %d",
+			len, cfg_head_len);
+		return -EINVAL;
+	} else if (len != cfg_head_len + cfg_head->cfg_len) {
+		ts_err("config buffer size %d not equal to head %d + cfg_len %d",
+			len, cfg_head_len, cfg_head->cfg_len);
+		return -EINVAL;
+	} else if (checksum_cmp(cfg, cfg_head_len, CHECKSUM_MODE_U8_LE)) {
+		ts_err("config head checksum error");
+		return -EINVAL;
+	} else if (checksum_cmp(cfg + cfg_head_len, cfg_head->cfg_len, CHECKSUM_MODE_U16_LE)) {
+		ts_err("config body checksum error");
+		return -EINVAL;
+	}
+
+	tmp_buf = kzalloc(len, GFP_KERNEL);
+	if (!tmp_buf)
+		return -ENOMEM;
+
+	cfg_cmd.len = CONFIG_CND_LEN;
+	cfg_cmd.cmd = CONFIG_CMD_START;
+	ret = send_cfg_cmd(cd, &cfg_cmd);
+	if (ret) {
+		ts_err("failed write cfg prepare cmd %d", ret);
+		goto exit;
+	}
+
+	ts_debug("try send config to 0x%x, len %d", misc->fw_buffer_addr, len);
+	ret = hw_ops->write(cd, misc->fw_buffer_addr, cfg, len);
+	if (ret) {
+		ts_err("failed write config data, %d", ret);
+		goto exit;
+	}
+	ret = hw_ops->read(cd, misc->fw_buffer_addr, tmp_buf, len);
+	if (ret) {
+		ts_err("failed read back config data");
+		goto exit;
+	}
+
+	if (memcmp(cfg, tmp_buf, len)) {
+		ts_err("config data read back compare file");
+		ret = -EINVAL;
+		goto exit;
+	}
+	/* notify fw for recive config */
+	memset(cfg_cmd.buf, 0, sizeof(cfg_cmd));
+	cfg_cmd.len = CONFIG_CND_LEN;
+	cfg_cmd.cmd = CONFIG_CMD_WRITE;
+	ret = send_cfg_cmd(cd, &cfg_cmd);
+	if (ret)
+		ts_err("failed send config data ready cmd %d", ret);
+
+exit:
+	memset(cfg_cmd.buf, 0, sizeof(cfg_cmd));
+	cfg_cmd.len = CONFIG_CND_LEN;
+	cfg_cmd.cmd = CONFIG_CMD_EXIT;
+	if (send_cfg_cmd(cd, &cfg_cmd)) {
+		ts_err("failed send config write end command");
+		ret = -EINVAL;
+	}
+
+	if (!ret) {
+		ts_info("success send config");
+		msleep(100);
+	}
+
+	kfree(tmp_buf);
+	return ret;
+}
+
+/*
+ * return: return config length on succes, other wise return < 0
+ **/
+static int brl_read_config(struct goodix_ts_core *cd, u8 *cfg, int size)
+{
+	int ret;
+	struct goodix_ts_cmd cfg_cmd;
+	struct goodix_ic_info_misc *misc = &cd->ic_info.misc;
+	struct goodix_ts_hw_ops *hw_ops = cd->hw_ops;
+	struct goodix_config_head cfg_head;
+
+	if (!cfg || sizeof(cfg_head) > size)
+		return -EINVAL;
+
+	cfg_cmd.len = CONFIG_CND_LEN;
+	cfg_cmd.cmd = CONFIG_CMD_READ_START;
+	ret = send_cfg_cmd(cd, &cfg_cmd);
+	if (ret) {
+		ts_err("failed send config read prepare command");
+		return ret;
+	}
+
+	ret = hw_ops->read(cd, misc->fw_buffer_addr,
+			cfg_head.buf, sizeof(cfg_head));
+	if (ret) {
+		ts_err("failed read config head %d", ret);
+		goto exit;
+	}
+
+	if (checksum_cmp(cfg_head.buf, sizeof(cfg_head), CHECKSUM_MODE_U8_LE)) {
+		ts_err("config head checksum error");
+		ret = -EINVAL;
+		goto exit;
+	}
+
+	cfg_head.cfg_len = le16_to_cpu(cfg_head.cfg_len);
+	if (cfg_head.cfg_len > misc->fw_buffer_max_len ||
+		cfg_head.cfg_len > size) {
+		ts_err("cfg len exceed buffer size %d > %d", cfg_head.cfg_len,
+			misc->fw_buffer_max_len);
+		ret = -EINVAL;
+		goto exit;
+	}
+
+	memcpy(cfg, cfg_head.buf, sizeof(cfg_head));
+	ret = hw_ops->read(cd, misc->fw_buffer_addr + sizeof(cfg_head),
+			   cfg + sizeof(cfg_head), cfg_head.cfg_len);
+	if (ret) {
+		ts_err("failed read cfg pack, %d", ret);
+		goto exit;
+	}
+
+	ts_info("config len %d", cfg_head.cfg_len);
+	if (checksum_cmp(cfg + sizeof(cfg_head),
+			 cfg_head.cfg_len, CHECKSUM_MODE_U16_LE)) {
+		ts_err("config body checksum error");
+		ret = -EINVAL;
+		goto exit;
+	}
+	ts_info("success read config data: len %zu",
+		cfg_head.cfg_len + sizeof(cfg_head));
+exit:
+	memset(cfg_cmd.buf, 0, sizeof(cfg_cmd));
+	cfg_cmd.len = CONFIG_CND_LEN;
+	cfg_cmd.cmd = CONFIG_CMD_READ_EXIT;
+	if (send_cfg_cmd(cd, &cfg_cmd)) {
+		ts_err("failed send config read finish command");
+		ret = -EINVAL;
+	}
+	if (ret)
+		return -EINVAL;
+	return cfg_head.cfg_len + sizeof(cfg_head);
+}
+
+/*
+ *	return: 0 for no error.
+ *	GOODIX_EBUS when encounter a bus error
+ *	GOODIX_ECHECKSUM version checksum error
+ *	GOODIX_EVERSION  patch ID compare failed,
+ *	in this case the sensorID is valid.
+ */
+static int brl_read_version(struct goodix_ts_core *cd,
+			struct goodix_fw_version *version)
+{
+	int ret, i;
+	u32 fw_addr;
+	struct goodix_ts_hw_ops *hw_ops = cd->hw_ops;
+	u8 buf[sizeof(struct goodix_fw_version)] = {0};
+	u8 temp_pid[8] = {0};
+
+	if (cd->bus->ic_type == IC_TYPE_BERLIN_A)
+		fw_addr = FW_VERSION_INFO_ADDR_BRA;
+	else
+		fw_addr = FW_VERSION_INFO_ADDR;
+
+	for (i = 0; i < 2; i++) {
+		ret = hw_ops->read(cd, fw_addr, buf, sizeof(buf));
+		if (ret) {
+			ts_info("read fw version: %d, retry %d", ret, i);
+			ret = -GOODIX_EBUS;
+			usleep_range(5000, 5100);
+			continue;
+		}
+
+		if (!checksum_cmp(buf, sizeof(buf), CHECKSUM_MODE_U8_LE))
+			break;
+
+		ts_info("invalid fw version: checksum error!");
+		ts_info("fw version:%*ph", (int)sizeof(buf), buf);
+		ret = -GOODIX_ECHECKSUM;
+		usleep_range(10000, 11000);
+	}
+	if (ret) {
+		ts_err("failed get valied fw version");
+		return ret;
+	}
+	memcpy(version, buf, sizeof(*version));
+	memcpy(temp_pid, version->rom_pid, sizeof(version->rom_pid));
+	ts_info("rom_pid:%s", temp_pid);
+	ts_info("rom_vid:%*ph", (int)sizeof(version->rom_vid),
+		version->rom_vid);
+	ts_info("pid:%s", version->patch_pid);
+	ts_info("vid:%*ph", (int)sizeof(version->patch_vid),
+		version->patch_vid);
+	ts_info("sensor_id:%d", version->sensor_id);
+
+	return 0;
+}
+
+#define LE16_TO_CPU(x)  (x = le16_to_cpu(x))
+#define LE32_TO_CPU(x)  (x = le32_to_cpu(x))
+static int convert_ic_info(struct goodix_ic_info *info, const u8 *data)
+{
+	int i;
+	struct goodix_ic_info_version *version = &info->version;
+	struct goodix_ic_info_feature *feature = &info->feature;
+	struct goodix_ic_info_param *parm = &info->parm;
+	struct goodix_ic_info_misc *misc = &info->misc;
+
+	info->length = le16_to_cpup((__le16 *)data);
+
+	data += 2;
+	memcpy(version, data, sizeof(*version));
+	version->config_id = le32_to_cpu(version->config_id);
+
+	data += sizeof(struct goodix_ic_info_version);
+	memcpy(feature, data, sizeof(*feature));
+	feature->freqhop_feature =
+		le16_to_cpu(feature->freqhop_feature);
+	feature->calibration_feature =
+		le16_to_cpu(feature->calibration_feature);
+	feature->gesture_feature =
+		le16_to_cpu(feature->gesture_feature);
+	feature->side_touch_feature =
+		le16_to_cpu(feature->side_touch_feature);
+	feature->stylus_feature =
+		le16_to_cpu(feature->stylus_feature);
+
+	data += sizeof(struct goodix_ic_info_feature);
+	parm->drv_num = *(data++);
+	parm->sen_num = *(data++);
+	parm->button_num = *(data++);
+	parm->force_num = *(data++);
+	parm->active_scan_rate_num = *(data++);
+	if (parm->active_scan_rate_num > MAX_SCAN_RATE_NUM) {
+		ts_err("invalid scan rate num %d > %d",
+			parm->active_scan_rate_num, MAX_SCAN_RATE_NUM);
+		return -EINVAL;
+	}
+	for (i = 0; i < parm->active_scan_rate_num; i++)
+		parm->active_scan_rate[i] =
+			le16_to_cpup((__le16 *)(data + i * 2));
+
+	data += parm->active_scan_rate_num * 2;
+	parm->mutual_freq_num = *(data++);
+	if (parm->mutual_freq_num > MAX_SCAN_FREQ_NUM) {
+		ts_err("invalid mntual freq num %d > %d",
+			parm->mutual_freq_num, MAX_SCAN_FREQ_NUM);
+		return -EINVAL;
+	}
+	for (i = 0; i < parm->mutual_freq_num; i++)
+		parm->mutual_freq[i] =
+			le16_to_cpup((__le16 *)(data + i * 2));
+
+	data += parm->mutual_freq_num * 2;
+	parm->self_tx_freq_num = *(data++);
+	if (parm->self_tx_freq_num > MAX_SCAN_FREQ_NUM) {
+		ts_err("invalid tx freq num %d > %d",
+			parm->self_tx_freq_num, MAX_SCAN_FREQ_NUM);
+		return -EINVAL;
+	}
+	for (i = 0; i < parm->self_tx_freq_num; i++)
+		parm->self_tx_freq[i] =
+			le16_to_cpup((__le16 *)(data + i * 2));
+
+	data += parm->self_tx_freq_num * 2;
+	parm->self_rx_freq_num = *(data++);
+	if (parm->self_rx_freq_num > MAX_SCAN_FREQ_NUM) {
+		ts_err("invalid rx freq num %d > %d",
+			parm->self_rx_freq_num, MAX_SCAN_FREQ_NUM);
+		return -EINVAL;
+	}
+	for (i = 0; i < parm->self_rx_freq_num; i++)
+		parm->self_rx_freq[i] =
+			le16_to_cpup((__le16 *)(data + i * 2));
+
+	data += parm->self_rx_freq_num * 2;
+	parm->stylus_freq_num = *(data++);
+	if (parm->stylus_freq_num > MAX_FREQ_NUM_STYLUS) {
+		ts_err("invalid stylus freq num %d > %d",
+			parm->stylus_freq_num, MAX_FREQ_NUM_STYLUS);
+		return -EINVAL;
+	}
+	for (i = 0; i < parm->stylus_freq_num; i++)
+		parm->stylus_freq[i] =
+			le16_to_cpup((__le16 *)(data + i * 2));
+
+	data += parm->stylus_freq_num * 2;
+	memcpy(misc, data, sizeof(*misc));
+	misc->cmd_addr = le32_to_cpu(misc->cmd_addr);
+	misc->cmd_max_len = le16_to_cpu(misc->cmd_max_len);
+	misc->cmd_reply_addr = le32_to_cpu(misc->cmd_reply_addr);
+	misc->cmd_reply_len = le16_to_cpu(misc->cmd_reply_len);
+	misc->fw_state_addr = le32_to_cpu(misc->fw_state_addr);
+	misc->fw_state_len = le16_to_cpu(misc->fw_state_len);
+	misc->fw_buffer_addr = le32_to_cpu(misc->fw_buffer_addr);
+	misc->fw_buffer_max_len = le16_to_cpu(misc->fw_buffer_max_len);
+	misc->frame_data_addr = le32_to_cpu(misc->frame_data_addr);
+	misc->frame_data_head_len = le16_to_cpu(misc->frame_data_head_len);
+
+	misc->fw_attr_len = le16_to_cpu(misc->fw_attr_len);
+	misc->fw_log_len = le16_to_cpu(misc->fw_log_len);
+	misc->stylus_struct_len = le16_to_cpu(misc->stylus_struct_len);
+	misc->mutual_struct_len = le16_to_cpu(misc->mutual_struct_len);
+	misc->self_struct_len = le16_to_cpu(misc->self_struct_len);
+	misc->noise_struct_len = le16_to_cpu(misc->noise_struct_len);
+	misc->touch_data_addr = le32_to_cpu(misc->touch_data_addr);
+	misc->touch_data_head_len = le16_to_cpu(misc->touch_data_head_len);
+	misc->point_struct_len = le16_to_cpu(misc->point_struct_len);
+	LE32_TO_CPU(misc->mutual_rawdata_addr);
+	LE32_TO_CPU(misc->mutual_diffdata_addr);
+	LE32_TO_CPU(misc->mutual_refdata_addr);
+	LE32_TO_CPU(misc->self_rawdata_addr);
+	LE32_TO_CPU(misc->self_diffdata_addr);
+	LE32_TO_CPU(misc->self_refdata_addr);
+	LE32_TO_CPU(misc->iq_rawdata_addr);
+	LE32_TO_CPU(misc->iq_refdata_addr);
+	LE32_TO_CPU(misc->im_rawdata_addr);
+	LE16_TO_CPU(misc->im_readata_len);
+	LE32_TO_CPU(misc->noise_rawdata_addr);
+	LE16_TO_CPU(misc->noise_rawdata_len);
+	LE32_TO_CPU(misc->stylus_rawdata_addr);
+	LE16_TO_CPU(misc->stylus_rawdata_len);
+	LE32_TO_CPU(misc->noise_data_addr);
+	LE32_TO_CPU(misc->esd_addr);
+
+	return 0;
+}
+
+static void print_ic_info(struct goodix_ic_info *ic_info)
+{
+	struct goodix_ic_info_version *version = &ic_info->version;
+	struct goodix_ic_info_feature *feature = &ic_info->feature;
+	struct goodix_ic_info_param *parm = &ic_info->parm;
+	struct goodix_ic_info_misc *misc = &ic_info->misc;
+
+	ts_info("ic_info_length:                %d",
+		ic_info->length);
+	ts_info("info_customer_id:              0x%01X",
+		version->info_customer_id);
+	ts_info("info_version_id:               0x%01X",
+		version->info_version_id);
+	ts_info("ic_die_id:                     0x%01X",
+		version->ic_die_id);
+	ts_info("ic_version_id:                 0x%01X",
+		version->ic_version_id);
+	ts_info("config_id:                     0x%4X",
+		version->config_id);
+	ts_info("config_version:                0x%01X",
+		version->config_version);
+	ts_info("frame_data_customer_id:        0x%01X",
+		version->frame_data_customer_id);
+	ts_info("frame_data_version_id:         0x%01X",
+		version->frame_data_version_id);
+	ts_info("touch_data_customer_id:        0x%01X",
+		version->touch_data_customer_id);
+	ts_info("touch_data_version_id:         0x%01X",
+		version->touch_data_version_id);
+
+	ts_info("freqhop_feature:               0x%04X",
+		feature->freqhop_feature);
+	ts_info("calibration_feature:           0x%04X",
+		feature->calibration_feature);
+	ts_info("gesture_feature:               0x%04X",
+		feature->gesture_feature);
+	ts_info("side_touch_feature:            0x%04X",
+		feature->side_touch_feature);
+	ts_info("stylus_feature:                0x%04X",
+		feature->stylus_feature);
+
+	ts_info("Drv*Sen,Button,Force num:      %d x %d, %d, %d",
+		parm->drv_num, parm->sen_num,
+		parm->button_num, parm->force_num);
+
+	ts_info("Cmd:                           0x%04X, %d",
+		misc->cmd_addr, misc->cmd_max_len);
+	ts_info("Cmd-Reply:                     0x%04X, %d",
+		misc->cmd_reply_addr, misc->cmd_reply_len);
+	ts_info("FW-State:                      0x%04X, %d",
+		misc->fw_state_addr, misc->fw_state_len);
+	ts_info("FW-Buffer:                     0x%04X, %d",
+		misc->fw_buffer_addr, misc->fw_buffer_max_len);
+	ts_info("Touch-Data:                    0x%04X, %d",
+		misc->touch_data_addr, misc->touch_data_head_len);
+	ts_info("point_struct_len:              %d",
+		misc->point_struct_len);
+	ts_info("mutual_rawdata_addr:           0x%04X",
+		misc->mutual_rawdata_addr);
+	ts_info("mutual_diffdata_addr:          0x%04X",
+		misc->mutual_diffdata_addr);
+	ts_info("self_rawdata_addr:             0x%04X",
+		misc->self_rawdata_addr);
+	ts_info("self_diffdata_addr:            0x%04X",
+		misc->self_diffdata_addr);
+	ts_info("stylus_rawdata_addr:           0x%04X, %d",
+		misc->stylus_rawdata_addr, misc->stylus_rawdata_len);
+	ts_info("esd_addr:                      0x%04X",
+		misc->esd_addr);
+}
+
+static int brl_get_ic_info(struct goodix_ts_core *cd,
+	struct goodix_ic_info *ic_info)
+{
+	int ret, i;
+	u16 length = 0;
+	u32 ic_addr;
+	u8 afe_data[GOODIX_IC_INFO_MAX_LEN] = {0};
+	struct goodix_ts_hw_ops *hw_ops = cd->hw_ops;
+
+	if (cd->bus->ic_type == IC_TYPE_BERLIN_A)
+		ic_addr = GOODIX_IC_INFO_ADDR_BRA;
+	else
+		ic_addr = GOODIX_IC_INFO_ADDR;
+
+	for (i = 0; i < GOODIX_RETRY_3; i++) {
+		ret = hw_ops->read(cd, ic_addr,
+				   (u8 *)&length, sizeof(length));
+		if (ret) {
+			ts_info("failed get ic info length, %d", ret);
+			usleep_range(5000, 5100);
+			continue;
+		}
+		length = le16_to_cpu(length);
+		if (length >= GOODIX_IC_INFO_MAX_LEN) {
+			ts_info("invalid ic info length %d, retry %d",
+				length, i);
+			continue;
+		}
+
+		ret = hw_ops->read(cd, ic_addr, afe_data, length);
+		if (ret) {
+			ts_info("failed get ic info data, %d", ret);
+			usleep_range(5000, 5100);
+			continue;
+		}
+		/* judge whether the data is valid */
+		if (is_risk_data((const uint8_t *)afe_data, length)) {
+			ts_info("fw info data invalid");
+			usleep_range(5000, 5100);
+			continue;
+		}
+		if (checksum_cmp((const uint8_t *)afe_data,
+				length, CHECKSUM_MODE_U8_LE)) {
+			ts_info("fw info checksum error!");
+			usleep_range(5000, 5100);
+			continue;
+		}
+		break;
+	}
+	if (i == GOODIX_RETRY_3) {
+		ts_err("failed get ic info");
+		return -EINVAL;
+	}
+
+	ret = convert_ic_info(ic_info, afe_data);
+	if (ret) {
+		ts_err("convert ic info encounter error");
+		return ret;
+	}
+
+	print_ic_info(ic_info);
+
+	/* check some key info */
+	if (!ic_info->misc.cmd_addr || !ic_info->misc.fw_buffer_addr ||
+	    !ic_info->misc.touch_data_addr) {
+		ts_err("cmd_addr fw_buf_addr and touch_data_addr is null");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+#define GOODIX_ESD_TICK_WRITE_DATA	0xAA
+static int brl_esd_check(struct goodix_ts_core *cd)
+{
+	int ret;
+	u32 esd_addr;
+	u8 esd_value;
+
+	if (!cd->ic_info.misc.esd_addr)
+		return 0;
+
+	esd_addr = cd->ic_info.misc.esd_addr;
+	ret = cd->hw_ops->read(cd, esd_addr, &esd_value, 1);
+	if (ret) {
+		ts_err("failed get esd value, %d", ret);
+		return ret;
+	}
+
+	if (esd_value == GOODIX_ESD_TICK_WRITE_DATA) {
+		ts_err("esd check failed, 0x%x", esd_value);
+		return -EINVAL;
+	}
+	esd_value = GOODIX_ESD_TICK_WRITE_DATA;
+	ret = cd->hw_ops->write(cd, esd_addr, &esd_value, 1);
+	if (ret) {
+		ts_err("failed refrash esd value");
+		return ret;
+	}
+	return 0;
+}
+
+#define IRQ_EVENT_HEAD_LEN			8
+#define BYTES_PER_POINT				8
+#define COOR_DATA_CHECKSUM_SIZE		2
+
+#define GOODIX_TOUCH_EVENT			0x80
+#define GOODIX_REQUEST_EVENT		0x40
+#define GOODIX_GESTURE_EVENT		0x20
+#define POINT_TYPE_STYLUS_HOVER		0x01
+#define POINT_TYPE_STYLUS			0x03
+
+static void goodix_parse_finger(struct goodix_touch_data *touch_data,
+				u8 *buf, int touch_num)
+{
+	unsigned int id = 0, x = 0, y = 0, w = 0;
+	u8 *coor_data;
+	int i;
+
+	coor_data = &buf[IRQ_EVENT_HEAD_LEN];
+	for (i = 0; i < touch_num; i++) {
+		id = (coor_data[0] >> 4) & 0x0F;
+		if (id >= GOODIX_MAX_TOUCH) {
+			ts_info("invalid finger id =%d", id);
+			touch_data->touch_num = 0;
+			return;
+		}
+		x = le16_to_cpup((__le16 *)(coor_data + 2));
+		y = le16_to_cpup((__le16 *)(coor_data + 4));
+		w = le16_to_cpup((__le16 *)(coor_data + 6));
+		touch_data->coords[id].status = TS_TOUCH;
+		touch_data->coords[id].x = x;
+		touch_data->coords[id].y = y;
+		touch_data->coords[id].w = w;
+		coor_data += BYTES_PER_POINT;
+	}
+	touch_data->touch_num = touch_num;
+}
+
+static unsigned int goodix_pen_btn_code[] = {BTN_STYLUS, BTN_STYLUS2};
+static void goodix_parse_pen(struct goodix_pen_data *pen_data,
+	u8 *buf, int touch_num)
+{
+	unsigned int id = 0;
+	u8 cur_key_map = 0;
+	u8 *coor_data;
+	int16_t x_angle, y_angle;
+	int i;
+
+	pen_data->coords.tool_type = BTN_TOOL_PEN;
+
+	if (touch_num) {
+		pen_data->coords.status = TS_TOUCH;
+		coor_data = &buf[IRQ_EVENT_HEAD_LEN];
+
+		id = (coor_data[0] >> 4) & 0x0F;
+		pen_data->coords.x = le16_to_cpup((__le16 *)(coor_data + 2));
+		pen_data->coords.y = le16_to_cpup((__le16 *)(coor_data + 4));
+		pen_data->coords.p = le16_to_cpup((__le16 *)(coor_data + 6));
+		x_angle = le16_to_cpup((__le16 *)(coor_data + 8));
+		y_angle = le16_to_cpup((__le16 *)(coor_data + 10));
+		pen_data->coords.tilt_x = x_angle / 100;
+		pen_data->coords.tilt_y = y_angle / 100;
+	} else {
+		pen_data->coords.status = TS_RELEASE;
+	}
+
+	cur_key_map = (buf[3] & 0x0F) >> 1;
+	for (i = 0; i < GOODIX_MAX_PEN_KEY; i++) {
+		pen_data->keys[i].code = goodix_pen_btn_code[i];
+		if (!(cur_key_map & (1 << i)))
+			continue;
+		pen_data->keys[i].status = TS_TOUCH;
+	}
+}
+
+static int goodix_touch_handler(struct goodix_ts_core *cd,
+				struct goodix_ts_event *ts_event,
+				u8 *pre_buf, u32 pre_buf_len)
+{
+	struct goodix_ts_hw_ops *hw_ops = cd->hw_ops;
+	struct goodix_ic_info_misc *misc = &cd->ic_info.misc;
+	struct goodix_touch_data *touch_data = &ts_event->touch_data;
+	struct goodix_pen_data *pen_data = &ts_event->pen_data;
+	static u8 buffer[IRQ_EVENT_HEAD_LEN +
+			 BYTES_PER_POINT * GOODIX_MAX_TOUCH + 2];
+	u8 touch_num = 0;
+	int ret = 0;
+	u8 point_type = 0;
+	static u8 pre_finger_num;
+	static u8 pre_pen_num;
+
+	/* clean event buffer */
+	memset(ts_event, 0, sizeof(*ts_event));
+	/* copy pre-data to buffer */
+	memcpy(buffer, pre_buf, pre_buf_len);
+
+	touch_num = buffer[2] & 0x0F;
+
+	if (touch_num > GOODIX_MAX_TOUCH) {
+		ts_debug("invalid touch num %d", touch_num);
+		return -EINVAL;
+	}
+
+	if (unlikely(touch_num > 2)) {
+		ret = hw_ops->read(cd,
+				misc->touch_data_addr + pre_buf_len,
+				&buffer[pre_buf_len],
+				(touch_num - 2) * BYTES_PER_POINT);
+		if (ret) {
+			ts_debug("failed get touch data");
+			return ret;
+		}
+	}
+
+	/* read done */
+	hw_ops->after_event_handler(cd);
+
+	if (touch_num > 0) {
+		point_type = buffer[IRQ_EVENT_HEAD_LEN] & 0x0F;
+		if (point_type == POINT_TYPE_STYLUS ||
+				point_type == POINT_TYPE_STYLUS_HOVER) {
+			ret = checksum_cmp(&buffer[IRQ_EVENT_HEAD_LEN],
+					BYTES_PER_POINT * 2 + 2,
+					CHECKSUM_MODE_U8_LE);
+			if (ret) {
+				ts_debug("touch data checksum error");
+				ts_debug("data:%*ph", BYTES_PER_POINT * 2 + 2,
+						&buffer[IRQ_EVENT_HEAD_LEN]);
+				return -EINVAL;
+			}
+		} else {
+			ret = checksum_cmp(&buffer[IRQ_EVENT_HEAD_LEN],
+					touch_num * BYTES_PER_POINT + 2,
+					CHECKSUM_MODE_U8_LE);
+			if (ret) {
+				ts_debug("touch data checksum error");
+				ts_debug("data:%*ph",
+						touch_num * BYTES_PER_POINT + 2,
+						&buffer[IRQ_EVENT_HEAD_LEN]);
+				return -EINVAL;
+			}
+		}
+	}
+
+	if (touch_num > 0 && (point_type == POINT_TYPE_STYLUS
+				|| point_type == POINT_TYPE_STYLUS_HOVER)) {
+		/* stylus info */
+		if (pre_finger_num) {
+			ts_event->event_type = EVENT_TOUCH;
+			goodix_parse_finger(touch_data, buffer, 0);
+			pre_finger_num = 0;
+		} else {
+			pre_pen_num = 1;
+			ts_event->event_type = EVENT_PEN;
+			goodix_parse_pen(pen_data, buffer, touch_num);
+		}
+	} else {
+		/* finger info */
+		if (pre_pen_num) {
+			ts_event->event_type = EVENT_PEN;
+			goodix_parse_pen(pen_data, buffer, 0);
+			pre_pen_num = 0;
+		} else {
+			ts_event->event_type = EVENT_TOUCH;
+			goodix_parse_finger(touch_data, buffer, touch_num);
+			pre_finger_num = touch_num;
+		}
+	}
+
+	/* process custom info */
+	if (buffer[3] & 0x01)
+		ts_debug("TODO add custom info process function");
+
+	return 0;
+}
+
+static int brl_event_handler(struct goodix_ts_core *cd,
+			 struct goodix_ts_event *ts_event)
+{
+	struct goodix_ts_hw_ops *hw_ops = cd->hw_ops;
+	struct goodix_ic_info_misc *misc = &cd->ic_info.misc;
+	int pre_read_len;
+	u8 pre_buf[32];
+	u8 event_status;
+	int ret;
+
+	pre_read_len = IRQ_EVENT_HEAD_LEN +
+		BYTES_PER_POINT * 2 + COOR_DATA_CHECKSUM_SIZE;
+	ret = hw_ops->read(cd, misc->touch_data_addr,
+			   pre_buf, pre_read_len);
+	if (ret) {
+		ts_debug("failed get event head data");
+		return ret;
+	}
+
+	if (pre_buf[0] == 0x00) {
+		ts_debug("invalid touch head");
+		return -EINVAL;
+	}
+
+	if (checksum_cmp(pre_buf, IRQ_EVENT_HEAD_LEN, CHECKSUM_MODE_U8_LE)) {
+		ts_debug("touch head checksum err[%*ph]",
+				IRQ_EVENT_HEAD_LEN, pre_buf);
+		return -EINVAL;
+	}
+
+	event_status = pre_buf[0];
+	if (event_status & GOODIX_TOUCH_EVENT)
+		return goodix_touch_handler(cd, ts_event,
+					    pre_buf, pre_read_len);
+
+	if (event_status & GOODIX_REQUEST_EVENT) {
+		ts_event->event_type = EVENT_REQUEST;
+		if (pre_buf[2] == BRL_REQUEST_CODE_CONFIG)
+			ts_event->request_code = REQUEST_TYPE_CONFIG;
+		else if (pre_buf[2] == BRL_REQUEST_CODE_RESET)
+			ts_event->request_code = REQUEST_TYPE_RESET;
+		else
+			ts_debug("unsupported request code 0x%x", pre_buf[2]);
+	}
+
+	if (event_status & GOODIX_GESTURE_EVENT) {
+		ts_event->event_type = EVENT_GESTURE;
+		ts_event->gesture_type = pre_buf[4];
+		memcpy(ts_event->gesture_data, &pre_buf[8],
+				GOODIX_GESTURE_DATA_LEN);
+	}
+	/* read done */
+	hw_ops->after_event_handler(cd);
+
+	return 0;
+}
+
+static int brl_after_event_handler(struct goodix_ts_core *cd)
+{
+	struct goodix_ts_hw_ops *hw_ops = cd->hw_ops;
+	struct goodix_ic_info_misc *misc = &cd->ic_info.misc;
+	u8 sync_clean = 0;
+
+	if (cd->tools_ctrl_sync)
+		return 0;
+	return hw_ops->write(cd, misc->touch_data_addr,
+		&sync_clean, 1);
+}
+
+static int brld_get_framedata(struct goodix_ts_core *cd,
+		struct ts_rawdata_info *info)
+{
+	int ret;
+	unsigned char val;
+	int retry = 20;
+	struct frame_head *frame_head;
+	unsigned char frame_buf[GOODIX_MAX_FRAMEDATA_LEN];
+	unsigned char *cur_ptr;
+	unsigned int flag_addr = cd->ic_info.misc.frame_data_addr;
+
+	/* clean touch event flag */
+	val = 0;
+	ret = brl_write(cd, flag_addr, &val, 1);
+	if (ret < 0) {
+		ts_err("clean touch event failed, exit!");
+		return ret;
+	}
+
+	while (retry--) {
+		usleep_range(2000, 2100);
+		ret = brl_read(cd, flag_addr, &val, 1);
+		if (!ret && (val & GOODIX_TOUCH_EVENT))
+			break;
+	}
+	if (retry < 0) {
+		ts_err("framedata is not ready val:0x%02x, exit!", val);
+		return -EINVAL;
+	}
+
+	ret = brl_read(cd, flag_addr, frame_buf, GOODIX_MAX_FRAMEDATA_LEN);
+	if (ret < 0) {
+		ts_err("read frame data failed");
+		return ret;
+	}
+
+	if (checksum_cmp(frame_buf, cd->ic_info.misc.frame_data_head_len,
+			CHECKSUM_MODE_U8_LE)) {
+		ts_err("frame head checksum error");
+		return -EINVAL;
+	}
+
+	frame_head = (struct frame_head *)frame_buf;
+	if (checksum_cmp(frame_buf, frame_head->cur_frame_len,
+			CHECKSUM_MODE_U16_LE)) {
+		ts_err("frame body checksum error");
+		return -EINVAL;
+	}
+	cur_ptr = frame_buf;
+	cur_ptr += cd->ic_info.misc.frame_data_head_len;
+	cur_ptr += cd->ic_info.misc.fw_attr_len;
+	cur_ptr += cd->ic_info.misc.fw_log_len;
+	memcpy((u8 *)(info->buff + info->used_size), cur_ptr + 8,
+			cd->ic_info.misc.mutual_struct_len - 8);
+
+	return 0;
+}
+
+static int brld_get_cap_data(struct goodix_ts_core *cd,
+		struct ts_rawdata_info *info)
+{
+	struct goodix_ts_cmd temp_cmd;
+	int tx = cd->ic_info.parm.drv_num;
+	int rx = cd->ic_info.parm.sen_num;
+	int size = tx * rx;
+	int ret;
+
+	/* disable irq & close esd */
+	brl_irq_enbale(cd, false);
+	goodix_ts_blocking_notify(NOTIFY_ESD_OFF, NULL);
+
+	info->buff[0] = rx;
+	info->buff[1] = tx;
+	info->used_size = 2;
+
+	temp_cmd.cmd = 0x90;
+	temp_cmd.data[0] = 0x81;
+	temp_cmd.len = 5;
+	ret = brl_send_cmd(cd, &temp_cmd);
+	if (ret < 0) {
+		ts_err("report rawdata failed, exit!");
+		goto exit;
+	}
+
+	ret = brld_get_framedata(cd, info);
+	if (ret < 0) {
+		ts_err("brld get rawdata failed");
+		goto exit;
+	}
+	goodix_rotate_abcd2cbad(tx, rx, &info->buff[info->used_size]);
+	info->used_size += size;
+
+	temp_cmd.cmd = 0x90;
+	temp_cmd.data[0] = 0x82;
+	temp_cmd.len = 5;
+	ret = brl_send_cmd(cd, &temp_cmd);
+	if (ret < 0) {
+		ts_err("report diffdata failed, exit!");
+		goto exit;
+	}
+
+	ret = brld_get_framedata(cd, info);
+	if (ret < 0) {
+		ts_err("brld get diffdata failed");
+		goto exit;
+	}
+	goodix_rotate_abcd2cbad(tx, rx, &info->buff[info->used_size]);
+	info->used_size += size;
+
+exit:
+	temp_cmd.cmd = 0x90;
+	temp_cmd.data[0] = 0;
+	temp_cmd.len = 5;
+	brl_send_cmd(cd, &temp_cmd);
+	/* enable irq & esd */
+	brl_irq_enbale(cd, true);
+	goodix_ts_blocking_notify(NOTIFY_ESD_ON, NULL);
+	return ret;
+}
+
+#define GOODIX_CMD_RAWDATA	2
+#define GOODIX_CMD_COORD	0
+static int brl_get_capacitance_data(struct goodix_ts_core *cd,
+		struct ts_rawdata_info *info)
+{
+	int ret;
+	int retry = 20;
+	struct goodix_ts_cmd temp_cmd;
+	u32 flag_addr = cd->ic_info.misc.touch_data_addr;
+	u32 raw_addr = cd->ic_info.misc.mutual_rawdata_addr;
+	u32 diff_addr = cd->ic_info.misc.mutual_diffdata_addr;
+	int tx = cd->ic_info.parm.drv_num;
+	int rx = cd->ic_info.parm.sen_num;
+	int size = tx * rx;
+	u8 val;
+
+	if (!info) {
+		ts_err("input null ptr");
+		return -EIO;
+	}
+
+	if (cd->bus->ic_type == IC_TYPE_BERLIN_D)
+		return brld_get_cap_data(cd, info);
+
+	/* disable irq & close esd */
+	brl_irq_enbale(cd, false);
+	goodix_ts_blocking_notify(NOTIFY_ESD_OFF, NULL);
+
+	/* switch rawdata mode */
+	temp_cmd.cmd = GOODIX_CMD_RAWDATA;
+	temp_cmd.len = 4;
+	ret = brl_send_cmd(cd, &temp_cmd);
+	if (ret < 0) {
+		ts_err("switch rawdata mode failed, exit!");
+		goto exit;
+	}
+
+	/* clean touch event flag */
+	val = 0;
+	ret = brl_write(cd, flag_addr, &val, 1);
+	if (ret < 0) {
+		ts_err("clean touch event failed, exit!");
+		goto exit;
+	}
+
+	while (retry--) {
+		usleep_range(5000, 5100);
+		ret = brl_read(cd, flag_addr, &val, 1);
+		if (!ret && (val & GOODIX_TOUCH_EVENT))
+			break;
+	}
+	if (retry < 0) {
+		ts_err("rawdata is not ready val:0x%02x, exit!", val);
+		goto exit;
+	}
+
+	/* obtain rawdata & diff_rawdata */
+	info->buff[0] = rx;
+	info->buff[1] = tx;
+	info->used_size = 2;
+
+	ret = brl_read(cd, raw_addr, (u8 *)&info->buff[info->used_size],
+			size * sizeof(s16));
+	if (ret < 0) {
+		ts_err("obtian raw_data failed, exit!");
+		goto exit;
+	}
+	goodix_rotate_abcd2cbad(tx, rx, &info->buff[info->used_size]);
+	info->used_size += size;
+
+	ret = brl_read(cd, diff_addr, (u8 *)&info->buff[info->used_size],
+			size * sizeof(s16));
+	if (ret < 0) {
+		ts_err("obtian diff_data failed, exit!");
+		goto exit;
+	}
+	goodix_rotate_abcd2cbad(tx, rx, &info->buff[info->used_size]);
+	info->used_size += size;
+
+exit:
+	/* switch coor mode */
+	temp_cmd.cmd = GOODIX_CMD_COORD;
+	temp_cmd.len = 4;
+	brl_send_cmd(cd, &temp_cmd);
+	/* clean touch event flag */
+	val = 0;
+	brl_write(cd, flag_addr, &val, 1);
+	/* enable irq & esd */
+	brl_irq_enbale(cd, true);
+	goodix_ts_blocking_notify(NOTIFY_ESD_ON, NULL);
+	return ret;
+}
+
+static struct goodix_ts_hw_ops brl_hw_ops = {
+	.power_on = brl_power_on,
+	.resume = brl_resume,
+	.suspend = brl_suspend,
+	.gesture = brl_gesture,
+	.reset = brl_reset,
+	.irq_enable = brl_irq_enbale,
+	.read = brl_read,
+	.write = brl_write,
+	.send_cmd = brl_send_cmd,
+	.send_config = brl_send_config,
+	.read_config = brl_read_config,
+	.read_version = brl_read_version,
+	.get_ic_info = brl_get_ic_info,
+	.esd_check = brl_esd_check,
+	.event_handler = brl_event_handler,
+	.after_event_handler = brl_after_event_handler,
+	.get_capacitance_data = brl_get_capacitance_data,
+};
+
+struct goodix_ts_hw_ops *goodix_get_hw_ops(void)
+{
+	return &brl_hw_ops;
+}

+ 272 - 0
qcom/opensource/touch-drivers/goodix_berlin_driver/goodix_brl_i2c.c

@@ -0,0 +1,272 @@
+ /*
+  * Goodix Touchscreen Driver
+  * Copyright (C) 2020 - 2021 Goodix, Inc.
+  *
+  * This program is free software; you can redistribute it and/or modify
+  * it under the terms of the GNU General Public License as published by
+  * the Free Software Foundation; either version 2 of the License, or
+  * (at your option) any later version.
+  *
+  * This program is distributed in the hope that it will be a reference
+  * to you, when you are integrating the GOODiX's CTP IC into your system,
+  * but WITHOUT ANY WARRANTY; without even the implied warranty of
+  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  * General Public License for more details.
+  *
+  */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/version.h>
+
+#include "goodix_ts_core.h"
+
+#define TS_DRIVER_NAME				"gtx8_i2c"
+#define I2C_MAX_TRANSFER_SIZE		256
+#define GOODIX_BUS_RETRY_TIMES		2
+#define GOODIX_REG_ADDR_SIZE		4
+
+static struct platform_device *goodix_pdev;
+struct goodix_bus_interface goodix_i2c_bus;
+
+static int goodix_i2c_read(struct device *dev, unsigned int reg,
+			 unsigned char *data, unsigned int len)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	unsigned int transfer_length = 0;
+	unsigned int pos = 0, address = reg;
+	unsigned char get_buf[128], addr_buf[GOODIX_REG_ADDR_SIZE];
+	int retry, r = 0;
+	struct i2c_msg msgs[] = {
+		{
+			.addr = client->addr,
+			.flags = !I2C_M_RD,
+			.buf = &addr_buf[0],
+			.len = GOODIX_REG_ADDR_SIZE,
+		}, {
+			.addr = client->addr,
+			.flags = I2C_M_RD,
+		}
+	};
+
+	if (likely(len < sizeof(get_buf))) {
+		/* code optimize, use stack memory */
+		msgs[1].buf = &get_buf[0];
+	} else {
+		msgs[1].buf = kzalloc(len, GFP_KERNEL);
+		if (msgs[1].buf == NULL)
+			return -ENOMEM;
+	}
+
+	while (pos != len) {
+		if (unlikely(len - pos > I2C_MAX_TRANSFER_SIZE))
+			transfer_length = I2C_MAX_TRANSFER_SIZE;
+		else
+			transfer_length = len - pos;
+
+		msgs[0].buf[0] = (address >> 24) & 0xFF;
+		msgs[0].buf[1] = (address >> 16) & 0xFF;
+		msgs[0].buf[2] = (address >> 8) & 0xFF;
+		msgs[0].buf[3] = address & 0xFF;
+		msgs[1].len = transfer_length;
+
+		for (retry = 0; retry < GOODIX_BUS_RETRY_TIMES; retry++) {
+			if (likely(i2c_transfer(client->adapter, msgs, 2) == 2)) {
+				memcpy(&data[pos], msgs[1].buf, transfer_length);
+				pos += transfer_length;
+				address += transfer_length;
+				break;
+			}
+			ts_info("I2c read retry[%d]:0x%x", retry + 1, reg);
+			usleep_range(2000, 2100);
+		}
+		if (unlikely(retry == GOODIX_BUS_RETRY_TIMES)) {
+			ts_err("I2c read failed,dev:%02x,reg:%04x,size:%u",
+				client->addr, reg, len);
+			r = -EAGAIN;
+			goto read_exit;
+		}
+	}
+
+read_exit:
+	if (unlikely(len >= sizeof(get_buf)))
+		kfree(msgs[1].buf);
+	return r;
+}
+
+static int goodix_i2c_write(struct device *dev, unsigned int reg,
+			unsigned char *data, unsigned int len)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	unsigned int pos = 0, transfer_length = 0;
+	unsigned int address = reg;
+	unsigned char put_buf[128];
+	int retry, r = 0;
+	struct i2c_msg msg = {
+		.addr = client->addr,
+		.flags = !I2C_M_RD,
+	};
+
+	if (likely(len + GOODIX_REG_ADDR_SIZE < sizeof(put_buf))) {
+		/* code optimize,use stack memory*/
+		msg.buf = &put_buf[0];
+	} else {
+		msg.buf = kmalloc(len + GOODIX_REG_ADDR_SIZE, GFP_KERNEL);
+		if (msg.buf == NULL)
+			return -ENOMEM;
+	}
+
+	while (pos != len) {
+		if (unlikely(len - pos > I2C_MAX_TRANSFER_SIZE -
+				GOODIX_REG_ADDR_SIZE))
+			transfer_length = I2C_MAX_TRANSFER_SIZE -
+				GOODIX_REG_ADDR_SIZE;
+		else
+			transfer_length = len - pos;
+		msg.buf[0] = (address >> 24) & 0xFF;
+		msg.buf[1] = (address >> 16) & 0xFF;
+		msg.buf[2] = (address >> 8) & 0xFF;
+		msg.buf[3] = address & 0xFF;
+
+		msg.len = transfer_length + GOODIX_REG_ADDR_SIZE;
+		memcpy(&msg.buf[GOODIX_REG_ADDR_SIZE],
+			&data[pos], transfer_length);
+
+		for (retry = 0; retry < GOODIX_BUS_RETRY_TIMES; retry++) {
+			if (likely(i2c_transfer(client->adapter,
+						&msg, 1) == 1)) {
+				pos += transfer_length;
+				address += transfer_length;
+				break;
+			}
+			ts_debug("I2c write retry[%d]", retry + 1);
+			msleep(20);
+		}
+		if (unlikely(retry == GOODIX_BUS_RETRY_TIMES)) {
+			ts_err("I2c write failed,dev:%02x,reg:%04x,size:%u",
+				client->addr, reg, len);
+			r = -EAGAIN;
+			goto write_exit;
+		}
+	}
+
+write_exit:
+	if (likely(len + GOODIX_REG_ADDR_SIZE >= sizeof(put_buf)))
+		kfree(msg.buf);
+	return r;
+}
+
+static void goodix_pdev_release(struct device *dev)
+{
+	ts_info("goodix pdev released");
+	kfree(goodix_pdev);
+}
+
+static int goodix_i2c_probe(struct i2c_client *client,
+	const struct i2c_device_id *dev_id)
+{
+	int ret = 0;
+
+	ts_info("goodix i2c probe in");
+
+#ifndef CONFIG_ARCH_QTI_VM
+	ret = i2c_check_functionality(client->adapter, I2C_FUNC_I2C);
+	if (!ret)
+		return -EIO;
+#endif
+
+	/* get ic type */
+	ret = goodix_get_ic_type(client->dev.of_node);
+	if (ret < 0)
+		return ret;
+
+	goodix_i2c_bus.ic_type = ret;
+	goodix_i2c_bus.bus_type = GOODIX_BUS_TYPE_I2C;
+	goodix_i2c_bus.dev = &client->dev;
+	goodix_i2c_bus.read = goodix_i2c_read;
+	goodix_i2c_bus.write = goodix_i2c_write;
+	/* ts core device */
+	goodix_pdev = kzalloc(sizeof(struct platform_device), GFP_KERNEL);
+	if (!goodix_pdev)
+		return -ENOMEM;
+
+	goodix_pdev->name = GOODIX_CORE_DRIVER_NAME;
+	goodix_pdev->id = 0;
+	goodix_pdev->num_resources = 0;
+	/*
+	 * you can find this platform dev in
+	 * /sys/devices/platform/goodix_ts.0
+	 * goodix_pdev->dev.parent = &client->dev;
+	 */
+	goodix_pdev->dev.platform_data = &goodix_i2c_bus;
+	goodix_pdev->dev.release = goodix_pdev_release;
+
+	/* register platform device, then the goodix_ts_core
+	 * module will probe the touch device.
+	 */
+	ret = platform_device_register(goodix_pdev);
+	if (ret) {
+		ts_err("failed register goodix platform device, %d", ret);
+		goto err_pdev;
+	}
+	ts_info("i2c probe out");
+	return ret;
+
+err_pdev:
+	kfree(goodix_pdev);
+	goodix_pdev = NULL;
+	ts_info("i2c probe out, %d", ret);
+	return ret;
+}
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0))
+static void goodix_i2c_remove(struct i2c_client *client)
+{
+	platform_device_unregister(goodix_pdev);
+}
+#else
+static int goodix_i2c_remove(struct i2c_client *client)
+{
+	platform_device_unregister(goodix_pdev);
+	return 0;
+}
+#endif
+
+#ifdef CONFIG_OF
+static const struct of_device_id i2c_matchs[] = {
+	{.compatible = "goodix,gt9897",},
+	{.compatible = "goodix,gt9966",},
+	{.compatible = "goodix,gt9916",},
+	{},
+};
+MODULE_DEVICE_TABLE(of, i2c_matchs);
+#endif
+
+static const struct i2c_device_id i2c_id_table[] = {
+	{TS_DRIVER_NAME, 0},
+	{},
+};
+MODULE_DEVICE_TABLE(i2c, i2c_id_table);
+
+static struct i2c_driver goodix_i2c_driver = {
+	.driver = {
+		.name = TS_DRIVER_NAME,
+		//.owner = THIS_MODULE,
+		.of_match_table = of_match_ptr(i2c_matchs),
+	},
+	.probe = goodix_i2c_probe,
+	.remove = goodix_i2c_remove,
+	.id_table = i2c_id_table,
+};
+
+int goodix_i2c_bus_init(void)
+{
+	ts_info("Goodix i2c driver init");
+	return i2c_add_driver(&goodix_i2c_driver);
+}
+
+void goodix_i2c_bus_exit(void)
+{
+	ts_info("Goodix i2c driver exit");
+	i2c_del_driver(&goodix_i2c_driver);
+}

+ 317 - 0
qcom/opensource/touch-drivers/goodix_berlin_driver/goodix_brl_spi.c

@@ -0,0 +1,317 @@
+ /*
+  * Goodix Touchscreen Driver
+  * Copyright (C) 2020 - 2021 Goodix, Inc.
+  *
+  * This program is free software; you can redistribute it and/or modify
+  * it under the terms of the GNU General Public License as published by
+  * the Free Software Foundation; either version 2 of the License, or
+  * (at your option) any later version.
+  *
+  * This program is distributed in the hope that it will be a reference
+  * to you, when you are integrating the GOODiX's CTP IC into your system,
+  * but WITHOUT ANY WARRANTY; without even the implied warranty of
+  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  * General Public License for more details.
+  *
+  */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+#include <linux/version.h>
+
+#include "goodix_ts_core.h"
+#define TS_DRIVER_NAME		"gtx8_spi"
+
+#define SPI_TRANS_PREFIX_LEN    1
+#define REGISTER_WIDTH          4
+#define SPI_READ_DUMMY_LEN      4
+#define SPI_READ_PREFIX_LEN  \
+		(SPI_TRANS_PREFIX_LEN + REGISTER_WIDTH + SPI_READ_DUMMY_LEN)
+#define SPI_WRITE_PREFIX_LEN (SPI_TRANS_PREFIX_LEN + REGISTER_WIDTH)
+
+#define SPI_WRITE_FLAG  0xF0
+#define SPI_READ_FLAG   0xF1
+
+static struct platform_device *goodix_pdev;
+struct goodix_bus_interface goodix_spi_bus[MAX_SUPPORTED_TOUCH_PANELS];
+
+/**
+ * goodix_spi_read_bra- read device register through spi bus
+ * @dev: pointer to device data
+ * @addr: register address
+ * @data: read buffer
+ * @len: bytes to read
+ * return: 0 - read ok, < 0 - spi transter error
+ */
+static int goodix_spi_read_bra(struct device *dev, unsigned int addr,
+	unsigned char *data, unsigned int len)
+{
+	struct spi_device *spi = to_spi_device(dev);
+	u8 *rx_buf = NULL;
+	u8 *tx_buf = NULL;
+	struct spi_transfer xfers;
+	struct spi_message spi_msg;
+	int ret = 0;
+
+	rx_buf = kzalloc(SPI_READ_PREFIX_LEN + len, GFP_KERNEL);
+	tx_buf = kzalloc(SPI_READ_PREFIX_LEN + len, GFP_KERNEL);
+	if (!rx_buf || !tx_buf) {
+		ts_err("alloc tx/rx_buf failed, size:%d",
+			SPI_READ_PREFIX_LEN + len);
+		return -ENOMEM;
+	}
+
+	spi_message_init(&spi_msg);
+	memset(&xfers, 0, sizeof(xfers));
+
+	/*spi_read tx_buf format: 0xF1 + addr(4bytes) + data*/
+	tx_buf[0] = SPI_READ_FLAG;
+	tx_buf[1] = (addr >> 24) & 0xFF;
+	tx_buf[2] = (addr >> 16) & 0xFF;
+	tx_buf[3] = (addr >> 8) & 0xFF;
+	tx_buf[4] = addr & 0xFF;
+	tx_buf[5] = 0xFF;
+	tx_buf[6] = 0xFF;
+	tx_buf[7] = 0xFF;
+	tx_buf[8] = 0xFF;
+
+	xfers.tx_buf = tx_buf;
+	xfers.rx_buf = rx_buf;
+	xfers.len = SPI_READ_PREFIX_LEN + len;
+	xfers.cs_change = 0;
+	spi_message_add_tail(&xfers, &spi_msg);
+	ret = spi_sync(spi, &spi_msg);
+	if (ret < 0) {
+		ts_err("spi transfer error:%d", ret);
+		goto exit;
+	}
+	memcpy(data, &rx_buf[SPI_READ_PREFIX_LEN], len);
+
+exit:
+	kfree(rx_buf);
+	kfree(tx_buf);
+	return ret;
+}
+
+static int goodix_spi_read(struct device *dev, unsigned int addr,
+	unsigned char *data, unsigned int len)
+{
+	struct spi_device *spi = to_spi_device(dev);
+	u8 *rx_buf = NULL;
+	u8 *tx_buf = NULL;
+	struct spi_transfer xfers;
+	struct spi_message spi_msg;
+	int ret = 0;
+
+	rx_buf = kzalloc(SPI_READ_PREFIX_LEN - 1 + len, GFP_KERNEL);
+	tx_buf = kzalloc(SPI_READ_PREFIX_LEN - 1 + len, GFP_KERNEL);
+	if (!rx_buf || !tx_buf) {
+		ts_err("alloc tx/rx_buf failed, size:%d",
+			SPI_READ_PREFIX_LEN - 1 + len);
+		return -ENOMEM;
+	}
+
+	spi_message_init(&spi_msg);
+	memset(&xfers, 0, sizeof(xfers));
+
+	/*spi_read tx_buf format: 0xF1 + addr(4bytes) + data*/
+	tx_buf[0] = SPI_READ_FLAG;
+	tx_buf[1] = (addr >> 24) & 0xFF;
+	tx_buf[2] = (addr >> 16) & 0xFF;
+	tx_buf[3] = (addr >> 8) & 0xFF;
+	tx_buf[4] = addr & 0xFF;
+	tx_buf[5] = 0xFF;
+	tx_buf[6] = 0xFF;
+	tx_buf[7] = 0xFF;
+
+	xfers.tx_buf = tx_buf;
+	xfers.rx_buf = rx_buf;
+	xfers.len = SPI_READ_PREFIX_LEN - 1 + len;
+	xfers.cs_change = 0;
+	spi_message_add_tail(&xfers, &spi_msg);
+	ret = spi_sync(spi, &spi_msg);
+	if (ret < 0) {
+		ts_err("spi transfer error:%d", ret);
+		goto exit;
+	}
+	memcpy(data, &rx_buf[SPI_READ_PREFIX_LEN - 1], len);
+
+exit:
+	kfree(rx_buf);
+	kfree(tx_buf);
+	return ret;
+}
+
+/**
+ * goodix_spi_write- write device register through spi bus
+ * @dev: pointer to device data
+ * @addr: register address
+ * @data: write buffer
+ * @len: bytes to write
+ * return: 0 - write ok; < 0 - spi transter error.
+ */
+static int goodix_spi_write(struct device *dev, unsigned int addr,
+		unsigned char *data, unsigned int len)
+{
+	struct spi_device *spi = to_spi_device(dev);
+	u8 *tx_buf = NULL;
+	struct spi_transfer xfers;
+	struct spi_message spi_msg;
+	int ret = 0;
+
+	tx_buf = kzalloc(SPI_WRITE_PREFIX_LEN + len, GFP_KERNEL);
+	if (!tx_buf)
+		return -ENOMEM;
+
+	spi_message_init(&spi_msg);
+	memset(&xfers, 0, sizeof(xfers));
+
+	tx_buf[0] = SPI_WRITE_FLAG;
+	tx_buf[1] = (addr >> 24) & 0xFF;
+	tx_buf[2] = (addr >> 16) & 0xFF;
+	tx_buf[3] = (addr >> 8) & 0xFF;
+	tx_buf[4] = addr & 0xFF;
+	memcpy(&tx_buf[SPI_WRITE_PREFIX_LEN], data, len);
+	xfers.tx_buf = tx_buf;
+	xfers.len = SPI_WRITE_PREFIX_LEN + len;
+	xfers.cs_change = 0;
+	spi_message_add_tail(&xfers, &spi_msg);
+	ret = spi_sync(spi, &spi_msg);
+	if (ret < 0)
+		ts_err("spi transfer error:%d", ret);
+
+	kfree(tx_buf);
+	return ret;
+}
+
+static void goodix_pdev_release(struct device *dev)
+{
+	ts_info("goodix pdev released");
+	kfree(goodix_pdev);
+}
+
+static int goodix_spi_probe(struct spi_device *spi)
+{
+	int ret = 0, idx;
+
+	ts_info("goodix spi probe in");
+
+	/* init spi_device */
+	spi->mode          = SPI_MODE_0;
+	spi->bits_per_word = 8;
+
+	ret = spi_setup(spi);
+	if (ret) {
+		ts_err("failed set spi mode, %d", ret);
+		return ret;
+	}
+
+	/* get ic type */
+	ret = goodix_get_ic_type(spi->dev.of_node);
+	if (ret < 0)
+		return ret;
+
+	idx = goodix_get_touch_type(spi->dev.of_node);
+	if (idx < 0 || idx >= MAX_SUPPORTED_TOUCH_PANELS) {
+		ts_err("unsupported touch type idx:%d", idx);
+		return -ENODEV;
+	}
+
+	goodix_spi_bus[idx].ic_type = ret;
+	goodix_spi_bus[idx].bus_type = GOODIX_BUS_TYPE_SPI;
+	goodix_spi_bus[idx].dev = &spi->dev;
+	if (goodix_spi_bus[idx].ic_type == IC_TYPE_BERLIN_A)
+		goodix_spi_bus[idx].read = goodix_spi_read_bra;
+	else
+		goodix_spi_bus[idx].read = goodix_spi_read;
+	goodix_spi_bus[idx].write = goodix_spi_write;
+	/* ts core device */
+	goodix_pdev = kzalloc(sizeof(struct platform_device), GFP_KERNEL);
+	if (!goodix_pdev)
+		return -ENOMEM;
+
+	if (idx)
+		goodix_pdev->name = GOODIX_CORE_DEVICE_2_NAME;
+	else
+		goodix_pdev->name = GOODIX_CORE_DEVICE_NAME;
+
+	goodix_pdev->id = 0;
+	goodix_pdev->num_resources = 0;
+	/*
+	 * you can find this platform dev in
+	 * /sys/devices/platfrom/goodix_ts.0
+	 * goodix_pdev->dev.parent = &client->dev;
+	 */
+	goodix_pdev->dev.platform_data = &goodix_spi_bus[idx];
+	goodix_pdev->dev.release = goodix_pdev_release;
+
+	/*
+	 * register platform device, then the goodix_ts_core
+	 * module will probe the touch deivce.
+	 */
+	ret = platform_device_register(goodix_pdev);
+	if (ret) {
+		ts_err("failed register goodix platform device, %d", ret);
+		goto err_pdev;
+	}
+	ts_info("spi probe out");
+	return 0;
+
+err_pdev:
+	kfree(goodix_pdev);
+	goodix_pdev = NULL;
+	ts_info("spi probe out, %d", ret);
+	return ret;
+}
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 19, 0))
+	static void goodix_spi_remove(struct spi_device *spi)
+	{
+	platform_device_unregister(goodix_pdev);
+	}
+#else
+	static int goodix_spi_remove(struct spi_device *spi)
+	{
+	platform_device_unregister(goodix_pdev);
+	return 0;
+	}
+#endif
+
+#ifdef CONFIG_OF
+static const struct of_device_id spi_matchs[] = {
+	{.compatible = "goodix,gt9897S",},
+	{.compatible = "goodix,gt9897T",},
+	{.compatible = "goodix,gt9966S",},
+	{.compatible = "goodix,gt9916S",},
+	{.compatible = "goodix,gt9916S2",},
+	{},
+};
+#endif
+
+static const struct spi_device_id spi_id_table[] = {
+	{TS_DRIVER_NAME, 0},
+	{},
+};
+
+static struct spi_driver goodix_spi_driver = {
+	.driver = {
+		.name = TS_DRIVER_NAME,
+		//.owner = THIS_MODULE,
+		.of_match_table = spi_matchs,
+	},
+	.id_table = spi_id_table,
+	.probe = goodix_spi_probe,
+	.remove = goodix_spi_remove,
+};
+
+int goodix_spi_bus_init(void)
+{
+	ts_info("Goodix spi driver init");
+	return spi_register_driver(&goodix_spi_driver);
+}
+
+void goodix_spi_bus_exit(void)
+{
+	ts_info("Goodix spi driver exit");
+	spi_unregister_driver(&goodix_spi_driver);
+}

+ 343 - 0
qcom/opensource/touch-drivers/goodix_berlin_driver/goodix_cfg_bin.c

@@ -0,0 +1,343 @@
+ /*
+  * Goodix Touchscreen Driver
+  * Copyright (C) 2020 - 2021 Goodix, Inc.
+  *
+  * This program is free software; you can redistribute it and/or modify
+  * it under the terms of the GNU General Public License as published by
+  * the Free Software Foundation; either version 2 of the License, or
+  * (at your option) any later version.
+  *
+  * This program is distributed in the hope that it will be a reference
+  * to you, when you are integrating the GOODiX's CTP IC into your system,
+  * but WITHOUT ANY WARRANTY; without even the implied warranty of
+  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  * General Public License for more details.
+  *
+  */
+#include "goodix_ts_core.h"
+
+#define TS_BIN_VERSION_START_INDEX		5
+#define TS_BIN_VERSION_LEN				4
+#define TS_CFG_BIN_HEAD_RESERVED_LEN	6
+#define TS_CFG_OFFSET_LEN				2
+#define TS_IC_TYPE_NAME_MAX_LEN			15
+#define TS_CFG_BIN_HEAD_LEN \
+		(sizeof(struct goodix_cfg_bin_head) + \
+		TS_CFG_BIN_HEAD_RESERVED_LEN)
+#define TS_PKG_CONST_INFO_LEN \
+		(sizeof(struct goodix_cfg_pkg_const_info))
+#define TS_PKG_REG_INFO_LEN	\
+		(sizeof(struct goodix_cfg_pkg_reg_info))
+#define TS_PKG_HEAD_LEN \
+		(TS_PKG_CONST_INFO_LEN + TS_PKG_REG_INFO_LEN)
+
+/*cfg block definitin*/
+#define TS_CFG_BLOCK_PID_LEN		8
+#define TS_CFG_BLOCK_VID_LEN		8
+#define TS_CFG_BLOCK_FW_MASK_LEN	9
+#define TS_CFG_BLOCK_FW_PATCH_LEN	4
+#define TS_CFG_BLOCK_RESERVED_LEN	9
+
+#define TS_NORMAL_CFG				0x01
+#define TS_HIGH_SENSE_CFG			0x03
+#define TS_RQST_FW_RETRY_TIMES		2
+
+#pragma pack(1)
+struct goodix_cfg_pkg_reg {
+	u16 addr;
+	u8 reserved1;
+	u8 reserved2;
+};
+
+struct goodix_cfg_pkg_const_info {
+	u32 pkg_len;
+	u8 ic_type[TS_IC_TYPE_NAME_MAX_LEN];
+	u8 cfg_type;
+	u8 sensor_id;
+	u8 hw_pid[TS_CFG_BLOCK_PID_LEN];
+	u8 hw_vid[TS_CFG_BLOCK_VID_LEN];
+	u8 fw_mask[TS_CFG_BLOCK_FW_MASK_LEN];
+	u8 fw_patch[TS_CFG_BLOCK_FW_PATCH_LEN];
+	u16 x_res_offset;
+	u16 y_res_offset;
+	u16 trigger_offset;
+};
+
+struct goodix_cfg_pkg_reg_info {
+	struct goodix_cfg_pkg_reg cfg_send_flag;
+	struct goodix_cfg_pkg_reg version_base;
+	struct goodix_cfg_pkg_reg pid;
+	struct goodix_cfg_pkg_reg vid;
+	struct goodix_cfg_pkg_reg sensor_id;
+	struct goodix_cfg_pkg_reg fw_mask;
+	struct goodix_cfg_pkg_reg fw_status;
+	struct goodix_cfg_pkg_reg cfg_addr;
+	struct goodix_cfg_pkg_reg esd;
+	struct goodix_cfg_pkg_reg command;
+	struct goodix_cfg_pkg_reg coor;
+	struct goodix_cfg_pkg_reg gesture;
+	struct goodix_cfg_pkg_reg fw_request;
+	struct goodix_cfg_pkg_reg proximity;
+	u8 reserved[TS_CFG_BLOCK_RESERVED_LEN];
+};
+
+struct goodix_cfg_bin_head {
+	u32 bin_len;
+	u8 checksum;
+	u8 bin_version[TS_BIN_VERSION_LEN];
+	u8 pkg_num;
+};
+
+#pragma pack()
+
+struct goodix_cfg_package {
+	struct goodix_cfg_pkg_const_info cnst_info;
+	struct goodix_cfg_pkg_reg_info reg_info;
+	const u8 *cfg;
+	u32 pkg_len;
+};
+
+struct goodix_cfg_bin {
+	unsigned char *bin_data;
+	unsigned int bin_data_len;
+	struct goodix_cfg_bin_head head;
+	struct goodix_cfg_package *cfg_pkgs;
+};
+
+static int goodix_read_cfg_bin(struct device *dev, const char *cfg_name,
+			struct goodix_cfg_bin *cfg_bin)
+{
+	const struct firmware *firmware = NULL;
+	int ret;
+	int retry = GOODIX_RETRY_3;
+
+	ts_info("cfg_bin_name:%s", cfg_name);
+
+	while (retry--) {
+		ret = request_firmware(&firmware, cfg_name, dev);
+		if (!ret)
+			break;
+		ts_info("get cfg bin retry:[%d]", GOODIX_RETRY_3 - retry);
+		msleep(200);
+	}
+	if (retry < 0) {
+		ts_err("failed get cfg bin[%s] error:%d", cfg_name, ret);
+		return ret;
+	}
+
+	if (firmware->size <= 0) {
+		ts_err("request_firmware, cfg_bin length ERROR,len:%zu",
+			firmware->size);
+		ret = -EINVAL;
+		goto exit;
+	}
+
+	cfg_bin->bin_data_len = firmware->size;
+	/* allocate memory for cfg_bin->bin_data */
+	cfg_bin->bin_data = kzalloc(cfg_bin->bin_data_len, GFP_KERNEL);
+	if (!cfg_bin->bin_data) {
+		ret = -ENOMEM;
+		goto exit;
+	}
+	memcpy(cfg_bin->bin_data, firmware->data, cfg_bin->bin_data_len);
+
+exit:
+	release_firmware(firmware);
+	return ret;
+}
+
+static int goodix_parse_cfg_bin(struct goodix_cfg_bin *cfg_bin)
+{
+	u16 offset1, offset2;
+	u8 checksum;
+	int i;
+
+	/* copy cfg_bin head info */
+	if (cfg_bin->bin_data_len < sizeof(struct goodix_cfg_bin_head)) {
+		ts_err("Invalid cfg_bin size:%d", cfg_bin->bin_data_len);
+		return -EINVAL;
+	}
+
+	memcpy(&cfg_bin->head, cfg_bin->bin_data,
+		sizeof(struct goodix_cfg_bin_head));
+	cfg_bin->head.bin_len = le32_to_cpu(cfg_bin->head.bin_len);
+
+	/*check length*/
+	if (cfg_bin->bin_data_len != cfg_bin->head.bin_len) {
+		ts_err("cfg_bin len check failed,%d != %d",
+			cfg_bin->head.bin_len, cfg_bin->bin_data_len);
+		return -EINVAL;
+	}
+
+	/*check cfg_bin valid*/
+	checksum = 0;
+	for (i = TS_BIN_VERSION_START_INDEX; i < cfg_bin->bin_data_len; i++)
+		checksum += cfg_bin->bin_data[i];
+
+	if (checksum != cfg_bin->head.checksum) {
+		ts_err("cfg_bin checksum check filed 0x%02x != 0x%02x",
+			cfg_bin->head.checksum, checksum);
+		return -EINVAL;
+	}
+
+	/*allocate memory for cfg packages*/
+	cfg_bin->cfg_pkgs = kzalloc(sizeof(struct goodix_cfg_package) *
+					cfg_bin->head.pkg_num, GFP_KERNEL);
+	if (!cfg_bin->cfg_pkgs)
+		return -ENOMEM;
+
+	/*get cfg_pkg's info*/
+	for (i = 0; i < cfg_bin->head.pkg_num; i++) {
+		/*get cfg pkg length*/
+		if (i == cfg_bin->head.pkg_num - 1) {
+			offset1 = cfg_bin->bin_data[TS_CFG_BIN_HEAD_LEN +
+					i * TS_CFG_OFFSET_LEN] +
+					(cfg_bin->bin_data[TS_CFG_BIN_HEAD_LEN +
+					i * TS_CFG_OFFSET_LEN + 1] << 8);
+
+			cfg_bin->cfg_pkgs[i].pkg_len =
+					cfg_bin->bin_data_len - offset1;
+		} else {
+			offset1 = cfg_bin->bin_data[TS_CFG_BIN_HEAD_LEN +
+					i * TS_CFG_OFFSET_LEN] +
+					(cfg_bin->bin_data[TS_CFG_BIN_HEAD_LEN +
+					i * TS_CFG_OFFSET_LEN + 1] << 8);
+
+			offset2 = cfg_bin->bin_data[TS_CFG_BIN_HEAD_LEN +
+					i * TS_CFG_OFFSET_LEN + 2] +
+					(cfg_bin->bin_data[TS_CFG_BIN_HEAD_LEN +
+					i * TS_CFG_OFFSET_LEN + 3] << 8);
+
+			if (offset2 <= offset1) {
+				ts_err("offset error,pkg:%d, offset1:%d, offset2:%d",
+						i, offset1, offset2);
+				goto exit;
+			}
+
+			cfg_bin->cfg_pkgs[i].pkg_len = offset2 - offset1;
+		}
+		/*get cfg pkg head*/
+		memcpy(&cfg_bin->cfg_pkgs[i].cnst_info,
+			&cfg_bin->bin_data[offset1], TS_PKG_CONST_INFO_LEN);
+		memcpy(&cfg_bin->cfg_pkgs[i].reg_info,
+			&cfg_bin->bin_data[offset1 + TS_PKG_CONST_INFO_LEN],
+			TS_PKG_REG_INFO_LEN);
+
+		/*get configuration data*/
+		cfg_bin->cfg_pkgs[i].cfg =
+				&cfg_bin->bin_data[offset1 + TS_PKG_HEAD_LEN];
+	}
+
+	/*debug, print pkg information*/
+	ts_info("Driver bin info: ver %s, len %d, pkgs %d",
+			cfg_bin->head.bin_version,
+			cfg_bin->head.bin_len,
+			cfg_bin->head.pkg_num);
+
+	return 0;
+exit:
+	kfree(cfg_bin->cfg_pkgs);
+	return -EINVAL;
+}
+
+static int goodix_get_reg_and_cfg(struct goodix_ts_core *cd, u8 sensor_id,
+			struct goodix_cfg_bin *cfg_bin)
+{
+	int i;
+	u8 cfg_type;
+	u32 cfg_len;
+	struct goodix_cfg_package *cfg_pkg;
+
+	if (!cfg_bin->head.pkg_num || !cfg_bin->cfg_pkgs) {
+		ts_err("there is none cfg package, pkg_num:%d",
+			cfg_bin->head.pkg_num);
+		return -EINVAL;
+	}
+
+	/* find cfg packages with same sensor_id */
+	for (i = 0; i < cfg_bin->head.pkg_num; i++) {
+		cfg_pkg = &cfg_bin->cfg_pkgs[i];
+		if (sensor_id != cfg_pkg->cnst_info.sensor_id) {
+			ts_info("pkg:%d, sensor id contrast FAILED, bin %d != %d",
+				i, cfg_pkg->cnst_info.sensor_id, sensor_id);
+			continue;
+		}
+		cfg_type = cfg_pkg->cnst_info.cfg_type;
+		if (cfg_type >= GOODIX_MAX_CONFIG_GROUP) {
+			ts_err("usupported config type %d",
+				cfg_pkg->cnst_info.cfg_type);
+			goto err_out;
+		}
+
+		cfg_len = cfg_pkg->pkg_len - TS_PKG_CONST_INFO_LEN -
+				TS_PKG_REG_INFO_LEN;
+		if (cfg_len > GOODIX_CFG_MAX_SIZE) {
+			ts_err("config len exceed limit %d > %d",
+				cfg_len, GOODIX_CFG_MAX_SIZE);
+			goto err_out;
+		}
+		if (cd->ic_configs[cfg_type]) {
+			ts_err("found same type config twice for sensor id %d, skiped",
+				sensor_id);
+			continue;
+		}
+		cd->ic_configs[cfg_type] =
+				kzalloc(sizeof(struct goodix_ic_config),
+				GFP_KERNEL);
+		if (!cd->ic_configs[cfg_type])
+			goto err_out;
+		cd->ic_configs[cfg_type]->len = cfg_len;
+		memcpy(cd->ic_configs[cfg_type]->data, cfg_pkg->cfg, cfg_len);
+		ts_info("get config type %d, len %d, for sensor id %d",
+			cfg_type, cfg_len, sensor_id);
+	}
+	return 0;
+
+err_out:
+	/* parse config enter error, release memory alloced */
+	for (i = 0; i < GOODIX_MAX_CONFIG_GROUP; i++) {
+		kfree(cd->ic_configs[i]);
+		cd->ic_configs[i] = NULL;
+	}
+	return -EINVAL;
+}
+
+static int goodix_get_config_data(struct goodix_ts_core *cd, u8 sensor_id)
+{
+	struct goodix_cfg_bin cfg_bin = {0};
+	char *cfg_name = cd->board_data.cfg_bin_name;
+	int ret;
+
+	/*get cfg_bin from file system*/
+	ret = goodix_read_cfg_bin(&cd->pdev->dev, cfg_name, &cfg_bin);
+	if (ret) {
+		ts_err("failed get valid config bin data");
+		return ret;
+	}
+
+	/*parse cfg bin*/
+	ret = goodix_parse_cfg_bin(&cfg_bin);
+	if (ret) {
+		ts_err("failed parse cfg bin");
+		goto err_out;
+	}
+
+	/*get register address and configuration from cfg bin*/
+	ret = goodix_get_reg_and_cfg(cd, sensor_id, &cfg_bin);
+	if (!ret)
+		ts_info("success get reg and cfg info from cfg bin");
+	else
+		ts_err("failed get cfg and reg info, update fw then retry");
+
+	kfree(cfg_bin.cfg_pkgs);
+err_out:
+	kfree(cfg_bin.bin_data);
+	return ret;
+}
+
+int goodix_get_config_proc(struct goodix_ts_core *cd)
+{
+	return goodix_get_config_data(cd, cd->fw_version.sensor_id);
+}
+
+

+ 2710 - 0
qcom/opensource/touch-drivers/goodix_berlin_driver/goodix_ts_core.c

@@ -0,0 +1,2710 @@
+ /*
+  * Goodix Touchscreen Driver
+  * Copyright (C) 2020 - 2021 Goodix, Inc.
+  * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
+  *
+  * This program is free software; you can redistribute it and/or modify
+  * it under the terms of the GNU General Public License as published by
+  * the Free Software Foundation; either version 2 of the License, or
+  * (at your option) any later version.
+  *
+  * This program is distributed in the hope that it will be a reference
+  * to you, when you are integrating the GOODiX's CTP IC into your system,
+  * but WITHOUT ANY WARRANTY; without even the implied warranty of
+  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  * General Public License for more details.
+  *
+  */
+#include <linux/version.h>
+#include <linux/fs.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/uaccess.h>
+#include <linux/soc/qcom/panel_event_notifier.h>
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38)
+#include <linux/input/mt.h>
+#define INPUT_TYPE_B_PROTOCOL
+#endif
+
+#include "goodix_ts_core.h"
+
+#define GOODIX_DEFAULT_CFG_NAME		"goodix_cfg_group.cfg"
+#define GOOIDX_INPUT_PHYS			"goodix_ts/input0"
+
+#if defined(CONFIG_DRM)
+static struct drm_panel *active_panel;
+static void goodix_panel_notifier_callback(enum panel_event_notifier_tag tag,
+		 struct panel_event_notification *event, void *client_data);
+static irqreturn_t  goodix_irq_handler(int irq, void *data);
+
+static void goodix_register_for_panel_events(struct device_node *dp,
+					struct goodix_ts_core *cd)
+{
+	void *cookie;
+
+	cookie = panel_event_notifier_register(PANEL_EVENT_NOTIFICATION_PRIMARY,
+			PANEL_EVENT_NOTIFIER_CLIENT_PRIMARY_TOUCH, active_panel,
+			&goodix_panel_notifier_callback, cd);
+	if (!cookie) {
+		pr_err("Failed to register for panel events\n");
+		return;
+	}
+
+	ts_debug("registered for panel notifications panel: 0x%x\n",
+			active_panel);
+
+	cd->notifier_cookie = cookie;
+}
+
+#endif
+
+struct goodix_module goodix_modules;
+int core_module_prob_sate = CORE_MODULE_UNPROBED;
+struct mutex goodix_later_init_tmutex;
+
+int goodix_ts_esd_init(struct goodix_ts_core *cd);
+
+static int goodix_send_ic_config(struct goodix_ts_core *cd, int type);
+/**
+ * __do_register_ext_module - register external module
+ * to register into touch core modules structure
+ * return 0 on success, otherwise return < 0
+ */
+static int __do_register_ext_module(struct goodix_ext_module *module)
+{
+	struct goodix_ext_module *ext_module, *next;
+	struct list_head *insert_point = &goodix_modules.head;
+
+	/* prority level *must* be set */
+	if (module->priority == EXTMOD_PRIO_RESERVED) {
+		ts_err("Priority of module [%s] needs to be set",
+			module->name);
+		return -EINVAL;
+	}
+	mutex_lock(&goodix_modules.mutex);
+	/* find insert point for the specified priority */
+	if (!list_empty(&goodix_modules.head)) {
+		list_for_each_entry_safe(ext_module, next,
+					 &goodix_modules.head, list) {
+			if (ext_module == module) {
+				ts_info("Module [%s] already exists",
+					module->name);
+				mutex_unlock(&goodix_modules.mutex);
+				return 0;
+			}
+		}
+
+		/* smaller priority value with higher priority level */
+		list_for_each_entry_safe(ext_module, next,
+					 &goodix_modules.head, list) {
+			if (ext_module->priority >= module->priority) {
+				insert_point = &ext_module->list;
+				break;
+			}
+		}
+	}
+
+	if (module->funcs && module->funcs->init) {
+		if (module->funcs->init(goodix_modules.core_data,
+					module) < 0) {
+			ts_err("Module [%s] init error",
+				module->name ? module->name : " ");
+			mutex_unlock(&goodix_modules.mutex);
+			return -EFAULT;
+		}
+	}
+
+	list_add(&module->list, insert_point->prev);
+	mutex_unlock(&goodix_modules.mutex);
+
+	ts_info("Module [%s] registered,priority:%u", module->name,
+		module->priority);
+	return 0;
+}
+
+static void goodix_register_ext_module_work(struct work_struct *work)
+{
+	struct goodix_ext_module *module =
+			container_of(work, struct goodix_ext_module, work);
+
+	ts_info("module register work IN");
+
+	/* driver probe failed */
+	if (core_module_prob_sate != CORE_MODULE_PROB_SUCCESS) {
+		ts_err("Can't register ext_module core error");
+		return;
+	}
+
+	if (__do_register_ext_module(module))
+		ts_err("failed register module: %s", module->name);
+	else
+		ts_info("success register module: %s", module->name);
+}
+
+static void goodix_core_module_init(void)
+{
+	if (goodix_modules.initilized)
+		return;
+	goodix_modules.initilized = true;
+	INIT_LIST_HEAD(&goodix_modules.head);
+	mutex_init(&goodix_modules.mutex);
+}
+
+/**
+ * goodix_register_ext_module - interface for register external module
+ * to the core. This will create a workqueue to finish the real register
+ * work and return immediately. The user need to check the final result
+ * to make sure registe is success or fail.
+ *
+ * @module: pointer to external module to be register
+ * return: 0 ok, <0 failed
+ */
+int goodix_register_ext_module(struct goodix_ext_module *module)
+{
+	if (!module)
+		return -EINVAL;
+
+	ts_info("IN");
+
+	goodix_core_module_init();
+	INIT_WORK(&module->work, goodix_register_ext_module_work);
+	schedule_work(&module->work);
+
+	ts_info("OUT");
+	return 0;
+}
+
+/**
+ * goodix_register_ext_module_no_wait
+ * return: 0 ok, <0 failed
+ */
+int goodix_register_ext_module_no_wait(struct goodix_ext_module *module)
+{
+	if (!module)
+		return -EINVAL;
+
+	ts_info("IN");
+	goodix_core_module_init();
+	/* driver probe failed */
+	if (core_module_prob_sate != CORE_MODULE_PROB_SUCCESS) {
+		ts_err("Can't register ext_module core error");
+		return -EINVAL;
+	}
+	return __do_register_ext_module(module);
+}
+
+/**
+ * goodix_unregister_ext_module - interface for external module
+ * to unregister external modules
+ *
+ * @module: pointer to external module
+ * return: 0 ok, <0 failed
+ */
+int goodix_unregister_ext_module(struct goodix_ext_module *module)
+{
+	struct goodix_ext_module *ext_module, *next;
+	bool found = false;
+
+	if (!module)
+		return -EINVAL;
+
+	if (!goodix_modules.initilized)
+		return -EINVAL;
+
+	if (!goodix_modules.core_data)
+		return -ENODEV;
+
+	mutex_lock(&goodix_modules.mutex);
+	if (!list_empty(&goodix_modules.head)) {
+		list_for_each_entry_safe(ext_module, next,
+					 &goodix_modules.head, list) {
+			if (ext_module == module) {
+				found = true;
+				break;
+			}
+		}
+	} else {
+		mutex_unlock(&goodix_modules.mutex);
+		return 0;
+	}
+
+	if (!found) {
+		ts_debug("Module [%s] never registed",
+				module->name);
+		mutex_unlock(&goodix_modules.mutex);
+		return 0;
+	}
+
+	list_del(&module->list);
+	mutex_unlock(&goodix_modules.mutex);
+
+	if (module->funcs && module->funcs->exit)
+		module->funcs->exit(goodix_modules.core_data, module);
+
+	ts_info("Moudle [%s] unregistered",
+		module->name ? module->name : " ");
+	return 0;
+}
+
+static void goodix_ext_sysfs_release(struct kobject *kobj)
+{
+	ts_info("Kobject released!");
+}
+
+#define to_ext_module(kobj)	container_of(kobj,\
+				struct goodix_ext_module, kobj)
+#define to_ext_attr(attr)	container_of(attr,\
+				struct goodix_ext_attribute, attr)
+
+static ssize_t goodix_ext_sysfs_show(struct kobject *kobj,
+		struct attribute *attr, char *buf)
+{
+	struct goodix_ext_module *module = to_ext_module(kobj);
+	struct goodix_ext_attribute *ext_attr = to_ext_attr(attr);
+
+	if (ext_attr->show)
+		return ext_attr->show(module, buf);
+
+	return -EIO;
+}
+
+static ssize_t goodix_ext_sysfs_store(struct kobject *kobj,
+		struct attribute *attr, const char *buf, size_t count)
+{
+	struct goodix_ext_module *module = to_ext_module(kobj);
+	struct goodix_ext_attribute *ext_attr = to_ext_attr(attr);
+
+	if (ext_attr->store)
+		return ext_attr->store(module, buf, count);
+
+	return -EIO;
+}
+
+static const struct sysfs_ops goodix_ext_ops = {
+	.show = goodix_ext_sysfs_show,
+	.store = goodix_ext_sysfs_store
+};
+
+static struct kobj_type goodix_ext_ktype = {
+	.release = goodix_ext_sysfs_release,
+	.sysfs_ops = &goodix_ext_ops,
+};
+
+struct kobj_type *goodix_get_default_ktype(void)
+{
+	return &goodix_ext_ktype;
+}
+
+struct kobject *goodix_get_default_kobj(void)
+{
+	struct kobject *kobj = NULL;
+
+	if (goodix_modules.core_data &&
+			goodix_modules.core_data->pdev)
+		kobj = &goodix_modules.core_data->pdev->dev.kobj;
+	return kobj;
+}
+
+/* show driver infomation */
+static ssize_t driver_info_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "DriverVersion:%s\n",
+			GOODIX_DRIVER_VERSION);
+}
+
+/* show chip infoamtion */
+static ssize_t chip_info_show(struct device  *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct goodix_ts_core *cd = dev_get_drvdata(dev);
+	struct goodix_ts_hw_ops *hw_ops = cd->hw_ops;
+	struct goodix_fw_version chip_ver;
+	struct goodix_ic_info ic_info;
+	u8 temp_pid[8] = {0};
+	int ret;
+	int cnt = -EINVAL;
+
+	if (hw_ops->read_version) {
+		ret = hw_ops->read_version(cd, &chip_ver);
+		if (!ret) {
+			memcpy(temp_pid, chip_ver.rom_pid,
+					sizeof(chip_ver.rom_pid));
+			cnt = snprintf(&buf[0], PAGE_SIZE,
+				"rom_pid:%s\nrom_vid:%02x%02x%02x\n",
+				temp_pid, chip_ver.rom_vid[0],
+				chip_ver.rom_vid[1], chip_ver.rom_vid[2]);
+			cnt += snprintf(&buf[cnt], PAGE_SIZE,
+				"patch_pid:%s\npatch_vid:%02x%02x%02x%02x\n",
+				chip_ver.patch_pid, chip_ver.patch_vid[0],
+				chip_ver.patch_vid[1], chip_ver.patch_vid[2],
+				chip_ver.patch_vid[3]);
+			cnt += snprintf(&buf[cnt], PAGE_SIZE,
+				"sensorid:%d\n", chip_ver.sensor_id);
+		}
+	}
+
+	if (hw_ops->get_ic_info) {
+		ret = hw_ops->get_ic_info(cd, &ic_info);
+		if (!ret) {
+			cnt += snprintf(&buf[cnt], PAGE_SIZE,
+					"config_id:%x\n",
+					ic_info.version.config_id);
+			cnt += snprintf(&buf[cnt], PAGE_SIZE,
+					"config_version:%x\n",
+					ic_info.version.config_version);
+		}
+	}
+
+	return cnt;
+}
+
+/* reset chip */
+static ssize_t goodix_ts_reset_store(struct device *dev,
+				     struct device_attribute *attr,
+				     const char *buf,
+				     size_t count)
+{
+	struct goodix_ts_core *core_data = dev_get_drvdata(dev);
+	struct goodix_ts_hw_ops *hw_ops = core_data->hw_ops;
+
+	if (!buf || count <= 0)
+		return -EINVAL;
+	if (buf[0] != '0')
+		hw_ops->reset(core_data, GOODIX_NORMAL_RESET_DELAY_MS);
+	return count;
+}
+
+/* read config */
+static ssize_t read_cfg_show(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	struct goodix_ts_core *core_data = dev_get_drvdata(dev);
+	struct goodix_ts_hw_ops *hw_ops = core_data->hw_ops;
+	int ret;
+	int i;
+	int offset;
+	char *cfg_buf = NULL;
+
+	cfg_buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
+	if (!cfg_buf)
+		return -ENOMEM;
+
+	if (hw_ops->read_config)
+		ret = hw_ops->read_config(core_data, cfg_buf, PAGE_SIZE);
+	else
+		ret = -EINVAL;
+
+	if (ret > 0) {
+		offset = 0;
+		for (i = 0; i < 200; i++) { // only print 200 bytes
+			offset += snprintf(&buf[offset], PAGE_SIZE - offset,
+					"%02x,", cfg_buf[i]);
+			if ((i + 1) % 20 == 0)
+				buf[offset++] = '\n';
+		}
+	}
+
+	kfree(cfg_buf);
+	if (ret <= 0)
+		return ret;
+
+	return offset;
+}
+
+static u8 ascii2hex(u8 a)
+{
+	s8 value = 0;
+
+	if (a >= '0' && a <= '9')
+		value = a - '0';
+	else if (a >= 'A' && a <= 'F')
+		value = a - 'A' + 0x0A;
+	else if (a >= 'a' && a <= 'f')
+		value = a - 'a' + 0x0A;
+	else
+		value = 0xff;
+
+	return value;
+}
+
+static int goodix_ts_convert_0x_data(const u8 *buf, int buf_size,
+				     u8 *out_buf, int *out_buf_len)
+{
+	int i, m_size = 0;
+	int temp_index = 0;
+	u8 high, low;
+
+	for (i = 0; i < buf_size; i++) {
+		if (buf[i] == 'x' || buf[i] == 'X')
+			m_size++;
+	}
+
+	if (m_size <= 1) {
+		ts_err("cfg file ERROR, valid data count:%d", m_size);
+		return -EINVAL;
+	}
+	*out_buf_len = m_size;
+
+	for (i = 0; i < buf_size; i++) {
+		if (buf[i] != 'x' && buf[i] != 'X')
+			continue;
+
+		if (temp_index >= m_size) {
+			ts_err("exchange cfg data error, overflow, temp_index:%d,m_size:%d",
+					temp_index, m_size);
+			return -EINVAL;
+		}
+		high = ascii2hex(buf[i + 1]);
+		low = ascii2hex(buf[i + 2]);
+		if (high == 0xff || low == 0xff) {
+			ts_err("failed convert: 0x%x, 0x%x",
+				buf[i + 1], buf[i + 2]);
+			return -EINVAL;
+		}
+		out_buf[temp_index++] = (high << 4) + low;
+	}
+	return 0;
+}
+
+/* send config */
+static ssize_t goodix_ts_send_cfg_store(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf, size_t count)
+{
+	struct goodix_ts_core *core_data = dev_get_drvdata(dev);
+	struct goodix_ts_hw_ops *hw_ops = core_data->hw_ops;
+	struct goodix_ic_config *config = NULL;
+	const struct firmware *cfg_img = NULL;
+	int ret;
+
+	if (buf[0] != '1')
+		return -EINVAL;
+
+	hw_ops->irq_enable(core_data, false);
+
+	ret = request_firmware(&cfg_img, GOODIX_DEFAULT_CFG_NAME, dev);
+	if (ret < 0) {
+		ts_err("cfg file [%s] not available,errno:%d",
+			GOODIX_DEFAULT_CFG_NAME, ret);
+		goto exit;
+	} else
+		ts_info("cfg file [%s] is ready", GOODIX_DEFAULT_CFG_NAME);
+
+	config = kzalloc(sizeof(*config), GFP_KERNEL);
+	if (!config)
+		goto exit;
+
+	if (goodix_ts_convert_0x_data(cfg_img->data, cfg_img->size,
+			config->data, &config->len)) {
+		ts_err("convert config data FAILED");
+		goto exit;
+	}
+
+	if (hw_ops->send_config) {
+		ret = hw_ops->send_config(core_data, config->data, config->len);
+		if (ret < 0)
+			ts_err("send config failed");
+	}
+
+exit:
+	hw_ops->irq_enable(core_data, true);
+	kfree(config);
+	if (cfg_img)
+		release_firmware(cfg_img);
+
+	return count;
+}
+
+/* reg read/write */
+static u32 rw_addr;
+static u32 rw_len;
+static u8 rw_flag;
+static u8 store_buf[32];
+static u8 show_buf[PAGE_SIZE];
+static ssize_t goodix_ts_reg_rw_show(struct device *dev,
+			struct device_attribute *attr, char *buf)
+{
+	struct goodix_ts_core *core_data = dev_get_drvdata(dev);
+	struct goodix_ts_hw_ops *hw_ops = core_data->hw_ops;
+	int ret;
+
+	if (!rw_addr || !rw_len) {
+		ts_err("address(0x%x) and length(%d) can't be null",
+			rw_addr, rw_len);
+		return -EINVAL;
+	}
+
+	if (rw_flag != 1) {
+		ts_err("invalid rw flag %d, only support [1/2]", rw_flag);
+		return -EINVAL;
+	}
+
+	ret = hw_ops->read(core_data, rw_addr, show_buf, rw_len);
+	if (ret < 0) {
+		ts_err("failed read addr(%x) length(%d)", rw_addr, rw_len);
+		return snprintf(buf, PAGE_SIZE,
+			"failed read addr(%x), len(%d)\n",
+			rw_addr, rw_len);
+	}
+
+	return snprintf(buf, PAGE_SIZE, "0x%x,%d {%*ph}\n",
+		rw_addr, rw_len, rw_len, show_buf);
+}
+
+static ssize_t goodix_ts_reg_rw_store(struct device *dev,
+				      struct device_attribute *attr,
+				      const char *buf, size_t count)
+{
+	struct goodix_ts_core *core_data = dev_get_drvdata(dev);
+	struct goodix_ts_hw_ops *hw_ops = core_data->hw_ops;
+	char *pos = NULL;
+	char *token = NULL;
+	long result = 0;
+	int ret;
+	int i;
+
+	if (!buf || !count) {
+		ts_err("invalid parame");
+		goto err_out;
+	}
+
+	if (buf[0] == 'r')
+		rw_flag = 1;
+	else if (buf[0] == 'w')
+		rw_flag = 2;
+	else {
+		ts_err("string must start with 'r/w'");
+		goto err_out;
+	}
+
+	/* get addr */
+	pos = (char *)buf;
+	pos += 2;
+	token = strsep(&pos, ":");
+	if (!token) {
+		ts_err("invalid address info");
+		goto err_out;
+	} else {
+		if (kstrtol(token, 16, &result)) {
+			ts_err("failed get addr info");
+			goto err_out;
+		}
+		rw_addr = (u32)result;
+		ts_info("rw addr is 0x%x", rw_addr);
+	}
+
+	/* get length */
+	token = strsep(&pos, ":");
+	if (!token) {
+		ts_err("invalid length info");
+		goto err_out;
+	} else {
+		if (kstrtol(token, 0, &result)) {
+			ts_err("failed get length info");
+			goto err_out;
+		}
+		rw_len = (u32)result;
+		ts_info("rw length info is %d", rw_len);
+		if (rw_len > sizeof(store_buf)) {
+			ts_err("data len > %lu", sizeof(store_buf));
+			goto err_out;
+		}
+	}
+
+	if (rw_flag == 1)
+		return count;
+
+	for (i = 0; i < rw_len; i++) {
+		token = strsep(&pos, ":");
+		if (!token) {
+			ts_err("invalid data info");
+			goto err_out;
+		} else {
+			if (kstrtol(token, 16, &result)) {
+				ts_err("failed get data[%d] info", i);
+				goto err_out;
+			}
+			store_buf[i] = (u8)result;
+			ts_info("get data[%d]=0x%x", i, store_buf[i]);
+		}
+	}
+	ret = hw_ops->write(core_data, rw_addr, store_buf, rw_len);
+	if (ret < 0) {
+		ts_err("failed write addr(%x) data %*ph", rw_addr,
+			rw_len, store_buf);
+		goto err_out;
+	}
+
+	ts_info("%s write to addr (%x) with data %*ph",
+		"success", rw_addr, rw_len, store_buf);
+
+	return count;
+err_out:
+	snprintf(show_buf, PAGE_SIZE, "%s\n",
+		"invalid params, format{r/w:4100:length:[41:21:31]}");
+	return -EINVAL;
+
+}
+
+/* show irq infomation */
+static ssize_t goodix_ts_irq_info_show(struct device *dev,
+				       struct device_attribute *attr,
+				       char *buf)
+{
+	struct goodix_ts_core *core_data = dev_get_drvdata(dev);
+	struct irq_desc *desc;
+	size_t offset = 0;
+	int r;
+
+	r = snprintf(&buf[offset], PAGE_SIZE, "irq:%u\n", core_data->irq);
+	if (r < 0)
+		return -EINVAL;
+
+	offset += r;
+	r = snprintf(&buf[offset], PAGE_SIZE - offset, "state:%s\n",
+			atomic_read(&core_data->irq_enabled) ? "enabled" : "disabled");
+	if (r < 0)
+		return -EINVAL;
+
+	desc = irq_to_desc(core_data->irq);
+	offset += r;
+	r = snprintf(&buf[offset], PAGE_SIZE - offset, "disable-depth:%d\n", desc->depth);
+	if (r < 0)
+		return -EINVAL;
+
+	offset += r;
+	r = snprintf(&buf[offset], PAGE_SIZE - offset, "trigger-count:%zu\n",
+		core_data->irq_trig_cnt);
+	if (r < 0)
+		return -EINVAL;
+
+	offset += r;
+	r = snprintf(&buf[offset], PAGE_SIZE - offset,
+		     "echo 0/1 > irq_info to disable/enable irq\n");
+	if (r < 0)
+		return -EINVAL;
+
+	offset += r;
+	return offset;
+}
+
+/* enable/disable irq */
+static ssize_t goodix_ts_irq_info_store(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf, size_t count)
+{
+	struct goodix_ts_core *core_data = dev_get_drvdata(dev);
+	struct goodix_ts_hw_ops *hw_ops = core_data->hw_ops;
+
+	if (!buf || count <= 0)
+		return -EINVAL;
+
+	if (buf[0] != '0')
+		hw_ops->irq_enable(core_data, true);
+	else
+		hw_ops->irq_enable(core_data, false);
+	return count;
+}
+
+/* show esd status */
+static ssize_t goodix_ts_esd_info_show(struct device *dev,
+				       struct device_attribute *attr,
+				       char *buf)
+{
+	struct goodix_ts_core *core_data = dev_get_drvdata(dev);
+	struct goodix_ts_esd *ts_esd = &core_data->ts_esd;
+	int r = 0;
+
+	r = snprintf(buf, PAGE_SIZE, "state:%s\n",
+			atomic_read(&ts_esd->esd_on) ? "enabled" : "disabled");
+
+	return r;
+}
+
+/* enable/disable esd */
+static ssize_t goodix_ts_esd_info_store(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf, size_t count)
+{
+	struct goodix_ts_core *core_data = dev_get_drvdata(dev);
+
+	if (!buf || count <= 0)
+		return -EINVAL;
+
+	if (buf[0] != '0') {
+		if (!core_data->esd_initialized)
+			goodix_ts_esd_init(core_data);
+		goodix_ts_blocking_notify(NOTIFY_ESD_ON, NULL);
+	} else if (core_data->esd_initialized) {
+		goodix_ts_blocking_notify(NOTIFY_ESD_OFF, NULL);
+	}
+
+	return count;
+}
+
+/* debug level show */
+static ssize_t goodix_ts_debug_log_show(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	int r = 0;
+
+	r = snprintf(buf, PAGE_SIZE, "state:%s\n",
+			debug_log_flag ? "enabled" : "disabled");
+
+	return r;
+}
+
+/* debug level store */
+static ssize_t goodix_ts_debug_log_store(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf, size_t count)
+{
+	if (!buf || count <= 0)
+		return -EINVAL;
+
+	if (buf[0] != '0')
+		debug_log_flag = true;
+	else
+		debug_log_flag = false;
+	return count;
+}
+
+static DEVICE_ATTR(driver_info, 0440,
+		driver_info_show, NULL);
+static DEVICE_ATTR(chip_info, 0440,
+		chip_info_show, NULL);
+static DEVICE_ATTR(reset, 0220,
+		NULL, goodix_ts_reset_store);
+static DEVICE_ATTR(send_cfg, 0220,
+		NULL, goodix_ts_send_cfg_store);
+static DEVICE_ATTR(read_cfg, 0440,
+		read_cfg_show, NULL);
+static DEVICE_ATTR(reg_rw, 0664,
+		goodix_ts_reg_rw_show, goodix_ts_reg_rw_store);
+static DEVICE_ATTR(irq_info, 0664,
+		goodix_ts_irq_info_show, goodix_ts_irq_info_store);
+static DEVICE_ATTR(esd_info, 0664,
+		goodix_ts_esd_info_show, goodix_ts_esd_info_store);
+static DEVICE_ATTR(debug_log, 0664,
+		goodix_ts_debug_log_show, goodix_ts_debug_log_store);
+
+static struct attribute *sysfs_attrs[] = {
+	&dev_attr_driver_info.attr,
+	&dev_attr_chip_info.attr,
+	&dev_attr_reset.attr,
+	&dev_attr_send_cfg.attr,
+	&dev_attr_read_cfg.attr,
+	&dev_attr_reg_rw.attr,
+	&dev_attr_irq_info.attr,
+	&dev_attr_esd_info.attr,
+	&dev_attr_debug_log.attr,
+	NULL,
+};
+
+static const struct attribute_group sysfs_group = {
+	.attrs = sysfs_attrs,
+};
+
+static int goodix_ts_sysfs_init(struct goodix_ts_core *core_data)
+{
+	int ret;
+
+	ret = sysfs_create_group(&core_data->pdev->dev.kobj, &sysfs_group);
+	if (ret) {
+		ts_err("failed create core sysfs group");
+		return ret;
+	}
+
+	return ret;
+}
+
+static void goodix_ts_sysfs_exit(struct goodix_ts_core *core_data)
+{
+	sysfs_remove_group(&core_data->pdev->dev.kobj, &sysfs_group);
+}
+
+/* prosfs create */
+static int rawdata_proc_show(struct seq_file *m, void *v)
+{
+	struct ts_rawdata_info *info;
+	struct goodix_ts_core *cd = m->private;
+	int tx;
+	int rx;
+	int ret;
+	int i;
+	int index;
+
+	if (!m || !v || !cd)
+		return -EIO;
+
+	info = kzalloc(sizeof(*info), GFP_KERNEL);
+	if (!info)
+		return -ENOMEM;
+
+	ret = cd->hw_ops->get_capacitance_data(cd, info);
+	if (ret < 0) {
+		ts_err("failed to get_capacitance_data, exit!");
+		goto exit;
+	}
+
+	rx = info->buff[0];
+	tx = info->buff[1];
+	seq_printf(m, "TX:%d  RX:%d\n", tx, rx);
+	seq_puts(m, "mutual_rawdata:\n");
+	index = 2;
+	for (i = 0; i < tx * rx; i++) {
+		seq_printf(m, "%5d,", info->buff[index + i]);
+		if ((i + 1) % tx == 0)
+			seq_puts(m, "\n");
+	}
+	seq_puts(m, "mutual_diffdata:\n");
+	index += tx * rx;
+	for (i = 0; i < tx * rx; i++) {
+		seq_printf(m, "%3d,", info->buff[index + i]);
+		if ((i + 1) % tx == 0)
+			seq_puts(m, "\n");
+	}
+
+exit:
+	kfree(info);
+	return ret;
+}
+
+static int rawdata_proc_open(struct inode *inode, struct file *file)
+{
+	#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 19, 0))
+		return single_open_size(file, rawdata_proc_show,
+			pde_data(inode), PAGE_SIZE * 10);
+	#else
+		return single_open_size(file, rawdata_proc_show,
+			PDE_DATA(inode), PAGE_SIZE * 10);
+	#endif
+}
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0))
+static const struct proc_ops rawdata_proc_fops = {
+	.proc_open = rawdata_proc_open,
+	.proc_read = seq_read,
+	.proc_lseek = seq_lseek,
+	.proc_release = single_release,
+};
+#else
+static const struct file_operations rawdata_proc_fops = {
+	.open = rawdata_proc_open,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = single_release,
+};
+#endif
+
+static void goodix_ts_procfs_init(struct goodix_ts_core *core_data)
+{
+	struct proc_dir_entry *proc_entry;
+
+	if (!proc_mkdir("goodix_ts", NULL))
+		return;
+	proc_entry = proc_create_data("goodix_ts/tp_capacitance_data",
+			0664, NULL, &rawdata_proc_fops, core_data);
+	if (!proc_entry)
+		ts_err("failed to create proc entry");
+}
+
+static void goodix_ts_procfs_exit(struct goodix_ts_core *core_data)
+{
+	remove_proc_entry("goodix_ts/tp_capacitance_data", NULL);
+	remove_proc_entry("goodix_ts", NULL);
+}
+
+/* event notifier */
+static BLOCKING_NOTIFIER_HEAD(ts_notifier_list);
+/**
+ * goodix_ts_register_client - register a client notifier
+ * @nb: notifier block to callback on events
+ *  see enum ts_notify_event in goodix_ts_core.h
+ */
+int goodix_ts_register_notifier(struct notifier_block *nb)
+{
+	return blocking_notifier_chain_register(&ts_notifier_list, nb);
+}
+
+/**
+ * goodix_ts_unregister_client - unregister a client notifier
+ * @nb: notifier block to callback on events
+ *	see enum ts_notify_event in goodix_ts_core.h
+ */
+int goodix_ts_unregister_notifier(struct notifier_block *nb)
+{
+	return blocking_notifier_chain_unregister(&ts_notifier_list, nb);
+}
+
+/**
+ * fb_notifier_call_chain - notify clients of fb_events
+ *	see enum ts_notify_event in goodix_ts_core.h
+ */
+int goodix_ts_blocking_notify(enum ts_notify_event evt, void *v)
+{
+	int ret;
+
+	ret = blocking_notifier_call_chain(&ts_notifier_list,
+			(unsigned long)evt, v);
+	return ret;
+}
+
+#if IS_ENABLED(CONFIG_OF)
+/**
+ * goodix_parse_dt_resolution - parse resolution from dt
+ * @node: devicetree node
+ * @board_data: pointer to board data structure
+ * return: 0 - no error, <0 error
+ */
+static int goodix_parse_dt_resolution(struct device_node *node,
+		struct goodix_ts_board_data *board_data)
+{
+	int ret;
+
+	ret = of_property_read_u32(node, "goodix,panel-max-x",
+				&board_data->panel_max_x);
+	if (ret) {
+		ts_err("failed get panel-max-x");
+		return ret;
+	}
+
+	ret = of_property_read_u32(node, "goodix,panel-max-y",
+				&board_data->panel_max_y);
+	if (ret) {
+		ts_err("failed get panel-max-y");
+		return ret;
+	}
+
+	board_data->invert_xy = of_property_read_bool(node,
+					"invert_xy");
+	if (board_data->invert_xy) {
+		swap(board_data->panel_max_x, board_data->panel_max_y);
+		ts_info("Panel max x and max y inverted\n");
+	}
+
+	ret = of_property_read_u32(node, "goodix,panel-max-w",
+				&board_data->panel_max_w);
+	if (ret) {
+		ts_err("failed get panel-max-w");
+		return ret;
+	}
+
+	ret = of_property_read_u32(node, "goodix,panel-max-p",
+				&board_data->panel_max_p);
+	if (ret) {
+		ts_err("failed get panel-max-p, use default");
+		board_data->panel_max_p = GOODIX_PEN_MAX_PRESSURE;
+	}
+
+	return 0;
+}
+
+/**
+ * goodix_parse_dt - parse board data from dt
+ * @dev: pointer to device
+ * @board_data: pointer to board data structure
+ * return: 0 - no error, <0 error
+ */
+static int goodix_parse_dt(struct device_node *node,
+	struct goodix_ts_board_data *board_data)
+{
+	const char *name_tmp;
+	int r;
+
+	if (!board_data) {
+		ts_err("invalid board data");
+		return -EINVAL;
+	}
+
+	r = of_get_named_gpio(node, "goodix,avdd-gpio", 0);
+	if (r < 0) {
+		ts_info("can't find avdd-gpio, use other power supply");
+		board_data->avdd_gpio = 0;
+	} else {
+		ts_info("get avdd-gpio[%d] from dt", r);
+		board_data->avdd_gpio = r;
+	}
+
+	r = of_get_named_gpio(node, "goodix,iovdd-gpio", 0);
+	if (r < 0) {
+		ts_info("can't find iovdd-gpio, use other power supply");
+		board_data->iovdd_gpio = 0;
+	} else {
+		ts_info("get iovdd-gpio[%d] from dt", r);
+		board_data->iovdd_gpio = r;
+	}
+
+#ifndef CONFIG_ARCH_QTI_VM
+	r = of_get_named_gpio(node, "goodix,reset-gpio", 0);
+	if (r < 0) {
+		ts_err("invalid reset-gpio in dt: %d", r);
+		return -EINVAL;
+	}
+	ts_info("get reset-gpio[%d] from dt", r);
+	board_data->reset_gpio = r;
+
+	r = of_get_named_gpio(node, "goodix,irq-gpio", 0);
+	if (r < 0) {
+		ts_err("invalid irq-gpio in dt: %d", r);
+		return -EINVAL;
+	}
+	ts_info("get irq-gpio[%d] from dt", r);
+	board_data->irq_gpio = r;
+
+	r = of_property_read_u32(node, "goodix,irq-flags",
+			&board_data->irq_flags);
+	if (r) {
+		ts_err("invalid irq-flags");
+		return -EINVAL;
+	}
+#endif
+
+	memset(board_data->avdd_name, 0, sizeof(board_data->avdd_name));
+	r = of_property_read_string(node, "goodix,avdd-name", &name_tmp);
+	if (!r) {
+		ts_info("avdd name from dt: %s", name_tmp);
+		if (strlen(name_tmp) < sizeof(board_data->avdd_name))
+			strlcpy(board_data->avdd_name,
+				name_tmp, sizeof(board_data->avdd_name));
+		else
+			ts_info("invalied avdd name length: %ld > %ld",
+				strlen(name_tmp),
+				sizeof(board_data->avdd_name));
+	}
+
+	memset(board_data->iovdd_name, 0, sizeof(board_data->iovdd_name));
+	r = of_property_read_string(node, "goodix,iovdd-name", &name_tmp);
+	if (!r) {
+		ts_info("iovdd name from dt: %s", name_tmp);
+		if (strlen(name_tmp) < sizeof(board_data->iovdd_name))
+			strlcpy(board_data->iovdd_name,
+				name_tmp, sizeof(board_data->iovdd_name));
+		else
+			ts_info("invalied iovdd name length: %ld > %ld",
+				strlen(name_tmp),
+				sizeof(board_data->iovdd_name));
+	}
+
+	/* get firmware file name */
+	r = of_property_read_string(node, "goodix,firmware-name", &name_tmp);
+	if (!r) {
+		ts_info("firmware name from dt: %s", name_tmp);
+		strlcpy(board_data->fw_name,
+				name_tmp, sizeof(board_data->fw_name));
+	} else {
+		ts_info("can't find firmware name, use default: %s",
+				TS_DEFAULT_FIRMWARE);
+		strlcpy(board_data->fw_name,
+				TS_DEFAULT_FIRMWARE,
+				sizeof(board_data->fw_name));
+	}
+
+	/* get config file name */
+	r = of_property_read_string(node, "goodix,config-name", &name_tmp);
+	if (!r) {
+		ts_info("config name from dt: %s", name_tmp);
+		strlcpy(board_data->cfg_bin_name, name_tmp,
+				sizeof(board_data->cfg_bin_name));
+	} else {
+		ts_info("can't find config name, use default: %s",
+				TS_DEFAULT_CFG_BIN);
+		strlcpy(board_data->cfg_bin_name,
+				TS_DEFAULT_CFG_BIN,
+				sizeof(board_data->cfg_bin_name));
+	}
+
+	/* get xyz resolutions */
+	r = goodix_parse_dt_resolution(node, board_data);
+	if (r) {
+		ts_err("Failed to parse resolutions:%d", r);
+		return r;
+	}
+
+
+	/*get pen-enable switch and pen keys, must after "key map"*/
+	board_data->pen_enable = of_property_read_bool(node,
+					"goodix,pen-enable");
+	if (board_data->pen_enable)
+		ts_info("goodix pen enabled");
+
+	ts_debug("[DT]x:%d, y:%d, w:%d, p:%d", board_data->panel_max_x,
+		 board_data->panel_max_y, board_data->panel_max_w,
+		 board_data->panel_max_p);
+	return 0;
+}
+#endif
+
+static void goodix_ts_report_pen(struct input_dev *dev,
+		struct goodix_pen_data *pen_data)
+{
+	int i;
+
+	mutex_lock(&dev->mutex);
+
+	if (pen_data->coords.status == TS_TOUCH) {
+		input_report_key(dev, BTN_TOUCH, 1);
+		input_report_key(dev, pen_data->coords.tool_type, 1);
+		input_report_abs(dev, ABS_X, pen_data->coords.x);
+		input_report_abs(dev, ABS_Y, pen_data->coords.y);
+		input_report_abs(dev, ABS_PRESSURE, pen_data->coords.p);
+		input_report_abs(dev, ABS_TILT_X, pen_data->coords.tilt_x);
+		input_report_abs(dev, ABS_TILT_Y, pen_data->coords.tilt_y);
+		ts_debug("pen_data:x %d, y %d, p %d, tilt_x %d tilt_y %d key[%d %d]",
+				pen_data->coords.x, pen_data->coords.y,
+				pen_data->coords.p, pen_data->coords.tilt_x,
+				pen_data->coords.tilt_y,
+				pen_data->keys[0].status == TS_TOUCH ? 1 : 0,
+				pen_data->keys[1].status == TS_TOUCH ? 1 : 0);
+	} else {
+		input_report_key(dev, BTN_TOUCH, 0);
+		input_report_key(dev, pen_data->coords.tool_type, 0);
+	}
+	/* report pen button */
+	for (i = 0; i < GOODIX_MAX_PEN_KEY; i++) {
+		if (pen_data->keys[i].status == TS_TOUCH)
+			input_report_key(dev, pen_data->keys[i].code, 1);
+		else
+			input_report_key(dev, pen_data->keys[i].code, 0);
+	}
+
+	input_sync(dev);
+	mutex_unlock(&dev->mutex);
+}
+
+static void goodix_ts_report_finger(struct input_dev *dev,
+		struct goodix_touch_data *touch_data,
+		bool invert_xy)
+{
+	unsigned int touch_num = touch_data->touch_num;
+	int i;
+
+	mutex_lock(&dev->mutex);
+
+	for (i = 0; i < GOODIX_MAX_TOUCH; i++) {
+		if (touch_data->coords[i].status == TS_TOUCH) {
+			ts_debug("report: id[%d], x %d, y %d, w %d", i,
+				touch_data->coords[i].x,
+				touch_data->coords[i].y,
+				touch_data->coords[i].w);
+
+			if (invert_xy)
+				swap(touch_data->coords[i].x,
+						touch_data->coords[i].y);
+
+			input_mt_slot(dev, i);
+			input_mt_report_slot_state(dev, MT_TOOL_FINGER, true);
+			input_report_abs(dev, ABS_MT_POSITION_X,
+					touch_data->coords[i].x);
+			input_report_abs(dev, ABS_MT_POSITION_Y,
+					touch_data->coords[i].y);
+			input_report_abs(dev, ABS_MT_TOUCH_MAJOR,
+					touch_data->coords[i].w);
+		} else {
+			input_mt_slot(dev, i);
+			input_mt_report_slot_state(dev, MT_TOOL_FINGER, false);
+		}
+	}
+
+	input_report_key(dev, BTN_TOUCH, touch_num > 0 ? 1 : 0);
+	input_sync(dev);
+
+	mutex_unlock(&dev->mutex);
+}
+
+static int goodix_ts_request_handle(struct goodix_ts_core *cd,
+	struct goodix_ts_event *ts_event)
+{
+	struct goodix_ts_hw_ops *hw_ops = cd->hw_ops;
+	int ret = -1;
+
+	if (ts_event->request_code == REQUEST_TYPE_CONFIG)
+		ret = goodix_send_ic_config(cd, CONFIG_TYPE_NORMAL);
+	else if (ts_event->request_code == REQUEST_TYPE_RESET)
+		ret = hw_ops->reset(cd, GOODIX_NORMAL_RESET_DELAY_MS);
+	else
+		ts_info("can not handle request type 0x%x",
+			ts_event->request_code);
+	if (ret)
+		ts_err("failed handle request 0x%x",
+			ts_event->request_code);
+	else
+		ts_info("success handle ic request 0x%x",
+			ts_event->request_code);
+	return ret;
+}
+
+/**
+ * goodix_ts_threadirq_func - Bottom half of interrupt
+ * This functions is excuted in thread context,
+ * sleep in this function is permit.
+ *
+ * @data: pointer to touch core data
+ * return: 0 ok, <0 failed
+ */
+static irqreturn_t goodix_ts_threadirq_func(int irq, void *data)
+{
+	struct goodix_ts_core *core_data = data;
+	struct goodix_ts_hw_ops *hw_ops = core_data->hw_ops;
+	struct goodix_ext_module *ext_module, *next;
+	struct goodix_ts_event *ts_event = &core_data->ts_event;
+	struct goodix_ts_esd *ts_esd = &core_data->ts_esd;
+	int ret;
+
+	ts_esd->irq_status = true;
+	core_data->irq_trig_cnt++;
+	/* inform external module */
+	mutex_lock(&goodix_modules.mutex);
+	list_for_each_entry_safe(ext_module, next,
+				 &goodix_modules.head, list) {
+		if (!ext_module->funcs->irq_event)
+			continue;
+		ret = ext_module->funcs->irq_event(core_data, ext_module);
+		if (ret == EVT_CANCEL_IRQEVT) {
+			mutex_unlock(&goodix_modules.mutex);
+			return IRQ_HANDLED;
+		}
+	}
+	mutex_unlock(&goodix_modules.mutex);
+
+	/* read touch data from touch device */
+	ret = hw_ops->event_handler(core_data, ts_event);
+	if (likely(!ret)) {
+		if (ts_event->event_type == EVENT_TOUCH) {
+			/* report touch */
+			goodix_ts_report_finger(core_data->input_dev,
+					&ts_event->touch_data,
+					core_data->board_data.invert_xy);
+		}
+		if (core_data->board_data.pen_enable &&
+				ts_event->event_type == EVENT_PEN) {
+			goodix_ts_report_pen(core_data->pen_dev,
+					&ts_event->pen_data);
+		}
+		if (ts_event->event_type == EVENT_REQUEST)
+			goodix_ts_request_handle(core_data, ts_event);
+	}
+
+	return IRQ_HANDLED;
+}
+
+/**
+ * goodix_ts_init_irq - Requset interrput line from system
+ * @core_data: pointer to touch core data
+ * return: 0 ok, <0 failed
+ */
+static int goodix_ts_irq_setup(struct goodix_ts_core *core_data)
+{
+	const struct goodix_ts_board_data *ts_bdata = board_data(core_data);
+	int ret;
+
+	/* if ts_bdata-> irq is invalid */
+	core_data->irq = gpio_to_irq(ts_bdata->irq_gpio);
+	if (core_data->irq < 0) {
+		ts_err("failed get irq num %d", core_data->irq);
+		return -EINVAL;
+	}
+
+	ts_info("IRQ:%u,flags:%d", core_data->irq, (int)ts_bdata->irq_flags);
+	ret = devm_request_threaded_irq(&core_data->pdev->dev,
+					core_data->irq, NULL,
+					goodix_irq_handler,
+					ts_bdata->irq_flags | IRQF_ONESHOT,
+					GOODIX_CORE_DRIVER_NAME,
+					core_data);
+	if (ret < 0)
+		ts_err("Failed to requeset threaded irq:%d", ret);
+	else
+		atomic_set(&core_data->irq_enabled, 1);
+
+	return ret;
+}
+
+/**
+ * goodix_ts_power_init - Get regulator for touch device
+ * @core_data: pointer to touch core data
+ * return: 0 ok, <0 failed
+ */
+static int goodix_ts_power_init(struct goodix_ts_core *core_data)
+{
+	struct goodix_ts_board_data *ts_bdata = board_data(core_data);
+	struct device *dev = core_data->bus->dev;
+	int ret = 0;
+
+	ts_info("Power init");
+	if (strlen(ts_bdata->avdd_name)) {
+		core_data->avdd = devm_regulator_get(dev, ts_bdata->avdd_name);
+		if (IS_ERR_OR_NULL(core_data->avdd)) {
+			ret = PTR_ERR(core_data->avdd);
+			ts_err("Failed to get regulator avdd:%d", ret);
+			core_data->avdd = NULL;
+			return ret;
+		}
+	} else
+		ts_info("Avdd name is NULL");
+
+	if (strlen(ts_bdata->iovdd_name)) {
+		core_data->iovdd = devm_regulator_get(dev,
+				 ts_bdata->iovdd_name);
+		if (IS_ERR_OR_NULL(core_data->iovdd)) {
+			ret = PTR_ERR(core_data->iovdd);
+			ts_err("Failed to get regulator iovdd:%d", ret);
+			core_data->iovdd = NULL;
+		}
+	} else
+		ts_info("iovdd name is NULL");
+
+	return ret;
+}
+
+/**
+ * goodix_ts_power_on - Turn on power to the touch device
+ * @core_data: pointer to touch core data
+ * return: 0 ok, <0 failed
+ */
+int goodix_ts_power_on(struct goodix_ts_core *cd)
+{
+	int ret = 0;
+
+	ts_info("Device power on");
+	if (cd->power_on)
+		return 0;
+
+	ret = cd->hw_ops->power_on(cd, true);
+	if (!ret)
+		cd->power_on = 1;
+	else
+		ts_err("failed power on, %d", ret);
+	return ret;
+}
+
+/**
+ * goodix_ts_power_off - Turn off power to the touch device
+ * @core_data: pointer to touch core data
+ * return: 0 ok, <0 failed
+ */
+int goodix_ts_power_off(struct goodix_ts_core *cd)
+{
+	int ret;
+
+	ts_info("Device power off");
+	if (!cd->power_on)
+		return 0;
+
+	ret = cd->hw_ops->power_on(cd, false);
+	if (!ret)
+		cd->power_on = 0;
+	else
+		ts_err("failed power off, %d", ret);
+
+	return ret;
+}
+
+/**
+ * goodix_ts_gpio_setup - Request gpio resources from GPIO subsysten
+ * @core_data: pointer to touch core data
+ * return: 0 ok, <0 failed
+ */
+static int goodix_ts_gpio_setup(struct goodix_ts_core *core_data)
+{
+	struct goodix_ts_board_data *ts_bdata = board_data(core_data);
+	int r = 0;
+
+	ts_info("GPIO setup,reset-gpio:%d, irq-gpio:%d",
+		ts_bdata->reset_gpio, ts_bdata->irq_gpio);
+	/*
+	 * after kenerl3.13, gpio_ api is deprecated, new
+	 * driver should use gpiod_ api.
+	 */
+	r = devm_gpio_request_one(&core_data->pdev->dev,
+			ts_bdata->reset_gpio,
+			GPIOF_OUT_INIT_LOW, "ts_reset_gpio");
+	if (r < 0) {
+		ts_err("Failed to request reset gpio, r:%d", r);
+		return r;
+	}
+
+	r = devm_gpio_request_one(&core_data->pdev->dev,
+			ts_bdata->irq_gpio,
+			GPIOF_IN, "ts_irq_gpio");
+	if (r < 0) {
+		ts_err("Failed to request irq gpio, r:%d", r);
+		return r;
+	}
+
+	if (ts_bdata->avdd_gpio > 0) {
+		r = devm_gpio_request_one(&core_data->pdev->dev,
+				ts_bdata->avdd_gpio,
+				GPIOF_OUT_INIT_LOW, "ts_avdd_gpio");
+		if (r < 0) {
+			ts_err("Failed to request avdd-gpio, r:%d", r);
+			return r;
+		}
+	}
+
+	if (ts_bdata->iovdd_gpio > 0) {
+		r = devm_gpio_request_one(&core_data->pdev->dev,
+				ts_bdata->iovdd_gpio,
+				GPIOF_OUT_INIT_LOW, "ts_iovdd_gpio");
+		if (r < 0) {
+			ts_err("Failed to request iovdd-gpio, r:%d", r);
+			return r;
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * goodix_ts_input_dev_config - Requset and config a input device
+ *  then register it to input sybsystem.
+ * @core_data: pointer to touch core data
+ * return: 0 ok, <0 failed
+ */
+static int goodix_ts_input_dev_config(struct goodix_ts_core *core_data)
+{
+	struct goodix_ts_board_data *ts_bdata = board_data(core_data);
+	struct input_dev *input_dev = NULL;
+	int r;
+
+	input_dev = input_allocate_device();
+	if (!input_dev) {
+		ts_err("Failed to allocated input device");
+		return -ENOMEM;
+	}
+
+	core_data->input_dev = input_dev;
+	input_set_drvdata(input_dev, core_data);
+
+	input_dev->name = GOODIX_CORE_DRIVER_NAME;
+	input_dev->phys = GOOIDX_INPUT_PHYS;
+	input_dev->id.product = 0xDEAD;
+	input_dev->id.vendor = 0xBEEF;
+	input_dev->id.version = 10427;
+	input_dev->dev.parent = core_data->bus->dev;
+
+	set_bit(EV_SYN, input_dev->evbit);
+	set_bit(EV_KEY, input_dev->evbit);
+	set_bit(EV_ABS, input_dev->evbit);
+	set_bit(BTN_TOUCH, input_dev->keybit);
+	set_bit(BTN_TOOL_FINGER, input_dev->keybit);
+	set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
+
+	/* set input parameters */
+	input_set_abs_params(input_dev, ABS_MT_POSITION_X,
+			     0, ts_bdata->panel_max_x, 0, 0);
+	input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
+			     0, ts_bdata->panel_max_y, 0, 0);
+	input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
+			     0, ts_bdata->panel_max_w, 0, 0);
+#ifdef INPUT_TYPE_B_PROTOCOL
+#if LINUX_VERSION_CODE > KERNEL_VERSION(3, 7, 0)
+	input_mt_init_slots(input_dev, GOODIX_MAX_TOUCH,
+			    INPUT_MT_DIRECT);
+#else
+	input_mt_init_slots(input_dev, GOODIX_MAX_TOUCH);
+#endif
+#endif
+
+	input_set_capability(input_dev, EV_KEY, KEY_POWER);
+	input_set_capability(input_dev, EV_KEY, KEY_WAKEUP);
+	input_set_capability(input_dev, EV_KEY, KEY_GOTO);
+
+	r = input_register_device(input_dev);
+	if (r < 0) {
+		ts_err("Unable to register input device");
+		input_free_device(input_dev);
+		return r;
+	}
+
+	return 0;
+}
+
+static int goodix_ts_pen_dev_config(struct goodix_ts_core *core_data)
+{
+	struct goodix_ts_board_data *ts_bdata = board_data(core_data);
+	struct input_dev *pen_dev = NULL;
+	int r;
+
+	pen_dev = input_allocate_device();
+	if (!pen_dev) {
+		ts_err("Failed to allocated pen device");
+		return -ENOMEM;
+	}
+
+	core_data->pen_dev = pen_dev;
+	input_set_drvdata(pen_dev, core_data);
+
+	pen_dev->name = GOODIX_PEN_DRIVER_NAME;
+	pen_dev->id.product = 0xDEAD;
+	pen_dev->id.vendor = 0xBEEF;
+	pen_dev->id.version = 10427;
+
+	pen_dev->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+	set_bit(ABS_X, pen_dev->absbit);
+	set_bit(ABS_Y, pen_dev->absbit);
+	set_bit(ABS_TILT_X, pen_dev->absbit);
+	set_bit(ABS_TILT_Y, pen_dev->absbit);
+	set_bit(BTN_STYLUS, pen_dev->keybit);
+	set_bit(BTN_STYLUS2, pen_dev->keybit);
+	set_bit(BTN_TOUCH, pen_dev->keybit);
+	set_bit(BTN_TOOL_PEN, pen_dev->keybit);
+	set_bit(INPUT_PROP_DIRECT, pen_dev->propbit);
+	input_set_abs_params(pen_dev, ABS_X, 0, ts_bdata->panel_max_x, 0, 0);
+	input_set_abs_params(pen_dev, ABS_Y, 0, ts_bdata->panel_max_y, 0, 0);
+	input_set_abs_params(pen_dev, ABS_PRESSURE, 0,
+			ts_bdata->panel_max_p, 0, 0);
+	input_set_abs_params(pen_dev, ABS_TILT_X,
+			-GOODIX_PEN_MAX_TILT, GOODIX_PEN_MAX_TILT, 0, 0);
+	input_set_abs_params(pen_dev, ABS_TILT_Y,
+			-GOODIX_PEN_MAX_TILT, GOODIX_PEN_MAX_TILT, 0, 0);
+
+	r = input_register_device(pen_dev);
+	if (r < 0) {
+		ts_err("Unable to register pen device");
+		input_free_device(pen_dev);
+		return r;
+	}
+
+	return 0;
+}
+
+void goodix_ts_input_dev_remove(struct goodix_ts_core *core_data)
+{
+	if (!core_data->input_dev)
+		return;
+	input_unregister_device(core_data->input_dev);
+	input_free_device(core_data->input_dev);
+	core_data->input_dev = NULL;
+}
+
+void goodix_ts_pen_dev_remove(struct goodix_ts_core *core_data)
+{
+	if (!core_data->pen_dev)
+		return;
+	input_unregister_device(core_data->pen_dev);
+	input_free_device(core_data->pen_dev);
+	core_data->pen_dev = NULL;
+}
+
+/**
+ * goodix_ts_esd_work - check hardware status and recovery
+ *  the hardware if needed.
+ */
+static void goodix_ts_esd_work(struct work_struct *work)
+{
+	struct delayed_work *dwork = to_delayed_work(work);
+	struct goodix_ts_esd *ts_esd = container_of(dwork,
+			struct goodix_ts_esd, esd_work);
+	struct goodix_ts_core *cd = container_of(ts_esd,
+			struct goodix_ts_core, ts_esd);
+	const struct goodix_ts_hw_ops *hw_ops = cd->hw_ops;
+	int ret = 0;
+
+	if (ts_esd->irq_status)
+		goto exit;
+
+	if (!atomic_read(&ts_esd->esd_on))
+		return;
+
+	if (!hw_ops->esd_check)
+		return;
+
+	ret = hw_ops->esd_check(cd);
+	if (ret) {
+		ts_err("esd check failed");
+		goodix_ts_power_off(cd);
+		usleep_range(5000, 5100);
+		goodix_ts_power_on(cd);
+	}
+
+exit:
+	ts_esd->irq_status = false;
+	if (atomic_read(&ts_esd->esd_on))
+		schedule_delayed_work(&ts_esd->esd_work, 2 * HZ);
+}
+
+/**
+ * goodix_ts_esd_on - turn on esd protection
+ */
+static void goodix_ts_esd_on(struct goodix_ts_core *cd)
+{
+	struct goodix_ic_info_misc *misc = &cd->ic_info.misc;
+	struct goodix_ts_esd *ts_esd = &cd->ts_esd;
+
+	if (!misc->esd_addr)
+		return;
+
+	if (atomic_read(&ts_esd->esd_on))
+		return;
+
+	atomic_set(&ts_esd->esd_on, 1);
+	if (!schedule_delayed_work(&ts_esd->esd_work, 2 * HZ))
+		ts_info("esd work already in workqueue");
+
+	ts_info("esd on");
+}
+
+/**
+ * goodix_ts_esd_off - turn off esd protection
+ */
+static void goodix_ts_esd_off(struct goodix_ts_core *cd)
+{
+	struct goodix_ts_esd *ts_esd = &cd->ts_esd;
+	int ret;
+
+	if (!atomic_read(&ts_esd->esd_on))
+		return;
+
+	atomic_set(&ts_esd->esd_on, 0);
+	ret = cancel_delayed_work_sync(&ts_esd->esd_work);
+	ts_info("Esd off, esd work state %d", ret);
+}
+
+/**
+ * goodix_esd_notifier_callback - notification callback
+ *  under certain condition, we need to turn off/on the esd
+ *  protector, we use kernel notify call chain to achieve this.
+ *
+ *  for example: before firmware update we need to turn off the
+ *  esd protector and after firmware update finished, we should
+ *  turn on the esd protector.
+ */
+static int goodix_esd_notifier_callback(struct notifier_block *nb,
+		unsigned long action, void *data)
+{
+	struct goodix_ts_esd *ts_esd = container_of(nb,
+			struct goodix_ts_esd, esd_notifier);
+
+	switch (action) {
+	case NOTIFY_FWUPDATE_START:
+	case NOTIFY_SUSPEND:
+	case NOTIFY_ESD_OFF:
+		goodix_ts_esd_off(ts_esd->ts_core);
+		break;
+	case NOTIFY_FWUPDATE_FAILED:
+	case NOTIFY_FWUPDATE_SUCCESS:
+	case NOTIFY_RESUME:
+	case NOTIFY_ESD_ON:
+		goodix_ts_esd_on(ts_esd->ts_core);
+		break;
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+/**
+ * goodix_ts_esd_init - initialize esd protection
+ */
+int goodix_ts_esd_init(struct goodix_ts_core *cd)
+{
+	struct goodix_ic_info_misc *misc = &cd->ic_info.misc;
+	struct goodix_ts_esd *ts_esd = &cd->ts_esd;
+
+	if (!cd->hw_ops->esd_check || !misc->esd_addr) {
+		ts_info("missing key info for esd check");
+		return 0;
+	}
+
+	INIT_DELAYED_WORK(&ts_esd->esd_work, goodix_ts_esd_work);
+	ts_esd->ts_core = cd;
+	atomic_set(&ts_esd->esd_on, 0);
+	ts_esd->esd_notifier.notifier_call = goodix_esd_notifier_callback;
+	goodix_ts_register_notifier(&ts_esd->esd_notifier);
+	goodix_ts_esd_on(cd);
+	cd->esd_initialized = true;
+
+	return 0;
+}
+
+static void goodix_ts_release_connects(struct goodix_ts_core *core_data)
+{
+	struct input_dev *input_dev = core_data->input_dev;
+	int i;
+
+	mutex_lock(&input_dev->mutex);
+
+	for (i = 0; i < GOODIX_MAX_TOUCH; i++) {
+		input_mt_slot(input_dev, i);
+		input_mt_report_slot_state(input_dev,
+				MT_TOOL_FINGER,
+				false);
+	}
+	input_report_key(input_dev, BTN_TOUCH, 0);
+	input_mt_sync_frame(input_dev);
+	input_sync(input_dev);
+
+	mutex_unlock(&input_dev->mutex);
+}
+
+/**
+ * goodix_ts_suspend - Touchscreen suspend function
+ * Called by PM/FB/EARLYSUSPEN module to put the device to  sleep
+ */
+static int goodix_ts_suspend(struct goodix_ts_core *core_data)
+{
+	struct goodix_ext_module *ext_module, *next;
+	struct goodix_ts_hw_ops *hw_ops = core_data->hw_ops;
+	int ret;
+
+	if (core_data->init_stage < CORE_INIT_STAGE2 ||
+			atomic_read(&core_data->suspended))
+		return 0;
+
+	ts_info("Suspend start");
+	atomic_set(&core_data->suspended, 1);
+	/* disable irq */
+	hw_ops->irq_enable(core_data, false);
+
+	/*
+	 * notify suspend event, inform the esd protector
+	 * and charger detector to turn off the work
+	 */
+	goodix_ts_blocking_notify(NOTIFY_SUSPEND, NULL);
+
+	/* inform external module */
+	mutex_lock(&goodix_modules.mutex);
+	if (!list_empty(&goodix_modules.head)) {
+		list_for_each_entry_safe(ext_module, next,
+					 &goodix_modules.head, list) {
+			if (!ext_module->funcs->before_suspend)
+				continue;
+
+			ret = ext_module->funcs->before_suspend(core_data, ext_module);
+			if (ret == EVT_CANCEL_SUSPEND) {
+				mutex_unlock(&goodix_modules.mutex);
+				ts_info("Canceled by module:%s",
+					ext_module->name);
+				goto out;
+			}
+		}
+	}
+	mutex_unlock(&goodix_modules.mutex);
+
+	/* enter sleep mode or power off */
+	if (hw_ops->suspend)
+		hw_ops->suspend(core_data);
+
+	/* inform exteranl modules */
+	mutex_lock(&goodix_modules.mutex);
+	if (!list_empty(&goodix_modules.head)) {
+		list_for_each_entry_safe(ext_module, next,
+					&goodix_modules.head, list) {
+			if (!ext_module->funcs->after_suspend)
+				continue;
+
+			ret = ext_module->funcs->after_suspend(core_data, ext_module);
+			if (ret == EVT_CANCEL_SUSPEND) {
+				mutex_unlock(&goodix_modules.mutex);
+				ts_info("Canceled by module:%s",
+					ext_module->name);
+				goto out;
+			}
+		}
+	}
+	mutex_unlock(&goodix_modules.mutex);
+
+out:
+	goodix_ts_release_connects(core_data);
+	ts_info("Suspend end");
+	return 0;
+}
+
+/**
+ * goodix_ts_resume - Touchscreen resume function
+ * Called by PM/FB/EARLYSUSPEN module to wakeup device
+ */
+static int goodix_ts_resume(struct goodix_ts_core *core_data)
+{
+	struct goodix_ext_module *ext_module, *next;
+	struct goodix_ts_hw_ops *hw_ops = core_data->hw_ops;
+	int ret;
+
+	if (core_data->init_stage < CORE_INIT_STAGE2 ||
+			!atomic_read(&core_data->suspended))
+		return 0;
+
+	ts_info("Resume start");
+	atomic_set(&core_data->suspended, 0);
+	hw_ops->irq_enable(core_data, false);
+
+	mutex_lock(&goodix_modules.mutex);
+	if (!list_empty(&goodix_modules.head)) {
+		list_for_each_entry_safe(ext_module, next,
+					&goodix_modules.head, list) {
+			if (!ext_module->funcs->before_resume)
+				continue;
+
+			ret = ext_module->funcs->before_resume(core_data, ext_module);
+			if (ret == EVT_CANCEL_RESUME) {
+				mutex_unlock(&goodix_modules.mutex);
+				ts_info("Canceled by module:%s", ext_module->name);
+				goto out;
+			}
+		}
+	}
+	mutex_unlock(&goodix_modules.mutex);
+
+	/* reset device or power on*/
+	if (hw_ops->resume)
+		hw_ops->resume(core_data);
+
+	mutex_lock(&goodix_modules.mutex);
+	if (!list_empty(&goodix_modules.head)) {
+		list_for_each_entry_safe(ext_module, next,
+					&goodix_modules.head, list) {
+			if (!ext_module->funcs->after_resume)
+				continue;
+
+			ret = ext_module->funcs->after_resume(core_data, ext_module);
+			if (ret == EVT_CANCEL_RESUME) {
+				mutex_unlock(&goodix_modules.mutex);
+				ts_info("Canceled by module:%s",
+					ext_module->name);
+				goto out;
+			}
+		}
+	}
+	mutex_unlock(&goodix_modules.mutex);
+
+out:
+	/* enable irq */
+	hw_ops->irq_enable(core_data, true);
+	/* open esd */
+	goodix_ts_blocking_notify(NOTIFY_RESUME, NULL);
+	ts_info("Resume end");
+	return 0;
+}
+
+#if defined(CONFIG_DRM)
+
+static void goodix_panel_notifier_callback(enum panel_event_notifier_tag tag,
+		 struct panel_event_notification *notification, void *client_data)
+{
+	struct goodix_ts_core *core_data = client_data;
+
+	if (!notification) {
+		pr_err("Invalid notification\n");
+		return;
+	}
+
+	ts_debug("Notification type:%d, early_trigger:%d",
+			notification->notif_type,
+			notification->notif_data.early_trigger);
+	switch (notification->notif_type) {
+	case DRM_PANEL_EVENT_UNBLANK:
+		if (!notification->notif_data.early_trigger)
+			goodix_ts_resume(core_data);
+		break;
+
+	case DRM_PANEL_EVENT_BLANK:
+		if (notification->notif_data.early_trigger)
+			goodix_ts_suspend(core_data);
+		break;
+
+	case DRM_PANEL_EVENT_BLANK_LP:
+		ts_debug("received lp event\n");
+		break;
+
+	case DRM_PANEL_EVENT_FPS_CHANGE:
+		ts_debug("Received fps change old fps:%d new fps:%d\n",
+				notification->notif_data.old_fps,
+				notification->notif_data.new_fps);
+		break;
+
+	default:
+		ts_debug("notification serviced :%d\n",
+				notification->notif_type);
+		break;
+	}
+}
+
+#elif IS_ENABLED(CONFIG_FB)
+
+/**
+ * goodix_ts_fb_notifier_callback - Framebuffer notifier callback
+ * Called by kernel during framebuffer blanck/unblank phrase
+ */
+int goodix_ts_fb_notifier_callback(struct notifier_block *self,
+	unsigned long event, void *data)
+{
+	struct goodix_ts_core *core_data =
+		container_of(self, struct goodix_ts_core, fb_notifier);
+	struct fb_event *fb_event = data;
+
+	if (fb_event && fb_event->data && core_data) {
+		if (event == FB_EARLY_EVENT_BLANK) {
+			/* before fb blank */
+		} else if (event == FB_EVENT_BLANK) {
+			int *blank = fb_event->data;
+
+			if (*blank == FB_BLANK_UNBLANK)
+				goodix_ts_resume(core_data);
+			else if (*blank == FB_BLANK_POWERDOWN)
+				goodix_ts_suspend(core_data);
+		}
+	}
+
+	return 0;
+}
+#endif
+
+
+#if defined(CONFIG_PM) && !defined(CONFIG_DRM)
+#if !defined(CONFIG_FB) && !defined(CONFIG_HAS_EARLYSUSPEND)
+/**
+ * goodix_ts_pm_suspend - PM suspend function
+ * Called by kernel during system suspend phrase
+ */
+static int goodix_ts_pm_suspend(struct device *dev)
+{
+	struct goodix_ts_core *core_data =
+		dev_get_drvdata(dev);
+
+	return goodix_ts_suspend(core_data);
+}
+/**
+ * goodix_ts_pm_resume - PM resume function
+ * Called by kernel during system wakeup
+ */
+static int goodix_ts_pm_resume(struct device *dev)
+{
+	struct goodix_ts_core *core_data =
+		dev_get_drvdata(dev);
+
+	return goodix_ts_resume(core_data);
+}
+#endif
+#endif
+
+/**
+ * goodix_generic_noti_callback - generic notifier callback
+ *  for goodix touch notification event.
+ */
+static int goodix_generic_noti_callback(struct notifier_block *self,
+		unsigned long action, void *data)
+{
+	struct goodix_ts_core *cd = container_of(self,
+			struct goodix_ts_core, ts_notifier);
+	const struct goodix_ts_hw_ops *hw_ops = cd->hw_ops;
+
+	if (cd->init_stage < CORE_INIT_STAGE2)
+		return 0;
+
+	ts_info("notify event type 0x%x", (unsigned int)action);
+	switch (action) {
+	case NOTIFY_FWUPDATE_START:
+		hw_ops->irq_enable(cd, 0);
+		break;
+	case NOTIFY_FWUPDATE_SUCCESS:
+	case NOTIFY_FWUPDATE_FAILED:
+		if (hw_ops->read_version(cd, &cd->fw_version))
+			ts_info("failed read fw version info[ignore]");
+		hw_ops->irq_enable(cd, 1);
+		break;
+	default:
+		break;
+	}
+	return 0;
+}
+
+int goodix_ts_stage2_init(struct goodix_ts_core *cd)
+{
+	int ret;
+	struct goodix_bus_interface *bus_interface;
+	struct device_node *node;
+	bool is_primary;
+
+	bus_interface = cd->bus;
+	node = bus_interface->dev->of_node;
+	is_primary = (goodix_get_touch_type(node) == PRIMARY_TOUCH_IDX) ? 1 : 0;
+
+
+	/* alloc/config/register input device */
+	ret = goodix_ts_input_dev_config(cd);
+	if (ret < 0) {
+		ts_err("failed set input device");
+		return ret;
+	}
+
+	if (cd->board_data.pen_enable) {
+		ret = goodix_ts_pen_dev_config(cd);
+		if (ret < 0) {
+			ts_err("failed set pen device");
+			goto err_finger;
+		}
+	}
+#ifdef CONFIG_ARCH_QTI_VM
+		goto skip_goodix_ts_irq_setup;
+#endif
+	/* request irq line */
+	ret = goodix_ts_irq_setup(cd);
+	if (ret < 0) {
+		ts_info("failed set irq");
+		goto exit;
+	}
+	ts_info("success register irq");
+
+#if defined(CONFIG_DRM)
+	if (is_primary && cd->touch_environment && !strcmp(cd->touch_environment, "pvm"))
+		goodix_register_for_panel_events(cd->bus->dev->of_node, cd);
+
+#elif defined(CONFIG_FB)
+	cd->fb_notifier.notifier_call = goodix_ts_fb_notifier_callback;
+	if (fb_register_client(&cd->fb_notifier))
+		ts_err("Failed to register fb notifier client:%d", ret);
+#endif
+
+#ifdef CONFIG_ARCH_QTI_VM
+skip_goodix_ts_irq_setup:
+#endif
+	/* create sysfs files */
+	goodix_ts_sysfs_init(cd);
+
+	if (!is_primary)
+		return 0;
+
+	/* create procfs files */
+	goodix_ts_procfs_init(cd);
+#ifdef GOODIX_SUSPEND_GESTURE_ENABLE
+	/* gesture init */
+	gesture_module_init();
+#endif
+	/* inspect init */
+	inspect_module_init();
+
+	return 0;
+exit:
+	goodix_ts_pen_dev_remove(cd);
+err_finger:
+	goodix_ts_input_dev_remove(cd);
+	return ret;
+}
+
+/* try send the config specified with type */
+static int goodix_send_ic_config(struct goodix_ts_core *cd, int type)
+{
+	u32 config_id;
+	struct goodix_ic_config *cfg;
+
+	if (type >= GOODIX_MAX_CONFIG_GROUP) {
+		ts_err("unsupproted config type %d", type);
+		return -EINVAL;
+	}
+
+	cfg = cd->ic_configs[type];
+	if (!cfg || cfg->len <= 0) {
+		ts_info("no valid normal config found");
+		return -EINVAL;
+	}
+
+	config_id = goodix_get_file_config_id(cfg->data);
+	if (cd->ic_info.version.config_id == config_id) {
+		ts_info("config id is equal 0x%x, skiped", config_id);
+		return 0;
+	}
+
+	ts_info("try send config, id=0x%x", config_id);
+	return cd->hw_ops->send_config(cd, cfg->data, cfg->len);
+}
+
+/**
+ * goodix_later_init_thread - init IC fw and config
+ * @data: point to goodix_ts_core
+ *
+ * This function respond for get fw version and try upgrade fw and config.
+ * Note: when init encounter error, need release all resource allocated here.
+ */
+static int goodix_later_init_thread(void *data)
+{
+	int ret, i;
+	int update_flag = UPDATE_MODE_BLOCK | UPDATE_MODE_SRC_REQUEST;
+	struct goodix_ts_core *cd = data;
+	struct goodix_ts_hw_ops *hw_ops = cd->hw_ops;
+
+	mutex_lock(&goodix_later_init_tmutex);
+#ifdef CONFIG_ARCH_QTI_VM
+	goto skip_to_stage2_init;
+#endif
+
+	/* step 1: read version */
+	ret = cd->hw_ops->read_version(cd, &cd->fw_version);
+	if (ret < 0) {
+		ts_err("failed to get version info, try to upgrade");
+		update_flag |= UPDATE_MODE_FORCE;
+		goto upgrade;
+	}
+
+	/* step 2: get config data from config bin */
+	ret = goodix_get_config_proc(cd);
+	if (ret)
+		ts_info("no valid ic config found");
+	else
+		ts_info("success get valid ic config");
+
+upgrade:
+	/* step 3: init fw struct add try do fw upgrade */
+	ret = goodix_fw_update_init(cd);
+	if (ret) {
+		ts_err("failed init fw update module");
+		goto err_out;
+	}
+
+	ts_info("update flag: 0x%X", update_flag);
+	ret = goodix_do_fw_update(cd->ic_configs[CONFIG_TYPE_NORMAL],
+			update_flag);
+	if (ret)
+		ts_err("failed do fw update");
+
+	/* step 4: get fw version and ic_info
+	 * at this step we believe that the ic is in normal mode,
+	 * if the version info is invalid there must have some
+	 * problem we cann't cover so exit init directly.
+	 */
+	ret = hw_ops->read_version(cd, &cd->fw_version);
+	if (ret) {
+		ts_err("invalid fw version, abort");
+		goto uninit_fw;
+	}
+	ret = hw_ops->get_ic_info(cd, &cd->ic_info);
+	if (ret) {
+		ts_err("invalid ic info, abort");
+		goto uninit_fw;
+	}
+
+	/* the recomend way to update ic config is throuth ISP,
+	 * if not we will send config with interactive mode
+	 */
+	goodix_send_ic_config(cd, CONFIG_TYPE_NORMAL);
+#ifdef CONFIG_ARCH_QTI_VM
+skip_to_stage2_init:
+#endif
+	/* init other resources */
+	ret = goodix_ts_stage2_init(cd);
+	if (ret) {
+		ts_err("stage2 init failed");
+		goto uninit_fw;
+	}
+	cd->init_stage = CORE_INIT_STAGE2;
+	mutex_unlock(&goodix_later_init_tmutex);
+	return 0;
+
+uninit_fw:
+	goodix_fw_update_uninit();
+err_out:
+	ts_err("stage2 init failed");
+	cd->init_stage = CORE_INIT_FAIL;
+	for (i = 0; i < GOODIX_MAX_CONFIG_GROUP; i++) {
+		kfree(cd->ic_configs[i]);
+		cd->ic_configs[i] = NULL;
+	}
+	mutex_unlock(&goodix_later_init_tmutex);
+	return ret;
+}
+
+static int goodix_start_later_init(struct goodix_ts_core *ts_core)
+{
+	struct task_struct *init_thrd;
+	/* create and run update thread */
+	init_thrd = kthread_run(goodix_later_init_thread,
+				ts_core, "goodix_init_thread");
+	if (IS_ERR_OR_NULL(init_thrd)) {
+		ts_err("Failed to create update thread:%ld",
+			PTR_ERR(init_thrd));
+		return -EFAULT;
+	}
+	return 0;
+}
+
+#if defined(CONFIG_DRM)
+static int goodix_check_dt(struct device_node *np)
+{
+	int i;
+	int count;
+	struct device_node *node;
+	struct drm_panel *panel;
+
+	count = of_count_phandle_with_args(np, "panel", NULL);
+	if (count <= 0)
+		return 0;
+
+	for (i = 0; i < count; i++) {
+		node = of_parse_phandle(np, "panel", i);
+		panel = of_drm_find_panel(node);
+		of_node_put(node);
+		if (!IS_ERR(panel)) {
+			active_panel = panel;
+			return 0;
+		}
+	}
+
+	return PTR_ERR(panel);
+}
+
+static int goodix_check_default_tp(struct device_node *dt, const char *prop)
+{
+	const char **active_tp = NULL;
+	int count, tmp, score = 0;
+	const char *active;
+	int ret, i;
+
+	count = of_property_count_strings(dt->parent, prop);
+	if (count <= 0 || count > 3)
+		return -ENODEV;
+
+	active_tp = kcalloc(count, sizeof(char *),  GFP_KERNEL);
+	if (!active_tp) {
+		ts_err("FTS alloc failed\n");
+		return -ENOMEM;
+	}
+
+	ret = of_property_read_string_array(dt->parent, prop,
+			active_tp, count);
+	if (ret < 0) {
+		ts_err("fail to read %s %d\n", prop, ret);
+		ret = -ENODEV;
+		goto out;
+	}
+
+	for (i = 0; i < count; i++) {
+		active = active_tp[i];
+		if (active != NULL) {
+			tmp = of_device_is_compatible(dt, active);
+			if (tmp > 0)
+				score++;
+		}
+	}
+
+	if (score <= 0) {
+		ts_err("not match this driver\n");
+		ret = -ENODEV;
+		goto out;
+	}
+	ret = 0;
+out:
+	kfree(active_tp);
+	return ret;
+}
+
+#endif
+
+static int goodix_ts_suspend_helper(void *data)
+{
+	struct goodix_ts_core *core_data = data;
+
+	if (!core_data || !core_data->ready)
+		return 0;
+
+	return goodix_ts_suspend(core_data);
+}
+
+static int goodix_ts_resume_helper(void *data)
+{
+	struct goodix_ts_core *core_data = data;
+
+	if (!core_data || !core_data->ready)
+		return 0;
+
+	return goodix_ts_resume(core_data);
+}
+
+static int goodix_ts_enable_touch_irq(void *data, bool enable)
+{
+	struct goodix_ts_core *core_data = data;
+	struct goodix_ts_hw_ops *hw_ops = core_data->hw_ops;
+
+	hw_ops->irq_enable(core_data, enable);
+	return 0;
+}
+
+static int goodix_ts_pre_la_tui_enable(void *data)
+{
+	struct goodix_ts_core *core_data = data;
+	struct goodix_ts_esd *ts_esd = &core_data->ts_esd;
+
+	mutex_lock(&core_data->tui_transition_lock);
+	atomic_set(&ts_esd->esd_on, 0);
+
+	return 0;
+}
+
+static int goodix_ts_post_la_tui_enable(void *data)
+{
+	struct goodix_ts_core *core_data = data;
+
+	goodix_ts_release_connects(core_data);
+	mutex_unlock(&core_data->tui_transition_lock);
+	return 0;
+}
+
+static int goodix_ts_post_la_tui_disable(void *data)
+{
+	struct goodix_ts_core *core_data = data;
+	struct goodix_ts_esd *ts_esd = &core_data->ts_esd;
+
+	atomic_set(&ts_esd->esd_on, 1);
+	return 0;
+}
+
+static int goodix_ts_post_le_tui_enable(void *data)
+{
+	int ret;
+	struct goodix_ts_core *cd = data;
+	struct goodix_ts_hw_ops *hw_ops = cd->hw_ops;
+
+	ret = hw_ops->read_version(cd, &cd->fw_version);
+	if (ret) {
+		ts_err("invalid fw version, abort");
+		return -EINVAL;
+	}
+	ret = hw_ops->get_ic_info(cd, &cd->ic_info);
+	if (ret) {
+		ts_err("invalid ic info, abort");
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int goodix_ts_post_le_tui_disable(void *data)
+{
+	struct goodix_ts_core *core_data = data;
+
+	goodix_ts_release_connects(core_data);
+	return 0;
+}
+
+static int goodix_ts_get_irq_num(void *data)
+{
+	struct goodix_ts_core *core_data = data;
+
+	return core_data->irq;
+}
+
+static irqreturn_t  goodix_irq_handler(int irq, void *data)
+{
+	struct goodix_ts_core *core_data = data;
+
+	if (!mutex_trylock(&core_data->tui_transition_lock))
+		return IRQ_HANDLED;
+
+	goodix_ts_threadirq_func(irq, data);
+	mutex_unlock(&core_data->tui_transition_lock);
+
+	return IRQ_HANDLED;
+}
+
+static void goodix_ts_fill_qts_vendor_data(struct qts_vendor_data *qts_vendor_data,
+		 struct goodix_ts_core *core_data)
+{
+	struct goodix_bus_interface *bus_interface;
+	struct device_node *node;
+	const char *touch_type;
+	int rc = 0;
+
+	bus_interface = core_data->bus;
+	node = bus_interface->dev->of_node;
+
+	rc = of_property_read_string(node, "goodix,touch-type", &touch_type);
+	if (rc) {
+		ts_err("No touch type\n");
+		return;
+	}
+
+	if (!strcmp(touch_type, "primary"))
+		qts_vendor_data->client_type = QTS_CLIENT_PRIMARY_TOUCH;
+	else
+		qts_vendor_data->client_type = QTS_CLIENT_SECONDARY_TOUCH;
+
+	switch (bus_interface->bus_type) {
+	case GOODIX_BUS_TYPE_I2C:
+		qts_vendor_data->client = to_i2c_client(bus_interface->dev);
+		qts_vendor_data->spi = NULL;
+		qts_vendor_data->bus_type = QTS_BUS_TYPE_I2C;
+		break;
+
+	case GOODIX_BUS_TYPE_SPI:
+		qts_vendor_data->client = NULL;
+		qts_vendor_data->spi = to_spi_device(bus_interface->dev);
+		qts_vendor_data->bus_type = QTS_BUS_TYPE_SPI;
+		break;
+
+	default:
+		ts_err("Invalid bus type :%d\n",
+				bus_interface->bus_type);
+		break;
+	}
+
+	qts_vendor_data->vendor_data = core_data;
+	qts_vendor_data->schedule_suspend = false;
+	qts_vendor_data->schedule_resume = false;
+	qts_vendor_data->qts_vendor_ops.suspend = goodix_ts_suspend_helper;
+	qts_vendor_data->qts_vendor_ops.resume = goodix_ts_resume_helper;
+	qts_vendor_data->qts_vendor_ops.enable_touch_irq = goodix_ts_enable_touch_irq;
+	qts_vendor_data->qts_vendor_ops.get_irq_num = goodix_ts_get_irq_num;
+	qts_vendor_data->qts_vendor_ops.pre_la_tui_enable = goodix_ts_pre_la_tui_enable;
+	qts_vendor_data->qts_vendor_ops.post_la_tui_enable = goodix_ts_post_la_tui_enable;
+	qts_vendor_data->qts_vendor_ops.pre_la_tui_disable = NULL;
+	qts_vendor_data->qts_vendor_ops.post_la_tui_disable = goodix_ts_post_la_tui_disable;
+	qts_vendor_data->qts_vendor_ops.pre_le_tui_enable = NULL;
+	qts_vendor_data->qts_vendor_ops.post_le_tui_enable = goodix_ts_post_le_tui_enable;
+	qts_vendor_data->qts_vendor_ops.pre_le_tui_disable = NULL;
+	qts_vendor_data->qts_vendor_ops.post_le_tui_disable = goodix_ts_post_le_tui_disable;
+	qts_vendor_data->qts_vendor_ops.irq_handler = goodix_irq_handler;
+}
+
+/**
+ * goodix_ts_probe - called by kernel when Goodix touch
+ *  platform driver is added.
+ */
+static int goodix_ts_probe(struct platform_device *pdev)
+{
+	struct goodix_ts_core *core_data = NULL;
+	struct goodix_bus_interface *bus_interface;
+	int ret;
+	struct device_node *node;
+	bool qts_en = false;
+	struct qts_vendor_data qts_vendor_data;
+	bool is_primary;
+
+	ts_info("goodix_ts_probe IN");
+
+	if (!core_module_prob_sate)
+		mutex_init(&goodix_later_init_tmutex);
+
+	bus_interface = pdev->dev.platform_data;
+	if (!bus_interface) {
+		ts_err("Invalid touch device");
+		core_module_prob_sate = CORE_MODULE_PROB_FAILED;
+		return -ENODEV;
+	}
+	node = bus_interface->dev->of_node;
+	is_primary = (goodix_get_touch_type(node) == PRIMARY_TOUCH_IDX) ? 1 : 0;
+
+#if defined(CONFIG_DRM)
+	ret = goodix_check_dt(node);
+	if (ret == -EPROBE_DEFER)
+		return ret;
+
+	if (ret) {
+		if (!goodix_check_default_tp(node, "qcom,touch-active"))
+			ret = -EPROBE_DEFER;
+		else
+			ret = -ENODEV;
+
+		return ret;
+	}
+#endif
+
+	core_data = devm_kzalloc(&pdev->dev,
+			sizeof(struct goodix_ts_core), GFP_KERNEL);
+	if (!core_data) {
+		core_module_prob_sate = CORE_MODULE_PROB_FAILED;
+		return -ENOMEM;
+	}
+
+	core_data->bus = bus_interface;
+
+	if (IS_ENABLED(CONFIG_OF) && bus_interface->dev->of_node) {
+		/* parse devicetree property */
+		ret = goodix_parse_dt(node, &core_data->board_data);
+		if (ret) {
+			ts_err("failed parse device info form dts, %d", ret);
+			ret = -EINVAL;
+			goto err_out;
+		}
+#if defined(CONFIG_DRM)
+		of_property_read_string(node, "qcom,touch-environment",
+				&core_data->touch_environment);
+#endif
+	} else {
+		ts_err("no valid device tree node found");
+		ret = -ENODEV;
+		goto err_out;
+	}
+
+	core_data->hw_ops = goodix_get_hw_ops();
+	if (!core_data->hw_ops) {
+		ts_err("hw ops is NULL");
+		ret = -EINVAL;
+		goto err_out;
+	}
+
+	if (is_primary)
+		goodix_core_module_init();
+	/* touch core layer is a platform driver */
+	core_data->pdev = pdev;
+	platform_set_drvdata(pdev, core_data);
+
+	/* get GPIO resource */
+#ifdef CONFIG_ARCH_QTI_VM
+	goto skip_to_power_gpio_setup;
+#endif
+	ret = goodix_ts_gpio_setup(core_data);
+	if (ret) {
+		ts_err("failed init gpio");
+		goto err_out;
+	}
+
+	ret = goodix_ts_power_init(core_data);
+	if (ret) {
+		ts_err("failed init power");
+		goto err_out;
+	}
+
+	ret = goodix_ts_power_on(core_data);
+	if (ret) {
+		ts_err("failed power on");
+		goto err_out;
+	}
+
+#ifdef CONFIG_ARCH_QTI_VM
+skip_to_power_gpio_setup:
+#endif
+
+	qts_en = of_property_read_bool(node, "goodix,qts_en");
+	if (qts_en) {
+		mutex_init(&core_data->tui_transition_lock);
+		goodix_ts_fill_qts_vendor_data(&qts_vendor_data, core_data);
+
+		ret = qts_client_register(qts_vendor_data);
+		if (ret) {
+			pr_err("qts client register failed, rc %d\n", ret);
+			goto err_out;
+		}
+		core_data->qts_en = qts_en;
+	}
+
+	/* generic notifier callback */
+	core_data->ts_notifier.notifier_call = goodix_generic_noti_callback;
+	goodix_ts_register_notifier(&core_data->ts_notifier);
+
+	/* debug node init */
+	if (is_primary)
+		goodix_tools_init();
+
+	core_data->init_stage = CORE_INIT_STAGE1;
+	goodix_modules.core_data = core_data;
+	core_module_prob_sate = CORE_MODULE_PROB_SUCCESS;
+	core_data->ready = true;
+
+	/* Try start a thread to get config-bin info */
+	goodix_start_later_init(core_data);
+
+	ts_info("goodix_ts_core probe success");
+	return 0;
+
+err_out:
+	devm_kfree(&pdev->dev, core_data);
+	core_module_prob_sate = CORE_MODULE_PROB_FAILED;
+	ts_err("goodix_ts_core failed, ret:%d", ret);
+	return ret;
+}
+
+static int goodix_ts_remove(struct platform_device *pdev)
+{
+	struct goodix_ts_core *core_data = platform_get_drvdata(pdev);
+	struct goodix_ts_hw_ops *hw_ops = core_data->hw_ops;
+	struct goodix_ts_esd *ts_esd = &core_data->ts_esd;
+
+	goodix_ts_unregister_notifier(&core_data->ts_notifier);
+	goodix_tools_exit();
+
+	if (core_data->init_stage >= CORE_INIT_STAGE2) {
+	#ifdef GOODIX_SUSPEND_GESTURE_ENABLE
+		gesture_module_exit();
+	#endif
+		inspect_module_exit();
+		hw_ops->irq_enable(core_data, false);
+
+	#if defined(CONFIG_DRM)
+		if (core_data->notifier_cookie)
+			panel_event_notifier_unregister(core_data->notifier_cookie);
+	#elif IS_ENABLED(CONFIG_FB)
+		fb_unregister_client(&core_data->fb_notifier);
+	#endif
+		core_module_prob_sate = CORE_MODULE_REMOVED;
+		if (atomic_read(&core_data->ts_esd.esd_on))
+			goodix_ts_esd_off(core_data);
+		goodix_ts_unregister_notifier(&ts_esd->esd_notifier);
+
+		goodix_fw_update_uninit();
+		goodix_ts_input_dev_remove(core_data);
+		goodix_ts_pen_dev_remove(core_data);
+		goodix_ts_sysfs_exit(core_data);
+		goodix_ts_procfs_exit(core_data);
+		goodix_ts_power_off(core_data);
+	}
+
+	return 0;
+}
+
+#if defined(CONFIG_PM) && !defined(CONFIG_DRM)
+static const struct dev_pm_ops dev_pm_ops = {
+#if !defined(CONFIG_FB) && !defined(CONFIG_HAS_EARLYSUSPEND)
+	.suspend = goodix_ts_pm_suspend,
+	.resume = goodix_ts_pm_resume,
+#endif
+};
+#endif
+
+static const struct platform_device_id ts_core_ids[] = {
+	{.name = GOODIX_CORE_DEVICE_NAME},
+	{.name = GOODIX_CORE_DEVICE_2_NAME},
+	{}
+};
+MODULE_DEVICE_TABLE(platform, ts_core_ids);
+
+static struct platform_driver goodix_ts_driver = {
+	.driver = {
+		.name = GOODIX_CORE_DRIVER_NAME,
+		.owner = THIS_MODULE,
+#if defined(CONFIG_PM) && !defined(CONFIG_DRM)
+		.pm = &dev_pm_ops,
+#endif
+	},
+	.probe = goodix_ts_probe,
+	.remove = goodix_ts_remove,
+	.id_table = ts_core_ids,
+};
+
+static int __init goodix_ts_core_init(void)
+{
+	int ret = 0;
+
+	ts_info("Core layer init:%s", GOODIX_DRIVER_VERSION);
+
+	ret = goodix_spi_bus_init();
+	ret |= goodix_i2c_bus_init();
+	if (ret) {
+		ts_err("failed add bus driver");
+		return ret;
+	}
+	return platform_driver_register(&goodix_ts_driver);
+}
+
+static void __exit goodix_ts_core_exit(void)
+{
+	ts_info("Core layer exit");
+	platform_driver_unregister(&goodix_ts_driver);
+
+	goodix_spi_bus_exit();
+
+	goodix_i2c_bus_exit();
+}
+
+#ifdef CONFIG_ARCH_QTI_VM
+module_init(goodix_ts_core_init);
+#else
+late_initcall(goodix_ts_core_init);
+#endif
+module_exit(goodix_ts_core_exit);
+
+MODULE_DESCRIPTION("Goodix Touchscreen Core Module");
+MODULE_AUTHOR("Goodix, Inc.");
+MODULE_LICENSE("GPL v2");

+ 707 - 0
qcom/opensource/touch-drivers/goodix_berlin_driver/goodix_ts_core.h

@@ -0,0 +1,707 @@
+/*
+ * Goodix Touchscreen Driver
+ * Copyright (C) 2020 - 2021 Goodix, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be a reference
+ * to you, when you are integrating the GOODiX's CTP IC into your system,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ */
+#ifndef _GOODIX_TS_CORE_H_
+#define _GOODIX_TS_CORE_H_
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/firmware.h>
+#include <linux/slab.h>
+#include <asm/unaligned.h>
+#include <linux/vmalloc.h>
+#include <linux/kthread.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/completion.h>
+#include <linux/of_irq.h>
+#if IS_ENABLED(CONFIG_OF)
+#include <linux/of_gpio.h>
+#include <linux/regulator/consumer.h>
+#endif
+#if IS_ENABLED(CONFIG_FB)
+#include <linux/notifier.h>
+#include <linux/fb.h>
+#endif
+#include <linux/i2c.h>
+#include <linux/spi/spi.h>
+#include "../qts/qts_core_common.h"
+
+#define GOODIX_CORE_DRIVER_NAME			"goodix_ts"
+#define GOODIX_PEN_DRIVER_NAME			"goodix_ts,pen"
+#define GOODIX_CORE_DEVICE_NAME			"goodix_ts"
+#define GOODIX_CORE_DEVICE_2_NAME		"goodix_ts2"
+#define GOODIX_DRIVER_VERSION			"v1.2.4"
+#define GOODIX_MAX_TOUCH				10
+#define GOODIX_PEN_MAX_PRESSURE			4096
+#define GOODIX_MAX_PEN_KEY				2
+#define GOODIX_PEN_MAX_TILT				90
+#define GOODIX_CFG_MAX_SIZE				4096
+#define GOODIX_FW_MAX_SIEZE				(300 * 1024)
+#define GOODIX_MAX_STR_LABLE_LEN		32
+#define GOODIX_MAX_FRAMEDATA_LEN		1700
+#define GOODIX_GESTURE_DATA_LEN			16
+
+#define GOODIX_NORMAL_RESET_DELAY_MS	100
+#define GOODIX_HOLD_CPU_RESET_DELAY_MS  5
+
+#define GOODIX_RETRY_3					3
+#define GOODIX_RETRY_5					5
+#define GOODIX_RETRY_10					10
+
+#define TS_DEFAULT_FIRMWARE				"goodix_firmware.bin"
+#define TS_DEFAULT_CFG_BIN				"goodix_cfg_group.bin"
+
+enum GOODIX_GESTURE_TYP {
+	GESTURE_SINGLE_TAP = (1 << 0),
+	GESTURE_DOUBLE_TAP = (1 << 1),
+	GESTURE_FOD_PRESS  = (1 << 2)
+};
+
+enum CORD_PROB_STA {
+	CORE_MODULE_UNPROBED = 0,
+	CORE_MODULE_PROB_SUCCESS = 1,
+	CORE_MODULE_PROB_FAILED = -1,
+	CORE_MODULE_REMOVED = -2,
+};
+
+enum GOODIX_ERR_CODE {
+	GOODIX_EBUS      = (1<<0),
+	GOODIX_ECHECKSUM = (1<<1),
+	GOODIX_EVERSION  = (1<<2),
+	GOODIX_ETIMEOUT  = (1<<3),
+	GOODIX_EMEMCMP   = (1<<4),
+
+	GOODIX_EOTHER    = (1<<7)
+};
+
+enum IC_TYPE_ID {
+	IC_TYPE_NONE,
+	IC_TYPE_NORMANDY,
+	IC_TYPE_NANJING,
+	IC_TYPE_YELLOWSTONE,
+	IC_TYPE_BERLIN_A,
+	IC_TYPE_BERLIN_B,
+	IC_TYPE_BERLIN_D
+};
+
+enum GOODIX_IC_CONFIG_TYPE {
+	CONFIG_TYPE_TEST = 0,
+	CONFIG_TYPE_NORMAL = 1,
+	CONFIG_TYPE_HIGHSENSE = 2,
+	CONFIG_TYPE_CHARGER = 3,
+	CONFIG_TYPE_CHARGER_HS = 4,
+	CONFIG_TYPE_HOLSTER = 5,
+	CONFIG_TYPE_HOSTER_CH = 6,
+	CONFIG_TYPE_OTHER = 7,
+	/* keep this at the last */
+	GOODIX_MAX_CONFIG_GROUP = 8,
+};
+
+enum CHECKSUM_MODE {
+	CHECKSUM_MODE_U8_LE,
+	CHECKSUM_MODE_U16_LE,
+};
+
+enum DUAL_TOUCH_TYPE {
+	PRIMARY_TOUCH_IDX,
+	SECONDARY_TOUCH_IDX,
+	MAX_SUPPORTED_TOUCH_PANELS,
+};
+
+#define MAX_SCAN_FREQ_NUM            8
+#define MAX_SCAN_RATE_NUM            8
+#define MAX_FREQ_NUM_STYLUS          8
+#define MAX_STYLUS_SCAN_FREQ_NUM     6
+#pragma pack(1)
+struct frame_head {
+	uint8_t sync;
+	uint16_t frame_index;
+	uint16_t cur_frame_len;
+	uint16_t next_frame_len;
+	uint32_t data_en; /* 0- 7 for pack_en; 8 - 31 for type en */
+	uint8_t touch_pack_index;
+	uint8_t stylus_pack_index;
+	uint8_t res;
+	uint16_t checksum;
+};
+
+struct goodix_fw_version {
+	u8 rom_pid[6];      /* rom PID */
+	u8 rom_vid[3];      /* Mask VID */
+	u8 rom_vid_reserved;
+	u8 patch_pid[8];    /* Patch PID */
+	u8 patch_vid[4];    /* Patch VID */
+	u8 patch_vid_reserved;
+	u8 sensor_id;
+	u8 reserved[2];
+	u16 checksum;
+};
+
+struct goodix_ic_info_version {
+	u8 info_customer_id;
+	u8 info_version_id;
+	u8 ic_die_id;
+	u8 ic_version_id;
+	u32 config_id;
+	u8 config_version;
+	u8 frame_data_customer_id;
+	u8 frame_data_version_id;
+	u8 touch_data_customer_id;
+	u8 touch_data_version_id;
+	u8 reserved[3];
+};
+
+struct goodix_ic_info_feature { /* feature info*/
+	u16 freqhop_feature;
+	u16 calibration_feature;
+	u16 gesture_feature;
+	u16 side_touch_feature;
+	u16 stylus_feature;
+};
+
+struct goodix_ic_info_param { /* param */
+	u8 drv_num;
+	u8 sen_num;
+	u8 button_num;
+	u8 force_num;
+	u8 active_scan_rate_num;
+	u16 active_scan_rate[MAX_SCAN_RATE_NUM];
+	u8 mutual_freq_num;
+	u16 mutual_freq[MAX_SCAN_FREQ_NUM];
+	u8 self_tx_freq_num;
+	u16 self_tx_freq[MAX_SCAN_FREQ_NUM];
+	u8 self_rx_freq_num;
+	u16 self_rx_freq[MAX_SCAN_FREQ_NUM];
+	u8 stylus_freq_num;
+	u16 stylus_freq[MAX_FREQ_NUM_STYLUS];
+};
+
+struct goodix_ic_info_misc { /* other data */
+	u32 cmd_addr;
+	u16 cmd_max_len;
+	u32 cmd_reply_addr;
+	u16 cmd_reply_len;
+	u32 fw_state_addr;
+	u16 fw_state_len;
+	u32 fw_buffer_addr;
+	u16 fw_buffer_max_len;
+	u32 frame_data_addr;
+	u16 frame_data_head_len;
+	u16 fw_attr_len;
+	u16 fw_log_len;
+	u8 pack_max_num;
+	u8 pack_compress_version;
+	u16 stylus_struct_len;
+	u16 mutual_struct_len;
+	u16 self_struct_len;
+	u16 noise_struct_len;
+	u32 touch_data_addr;
+	u16 touch_data_head_len;
+	u16 point_struct_len;
+	u16 reserved1;
+	u16 reserved2;
+	u32 mutual_rawdata_addr;
+	u32 mutual_diffdata_addr;
+	u32 mutual_refdata_addr;
+	u32 self_rawdata_addr;
+	u32 self_diffdata_addr;
+	u32 self_refdata_addr;
+	u32 iq_rawdata_addr;
+	u32 iq_refdata_addr;
+	u32 im_rawdata_addr;
+	u16 im_readata_len;
+	u32 noise_rawdata_addr;
+	u16 noise_rawdata_len;
+	u32 stylus_rawdata_addr;
+	u16 stylus_rawdata_len;
+	u32 noise_data_addr;
+	u32 esd_addr;
+};
+
+struct goodix_ic_info {
+	u16 length;
+	struct goodix_ic_info_version version;
+	struct goodix_ic_info_feature feature;
+	struct goodix_ic_info_param parm;
+	struct goodix_ic_info_misc misc;
+};
+#pragma pack()
+
+/*
+ * struct ts_rawdata_info
+ *
+ */
+#define TS_RAWDATA_BUFF_MAX             7000
+#define TS_RAWDATA_RESULT_MAX           100
+struct ts_rawdata_info {
+	int used_size; //fill in rawdata size
+	s16 buff[TS_RAWDATA_BUFF_MAX];
+	char result[TS_RAWDATA_RESULT_MAX];
+};
+
+/*
+ * struct goodix_module - external modules container
+ * @head: external modules list
+ * @initilized: whether this struct is initilized
+ * @mutex: mutex lock
+ * @wq: workqueue to do register work
+ * @core_data: core_data pointer
+ */
+struct goodix_module {
+	struct list_head head;
+	bool initilized;
+	struct mutex mutex;
+	struct workqueue_struct *wq;
+	struct goodix_ts_core *core_data;
+};
+
+/*
+ * struct goodix_ts_board_data -  board data
+ * @avdd_name: name of analoy regulator
+ * @iovdd_name: name of analoy regulator
+ * @reset_gpio: reset gpio number
+ * @irq_gpio: interrupt gpio number
+ * @irq_flag: irq trigger type
+ * @swap_axis: whether swaw x y axis
+ * @panel_max_x/y/w/p: resolution and size
+ * @invert_xy: invert x and y for inversely mounted IC
+ * @pannel_key_map: key map
+ * @fw_name: name of the firmware image
+ */
+struct goodix_ts_board_data {
+	char avdd_name[GOODIX_MAX_STR_LABLE_LEN];
+	char iovdd_name[GOODIX_MAX_STR_LABLE_LEN];
+	int reset_gpio;
+	int irq_gpio;
+	int avdd_gpio;
+	int iovdd_gpio;
+	unsigned int  irq_flags;
+
+	unsigned int swap_axis;
+	unsigned int panel_max_x;
+	unsigned int panel_max_y;
+	unsigned int panel_max_w; /*major and minor*/
+	unsigned int panel_max_p; /*pressure*/
+	bool invert_xy;
+
+	bool pen_enable;
+	char fw_name[GOODIX_MAX_STR_LABLE_LEN];
+	char cfg_bin_name[GOODIX_MAX_STR_LABLE_LEN];
+};
+
+enum goodix_fw_update_mode {
+	UPDATE_MODE_DEFAULT = 0,
+	UPDATE_MODE_FORCE = (1 << 0), /* force update mode */
+	UPDATE_MODE_BLOCK = (1 << 1), /* update in block mode */
+	UPDATE_MODE_FLASH_CFG = (1 << 2), /* reflash config */
+	UPDATE_MODE_SRC_SYSFS = (1 << 4), /* firmware file from sysfs */
+	UPDATE_MODE_SRC_HEAD = (1 << 5), /* firmware file from head file */
+	UPDATE_MODE_SRC_REQUEST = (1 << 6), /* request firmware */
+	UPDATE_MODE_SRC_ARGS = (1 << 7), /* firmware data from function args */
+};
+
+#define MAX_CMD_DATA_LEN 10
+#define MAX_CMD_BUF_LEN  16
+#pragma pack(1)
+struct goodix_ts_cmd {
+	union {
+		struct {
+			u8 state;
+			u8 ack;
+			u8 len;
+			u8 cmd;
+			u8 data[MAX_CMD_DATA_LEN];
+		};
+		u8 buf[MAX_CMD_BUF_LEN];
+	};
+};
+#pragma pack()
+
+/* interrupt event type */
+enum ts_event_type {
+	EVENT_INVALID = 0,
+	EVENT_TOUCH = (1 << 0), /* finger touch event */
+	EVENT_PEN = (1 << 1),   /* pen event */
+	EVENT_REQUEST = (1 << 2),
+	EVENT_GESTURE = (1 << 3),
+};
+
+enum ts_request_type {
+	REQUEST_TYPE_CONFIG = 1,
+	REQUEST_TYPE_RESET = 3,
+};
+
+/* notifier event */
+enum ts_notify_event {
+	NOTIFY_FWUPDATE_START,
+	NOTIFY_FWUPDATE_FAILED,
+	NOTIFY_FWUPDATE_SUCCESS,
+	NOTIFY_SUSPEND,
+	NOTIFY_RESUME,
+	NOTIFY_ESD_OFF,
+	NOTIFY_ESD_ON,
+	NOTIFY_CFG_BIN_FAILED,
+	NOTIFY_CFG_BIN_SUCCESS,
+};
+
+enum touch_point_status {
+	TS_NONE,
+	TS_RELEASE,
+	TS_TOUCH,
+};
+/* coordinate package */
+struct goodix_ts_coords {
+	int status; /* NONE, RELEASE, TOUCH */
+	unsigned int x, y, w, p;
+};
+
+struct goodix_pen_coords {
+	int status; /* NONE, RELEASE, TOUCH */
+	int tool_type;  /* BTN_TOOL_RUBBER BTN_TOOL_PEN */
+	unsigned int x, y, p;
+	signed char tilt_x;
+	signed char tilt_y;
+};
+
+/* touch event data */
+struct goodix_touch_data {
+	int touch_num;
+	struct goodix_ts_coords coords[GOODIX_MAX_TOUCH];
+};
+
+struct goodix_ts_key {
+	int status;
+	int code;
+};
+
+struct goodix_pen_data {
+	struct goodix_pen_coords coords;
+	struct goodix_ts_key keys[GOODIX_MAX_PEN_KEY];
+};
+
+/*
+ * struct goodix_ts_event - touch event struct
+ * @event_type: touch event type, touch data or
+ *	request event
+ * @event_data: event data
+ */
+struct goodix_ts_event {
+	enum ts_event_type event_type;
+	u8 request_code; /* represent the request type */
+	u8 gesture_type;
+	u8 gesture_data[GOODIX_GESTURE_DATA_LEN];
+	struct goodix_touch_data touch_data;
+	struct goodix_pen_data pen_data;
+};
+
+enum goodix_ic_bus_type {
+	GOODIX_BUS_TYPE_I2C,
+	GOODIX_BUS_TYPE_SPI,
+	GOODIX_BUS_TYPE_I3C,
+};
+
+struct goodix_bus_interface {
+	int bus_type;
+	int ic_type;
+	struct device *dev;
+	int (*read)(struct device *dev, unsigned int addr,
+			unsigned char *data, unsigned int len);
+	int (*write)(struct device *dev, unsigned int addr,
+			unsigned char *data, unsigned int len);
+};
+
+struct goodix_ts_hw_ops {
+	int (*power_on)(struct goodix_ts_core *cd, bool on);
+	int (*resume)(struct goodix_ts_core *cd);
+	int (*suspend)(struct goodix_ts_core *cd);
+	int (*gesture)(struct goodix_ts_core *cd, int gesture_type);
+	int (*reset)(struct goodix_ts_core *cd, int delay_ms);
+	int (*irq_enable)(struct goodix_ts_core *cd, bool enable);
+	int (*read)(struct goodix_ts_core *cd, unsigned int addr,
+		    unsigned char *data, unsigned int len);
+	int (*write)(struct goodix_ts_core *cd, unsigned int addr,
+		     unsigned char *data, unsigned int len);
+	int (*send_cmd)(struct goodix_ts_core *cd,
+			struct goodix_ts_cmd *cmd);
+	int (*send_config)(struct goodix_ts_core *cd,
+			u8 *config, int len);
+	int (*read_config)(struct goodix_ts_core *cd,
+			u8 *config_data, int size);
+	int (*read_version)(struct goodix_ts_core *cd,
+			struct goodix_fw_version *version);
+	int (*get_ic_info)(struct goodix_ts_core *cd,
+			struct goodix_ic_info *ic_info);
+	int (*esd_check)(struct goodix_ts_core *cd);
+	int (*event_handler)(struct goodix_ts_core *cd,
+			struct goodix_ts_event *ts_event);
+	int (*after_event_handler)(struct goodix_ts_core *cd);
+	int (*get_capacitance_data)(struct goodix_ts_core *cd,
+			struct ts_rawdata_info *info);
+};
+
+/*
+ * struct goodix_ts_esd - esd protector structure
+ * @esd_work: esd delayed work
+ * @esd_on: 1 - turn on esd protection, 0 - turn
+ *  off esd protection
+ */
+struct goodix_ts_esd {
+	bool irq_status;
+	atomic_t esd_on;
+	struct delayed_work esd_work;
+	struct notifier_block esd_notifier;
+	struct goodix_ts_core *ts_core;
+};
+
+enum goodix_core_init_stage {
+	CORE_UNINIT,
+	CORE_INIT_FAIL,
+	CORE_INIT_STAGE1,
+	CORE_INIT_STAGE2
+};
+
+struct goodix_ic_config {
+	int len;
+	u8 data[GOODIX_CFG_MAX_SIZE];
+};
+
+struct goodix_ts_core {
+	int init_stage;
+	struct platform_device *pdev;
+	struct goodix_fw_version fw_version;
+	struct goodix_ic_info ic_info;
+	struct goodix_bus_interface *bus;
+	struct goodix_ts_board_data board_data;
+	struct goodix_ts_hw_ops *hw_ops;
+	struct input_dev *input_dev;
+	struct input_dev *pen_dev;
+	/* TODO counld we remove this from core data? */
+	struct goodix_ts_event ts_event;
+
+	/* every pointer of this array represent a kind of config */
+	struct goodix_ic_config *ic_configs[GOODIX_MAX_CONFIG_GROUP];
+	struct regulator *avdd;
+	struct regulator *iovdd;
+	unsigned char gesture_type;
+
+	int power_on;
+	int irq;
+	size_t irq_trig_cnt;
+
+	atomic_t irq_enabled;
+	atomic_t suspended;
+	/* when this flag is true, driver should not clean the sync flag */
+	bool tools_ctrl_sync;
+
+	struct notifier_block ts_notifier;
+	struct goodix_ts_esd ts_esd;
+	bool esd_initialized;
+	bool ready;
+#if defined(CONFIG_DRM)
+	struct notifier_block fb_notifier;
+	void *notifier_cookie;
+	const char *touch_environment;
+#elif defined(CONFIG_FB)
+	struct notifier_block fb_notifier;
+#endif
+
+#ifdef CONFIG_GOODIX_TRUSTED_TOUCH
+	struct trusted_touch_vm_info *vm_info;
+	struct mutex fts_clk_io_ctrl_mutex;
+	struct completion trusted_touch_powerdown;
+	struct clk *core_clk;
+	struct clk *iface_clk;
+	atomic_t trusted_touch_initialized;
+	atomic_t trusted_touch_enabled;
+	atomic_t trusted_touch_transition;
+	atomic_t trusted_touch_event;
+	atomic_t trusted_touch_abort_status;
+	atomic_t delayed_vm_probe_pending;
+	atomic_t trusted_touch_mode;
+#endif
+	bool qts_en;
+	struct mutex tui_transition_lock;
+};
+
+/* external module structures */
+enum goodix_ext_priority {
+	EXTMOD_PRIO_RESERVED = 0,
+	EXTMOD_PRIO_FWUPDATE,
+	EXTMOD_PRIO_GESTURE,
+	EXTMOD_PRIO_HOTKNOT,
+	EXTMOD_PRIO_DBGTOOL,
+	EXTMOD_PRIO_DEFAULT,
+};
+
+#define EVT_HANDLED				0
+#define EVT_CONTINUE			0
+#define EVT_CANCEL				1
+#define EVT_CANCEL_IRQEVT		1
+#define EVT_CANCEL_SUSPEND		1
+#define EVT_CANCEL_RESUME		1
+#define EVT_CANCEL_RESET		1
+
+struct goodix_ext_module;
+/* external module's operations callback */
+struct goodix_ext_module_funcs {
+	int (*init)(struct goodix_ts_core *core_data,
+		    struct goodix_ext_module *module);
+	int (*exit)(struct goodix_ts_core *core_data,
+		    struct goodix_ext_module *module);
+	int (*before_reset)(struct goodix_ts_core *core_data,
+			    struct goodix_ext_module *module);
+	int (*after_reset)(struct goodix_ts_core *core_data,
+			   struct goodix_ext_module *module);
+	int (*before_suspend)(struct goodix_ts_core *core_data,
+			      struct goodix_ext_module *module);
+	int (*after_suspend)(struct goodix_ts_core *core_data,
+			     struct goodix_ext_module *module);
+	int (*before_resume)(struct goodix_ts_core *core_data,
+			     struct goodix_ext_module *module);
+	int (*after_resume)(struct goodix_ts_core *core_data,
+			    struct goodix_ext_module *module);
+	int (*irq_event)(struct goodix_ts_core *core_data,
+			 struct goodix_ext_module *module);
+};
+
+/*
+ * struct goodix_ext_module - external module struct
+ * @list: list used to link into modules manager
+ * @name: name of external module
+ * @priority: module priority vlaue, zero is invalid
+ * @funcs: operations callback
+ * @priv_data: private data region
+ * @kobj: kobject
+ * @work: used to queue one work to do registration
+ */
+struct goodix_ext_module {
+	struct list_head list;
+	char *name;
+	enum goodix_ext_priority priority;
+	const struct goodix_ext_module_funcs *funcs;
+	void *priv_data;
+	struct kobject kobj;
+	struct work_struct work;
+};
+
+/*
+ * struct goodix_ext_attribute - exteranl attribute struct
+ * @attr: attribute
+ * @show: show interface of external attribute
+ * @store: store interface of external attribute
+ */
+struct goodix_ext_attribute {
+	struct attribute attr;
+	ssize_t (*show)(struct goodix_ext_module *module, char *buf);
+	ssize_t (*store)(struct goodix_ext_module *module,
+			const char *buf, size_t len);
+};
+
+/* external attrs helper macro */
+#define __EXTMOD_ATTR(_name, _mode, _show, _store)	{	\
+	.attr = {.name = __stringify(_name), .mode = _mode },	\
+	.show  = _show,	\
+	.store = _store,	\
+}
+
+/* external attrs helper macro, used to define external attrs */
+#define DEFINE_EXTMOD_ATTR(_name, _mode, _show, _store)	\
+static struct goodix_ext_attribute ext_attr_##_name = \
+	__EXTMOD_ATTR(_name, _mode, _show, _store)
+
+/* log macro */
+extern bool debug_log_flag;
+#define ts_info(fmt, arg...) \
+		pr_info("[GTP-INF][%s:%d] "fmt"\n", __func__, __LINE__, ##arg)
+#define	ts_err(fmt, arg...) \
+		pr_err("[GTP-ERR][%s:%d] "fmt"\n", __func__, __LINE__, ##arg)
+#define ts_debug(fmt, arg...) \
+		{if (debug_log_flag) \
+		pr_info("[GTP-DBG][%s:%d] "fmt"\n", __func__, __LINE__, ##arg);}
+
+/*
+ * get board data pointer
+ */
+static inline struct goodix_ts_board_data *board_data(
+		struct goodix_ts_core *core)
+{
+	if (!core)
+		return NULL;
+	return &(core->board_data);
+}
+
+/**
+ * goodix_register_ext_module - interface for external module
+ * to register into touch core modules structure
+ *
+ * @module: pointer to external module to be register
+ * return: 0 ok, <0 failed
+ */
+int goodix_register_ext_module(struct goodix_ext_module *module);
+/* register module no wait */
+int goodix_register_ext_module_no_wait(struct goodix_ext_module *module);
+/**
+ * goodix_unregister_ext_module - interface for external module
+ * to unregister external modules
+ *
+ * @module: pointer to external module
+ * return: 0 ok, <0 failed
+ */
+int goodix_unregister_ext_module(struct goodix_ext_module *module);
+/* remove all registered ext module
+ * return 0 on success, otherwise return < 0
+ */
+int goodix_ts_blocking_notify(enum ts_notify_event evt, void *v);
+struct kobj_type *goodix_get_default_ktype(void);
+struct kobject *goodix_get_default_kobj(void);
+
+struct goodix_ts_hw_ops *goodix_get_hw_ops(void);
+int goodix_get_config_proc(struct goodix_ts_core *cd);
+
+int goodix_spi_bus_init(void);
+void goodix_spi_bus_exit(void);
+int goodix_i2c_bus_init(void);
+void goodix_i2c_bus_exit(void);
+
+u32 goodix_append_checksum(u8 *data, int len, int mode);
+int checksum_cmp(const u8 *data, int size, int mode);
+int is_risk_data(const u8 *data, int size);
+u32 goodix_get_file_config_id(u8 *ic_config);
+void goodix_rotate_abcd2cbad(int tx, int rx, s16 *data);
+
+int goodix_fw_update_init(struct goodix_ts_core *core_data);
+void goodix_fw_update_uninit(void);
+int goodix_do_fw_update(struct goodix_ic_config *ic_config, int mode);
+
+int goodix_get_ic_type(struct device_node *node);
+int gesture_module_init(void);
+void gesture_module_exit(void);
+int inspect_module_init(void);
+void inspect_module_exit(void);
+int goodix_tools_init(void);
+void goodix_tools_exit(void);
+int goodix_get_touch_type(struct device_node *np);
+
+/* goodix FB test */
+/*
+void goodix_fb_ext_ctrl(int suspend);
+*/
+
+#endif

+ 446 - 0
qcom/opensource/touch-drivers/goodix_berlin_driver/goodix_ts_gesture.c

@@ -0,0 +1,446 @@
+/*
+ * Goodix Gesture Module
+ *
+ * Copyright (C) 2019 - 2020 Goodix, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be a reference
+ * to you, when you are integrating the GOODiX's CTP IC into your system,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+#include <linux/spinlock.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/input.h>
+#include <linux/platform_device.h>
+#include <linux/version.h>
+#include <linux/delay.h>
+#include <linux/atomic.h>
+#include <linux/input/mt.h>
+#include "goodix_ts_core.h"
+
+
+#define GOODIX_GESTURE_DOUBLE_TAP		0xCC
+#define GOODIX_GESTURE_SINGLE_TAP		0x4C
+#define GOODIX_GESTURE_FOD_DOWN			0x46
+#define GOODIX_GESTURE_FOD_UP			0x55
+
+/*
+ * struct gesture_module - gesture module data
+ * @registered: module register state
+ * @sysfs_node_created: sysfs node state
+ * @gesture_type: valid gesture type, each bit represent one gesture type
+ * @gesture_data: store latest gesture code get from irq event
+ * @gesture_ts_cmd: gesture command data
+ */
+struct gesture_module {
+	atomic_t registered;
+	struct goodix_ts_core *ts_core;
+	struct goodix_ext_module module;
+};
+
+static struct gesture_module *gsx_gesture; /*allocated in gesture init module*/
+static bool module_initialized;
+
+static ssize_t gsx_double_type_show(struct goodix_ext_module *module,
+		char *buf)
+{
+	struct gesture_module *gsx = module->priv_data;
+	unsigned char type = gsx->ts_core->gesture_type;
+
+	if (!gsx)
+		return -EIO;
+
+	if (atomic_read(&gsx->registered) == 0) {
+		ts_err("gesture module is not registered");
+		return 0;
+	}
+
+	return scnprintf(buf, PAGE_SIZE, "%s\n",
+			(type & GESTURE_DOUBLE_TAP) ? "enable" : "disable");
+}
+
+static ssize_t gsx_double_type_store(struct goodix_ext_module *module,
+		const char *buf, size_t count)
+{
+	struct gesture_module *gsx = module->priv_data;
+
+	if (!gsx)
+		return -EIO;
+
+	if (atomic_read(&gsx->registered) == 0) {
+		ts_err("gesture module is not registered");
+		return 0;
+	}
+
+	if (buf[0] == '1') {
+		ts_info("enable double tap");
+		gsx->ts_core->gesture_type |= GESTURE_DOUBLE_TAP;
+	} else if (buf[0] == '0') {
+		ts_info("disable double tap");
+		gsx->ts_core->gesture_type &= ~GESTURE_DOUBLE_TAP;
+	} else
+		ts_err("invalid cmd[%d]", buf[0]);
+
+	return count;
+}
+
+static ssize_t gsx_single_type_show(struct goodix_ext_module *module,
+		char *buf)
+{
+	struct gesture_module *gsx = module->priv_data;
+	unsigned char type = gsx->ts_core->gesture_type;
+
+	if (!gsx)
+		return -EIO;
+
+	if (atomic_read(&gsx->registered) == 0) {
+		ts_err("gesture module is not registered");
+		return 0;
+	}
+
+	return scnprintf(buf, PAGE_SIZE, "%s\n",
+			(type & GESTURE_SINGLE_TAP) ? "enable" : "disable");
+}
+
+static ssize_t gsx_single_type_store(struct goodix_ext_module *module,
+		const char *buf, size_t count)
+{
+	struct gesture_module *gsx = module->priv_data;
+
+	if (!gsx)
+		return -EIO;
+
+	if (atomic_read(&gsx->registered) == 0) {
+		ts_err("gesture module is not registered");
+		return 0;
+	}
+
+	if (buf[0] == '1') {
+		ts_info("enable single tap");
+		gsx->ts_core->gesture_type |= GESTURE_SINGLE_TAP;
+	} else if (buf[0] == '0') {
+		ts_info("disable single tap");
+		gsx->ts_core->gesture_type &= ~GESTURE_SINGLE_TAP;
+	} else
+		ts_err("invalid cmd[%d]", buf[0]);
+
+	return count;
+}
+
+static ssize_t gsx_fod_type_show(struct goodix_ext_module *module,
+		char *buf)
+{
+	struct gesture_module *gsx = module->priv_data;
+	unsigned char type = gsx->ts_core->gesture_type;
+
+	if (!gsx)
+		return -EIO;
+
+	if (atomic_read(&gsx->registered) == 0) {
+		ts_err("gesture module is not registered");
+		return 0;
+	}
+
+	return scnprintf(buf, PAGE_SIZE, "%s\n",
+			(type & GESTURE_FOD_PRESS) ? "enable" : "disable");
+}
+
+static ssize_t gsx_fod_type_store(struct goodix_ext_module *module,
+		const char *buf, size_t count)
+{
+	struct gesture_module *gsx = module->priv_data;
+
+	if (!gsx)
+		return -EIO;
+
+	if (atomic_read(&gsx->registered) == 0) {
+		ts_err("gesture module is not registered");
+		return 0;
+	}
+
+	if (buf[0] == '1') {
+		ts_info("enable fod");
+		gsx->ts_core->gesture_type |= GESTURE_FOD_PRESS;
+	} else if (buf[0] == '0') {
+		ts_info("disable fod");
+		gsx->ts_core->gesture_type &= ~GESTURE_FOD_PRESS;
+	} else
+		ts_err("invalid cmd[%d]", buf[0]);
+
+	return count;
+}
+
+
+const struct goodix_ext_attribute gesture_attrs[] = {
+	__EXTMOD_ATTR(double_en, 0664,
+			gsx_double_type_show, gsx_double_type_store),
+	__EXTMOD_ATTR(single_en, 0664,
+			gsx_single_type_show, gsx_single_type_store),
+	__EXTMOD_ATTR(fod_en, 0664,
+			gsx_fod_type_show, gsx_fod_type_store),
+};
+
+static int gsx_gesture_init(struct goodix_ts_core *cd,
+		struct goodix_ext_module *module)
+{
+	struct gesture_module *gsx = module->priv_data;
+
+	if (!cd || !cd->hw_ops->gesture) {
+		ts_err("gesture unsupported");
+		return -EINVAL;
+	}
+
+	gsx->ts_core = cd;
+	gsx->ts_core->gesture_type = 0;
+	atomic_set(&gsx->registered, 1);
+
+	return 0;
+}
+
+static int gsx_gesture_exit(struct goodix_ts_core *cd,
+		struct goodix_ext_module *module)
+{
+	struct gesture_module *gsx = module->priv_data;
+
+	if (!cd || !cd->hw_ops->gesture) {
+		ts_err("gesture unsupported");
+		return -EINVAL;
+	}
+
+	atomic_set(&gsx->registered, 0);
+
+	return 0;
+}
+
+/**
+ * gsx_gesture_ist - Gesture Irq handle
+ * This functions is excuted when interrupt happended and
+ * ic in doze mode.
+ *
+ * @cd: pointer to touch core data
+ * @module: pointer to goodix_ext_module struct
+ * return: 0 goon execute, EVT_CANCEL_IRQEVT  stop execute
+ */
+static int gsx_gesture_ist(struct goodix_ts_core *cd,
+	struct goodix_ext_module *module)
+{
+	struct goodix_ts_hw_ops *hw_ops = cd->hw_ops;
+	struct goodix_ts_event gs_event = {0};
+	int fodx, fody, overlay_area;
+	int ret;
+
+	if (atomic_read(&cd->suspended) == 0 || cd->gesture_type == 0)
+		return EVT_CONTINUE;
+
+	ret = hw_ops->event_handler(cd, &gs_event);
+	if (ret) {
+		ts_err("failed get gesture data");
+		goto re_send_ges_cmd;
+	}
+
+	if (!(gs_event.event_type & EVENT_GESTURE)) {
+		ts_err("invalid event type: 0x%x",
+			cd->ts_event.event_type);
+		goto re_send_ges_cmd;
+	}
+
+	switch (gs_event.gesture_type) {
+	case GOODIX_GESTURE_SINGLE_TAP:
+		if (cd->gesture_type & GESTURE_SINGLE_TAP) {
+			ts_info("get SINGLE-TAP gesture");
+			input_report_key(cd->input_dev, KEY_WAKEUP, 1);
+			// input_report_key(cd->input_dev, KEY_GOTO, 1);
+			input_sync(cd->input_dev);
+			input_report_key(cd->input_dev, KEY_WAKEUP, 0);
+			// input_report_key(cd->input_dev, KEY_GOTO, 0);
+			input_sync(cd->input_dev);
+		} else {
+			ts_debug("not enable SINGLE-TAP");
+		}
+		break;
+	case GOODIX_GESTURE_DOUBLE_TAP:
+		if (cd->gesture_type & GESTURE_DOUBLE_TAP) {
+			ts_info("get DOUBLE-TAP gesture");
+			input_report_key(cd->input_dev, KEY_WAKEUP, 1);
+			input_sync(cd->input_dev);
+			input_report_key(cd->input_dev, KEY_WAKEUP, 0);
+			input_sync(cd->input_dev);
+		} else {
+			ts_debug("not enable DOUBLE-TAP");
+		}
+		break;
+	case GOODIX_GESTURE_FOD_DOWN:
+		if (cd->gesture_type & GESTURE_FOD_PRESS) {
+			ts_info("get FOD-DOWN gesture");
+			fodx = le16_to_cpup((__le16 *)gs_event.gesture_data);
+			fody = le16_to_cpup((__le16 *)(gs_event.gesture_data + 2));
+			overlay_area = gs_event.gesture_data[4];
+			ts_debug("fodx:%d fody:%d overlay_area:%d", fodx, fody, overlay_area);
+			input_report_key(cd->input_dev, BTN_TOUCH, 1);
+			input_mt_slot(cd->input_dev, 0);
+			input_mt_report_slot_state(cd->input_dev, MT_TOOL_FINGER, 1);
+			input_report_abs(cd->input_dev, ABS_MT_POSITION_X, fodx);
+			input_report_abs(cd->input_dev, ABS_MT_POSITION_Y, fody);
+			input_report_abs(cd->input_dev, ABS_MT_WIDTH_MAJOR, overlay_area);
+			input_sync(cd->input_dev);
+		} else {
+			ts_debug("not enable FOD-DOWN");
+		}
+		break;
+	case GOODIX_GESTURE_FOD_UP:
+		if (cd->gesture_type & GESTURE_FOD_PRESS) {
+			ts_info("get FOD-UP gesture");
+			fodx = le16_to_cpup((__le16 *)gs_event.gesture_data);
+			fody = le16_to_cpup((__le16 *)(gs_event.gesture_data + 2));
+			overlay_area = gs_event.gesture_data[4];
+			input_report_key(cd->input_dev, BTN_TOUCH, 0);
+			input_mt_slot(cd->input_dev, 0);
+			input_mt_report_slot_state(cd->input_dev,
+					MT_TOOL_FINGER, 0);
+			input_sync(cd->input_dev);
+		} else {
+			ts_debug("not enable FOD-UP");
+		}
+		break;
+	default:
+		ts_err("not support gesture type[%02X]", gs_event.gesture_type);
+		break;
+	}
+
+re_send_ges_cmd:
+	if (hw_ops->gesture(cd, 0))
+		ts_info("warning: failed re_send gesture cmd");
+	return EVT_CANCEL_IRQEVT;
+}
+
+/**
+ * gsx_gesture_before_suspend - execute gesture suspend routine
+ * This functions is excuted to set ic into doze mode
+ *
+ * @cd: pointer to touch core data
+ * @module: pointer to goodix_ext_module struct
+ * return: 0 goon execute, EVT_IRQCANCLED  stop execute
+ */
+static int gsx_gesture_before_suspend(struct goodix_ts_core *cd,
+	struct goodix_ext_module *module)
+{
+	int ret;
+	const struct goodix_ts_hw_ops *hw_ops = cd->hw_ops;
+
+	if (cd->gesture_type == 0)
+		return EVT_CONTINUE;
+
+	ret = hw_ops->gesture(cd, 0);
+	if (ret)
+		ts_err("failed enter gesture mode");
+	else
+		ts_info("enter gesture mode, type[0x%02X]", cd->gesture_type);
+
+	hw_ops->irq_enable(cd, true);
+	enable_irq_wake(cd->irq);
+
+	return EVT_CANCEL_SUSPEND;
+}
+
+static int gsx_gesture_before_resume(struct goodix_ts_core *cd,
+	struct goodix_ext_module *module)
+{
+	const struct goodix_ts_hw_ops *hw_ops = cd->hw_ops;
+
+	if (cd->gesture_type == 0)
+		return EVT_CONTINUE;
+
+	disable_irq_wake(cd->irq);
+	hw_ops->reset(cd, GOODIX_NORMAL_RESET_DELAY_MS);
+
+	return EVT_CANCEL_RESUME;
+}
+
+static struct goodix_ext_module_funcs gsx_gesture_funcs = {
+	.irq_event = gsx_gesture_ist,
+	.init = gsx_gesture_init,
+	.exit = gsx_gesture_exit,
+	.before_suspend = gsx_gesture_before_suspend,
+	.before_resume = gsx_gesture_before_resume,
+};
+
+int gesture_module_init(void)
+{
+	int ret;
+	int i;
+	struct kobject *def_kobj = goodix_get_default_kobj();
+	struct kobj_type *def_kobj_type = goodix_get_default_ktype();
+
+	gsx_gesture = kzalloc(sizeof(struct gesture_module), GFP_KERNEL);
+	if (!gsx_gesture)
+		return -ENOMEM;
+
+	gsx_gesture->module.funcs = &gsx_gesture_funcs;
+	gsx_gesture->module.priority = EXTMOD_PRIO_GESTURE;
+	gsx_gesture->module.name = "Goodix_gsx_gesture";
+	gsx_gesture->module.priv_data = gsx_gesture;
+
+	atomic_set(&gsx_gesture->registered, 0);
+
+	/* gesture sysfs init */
+	ret = kobject_init_and_add(&gsx_gesture->module.kobj,
+			def_kobj_type, def_kobj, "gesture");
+	if (ret) {
+		ts_err("failed create gesture sysfs node!");
+		goto err_out;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(gesture_attrs) && !ret; i++)
+		ret = sysfs_create_file(&gsx_gesture->module.kobj,
+				&gesture_attrs[i].attr);
+	if (ret) {
+		ts_err("failed create gst sysfs files");
+		while (--i >= 0)
+			sysfs_remove_file(&gsx_gesture->module.kobj,
+					&gesture_attrs[i].attr);
+
+		kobject_put(&gsx_gesture->module.kobj);
+		goto err_out;
+	}
+
+	module_initialized = true;
+	goodix_register_ext_module_no_wait(&gsx_gesture->module);
+	ts_info("gesture module init success");
+
+	return 0;
+
+err_out:
+	ts_err("gesture module init failed!");
+	kfree(gsx_gesture);
+	return ret;
+}
+
+void gesture_module_exit(void)
+{
+	int i;
+
+	ts_info("gesture module exit");
+	if (!module_initialized)
+		return;
+
+	goodix_unregister_ext_module(&gsx_gesture->module);
+
+	/* deinit sysfs */
+	for (i = 0; i < ARRAY_SIZE(gesture_attrs); i++)
+		sysfs_remove_file(&gsx_gesture->module.kobj,
+					&gesture_attrs[i].attr);
+
+	kobject_put(&gsx_gesture->module.kobj);
+	kfree(gsx_gesture);
+	module_initialized = false;
+}

+ 2981 - 0
qcom/opensource/touch-drivers/goodix_berlin_driver/goodix_ts_inspect.c

@@ -0,0 +1,2981 @@
+/*
+ * Goodix Touchscreen Driver
+ * Copyright (C) 2020 - 2021 Goodix, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be a reference
+ * to you, when you are integrating the GOODiX's CTP IC into your system,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ */
+
+#include "goodix_ts_core.h"
+#include <linux/rtc.h>
+#include <linux/timer.h>
+#include <linux/version.h>
+#include <linux/fs.h>
+#include <asm/uaccess.h>
+
+
+/* test config */
+#define TOTAL_FRAME_NUM 					1 /* rawdata test frames */
+#define NOISEDATA_TEST_TIMES				1  /* noise test frames */
+// #define SAVE_IN_CSV
+
+#define GOODIX_RESULT_SAVE_PATH				"/vendor/etc/Test_Data.csv"
+#define GOODIX_TEST_FILE_NAME				"goodix"
+#define MAX_DATA_BUFFER						28000
+#define MAX_SHORT_NUM						15
+#define MAX_LINE_LEN                		(1024 * 3 * 7)
+#define MAX_DRV_NUM							52
+#define MAX_SEN_NUM							75
+
+#define STATISTICS_DATA_LEN					32
+#define MAX_STR_LEN				 			32
+#define MAX_TEST_ITEMS			    		10 /* 0P-1P-2P-3P-5P total test items */
+#define GTP_CAP_TEST						1
+#define GTP_DELTA_TEST						2
+#define GTP_NOISE_TEST						3
+#define GTP_SHORT_TEST						5
+#define GTP_SELFCAP_TEST					6
+#define GTP_SELFNOISE_TEST					7
+
+#define GTP_TEST_PASS						1 
+#define GTP_PANEL_REASON					2
+#define SYS_SOFTWARE_REASON					3
+
+#define CHN_VDD								0xFF
+#define CHN_GND								0x7F
+#define DRV_CHANNEL_FLAG		    		0x80
+
+#define CSV_TP_SPECIAL_RAW_MIN				"special_raw_min"
+#define CSV_TP_SPECIAL_RAW_MAX				"special_raw_max"
+#define CSV_TP_SPECIAL_RAW_DELTA			"special_raw_delta"
+#define CSV_TP_SHORT_THRESHOLD				"shortciurt_threshold"
+#define CSV_TP_SPECIAL_SELFRAW_MAX			"special_selfraw_max"
+#define CSV_TP_SPECIAL_SELFRAW_MIN			"special_selfraw_min"
+#define CSV_TP_NOISE_LIMIT					"noise_data_limit"
+#define CSV_TP_SELFNOISE_LIMIT				"noise_selfdata_limit"
+#define CSV_TP_TEST_CONFIG					"test_config"
+
+#define MAX_TEST_TIME_MS            		15000
+#define DEFAULT_TEST_TIME_MS				7000
+
+/* berlin A */
+#define MAX_DRV_NUM_BRA				    	21
+#define MAX_SEN_NUM_BRA				    	42
+#define SHORT_TEST_TIME_REG_BRA				0x11FF2
+#define DFT_ADC_DUMP_NUM_BRA				1396
+#define DFT_SHORT_THRESHOLD_BRA  			16
+#define DFT_DIFFCODE_SHORT_THRESHOLD_BRA	16
+#define SHORT_TEST_STATUS_REG_BRA			0x10400
+#define SHORT_TEST_RESULT_REG_BRA			0x10410
+#define DRV_DRV_SELFCODE_REG_BRA			0x1045E
+#define SEN_SEN_SELFCODE_REG_BRA 			0x1084E
+#define DRV_SEN_SELFCODE_REG_BRA			0x11712
+#define DIFF_CODE_DATA_REG_BRA				0x11F72
+
+/* berlin B */
+#define MAX_DRV_NUM_BRB				    	52
+#define MAX_SEN_NUM_BRB				    	75
+#define SHORT_TEST_TIME_REG_BRB				0x26AE0
+#define DFT_ADC_DUMP_NUM_BRB				762
+#define DFT_SHORT_THRESHOLD_BRB				100
+#define DFT_DIFFCODE_SHORT_THRESHOLD_BRB	32
+#define SHORT_TEST_STATUS_REG_BRB			0x20400
+#define SHORT_TEST_RESULT_REG_BRB			0x20410
+#define DRV_DRV_SELFCODE_REG_BRB			0x2049A
+#define SEN_SEN_SELFCODE_REG_BRB 			0x21AF2
+#define DRV_SEN_SELFCODE_REG_BRB			0x248A6
+#define DIFF_CODE_DATA_REG_BRB				0x269E0
+
+/* berlinD */
+#define MAX_DRV_NUM_BRD				    	20
+#define MAX_SEN_NUM_BRD				    	40
+#define SHORT_TEST_TIME_REG_BRD				0x14D7A
+#define DFT_ADC_DUMP_NUM_BRD				762
+#define DFT_SHORT_THRESHOLD_BRD				100
+#define DFT_DIFFCODE_SHORT_THRESHOLD_BRD	32
+#define SHORT_TEST_STATUS_REG_BRD			0x13400
+#define SHORT_TEST_RESULT_REG_BRD			0x13408
+#define DRV_DRV_SELFCODE_REG_BRD			0x1344E
+#define SEN_SEN_SELFCODE_REG_BRD 			0x137E6
+#define DRV_SEN_SELFCODE_REG_BRD			0x14556
+#define DIFF_CODE_DATA_REG_BRD				0x14D00
+
+
+#define ABS(val)			((val < 0)? -(val) : val)
+#define MAX(a, b)			((a > b)? a : b)
+
+static bool module_initialized;
+
+/* berlin A drv-sen map */
+static u8 brl_a_drv_map[] = {
+	42, 43, 44, 45, 46, 47, 48, 49,
+	50, 51, 52, 53, 54, 55, 56, 57,
+	58, 59, 60, 61, 62
+};
+
+static u8 brl_a_sen_map[] = {
+	0, 1, 2, 3, 4, 5, 6, 7,
+	8, 9, 10, 11, 12, 13, 14, 15,
+	16, 17, 18, 19, 20, 21, 22, 23,
+	24, 25, 26, 27, 28, 29, 30, 31,
+	32, 33, 34, 35, 36, 37, 38, 39,
+	40, 41
+};
+
+/* berlin B drv-sen map */
+static u8 brl_b_drv_map[] = {
+	75, 76, 77, 78, 79, 80, 81, 82,
+	83, 84, 85, 86, 87, 88, 89, 90,
+	91, 92, 93, 94, 95, 96, 97, 98,
+	99, 100, 101, 102, 103, 104, 105,
+	106, 107, 108, 109, 110, 111, 112,
+	113, 114, 115, 116, 117, 118, 119,
+	120, 121, 122, 123, 124, 125, 126
+};
+
+static u8 brl_b_sen_map[] = {
+	0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+	11, 12, 13, 14, 15, 16, 17, 18,
+	19, 20, 21, 22, 23, 24, 25, 26,
+	27, 28, 29, 30, 31, 32, 33, 34,
+	35, 36, 37, 38, 39, 40, 41, 42,
+	43, 44, 45, 46, 47, 48, 49, 50,
+	51, 52, 53, 54, 55, 56, 57, 58,
+	59, 60, 61, 62, 63, 64, 65, 66,
+	67, 68, 69, 70, 71, 72, 73, 74
+};
+
+/* berlin D drv-sen map */
+static u8 brl_d_drv_map[] = {
+	40, 41, 42, 43, 44, 45, 46, 47,
+	48, 49, 50, 51, 52, 53, 54, 55,
+	56, 57, 58, 59,
+};
+
+static u8 brl_d_sen_map[] = {
+	0, 1, 2, 3, 4, 5, 6, 7,
+	8, 9, 10, 11, 12, 13, 14, 15,
+	16, 17, 18, 19, 20, 21, 22, 23,
+	24, 25, 26, 27, 28, 29, 30, 31,
+	32, 33, 34, 35, 36, 37, 38, 39,
+};
+
+typedef struct __attribute__((packed)) {
+	u8 result;
+	u8 drv_drv_num;
+	u8 sen_sen_num;
+	u8 drv_sen_num;
+	u8 drv_gnd_avdd_num;
+	u8 sen_gnd_avdd_num;
+	u16 checksum;
+} test_result_t;
+
+struct params_info_t {
+	u32 max_drv_num;
+	u32 max_sen_num;
+	u8 *drv_map;
+	u8 *sen_map;
+	u32 short_test_time_reg;
+	u32 short_test_status_reg;
+	u32 short_test_result_reg;
+	u32 drv_drv_selfcode_reg;
+	u32 sen_sen_selfcode_reg;
+	u32 drv_sen_selfcode_reg;
+	u32 diffcode_data_reg;
+	u16 short_test_dump_num;
+	u16 dft_short_threshold;
+	u16 short_diffcode_threshold;
+};
+
+struct params_info_t params_bra = {
+	MAX_DRV_NUM_BRA,
+	MAX_SEN_NUM_BRA,
+	brl_a_drv_map,
+	brl_a_sen_map,
+	SHORT_TEST_TIME_REG_BRA,
+	SHORT_TEST_STATUS_REG_BRA,
+	SHORT_TEST_RESULT_REG_BRA,
+	DRV_DRV_SELFCODE_REG_BRA,
+	SEN_SEN_SELFCODE_REG_BRA,
+	DRV_SEN_SELFCODE_REG_BRA,
+	DIFF_CODE_DATA_REG_BRA,
+	DFT_ADC_DUMP_NUM_BRA,
+	DFT_SHORT_THRESHOLD_BRA,
+	DFT_DIFFCODE_SHORT_THRESHOLD_BRA,
+};
+
+struct params_info_t params_brb = {
+	MAX_DRV_NUM_BRB,
+	MAX_SEN_NUM_BRB,
+	brl_b_drv_map,
+	brl_b_sen_map,
+	SHORT_TEST_TIME_REG_BRB,
+	SHORT_TEST_STATUS_REG_BRB,
+	SHORT_TEST_RESULT_REG_BRB,
+	DRV_DRV_SELFCODE_REG_BRB,
+	SEN_SEN_SELFCODE_REG_BRB,
+	DRV_SEN_SELFCODE_REG_BRB,
+	DIFF_CODE_DATA_REG_BRB,
+	DFT_ADC_DUMP_NUM_BRB,
+	DFT_SHORT_THRESHOLD_BRB,
+	DFT_DIFFCODE_SHORT_THRESHOLD_BRB,
+};
+
+struct params_info_t params_brd = {
+	MAX_DRV_NUM_BRD,
+	MAX_SEN_NUM_BRD,
+	brl_d_drv_map,
+	brl_d_sen_map,
+	SHORT_TEST_TIME_REG_BRD,
+	SHORT_TEST_STATUS_REG_BRD,
+	SHORT_TEST_RESULT_REG_BRD,
+	DRV_DRV_SELFCODE_REG_BRD,
+	SEN_SEN_SELFCODE_REG_BRD,
+	DRV_SEN_SELFCODE_REG_BRD,
+	DIFF_CODE_DATA_REG_BRD,
+	DFT_ADC_DUMP_NUM_BRD,
+	DFT_SHORT_THRESHOLD_BRD,
+	DFT_DIFFCODE_SHORT_THRESHOLD_BRD,
+};
+
+struct ts_test_params {
+	bool test_items[MAX_TEST_ITEMS];
+
+	u32 rawdata_addr;
+	u32 noisedata_addr;
+	u32 self_rawdata_addr;
+	u32 self_noisedata_addr;
+
+	u32 drv_num;
+	u32 sen_num;
+
+	struct params_info_t *params_info;
+
+	s32 cfg_buf[GOODIX_CFG_MAX_SIZE];
+	s32 max_limits[MAX_DRV_NUM * MAX_SEN_NUM];
+	s32 min_limits[MAX_DRV_NUM * MAX_SEN_NUM];
+	s32 deviation_limits[MAX_DRV_NUM * MAX_SEN_NUM];
+	s32 self_max_limits[MAX_DRV_NUM + MAX_SEN_NUM];
+	s32 self_min_limits[MAX_DRV_NUM + MAX_SEN_NUM];
+	s32 noise_threshold;
+	s32 self_noise_threshold;
+
+	u32 short_threshold;
+	u32 r_drv_drv_threshold;
+	u32 r_drv_sen_threshold;
+	u32 r_sen_sen_threshold;
+	u32 r_drv_gnd_threshold;
+	u32 r_sen_gnd_threshold;
+	u32 avdd_value;
+};
+
+struct ts_test_rawdata {
+	s16 data[MAX_DRV_NUM * MAX_SEN_NUM];
+	u32 size;
+};
+
+struct ts_test_self_rawdata {
+	s16 data[MAX_DRV_NUM + MAX_SEN_NUM];
+	u32 size;
+};
+
+struct ts_short_res {
+	u8 short_num;
+	s16 short_msg[4 * MAX_SHORT_NUM];
+};
+
+struct ts_open_res {
+	u8 beyond_max_limit_cnt[MAX_DRV_NUM * MAX_SEN_NUM];
+	u8 beyond_min_limit_cnt[MAX_DRV_NUM * MAX_SEN_NUM];
+	u8 beyond_accord_limit_cnt[MAX_DRV_NUM * MAX_SEN_NUM];
+};
+
+struct goodix_ts_test {
+	struct goodix_ts_core *ts;
+	struct ts_test_params test_params;
+	struct ts_test_rawdata rawdata[TOTAL_FRAME_NUM];
+	struct ts_test_rawdata accord_arr[TOTAL_FRAME_NUM];
+	struct ts_test_rawdata noisedata[NOISEDATA_TEST_TIMES];
+	struct goodix_ic_config test_config;
+	struct ts_test_self_rawdata self_rawdata;
+	struct ts_test_self_rawdata self_noisedata;
+	struct ts_short_res short_res;
+	struct ts_open_res open_res;
+
+	/*[0][0][0][0][0]..  0 without test; 1 pass, 2 panel failed; 3 software failed */
+	char test_result[MAX_TEST_ITEMS];
+	char test_info[TS_RAWDATA_RESULT_MAX];
+};
+
+static int cal_cha_to_cha_res(struct goodix_ts_test *ts_test, int v1, int v2)
+{
+	if (ts_test->ts->bus->ic_type == IC_TYPE_BERLIN_A)
+		return (v1 - v2) * 63 / v2;
+	else if (ts_test->ts->bus->ic_type == IC_TYPE_BERLIN_B)
+		return (v1 - v2) * 74 / v2 + 20;
+	else
+		return (v1 / v2 - 1) * 70 + 59;
+}
+
+static int cal_cha_to_avdd_res(struct goodix_ts_test *ts_test, int v1, int v2)
+{
+	if (ts_test->ts->bus->ic_type == IC_TYPE_BERLIN_A)
+		return 64 * (2 * v2 - 25) * 40 / v1 - 40;
+	else if (ts_test->ts->bus->ic_type == IC_TYPE_BERLIN_B)
+		return 64 * (2 * v2 - 25) * 99 / v1 - 60;
+	else
+		return 64 * (2 * v2 - 25) * 93 / v1 - 20;
+}
+
+static int cal_cha_to_gnd_res(struct goodix_ts_test *ts_test, int v)
+{
+	if (ts_test->ts->bus->ic_type == IC_TYPE_BERLIN_A)
+		return 64148 / v - 40;
+	else if (ts_test->ts->bus->ic_type == IC_TYPE_BERLIN_B)
+		return 150500 / v - 60;
+	else
+		return 145000 / v - 15;
+}
+
+static int ts_test_reset(struct goodix_ts_test *ts_test, u32 delay_ms)
+{
+	return ts_test->ts->hw_ops->reset(ts_test->ts, delay_ms);
+}
+
+static int ts_test_read(struct goodix_ts_test *ts_test,
+	u32 addr, u8 *data, u32 len)
+{
+	return ts_test->ts->hw_ops->read(ts_test->ts, addr, data, len);
+}
+
+static int ts_test_write(struct goodix_ts_test *ts_test,
+	u32 addr, u8 *data, u32 len)
+{
+	return ts_test->ts->hw_ops->write(ts_test->ts, addr, data, len);
+}
+
+static int ts_test_send_cmd(struct goodix_ts_test *ts_test,
+	struct goodix_ts_cmd *cmd)
+{
+	return ts_test->ts->hw_ops->send_cmd(ts_test->ts, cmd);
+}
+
+static int ts_test_irq_enable(struct goodix_ts_test *ts_test, bool flag)
+{
+	return ts_test->ts->hw_ops->irq_enable(ts_test->ts, flag);
+}
+
+static int ts_test_send_config(struct goodix_ts_test *ts_test, int type)
+{
+	struct goodix_ic_config *cfg;
+
+	if (type >= GOODIX_MAX_CONFIG_GROUP) {
+		ts_err("unsupproted config type %d", type);
+		return -EINVAL;
+	}
+	cfg = ts_test->ts->ic_configs[type];
+	if (!cfg || cfg->len <= 0) {
+		ts_err("no valid normal config found");
+		return -EINVAL;
+	}
+
+	return ts_test->ts->hw_ops->send_config(ts_test->ts, cfg->data, cfg->len);
+}
+
+static int ts_test_read_version(struct goodix_ts_test *ts_test,
+	struct goodix_fw_version *version)
+{
+	return ts_test->ts->hw_ops->read_version(ts_test->ts, version);
+}
+
+static void goto_next_line(char **ptr)
+{
+	do {
+		*ptr = *ptr + 1;
+	} while (**ptr != '\n' && **ptr != '\0');
+
+	if (**ptr == '\0')
+		return;
+
+	*ptr = *ptr + 1;
+}
+
+static void copy_this_line(char *dest, char *src)
+{
+	char *copy_from;
+	char *copy_to;
+
+	copy_from = src;
+	copy_to = dest;
+	do {
+		*copy_to = *copy_from;
+		copy_from++;
+		copy_to++;
+	} while((*copy_from != '\n') && (*copy_from != '\r') && (*copy_from != '\0'));
+	*copy_to = '\0';
+}
+
+static int getrid_space(s8* data, s32 len)
+{
+	u8* buf = NULL;
+	s32 i;
+	u32 count = 0;
+
+	buf = (char*)kzalloc(len + 5, GFP_KERNEL);
+	if (!buf) {
+		ts_err("get space kzalloc error");
+		return -ESRCH;
+	}
+
+	for (i = 0; i < len; i++) {
+		if (data[i] == ' ' || data[i] == '\r' || data[i] == '\n')
+			continue;
+
+		buf[count++] = data[i];
+	}
+
+	buf[count++] = '\0';
+
+	memcpy(data, buf, count);
+	kfree(buf);
+
+	return count;
+}
+
+static int parse_valid_data(char *buf_start, loff_t buf_size,
+	char *ptr, s32 *data, s32 rows)
+{
+	int i = 0;
+	int j = 0;
+	char *token = NULL;
+	char *tok_ptr = NULL;
+	char *row_data = NULL;
+	long temp_val;
+
+	if (!ptr) {
+		ts_err("ptr is NULL");
+		return -EINVAL;
+	}
+	if (!data) {
+		ts_err("data is NULL");
+		return -EINVAL;
+	}
+
+	row_data = (char *)kzalloc(MAX_LINE_LEN, GFP_KERNEL);
+	if (!row_data) {
+		ts_err("alloc bytes %d failed.", MAX_LINE_LEN);
+		return -ENOMEM;
+	}
+
+	for (i = 0; i < rows; i++) {
+		memset(row_data, 0, MAX_LINE_LEN);
+		copy_this_line(row_data, ptr);
+		getrid_space(row_data, strlen(row_data));
+		tok_ptr = row_data;
+		while ((token = strsep(&tok_ptr,","))) {
+			if (strlen(token) == 0)
+				continue;
+			if (kstrtol(token, 0, &temp_val)) {
+				kfree(row_data);
+				return -EINVAL;
+			}
+			data[j++] = (s32)temp_val;
+		}
+		if (i == rows - 1)
+			break;
+		goto_next_line(&ptr); //next row
+		if(!ptr || (strlen(ptr) == 0) || (ptr >= (buf_start + buf_size))) {
+			ts_info("invalid ptr, return");
+			kfree(row_data);
+			row_data = NULL;
+			return -EPERM;
+		}
+	}
+	kfree(row_data);
+	return j;
+}
+
+static int parse_csvfile(char *buf, size_t size, char *target_name,
+	s32 *data, s32 rows, s32 col)
+{
+	int ret = 0;
+	char *ptr = NULL;
+	int read_ret;
+
+	read_ret = size;
+	if (read_ret > 0) {
+		ptr = buf;
+		ptr = strstr(ptr, target_name);
+		if (!ptr) {
+			ts_info("load %s failed 1, maybe not this item", target_name);
+			return -EINTR;
+		}
+
+		goto_next_line(&ptr);
+		if (!ptr || (strlen(ptr) == 0 )) {
+			ts_err("load %s failed 2!", target_name);
+			return -EIO;
+		}
+
+		if (data) {
+			ret = parse_valid_data(buf, size, ptr, data, rows);
+		} else {
+			ts_err("load %s failed 3!", target_name);
+			return -EINTR;
+		}
+	} else {
+		ts_err("ret=%d, read_ret=%d", ret, read_ret);
+		ret = -ENXIO;
+	}
+
+	return ret;
+}
+
+static void goodix_init_params(struct goodix_ts_test *ts_test)
+{
+	struct goodix_ts_core *ts = ts_test->ts;
+	struct ts_test_params *test_params = &ts_test->test_params;
+
+	test_params->rawdata_addr = ts->ic_info.misc.mutual_rawdata_addr;
+	test_params->noisedata_addr = ts->ic_info.misc.mutual_diffdata_addr;
+	test_params->self_rawdata_addr = ts->ic_info.misc.self_rawdata_addr;
+	test_params->self_noisedata_addr = ts->ic_info.misc.self_diffdata_addr;
+
+	test_params->drv_num = ts->ic_info.parm.drv_num;
+	test_params->sen_num = ts->ic_info.parm.sen_num;
+
+	if (ts_test->ts->bus->ic_type == IC_TYPE_BERLIN_A)
+		test_params->params_info = &params_bra;
+	else if (ts_test->ts->bus->ic_type == IC_TYPE_BERLIN_B)
+		test_params->params_info = &params_brb;
+	else
+		test_params->params_info = &params_brd;
+}
+
+static int goodix_init_testlimits(struct goodix_ts_test *ts_test)
+{
+	int ret;
+	int i;
+	u32 data_buf[10] = {0};
+	char *temp_buf = NULL;
+	struct ts_test_params *test_params = &ts_test->test_params;
+	struct goodix_ts_core *ts_core = ts_test->ts;
+	const struct firmware *firmware = NULL;
+	struct device *dev = &ts_core->pdev->dev;
+	char limit_file[100] = {0};
+	u32 tx = test_params->drv_num;
+	u32 rx = test_params->sen_num;
+
+	scnprintf(limit_file, ARRAY_SIZE(limit_file), "%s_test_limits_%d.csv",
+			GOODIX_TEST_FILE_NAME, ts_core->fw_version.sensor_id);
+	ts_info("limit_file_name:%s", limit_file);
+
+	ret = request_firmware(&firmware, limit_file, dev);
+	if (ret < 0) {
+		ts_err("limits file [%s] not available", limit_file);
+		return -EINVAL;
+	}
+	if (!firmware) {
+		ts_err("request_firmware failed");
+		return -EINVAL;
+	}
+	if (firmware->size <= 0) {
+		ts_err("request_firmware, limits param length error,len:%zu",
+			firmware->size);
+		ret = -EINVAL;
+		goto exit_free;
+	}
+	temp_buf = kzalloc(firmware->size + 1, GFP_KERNEL);
+	if (!temp_buf) {
+		ts_err("kzalloc bytes failed.");
+		ret = -ENOMEM;
+		goto exit_free;
+	}
+	memcpy(temp_buf, firmware->data, firmware->size);
+
+	/* obtain config data */
+	ret = parse_csvfile(temp_buf, firmware->size, CSV_TP_TEST_CONFIG,
+		test_params->cfg_buf, 1, GOODIX_CFG_MAX_SIZE);
+	if (ret < 0) {
+		ts_info("Can't find %s", CSV_TP_TEST_CONFIG);
+	} else {
+		ts_info("parse_csvfile %s OK, cfg_len:%d", CSV_TP_TEST_CONFIG, ret);
+		for (i = 0; i < ret; i++)
+			ts_test->test_config.data[i] = (u8)test_params->cfg_buf[i];
+		ts_test->test_config.len = ret;
+	}
+
+	/* obtain mutual_raw min */
+	ret = parse_csvfile(temp_buf, firmware->size, CSV_TP_SPECIAL_RAW_MIN,
+	test_params->min_limits, rx, tx);
+	if (ret < 0) {
+		ts_err("Failed get min_limits");
+		goto exit_free;
+	} else
+		ts_info("parse_csvfile %s OK", CSV_TP_SPECIAL_RAW_MIN);
+
+	/* obtain mutual_raw max */
+	ret = parse_csvfile(temp_buf, firmware->size, CSV_TP_SPECIAL_RAW_MAX,
+		test_params->max_limits, rx, tx);
+	if (ret < 0) {
+		ts_err("Failed get max_limits");
+		goto exit_free;
+	} else
+		ts_info("parse_csvfile %s OK", CSV_TP_SPECIAL_RAW_MAX);
+
+	/* obtain delta limit */
+	ret = parse_csvfile(temp_buf, firmware->size, CSV_TP_SPECIAL_RAW_DELTA,
+		test_params->deviation_limits, rx, tx);
+	if (ret < 0) {
+		ts_err("Failed get delta limit");
+		goto exit_free;
+	} else {
+		ts_info("parse_csvfile %s OK", CSV_TP_SPECIAL_RAW_DELTA);
+	}
+
+	/* obtain self_raw min */
+	ret = parse_csvfile(temp_buf, firmware->size, CSV_TP_SPECIAL_SELFRAW_MIN,
+		test_params->self_min_limits, 1, tx + rx);
+	/* obtain self_raw max */
+	ret |= parse_csvfile(temp_buf, firmware->size, CSV_TP_SPECIAL_SELFRAW_MAX,
+		test_params->self_max_limits, 1, tx + rx);
+	if (ret < 0) {
+		ts_info("Can't find self_min_max_limits, ship this item");
+		ret = 0;
+		test_params->test_items[GTP_SELFCAP_TEST] = false;
+	} else {
+		ts_info("parse_csvfile %s OK", CSV_TP_SPECIAL_SELFRAW_MIN);
+		ts_info("parse_csvfile %s OK", CSV_TP_SPECIAL_SELFRAW_MAX);
+		test_params->test_items[GTP_SELFCAP_TEST] = true;
+	}
+
+	/* obtain noise_threshold */
+	ret = parse_csvfile(temp_buf, firmware->size, CSV_TP_NOISE_LIMIT,
+		&test_params->noise_threshold, 1, 1);
+	if (ret < 0) {
+		ts_info("Can't find noise_threshold, skip this item");
+		ret = 0;
+		test_params->test_items[GTP_NOISE_TEST] = false;
+	} else {
+		ts_info("parse_csvfile %s OK", CSV_TP_NOISE_LIMIT);
+		test_params->test_items[GTP_NOISE_TEST] = true;
+	}
+
+	/* obtain self_noise_threshold */
+	ret = parse_csvfile(temp_buf, firmware->size, CSV_TP_SELFNOISE_LIMIT,
+		&test_params->self_noise_threshold, 1, 1);
+	if (ret < 0) {
+		ts_info("Can't find self_noise_threshold, skip this item");
+		ret = 0;
+		test_params->test_items[GTP_SELFNOISE_TEST] = false;
+	} else {
+		ts_info("parse_csvfile %s OK", CSV_TP_SELFNOISE_LIMIT);
+		test_params->test_items[GTP_SELFNOISE_TEST] = true;
+	}
+
+	 /* obtain short_params */
+	ret = parse_csvfile(temp_buf, firmware->size, CSV_TP_SHORT_THRESHOLD,
+		(s32 *)data_buf, 1, 7);
+	if (ret < 0) {
+		ts_info("Can't find short shortciurt_threshold, skip this item");
+		ret = 0;
+		test_params->test_items[GTP_SHORT_TEST] = false;
+	} else {
+		ts_info("parse_csvfile %s OK", CSV_TP_SHORT_THRESHOLD);
+		test_params->test_items[GTP_SHORT_TEST] = true;
+		test_params->short_threshold = data_buf[0];
+		test_params->r_drv_drv_threshold = data_buf[1];
+		test_params->r_drv_sen_threshold = data_buf[2];
+		test_params->r_sen_sen_threshold = data_buf[3];
+		test_params->r_drv_gnd_threshold = data_buf[4];
+		test_params->r_sen_gnd_threshold = data_buf[5];
+		test_params->avdd_value = data_buf[6];
+	}
+
+exit_free:
+	kfree(temp_buf);
+	release_firmware(firmware);
+	return ret;
+}
+
+static int goodix_tptest_prepare(struct goodix_ts_test *ts_test)
+{
+	int ret;
+	struct goodix_ic_config *cfg = &ts_test->test_config;
+
+	ts_info("TP test prepare IN");
+
+	goodix_init_params(ts_test);
+	/* parse test limits from csv */
+	ret = goodix_init_testlimits(ts_test);
+	if (ret < 0) {
+		ts_err("Failed to init testlimits from csv.");
+		return ret;
+	}
+
+	/* disable irq */
+	ts_test_irq_enable(ts_test, false);
+	/* close esd */
+	goodix_ts_blocking_notify(NOTIFY_ESD_OFF, NULL);
+
+	/* send test config if exist */
+	if (cfg->len > 0) {
+		ts_info("Test config exists and send it");
+		ret = ts_test->ts->hw_ops->send_config(ts_test->ts, cfg->data, cfg->len);
+		if (ret < 0) {
+			ts_err("Send test config failed, exit");
+			goodix_ts_blocking_notify(NOTIFY_ESD_ON, NULL);
+			ts_test_irq_enable(ts_test, true);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static void goodix_tptest_finish(struct goodix_ts_test *ts_test)
+{
+	ts_info("TP test finish IN");
+	/* reset chip */
+	ts_test_reset(ts_test, 100);
+	/* send normal config */
+	if (ts_test->test_config.len > 0) {
+		if (ts_test_send_config(ts_test, CONFIG_TYPE_NORMAL))
+			ts_err("Send normal config failed");
+	}
+
+	/* open esd */
+	goodix_ts_blocking_notify(NOTIFY_ESD_ON, NULL);
+	/* enable irq */
+	ts_test_irq_enable(ts_test, true);
+}
+
+#define SHORT_TEST_RUN_REG			0x10400
+#define SHORT_TEST_RUN_FLAG			0xAA
+#define INSPECT_FW_SWITCH_CMD		0x85
+#define TEST_FW_PID  				"OST"
+static int goodix_short_test_prepare(struct goodix_ts_test *ts_test)
+{
+	struct goodix_ts_cmd tmp_cmd;
+	struct goodix_fw_version fw_ver;
+	int ret;
+	int retry;
+	int resend = 3;
+	u8 status;
+
+	ts_info("short test prepare IN");
+	ts_test->test_result[GTP_SHORT_TEST] = SYS_SOFTWARE_REASON;
+	tmp_cmd.len = 4;
+	tmp_cmd.cmd = INSPECT_FW_SWITCH_CMD;
+
+resend_cmd:
+	ret = ts_test_send_cmd(ts_test, &tmp_cmd);
+	if (ret < 0) {
+		ts_err("send test mode failed");
+		return ret;
+	}
+
+	retry = 3;
+	while (retry--) {
+		msleep(40);
+		if (ts_test->ts->bus->ic_type == IC_TYPE_BERLIN_A) {
+			ret = ts_test_read_version(ts_test, &fw_ver);
+			if (ret < 0) {
+				ts_err("read test version failed");
+				return ret;
+			}
+			ret = memcmp(&(fw_ver.patch_pid[3]), TEST_FW_PID, strlen(TEST_FW_PID));
+			if (ret == 0)
+				return 0;
+			ts_info("patch ID dismatch %s != %s", fw_ver.patch_pid, TEST_FW_PID);
+		} else {
+			ret = ts_test_read(ts_test, SHORT_TEST_RUN_REG, &status, 1);
+			if (!ret && status == SHORT_TEST_RUN_FLAG)
+				return 0;
+			ts_info("short_mode_status=0x%02x ret=%d", status, ret);
+		}
+	}
+
+	if (resend--) {
+		ts_test_reset(ts_test, 100);
+		goto resend_cmd;
+	}
+
+	return -EINVAL;
+}
+
+static u32 map_die2pin(struct ts_test_params *test_params, u32 chn_num)
+{
+	int i = 0;
+	u32 res = 255;
+
+	if (chn_num & DRV_CHANNEL_FLAG)
+		chn_num = (chn_num & ~DRV_CHANNEL_FLAG) + test_params->params_info->max_sen_num;
+
+	for (i = 0; i < test_params->params_info->max_sen_num; i++) {
+		if (test_params->params_info->sen_map[i] == chn_num) {
+			res = i;
+			break;
+		}
+	}
+	/* res != 255 mean found the corresponding channel num */
+	if (res != 255)
+		return res;
+	/* if cannot find in SenMap try find in DrvMap */
+	for (i = 0; i < test_params->params_info->max_drv_num; i++) {
+		if (test_params->params_info->drv_map[i] == chn_num) {
+			res = i;
+			break;
+		}
+	}
+	if (i >= test_params->params_info->max_drv_num)
+		ts_err("Faild found corrresponding channel num:%d", chn_num);
+	else
+		res |= DRV_CHANNEL_FLAG;
+
+	return res;
+}
+
+static void goodix_save_short_res(struct ts_test_params *params,
+	u16 chn1, u16 chn2, int r)
+{
+	int i;
+	u8 repeat_cnt = 0;
+	u8 repeat = 0;
+	struct goodix_ts_test *ts_test = container_of(params, 
+		struct goodix_ts_test, test_params);
+	struct ts_short_res *short_res = &ts_test->short_res;
+
+	if (chn1 == chn2 || short_res->short_num >= MAX_SHORT_NUM)
+		return;
+
+	for (i = 0; i < short_res->short_num; i++) {
+		repeat_cnt = 0;
+		if (short_res->short_msg[4 * i] == chn1)
+			repeat_cnt++;
+		if (short_res->short_msg[4 * i] == chn2)
+			repeat_cnt++;
+		if (short_res->short_msg[4 * i + 1] == chn1)
+			repeat_cnt++;
+		if (short_res->short_msg[4 * i + 1] == chn2)
+			repeat_cnt++;
+		if (repeat_cnt >= 2){
+			repeat = 1;
+			break;
+		}
+	}
+	if (repeat == 0) {
+		short_res->short_msg[4 * short_res->short_num + 0] = chn1;
+		short_res->short_msg[4 * short_res->short_num + 1] = chn2;
+		short_res->short_msg[4 * short_res->short_num + 2] = (r >> 8) & 0xFF;
+		short_res->short_msg[4 * short_res->short_num + 3] = r & 0xFF;
+		if (short_res->short_num < MAX_SHORT_NUM)
+			short_res->short_num++;
+	}
+}
+
+static int gdix_check_tx_tx_shortcircut(struct goodix_ts_test *ts_test,
+	u8 short_ch_num)
+{
+	int ret = 0, err = 0;
+	u32 r_threshold = 0, short_r = 0;
+	int size = 0, i = 0, j = 0;
+	u16 adc_signal = 0;
+	u8 master_pin_num, slave_pin_num;
+	u8 *data_buf;
+	u32 data_reg;
+	struct ts_test_params *test_params = &ts_test->test_params;
+	int max_drv_num = test_params->params_info->max_drv_num;
+	int max_sen_num = test_params->params_info->max_sen_num;
+	u16 self_capdata, short_die_num = 0;
+
+	size = 4 + max_drv_num * 2 + 2;
+	data_buf = kzalloc(size, GFP_KERNEL);
+	if (!data_buf) {
+		ts_err("Failed to alloc memory");
+		return -ENOMEM;
+	}
+	/* drv&drv shortcircut check */
+	data_reg = test_params->params_info->drv_drv_selfcode_reg;
+	for (i = 0; i < short_ch_num; i++) {
+		ret = ts_test_read(ts_test, data_reg, data_buf, size);
+		if (ret < 0) {
+			ts_err("Failed read Drv-to-Drv short rawdata");
+			err = -EINVAL;
+			break;
+		}
+
+		if (checksum_cmp(data_buf, size, CHECKSUM_MODE_U8_LE)) {
+			ts_err("Drv-to-Drv adc data checksum error");
+			err = -EINVAL;
+			break;
+		}
+
+		r_threshold = test_params->r_drv_drv_threshold;
+		short_die_num = le16_to_cpup((__le16 *)&data_buf[0]);
+		short_die_num -= max_sen_num;
+		if (short_die_num >= max_drv_num) {
+			ts_info("invalid short pad num:%d",
+				short_die_num + max_sen_num);
+			continue;
+		}
+
+		/* TODO: j start position need recheck */
+		self_capdata = le16_to_cpup((__le16 *)&data_buf[2]);
+		if (self_capdata == 0xffff || self_capdata == 0) {
+			ts_info("invalid self_capdata:0x%x", self_capdata);
+			continue;
+		}
+
+		for (j = short_die_num + 1; j < max_drv_num; j++) {
+			adc_signal = le16_to_cpup((__le16 *)&data_buf[4 + j * 2]);
+
+			if (adc_signal < test_params->short_threshold)
+				continue;
+
+			short_r = (u32)cal_cha_to_cha_res(ts_test, self_capdata, adc_signal);
+			if (short_r < r_threshold) {
+				master_pin_num =
+					map_die2pin(test_params, short_die_num + max_sen_num);
+				slave_pin_num =
+					map_die2pin(test_params, j + max_sen_num);
+				if (master_pin_num == 0xFF || slave_pin_num == 0xFF) {
+					ts_info("WARNNING invalid pin");
+					continue;
+				}
+				goodix_save_short_res(test_params, master_pin_num,
+					slave_pin_num, short_r);
+				ts_err("short circut:R=%dK,R_Threshold=%dK",
+							short_r, r_threshold);
+				ts_err("%s%d--%s%d shortcircut",
+					(master_pin_num & DRV_CHANNEL_FLAG) ? "DRV" : "SEN",
+					(master_pin_num & ~DRV_CHANNEL_FLAG),
+					(slave_pin_num & DRV_CHANNEL_FLAG) ? "DRV" : "SEN",
+					(slave_pin_num & ~DRV_CHANNEL_FLAG));
+				err = -EINVAL;
+			}
+		}
+		data_reg += size;
+	}
+
+	kfree(data_buf);
+	return err;
+}
+
+static int gdix_check_rx_rx_shortcircut(struct goodix_ts_test *ts_test,
+	u8 short_ch_num)
+{
+	int ret = 0, err = 0;
+	u32 r_threshold = 0, short_r = 0;
+	int size = 0, i = 0, j = 0;
+	u16 adc_signal = 0;
+	u8 master_pin_num, slave_pin_num;
+	u8 *data_buf;
+	u32 data_reg;
+	struct ts_test_params *test_params = &ts_test->test_params;
+	int max_sen_num = test_params->params_info->max_sen_num;
+	u16 self_capdata, short_die_num = 0;
+
+	size = 4 + max_sen_num * 2 + 2;
+	data_buf = kzalloc(size, GFP_KERNEL);
+	if (!data_buf) {
+		ts_err("Failed to alloc memory");
+		return -ENOMEM;
+	}
+	/* drv&drv shortcircut check */
+	data_reg = test_params->params_info->sen_sen_selfcode_reg;
+	for (i = 0; i < short_ch_num; i++) {
+		ret = ts_test_read(ts_test, data_reg, data_buf, size);
+		if (ret) {
+			ts_err("Failed read Sen-to-Sen short rawdata");
+			err = -EINVAL;
+			break;
+		}
+
+		if (checksum_cmp(data_buf, size, CHECKSUM_MODE_U8_LE)) {
+			ts_err("Sen-to-Sen adc data checksum error");
+			err = -EINVAL;
+			break;
+		}
+
+		r_threshold = test_params->r_sen_sen_threshold;
+		short_die_num = le16_to_cpup((__le16 *)&data_buf[0]);
+		if (short_die_num >= max_sen_num) {
+			ts_info("invalid short pad num:%d", short_die_num);
+			continue;
+		}
+
+		/* TODO: j start position need recheck */
+		self_capdata = le16_to_cpup((__le16 *)&data_buf[2]);
+		if (self_capdata == 0xffff || self_capdata == 0) {
+			ts_info("invalid self_capdata:0x%x", self_capdata);
+			continue;
+		}
+
+		for (j = short_die_num + 1; j < max_sen_num; j++) {
+			adc_signal = le16_to_cpup((__le16 *)&data_buf[4 + j * 2]);
+			
+			if (adc_signal < test_params->short_threshold)
+				continue;
+
+			short_r = (u32)cal_cha_to_cha_res(ts_test, self_capdata, adc_signal);
+			if (short_r < r_threshold) {
+				master_pin_num = map_die2pin(test_params, short_die_num);
+				slave_pin_num = map_die2pin(test_params, j);
+				if (master_pin_num == 0xFF || slave_pin_num == 0xFF) {
+					ts_info("WARNNING invalid pin");
+					continue;
+				}
+				goodix_save_short_res(test_params, master_pin_num,
+					slave_pin_num, short_r);
+				ts_err("short circut:R=%dK,R_Threshold=%dK",
+							short_r, r_threshold);
+				ts_err("%s%d--%s%d shortcircut",
+					(master_pin_num & DRV_CHANNEL_FLAG) ? "DRV" : "SEN",
+					(master_pin_num & ~DRV_CHANNEL_FLAG),
+					(slave_pin_num & DRV_CHANNEL_FLAG) ? "DRV" : "SEN",
+					(slave_pin_num & ~DRV_CHANNEL_FLAG));
+				err = -EINVAL;
+			}
+		}
+		data_reg += size;
+	}
+
+	kfree(data_buf);
+	return err;
+}
+
+static int gdix_check_tx_rx_shortcircut(struct goodix_ts_test *ts_test,
+	u8 short_ch_num)
+{
+	int ret = 0, err = 0;
+	u32 r_threshold = 0, short_r = 0;
+	int size = 0, i = 0, j = 0;
+	u16 adc_signal = 0;
+	u8 master_pin_num, slave_pin_num;
+	u8 *data_buf = NULL;
+	u32 data_reg;
+	struct ts_test_params *test_params = &ts_test->test_params;
+	int max_drv_num = test_params->params_info->max_drv_num;
+	int max_sen_num = test_params->params_info->max_sen_num;
+	u16 self_capdata, short_die_num = 0;
+
+	size = 4 + max_drv_num * 2 + 2;
+	data_buf = kzalloc(size, GFP_KERNEL);
+	if (!data_buf) {
+		ts_err("Failed to alloc memory");
+		return -ENOMEM;
+	}
+	/* drv&sen shortcircut check */
+	data_reg = test_params->params_info->drv_sen_selfcode_reg;
+	for (i = 0; i < short_ch_num; i++) {
+		ret = ts_test_read(ts_test, data_reg, data_buf, size);
+		if (ret) {
+			ts_err("Failed read Drv-to-Sen short rawdata");
+			err = -EINVAL;
+			break;
+		}
+
+		if (checksum_cmp(data_buf, size, CHECKSUM_MODE_U8_LE)) {
+			ts_err("Drv-to-Sen adc data checksum error");
+			err = -EINVAL;
+			break;
+		}
+
+		r_threshold = test_params->r_drv_sen_threshold;
+		short_die_num = le16_to_cpup((__le16 *)&data_buf[0]);
+		if (short_die_num >= max_sen_num) {
+			ts_info("invalid short pad num:%d", short_die_num);
+			continue;
+		}
+
+		/* TODO: j start position need recheck */
+		self_capdata = le16_to_cpup((__le16 *)&data_buf[2]);
+		if (self_capdata == 0xffff || self_capdata == 0) {
+			ts_info("invalid self_capdata:0x%x", self_capdata);
+			continue;
+		}
+
+		for (j = 0; j < max_drv_num; j++) {
+			adc_signal = le16_to_cpup((__le16 *)&data_buf[4 + j * 2]);
+
+			if (adc_signal < test_params->short_threshold)
+				continue;
+
+			short_r = (u32)cal_cha_to_cha_res(ts_test, self_capdata, adc_signal);
+			if (short_r < r_threshold) {
+				master_pin_num = map_die2pin(test_params, short_die_num);
+				slave_pin_num = map_die2pin(test_params, j + max_sen_num);
+				if (master_pin_num == 0xFF || slave_pin_num == 0xFF) {
+					ts_info("WARNNING invalid pin");
+					continue;
+				}
+				goodix_save_short_res(test_params, master_pin_num,
+					slave_pin_num, short_r);
+				ts_err("short circut:R=%dK,R_Threshold=%dK",
+							short_r, r_threshold);
+				ts_err("%s%d--%s%d shortcircut",
+					(master_pin_num & DRV_CHANNEL_FLAG) ? "DRV" : "SEN",
+					(master_pin_num & ~DRV_CHANNEL_FLAG),
+					(slave_pin_num & DRV_CHANNEL_FLAG) ? "DRV" : "SEN",
+					(slave_pin_num & ~DRV_CHANNEL_FLAG));
+				err = -EINVAL;
+			}
+		}
+		data_reg += size;
+	}
+
+	kfree(data_buf);
+	return err;
+}
+
+static int gdix_check_resistance_to_gnd(struct ts_test_params *test_params,
+	u16 adc_signal, u32 pos)
+{
+	long r = 0;
+	u16 r_th = 0, avdd_value = 0;
+	u16 chn_id_tmp = 0;
+	u8 pin_num = 0;
+	unsigned short short_type;
+	struct goodix_ts_test *ts_test = container_of(test_params,
+					struct goodix_ts_test, test_params);
+	int max_drv_num = test_params->params_info->max_drv_num;
+	int max_sen_num = test_params->params_info->max_sen_num;
+
+	avdd_value = test_params->avdd_value;
+	short_type = adc_signal & 0x8000;
+	adc_signal &= ~0x8000;
+	if (adc_signal == 0)
+		adc_signal = 1;
+
+	if (short_type == 0) {
+		/* short to GND */
+		r = cal_cha_to_gnd_res(ts_test, adc_signal);
+	} else {
+		/* short to VDD */
+		r = cal_cha_to_avdd_res(ts_test, adc_signal, avdd_value);
+	}
+
+	if (pos < max_drv_num)
+		r_th = test_params->r_drv_gnd_threshold;
+	else
+		r_th = test_params->r_sen_gnd_threshold;
+
+	chn_id_tmp = pos;
+	if (chn_id_tmp < max_drv_num)
+		chn_id_tmp += max_sen_num;
+	else
+		chn_id_tmp -= max_drv_num;
+
+	if (r < r_th) {
+		pin_num = map_die2pin(test_params, chn_id_tmp);
+		goodix_save_short_res(test_params, pin_num,
+				short_type ? CHN_VDD : CHN_GND, r);
+		ts_err("%s%d shortcircut to %s,R=%ldK,R_Threshold=%dK",
+				(pin_num & DRV_CHANNEL_FLAG) ? "DRV" : "SEN",
+				(pin_num & ~DRV_CHANNEL_FLAG),
+				short_type ? "VDD" : "GND",
+				r, r_th);
+
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int gdix_check_gndvdd_shortcircut(struct goodix_ts_test *ts_test)
+{
+	int ret = 0, err = 0;
+	int size = 0, i = 0;
+	u16 adc_signal = 0;
+	u32 data_reg;
+	u8 *data_buf = NULL;
+	int max_drv_num = ts_test->test_params.params_info->max_drv_num;
+	int max_sen_num = ts_test->test_params.params_info->max_sen_num;
+
+	size = (max_drv_num + max_sen_num) * 2 + 2;
+	data_buf = kzalloc(size, GFP_KERNEL);
+	if (!data_buf) {
+		ts_err("Failed to alloc memory");
+		return -ENOMEM;
+	}
+	/* read diff code, diff code will be used to calculate
+		* resistance between channel and GND */
+	data_reg = ts_test->test_params.params_info->diffcode_data_reg;
+	ret = ts_test_read(ts_test, data_reg, data_buf, size);
+	if (ret < 0) {
+		ts_err("Failed read to-gnd rawdata");
+		err = -EINVAL;
+		goto err_out;
+	}
+
+	if (checksum_cmp(data_buf, size, CHECKSUM_MODE_U8_LE)) {
+		ts_err("diff code checksum error");
+		err = -EINVAL;
+		goto err_out;
+	}
+
+	for (i = 0; i < max_drv_num + max_sen_num; i++) {
+		adc_signal = le16_to_cpup((__le16 *)&data_buf[i * 2]);
+		ret = gdix_check_resistance_to_gnd(&ts_test->test_params,
+					adc_signal, i);
+		if (ret != 0) {
+			ts_err("Resistance to-gnd/vdd short");
+			err = ret;
+		}
+	}
+
+err_out:
+	kfree(data_buf);
+	return err;
+}
+
+static int goodix_shortcircut_analysis(struct goodix_ts_test *ts_test)
+{
+	int ret;
+	int err = 0;
+	test_result_t test_result;
+
+	ret = ts_test_read(ts_test,
+			ts_test->test_params.params_info->short_test_result_reg,
+			(u8 *)&test_result, sizeof(test_result));
+	if (ret < 0) {
+		ts_err("Read TEST_RESULT_REG failed");
+		return ret;
+	}
+
+	if (checksum_cmp((u8 *)&test_result, sizeof(test_result),
+		CHECKSUM_MODE_U8_LE)) {
+		ts_err("shrot result checksum err");
+		return -EINVAL;
+	}
+
+	if (!(test_result.result & 0x0F)) {
+		ts_info(">>>>> No shortcircut");
+		return 0;
+	}
+	ts_info("short flag 0x%02x, drv&drv:%d, sen&sen:%d, drv&sen:%d, drv/GNDVDD:%d, sen/GNDVDD:%d",
+		test_result.result, test_result.drv_drv_num, test_result.sen_sen_num,
+		test_result.drv_sen_num, test_result.drv_gnd_avdd_num, test_result.sen_gnd_avdd_num);
+
+	if (test_result.drv_drv_num)
+		err |= gdix_check_tx_tx_shortcircut(ts_test, test_result.drv_drv_num);
+	if (test_result.sen_sen_num)
+		err |= gdix_check_rx_rx_shortcircut(ts_test, test_result.sen_sen_num);
+	if (test_result.drv_sen_num)
+		err |= gdix_check_tx_rx_shortcircut(ts_test, test_result.drv_sen_num);
+	if (test_result.drv_gnd_avdd_num || test_result.sen_gnd_avdd_num)
+		err |= gdix_check_gndvdd_shortcircut(ts_test);
+
+	ts_info(">>>>> short check return 0x%x", err);
+
+	return err;
+}
+
+#define SHORT_FW_CMD_REG				0x10400
+static int send_test_cmd(struct goodix_ts_test *ts_test,
+	struct goodix_ts_cmd *cmd)
+{
+	int ret;
+	u32 reg = SHORT_FW_CMD_REG;
+
+	cmd->state = 0;
+	cmd->ack = 0;
+	goodix_append_checksum(&(cmd->buf[2]), cmd->len - 2, CHECKSUM_MODE_U8_LE);
+	ret = ts_test_write(ts_test, reg, cmd->buf, cmd->len + 2);
+	if (ret < 0)
+		return ret;
+	usleep_range(10000, 11000);
+	return ret;
+}
+
+
+#define INSPECT_PARAM_CMD				0xAA
+#define SHORT_TEST_FINISH_FLAG  		0x88
+#define SHORT_TEST_THRESHOLD_REG		0x20402
+static void goodix_shortcircut_test(struct goodix_ts_test *ts_test)
+{
+	int ret = 0;
+	int retry;
+	u16 test_time;
+	u8 status;
+	int ic_type = ts_test->ts->bus->ic_type;
+	struct goodix_ts_cmd test_parm_cmd;
+	// u8 test_param[6];
+
+	ts_info("---------------------- short_test begin ----------------------");
+	ret = goodix_short_test_prepare(ts_test);
+	if (ret < 0) {
+		ts_err("Failed enter short test mode");
+		return;
+	}
+
+	/* get short test time */
+	ret = ts_test_read(ts_test,
+		ts_test->test_params.params_info->short_test_time_reg,
+		(u8 *)&test_time, 2);
+	if (ret < 0) {
+		ts_err("Failed to get test_time, default %dms", DEFAULT_TEST_TIME_MS);
+		test_time = DEFAULT_TEST_TIME_MS;
+	} else {
+		if (ic_type == IC_TYPE_BERLIN_A)
+			test_time /= 10;
+		if (test_time > MAX_TEST_TIME_MS) {
+			ts_info("test time too long %d > %d",
+			test_time, MAX_TEST_TIME_MS);
+			test_time = MAX_TEST_TIME_MS;
+		}
+		ts_info("get test time %dms", test_time);
+	}
+
+	/* start short test */
+	if (ic_type == IC_TYPE_BERLIN_A) {
+		test_parm_cmd.len = 0x0A;
+		test_parm_cmd.cmd = INSPECT_PARAM_CMD;
+		test_parm_cmd.data[0] =
+			ts_test->test_params.params_info->dft_short_threshold & 0xFF;
+		test_parm_cmd.data[1] =
+			(ts_test->test_params.params_info->dft_short_threshold >> 8) & 0xFF;
+		test_parm_cmd.data[2] =
+			ts_test->test_params.params_info->short_diffcode_threshold & 0xFF;
+		test_parm_cmd.data[3] =
+			(ts_test->test_params.params_info->short_diffcode_threshold >> 8) & 0xFF;
+		test_parm_cmd.data[4] =
+			ts_test->test_params.params_info->short_test_dump_num & 0xFF;
+		test_parm_cmd.data[5] =
+			(ts_test->test_params.params_info->short_test_dump_num >> 8) & 0xFF;
+		ret = send_test_cmd(ts_test, &test_parm_cmd);
+		if (ret < 0) {
+			ts_err("send INSPECT_PARAM_CMD failed");
+			return;
+		}
+	} else {
+		// test_param[0] = ts_test->test_params.params_info->dft_short_threshold & 0xFF;
+		// test_param[1] =
+		//	(ts_test->test_params.params_info->dft_short_threshold >> 8) & 0xFF;
+		// test_param[2] =
+		//	ts_test->test_params.params_info->short_diffcode_threshold & 0xFF;
+		// test_param[3] =
+		//	(ts_test->test_params.params_info->short_diffcode_threshold >> 8) & 0xFF;
+		// test_param[4] =
+		//	ts_test->test_params.params_info->short_test_dump_num & 0xFF;
+		// test_param[5] =
+		//	(ts_test->test_params.params_info->short_test_dump_num >> 8) & 0xFF;
+		// ts_test_write(ts_test, SHORT_TEST_THRESHOLD_REG,
+		//	test_param, sizeof(test_param));
+		status = 0;
+		ts_test_write(ts_test, SHORT_TEST_RUN_REG, &status, 1);
+	}
+
+	/* wait short test finish */
+	msleep(test_time);
+	retry = 50;
+	while (retry--) {
+		ret = ts_test_read(ts_test,
+			ts_test->test_params.params_info->short_test_status_reg, &status, 1);
+		if (!ret && status == SHORT_TEST_FINISH_FLAG)
+			break;
+		msleep(50);
+	}
+	if (retry < 0) {
+		ts_err("short test failed, status:0x%02x", status);
+		return;
+	}
+
+	/* start analysis short result */
+	ts_info("short_test finished, start analysis");
+	ret = goodix_shortcircut_analysis(ts_test);
+	if (ret < 0)
+		ts_test->test_result[GTP_SHORT_TEST] = GTP_PANEL_REASON;
+	else
+		ts_test->test_result[GTP_SHORT_TEST] = GTP_TEST_PASS;
+}
+
+#define GOODIX_CMD_RAWDATA	2
+#define GOODIX_TOUCH_EVENT	0x80
+static int goodix_cap_test_prepare(struct goodix_ts_test *ts_test)
+{
+	int ret;
+	struct goodix_ts_cmd temp_cmd;
+
+	ts_info("cap test prepare IN");
+	ts_test->test_result[GTP_CAP_TEST] = SYS_SOFTWARE_REASON;
+	ts_test->test_result[GTP_DELTA_TEST] = SYS_SOFTWARE_REASON;
+	if (ts_test->test_params.test_items[GTP_SELFCAP_TEST])
+		ts_test->test_result[GTP_SELFCAP_TEST] = SYS_SOFTWARE_REASON;
+	if (ts_test->test_params.test_items[GTP_NOISE_TEST])
+		ts_test->test_result[GTP_NOISE_TEST] = SYS_SOFTWARE_REASON;
+	if (ts_test->test_params.test_items[GTP_SELFNOISE_TEST])
+		ts_test->test_result[GTP_SELFNOISE_TEST] = SYS_SOFTWARE_REASON;
+
+	/* switch rawdata mode */
+	if (ts_test->ts->bus->ic_type == IC_TYPE_BERLIN_D) {
+		temp_cmd.cmd = 0x90;
+		temp_cmd.data[0] = 0x81;
+		temp_cmd.len = 5;
+	} else {
+		temp_cmd.cmd = GOODIX_CMD_RAWDATA;
+		temp_cmd.len = 4;
+	}
+	ret = ts_test_send_cmd(ts_test, &temp_cmd);
+	if (ret < 0)
+		ts_err("Enter rawdata mode failed");
+
+	return ret;
+}
+
+static int goodix_cap_test_finish(struct goodix_ts_test *ts_test)
+{
+	ts_info("cap_test finished");
+	/* switch coor mode */
+	ts_test_reset(ts_test, 100);
+	return 0;
+}
+
+static int goodix_cache_rawdata(struct goodix_ts_test *ts_test)
+{
+	int ret;
+	int i;
+	int retry;
+	u8 val;
+	unsigned char frame_buf[GOODIX_MAX_FRAMEDATA_LEN];
+	struct frame_head *frame_head;
+	struct goodix_ts_core *cd = ts_test->ts;
+	unsigned char *cur_ptr;
+	u32 sen_num = ts_test->test_params.sen_num;
+	u32 drv_num = ts_test->test_params.drv_num;
+	u32 data_size = sen_num * drv_num;
+	u32 data_addr = ts_test->test_params.rawdata_addr;
+	u32 flag_addr = ts_test->ts->ic_info.misc.touch_data_addr;
+
+	if (ts_test->ts->bus->ic_type == IC_TYPE_BERLIN_D)
+		flag_addr = ts_test->ts->ic_info.misc.frame_data_addr;
+
+	for (i = 0; i < TOTAL_FRAME_NUM; i++) {
+		val = 0;
+		ret = ts_test_write(ts_test, flag_addr, &val, 1);
+		if (ret < 0) {
+			ts_err("clean touch event failed, exit");
+			return -EAGAIN;
+		}
+		retry = 20;
+		while (retry--) {
+			usleep_range(5000, 5100);
+			ret = ts_test_read(ts_test, flag_addr, &val, 1);
+			if (!ret && (val & 0x80))
+				break;
+		}
+		if (retry < 0) {
+			ts_err("rawdata is not ready val:0x%02x i:%d, exit", val, i);
+			return -EAGAIN;
+		}
+
+		if (cd->bus->ic_type == IC_TYPE_BERLIN_D) {
+			ret = ts_test_read(ts_test, flag_addr, frame_buf, sizeof(frame_buf));
+			if (ret < 0)
+				return ret;
+			if (checksum_cmp(frame_buf,
+				cd->ic_info.misc.frame_data_head_len, CHECKSUM_MODE_U8_LE)) {
+				ts_err("frame head checksum error");
+				return -EINVAL; 
+			}
+			frame_head = (struct frame_head *)frame_buf;
+			if (checksum_cmp(frame_buf,
+				frame_head->cur_frame_len, CHECKSUM_MODE_U16_LE)) {
+				ts_err("frame body checksum error");
+				return -EINVAL;
+			}
+			cur_ptr = frame_buf;
+			cur_ptr += cd->ic_info.misc.frame_data_head_len;
+			cur_ptr += cd->ic_info.misc.fw_attr_len;
+			cur_ptr += cd->ic_info.misc.fw_log_len;
+			memcpy((u8 *)ts_test->rawdata[i].data, cur_ptr + 8,
+					cd->ic_info.misc.mutual_struct_len - 8);
+		} else {
+			ret = ts_test_read(ts_test, data_addr,
+				(u8 *)ts_test->rawdata[i].data, data_size * sizeof(s16));
+			if (ret < 0)
+				return ret;
+		}
+
+		ts_test->rawdata[i].size = data_size;
+		goodix_rotate_abcd2cbad(drv_num, sen_num, ts_test->rawdata[i].data);
+	}
+
+	return ret;
+}
+
+static void goodix_cache_deltadata(struct goodix_ts_test *ts_test)
+{
+	u32 data_size;
+	int tx = ts_test->test_params.drv_num;
+	int i;
+	int j;
+	int max_val;
+	int raw;
+	int temp;
+
+	for (i = 0; i < TOTAL_FRAME_NUM; i++) {
+		data_size = ts_test->rawdata[i].size;
+		if (data_size == 0)
+			continue;
+
+		for (j = 0; j < data_size; j++) {
+			raw = ts_test->rawdata[i].data[j];
+			max_val = 0;
+			/* calcu delta with above node */
+			if (j - tx >= 0) {
+				temp = ts_test->rawdata[i].data[j - tx];
+				temp = ABS(temp - raw);
+				max_val = MAX(max_val, temp);
+			}
+			/* calcu delta with bellow node */
+			if (j + tx < data_size) {
+				temp = ts_test->rawdata[i].data[j + tx];
+				temp = ABS(temp - raw);
+				max_val = MAX(max_val, temp);
+			}
+			/* calcu delta with left node */
+			if (j > 0 && j % tx) {
+				temp = ts_test->rawdata[i].data[j - 1];
+				temp = ABS(temp - raw);
+				max_val = MAX(max_val, temp);
+			}
+			/* calcu delta with right node */
+			if ((j + 1) % tx) {
+				temp = ts_test->rawdata[i].data[j + 1];
+				temp = ABS(temp - raw);
+				max_val = MAX(max_val, temp);
+			}
+			ts_test->accord_arr[i].data[j] = max_val * 1000 / raw;
+		}
+		ts_test->accord_arr[i].size = data_size;
+	}
+}
+
+static int goodix_cache_self_rawdata(struct goodix_ts_test *ts_test)
+{
+	int ret;
+	u32 sen_num = ts_test->test_params.sen_num;
+	u32 drv_num = ts_test->test_params.drv_num;
+	u32 data_size = sen_num + drv_num;
+	u32 data_addr = ts_test->test_params.self_rawdata_addr;
+	u32 flag_addr = ts_test->ts->ic_info.misc.frame_data_addr;
+	struct frame_head *frame_head;
+	struct goodix_ts_core *cd = ts_test->ts;
+	unsigned char frame_buf[GOODIX_MAX_FRAMEDATA_LEN];
+	unsigned char *cur_ptr;
+
+	if (cd->bus->ic_type == IC_TYPE_BERLIN_D) {
+		ret = ts_test_read(ts_test, flag_addr, frame_buf, sizeof(frame_buf));
+		if (ret < 0)
+			return ret;
+
+		if (checksum_cmp(frame_buf,
+			cd->ic_info.misc.frame_data_head_len, CHECKSUM_MODE_U8_LE)) {
+			ts_err("frame head checksum error");
+			return -EINVAL;
+		}
+		frame_head = (struct frame_head *)frame_buf;
+		if (checksum_cmp(frame_buf, frame_head->cur_frame_len, CHECKSUM_MODE_U16_LE)) {
+			ts_err("frame body checksum error");
+			return -EINVAL;
+		}
+		cur_ptr = frame_buf;
+		cur_ptr += cd->ic_info.misc.frame_data_head_len;
+		cur_ptr += cd->ic_info.misc.fw_attr_len;
+		cur_ptr += cd->ic_info.misc.fw_log_len;
+		cur_ptr += cd->ic_info.misc.mutual_struct_len;
+		memcpy((u8 *)ts_test->self_rawdata.data, cur_ptr + 10,
+				cd->ic_info.misc.self_struct_len - 10);
+	} else {
+		ret = ts_test_read(ts_test, data_addr,
+				(u8 *)ts_test->self_rawdata.data,
+				data_size * sizeof(s16));
+		if (ret < 0)
+			return ret;
+	}
+	ts_test->self_rawdata.size = data_size;
+
+	return ret;
+}
+
+static int goodix_cache_noisedata(struct goodix_ts_test *ts_test)
+{
+	int ret;
+	int i;
+	int cnt;
+	int retry;
+	u8 val;
+	unsigned char frame_buf[GOODIX_MAX_FRAMEDATA_LEN];
+	unsigned char *cur_ptr;
+	struct frame_head *frame_head;
+	struct goodix_ts_cmd temp_cmd;
+	struct goodix_ts_core *cd = ts_test->ts;
+	u32 sen_num = ts_test->test_params.sen_num;
+	u32 drv_num = ts_test->test_params.drv_num;
+	u32 data_size = sen_num * drv_num;
+	u32 data_addr = ts_test->test_params.noisedata_addr;
+	u32 flag_addr = ts_test->ts->ic_info.misc.touch_data_addr;
+
+	if (cd->bus->ic_type == IC_TYPE_BERLIN_D) {
+		flag_addr = ts_test->ts->ic_info.misc.frame_data_addr;
+		temp_cmd.cmd = 0x90;
+		temp_cmd.data[0] = 0x82;
+		temp_cmd.len = 5;
+		ret = ts_test_send_cmd(ts_test, &temp_cmd);
+		if (ret < 0) {
+			ts_err("switch diffdata mode failed, exit!");
+			return ret;
+		}
+	}
+
+	for (cnt = 0; cnt < NOISEDATA_TEST_TIMES; cnt++) {
+		val = 0;
+		ret = ts_test_write(ts_test, flag_addr, &val, 1);
+		if (ret < 0) {
+			ts_err("clean touch event failed, exit");
+			return -EAGAIN;
+		}
+		retry = 20;
+		while (retry--) {
+			usleep_range(5000, 5100);
+			ret = ts_test_read(ts_test, flag_addr, &val, 1);
+			if (!ret && (val & 0x80))
+				break;
+		}
+		if (retry < 0) {
+			ts_err("noisedata is not ready val:0x%02x i:%d, exit", val, cnt);
+			return -EAGAIN;
+		}
+
+		if (cd->bus->ic_type == IC_TYPE_BERLIN_D) {
+			ret = ts_test_read(ts_test, flag_addr, frame_buf, sizeof(frame_buf));
+			if (ret < 0)
+				return ret;
+			if (checksum_cmp(frame_buf,
+					cd->ic_info.misc.frame_data_head_len,
+					CHECKSUM_MODE_U8_LE)) {
+				ts_err("frame head checksum error");
+				return -EINVAL; 
+			}
+			frame_head = (struct frame_head *)frame_buf;
+			if (checksum_cmp(frame_buf,
+					frame_head->cur_frame_len,
+					CHECKSUM_MODE_U16_LE)) {
+				ts_err("frame body checksum error");
+				return -EINVAL;
+			}
+			cur_ptr = frame_buf;
+			cur_ptr += cd->ic_info.misc.frame_data_head_len;
+			cur_ptr += cd->ic_info.misc.fw_attr_len;
+			cur_ptr += cd->ic_info.misc.fw_log_len;
+			memcpy((u8 *)ts_test->noisedata[cnt].data, cur_ptr + 8,
+					cd->ic_info.misc.mutual_struct_len - 8);
+		} else {
+			ret = ts_test_read(ts_test, data_addr,
+				(u8 *)ts_test->noisedata[cnt].data, data_size * sizeof(s16));
+			if (ret < 0)
+				return ret;
+		}
+
+		ts_test->noisedata[cnt].size = data_size;
+		goodix_rotate_abcd2cbad(drv_num, sen_num, ts_test->noisedata[cnt].data);
+		for (i = 0; i < data_size; i++)
+			ts_test->noisedata[cnt].data[i] = ABS(ts_test->noisedata[cnt].data[i]);
+	}
+
+	return ret;
+}
+
+static int goodix_cache_self_noisedata(struct goodix_ts_test *ts_test)
+{
+	int ret;
+	int i;
+	u32 sen_num = ts_test->test_params.sen_num;
+	u32 drv_num = ts_test->test_params.drv_num;
+	u32 data_size = sen_num + drv_num;
+	u32 data_addr = ts_test->test_params.self_noisedata_addr;
+	u32 flag_addr = ts_test->ts->ic_info.misc.frame_data_addr;
+	struct frame_head *frame_head;
+	struct goodix_ts_core *cd = ts_test->ts;
+	unsigned char frame_buf[GOODIX_MAX_FRAMEDATA_LEN];
+	unsigned char *cur_ptr;
+
+	if (cd->bus->ic_type == IC_TYPE_BERLIN_D) {
+		ret = ts_test_read(ts_test, flag_addr, frame_buf, sizeof(frame_buf));
+		if (ret < 0)
+			return ret;
+		if (checksum_cmp(frame_buf,
+			cd->ic_info.misc.frame_data_head_len, CHECKSUM_MODE_U8_LE)) {
+			ts_err("frame head checksum error");
+			return -EINVAL;
+		}
+		frame_head = (struct frame_head *)frame_buf;
+		if (checksum_cmp(frame_buf, frame_head->cur_frame_len, CHECKSUM_MODE_U16_LE)) {
+			ts_err("frame body checksum error");
+			return -EINVAL;
+		}
+		cur_ptr = frame_buf;
+		cur_ptr += cd->ic_info.misc.frame_data_head_len;
+		cur_ptr += cd->ic_info.misc.fw_attr_len;
+		cur_ptr += cd->ic_info.misc.fw_log_len;
+		cur_ptr += cd->ic_info.misc.mutual_struct_len;
+		memcpy((u8 *)ts_test->self_noisedata.data, cur_ptr + 10,
+				cd->ic_info.misc.self_struct_len - 10);
+	} else {
+		ret = ts_test_read(ts_test, data_addr,
+			(u8 *)ts_test->self_noisedata.data, data_size * sizeof(s16));
+		if (ret < 0)
+			return ret;
+	}
+
+	ts_test->self_noisedata.size = data_size;
+	for (i = 0; i < data_size; i++)
+		ts_test->self_noisedata.data[i] = ABS(ts_test->self_noisedata.data[i]);
+
+	return ret;
+}
+
+static int goodix_analysis_rawdata(struct goodix_ts_test *ts_test)
+{
+	int i;
+	int j;
+	bool fail_flag = false;
+	int err_cnt = 0;
+	int times = TOTAL_FRAME_NUM;
+	s16 val;
+	u32 data_size = ts_test->rawdata[0].size;
+
+	for (i = 0; i < times; i++) {
+		for (j = 0; j < data_size; j++) {
+			val = ts_test->rawdata[i].data[j];
+			if (val < ts_test->test_params.min_limits[j]) {
+				fail_flag = true;
+				ts_test->open_res.beyond_min_limit_cnt[j]++;
+			}
+			if (val > ts_test->test_params.max_limits[j]) {
+				fail_flag = true;
+				ts_test->open_res.beyond_max_limit_cnt[j]++;
+			}
+		}
+		if (fail_flag)
+			err_cnt++;
+		fail_flag = false;
+	}
+
+	if (err_cnt > 0)
+		ts_err("rawdata have %d frames out of range", err_cnt);
+
+	err_cnt *= 100;
+	if (err_cnt > times * 100 * 9 / 10)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int goodix_analysis_deltadata(struct goodix_ts_test *ts_test)
+{
+	int i;
+	int j;
+	int ret = 0;
+	s16 val;
+	u32 data_size = ts_test->accord_arr[0].size;
+
+	for (i = 0; i < TOTAL_FRAME_NUM; i++) {
+		for (j = 0; j < data_size; j++) {
+			val = ts_test->accord_arr[i].data[j];
+			if (val > ts_test->test_params.deviation_limits[j]) {
+				ts_test->open_res.beyond_accord_limit_cnt[j]++;
+				ret = -EINVAL;
+			}
+		}
+	}
+
+	return ret;
+}
+
+static int goodix_analysis_self_rawdata(struct goodix_ts_test *ts_test)
+{
+	int i;
+	s16 val;
+	u32 data_size = ts_test->self_rawdata.size;
+
+	for (i = 0; i < data_size; i++) {
+		val = ts_test->self_rawdata.data[i];
+		if (val < ts_test->test_params.self_min_limits[i] ||
+			val > ts_test->test_params.self_max_limits[i]) {
+			ts_err("self_rawdata isn't in range, val:%d threshold:[%d,%d]",
+				val, ts_test->test_params.self_min_limits[i],
+				ts_test->test_params.self_max_limits[i]);
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+static int goodix_analysis_noisedata(struct goodix_ts_test *ts_test)
+{
+	int cnt;
+	int i;
+	bool fail_flag = false;
+	int err_cnt = 0;
+	int times = NOISEDATA_TEST_TIMES;
+	s16 val;
+	u32 data_size = ts_test->noisedata[0].size;
+
+	for (cnt = 0; cnt < times; cnt++) {
+		for (i = 0; i < data_size; i++) {
+			val = ts_test->noisedata[cnt].data[i];
+			if (val > ts_test->test_params.noise_threshold)
+				fail_flag = true;
+		}
+		if (fail_flag)
+			err_cnt++;
+		fail_flag = false;
+	}
+
+	if (err_cnt > 0)
+		ts_err("noisedata have %d frames out of range", err_cnt);
+
+	err_cnt *= 100;
+	if (err_cnt > times * 100 * 2 / 10)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int goodix_analysis_self_noisedata(struct goodix_ts_test *ts_test)
+{
+	int i;
+	s16 val;
+	u32 data_size = ts_test->self_noisedata.size;
+
+	for (i = 0; i < data_size; i++) {
+		val = ts_test->self_noisedata.data[i];
+		if (val > ts_test->test_params.self_noise_threshold) {
+			ts_err("self noisedata isn't in range, val:%d threshold:[0,%d]",
+				val, ts_test->test_params.self_noise_threshold);
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+static void goodix_capacitance_test(struct goodix_ts_test *ts_test)
+{
+	int ret;
+
+	ts_info("---------------------- cap_test begin ----------------------");
+	ret = goodix_cap_test_prepare(ts_test);
+	if (ret < 0) {
+		ts_err("cap_test prepare failed, exit");
+		goto exit;
+	}
+	ts_info("cap rawdata prepare OK");
+
+	/* obtain rawdata */
+	ret = goodix_cache_rawdata(ts_test);
+	if (ret < 0) {
+		if (ret == -EAGAIN) {
+			ts_err("Capacitance exit");
+			goto exit;
+		} else
+			ts_err("Failed to read capdata");
+
+	} else {
+		ts_info("get rawdata finish, start analysis");
+		ret = goodix_analysis_rawdata(ts_test);
+		if (ret < 0)
+			ts_test->test_result[GTP_CAP_TEST] = GTP_PANEL_REASON;
+		else
+			ts_test->test_result[GTP_CAP_TEST] = GTP_TEST_PASS;
+	}
+
+	/* obtain delta_data */
+	goodix_cache_deltadata(ts_test);
+	ts_info("get deltadata finish, start analysis");
+	ret = goodix_analysis_deltadata(ts_test);
+	if (ret < 0)
+		ts_test->test_result[GTP_DELTA_TEST] = GTP_PANEL_REASON;
+	else
+		ts_test->test_result[GTP_DELTA_TEST] = GTP_TEST_PASS;
+
+	/* obtain self_rawdata */
+	if (ts_test->test_params.test_items[GTP_SELFCAP_TEST]) {
+		ret = goodix_cache_self_rawdata(ts_test);
+		if (ret < 0) {
+			ts_err("Failed to read self_capdata");
+		} else {
+			ts_info("get self_rawdata finish, start analysis");
+			ret = goodix_analysis_self_rawdata(ts_test);
+			if (ret < 0)
+				ts_test->test_result[GTP_SELFCAP_TEST] = GTP_PANEL_REASON;
+			else
+				ts_test->test_result[GTP_SELFCAP_TEST] = GTP_TEST_PASS;
+		}
+	}
+
+	/* obtain noisedata */
+	if (ts_test->test_params.test_items[GTP_NOISE_TEST]) {
+		ret = goodix_cache_noisedata(ts_test);
+		if (ret < 0) {
+			ts_err("Failed to read noisedata");
+		} else {
+			ts_info("get noisedata finish, start analysis");
+			ret = goodix_analysis_noisedata(ts_test);
+			if (ret < 0)
+				ts_test->test_result[GTP_NOISE_TEST] = GTP_PANEL_REASON;
+			else
+				ts_test->test_result[GTP_NOISE_TEST] = GTP_TEST_PASS;
+		}
+	}
+
+	/* obtain self_noisedata */
+	if (ts_test->test_params.test_items[GTP_SELFNOISE_TEST]) {
+		ret = goodix_cache_self_noisedata(ts_test);
+		if (ret < 0) {
+			ts_err("Failed to read self_noisedata");
+		} else {
+			ts_info("get self_noisedata finish, start analysis");
+			ret = goodix_analysis_self_noisedata(ts_test);
+			if (ret < 0)
+				ts_test->test_result[GTP_SELFNOISE_TEST] = GTP_PANEL_REASON;
+			else
+				ts_test->test_result[GTP_SELFNOISE_TEST] = GTP_TEST_PASS;
+		}		
+	}
+
+exit:
+	goodix_cap_test_finish(ts_test);
+}
+
+char *goodix_strncat(char *dest, char *src, size_t dest_size)
+{
+	size_t dest_len = 0;
+
+	dest_len = strnlen(dest, dest_size);
+	strlcat(&dest[dest_len], src, dest_size - dest_len - 1);
+	return dest;
+}
+
+char *goodix_strncatint(char *dest, int src, char *format, size_t dest_size)
+{
+	char src_str[MAX_STR_LEN] = {0};
+
+	snprintf(src_str, MAX_STR_LEN, format, src);
+	return goodix_strncat(dest, src_str, dest_size);
+}
+
+static void goodix_data_cal(s16 *data, size_t data_size, s16 *stat_result)
+{
+	int i = 0;
+	s16 avg = 0;
+	s16 min = 0;
+	s16 max = 0;
+	long long sum = 0;
+
+	min = data[0];
+	max = data[0];
+	for (i = 0; i < data_size; i++) {
+		sum += data[i];
+		if (max < data[i])
+			max = data[i];
+		if (min > data[i])
+			min = data[i];
+	}
+	avg = div_s64(sum, data_size);
+	stat_result[0] = avg;
+	stat_result[1] = max;
+	stat_result[2] = min;
+}
+
+static void goodix_data_statistics(s16 *data, size_t data_size,
+		char *result, size_t res_size)
+{
+	s16 stat_value[3];
+
+	if (!data || !result) {
+		ts_err("parameters error please check *data and *result value");
+		return;
+	}
+
+	if (data_size <= 0 || res_size <= 0) {
+		ts_err("input parameter is illegva:data_size=%ld, res_size=%ld",
+			data_size, res_size);
+		return;
+	}
+	goodix_data_cal(data, data_size, stat_value);
+
+	memset(result, 0, res_size);
+	snprintf(result, res_size, "[%d,%d,%d]",
+			stat_value[0], stat_value[1], stat_value[2]);
+	return;
+}
+
+#ifdef SAVE_IN_CSV
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)
+static ssize_t fs_write(const void* buf, size_t size, struct file* fp)
+{
+	loff_t pos;
+	ssize_t len;
+
+	pos = fp->f_pos;
+	len = kernel_write(fp, buf, size, &pos);
+	fp->f_pos = pos;
+
+	return len;
+}
+#else
+static ssize_t fs_write(const void* buf, size_t size, struct file* fp)
+{
+	mm_segment_t old_fs;
+	loff_t pos;
+	ssize_t len;
+
+	pos = fp->f_pos;
+	old_fs = get_fs();
+	set_fs(KERNEL_DS);
+	len = vfs_write(fp, buf, size, &pos);
+	set_fs(old_fs);
+	fp->f_pos = pos;
+
+	return len;
+}
+#endif
+
+static int goodix_save_test_config(struct goodix_ts_test *ts_test,
+		struct file *fp)
+{
+	int ret = 0;
+	int i;
+	int bytes = 0;
+	char *data;
+	struct goodix_ic_config *cfg = &ts_test->test_config;
+
+	if (cfg->len <= 0) {
+		ts_info("Can't find vaild test config");
+		return 0;
+	}
+
+	data = kzalloc(MAX_DATA_BUFFER, GFP_KERNEL);
+	if (!data) {
+		ts_err("alloc memory failed");
+		return -ENOMEM;
+	}
+
+	bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "<OrderConfig>\n");
+	for (i = 0; i < cfg->len; i++)
+		bytes += scnprintf(&data[bytes],
+				MAX_DATA_BUFFER - bytes, "0x%02x,", cfg->data[i]);
+
+	bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "\n");
+	bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "</OrderConfig>\n");
+	ret = fs_write(data, bytes, fp);
+	if (ret < 0) {
+		ts_err("test config write failed");
+		goto save_end;
+	}
+
+save_end:
+	kfree(data);
+	return ret;
+}
+
+static int goodix_save_header(struct goodix_ts_test *ts_test,
+		struct file *fp)
+{
+	int ret;
+	int i;
+	int bytes = 0;
+	bool result = false;
+	char *data = NULL;
+	struct goodix_ts_core *ts = ts_test->ts;
+
+	data = kzalloc(MAX_DATA_BUFFER, GFP_KERNEL);
+	if (!data) {
+		ts_err("alloc memory failed");
+		return -ENOMEM;
+	}
+
+	bytes += scnprintf(&data[bytes],
+				MAX_DATA_BUFFER - bytes,
+				"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
+	bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "<TESTLOG>\n");
+	bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "<Header>\n");
+	/* sava test result */
+	for (i = 0; i < MAX_TEST_ITEMS; i++) {
+		if ((ts_test->test_result[i] > 0) &&
+			(ts_test->test_result[i] != GTP_TEST_PASS)) {
+				result = true;
+				break;
+		}
+	}
+	if (result)
+		bytes += scnprintf(&data[bytes],
+				MAX_DATA_BUFFER - bytes,
+				"<Result>NG</Result>\n");
+	else
+		bytes += scnprintf(&data[bytes],
+				MAX_DATA_BUFFER - bytes,
+				"<Result>OK</Result>\n");
+	bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes,
+				"<DeviceType>GT%s</DeviceType>\n",
+				ts->fw_version.patch_pid);
+	bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes,
+				"<SensorId>%d</SensorId>\n",
+				ts_test->ts->fw_version.sensor_id);
+
+	ret = fs_write(data, bytes, fp);
+	if (ret < 0) {
+		ts_err("header write failed");
+		goto save_end;
+	}
+	bytes = 0;
+	/* save test config */
+	ret = goodix_save_test_config(ts_test, fp);
+	if (ret < 0)  {
+		ts_err("save test config failed");
+		goto save_end;
+	}
+
+	bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "</Header>\n");
+	ret = fs_write(data, bytes, fp);
+	if (ret < 0) {
+		ts_err("header write failed");
+		goto save_end;
+	}
+	bytes = 0;
+
+	/* item list */
+	bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "<ItemList>\n");
+	if (ts_test->test_result[GTP_CAP_TEST]) {
+		if (GTP_TEST_PASS == ts_test->test_result[GTP_CAP_TEST])
+			bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes,
+					"<Item name=\"Rawdata MAX/MIN Test\" result=\"OK\"/>\n");
+		else
+			bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes,
+					"<Item name=\"Rawdata MAX/MIN Test\" result=\"NG\"/>\n");
+	}
+
+	if (ts_test->test_result[GTP_DELTA_TEST]) {
+		if (GTP_TEST_PASS == ts_test->test_result[GTP_DELTA_TEST])
+			bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes,
+			      "<Item name=\"Rawdata Adjcent Deviation Test\" result=\"OK\"/>\n");
+		else
+			bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes,
+			      "<Item name=\"Rawdata Adjcent Deviation Test\" result=\"NG\"/>\n");
+	}
+
+	if (ts_test->test_result[GTP_NOISE_TEST]) {
+		if (GTP_TEST_PASS == ts_test->test_result[GTP_NOISE_TEST])
+			bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes,
+					"<Item name=\"Diffdata Jitter Test\" result=\"OK\"/>\n");
+		else
+			bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes,
+					"<Item name=\"Diffdata Jitter Test\" result=\"NG\"/>\n");
+	}
+
+	if (ts_test->test_result[GTP_SELFNOISE_TEST]) {
+		if (GTP_TEST_PASS == ts_test->test_result[GTP_SELFNOISE_TEST])
+			bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes,
+			      "<Item name=\"Self Diffdata Jitter Limit Test\" result=\"OK\"/>\n");
+		else
+			bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes,
+			      "<Item name=\"Self Diffdata Jitter Limit Test\" result=\"NG\"/>\n");
+	}
+
+	if (ts_test->test_result[GTP_SELFCAP_TEST]) {
+		if (GTP_TEST_PASS == ts_test->test_result[GTP_SELFCAP_TEST])
+			bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes,
+			      "<Item name=\"Self Rawdata Upper Limit Test\" result=\"OK\"/>\n");
+		else
+			bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes,
+			      "<Item name=\"Self Rawdata Upper Limit Test\" result=\"NG\"/>\n");
+	}
+
+	if (ts_test->test_result[GTP_SHORT_TEST]) {
+		if (GTP_TEST_PASS == ts_test->test_result[GTP_SHORT_TEST])
+			bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes,
+					"<Item name=\"Short Test\" result=\"OK\"/>\n");
+		else
+			bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes,
+					"<Item name=\"Short Test\" result=\"NG\"/>\n");
+	}
+
+	bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "</ItemList>\n");
+	ret = fs_write(data, bytes, fp);
+	if (ret < 0) {
+		ts_err("item list write failed");
+		goto save_end;
+	}
+
+save_end:
+	kfree(data);
+	return ret;
+}
+
+static int goodix_save_limits(struct goodix_ts_test *ts_test,
+		struct file *fp)
+{
+	int ret;
+	int i;
+	int bytes = 0;
+	char *data = NULL;
+	int tx = ts_test->test_params.drv_num;
+	int rx = ts_test->test_params.sen_num;
+	int chn1;
+	int chn2;
+	int r;
+
+	data = kzalloc(MAX_DATA_BUFFER, GFP_KERNEL);
+	if (!data) {
+		return -ENOMEM;
+
+	bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "<TestItems>\n");
+
+	/* save short result */
+	if (ts_test->test_result[GTP_SHORT_TEST]) {
+		bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes,
+				"<Item name=\"Short Test\">\n");
+		bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes,
+				"<ShortNum>%d</ShortNum>\n",
+				ts_test->short_res.short_num);
+		for (i = 0; i < ts_test->short_res.short_num; i++) {
+			chn1 = ts_test->short_res.short_msg[4 * i];
+			chn2 = ts_test->short_res.short_msg[4 * i + 1];
+			r = (ts_test->short_res.short_msg[4 * i + 2] << 8) +
+				ts_test->short_res.short_msg[4 * i + 3];
+			if (chn1 == CHN_VDD)
+				bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes,
+						"<ShortMess Chn1=\"VDD\" ");
+			else if (chn1 == CHN_GND)
+				bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes,
+						"<ShortMess Chn1=\"GND\" ");
+			else if (chn1 & DRV_CHANNEL_FLAG)
+				bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes,
+						"<ShortMess Chn1=\"Tx%d\" ",
+						chn1 & 0x7f);
+			else
+				bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes,
+						"<ShortMess Chn1=\"Rx%d\" ",
+						chn1 & 0x7f);
+			if (chn2 == CHN_VDD)
+				bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes,
+						"Chn2=\"VDD\" ShortResistor= \"%dKom\"/>\n", r);
+			else if (chn2 == CHN_GND)
+				bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes,
+						"Chn2=\"GND\" ShortResistor= \"%dKom\"/>\n", r);
+			else if (chn2 & DRV_CHANNEL_FLAG)
+				bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes,
+						"Chn2=\"Tx%d\" ShortResistor= \"%dKom\"/>\n",
+						chn2 & 0x7f, r);
+			else
+				bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes,
+						"Chn2=\"Rx%d\" ShortResistor= \"%dKom\"/>\n",
+						chn2 & 0x7f, r);
+		}
+		bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "</Item>\n");
+		ret = fs_write(data, bytes, fp);
+		if (ret < 0) {
+			ts_err("short res write fail.");
+			goto save_end;
+		}
+		bytes = 0;
+	}
+
+	/* rawdata max limit */
+	bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes,
+			"<Item name=\"Rawdata Test Sets\">\n");
+	bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes,
+			"<TotalFrameCnt>%d</TotalFrameCnt>\n", TOTAL_FRAME_NUM);
+	bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "<MaxRawLimit>\n");
+	for (i = 0; i < tx * rx; i++) {
+		bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "%d,",
+			ts_test->test_params.max_limits[i]);
+		if ((i + 1) % tx == 0)
+			bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "\n");
+	}
+	bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "</MaxRawLimit>\n");
+	/* BeyondRawdataUpperLimit */
+	bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes,
+			"<BeyondRawdataUpperLimitCnt>\n");
+	for (i = 0; i < tx * rx; i++) {
+		bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "%d,",
+				ts_test->open_res.beyond_max_limit_cnt[i]);
+		if ((i + 1) % tx == 0)
+			bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "\n");
+	}
+	bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes,
+			"</BeyondRawdataUpperLimitCnt>\n");
+	ret = fs_write(data, bytes, fp);
+	if (ret < 0) {
+		ts_err("rawdata limit write failed");
+		goto save_end;
+	}
+	bytes = 0;
+
+	/* rawdata min limit */
+	bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "<MinRawLimit>\n");
+	for (i = 0; i < tx * rx; i++) {
+		bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "%d,",
+			ts_test->test_params.min_limits[i]);
+		if ((i + 1) % tx == 0)
+			bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "\n");
+	}
+	bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "</MinRawLimit>\n");
+	/* BeyondRawdataLower limit */
+	bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes,
+			"<BeyondRawdataLowerLimitCnt>\n");
+	for (i = 0; i < tx * rx; i++) {
+		bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "%d,",
+				ts_test->open_res.beyond_min_limit_cnt[i]);
+		if ((i + 1) % tx == 0)
+			bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "\n");
+	}
+	bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes,
+			"</BeyondRawdataLowerLimitCnt>\n");
+	ret = fs_write(data, bytes, fp);
+	if (ret < 0) {
+		ts_err("rawdata limit write failed");
+		goto save_end;
+	}
+	bytes = 0;
+
+	/* Max Accord limit */
+	bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "<MaxAccordLimit>\n");
+	for (i = 0; i < tx * rx; i++) {
+		bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "%d,",
+			ts_test->test_params.deviation_limits[i]);
+		if ((i + 1) % tx == 0)
+			bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "\n");
+	}
+	bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "</MaxAccordLimit>\n");
+	/* BeyondAccordLimitCnt */
+	bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "<BeyondAccordLimitCnt>\n");
+	for (i = 0; i < tx * rx; i++) {
+		bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "%d,",
+			ts_test->open_res.beyond_accord_limit_cnt[i]);
+		if ((i + 1) % tx == 0)
+			bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "\n");
+	}
+	bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "</BeyondAccordLimitCnt>\n");
+	bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "</Item>\n");
+	ret = fs_write(data, bytes, fp);
+	if (ret < 0) {
+		ts_err("rawdata limit write failed");
+		goto save_end;
+	}
+	bytes = 0;
+
+	/* save noise limit */
+	if (ts_test->test_result[GTP_NOISE_TEST]) {
+		bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes,
+				"<Item name=\"Diffdata Test Sets\">\n");
+		bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes,
+				"<TotalFrameCnt>%d</TotalFrameCnt>\n",
+				NOISEDATA_TEST_TIMES);
+		bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes,
+				"<MaxJitterLimit>%d</MaxJitterLimit>\n",
+				ts_test->test_params.noise_threshold);
+		bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "</Item>\n");
+		ret = fs_write(data, bytes, fp);
+		if (ret < 0) {
+			ts_err("noise limit write failed");
+			goto save_end;
+		}
+		bytes = 0;
+	}
+
+	/* save self rawdata limit */
+	if (ts_test->test_result[GTP_SELFCAP_TEST]) {
+		bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes,
+				"<Item name=\"Self Rawdata Test Sets\">\n");
+		bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes,
+				"<TotalFrameCnt>1</TotalFrameCnt>\n");
+		bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "<MaxRawLimit>\n");
+		for (i = 0; i < tx + rx; i++) {
+			bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "%d,",
+					ts_test->test_params.self_max_limits[i]);
+			if ((i + 1) % tx == 0)
+				bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "\n");
+		}
+		if ((tx + rx) % tx != 0)
+			bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "\n");
+		bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "</MaxRawLimit>\n");
+		bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "<MinRawLimit>\n");
+		for (i = 0; i < tx + rx; i++) {
+			bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "%d,",
+					ts_test->test_params.self_min_limits[i]);
+			if ((i + 1) % tx == 0)
+				bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "\n");
+		}
+		if ((tx + rx) % tx != 0)
+			bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "\n");
+		bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "</MinRawLimit>\n");
+		bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "</Item>\n");
+		ret = fs_write(data, bytes, fp);
+		if (ret < 0) {
+			ts_err("self rawdata limit write failed");
+			goto save_end;
+		}
+		bytes = 0;
+	}
+
+	/* save selfnoise limit */
+	if (ts_test->test_result[GTP_SELFNOISE_TEST]) {
+		bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes,
+				"<Item name=\"Self Diffdata Test Sets\">\n");
+		bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes,
+				"<TotalFrameCnt>1</TotalFrameCnt>\n");
+		bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes,
+				"<MaxJitterLimit>%d</MaxJitterLimit>\n",
+				ts_test->test_params.self_noise_threshold);
+		bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "</Item>\n");
+		ret = fs_write(data, bytes, fp);
+		if (ret < 0) {
+			ts_err("raw limit write failed");
+			goto save_end;
+		}
+		bytes = 0;
+	}
+
+	bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "</TestItems>\n");
+	ret = fs_write(data, bytes, fp);
+	if (ret < 0)
+		ts_err("limit write fail.");
+
+save_end:
+	kfree(data);
+	return ret;
+}
+
+static int goodix_save_rawdata(struct goodix_ts_test *ts_test,
+		struct file *fp)
+{
+	int i;
+	int j;
+	int ret;
+	int bytes = 0;
+	s16 stat_result[3];
+	char *data = NULL;
+	int tx = ts_test->test_params.drv_num;
+	int rx = ts_test->test_params.sen_num;
+	int len = tx * rx;
+
+	data = kzalloc(MAX_DATA_BUFFER, GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "<RawDataRecord>\n");
+	for (i = 0; i < TOTAL_FRAME_NUM; i++) {
+		goodix_data_cal(ts_test->rawdata[i].data, len, stat_result);
+		bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes,
+			"<DataContent No.=\"%d\" DataCount=\"%d\" Maximum=\"%d\" Minimum=\"%d\" Average=\"%d\">\n",
+			i, len, stat_result[1], stat_result[2], stat_result[0]);
+		for (j = 0; j < len; j++) {
+			bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes,
+					"%d,", ts_test->rawdata[i].data[j]);
+			if ((j + 1) % tx == 0)
+				bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "\n");
+		}
+		bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "</DataContent>\n");
+		goodix_data_cal(ts_test->accord_arr[i].data, len, stat_result);
+		bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes,
+			"<RawAccord No.=\"%d\" DataCount=\"%d\" Maximum=\"%d\" Minimum=\"%d\" Average=\"%d\">\n",
+			i, len, stat_result[1], stat_result[2], stat_result[0]);
+		for (j = 0; j < len; j++) {
+			bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes,
+					"%d,", ts_test->accord_arr[i].data[j]);
+			if ((j + 1) % tx == 0)
+				bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "\n");
+		}
+		bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "</RawAccord>\n");
+		ret = fs_write(data, bytes, fp);
+		if (ret < 0) {
+			ts_err("rawdata write fail.");
+			goto save_end;
+		}
+		bytes = 0;
+	}
+
+	bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "</RawDataRecord>\n");
+	ret = fs_write(data, bytes, fp);
+	if (ret < 0)
+		ts_err("rawdata write fail.");
+
+save_end:
+	kfree(data);
+	return ret;
+}
+
+static int goodix_save_noise_data(struct goodix_ts_test *ts_test,
+		struct file *fp)
+{
+	int i;
+	int j;
+	int ret = 0;
+	int bytes = 0;
+	s16 stat_result[3];
+	char *data = NULL;
+	int tx = ts_test->test_params.drv_num;
+	int rx = ts_test->test_params.sen_num;
+	int len = tx * rx;
+
+	data = kzalloc(MAX_DATA_BUFFER, GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "<DiffDataRecord>\n");
+	for (i = 0; i < NOISEDATA_TEST_TIMES; i++) {
+		goodix_data_cal(ts_test->noisedata[i].data, len, stat_result);
+		bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes,
+			"<DataContent No.=\"%d\" DataCount=\"%d\" Maximum=\"%d\" Minimum=\"%d\" Average=\"%d\">\n",
+			i, len, stat_result[1], stat_result[2], stat_result[0]);
+		for (j = 0; j < len; j++) {
+			bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes,
+					"%d,", ts_test->noisedata[i].data[j]);
+			if ((j + 1) % tx == 0)
+				bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "\n");
+		}
+		bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "</DataContent>\n");
+		ret = fs_write(data, bytes, fp);
+		if (ret < 0) {
+			ts_err("noisedata write fail.");
+			goto save_end;
+		}
+		bytes = 0;
+	}
+
+	bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "</DiffDataRecord>\n");
+	ret = fs_write(data, bytes, fp);
+	if (ret < 0)
+		ts_err("noisedata write fail.");
+
+save_end:
+	kfree(data);
+	return ret;
+}
+
+static int goodix_save_self_data(struct goodix_ts_test *ts_test,
+		struct file *fp, s16 *src_data, u8 *title, int len)
+{
+	int i;
+	int ret = 0;
+	s32 bytes = 0;
+	char *data;
+	s16 stat_result[3];
+	int tx = ts_test->test_params.drv_num;
+
+	data = kzalloc(MAX_DATA_BUFFER, GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "<%s>\n",title);
+	ret = fs_write(data, bytes, fp);
+	if (ret < 0) {
+		ts_err("rawdata write fail.");
+		goto save_end;
+	}
+	bytes = 0;
+
+	goodix_data_cal(src_data, len, stat_result);
+	bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes,
+		"<DataContent No.=\"0\" DataCount=\"%d\" Maximum=\"%d\" Minimum=\"%d\" Average=\"%d\">\n",
+		len, stat_result[1], stat_result[2], stat_result[0]);
+	for (i = 0; i < len; i++) {
+		bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "%d,", src_data[i]);
+		if ((i + 1) % tx == 0)
+			bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "\n");
+	}
+	if (len % tx != 0)
+		bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "\n");
+	bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "</DataContent>\n");
+	bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "</%s>\n",title);
+	ret = fs_write(data, bytes, fp);
+	if (ret < 0)
+		ts_err("rawdata write fail.");
+
+save_end:
+	kfree(data);
+	return ret;
+}
+
+static int goodix_save_data(struct goodix_ts_test *ts_test,
+		struct file *fp)
+{
+	int ret;
+	int bytes = 0;
+	char *data = NULL;
+
+	data = kzalloc(MAX_DATA_BUFFER, GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "<DataRecord>\n");
+	ret = fs_write(data, bytes, fp);
+	if (ret < 0) {
+		ts_err("rawdata record lable failed");
+		goto save_end;
+	}
+	bytes = 0;
+
+	ret = goodix_save_rawdata(ts_test, fp);
+	if (ret < 0)
+		goto save_end;
+
+	if (ts_test->test_result[GTP_NOISE_TEST]) {
+		ret = goodix_save_noise_data(ts_test, fp);
+		if (ret < 0)
+			goto save_end;
+	}
+
+	if (ts_test->test_result[GTP_SELFCAP_TEST]) {
+		ret = goodix_save_self_data(ts_test, fp,
+				ts_test->self_rawdata.data,
+				"selfDataRecord",
+				ts_test->self_rawdata.size);
+		if (ret < 0)
+			goto save_end;
+	}
+
+	if (ts_test->test_result[GTP_SELFNOISE_TEST]) {
+		ret = goodix_save_self_data(ts_test, fp,
+				ts_test->self_noisedata.data,
+				"selfDiffDataRecord",
+				ts_test->self_noisedata.size);
+		if (ret < 0)
+			goto save_end;
+	}
+
+	bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "</DataRecord>\n");
+	ret = fs_write(data, bytes, fp);
+	if (ret < 0)
+		ts_err("rawdata data record lable fail.");
+
+save_end:
+	kfree(data);
+	return ret;
+}
+
+/* save end tag in csv file */
+static int goodix_save_tail(struct goodix_ts_test *ts_test,
+		struct file *fp)
+{
+	int ret = 0;
+	int bytes = 0;
+	char *data = NULL;
+
+	data = kzalloc(MAX_DATA_BUFFER, GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER, "</TESTLOG>\n");
+	ret = fs_write(data, bytes, fp);
+	if (ret < 0)
+		ts_err("tail write failed");
+
+	kfree(data);
+	return ret;
+}
+
+static void goodix_save_result_data(struct goodix_ts_test *ts_test)
+{
+	int ret = 0;
+	char save_path[100];
+	struct file *fp = NULL;
+
+	/* format result file */
+	scnprintf(save_path, ARRAY_SIZE(save_path), GOODIX_RESULT_SAVE_PATH);
+	ts_info("save result IN, file_name:%s", save_path);
+
+	fp = filp_open(save_path, O_CREAT | O_WRONLY | O_TRUNC, 0666);
+	if (IS_ERR(fp)) {
+		ts_err("create file:%s failed, fp:%ld", save_path, PTR_ERR(fp));
+		return;
+	}
+
+	/* save header */
+	ret = goodix_save_header(ts_test, fp);
+	if (ret < 0)
+		goto save_end;
+
+	/* save limits */
+	ret = goodix_save_limits(ts_test, fp);
+	if (ret < 0)
+		goto save_end;
+
+	/* save data */
+	ret = goodix_save_data(ts_test, fp);
+	if (ret < 0)
+		goto save_end;
+
+	/* save tail */
+	ret = goodix_save_tail(ts_test, fp);
+	if (ret < 0)
+		goto save_end;
+
+	ts_info("the test result save in %s", save_path);
+save_end:
+	filp_close(fp, NULL);
+}
+#endif // SAVE_IN_CSV
+
+static void goodix_put_test_result(struct goodix_ts_test *ts_test,
+		struct ts_rawdata_info *info)
+{
+	int i;
+	bool have_bus_error = false;
+	bool have_panel_error = false;
+	char statistics_data[STATISTICS_DATA_LEN] = {0};
+	struct goodix_ts_core *ts = ts_test->ts;
+
+	ts_info("put test result IN");
+
+	info->buff[0] = ts_test->test_params.sen_num;
+	info->buff[1] = ts_test->test_params.drv_num;
+	info->used_size = 2;
+	/* save rawdata to info->buff, only one frame */
+	if (ts_test->rawdata[0].size) {
+		for (i = 0; i < ts_test->rawdata[0].size; i++)
+			info->buff[info->used_size + i] = ts_test->rawdata[0].data[i];
+		info->used_size += ts_test->rawdata[0].size;
+	}
+
+	/* save noisedata to info->buff */
+	if (ts_test->noisedata[0].size) {
+		for (i = 0; i < ts_test->noisedata[0].size; i++)
+			info->buff[info->used_size + i] = ts_test->noisedata[0].data[i];
+		info->used_size += ts_test->noisedata[0].size;
+	}
+
+	/* save self_noisedata to info->buff */
+	if (ts_test->self_noisedata.size) {
+		for (i = 0; i < ts_test->self_noisedata.size; i++)
+			info->buff[info->used_size + i] = ts_test->self_noisedata.data[i];
+		info->used_size += ts_test->self_noisedata.size;
+	}
+
+	/* save self_rawdata to info->buff */
+	if (ts_test->self_rawdata.size) {
+		for (i = 0; i < ts_test->self_rawdata.size; i++)
+			info->buff[info->used_size + i] = ts_test->self_rawdata.data[i];
+		info->used_size += ts_test->self_rawdata.size;
+	}
+
+	/* check if there have bus error */
+	for (i = 0; i < MAX_TEST_ITEMS; i++) {
+		if (ts_test->test_result[i] == SYS_SOFTWARE_REASON)
+			have_bus_error = true;
+		else if (ts_test->test_result[i] == GTP_PANEL_REASON)
+			have_panel_error = true;
+	}
+	ts_info("Have bus error:%d", have_bus_error);
+	if (have_bus_error || have_panel_error)
+		goodix_strncat(ts_test->test_info, "[FAIL]-",
+				TS_RAWDATA_RESULT_MAX);
+	else
+		goodix_strncat(ts_test->test_info, "[PASS]-",
+				TS_RAWDATA_RESULT_MAX);
+
+	if (have_bus_error)
+		goodix_strncat(ts_test->test_info, "0F-",
+				TS_RAWDATA_RESULT_MAX);
+	else
+		goodix_strncat(ts_test->test_info, "0P-",
+				TS_RAWDATA_RESULT_MAX);
+
+	for (i = 0; i < MAX_TEST_ITEMS; i++) {
+		/* if have tested, show result */
+		if (ts_test->test_result[i]) {
+			if (ts_test->test_result[i] == GTP_TEST_PASS)
+				goodix_strncatint(ts_test->test_info, i, "%dP-",
+					TS_RAWDATA_RESULT_MAX);
+			else
+				goodix_strncatint(ts_test->test_info, i, "%dF-",
+					TS_RAWDATA_RESULT_MAX);
+		}
+	}
+
+	/* calculate rawdata min avg max value*/
+	if (ts_test->rawdata[0].size) {
+		goodix_data_statistics(
+				ts_test->rawdata[0].data,
+				ts_test->rawdata[0].size,
+				statistics_data,
+				STATISTICS_DATA_LEN);
+		goodix_strncat(ts_test->test_info, statistics_data,
+			TS_RAWDATA_RESULT_MAX);
+	} else {
+		ts_err("NO valiable rawdata");
+		goodix_strncat(ts_test->test_info, "[0,0,0]",
+			TS_RAWDATA_RESULT_MAX);
+	}
+
+	/* calculate noisedata min avg max value*/
+	if (ts_test->test_params.test_items[GTP_NOISE_TEST]) {
+		if (ts_test->noisedata[0].size) {
+			goodix_data_statistics(
+					ts_test->noisedata[0].data,
+					ts_test->noisedata[0].size,
+					statistics_data,
+					STATISTICS_DATA_LEN);
+			goodix_strncat(ts_test->test_info, statistics_data,
+				TS_RAWDATA_RESULT_MAX);
+		} else {
+			ts_err("NO valiable noisedata");
+			goodix_strncat(ts_test->test_info, "[0,0,0]",
+				TS_RAWDATA_RESULT_MAX);
+		}
+	}
+
+	/* calculate self_rawdata min avg max value*/
+	if (ts_test->test_params.test_items[GTP_SELFCAP_TEST]) {
+		if (ts_test->self_rawdata.size <=
+			sizeof(ts_test->self_rawdata.data) / sizeof(ts_test->self_rawdata.data[0])) {
+			goodix_data_statistics(
+					ts_test->self_rawdata.data,
+					ts_test->self_rawdata.size,
+					statistics_data,
+					STATISTICS_DATA_LEN);
+			goodix_strncat(ts_test->test_info, statistics_data,
+				TS_RAWDATA_RESULT_MAX);
+		} else {
+			ts_err("NO valiable self_rawdata");
+			goodix_strncat(ts_test->test_info, "[0,0,0]",
+				TS_RAWDATA_RESULT_MAX);
+		}
+	}
+
+	/* calculate self_noisedata min avg max value*/
+	if (ts_test->test_params.test_items[GTP_SELFNOISE_TEST]) {
+		if (ts_test->self_noisedata.size <=
+			sizeof(ts_test->self_noisedata.data) / sizeof(ts_test->self_noisedata.data[0])) {
+			goodix_data_statistics(
+					ts_test->self_noisedata.data,
+					ts_test->self_noisedata.size,
+					statistics_data,
+					STATISTICS_DATA_LEN);
+			goodix_strncat(ts_test->test_info, statistics_data,
+				TS_RAWDATA_RESULT_MAX);
+		} else {
+			ts_err("NO valiable self_noisedata");
+			goodix_strncat(ts_test->test_info, "[0,0,0]",
+				TS_RAWDATA_RESULT_MAX);
+		}
+	}
+
+	goodix_strncat(ts_test->test_info, "-GT",
+		TS_RAWDATA_RESULT_MAX);
+	goodix_strncat(ts_test->test_info, ts->fw_version.patch_pid,
+		TS_RAWDATA_RESULT_MAX);
+	goodix_strncat(ts_test->test_info, "\n",
+		TS_RAWDATA_RESULT_MAX);
+	strlcpy(info->result, ts_test->test_info, TS_RAWDATA_RESULT_MAX - 1);
+
+#ifdef SAVE_IN_CSV
+	/* save result to file */
+	goodix_save_result_data(ts_test);
+#endif
+}
+
+static int goodix_do_inspect(struct goodix_ts_core *cd, struct ts_rawdata_info *info)
+{
+	int ret;
+	struct goodix_ts_test *ts_test = NULL;
+
+	if (!cd || !info) {
+		ts_err("core_data or info is NULL");
+		return -ENODEV;
+	}
+
+	ts_test = kzalloc(sizeof(*ts_test), GFP_KERNEL);
+	if (!ts_test)
+		return -ENOMEM;
+
+	ts_test->ts = cd;
+	ret = goodix_tptest_prepare(ts_test);
+	if (ret < 0) {
+		ts_err("Failed to prepare TP test, exit");
+		strlcpy(info->result, "[FAIL]-0F-software reason\n",
+				TS_RAWDATA_RESULT_MAX - 1);
+		goto exit_finish;
+	}
+	ts_info("TP test prepare OK");
+
+	goodix_capacitance_test(ts_test); /* 1F 3F 6F 7F test */
+	if (ts_test->test_params.test_items[GTP_SHORT_TEST])
+		goodix_shortcircut_test(ts_test); /* 5F test */
+	goodix_put_test_result(ts_test, info);
+	goodix_tptest_finish(ts_test);
+
+exit_finish:
+	kfree(ts_test);
+	return ret;
+}
+
+/* show rawdata */
+static ssize_t get_rawdata_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	int ret = 0;
+	struct ts_rawdata_info *info = NULL;
+	struct goodix_ts_core *cd = dev_get_drvdata(dev);
+
+	info = kzalloc(sizeof(*info), GFP_KERNEL);
+	if (!info)
+		return -ENOMEM;
+
+	goodix_do_inspect(cd, info);
+
+	ret = snprintf(buf, PAGE_SIZE, "resultInfo: %s", info->result);
+
+	kfree(info);
+	return ret;
+}
+
+static DEVICE_ATTR(get_rawdata, 0444, get_rawdata_show, NULL);
+
+int inspect_module_init(void)
+{
+	int ret;
+	struct kobject *def_kobj = goodix_get_default_kobj();
+
+	/* create sysfs */
+	ret = sysfs_create_file(def_kobj, &dev_attr_get_rawdata.attr);
+	if (ret < 0) {
+		ts_err("create sysfs of get_rawdata failed");
+		goto err_out;
+	}
+
+	module_initialized = true;
+	ts_info("inspect module init success");
+	return 0;
+
+err_out:
+	ts_err("inspect module init failed!");
+	return ret;
+}
+
+void inspect_module_exit(void)
+{
+	struct kobject *def_kobj = goodix_get_default_kobj();
+
+	ts_info("inspect module exit");
+	if (!module_initialized)
+		return;
+
+	sysfs_remove_file(def_kobj, &dev_attr_get_rawdata.attr);
+	module_initialized = false;
+}

+ 503 - 0
qcom/opensource/touch-drivers/goodix_berlin_driver/goodix_ts_tools.c

@@ -0,0 +1,503 @@
+ /*
+  * Goodix Touchscreen Driver
+  * Copyright (C) 2020 - 2021 Goodix, Inc.
+  *
+  * This program is free software; you can redistribute it and/or modify
+  * it under the terms of the GNU General Public License as published by
+  * the Free Software Foundation; either version 2 of the License, or
+  * (at your option) any later version.
+  *
+  * This program is distributed in the hope that it will be a reference
+  * to you, when you are integrating the GOODiX's CTP IC into your system,
+  * but WITHOUT ANY WARRANTY; without even the implied warranty of
+  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  * General Public License for more details.
+  *
+  */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/atomic.h>
+#include <linux/miscdevice.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/fs.h>
+#include <linux/list.h>
+#include <linux/ioctl.h>
+#include <linux/wait.h>
+#include "goodix_ts_core.h"
+
+#define GOODIX_TOOLS_NAME		"gtp_tools"
+#define GOODIX_TOOLS_VER_MAJOR		1
+#define GOODIX_TOOLS_VER_MINOR		0
+static const u16 goodix_tools_ver = ((GOODIX_TOOLS_VER_MAJOR << 8) +
+			(GOODIX_TOOLS_VER_MINOR));
+
+#define GOODIX_TS_IOC_MAGIC		'G'
+#define NEGLECT_SIZE_MASK		(~(_IOC_SIZEMASK << _IOC_SIZESHIFT))
+
+#define GTP_IRQ_ENABLE	_IO(GOODIX_TS_IOC_MAGIC, 0)
+#define GTP_DEV_RESET	_IO(GOODIX_TS_IOC_MAGIC, 1)
+#define GTP_SEND_COMMAND (_IOW(GOODIX_TS_IOC_MAGIC, 2, u8) & NEGLECT_SIZE_MASK)
+#define GTP_SEND_CONFIG	(_IOW(GOODIX_TS_IOC_MAGIC, 3, u8) & NEGLECT_SIZE_MASK)
+#define GTP_ASYNC_READ	(_IOR(GOODIX_TS_IOC_MAGIC, 4, u8) & NEGLECT_SIZE_MASK)
+#define GTP_SYNC_READ	(_IOR(GOODIX_TS_IOC_MAGIC, 5, u8) & NEGLECT_SIZE_MASK)
+#define GTP_ASYNC_WRITE	(_IOW(GOODIX_TS_IOC_MAGIC, 6, u8) & NEGLECT_SIZE_MASK)
+#define GTP_READ_CONFIG	(_IOW(GOODIX_TS_IOC_MAGIC, 7, u8) & NEGLECT_SIZE_MASK)
+#define GTP_ESD_ENABLE	_IO(GOODIX_TS_IOC_MAGIC, 8)
+#define GTP_TOOLS_VER   (_IOR(GOODIX_TS_IOC_MAGIC, 9, u8) & NEGLECT_SIZE_MASK)
+#define GTP_TOOLS_CTRL_SYNC (_IOW(GOODIX_TS_IOC_MAGIC, 10, u8) & NEGLECT_SIZE_MASK)
+
+#define MAX_BUF_LENGTH		(16*1024)
+#define IRQ_FALG		(0x01 << 2)
+
+#define I2C_MSG_HEAD_LEN	20
+
+/*
+ * struct goodix_tools_dev - goodix tools device struct
+ * @ts_core: The core data struct of ts driver
+ * @ops_mode: represent device work mode
+ * @rawdiffcmd: Set slave device into rawdata mode
+ * @normalcmd: Set slave device into normal mode
+ * @wq: Wait queue struct use in synchronous data read
+ * @mutex: Protect goodix_tools_dev
+ * @in_use: device in use
+ */
+struct goodix_tools_dev {
+	struct goodix_ts_core *ts_core;
+	struct list_head head;
+	unsigned int ops_mode;
+	struct goodix_ts_cmd rawdiffcmd, normalcmd;
+	wait_queue_head_t wq;
+	struct mutex mutex;
+	atomic_t in_use;
+	struct goodix_ext_module module;
+} *goodix_tools_dev;
+
+
+/* read data asynchronous,
+ * success return data length, otherwise return < 0
+ */
+static int async_read(struct goodix_tools_dev *dev, void __user *arg)
+{
+	u8 *databuf = NULL;
+	int ret = 0;
+	u32 reg_addr, length;
+	u8 i2c_msg_head[I2C_MSG_HEAD_LEN];
+	const struct goodix_ts_hw_ops *hw_ops = dev->ts_core->hw_ops;
+
+	ret = copy_from_user(&i2c_msg_head, arg, I2C_MSG_HEAD_LEN);
+	if (ret)
+		return -EFAULT;
+
+	reg_addr = i2c_msg_head[0] + (i2c_msg_head[1] << 8)
+			+ (i2c_msg_head[2] << 16) + (i2c_msg_head[3] << 24);
+	length = i2c_msg_head[4] + (i2c_msg_head[5] << 8)
+			+ (i2c_msg_head[6] << 16) + (i2c_msg_head[7] << 24);
+	if (length > MAX_BUF_LENGTH) {
+		ts_err("buffer too long:%d > %d", length, MAX_BUF_LENGTH);
+		return -EINVAL;
+	}
+	databuf = kzalloc(length, GFP_KERNEL);
+	if (!databuf) {
+		ts_err("Alloc memory failed");
+		return -ENOMEM;
+	}
+
+	if (hw_ops->read(dev->ts_core, reg_addr, databuf, length)) {
+		ret = -EBUSY;
+		ts_err("Read i2c failed");
+		goto err_out;
+	}
+	ret = copy_to_user((u8 *)arg + I2C_MSG_HEAD_LEN, databuf, length);
+	if (ret) {
+		ret = -EFAULT;
+		ts_err("Copy_to_user failed");
+		goto err_out;
+	}
+	ret = length;
+err_out:
+	kfree(databuf);
+	return ret;
+}
+
+/* if success return config data length */
+static int read_config_data(struct goodix_ts_core *ts_core, void __user *arg)
+{
+	int ret = 0;
+	u32 reg_addr, length;
+	u8 i2c_msg_head[I2C_MSG_HEAD_LEN];
+	u8 *tmp_buf;
+
+	ret = copy_from_user(&i2c_msg_head, arg, I2C_MSG_HEAD_LEN);
+	if (ret) {
+		ts_err("Copy data from user failed");
+		return -EFAULT;
+	}
+	reg_addr = i2c_msg_head[0] + (i2c_msg_head[1] << 8)
+		   + (i2c_msg_head[2] << 16) + (i2c_msg_head[3] << 24);
+	length = i2c_msg_head[4] + (i2c_msg_head[5] << 8)
+		 + (i2c_msg_head[6] << 16) + (i2c_msg_head[7] << 24);
+	ts_info("read config,reg_addr=0x%x, length=%d", reg_addr, length);
+	if (length > MAX_BUF_LENGTH) {
+		ts_err("buffer too long:%d > %d", length, MAX_BUF_LENGTH);
+		return -EINVAL;
+	}
+	tmp_buf = kzalloc(length, GFP_KERNEL);
+	if (!tmp_buf) {
+		ts_err("failed alloc memory");
+		return -ENOMEM;
+	}
+	/* if reg_addr == 0, read config data with specific flow */
+	if (!reg_addr) {
+		if (ts_core->hw_ops->read_config)
+			ret = ts_core->hw_ops->read_config(ts_core, tmp_buf, length);
+		else
+			ret = -EINVAL;
+	} else {
+		ret = ts_core->hw_ops->read(ts_core, reg_addr, tmp_buf, length);
+		if (!ret)
+			ret = length;
+	}
+	if (ret <= 0)
+		goto err_out;
+
+	if (copy_to_user((u8 *)arg + I2C_MSG_HEAD_LEN, tmp_buf, ret)) {
+		ret = -EFAULT;
+		ts_err("Copy_to_user failed");
+	}
+
+err_out:
+	kfree(tmp_buf);
+	return ret;
+}
+
+/* write data to i2c asynchronous,
+ * success return bytes write, else return <= 0
+ */
+static int async_write(struct goodix_tools_dev *dev, void __user *arg)
+{
+	u8 *databuf;
+	int ret = 0;
+	u32 reg_addr, length;
+	u8 i2c_msg_head[I2C_MSG_HEAD_LEN];
+	struct goodix_ts_core *ts_core = dev->ts_core;
+	const struct goodix_ts_hw_ops *hw_ops = ts_core->hw_ops;
+
+	ret = copy_from_user(&i2c_msg_head, arg, I2C_MSG_HEAD_LEN);
+	if (ret) {
+		ts_err("Copy data from user failed");
+		return -EFAULT;
+	}
+	reg_addr = i2c_msg_head[0] + (i2c_msg_head[1] << 8)
+			+ (i2c_msg_head[2] << 16) + (i2c_msg_head[3] << 24);
+	length = i2c_msg_head[4] + (i2c_msg_head[5] << 8)
+			+ (i2c_msg_head[6] << 16) + (i2c_msg_head[7] << 24);
+	if (length > MAX_BUF_LENGTH) {
+		ts_err("buffer too long:%d > %d", length, MAX_BUF_LENGTH);
+		return -EINVAL;
+	}
+
+	databuf = kzalloc(length, GFP_KERNEL);
+	if (!databuf) {
+		ts_err("Alloc memory failed");
+		return -ENOMEM;
+	}
+	ret = copy_from_user(databuf, (u8 *)arg + I2C_MSG_HEAD_LEN, length);
+	if (ret) {
+		ret = -EFAULT;
+		ts_err("Copy data from user failed");
+		goto err_out;
+	}
+
+	if (hw_ops->write(ts_core, reg_addr, databuf, length)) {
+		ret = -EBUSY;
+		ts_err("Write data to device failed");
+	} else {
+		ret = length;
+	}
+
+err_out:
+	kfree(databuf);
+	return ret;
+}
+
+static int init_cfg_data(struct goodix_ic_config *cfg, void __user *arg)
+{
+	int ret = 0;
+	u32 length;
+	u8 i2c_msg_head[I2C_MSG_HEAD_LEN] = {0};
+
+	ret = copy_from_user(&i2c_msg_head, arg, I2C_MSG_HEAD_LEN);
+	if (ret) {
+		ts_err("Copy data from user failed");
+		return -EFAULT;
+	}
+
+	length = i2c_msg_head[4] + (i2c_msg_head[5] << 8)
+			+ (i2c_msg_head[6] << 16) + (i2c_msg_head[7] << 24);
+	if (length > GOODIX_CFG_MAX_SIZE) {
+		ts_err("buffer too long:%d > %d", length, MAX_BUF_LENGTH);
+		return -EINVAL;
+	}
+	ret = copy_from_user(cfg->data, (u8 *)arg + I2C_MSG_HEAD_LEN, length);
+	if (ret) {
+		ts_err("Copy data from user failed");
+		return -EFAULT;
+	}
+	cfg->len = length;
+	return 0;
+}
+
+/**
+ * goodix_tools_ioctl - ioctl implementation
+ *
+ * @filp: Pointer to file opened
+ * @cmd: Ioctl opertion command
+ * @arg: Command data
+ * Returns >=0 - succeed, else failed
+ */
+static long goodix_tools_ioctl(struct file *filp, unsigned int cmd,
+					unsigned long arg)
+{
+	int ret = 0;
+	struct goodix_tools_dev *dev = filp->private_data;
+	struct goodix_ts_core *ts_core;
+	const struct goodix_ts_hw_ops *hw_ops;
+	struct goodix_ic_config *temp_cfg = NULL;
+
+	if (!dev->ts_core) {
+		ts_err("Tools module not register");
+		return -EINVAL;
+	}
+	ts_core = dev->ts_core;
+	hw_ops = ts_core->hw_ops;
+
+	if (_IOC_TYPE(cmd) != GOODIX_TS_IOC_MAGIC) {
+		ts_err("Bad magic num:%c", _IOC_TYPE(cmd));
+		return -ENOTTY;
+	}
+
+	switch (cmd & NEGLECT_SIZE_MASK) {
+	case GTP_IRQ_ENABLE:
+		if (arg == 1) {
+			hw_ops->irq_enable(ts_core, true);
+			mutex_lock(&dev->mutex);
+			dev->ops_mode |= IRQ_FALG;
+			mutex_unlock(&dev->mutex);
+			ts_info("IRQ enabled");
+		} else if (arg == 0) {
+			hw_ops->irq_enable(ts_core, false);
+			mutex_lock(&dev->mutex);
+			dev->ops_mode &= ~IRQ_FALG;
+			mutex_unlock(&dev->mutex);
+			ts_info("IRQ disabled");
+		} else {
+			ts_info("Irq aready set with, arg = %ld", arg);
+		}
+		ret = 0;
+		break;
+	case GTP_ESD_ENABLE:
+		if (arg == 0)
+			goodix_ts_blocking_notify(NOTIFY_ESD_OFF, NULL);
+		else
+			goodix_ts_blocking_notify(NOTIFY_ESD_ON, NULL);
+		break;
+	case GTP_DEV_RESET:
+		hw_ops->reset(ts_core, GOODIX_NORMAL_RESET_DELAY_MS);
+		break;
+	case GTP_SEND_COMMAND:
+		/* deprecated command */
+		ts_err("the GTP_SEND_COMMAND function has been removed");
+		ret = -EINVAL;
+		break;
+	case GTP_SEND_CONFIG:
+		temp_cfg = kzalloc(sizeof(struct goodix_ic_config), GFP_KERNEL);
+		if (!temp_cfg) {
+			ts_err("Memory allco err");
+			ret = -ENOMEM;
+			goto err_out;
+		}
+
+		ret = init_cfg_data(temp_cfg, (void __user *)arg);
+		if (!ret && hw_ops->send_config) {
+			ret = hw_ops->send_config(ts_core, temp_cfg->data, temp_cfg->len);
+			if (ret) {
+				ts_err("Failed send config");
+				ret = -EAGAIN;
+			} else {
+				ts_info("Send config success");
+				ret = 0;
+			}
+		}
+		kfree(temp_cfg);
+		temp_cfg = NULL;
+		break;
+	case GTP_READ_CONFIG:
+		ret = read_config_data(ts_core, (void __user *)arg);
+		if (ret > 0)
+			ts_info("success read config:len=%d", ret);
+		else
+			ts_err("failed read config:ret=0x%x", ret);
+		break;
+	case GTP_ASYNC_READ:
+		ret = async_read(dev, (void __user *)arg);
+		if (ret < 0)
+			ts_err("Async data read failed");
+		break;
+	case GTP_SYNC_READ:
+		ts_info("unsupport sync read");
+		break;
+	case GTP_ASYNC_WRITE:
+		ret = async_write(dev, (void __user *)arg);
+		if (ret < 0)
+			ts_err("Async data write failed");
+		break;
+	case GTP_TOOLS_VER:
+		ret = copy_to_user((u8 *)arg, &goodix_tools_ver, sizeof(u16));
+		if (ret)
+			ts_err("failed copy driver version info to user");
+		break;
+	case GTP_TOOLS_CTRL_SYNC:
+		ts_core->tools_ctrl_sync = !!arg;
+		ts_info("set tools ctrl sync %d", ts_core->tools_ctrl_sync);
+		break;
+	default:
+		ts_info("Invalid cmd");
+		ret = -ENOTTY;
+		break;
+	}
+
+err_out:
+	return ret;
+}
+
+#ifdef CONFIG_COMPAT
+static long goodix_tools_compat_ioctl(struct file *file, unsigned int cmd,
+				unsigned long arg)
+{
+	void __user *arg32 = compat_ptr(arg);
+
+	if (!file->f_op || !file->f_op->unlocked_ioctl)
+		return -ENOTTY;
+	return file->f_op->unlocked_ioctl(file, cmd, (unsigned long)arg32);
+}
+#endif
+
+static int goodix_tools_open(struct inode *inode, struct file *filp)
+{
+	int ret = 0;
+
+	ts_info("try open tool");
+	/* Only the first time open device need to register module */
+	ret = goodix_register_ext_module_no_wait(&goodix_tools_dev->module);
+	if (ret) {
+		ts_info("failed register to core module");
+		return -EFAULT;
+	}
+	ts_info("success open tools");
+	goodix_ts_blocking_notify(NOTIFY_ESD_OFF, NULL);
+	filp->private_data = goodix_tools_dev;
+	atomic_set(&goodix_tools_dev->in_use, 1);
+	return 0;
+}
+
+static int goodix_tools_release(struct inode *inode, struct file *filp)
+{
+	int ret = 0;
+
+	/* when the last close this dev node unregister the module */
+	goodix_tools_dev->ts_core->tools_ctrl_sync = false;
+	atomic_set(&goodix_tools_dev->in_use, 0);
+	goodix_ts_blocking_notify(NOTIFY_ESD_ON, NULL);
+	ret = goodix_unregister_ext_module(&goodix_tools_dev->module);
+	return ret;
+}
+
+static int goodix_tools_module_init(struct goodix_ts_core *core_data,
+			struct goodix_ext_module *module)
+{
+	struct goodix_tools_dev *tools_dev = module->priv_data;
+
+	if (core_data)
+		tools_dev->ts_core = core_data;
+	else
+		return -ENODEV;
+
+	return 0;
+}
+
+static int goodix_tools_module_exit(struct goodix_ts_core *core_data,
+		struct goodix_ext_module *module)
+{
+	struct goodix_tools_dev *tools_dev = module->priv_data;
+	ts_debug("tools module unregister");
+	if (atomic_read(&tools_dev->in_use)) {
+		ts_err("tools module busy, please close it then retry");
+		return -EBUSY;
+	}
+	return 0;
+}
+
+static const struct file_operations goodix_tools_fops = {
+	.owner		= THIS_MODULE,
+	.open		= goodix_tools_open,
+	.release	= goodix_tools_release,
+	.unlocked_ioctl	= goodix_tools_ioctl,
+#ifdef CONFIG_COMPAT
+	.compat_ioctl = goodix_tools_compat_ioctl,
+#endif
+};
+
+static struct miscdevice goodix_tools_miscdev = {
+	.minor	= MISC_DYNAMIC_MINOR,
+	.name	= GOODIX_TOOLS_NAME,
+	.fops	= &goodix_tools_fops,
+};
+
+static struct goodix_ext_module_funcs goodix_tools_module_funcs = {
+	.init = goodix_tools_module_init,
+	.exit = goodix_tools_module_exit,
+};
+
+/**
+ * goodix_tools_init - init goodix tools device and register a miscdevice
+ *
+ * return: 0 success, else failed
+ */
+int goodix_tools_init(void)
+{
+	int ret;
+
+	goodix_tools_dev = kzalloc(sizeof(struct goodix_tools_dev), GFP_KERNEL);
+	if (goodix_tools_dev == NULL) {
+		ts_err("Memory allco err");
+		return -ENOMEM;
+	}
+
+	INIT_LIST_HEAD(&goodix_tools_dev->head);
+	goodix_tools_dev->ops_mode = 0;
+	goodix_tools_dev->ops_mode |= IRQ_FALG;
+	init_waitqueue_head(&goodix_tools_dev->wq);
+	mutex_init(&goodix_tools_dev->mutex);
+	atomic_set(&goodix_tools_dev->in_use, 0);
+
+	goodix_tools_dev->module.funcs = &goodix_tools_module_funcs;
+	goodix_tools_dev->module.name = GOODIX_TOOLS_NAME;
+	goodix_tools_dev->module.priv_data = goodix_tools_dev;
+	goodix_tools_dev->module.priority = EXTMOD_PRIO_DBGTOOL;
+
+	ret = misc_register(&goodix_tools_miscdev);
+	if (ret)
+		ts_err("Debug tools miscdev register failed");
+	else
+		ts_info("Debug tools miscdev register success");
+
+	return ret;
+}
+
+void goodix_tools_exit(void)
+{
+	misc_deregister(&goodix_tools_miscdev);
+	kfree(goodix_tools_dev);
+	ts_info("Debug tools miscdev exit");
+}

+ 208 - 0
qcom/opensource/touch-drivers/goodix_berlin_driver/goodix_ts_utils.c

@@ -0,0 +1,208 @@
+ /*
+  * Goodix Touchscreen Driver
+  * Copyright (C) 2020 - 2021 Goodix, Inc.
+  *
+  * This program is free software; you can redistribute it and/or modify
+  * it under the terms of the GNU General Public License as published by
+  * the Free Software Foundation; either version 2 of the License, or
+  * (at your option) any later version.
+  *
+  * This program is distributed in the hope that it will be a reference
+  * to you, when you are integrating the GOODiX's CTP IC into your system,
+  * but WITHOUT ANY WARRANTY; without even the implied warranty of
+  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  * General Public License for more details.
+  *
+  */
+#include "goodix_ts_core.h"
+
+bool debug_log_flag = false;
+
+/*****************************************************************************
+* goodix_append_checksum
+* @summary
+*    Calcualte data checksum with the specified mode.
+*
+* @param data
+*   data need to be calculate
+* @param len
+*   data length
+* @param mode
+*   calculate for u8 or u16 checksum
+* @return
+*   return the data checksum value.
+*
+*****************************************************************************/
+u32 goodix_append_checksum(u8 *data, int len, int mode)
+{
+	u32 checksum = 0;
+	int i;
+
+	checksum = 0;
+	if (mode == CHECKSUM_MODE_U8_LE) {
+		for (i = 0; i < len; i++)
+			checksum += data[i];
+	} else {
+		for (i = 0; i < len; i+=2)
+			checksum += (data[i] + (data[i+1] << 8));
+	}
+
+	if (mode == CHECKSUM_MODE_U8_LE) {
+		data[len] = checksum & 0xff;
+		data[len + 1] = (checksum >> 8) & 0xff;
+		return 0xFFFF & checksum;
+	}
+	data[len] = checksum & 0xff;
+	data[len + 1] = (checksum >> 8) & 0xff;
+	data[len + 2] = (checksum >> 16) & 0xff;
+	data[len + 3] = (checksum >> 24) & 0xff;
+	return checksum;
+}
+
+/* checksum_cmp: check data valid or not
+ * @data: data need to be check
+ * @size: data length need to be check(include the checksum bytes)
+ * @mode: compare with U8 or U16 mode
+ * */
+int checksum_cmp(const u8 *data, int size, int mode)
+{
+	u32 cal_checksum = 0;
+	u32 r_checksum = 0;
+	u32 i;
+
+	if (((mode == CHECKSUM_MODE_U8_LE) && (size < 2)) ||
+	    ((mode == CHECKSUM_MODE_U16_LE) && (size < 4)) ||
+	    ((mode == CHECKSUM_MODE_U16_LE) && (size % 2 != 0)))
+		return 1;
+
+	if (mode == CHECKSUM_MODE_U8_LE) {
+		for (i = 0; i < size - 2; i++)
+			cal_checksum += data[i];
+		r_checksum += data[i++];
+		r_checksum += (data[i] << 8);
+		return (cal_checksum & 0xFFFF) == r_checksum ? 0 : 1;
+	}
+
+	if (mode == CHECKSUM_MODE_U16_LE) {
+		for (i = 0; i < size - 4; i += 2)
+			cal_checksum += data[i] + (data[i + 1] << 8);
+		r_checksum += data[i++];
+		r_checksum += (data[i++] << 8);
+		r_checksum += (data[i++] << 16);
+		r_checksum += (data[i] << 24);
+		return cal_checksum == r_checksum ? 0 : 1;
+	}
+
+	return 1;
+}
+
+/* return 1 if all data is zero or ff
+ * else return 0
+ */
+int is_risk_data(const u8 *data, int size)
+{
+	int i;
+	int zero_count =  0;
+	int ff_count = 0;
+
+	for (i = 0; i < size; i++) {
+		if (data[i] == 0)
+			zero_count++;
+		else if (data[i] == 0xff)
+			ff_count++;
+	}
+	if (zero_count == size || ff_count == size) {
+		ts_info("warning data is all %s\n",
+			zero_count == size ? "zero" : "0xff");
+		return 1;
+	}
+
+	return 0;
+}
+
+/* get config id form config file */
+#define CONFIG_ID_OFFSET 		30
+u32 goodix_get_file_config_id(u8 *ic_config)
+{
+	if (!ic_config)
+		return 0;
+	return le32_to_cpup((__le32 *)&ic_config[CONFIG_ID_OFFSET]);
+}
+
+/* matrix transpose */
+void goodix_rotate_abcd2cbad(int tx, int rx, s16 *data)
+{
+	s16 *temp_buf = NULL;
+	int size = tx * rx;
+	int i;
+	int j;
+	int col;
+
+	temp_buf = kcalloc(size, sizeof(s16), GFP_KERNEL);
+	if (!temp_buf) {
+		ts_err("malloc failed");
+		return;
+	}
+
+	for (i = 0, j = 0, col = 0; i < size; i++) {
+		temp_buf[i] = data[j++ * rx + col];
+		if (j == tx) {
+			j = 0;
+			col++;
+		}
+	}
+
+	memcpy(data, temp_buf, size * sizeof(s16));
+	kfree(temp_buf);
+}
+
+/* get ic type */
+int goodix_get_ic_type(struct device_node *node)
+{
+	const char *name_tmp;
+	int ret;
+
+	ret = of_property_read_string(node, "compatible", &name_tmp);
+	if (ret < 0) {
+		ts_err("get compatible failed");
+		return ret;
+	}
+
+	if (strstr(name_tmp, "9897")) {
+		ts_info("ic type is BerlinA");
+		ret = IC_TYPE_BERLIN_A;
+	} else if (strstr(name_tmp, "9966") || strstr(name_tmp, "7986")) {
+		ts_info("ic type is BerlinB");
+		ret = IC_TYPE_BERLIN_B;
+	} else if (strstr(name_tmp, "9916")) {
+		ts_info("ic type is BerlinD");
+		ret = IC_TYPE_BERLIN_D;
+	} else {
+		ts_info("can't find valid ic_type");
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+/* get touch type */
+int goodix_get_touch_type(struct device_node *node)
+{
+	const char *touch_type;
+	int ret;
+
+	ret = of_property_read_string(node, "goodix,touch-type", &touch_type);
+	if (ret) {
+		ts_err("No touch type found\n");
+		return -EINVAL;
+	}
+
+	if (!strcmp(touch_type, "primary"))
+		ret = PRIMARY_TOUCH_IDX;
+	else if (!strcmp(touch_type, "secondary"))
+		ret = SECONDARY_TOUCH_IDX;
+	else
+		ret = -EINVAL;
+
+	return ret;
+}

+ 4608 - 0
qcom/opensource/touch-drivers/nt36xxx/nt36xxx.c

@@ -0,0 +1,4608 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2010 - 2018 Novatek, Inc.
+ *
+ * $Revision: 47247 $
+ * $Date: 2019-07-10 10:41:36 +0800 (Wed, 10 Jul 2019) $
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#if !defined(NVT_NT36XXX_SPI) /* NT36XXX I2C */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/proc_fs.h>
+#include <linux/input/mt.h>
+#include <linux/of_gpio.h>
+#include <linux/of_irq.h>
+#include <linux/version.h>
+
+#if defined(CONFIG_DRM)
+#include <linux/soc/qcom/panel_event_notifier.h>
+#endif
+
+#if defined(CONFIG_DRM_PANEL)
+#include <drm/drm_panel.h>
+#elif defined(CONFIG_FB)
+#include <linux/notifier.h>
+#include <linux/fb.h>
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
+#include <linux/earlysuspend.h>
+#endif
+
+#include "nt36xxx.h"
+#if NVT_TOUCH_ESD_PROTECT
+#include <linux/jiffies.h>
+#endif /* #if NVT_TOUCH_ESD_PROTECT */
+
+#if NVT_TOUCH_ESD_PROTECT
+static struct delayed_work nvt_esd_check_work;
+static struct workqueue_struct *nvt_esd_check_wq;
+static unsigned long irq_timer = 0;
+uint8_t esd_check = false;
+uint8_t esd_retry = 0;
+#endif /* #if NVT_TOUCH_ESD_PROTECT */
+
+#if NVT_TOUCH_EXT_PROC
+extern int32_t nvt_extra_proc_init(void);
+extern void nvt_extra_proc_deinit(void);
+#endif
+
+#if NVT_TOUCH_MP
+extern int32_t nvt_mp_proc_init(void);
+extern void nvt_mp_proc_deinit(void);
+#endif
+
+struct nvt_ts_data *ts;
+
+#if BOOT_UPDATE_FIRMWARE
+static struct workqueue_struct *nvt_fwu_wq;
+extern void Boot_Update_Firmware(struct work_struct *work);
+#endif
+
+#if defined(CONFIG_DRM)
+static struct drm_panel *active_panel;
+static void nvt_i2c_panel_notifier_callback(enum panel_event_notifier_tag tag,
+			struct panel_event_notification *notification, void *client_data);
+
+#elif defined(_MSM_DRM_NOTIFY_H_)
+static int nvt_drm_notifier_callback(struct notifier_block *self, unsigned long event, void *data);
+
+#else
+static int nvt_fb_notifier_callback(struct notifier_block *self, unsigned long event, void *data);
+#endif
+
+#if TOUCH_KEY_NUM > 0
+const uint16_t touch_key_array[TOUCH_KEY_NUM] = {
+	KEY_BACK,
+	KEY_HOME,
+	KEY_MENU
+};
+#endif
+
+#if WAKEUP_GESTURE
+const uint16_t gesture_key_array[] = {
+	KEY_POWER,  //GESTURE_WORD_C
+	KEY_POWER,  //GESTURE_WORD_W
+	KEY_POWER,  //GESTURE_WORD_V
+	KEY_POWER,  //GESTURE_DOUBLE_CLICK
+	KEY_POWER,  //GESTURE_WORD_Z
+	KEY_POWER,  //GESTURE_WORD_M
+	KEY_POWER,  //GESTURE_WORD_O
+	KEY_POWER,  //GESTURE_WORD_e
+	KEY_POWER,  //GESTURE_WORD_S
+	KEY_POWER,  //GESTURE_SLIDE_UP
+	KEY_POWER,  //GESTURE_SLIDE_DOWN
+	KEY_POWER,  //GESTURE_SLIDE_LEFT
+	KEY_POWER,  //GESTURE_SLIDE_RIGHT
+};
+#endif
+
+static uint8_t bTouchIsAwake = 0;
+
+#if defined(CONFIG_DRM)
+static void nvt_i2c_register_for_panel_events(struct device_node *dp,
+		struct nvt_ts_data *ts)
+{
+	void *cookie = NULL;
+
+	cookie = panel_event_notifier_register(PANEL_EVENT_NOTIFICATION_PRIMARY,
+			PANEL_EVENT_NOTIFIER_CLIENT_PRIMARY_TOUCH, active_panel,
+			&nvt_i2c_panel_notifier_callback, ts);
+	if (!cookie) {
+		pr_err("Failed to register for panel events\n");
+		return;
+	}
+	ts->notifier_cookie = cookie;
+}
+#endif
+/*******************************************************
+ * Description:
+ *     Novatek touchscreen irq enable/disable function.
+ *
+ * return:
+ *     n.a.
+ *******************************************************/
+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 = true;
+		}
+	} else {
+		if (ts->irq_enabled) {
+			disable_irq(ts->client->irq);
+			ts->irq_enabled = false;
+		}
+	}
+
+	desc = irq_to_desc(ts->client->irq);
+	NVT_LOG("enable=%d, desc->depth=%d\n", enable, desc->depth);
+}
+
+/*******************************************************
+ * Description:
+ *     Novatek touchscreen i2c read function.
+ *
+ * return:
+ *     Executive outcomes. 2---succeed. -5---I/O error
+ *******************************************************/
+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 = -1;
+	int32_t retries = 0;
+
+	mutex_lock(&ts->xbuf_lock);
+
+	msgs[0].flags = !I2C_M_RD;
+	msgs[0].addr  = address;
+	msgs[0].len   = 1;
+	msgs[0].buf   = &buf[0];
+
+	msgs[1].flags = I2C_M_RD;
+	msgs[1].addr  = address;
+	msgs[1].len   = len - 1;
+	msgs[1].buf   = ts->xbuf;
+
+	while (retries < 5) {
+		ret = i2c_transfer(client->adapter, msgs, 2);
+		if (ret == 2)	break;
+		retries++;
+	}
+
+	if (unlikely(retries == 5)) {
+		NVT_ERR("error, ret=%d\n", ret);
+		ret = -EIO;
+	}
+
+	memcpy(buf + 1, ts->xbuf, len - 1);
+
+	mutex_unlock(&ts->xbuf_lock);
+
+	return ret;
+}
+
+/*******************************************************
+ * Description:
+ *     Novatek touchscreen i2c write function.
+ *
+ * return:
+ *     Executive outcomes. 1---succeed. -5---I/O error
+ *******************************************************/
+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 = -1;
+	int32_t retries = 0;
+
+	mutex_lock(&ts->xbuf_lock);
+
+	msg.flags = !I2C_M_RD;
+	msg.addr  = address;
+	msg.len   = len;
+	memcpy(ts->xbuf, buf, len);
+	msg.buf   = ts->xbuf;
+
+	while (retries < 5) {
+		ret = i2c_transfer(client->adapter, &msg, 1);
+		if (ret == 1)	break;
+		retries++;
+	}
+
+	if (unlikely(retries == 5)) {
+		NVT_ERR("error, ret=%d\n", ret);
+		ret = -EIO;
+	}
+
+	mutex_unlock(&ts->xbuf_lock);
+
+	return ret;
+}
+
+/*******************************************************
+ * Description:
+ *     Novatek touchscreen set index/page/addr address.
+ *
+ * return:
+ *     Executive outcomes. 0---succeed. -5---access fail.
+ ********************************************************/
+int32_t nvt_set_page(uint16_t i2c_addr, uint32_t addr)
+{
+	uint8_t buf[4] = {0};
+
+	buf[0] = 0xFF;	//set index/page/addr command
+	buf[1] = (addr >> 16) & 0xFF;
+	buf[2] = (addr >> 8) & 0xFF;
+
+	return CTP_I2C_WRITE(ts->client, i2c_addr, buf, 3);
+}
+
+/*******************************************************
+ * Description:
+ *     Novatek touchscreen reset MCU then into idle mode
+ * function.
+ *
+ * return:
+ *     n.a.
+ *******************************************************/
+void nvt_sw_reset_idle(void)
+{
+	uint8_t buf[4]={0};
+
+	//---write i2c cmds to reset idle---
+	buf[0]=0x00;
+	buf[1]=0xA5;
+	CTP_I2C_WRITE(ts->client, I2C_HW_Address, buf, 2);
+
+	msleep(15);
+}
+
+/*******************************************************
+ * Description:
+ *     Novatek touchscreen reset MCU (boot) function.
+ *
+ * return:
+ *     n.a.
+ *******************************************************/
+void nvt_bootloader_reset(void)
+{
+	uint8_t buf[8] = {0};
+
+	NVT_LOG("start\n");
+
+	//---write i2c cmds to reset---
+	buf[0] = 0x00;
+	buf[1] = 0x69;
+	CTP_I2C_WRITE(ts->client, I2C_HW_Address, buf, 2);
+
+	// need 35ms delay after bootloader reset
+	msleep(35);
+
+	NVT_LOG("end\n");
+}
+
+/*******************************************************
+ * Description:
+ *     Novatek touchscreen clear FW status function.
+ *
+ * return:
+ *     Executive outcomes. 0---succeed. -1---fail.
+ *******************************************************/
+int32_t nvt_clear_fw_status(void)
+{
+	uint8_t buf[8] = {0};
+	int32_t i = 0;
+	const int32_t retry = 20;
+
+	for (i = 0; i < retry; i++) {
+		//---set xdata index to EVENT BUF ADDR---
+		nvt_set_page(I2C_FW_Address, ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE);
+
+		//---clear fw status---
+		buf[0] = EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE;
+		buf[1] = 0x00;
+		CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 2);
+
+		//---read fw status---
+		buf[0] = EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE;
+		buf[1] = 0xFF;
+		CTP_I2C_READ(ts->client, I2C_FW_Address, buf, 2);
+
+		if (buf[1] == 0x00)
+			break;
+
+		usleep_range(10000, 10000);
+	}
+
+	if (i >= retry) {
+		NVT_ERR("failed, i=%d, buf[1]=0x%02X\n", i, buf[1]);
+		return -1;
+	} else {
+		return 0;
+	}
+}
+
+/*******************************************************
+ * Description:
+ *     Novatek touchscreen check FW status function.
+ *
+ * return:
+ *     Executive outcomes. 0---succeed. -1---failed.
+ *******************************************************/
+int32_t nvt_check_fw_status(void)
+{
+	uint8_t buf[8] = {0};
+	int32_t i = 0;
+	const int32_t retry = 50;
+
+	for (i = 0; i < retry; i++) {
+		//---set xdata index to EVENT BUF ADDR---
+		nvt_set_page(I2C_FW_Address, ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE);
+
+		//---read fw status---
+		buf[0] = EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE;
+		buf[1] = 0x00;
+		CTP_I2C_READ(ts->client, I2C_FW_Address, buf, 2);
+
+		if ((buf[1] & 0xF0) == 0xA0)
+			break;
+
+		usleep_range(10000, 10000);
+	}
+
+	if (i >= retry) {
+		NVT_ERR("failed, i=%d, buf[1]=0x%02X\n", i, buf[1]);
+		return -1;
+	} else {
+		return 0;
+	}
+}
+
+/*******************************************************
+ * Description:
+ *     Novatek touchscreen check FW reset state function.
+ *
+ * return:
+ *     Executive outcomes. 0---succeed. -1---failed.
+ ******************************************************/
+int32_t nvt_check_fw_reset_state(RST_COMPLETE_STATE check_reset_state)
+{
+	uint8_t buf[8] = {0};
+	int32_t ret = 0;
+	int32_t retry = 0;
+
+	while (1) {
+		usleep_range(10000, 10000);
+
+		//---read reset state---
+		buf[0] = EVENT_MAP_RESET_COMPLETE;
+		buf[1] = 0x00;
+		CTP_I2C_READ(ts->client, I2C_FW_Address, buf, 6);
+
+		if ((buf[1] >= check_reset_state) && (buf[1] <= RESET_STATE_MAX)) {
+			ret = 0;
+			break;
+		}
+
+		retry++;
+		if(unlikely(retry > 100)) {
+			NVT_ERR("error, retry=%d, buf[1]=0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X\n",
+				retry, buf[1], buf[2], buf[3], buf[4], buf[5]);
+			ret = -1;
+			break;
+		}
+	}
+
+	return ret;
+}
+
+/*******************************************************
+ * Description:
+ *     Novatek touchscreen get novatek project id information
+ * function.
+ *
+ * return:
+ *     Executive outcomes. 0---success. -1---fail.
+ *******************************************************/
+int32_t nvt_read_pid(void)
+{
+	uint8_t buf[3] = {0};
+
+	//---set xdata index to EVENT BUF ADDR---
+	nvt_set_page(I2C_FW_Address, ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_PROJECTID);
+
+	//---read project id---
+	buf[0] = EVENT_MAP_PROJECTID;
+	buf[1] = 0x00;
+	buf[2] = 0x00;
+	CTP_I2C_READ(ts->client, I2C_FW_Address, buf, 3);
+
+	ts->nvt_pid = (buf[2] << 8) + buf[1];
+
+	NVT_LOG("PID=%04X\n", ts->nvt_pid);
+
+	return 0;
+}
+
+/*******************************************************
+Description:
+	Novatek touchscreen get firmware related information
+	function.
+
+return:
+	Executive outcomes. 0---success. -1---fail.
+*******************************************************/
+int32_t nvt_get_fw_info(void)
+{
+	uint8_t buf[64] = {0};
+	uint32_t retry_count = 0;
+	int32_t ret = 0;
+
+info_retry:
+	//---set xdata index to EVENT BUF ADDR---
+	nvt_set_page(I2C_FW_Address, ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_FWINFO);
+
+	//---read fw info---
+	buf[0] = EVENT_MAP_FWINFO;
+	CTP_I2C_READ(ts->client, I2C_FW_Address, buf, 17);
+	ts->fw_ver = buf[1];
+	ts->x_num = buf[3];
+	ts->y_num = buf[4];
+	ts->abs_x_max = (uint16_t)((buf[5] << 8) | buf[6]);
+	ts->abs_y_max = (uint16_t)((buf[7] << 8) | buf[8]);
+	ts->max_button_num = buf[11];
+
+	//---clear x_num, y_num if fw info is broken---
+	if ((buf[1] + buf[2]) != 0xFF) {
+		NVT_ERR("FW info is broken! fw_ver=0x%02X, ~fw_ver=0x%02X\n", buf[1], buf[2]);
+		ts->fw_ver = 0;
+		ts->x_num = 18;
+		ts->y_num = 32;
+		ts->abs_x_max = TOUCH_DEFAULT_MAX_WIDTH;
+		ts->abs_y_max = TOUCH_DEFAULT_MAX_HEIGHT;
+		ts->max_button_num = TOUCH_KEY_NUM;
+
+		if(retry_count < 3) {
+			retry_count++;
+			NVT_ERR("retry_count=%d\n", retry_count);
+			goto info_retry;
+		} else {
+			NVT_ERR("Set default fw_ver=%d, x_num=%d, y_num=%d, "
+					"abs_x_max=%d, abs_y_max=%d, max_button_num=%d!\n",
+					ts->fw_ver, ts->x_num, ts->y_num,
+					ts->abs_x_max, ts->abs_y_max, ts->max_button_num);
+			ret = -1;
+		}
+	} else {
+		ret = 0;
+	}
+
+	//---Get Novatek PID---
+	nvt_read_pid();
+
+	return ret;
+}
+
+/*******************************************************
+ * Create Device Node (Proc Entry)
+ *******************************************************/
+#if NVT_TOUCH_PROC
+static struct proc_dir_entry *NVT_proc_entry;
+#define DEVICE_NAME	"NVTflash"
+
+/*******************************************************
+ * Description:
+ *     Novatek touchscreen /proc/NVTflash read function.
+ *
+ * return:
+ *     Executive outcomes. 2---succeed. -5,-14---failed.
+ *******************************************************/
+static ssize_t nvt_flash_read(struct file *file, char __user *buff,
+	size_t count, loff_t *offp)
+{
+	uint8_t str[68] = {0};
+	int32_t ret = -1;
+	int32_t retries = 0;
+	int8_t i2c_wr = 0;
+
+	if (count > sizeof(str)) {
+		NVT_ERR("error count=%zu\n", count);
+		return -EFAULT;
+	}
+
+	if (copy_from_user(str, buff, count)) {
+		NVT_ERR("copy from user error\n");
+		return -EFAULT;
+	}
+
+#if NVT_TOUCH_ESD_PROTECT
+	/*
+	 * stop esd check work to avoid case that 0x77 report righ after here to enable esd check again
+	 * finally lead to trigger esd recovery bootloader reset
+	 */
+	cancel_delayed_work_sync(&nvt_esd_check_work);
+	nvt_esd_check_enable(false);
+#endif /* #if NVT_TOUCH_ESD_PROTECT */
+
+	i2c_wr = str[0] >> 7;
+
+	if (i2c_wr == 0) {	//I2C write
+		while (retries < 20) {
+			ret = CTP_I2C_WRITE(ts->client, (str[0] & 0x7F), &str[2], str[1]);
+			if (ret == 1)
+				break;
+			else
+				NVT_ERR("error, retries=%d, ret=%d\n", retries, ret);
+
+			retries++;
+		}
+
+		if (unlikely(retries == 20)) {
+			NVT_ERR("error, ret = %d\n", ret);
+			return -EIO;
+		}
+
+		return ret;
+	} else if (i2c_wr == 1) {	//I2C read
+		while (retries < 20) {
+			ret = CTP_I2C_READ(ts->client, (str[0] & 0x7F), &str[2], str[1]);
+			if (ret == 2)
+				break;
+			else
+				NVT_ERR("error, retries=%d, ret=%d\n", retries, ret);
+
+			retries++;
+		}
+
+		// copy buff to user if i2c transfer
+		if (retries < 20) {
+			if (copy_to_user(buff, str, count))
+				return -EFAULT;
+		}
+
+		if (unlikely(retries == 20)) {
+			NVT_ERR("error, ret = %d\n", ret);
+			return -EIO;
+		}
+
+		return ret;
+	} else {
+		NVT_ERR("Call error, str[0]=%d\n", str[0]);
+		return -EFAULT;
+	}
+}
+
+/*******************************************************
+ * Description:
+ *     Novatek touchscreen /proc/NVTflash open function.
+ *
+ * return:
+ *     Executive outcomes. 0---succeed. -12---failed.
+ *******************************************************/
+static int32_t nvt_flash_open(struct inode *inode, struct file *file)
+{
+	struct nvt_flash_data *dev;
+
+	dev = kmalloc(sizeof(struct nvt_flash_data), GFP_KERNEL);
+	if (dev == NULL) {
+		NVT_ERR("Failed to allocate memory for nvt flash data\n");
+		return -ENOMEM;
+	}
+
+	rwlock_init(&dev->lock);
+	file->private_data = dev;
+
+	return 0;
+}
+
+/*******************************************************
+ * Description:
+ *     Novatek touchscreen /proc/NVTflash close function.
+ *
+ * return:
+ *     Executive outcomes. 0---succeed.
+ *******************************************************/
+static int32_t nvt_flash_close(struct inode *inode, struct file *file)
+{
+	struct nvt_flash_data *dev = file->private_data;
+
+	kfree(dev);
+
+	return 0;
+}
+
+static const struct proc_ops nvt_flash_fops = {
+	.proc_open = nvt_flash_open,
+	.proc_release = nvt_flash_close,
+	.proc_read = nvt_flash_read,
+};
+
+/*******************************************************
+ * Description:
+ *     Novatek touchscreen /proc/NVTflash initial function.
+ *
+ * return:
+ *     Executive outcomes. 0---succeed. -12---failed.
+ *******************************************************/
+static int32_t nvt_flash_proc_init(void)
+{
+	NVT_proc_entry = proc_create(DEVICE_NAME, 0444, NULL,&nvt_flash_fops);
+	if (NVT_proc_entry == NULL) {
+		NVT_ERR("Failed!\n");
+		return -ENOMEM;
+	} else {
+		NVT_LOG("Succeeded!\n");
+	}
+
+	NVT_LOG("==========================================================\n");
+	NVT_LOG("Create /proc/%s\n", DEVICE_NAME);
+	NVT_LOG("==========================================================\n");
+
+	return 0;
+}
+
+/*******************************************************
+ * Description:
+ *     Novatek touchscreen /proc/NVTflash deinitial function.
+ *
+ * return:
+ *     n.a.
+ *******************************************************/
+static void nvt_flash_proc_deinit(void)
+{
+	if (NVT_proc_entry != NULL) {
+		remove_proc_entry(DEVICE_NAME, NULL);
+		NVT_proc_entry = NULL;
+		NVT_LOG("Removed /proc/%s\n", DEVICE_NAME);
+	}
+}
+#endif
+
+#if WAKEUP_GESTURE
+#define GESTURE_WORD_C          12
+#define GESTURE_WORD_W          13
+#define GESTURE_WORD_V          14
+#define GESTURE_DOUBLE_CLICK    15
+#define GESTURE_WORD_Z          16
+#define GESTURE_WORD_M          17
+#define GESTURE_WORD_O          18
+#define GESTURE_WORD_e          19
+#define GESTURE_WORD_S          20
+#define GESTURE_SLIDE_UP        21
+#define GESTURE_SLIDE_DOWN      22
+#define GESTURE_SLIDE_LEFT      23
+#define GESTURE_SLIDE_RIGHT     24
+/* customized gesture id */
+#define DATA_PROTOCOL           30
+
+/* function page definition */
+#define FUNCPAGE_GESTURE         1
+
+/*******************************************************
+ * Description:
+ *     Novatek touchscreen wake up gesture key report function.
+ *
+ * return:
+ *     n.a.
+ *******************************************************/
+void nvt_ts_wakeup_gesture_report(uint8_t gesture_id, uint8_t *data)
+{
+	uint32_t keycode = 0;
+	uint8_t func_type = data[2];
+	uint8_t func_id = data[3];
+
+	/* support fw specifal data protocol */
+	if ((gesture_id == DATA_PROTOCOL) && (func_type == FUNCPAGE_GESTURE)) {
+		gesture_id = func_id;
+	} else if (gesture_id > DATA_PROTOCOL) {
+		NVT_ERR("gesture_id %d is invalid, func_type=%d, func_id=%d\n", gesture_id, func_type, func_id);
+		return;
+	}
+
+	NVT_LOG("gesture_id = %d\n", gesture_id);
+
+	switch (gesture_id) {
+		case GESTURE_WORD_C:
+			NVT_LOG("Gesture : Word-C.\n");
+			keycode = gesture_key_array[0];
+			break;
+		case GESTURE_WORD_W:
+			NVT_LOG("Gesture : Word-W.\n");
+			keycode = gesture_key_array[1];
+			break;
+		case GESTURE_WORD_V:
+			NVT_LOG("Gesture : Word-V.\n");
+			keycode = gesture_key_array[2];
+			break;
+		case GESTURE_DOUBLE_CLICK:
+			NVT_LOG("Gesture : Double Click.\n");
+			keycode = gesture_key_array[3];
+			break;
+		case GESTURE_WORD_Z:
+			NVT_LOG("Gesture : Word-Z.\n");
+			keycode = gesture_key_array[4];
+			break;
+		case GESTURE_WORD_M:
+			NVT_LOG("Gesture : Word-M.\n");
+			keycode = gesture_key_array[5];
+			break;
+		case GESTURE_WORD_O:
+			NVT_LOG("Gesture : Word-O.\n");
+			keycode = gesture_key_array[6];
+			break;
+		case GESTURE_WORD_e:
+			NVT_LOG("Gesture : Word-e.\n");
+			keycode = gesture_key_array[7];
+			break;
+		case GESTURE_WORD_S:
+			NVT_LOG("Gesture : Word-S.\n");
+			keycode = gesture_key_array[8];
+			break;
+		case GESTURE_SLIDE_UP:
+			NVT_LOG("Gesture : Slide UP.\n");
+			keycode = gesture_key_array[9];
+			break;
+		case GESTURE_SLIDE_DOWN:
+			NVT_LOG("Gesture : Slide DOWN.\n");
+			keycode = gesture_key_array[10];
+			break;
+		case GESTURE_SLIDE_LEFT:
+			NVT_LOG("Gesture : Slide LEFT.\n");
+			keycode = gesture_key_array[11];
+			break;
+		case GESTURE_SLIDE_RIGHT:
+			NVT_LOG("Gesture : Slide RIGHT.\n");
+			keycode = gesture_key_array[12];
+			break;
+		default:
+			break;
+	}
+
+	if (keycode > 0) {
+		input_report_key(ts->input_dev, keycode, 1);
+		input_sync(ts->input_dev);
+		input_report_key(ts->input_dev, keycode, 0);
+		input_sync(ts->input_dev);
+	}
+}
+#endif
+
+/*******************************************************
+ * Description:
+ *     Novatek touchscreen parse device tree function.
+ *
+ * return:
+ *     n.a.
+ *******************************************************/
+#ifdef CONFIG_OF
+static void nvt_parse_dt(struct device *dev)
+{
+	struct device_node *np = dev->of_node;
+
+#if NVT_TOUCH_SUPPORT_HW_RST
+	ts->reset_gpio = of_get_named_gpio_flags(np, "novatek,reset-gpio", 0, &ts->reset_flags);
+	NVT_LOG("novatek,reset-gpio=%d\n", ts->reset_gpio);
+#endif
+	ts->irq_gpio = of_get_named_gpio_flags(np, "novatek,irq-gpio", 0, &ts->irq_flags);
+	NVT_LOG("novatek,irq-gpio=%d\n", ts->irq_gpio);
+
+}
+#else
+static void nvt_parse_dt(struct device *dev)
+{
+#if NVT_TOUCH_SUPPORT_HW_RST
+	ts->reset_gpio = NVTTOUCH_RST_PIN;
+#endif
+	ts->irq_gpio = NVTTOUCH_INT_PIN;
+}
+#endif
+
+/*******************************************************
+ * Description:
+ *     Novatek touchscreen config and request gpio
+ *
+ * return:
+ *     Executive outcomes. 0---succeed. not 0---failed.
+ *******************************************************/
+static int nvt_gpio_config(struct nvt_ts_data *ts)
+{
+	int32_t ret = 0;
+
+#if NVT_TOUCH_SUPPORT_HW_RST
+	/* request RST-pin (Output/High) */
+	if (gpio_is_valid(ts->reset_gpio)) {
+		ret = gpio_request_one(ts->reset_gpio, GPIOF_OUT_INIT_HIGH, "NVT-tp-rst");
+		if (ret) {
+			NVT_ERR("Failed to request NVT-tp-rst GPIO\n");
+			goto err_request_reset_gpio;
+		}
+	}
+#endif
+
+	/* request INT-pin (Input) */
+	if (gpio_is_valid(ts->irq_gpio)) {
+		ret = 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:
+#if NVT_TOUCH_SUPPORT_HW_RST
+	gpio_free(ts->reset_gpio);
+err_request_reset_gpio:
+#endif
+	return ret;
+}
+
+/*******************************************************
+ * Description:
+ *     Novatek touchscreen deconfig gpio
+ *
+ * return:
+ *     n.a.
+ *******************************************************/
+static void nvt_gpio_deconfig(struct nvt_ts_data *ts)
+{
+	if (gpio_is_valid(ts->irq_gpio))
+		gpio_free(ts->irq_gpio);
+#if NVT_TOUCH_SUPPORT_HW_RST
+	if (gpio_is_valid(ts->reset_gpio))
+		gpio_free(ts->reset_gpio);
+#endif
+}
+
+static uint8_t nvt_fw_recovery(uint8_t *point_data)
+{
+	uint8_t i = 0;
+	uint8_t detected = true;
+
+	/* check pattern */
+	for (i=1 ; i<7 ; i++) {
+		if (point_data[i] != 0x77) {
+			detected = false;
+			break;
+		}
+	}
+
+	return detected;
+}
+
+#if NVT_TOUCH_ESD_PROTECT
+void nvt_esd_check_enable(uint8_t enable)
+{
+	/* update interrupt timer */
+	irq_timer = jiffies;
+	/* clear esd_retry counter, if protect function is enabled */
+	esd_retry = enable ? 0 : esd_retry;
+	/* enable/disable esd check flag */
+	esd_check = enable;
+}
+
+static void nvt_esd_check_func(struct work_struct *work)
+{
+	unsigned int timer = jiffies_to_msecs(jiffies - irq_timer);
+
+	//NVT_ERR("esd_check = %d (retry %d)\n", esd_check, esd_retry);	//DEBUG
+
+	if ((timer > NVT_TOUCH_ESD_CHECK_PERIOD) && esd_check) {
+		mutex_lock(&ts->lock);
+		NVT_ERR("do ESD recovery, timer = %d, retry = %d\n", timer, esd_retry);
+		/* do esd recovery, bootloader reset */
+		nvt_bootloader_reset();
+		mutex_unlock(&ts->lock);
+		/* update interrupt timer */
+		irq_timer = jiffies;
+		/* update esd_retry counter */
+		esd_retry++;
+	}
+
+	queue_delayed_work(nvt_esd_check_wq, &nvt_esd_check_work,
+			msecs_to_jiffies(NVT_TOUCH_ESD_CHECK_PERIOD));
+}
+#endif /* #if NVT_TOUCH_ESD_PROTECT */
+
+#define POINT_DATA_LEN 65
+/*******************************************************
+ * Description:
+ *     Novatek touchscreen work function.
+ *
+ * return:
+ *     n.a.
+ *******************************************************/
+static irqreturn_t nvt_ts_work_func(int irq, void *data)
+{
+	int32_t ret = -1;
+	uint8_t point_data[POINT_DATA_LEN + 1] = {0};
+	uint32_t position = 0;
+	uint32_t input_x = 0;
+	uint32_t input_y = 0;
+	uint32_t input_w = 0;
+	uint32_t input_p = 0;
+	uint8_t input_id = 0;
+#if MT_PROTOCOL_B
+	uint8_t press_id[TOUCH_MAX_FINGER_NUM] = {0};
+#endif /* MT_PROTOCOL_B */
+	int32_t i = 0;
+	int32_t finger_cnt = 0;
+
+#if WAKEUP_GESTURE
+	if (bTouchIsAwake == 0) {
+		pm_wakeup_event(&ts->input_dev->dev, 5000);
+	}
+#endif
+
+	mutex_lock(&ts->lock);
+
+	ret = CTP_I2C_READ(ts->client, I2C_FW_Address, point_data, POINT_DATA_LEN + 1);
+	if (ret < 0) {
+		NVT_ERR("CTP_I2C_READ failed.(%d)\n", ret);
+		goto XFER_ERROR;
+	}
+
+	if (nvt_fw_recovery(point_data)) {
+#if NVT_TOUCH_ESD_PROTECT
+		nvt_esd_check_enable(true);
+#endif /* #if NVT_TOUCH_ESD_PROTECT */
+		goto XFER_ERROR;
+	}
+
+#if WAKEUP_GESTURE
+	if (bTouchIsAwake == 0) {
+		input_id = (uint8_t)(point_data[1] >> 3);
+		nvt_ts_wakeup_gesture_report(input_id, point_data);
+		mutex_unlock(&ts->lock);
+		return IRQ_HANDLED;
+	}
+#endif
+
+	finger_cnt = 0;
+
+	for (i = 0; i < ts->max_touch_num; i++) {
+		position = 1 + 6 * i;
+		input_id = (uint8_t)(point_data[position + 0] >> 3);
+		if ((input_id == 0) || (input_id > ts->max_touch_num))
+			continue;
+
+		if (((point_data[position] & 0x07) == 0x01) || ((point_data[position] & 0x07) == 0x02)) {	//finger down (enter & moving)
+#if NVT_TOUCH_ESD_PROTECT
+			/* update interrupt timer */
+			irq_timer = jiffies;
+#endif /* #if NVT_TOUCH_ESD_PROTECT */
+			input_x = (uint32_t)(point_data[position + 1] << 4) + (uint32_t) (point_data[position + 3] >> 4);
+			input_y = (uint32_t)(point_data[position + 2] << 4) + (uint32_t) (point_data[position + 3] & 0x0F);
+			if ((input_x < 0) || (input_y < 0))
+				continue;
+			if ((input_x > ts->abs_x_max) || (input_y > ts->abs_y_max))
+				continue;
+			input_w = (uint32_t)(point_data[position + 4]);
+			if (input_w == 0)
+				input_w = 1;
+			if (i < 2) {
+				input_p = (uint32_t)(point_data[position + 5]) + (uint32_t)(point_data[i + 63] << 8);
+				if (input_p > TOUCH_FORCE_NUM)
+					input_p = TOUCH_FORCE_NUM;
+			} else {
+				input_p = (uint32_t)(point_data[position + 5]);
+			}
+			if (input_p == 0)
+				input_p = 1;
+
+#if MT_PROTOCOL_B
+			press_id[input_id - 1] = 1;
+			input_mt_slot(ts->input_dev, input_id - 1);
+			input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, true);
+#else /* MT_PROTOCOL_B */
+			input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, input_id - 1);
+			input_report_key(ts->input_dev, BTN_TOUCH, 1);
+#endif /* MT_PROTOCOL_B */
+
+			input_report_abs(ts->input_dev, ABS_MT_POSITION_X, input_x);
+			input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, input_y);
+			input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, input_w);
+			input_report_abs(ts->input_dev, ABS_MT_PRESSURE, input_p);
+
+#if MT_PROTOCOL_B
+#else /* MT_PROTOCOL_B */
+			input_mt_sync(ts->input_dev);
+#endif /* MT_PROTOCOL_B */
+
+			finger_cnt++;
+		}
+	}
+
+#if MT_PROTOCOL_B
+	for (i = 0; i < ts->max_touch_num; i++) {
+		if (press_id[i] != 1) {
+			input_mt_slot(ts->input_dev, i);
+			input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0);
+			input_report_abs(ts->input_dev, ABS_MT_PRESSURE, 0);
+			input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, false);
+		}
+	}
+
+	input_report_key(ts->input_dev, BTN_TOUCH, (finger_cnt > 0));
+#else /* MT_PROTOCOL_B */
+	if (finger_cnt == 0) {
+		input_report_key(ts->input_dev, BTN_TOUCH, 0);
+		input_mt_sync(ts->input_dev);
+	}
+#endif /* MT_PROTOCOL_B */
+
+#if TOUCH_KEY_NUM > 0
+	if (point_data[61] == 0xF8) {
+#if NVT_TOUCH_ESD_PROTECT
+		/* update interrupt timer */
+		irq_timer = jiffies;
+#endif /* #if NVT_TOUCH_ESD_PROTECT */
+		for (i = 0; i < ts->max_button_num; i++) {
+			input_report_key(ts->input_dev, touch_key_array[i], ((point_data[62] >> i) & 0x01));
+		}
+	} else {
+		for (i = 0; i < ts->max_button_num; i++) {
+			input_report_key(ts->input_dev, touch_key_array[i], 0);
+		}
+	}
+#endif
+
+	input_sync(ts->input_dev);
+
+XFER_ERROR:
+
+	mutex_unlock(&ts->lock);
+
+	return IRQ_HANDLED;
+}
+
+/*******************************************************
+ * Description:
+ *     Novatek touchscreen check and stop crc reboot loop.
+ *
+ * return:
+ *     n.a.
+*******************************************************/
+void nvt_stop_crc_reboot(void)
+{
+	uint8_t buf[8] = {0};
+	int32_t retry = 0;
+
+	//read dummy buffer to check CRC fail reboot is happening or not
+
+	//---change I2C index to prevent geting 0xFF, but not 0xFC---
+	nvt_set_page(I2C_BLDR_Address, 0x1F64E);
+
+	//---read to check if buf is 0xFC which means IC is in CRC reboot ---
+	buf[0] = 0x4E;
+	CTP_I2C_READ(ts->client, I2C_BLDR_Address, buf, 4);
+
+	if ((buf[1] == 0xFC) ||
+		((buf[1] == 0xFF) && (buf[2] == 0xFF) && (buf[3] == 0xFF))) {
+
+		//IC is in CRC fail reboot loop, needs to be stopped!
+		for (retry = 5; retry > 0; retry--) {
+
+			//---write i2c cmds to reset idle : 1st---
+			buf[0]=0x00;
+			buf[1]=0xA5;
+			CTP_I2C_WRITE(ts->client, I2C_HW_Address, buf, 2);
+
+			//---write i2c cmds to reset idle : 2rd---
+			buf[0]=0x00;
+			buf[1]=0xA5;
+			CTP_I2C_WRITE(ts->client, I2C_HW_Address, buf, 2);
+			msleep(1);
+
+			//---clear CRC_ERR_FLAG---
+			nvt_set_page(I2C_BLDR_Address, 0x3F135);
+
+			buf[0] = 0x35;
+			buf[1] = 0xA5;
+			CTP_I2C_WRITE(ts->client, I2C_BLDR_Address, buf, 2);
+
+			//---check CRC_ERR_FLAG---
+			nvt_set_page(I2C_BLDR_Address, 0x3F135);
+
+			buf[0] = 0x35;
+			buf[1] = 0x00;
+			CTP_I2C_READ(ts->client, I2C_BLDR_Address, buf, 2);
+
+			if (buf[1] == 0xA5)
+				break;
+		}
+		if (retry == 0)
+			NVT_ERR("CRC auto reboot is not able to be stopped!\n");
+	}
+
+	return;
+}
+
+/*******************************************************
+ * Description:
+ *     Novatek touchscreen check chip version trim function.
+ *
+ * return:
+ *     Executive outcomes. 0---NVT IC. -1---not NVT IC.
+*******************************************************/
+static int8_t nvt_ts_check_chip_ver_trim(uint32_t chip_ver_trim_addr)
+{
+	uint8_t buf[8] = {0};
+	int32_t retry = 0;
+	int32_t list = 0;
+	int32_t i = 0;
+	int32_t found_nvt_chip = 0;
+	int32_t ret = -1;
+
+	nvt_bootloader_reset(); // NOT in retry loop
+
+	//---Check for 5 times---
+	for (retry = 5; retry > 0; retry--) {
+		nvt_sw_reset_idle();
+
+		buf[0] = 0x00;
+		buf[1] = 0x35;
+		CTP_I2C_WRITE(ts->client, I2C_HW_Address, buf, 2);
+		msleep(10);
+
+		nvt_set_page(I2C_BLDR_Address, chip_ver_trim_addr);
+
+		buf[0] = chip_ver_trim_addr & 0xFF;
+		buf[1] = 0x00;
+		buf[2] = 0x00;
+		buf[3] = 0x00;
+		buf[4] = 0x00;
+		buf[5] = 0x00;
+		buf[6] = 0x00;
+		CTP_I2C_READ(ts->client, I2C_BLDR_Address, buf, 7);
+		NVT_LOG("buf[1]=0x%02X, buf[2]=0x%02X, buf[3]=0x%02X, buf[4]=0x%02X, buf[5]=0x%02X, buf[6]=0x%02X\n",
+			buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]);
+
+		//---Stop CRC check to prevent IC auto reboot---
+		if ((buf[1] == 0xFC) ||
+			((buf[1] == 0xFF) && (buf[2] == 0xFF) && (buf[3] == 0xFF))) {
+			nvt_stop_crc_reboot();
+			continue;
+		}
+
+		// compare read chip id on supported list
+		for (list = 0; list < (sizeof(trim_id_table) / sizeof(struct nvt_ts_trim_id_table)); list++) {
+			found_nvt_chip = 0;
+
+			// compare each byte
+			for (i = 0; i < NVT_ID_BYTE_MAX; i++) {
+				if (trim_id_table[list].mask[i]) {
+					if (buf[i + 1] != trim_id_table[list].id[i])
+						break;
+				}
+			}
+
+			if (i == NVT_ID_BYTE_MAX) {
+				found_nvt_chip = 1;
+			}
+
+			if (found_nvt_chip) {
+				NVT_LOG("This is NVT touch IC\n");
+				ts->mmap = trim_id_table[list].mmap;
+				ts->carrier_system = trim_id_table[list].hwinfo->carrier_system;
+				ret = 0;
+				goto out;
+			} else {
+				ts->mmap = NULL;
+				ret = -1;
+			}
+		}
+
+		msleep(10);
+	}
+
+out:
+	return ret;
+}
+
+#if defined(CONFIG_DRM)
+static int nvt_ts_check_dt(struct device_node *np)
+{
+	int i;
+	int count;
+	struct device_node *node;
+	struct drm_panel *panel;
+
+	count = of_count_phandle_with_args(np, "panel", NULL);
+	if (count <= 0)
+		return 0;
+
+	for (i = 0; i < count; i++) {
+		node = of_parse_phandle(np, "panel", i);
+		panel = of_drm_find_panel(node);
+		of_node_put(node);
+		if (!IS_ERR(panel)) {
+			active_panel = panel;
+			NVT_LOG(" %s:find\n", __func__);
+			return 0;
+		}
+	}
+
+	NVT_ERR(" %s: not find\n", __func__);
+	return -ENODEV;
+}
+
+static int nvt_ts_check_default_tp(struct device_node *dt, const char *prop)
+{
+	const char **active_tp = NULL;
+	int count, tmp, score = 0;
+	const char *active;
+	int ret, i;
+
+	count = of_property_count_strings(dt->parent, prop);
+	if (count <= 0 || count > 3)
+		return -ENODEV;
+
+	active_tp = kcalloc(count, sizeof(char *),  GFP_KERNEL);
+	if (!active_tp) {
+		NVT_ERR("FTS alloc failed\n");
+		return -ENOMEM;
+	}
+
+	ret = of_property_read_string_array(dt->parent, prop,
+			active_tp, count);
+	if (ret < 0) {
+		NVT_ERR("fail to read %s %d\n", prop, ret);
+		ret = -ENODEV;
+		goto out;
+	}
+
+	for (i = 0; i < count; i++) {
+		active = active_tp[i];
+		if (active != NULL) {
+			tmp = of_device_is_compatible(dt, active);
+			if (tmp > 0)
+				score++;
+		}
+	}
+
+	if (score <= 0) {
+		NVT_ERR("not match this driver\n");
+		ret = -ENODEV;
+		goto out;
+	}
+	ret = 0;
+out:
+	kfree(active_tp);
+	return ret;
+}
+#endif
+
+/*******************************************************
+ * Description:
+ *     Novatek touchscreen driver probe function.
+ *
+ * return:
+ *     Executive outcomes. 0---succeed. negative---failed
+ *******************************************************/
+static int32_t nvt_ts_late_probe(struct i2c_client *client,
+	const struct i2c_device_id *id)
+{
+	int32_t ret = 0;
+#if ((TOUCH_KEY_NUM > 0) || WAKEUP_GESTURE)
+	int32_t retry = 0;
+#endif
+	//---request and config GPIOs---
+	ret = nvt_gpio_config(ts);
+	if (ret) {
+		NVT_ERR("gpio config error!\n");
+		goto err_gpio_config_failed;
+	}
+
+	//---check chip version trim---
+	ret = nvt_ts_check_chip_ver_trim(CHIP_VER_TRIM_ADDR);
+	if (ret) {
+		NVT_LOG("try to check from old chip ver trim address\n");
+		ret = nvt_ts_check_chip_ver_trim(CHIP_VER_TRIM_OLD_ADDR);
+		if (ret) {
+			NVT_ERR("chip is not identified\n");
+			ret = -EINVAL;
+			goto err_chipvertrim_failed;
+		}
+	}
+
+	nvt_bootloader_reset();
+	nvt_check_fw_reset_state(RESET_STATE_INIT);
+	nvt_get_fw_info();
+
+	//---allocate input device---
+	ts->input_dev = input_allocate_device();
+	if (ts->input_dev == NULL) {
+		NVT_ERR("allocate input device failed\n");
+		ret = -ENOMEM;
+		goto err_input_dev_alloc_failed;
+	}
+
+	ts->max_touch_num = TOUCH_MAX_FINGER_NUM;
+
+#if TOUCH_KEY_NUM > 0
+	ts->max_button_num = TOUCH_KEY_NUM;
+#endif
+
+	ts->int_trigger_type = INT_TRIGGER_TYPE;
+
+
+	//---set input device info.---
+	ts->input_dev->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS) ;
+	ts->input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+	ts->input_dev->propbit[0] = BIT(INPUT_PROP_DIRECT);
+
+#if MT_PROTOCOL_B
+	input_mt_init_slots(ts->input_dev, ts->max_touch_num, 0);
+#endif
+
+	input_set_abs_params(ts->input_dev, ABS_MT_PRESSURE, 0, TOUCH_FORCE_NUM, 0, 0);    //pressure = TOUCH_FORCE_NUM
+
+#if TOUCH_MAX_FINGER_NUM > 1
+	input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);    //area = 255
+
+	input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X, 0, ts->abs_x_max, 0, 0);
+	input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y, 0, ts->abs_y_max, 0, 0);
+#if MT_PROTOCOL_B
+	// no need to set ABS_MT_TRACKING_ID, input_mt_init_slots() already set it
+#else
+	input_set_abs_params(ts->input_dev, ABS_MT_TRACKING_ID, 0, ts->max_touch_num, 0, 0);
+#endif //MT_PROTOCOL_B
+#endif //TOUCH_MAX_FINGER_NUM > 1
+
+#if TOUCH_KEY_NUM > 0
+	for (retry = 0; retry < ts->max_button_num; retry++) {
+		input_set_capability(ts->input_dev, EV_KEY, touch_key_array[retry]);
+	}
+#endif
+
+#if WAKEUP_GESTURE
+	for (retry = 0; retry < ARRAY_SIZE(gesture_key_array); retry++) {
+		input_set_capability(ts->input_dev, EV_KEY, gesture_key_array[retry]);
+	}
+#endif
+
+	snprintf(ts->phys, sizeof(ts->phys), "input/ts");
+	ts->input_dev->name = NVT_TS_NAME;
+	ts->input_dev->phys = ts->phys;
+	ts->input_dev->id.bustype = BUS_I2C;
+
+	//---register input device---
+	ret = input_register_device(ts->input_dev);
+	if (ret) {
+		NVT_ERR("register input device (%s) failed. ret=%d\n", ts->input_dev->name, ret);
+		goto err_input_register_device_failed;
+	}
+
+	//---set int-pin & request irq---
+	client->irq = gpio_to_irq(ts->irq_gpio);
+	if (client->irq) {
+		NVT_LOG("int_trigger_type=%d\n", ts->int_trigger_type);
+		ts->irq_enabled = true;
+		ret = request_threaded_irq(client->irq, NULL, nvt_ts_work_func,
+				ts->int_trigger_type | IRQF_ONESHOT, NVT_I2C_NAME, ts);
+		if (ret != 0) {
+			NVT_ERR("request irq failed. ret=%d\n", ret);
+			goto err_int_request_failed;
+		} else {
+			nvt_irq_enable(false);
+			NVT_LOG("request irq %d succeed\n", client->irq);
+		}
+	}
+
+#if WAKEUP_GESTURE
+	device_init_wakeup(&ts->input_dev->dev, 1);
+#endif
+
+#if BOOT_UPDATE_FIRMWARE
+	nvt_fwu_wq = alloc_workqueue("nvt_fwu_wq", WQ_UNBOUND | WQ_MEM_RECLAIM, 1);
+	if (!nvt_fwu_wq) {
+		NVT_ERR("nvt_fwu_wq create workqueue failed\n");
+		ret = -ENOMEM;
+		goto err_create_nvt_fwu_wq_failed;
+	}
+	INIT_DELAYED_WORK(&ts->nvt_fwu_work, Boot_Update_Firmware);
+	// please make sure boot update start after display reset(RESX) sequence
+	queue_delayed_work(nvt_fwu_wq, &ts->nvt_fwu_work, msecs_to_jiffies(14000));
+#endif
+
+	NVT_LOG("NVT_TOUCH_ESD_PROTECT is %d\n", NVT_TOUCH_ESD_PROTECT);
+#if NVT_TOUCH_ESD_PROTECT
+	INIT_DELAYED_WORK(&nvt_esd_check_work, nvt_esd_check_func);
+	nvt_esd_check_wq = alloc_workqueue("nvt_esd_check_wq", WQ_MEM_RECLAIM, 1);
+	if (!nvt_esd_check_wq) {
+		NVT_ERR("nvt_esd_check_wq create workqueue failed\n");
+		ret = -ENOMEM;
+		goto err_create_nvt_esd_check_wq_failed;
+	}
+	queue_delayed_work(nvt_esd_check_wq, &nvt_esd_check_work,
+			msecs_to_jiffies(NVT_TOUCH_ESD_CHECK_PERIOD));
+#endif /* #if NVT_TOUCH_ESD_PROTECT */
+
+	//---set device node---
+#if NVT_TOUCH_PROC
+	ret = nvt_flash_proc_init();
+	if (ret != 0) {
+		NVT_ERR("nvt flash proc init failed. ret=%d\n", ret);
+		goto err_flash_proc_init_failed;
+	}
+#endif
+
+#if NVT_TOUCH_EXT_PROC
+	ret = nvt_extra_proc_init();
+	if (ret != 0) {
+		NVT_ERR("nvt extra proc init failed. ret=%d\n", ret);
+		goto err_extra_proc_init_failed;
+	}
+#endif
+
+#if NVT_TOUCH_MP
+	ret = nvt_mp_proc_init();
+	if (ret != 0) {
+		NVT_ERR("nvt mp proc init failed. ret=%d\n", ret);
+		goto err_mp_proc_init_failed;
+	}
+#endif
+
+	bTouchIsAwake = 1;
+	NVT_LOG("end\n");
+
+	nvt_irq_enable(true);
+
+	return 0;
+
+#if NVT_TOUCH_MP
+nvt_mp_proc_deinit();
+err_mp_proc_init_failed:
+#endif
+#if NVT_TOUCH_EXT_PROC
+nvt_extra_proc_deinit();
+err_extra_proc_init_failed:
+#endif
+#if NVT_TOUCH_PROC
+nvt_flash_proc_deinit();
+err_flash_proc_init_failed:
+#endif
+#if NVT_TOUCH_ESD_PROTECT
+	if (nvt_esd_check_wq) {
+		cancel_delayed_work_sync(&nvt_esd_check_work);
+		destroy_workqueue(nvt_esd_check_wq);
+		nvt_esd_check_wq = NULL;
+	}
+err_create_nvt_esd_check_wq_failed:
+#endif
+#if BOOT_UPDATE_FIRMWARE
+	if (nvt_fwu_wq) {
+		cancel_delayed_work_sync(&ts->nvt_fwu_work);
+		destroy_workqueue(nvt_fwu_wq);
+		nvt_fwu_wq = NULL;
+	}
+err_create_nvt_fwu_wq_failed:
+#endif
+#if WAKEUP_GESTURE
+	device_init_wakeup(&ts->input_dev->dev, 0);
+#endif
+	free_irq(client->irq, ts);
+err_int_request_failed:
+	input_unregister_device(ts->input_dev);
+	ts->input_dev = NULL;
+err_input_register_device_failed:
+	if (ts->input_dev) {
+		input_free_device(ts->input_dev);
+		ts->input_dev = NULL;
+	}
+err_input_dev_alloc_failed:
+err_chipvertrim_failed:
+	nvt_gpio_deconfig(ts);
+err_gpio_config_failed:
+	NVT_ERR("ret = %d\n", ret);
+	return ret;
+}
+
+/*******************************************************
+ * Description:
+ *     Novatek touchscreen driver probe function.
+ *
+ * return:
+ *     Executive outcomes. 0---succeed. negative---failed
+ *******************************************************/
+static int32_t nvt_ts_probe(struct i2c_client *client,
+	const struct i2c_device_id *id)
+{
+	int32_t ret = 0;
+#if defined(CONFIG_DRM)
+	struct device_node *dp = client->dev.of_node;
+#endif
+
+	NVT_LOG("start\n");
+
+#if defined(CONFIG_DRM)
+	if (nvt_ts_check_dt(dp)) {
+		if (!nvt_ts_check_default_tp(dp, "qcom,i2c-touch-active"))
+			ret = -EPROBE_DEFER;
+		else
+			ret = -ENODEV;
+
+		return ret;
+	}
+#endif
+
+	ts = devm_kzalloc(&client->dev, sizeof(struct nvt_ts_data), GFP_KERNEL);
+	if (ts == NULL) {
+		NVT_ERR("failed to allocated memory for nvt ts data\n");
+		return -ENOMEM;
+	}
+
+	ts->client = client;
+	i2c_set_clientdata(client, ts);
+
+	//---parse dts---
+	nvt_parse_dt(&client->dev);
+
+	//---check i2c func.---
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+		NVT_ERR("i2c_check_functionality failed. (no I2C_FUNC_I2C)\n");
+		return  -ENODEV;
+	}
+
+	mutex_init(&ts->lock);
+	mutex_init(&ts->xbuf_lock);
+
+	ts->id = id;
+
+#if defined(CONFIG_DRM)
+	nvt_i2c_register_for_panel_events(client->dev.of_node, ts);
+#elif defined(_MSM_DRM_NOTIFY_H_)
+	ts->drm_notif.notifier_call = nvt_drm_notifier_callback;
+	ret = msm_drm_register_client(&ts->drm_notif);
+	if (ret) {
+		NVT_ERR("register drm_notifier failed. ret=%d\n", ret);
+		goto err_register_drm_notif_failed;
+	}
+#else
+	ts->fb_notif.notifier_call = nvt_fb_notifier_callback;
+	ts->fb_notif.notifier_call = nvt_fb_notifier_callback;
+	if (ret) {
+		NVT_ERR("register fb_notifier failed. ret=%d\n", ret);
+		goto err_register_fb_notif_failed;
+	}
+#endif
+	NVT_LOG("end\n");
+	return 0;
+#if defined(CONFIG_DRM)
+
+#elif defined(_MSM_DRM_NOTIFY_H_)
+err_register_drm_notif_failed:
+#else
+err_register_fb_notif_failed:
+#endif
+	return -ENODEV;
+}
+
+/*******************************************************
+ * Description:
+ *     Novatek touchscreen driver release function.
+ *
+ * return:
+ *     Executive outcomes. 0---succeed.
+ *******************************************************/
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0))
+static void  nvt_ts_remove(struct i2c_client *client)
+#else
+static int32_t  nvt_ts_remove(struct i2c_client *client)
+#endif
+{
+	NVT_LOG("Removing driver...\n");
+
+#if defined(CONFIG_DRM)
+	if (active_panel && ts->notifier_cookie)
+		panel_event_notifier_unregister(ts->notifier_cookie);
+#elif defined(_MSM_DRM_NOTIFY_H_)
+	if (msm_drm_unregister_client(&ts->drm_notif))
+		NVT_ERR("Error occurred while unregistering drm_notifier.\n");
+#else
+	if (fb_unregister_client(&ts->fb_notif))
+		NVT_ERR("Error occurred while unregistering fb_notifier.\n");
+#endif
+
+#if NVT_TOUCH_MP
+	nvt_mp_proc_deinit();
+#endif
+#if NVT_TOUCH_EXT_PROC
+	nvt_extra_proc_deinit();
+#endif
+#if NVT_TOUCH_PROC
+	nvt_flash_proc_deinit();
+#endif
+
+#if NVT_TOUCH_ESD_PROTECT
+	if (nvt_esd_check_wq) {
+		cancel_delayed_work_sync(&nvt_esd_check_work);
+		nvt_esd_check_enable(false);
+		destroy_workqueue(nvt_esd_check_wq);
+		nvt_esd_check_wq = NULL;
+	}
+#endif
+
+#if BOOT_UPDATE_FIRMWARE
+	if (nvt_fwu_wq) {
+		cancel_delayed_work_sync(&ts->nvt_fwu_work);
+		destroy_workqueue(nvt_fwu_wq);
+		nvt_fwu_wq = NULL;
+	}
+#endif
+
+#if WAKEUP_GESTURE
+	if (ts->input_dev)
+		device_init_wakeup(&ts->input_dev->dev, 0);
+#endif
+
+	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 = NULL;
+	}
+
+	i2c_set_clientdata(client, NULL);
+
+	if (ts) {
+		kfree(ts);
+		ts = NULL;
+	}
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0))
+	return 0;
+#endif
+}
+
+static void nvt_ts_shutdown(struct i2c_client *client)
+{
+	NVT_LOG("Shutdown driver...\n");
+
+	nvt_irq_enable(false);
+
+#if defined(CONFIG_DRM)
+	if (active_panel && ts->notifier_cookie)
+		panel_event_notifier_unregister(ts->notifier_cookie);
+#elif defined(_MSM_DRM_NOTIFY_H_)
+	if (msm_drm_unregister_client(&ts->drm_notif))
+		NVT_ERR("Error occurred while unregistering drm_notifier.\n");
+#else
+	if (fb_unregister_client(&ts->fb_notif))
+		NVT_ERR("Error occurred while unregistering fb_notifier.\n");
+#endif
+
+#if NVT_TOUCH_MP
+	nvt_mp_proc_deinit();
+#endif
+#if NVT_TOUCH_EXT_PROC
+	nvt_extra_proc_deinit();
+#endif
+#if NVT_TOUCH_PROC
+	nvt_flash_proc_deinit();
+#endif
+
+#if NVT_TOUCH_ESD_PROTECT
+	if (nvt_esd_check_wq) {
+		cancel_delayed_work_sync(&nvt_esd_check_work);
+		nvt_esd_check_enable(false);
+		destroy_workqueue(nvt_esd_check_wq);
+		nvt_esd_check_wq = NULL;
+	}
+#endif /* #if NVT_TOUCH_ESD_PROTECT */
+
+#if BOOT_UPDATE_FIRMWARE
+	if (nvt_fwu_wq) {
+		cancel_delayed_work_sync(&ts->nvt_fwu_work);
+		destroy_workqueue(nvt_fwu_wq);
+		nvt_fwu_wq = NULL;
+	}
+#endif
+
+#if WAKEUP_GESTURE
+	if (ts->input_dev)
+		device_init_wakeup(&ts->input_dev->dev, 0);
+#endif
+}
+
+/*******************************************************
+ * Description:
+ *     Novatek touchscreen driver suspend function.
+ *
+ * return:
+ *     Executive outcomes. 0---succeed.
+ *******************************************************/
+static int32_t nvt_ts_suspend(struct device *dev)
+{
+	uint8_t buf[4] = {0};
+#if MT_PROTOCOL_B
+	uint32_t i = 0;
+#endif
+
+	if (!bTouchIsAwake) {
+		NVT_LOG("Touch is already suspend\n");
+		return 0;
+	}
+
+#if !WAKEUP_GESTURE
+	nvt_irq_enable(false);
+#endif
+
+#if NVT_TOUCH_ESD_PROTECT
+	NVT_LOG("cancel delayed work sync\n");
+	cancel_delayed_work_sync(&nvt_esd_check_work);
+	nvt_esd_check_enable(false);
+#endif /* #if NVT_TOUCH_ESD_PROTECT */
+
+	mutex_lock(&ts->lock);
+
+	NVT_LOG("start\n");
+
+	bTouchIsAwake = 0;
+
+#if WAKEUP_GESTURE
+	//---write command to enter "wakeup gesture mode"---
+	buf[0] = EVENT_MAP_HOST_CMD;
+	buf[1] = 0x13;
+	CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 2);
+
+	enable_irq_wake(ts->client->irq);
+
+	NVT_LOG("Enabled touch wakeup gesture\n");
+
+#else // WAKEUP_GESTURE
+	//---write command to enter "deep sleep mode"---
+	buf[0] = EVENT_MAP_HOST_CMD;
+	buf[1] = 0x11;
+	CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 2);
+#endif // WAKEUP_GESTURE
+
+	mutex_unlock(&ts->lock);
+
+	/* release all touches */
+#if MT_PROTOCOL_B
+	for (i = 0; i < ts->max_touch_num; i++) {
+		input_mt_slot(ts->input_dev, i);
+		input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0);
+		input_report_abs(ts->input_dev, ABS_MT_PRESSURE, 0);
+		input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, 0);
+	}
+#endif
+	input_report_key(ts->input_dev, BTN_TOUCH, 0);
+#if !MT_PROTOCOL_B
+	input_mt_sync(ts->input_dev);
+#endif
+	input_sync(ts->input_dev);
+
+	msleep(50);
+
+	NVT_LOG("end\n");
+
+	return 0;
+}
+
+/*******************************************************
+ * Description:
+ *     Novatek touchscreen driver resume function.
+ *
+ * return:
+ *     Executive outcomes. 0---succeed.
+ *******************************************************/
+static int32_t nvt_ts_resume(struct device *dev)
+{
+	NVT_LOG("start\n");
+
+	if (bTouchIsAwake || ts->fw_ver == 0) {
+		nvt_ts_late_probe(ts->client, ts->id);
+		NVT_LOG("nvt_ts_late_probe\n");
+		return 0;
+	}
+
+	mutex_lock(&ts->lock);
+
+	// make sure display reset(RESX) sequence and dsi cmds sent before this
+#if NVT_TOUCH_SUPPORT_HW_RST
+	gpio_set_value(ts->reset_gpio, 1);
+#endif
+
+	// NT36772 IC due to no boot-load when RESX/TP_RESX
+	// nvt_bootloader_reset();
+	if (nvt_check_fw_reset_state(RESET_STATE_REK)) {
+		NVT_ERR("FW is not ready! Try to bootloader reset...\n");
+		nvt_bootloader_reset();
+		nvt_check_fw_reset_state(RESET_STATE_REK);
+	}
+
+#if !WAKEUP_GESTURE
+	nvt_irq_enable(true);
+#endif
+
+#if NVT_TOUCH_ESD_PROTECT
+	nvt_esd_check_enable(false);
+	queue_delayed_work(nvt_esd_check_wq, &nvt_esd_check_work,
+			msecs_to_jiffies(NVT_TOUCH_ESD_CHECK_PERIOD));
+#endif /* #if NVT_TOUCH_ESD_PROTECT */
+
+	bTouchIsAwake = 1;
+
+	mutex_unlock(&ts->lock);
+
+	NVT_LOG("end\n");
+
+	return 0;
+}
+
+#if defined(CONFIG_DRM)
+
+static void nvt_i2c_panel_notifier_callback(enum panel_event_notifier_tag tag,
+		struct panel_event_notification *notification, void *client_data)
+{
+	struct nvt_ts_data *ts = client_data;
+
+	if (!notification) {
+		pr_err("Invalid notification\n");
+		return;
+	}
+
+	NVT_LOG("Notification type:%d, early_trigger:%d",
+		notification->notif_type,
+		notification->notif_data.early_trigger);
+
+	switch (notification->notif_type) {
+	case DRM_PANEL_EVENT_UNBLANK:
+		if (notification->notif_data.early_trigger)
+			NVT_LOG("resume notification pre commit\n");
+		else
+			nvt_ts_resume(&ts->client->dev);
+		break;
+
+	case DRM_PANEL_EVENT_BLANK:
+		if (notification->notif_data.early_trigger)
+			nvt_ts_suspend(&ts->client->dev);
+		else
+			NVT_LOG("suspend notification post commit\n");
+		break;
+
+	case DRM_PANEL_EVENT_BLANK_LP:
+		NVT_LOG("received lp event\n");
+		break;
+
+	case DRM_PANEL_EVENT_FPS_CHANGE:
+		NVT_LOG("Received fps change old fps:%d new fps:%d\n",
+			notification->notif_data.old_fps,
+			notification->notif_data.new_fps);
+		break;
+	default:
+		NVT_LOG("notification serviced :%d\n",
+			notification->notif_type);
+		break;
+	}
+}
+
+#elif defined(_MSM_DRM_NOTIFY_H_)
+static int nvt_drm_notifier_callback(struct notifier_block *self, unsigned long event, void *data)
+{
+	struct msm_drm_notifier *evdata = data;
+	int *blank;
+	struct nvt_ts_data *ts =
+		container_of(self, struct nvt_ts_data, drm_notif);
+
+	if (!evdata || (evdata->id != 0))
+		return 0;
+
+	if (evdata->data && ts) {
+		blank = evdata->data;
+		if (event == MSM_DRM_EARLY_EVENT_BLANK) {
+			if (*blank == MSM_DRM_BLANK_POWERDOWN) {
+				NVT_LOG("event=%lu, *blank=%d\n", event, *blank);
+				nvt_ts_suspend(&ts->client->dev);
+			}
+		} else if (event == MSM_DRM_EVENT_BLANK) {
+			if (*blank == MSM_DRM_BLANK_UNBLANK) {
+				NVT_LOG("event=%lu, *blank=%d\n", event, *blank);
+				nvt_ts_resume(&ts->client->dev);
+			}
+		}
+	}
+
+	return 0;
+}
+
+#else
+static int nvt_fb_notifier_callback(struct notifier_block *self, unsigned long event, void *data)
+{
+	struct fb_event *evdata = data;
+	int *blank;
+	struct nvt_ts_data *ts =
+		container_of(self, struct nvt_ts_data, fb_notif);
+
+	if (evdata && evdata->data && event == FB_EARLY_EVENT_BLANK) {
+		blank = evdata->data;
+		if (*blank == FB_BLANK_POWERDOWN) {
+			NVT_LOG("event=%lu, *blank=%d\n", event, *blank);
+			nvt_ts_suspend(&ts->client->dev);
+		}
+	} else if (evdata && evdata->data && event == FB_EVENT_BLANK) {
+		blank = evdata->data;
+		if (*blank == FB_BLANK_UNBLANK) {
+			NVT_LOG("event=%lu, *blank=%d\n", event, *blank);
+			nvt_ts_resume(&ts->client->dev);
+		}
+	}
+
+	return 0;
+}
+#endif
+
+static const struct i2c_device_id nvt_ts_id[] = {
+	{ NVT_I2C_NAME, 0 },
+	{ }
+};
+
+#ifdef CONFIG_OF
+static struct of_device_id nvt_match_table[] = {
+	{ .compatible = "novatek,NVT-ts",},
+	{ },
+};
+#endif
+
+static struct i2c_driver nvt_i2c_driver = {
+	.probe		= nvt_ts_probe,
+	.remove		= nvt_ts_remove,
+	.shutdown	= nvt_ts_shutdown,
+	.id_table	= nvt_ts_id,
+	.driver = {
+		.name	= NVT_I2C_NAME,
+#ifdef CONFIG_OF
+		.of_match_table = nvt_match_table,
+#endif
+	},
+};
+
+/*******************************************************
+ * Description:
+ *     Driver Install function.
+ *
+ * return:
+ *     Executive Outcomes. 0---succeed. not 0---failed.
+ ********************************************************/
+static int32_t __init nvt_driver_init(void)
+{
+	int32_t ret = 0;
+
+	NVT_LOG("start\n");
+	//---add i2c driver---
+	ret = i2c_add_driver(&nvt_i2c_driver);
+	if (ret) {
+		NVT_ERR("failed to add i2c driver");
+		goto err_driver;
+	}
+
+	NVT_LOG("finished\n");
+
+err_driver:
+	return ret;
+}
+
+/*******************************************************
+ * Description:
+ *     Driver uninstall function.
+ *
+ * return:
+ *     n.a.
+ ********************************************************/
+static void __exit nvt_driver_exit(void)
+{
+	i2c_del_driver(&nvt_i2c_driver);
+}
+
+//late_initcall(nvt_driver_init);
+module_init(nvt_driver_init);
+module_exit(nvt_driver_exit);
+
+MODULE_DESCRIPTION("Novatek Touchscreen Driver");
+MODULE_LICENSE("GPL v2");
+
+#else  /* NT36XXX_SPI */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/proc_fs.h>
+#include <linux/input/mt.h>
+#include <linux/of_gpio.h>
+#include <linux/of_irq.h>
+
+#if defined(CONFIG_DRM)
+#include <linux/soc/qcom/panel_event_notifier.h>
+#endif
+
+#include "nt36xxx.h"
+#if NVT_SPI_TOUCH_ESD_PROTECT
+#include <linux/jiffies.h>
+#endif /* #if NVT_SPI_TOUCH_ESD_PROTECT */
+
+#if NVT_SPI_TOUCH_ESD_PROTECT
+static struct delayed_work nvt_spi_esd_check_work;
+static struct workqueue_struct *nvt_spi_esd_check_wq;
+static unsigned long nvt_spi_irq_timer;
+uint8_t nvt_spi_esd_check;
+uint8_t nvt_spi_esd_retry;
+#endif /* #if NVT_SPI_TOUCH_ESD_PROTECT */
+
+struct nvt_spi_data_t *nvt_spi_data;
+
+#if NVT_SPI_BOOT_UPDATE_FIRMWARE
+static struct workqueue_struct *nvt_spi_fwu_wq;
+#endif
+
+#if defined(CONFIG_DRM)
+static struct drm_panel *active_spi_panel;
+static void nvt_spi_panel_notifier_callback(enum panel_event_notifier_tag tag,
+			struct panel_event_notification *event, void *client_data);
+
+#elif defined(_MSM_DRM_NOTIFY_H_)
+static int nvt_drm_notifier_callback(
+		struct notifier_block *self, unsigned long event, void *data);
+#else
+static int nvt_fb_notifier_callback(
+		struct notifier_block *self, unsigned long event, void *data);
+#endif
+
+static uint32_t NVT_SPI_ENG_RST_ADDR = 0x7FFF80;
+uint32_t NVT_SPI_SWRST_N8_ADDR; //read from dtsi
+uint32_t NVT_SPI_RD_FAST_ADDR;  //read from dtsi
+
+#if NVT_SPI_TOUCH_KEY_NUM > 0
+const uint16_t nvt_spi_touch_key_array[NVT_SPI_TOUCH_KEY_NUM] = {
+	KEY_BACK,
+	KEY_HOME,
+	KEY_MENU
+};
+#endif
+
+#if NVT_SPI_WAKEUP_GESTURE
+const uint16_t nvt_spi_gesture_key_array[] = {
+	KEY_POWER,  //GESTURE_WORD_C
+	KEY_POWER,  //GESTURE_WORD_W
+	KEY_POWER,  //GESTURE_WORD_V
+	KEY_POWER,  //GESTURE_DOUBLE_CLICK
+	KEY_POWER,  //GESTURE_WORD_Z
+	KEY_POWER,  //GESTURE_WORD_M
+	KEY_POWER,  //GESTURE_WORD_O
+	KEY_POWER,  //GESTURE_WORD_e
+	KEY_POWER,  //GESTURE_WORD_S
+	KEY_POWER,  //GESTURE_SLIDE_UP
+	KEY_POWER,  //GESTURE_SLIDE_DOWN
+	KEY_POWER,  //GESTURE_SLIDE_LEFT
+	KEY_POWER,  //GESTURE_SLIDE_RIGHT
+};
+#endif
+
+static uint8_t bTouchIsAwake;
+
+#if defined(CONFIG_DRM)
+static void nvt_spi_register_for_panel_events(struct device_node *dp,
+					struct nvt_spi_data_t *ts)
+{
+	void *cookie = NULL;
+
+	cookie = panel_event_notifier_register(PANEL_EVENT_NOTIFICATION_PRIMARY,
+			PANEL_EVENT_NOTIFIER_CLIENT_PRIMARY_TOUCH, active_spi_panel,
+			&nvt_spi_panel_notifier_callback, ts);
+	if (!cookie) {
+		pr_err("Failed to register for panel events\n");
+		return;
+	}
+
+	ts->notifier_cookie = cookie;
+}
+#endif
+
+/*
+ *******************************************************
+ * Description:
+ *	Novatek touchscreen irq enable/disable function.
+ *
+ * return:
+ *	n.a.
+ ******************************************************
+ */
+static void nvt_spi_irq_enable(bool enable)
+{
+	struct irq_desc *desc;
+	struct nvt_spi_data_t *ts = nvt_spi_data;
+
+	if (enable) {
+		if (!ts->irq_enabled) {
+			enable_irq(ts->client->irq);
+			ts->irq_enabled = true;
+		}
+	} else {
+		if (ts->irq_enabled) {
+			disable_irq(ts->client->irq);
+			ts->irq_enabled = false;
+		}
+	}
+
+	desc = irq_to_desc(ts->client->irq);
+	NVT_LOG("enable=%d, desc->depth=%d\n", enable, desc->depth);
+}
+
+/*
+ *******************************************************
+ * Description:
+ *	Novatek touchscreen spi read/write core function.
+ *
+ * return:
+ *	Executive outcomes. 0---succeed.
+ ******************************************************
+ */
+static inline int32_t nvt_spi_read_write(
+		struct spi_device *client, uint8_t *buf, size_t len, enum NVT_SPI_RW rw)
+{
+	struct spi_message m;
+	struct spi_transfer t = {
+		.len    = len,
+	};
+	struct nvt_spi_data_t *ts = nvt_spi_data;
+
+	memset(ts->xbuf, 0, len + NVT_SPI_DUMMY_BYTES);
+	memcpy(ts->xbuf, buf, len);
+
+	switch (rw) {
+	case NVT_SPI_READ:
+		t.tx_buf = ts->xbuf;
+		t.rx_buf = ts->rbuf;
+		t.len = (len + NVT_SPI_DUMMY_BYTES);
+		break;
+
+	case NVT_SPI_WRITE:
+		t.tx_buf = ts->xbuf;
+		break;
+	}
+
+	spi_message_init(&m);
+	spi_message_add_tail(&t, &m);
+	return spi_sync(client, &m);
+}
+
+/*
+ ******************************************************
+ * Description:
+ *	Novatek touchscreen spi read function.
+ *
+ * return:
+ *	Executive outcomes. 2---succeed. -5---I/O error
+ ******************************************************
+ */
+int32_t nvt_spi_read(uint8_t *buf, uint16_t len)
+{
+	int32_t ret = -1;
+	int32_t retries = 0;
+	struct nvt_spi_data_t *ts = nvt_spi_data;
+
+	mutex_lock(&ts->xbuf_lock);
+
+	buf[0] = NVT_SPI_READ_MASK(buf[0]);
+
+	while (retries < 5) {
+		ret = nvt_spi_read_write(ts->client, buf, len, NVT_SPI_READ);
+		if (ret == 0)
+			break;
+		retries++;
+	}
+
+	if (unlikely(retries == 5)) {
+		NVT_ERR("read error, ret=%d\n", ret);
+		ret = -EIO;
+	} else
+		memcpy((buf+1), (ts->rbuf+2), (len-1));
+
+	mutex_unlock(&ts->xbuf_lock);
+
+	return ret;
+}
+
+/*
+ ******************************************************
+ * Description:
+ *	Novatek touchscreen spi write function.
+ *
+ * return:
+ *	Executive outcomes. 1---succeed. -5---I/O error
+ ******************************************************
+ */
+int32_t nvt_spi_write(uint8_t *buf, uint16_t len)
+{
+	int32_t ret = -1;
+	int32_t retries = 0;
+	struct nvt_spi_data_t *ts = nvt_spi_data;
+
+	mutex_lock(&ts->xbuf_lock);
+
+	buf[0] = NVT_SPI_WRITE_MASK(buf[0]);
+
+	while (retries < 5) {
+		ret = nvt_spi_read_write(ts->client, buf, len, NVT_SPI_WRITE);
+		if (ret == 0)
+			break;
+		retries++;
+	}
+
+	if (unlikely(retries == 5)) {
+		NVT_ERR("error, ret=%d\n", ret);
+		ret = -EIO;
+	}
+
+	mutex_unlock(&ts->xbuf_lock);
+
+	return ret;
+}
+
+/*
+ ******************************************************
+ * Description:
+ *	Novatek touchscreen set index/page/addr address.
+ *
+ * return:
+ *	Executive outcomes. 0---succeed. -5---access fail.
+ ******************************************************
+ */
+int32_t nvt_spi_set_page(uint32_t addr)
+{
+	uint8_t buf[4] = {0};
+
+	buf[0] = 0xFF;	//set index/page/addr command
+	buf[1] = (addr >> 15) & 0xFF;
+	buf[2] = (addr >> 7) & 0xFF;
+
+	return nvt_spi_write(buf, 3);
+}
+
+/*
+ *******************************************************
+ * Description:
+ *	Novatek touchscreen write data to specify address.
+ *
+ * return:
+ *	Executive outcomes. 0---succeed. -5---access fail.
+ *******************************************************
+ */
+int32_t nvt_spi_write_addr(uint32_t addr, uint8_t data)
+{
+	int32_t ret = 0;
+	uint8_t buf[4] = {0};
+
+	//---set xdata index---
+	buf[0] = 0xFF;	//set index/page/addr command
+	buf[1] = (addr >> 15) & 0xFF;
+	buf[2] = (addr >> 7) & 0xFF;
+	ret = nvt_spi_write(buf, 3);
+	if (ret) {
+		NVT_ERR("set page 0x%06X failed, ret = %d\n", addr, ret);
+		return ret;
+	}
+
+	//---write data to index---
+	buf[0] = addr & (0x7F);
+	buf[1] = data;
+	ret = nvt_spi_write(buf, 2);
+	if (ret) {
+		NVT_ERR("write data to 0x%06X failed, ret = %d\n", addr, ret);
+		return ret;
+	}
+
+	return ret;
+}
+
+/*
+ *******************************************************
+ * Description:
+ *	Novatek touchscreen enable hw bld crc function.
+ *
+ * return:
+ *	N/A.
+ ******************************************************
+ */
+void nvt_spi_bld_crc_enable(void)
+{
+	uint8_t buf[4] = {0};
+	struct nvt_spi_data_t *ts = nvt_spi_data;
+
+	//---set xdata index to BLD_CRC_EN_ADDR---
+	nvt_spi_set_page(ts->mmap->BLD_CRC_EN_ADDR);
+
+	//---read data from index---
+	buf[0] = ts->mmap->BLD_CRC_EN_ADDR & (0x7F);
+	buf[1] = 0xFF;
+	nvt_spi_read(buf, 2);
+
+	//---write data to index---
+	buf[0] = ts->mmap->BLD_CRC_EN_ADDR & (0x7F);
+	buf[1] = buf[1] | (0x01 << 7);
+	nvt_spi_write(buf, 2);
+}
+
+/*
+ *******************************************************
+ * Description:
+ *	Novatek touchscreen clear status & enable fw crc function.
+ *
+ * return:
+ *	N/A.
+ ******************************************************
+ */
+void nvt_spi_fw_crc_enable(void)
+{
+	uint8_t buf[4] = {0};
+	struct nvt_spi_data_t *ts = nvt_spi_data;
+
+	//---set xdata index to EVENT BUF ADDR---
+	nvt_spi_set_page(ts->mmap->EVENT_BUF_ADDR);
+
+	//---clear fw reset status---
+	buf[0] = NVT_SPI_EVENT_MAP_RESET_COMPLETE & (0x7F);
+	buf[1] = 0x00;
+	nvt_spi_write(buf, 2);
+
+	//---enable fw crc---
+	buf[0] = NVT_SPI_EVENT_MAP_HOST_CMD & (0x7F);
+	buf[1] = 0xAE;	//enable fw crc command
+	nvt_spi_write(buf, 2);
+}
+
+/*
+ *******************************************************
+ * Description:
+ *	Novatek touchscreen set boot ready function.
+ *
+ * return:
+ *	N/A.
+ ******************************************************
+ */
+void nvt_spi_boot_ready(void)
+{
+	struct nvt_spi_data_t *ts = nvt_spi_data;
+
+	//---write BOOT_RDY status cmds---
+	nvt_spi_write_addr(ts->mmap->BOOT_RDY_ADDR, 1);
+
+	mdelay(5);
+
+	if (!ts->hw_crc) {
+		//---write BOOT_RDY status cmds---
+		nvt_spi_write_addr(ts->mmap->BOOT_RDY_ADDR, 0);
+
+		//---write POR_CD cmds---
+		nvt_spi_write_addr(ts->mmap->POR_CD_ADDR, 0xA0);
+	}
+}
+
+/*
+ ******************************************************
+ * Description:
+ *	Novatek touchscreen enable auto copy mode function.
+ *
+ * return:
+ *	N/A.
+ ******************************************************
+ */
+void nvt_spi_tx_auto_copy_mode(void)
+{
+	struct nvt_spi_data_t *ts = nvt_spi_data;
+
+	//---write TX_AUTO_COPY_EN cmds---
+	nvt_spi_write_addr(ts->mmap->TX_AUTO_COPY_EN, 0x69);
+
+	NVT_ERR("tx auto copy mode enable\n");
+}
+
+/*
+ ******************************************************
+ * Description:
+ *	Novatek touchscreen check spi dma tx info function.
+ *
+ * return:
+ *	N/A.
+ ******************************************************
+ */
+int32_t nvt_spi_check_spi_dma_tx_info(void)
+{
+	uint8_t buf[8] = {0};
+	int32_t i = 0;
+	const int32_t retry = 200;
+	struct nvt_spi_data_t *ts = nvt_spi_data;
+
+	for (i = 0; i < retry; i++) {
+		//---set xdata index to EVENT BUF ADDR---
+		nvt_spi_set_page(ts->mmap->SPI_DMA_TX_INFO);
+
+		//---read fw status---
+		buf[0] = ts->mmap->SPI_DMA_TX_INFO & 0x7F;
+		buf[1] = 0xFF;
+		nvt_spi_read(buf, 2);
+
+		if (buf[1] == 0x00)
+			break;
+
+		usleep_range(1000, 1100);
+	}
+
+	if (i >= retry) {
+		NVT_ERR("failed, i=%d, buf[1]=0x%02X\n", i, buf[1]);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+/*
+ ******************************************************
+ * Description:
+ *	Novatek touchscreen eng reset cmd function.
+ *
+ * return:
+ *	n.a.
+ ******************************************************
+ */
+void nvt_spi_eng_reset(void)
+{
+	//---eng reset cmds to ENG_RST_ADDR---
+	nvt_spi_write_addr(NVT_SPI_ENG_RST_ADDR, 0x5A);
+
+	mdelay(1);	//wait tMCU_Idle2TP_REX_Hi after TP_RST
+}
+
+/*
+ *******************************************************
+ * Description:
+ *	Novatek touchscreen reset MCU function.
+ *
+ * return:
+ *	n.a.
+ ******************************************************
+ */
+void nvt_spi_sw_reset(void)
+{
+	//---software reset cmds to SWRST_N8_ADDR---
+	nvt_spi_write_addr(NVT_SPI_SWRST_N8_ADDR, 0x55);
+
+	msleep(20);
+}
+
+/*
+ *******************************************************
+ * Description:
+ *	Novatek touchscreen reset MCU then into idle mode
+ *	function.
+ *
+ * return:
+ *	n.a.
+ ******************************************************
+ */
+void nvt_spi_sw_reset_idle(void)
+{
+	//---MCU idle cmds to SWRST_N8_ADDR---
+	nvt_spi_write_addr(NVT_SPI_SWRST_N8_ADDR, 0xAA);
+
+	msleep(20);
+}
+
+/*
+ ******************************************************
+ * Description:
+ *	Novatek touchscreen reset MCU (boot) function.
+ *
+ * return:
+ *	n.a.
+ ******************************************************
+ */
+void nvt_spi_bootloader_reset(void)
+{
+	//---reset cmds to SWRST_N8_ADDR---
+	nvt_spi_write_addr(NVT_SPI_SWRST_N8_ADDR, 0x69);
+
+	mdelay(5);	//wait tBRST2FR after Bootload RST
+
+	if (NVT_SPI_RD_FAST_ADDR) {
+		/* disable SPI_RD_FAST */
+		nvt_spi_write_addr(NVT_SPI_RD_FAST_ADDR, 0x00);
+	}
+
+	NVT_LOG("end\n");
+}
+
+/*
+ *******************************************************
+ * Description:
+ *	Novatek touchscreen clear FW status function.
+ *
+ * return:
+ *	Executive outcomes. 0---succeed. -1---fail.
+ ******************************************************
+ */
+int32_t nvt_spi_clear_fw_status(void)
+{
+	uint32_t addr;
+	uint8_t buf[8] = {0};
+	int32_t i = 0;
+	const int32_t retry = 20;
+	struct nvt_spi_data_t *ts = nvt_spi_data;
+
+	for (i = 0; i < retry; i++) {
+		//---set xdata index to EVENT BUF ADDR---
+		addr = ts->mmap->EVENT_BUF_ADDR;
+		nvt_spi_set_page(addr | NVT_SPI_EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE);
+
+		//---clear fw status---
+		buf[0] = NVT_SPI_EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE;
+		buf[1] = 0x00;
+		nvt_spi_write(buf, 2);
+
+		//---read fw status---
+		buf[0] = NVT_SPI_EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE;
+		buf[1] = 0xFF;
+		nvt_spi_read(buf, 2);
+
+		if (buf[1] == 0x00)
+			break;
+
+		usleep_range(10000, 11000);
+	}
+
+	if (i >= retry) {
+		NVT_ERR("failed, i=%d, buf[1]=0x%02X\n", i, buf[1]);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+/*
+ ******************************************************
+ * Description:
+ *	Novatek touchscreen check FW status function.
+
+ * return:
+ *	Executive outcomes. 0---succeed. -1---failed.
+ ******************************************************
+ */
+int32_t nvt_spi_check_fw_status(void)
+{
+	uint32_t addr;
+	uint8_t buf[8] = {0};
+	int32_t i = 0;
+	const int32_t retry = 50;
+	struct nvt_spi_data_t *ts = nvt_spi_data;
+
+	usleep_range(20000, 21000);
+
+	for (i = 0; i < retry; i++) {
+		//---set xdata index to EVENT BUF ADDR---
+		addr = ts->mmap->EVENT_BUF_ADDR;
+		nvt_spi_set_page(addr | NVT_SPI_EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE);
+
+		//---read fw status---
+		buf[0] = NVT_SPI_EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE;
+		buf[1] = 0x00;
+		nvt_spi_read(buf, 2);
+
+		if ((buf[1] & 0xF0) == 0xA0)
+			break;
+
+		usleep_range(10000, 11000);
+	}
+
+	if (i >= retry) {
+		NVT_ERR("failed, i=%d, buf[1]=0x%02X\n", i, buf[1]);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+/*
+ ******************************************************
+ * Description:
+ *	Novatek touchscreen check FW reset state function.
+ *
+ * return:
+ *	Executive outcomes. 0---succeed. -1---failed.
+ ******************************************************
+ */
+int32_t nvt_spi_check_fw_reset_state(enum NVT_SPI_RST_COMPLETE_STATE check_reset_state)
+{
+	uint8_t buf[8] = {0};
+	int32_t ret = 0;
+	int32_t retry = 0;
+	int32_t retry_max = (check_reset_state == NVT_SPI_RESET_STATE_INIT) ? 10 : 50;
+	struct nvt_spi_data_t *ts = nvt_spi_data;
+
+	//---set xdata index to EVENT BUF ADDR---
+	nvt_spi_set_page(ts->mmap->EVENT_BUF_ADDR | NVT_SPI_EVENT_MAP_RESET_COMPLETE);
+
+	while (1) {
+		//---read reset state---
+		buf[0] = NVT_SPI_EVENT_MAP_RESET_COMPLETE;
+		buf[1] = 0x00;
+		nvt_spi_read(buf, 6);
+
+		if ((buf[1] >= check_reset_state) && (buf[1] <= NVT_SPI_RESET_STATE_MAX)) {
+			ret = 0;
+			break;
+		}
+
+		retry++;
+		if (unlikely(retry > retry_max)) {
+			NVT_ERR("error, retry=%d, buf[1]=0x%02X,0x%02X,0x%02X,0x%02X,0x%02X\n",
+				retry, buf[1], buf[2], buf[3], buf[4], buf[5]);
+			ret = -1;
+			break;
+		}
+
+		usleep_range(10000, 11000);
+	}
+
+	return ret;
+}
+
+/*
+ *******************************************************
+ * Description:
+ *	Novatek touchscreen get firmware related information
+ *	function.
+ *
+ * return:
+ *	Executive outcomes. 0---success. -1---fail.
+ ******************************************************
+ */
+int32_t nvt_spi_get_fw_info(void)
+{
+	uint8_t buf[64] = {0};
+	uint32_t retry_count = 0;
+	int32_t ret = 0;
+	struct nvt_spi_data_t *ts = nvt_spi_data;
+
+info_retry:
+	//---set xdata index to EVENT BUF ADDR---
+	nvt_spi_set_page(ts->mmap->EVENT_BUF_ADDR | NVT_SPI_EVENT_MAP_FWINFO);
+
+	//---read fw info---
+	buf[0] = NVT_SPI_EVENT_MAP_FWINFO;
+	nvt_spi_read(buf, 39);
+	if ((buf[1] + buf[2]) != 0xFF) {
+		NVT_ERR("FW info is broken! fw_ver=0x%02X, ~fw_ver=0x%02X\n", buf[1], buf[2]);
+		if (retry_count < 3) {
+			retry_count++;
+			NVT_ERR("retry_count=%d\n", retry_count);
+			goto info_retry;
+		} else {
+			ts->fw_ver = 0;
+			ts->abs_x_max = NVT_SPI_TOUCH_DEFAULT_MAX_WIDTH;
+			ts->abs_y_max = NVT_SPI_TOUCH_DEFAULT_MAX_HEIGHT;
+			ts->max_button_num = NVT_SPI_TOUCH_KEY_NUM;
+			NVT_ERR("Set default fw_ver=%d, abs_x_max=%d, ",
+					ts->fw_ver, ts->abs_x_max);
+			NVT_ERR("abs_y_max=%d, max_button_num=%d!\n",
+					ts->abs_y_max, ts->max_button_num);
+			ret = -1;
+			goto out;
+		}
+	}
+	ts->fw_ver = buf[1];
+	ts->x_num = buf[3];
+	ts->y_num = buf[4];
+	ts->abs_x_max = (uint16_t)((buf[5] << 8) | buf[6]);
+	ts->abs_y_max = (uint16_t)((buf[7] << 8) | buf[8]);
+	ts->max_button_num = buf[11];
+	ts->nvt_pid = (uint16_t)((buf[36] << 8) | buf[35]);
+	if (ts->pen_support) {
+		ts->x_gang_num = buf[37];
+		ts->y_gang_num = buf[38];
+	}
+
+	NVT_ERR("fw_ver=0x%02X, fw_type=0x%02X, PID=0x%04X\n", ts->fw_ver, buf[14], ts->nvt_pid);
+
+	ret = 0;
+out:
+
+	return ret;
+}
+
+/*
+ ******************************************************
+ * Create Device Node (Proc Entry)
+ ******************************************************
+ */
+#if NVT_SPI_TOUCH_PROC
+static struct proc_dir_entry *nvt_spi_proc_entry;
+#define DEVICE_NAME "NVTSPI"
+
+/*
+ ******************************************************
+ * Description:
+ *	Novatek touchscreen /proc/NVTSPI read function.
+ *
+ * return:
+ *	Executive outcomes. 2---succeed. -5,-14---failed.
+ ******************************************************
+ */
+static ssize_t nvt_spi_flash_read(
+	struct file *file, char __user *buff, size_t count, loff_t *offp)
+{
+	uint8_t *str = NULL;
+	int32_t ret = 0;
+	int32_t retries = 0;
+	int8_t spi_wr = 0;
+	uint8_t *buf;
+
+	if ((count > NVT_SPI_TRANSFER_LEN + 3) || (count < 3)) {
+		NVT_ERR("invalid transfer len!\n");
+		return -EFAULT;
+	}
+
+	/* allocate buffer for spi transfer */
+	str = kzalloc((count), GFP_KERNEL);
+	if (str == NULL) {
+		NVT_ERR("kzalloc for buf failed!\n");
+		ret = -ENOMEM;
+		goto kzalloc_failed;
+	}
+
+	buf = kzalloc((count), GFP_KERNEL | GFP_DMA);
+	if (buf == NULL) {
+		NVT_ERR("kzalloc for buf failed!\n");
+		ret = -ENOMEM;
+		kfree(str);
+		str = NULL;
+		goto kzalloc_failed;
+	}
+
+	if (copy_from_user(str, buff, count)) {
+		NVT_ERR("copy from user error\n");
+		ret = -EFAULT;
+		goto out;
+	}
+
+#if NVT_SPI_TOUCH_ESD_PROTECT
+	/*
+	 * stop esd check work to avoid case that 0x77 report righ after here to enable esd
+	 * check again finally lead to trigger esd recovery bootloader reset
+	 */
+	cancel_delayed_work_sync(&nvt_spi_esd_check_work);
+	nvt_spi_esd_check_enable(false);
+#endif /* #if NVT_SPI_TOUCH_ESD_PROTECT */
+
+	spi_wr = str[0] >> 7;
+	memcpy(buf, str+2, ((str[0] & 0x7F) << 8) | str[1]);
+
+	if (spi_wr == NVT_SPI_WRITE) {	//SPI write
+		while (retries < 20) {
+			ret = nvt_spi_write(buf, ((str[0] & 0x7F) << 8) | str[1]);
+			if (!ret)
+				break;
+			NVT_ERR("error, retries=%d, ret=%d\n", retries, ret);
+
+			retries++;
+		}
+
+		if (unlikely(retries == 20)) {
+			NVT_ERR("error, ret = %d\n", ret);
+			ret = -EIO;
+			goto out;
+		}
+	} else if (spi_wr == NVT_SPI_READ) {	//SPI read
+		while (retries < 20) {
+			ret = nvt_spi_read(buf, ((str[0] & 0x7F) << 8) | str[1]);
+			if (!ret)
+				break;
+			NVT_ERR("error, retries=%d, ret=%d\n", retries, ret);
+
+			retries++;
+		}
+
+		memcpy(str+2, buf, ((str[0] & 0x7F) << 8) | str[1]);
+		// copy buff to user if spi transfer
+		if (retries < 20) {
+			if (copy_to_user(buff, str, count)) {
+				ret = -EFAULT;
+				goto out;
+			}
+		}
+
+		if (unlikely(retries == 20)) {
+			NVT_ERR("error, ret = %d\n", ret);
+			ret = -EIO;
+			goto out;
+		}
+	} else {
+		NVT_ERR("Call error, str[0]=%d\n", str[0]);
+		ret = -EFAULT;
+		goto out;
+	}
+
+out:
+	kfree(str);
+	kfree(buf);
+kzalloc_failed:
+	return ret;
+}
+
+/*
+ ******************************************************
+ * Description:
+ *	Novatek touchscreen /proc/NVTSPI open function.
+ *
+ * return:
+ *	Executive outcomes. 0---succeed. -12---failed.
+ ******************************************************
+ */
+static int32_t nvt_spi_flash_open(struct inode *inode, struct file *file)
+{
+	struct nvt_spi_flash_data *dev;
+
+	dev = kmalloc(sizeof(struct nvt_spi_flash_data), GFP_KERNEL);
+	if (dev == NULL) {
+		NVT_ERR("Failed to allocate memory for nvt flash data\n");
+		return -ENOMEM;
+	}
+
+	rwlock_init(&dev->lock);
+	file->private_data = dev;
+
+	return 0;
+}
+
+/*
+ ******************************************************
+ * Description:
+ *	Novatek touchscreen /proc/NVTSPI close function.
+ *
+ * return:
+ *	Executive outcomes. 0---succeed.
+ ******************************************************
+ */
+static int32_t nvt_spi_flash_close(struct inode *inode, struct file *file)
+{
+	struct nvt_spi_flash_data *dev = file->private_data;
+
+	kfree(dev);
+
+	return 0;
+}
+
+static const struct proc_ops nvt_flash_fops_spi = {
+	.proc_open = nvt_spi_flash_open,
+	.proc_release = nvt_spi_flash_close,
+	.proc_read = nvt_spi_flash_read,
+};
+
+/*
+ ******************************************************
+ * Description:
+ *	Novatek touchscreen /proc/NVTSPI initial function.
+ *
+ * return:
+ *	Executive outcomes. 0---succeed. -12---failed.
+ ******************************************************
+ */
+static int32_t nvt_spi_flash_proc_init(void)
+{
+	nvt_spi_proc_entry = proc_create(DEVICE_NAME, 0444, NULL, &nvt_flash_fops_spi);
+	if (nvt_spi_proc_entry == NULL) {
+		NVT_ERR("Failed!\n");
+		return -ENOMEM;
+	}
+
+	NVT_LOG("Succeeded!\n");
+
+	NVT_LOG("============================================================\n");
+	NVT_LOG("Create /proc/%s\n", DEVICE_NAME);
+	NVT_LOG("============================================================\n");
+
+	return 0;
+}
+
+/*
+ ******************************************************
+ * Description:
+ *	Novatek touchscreen /proc/NVTSPI deinitial function.
+ *
+ * return:
+ *	n.a.
+ ******************************************************
+ */
+static void nvt_spi_flash_proc_deinit(void)
+{
+	if (nvt_spi_proc_entry != NULL) {
+		remove_proc_entry(DEVICE_NAME, NULL);
+		nvt_spi_proc_entry = NULL;
+		NVT_LOG("Removed /proc/%s\n", DEVICE_NAME);
+	}
+}
+#endif
+
+#if NVT_SPI_WAKEUP_GESTURE
+#define GESTURE_WORD_C          12
+#define GESTURE_WORD_W          13
+#define GESTURE_WORD_V          14
+#define GESTURE_DOUBLE_CLICK    15
+#define GESTURE_WORD_Z          16
+#define GESTURE_WORD_M          17
+#define GESTURE_WORD_O          18
+#define GESTURE_WORD_e          19
+#define GESTURE_WORD_S          20
+#define GESTURE_SLIDE_UP        21
+#define GESTURE_SLIDE_DOWN      22
+#define GESTURE_SLIDE_LEFT      23
+#define GESTURE_SLIDE_RIGHT     24
+/* customized gesture id */
+#define DATA_PROTOCOL           30
+
+/* function page definition */
+#define FUNCPAGE_GESTURE         1
+
+/*
+ *******************************************************
+ * Description:
+ *	Novatek touchscreen wake up gesture key report function.
+ *
+ * return:
+ *	n.a.
+ ******************************************************
+ */
+void nvt_spi_wakeup_gesture_report(uint8_t gesture_id, uint8_t *data)
+{
+	uint32_t keycode = 0;
+	uint8_t func_type = data[2];
+	uint8_t func_id = data[3];
+	struct nvt_spi_data_t *ts = nvt_spi_data;
+
+	/* support fw specifal data protocol */
+	if ((gesture_id == DATA_PROTOCOL) && (func_type == FUNCPAGE_GESTURE))
+		gesture_id = func_id;
+	else if (gesture_id > DATA_PROTOCOL) {
+		NVT_ERR("gesture_id %d is invalid, func_type=%d, func_id=%d\n",
+				gesture_id, func_type, func_id);
+		return;
+	}
+
+	NVT_LOG("gesture_id = %d\n", gesture_id);
+
+	switch (gesture_id) {
+	case GESTURE_WORD_C:
+		NVT_LOG("Gesture : Word-C.\n");
+		keycode = nvt_spi_gesture_key_array[0];
+		break;
+	case GESTURE_WORD_W:
+		NVT_LOG("Gesture : Word-W.\n");
+		keycode = nvt_spi_gesture_key_array[1];
+		break;
+	case GESTURE_WORD_V:
+		NVT_LOG("Gesture : Word-V.\n");
+		keycode = nvt_spi_gesture_key_array[2];
+		break;
+	case GESTURE_DOUBLE_CLICK:
+		NVT_LOG("Gesture : Double Click.\n");
+		keycode = nvt_spi_gesture_key_array[3];
+		break;
+	case GESTURE_WORD_Z:
+		NVT_LOG("Gesture : Word-Z.\n");
+		keycode = nvt_spi_gesture_key_array[4];
+		break;
+	case GESTURE_WORD_M:
+		NVT_LOG("Gesture : Word-M.\n");
+		keycode = nvt_spi_gesture_key_array[5];
+		break;
+	case GESTURE_WORD_O:
+		NVT_LOG("Gesture : Word-O.\n");
+		keycode = nvt_spi_gesture_key_array[6];
+		break;
+	case GESTURE_WORD_e:
+		NVT_LOG("Gesture : Word-e.\n");
+		keycode = nvt_spi_gesture_key_array[7];
+		break;
+	case GESTURE_WORD_S:
+		NVT_LOG("Gesture : Word-S.\n");
+		keycode = nvt_spi_gesture_key_array[8];
+		break;
+	case GESTURE_SLIDE_UP:
+		NVT_LOG("Gesture : Slide UP.\n");
+		keycode = nvt_spi_gesture_key_array[9];
+		break;
+	case GESTURE_SLIDE_DOWN:
+		NVT_LOG("Gesture : Slide DOWN.\n");
+		keycode = nvt_spi_gesture_key_array[10];
+		break;
+	case GESTURE_SLIDE_LEFT:
+		NVT_LOG("Gesture : Slide LEFT.\n");
+		keycode = nvt_spi_gesture_key_array[11];
+		break;
+	case GESTURE_SLIDE_RIGHT:
+		NVT_LOG("Gesture : Slide RIGHT.\n");
+		keycode = nvt_spi_gesture_key_array[12];
+		break;
+	default:
+		break;
+	}
+
+	if (keycode > 0) {
+		input_report_key(ts->input_dev, keycode, 1);
+		input_sync(ts->input_dev);
+		input_report_key(ts->input_dev, keycode, 0);
+		input_sync(ts->input_dev);
+	}
+}
+#endif
+
+/*
+ ******************************************************
+ * Description:
+ *	Novatek touchscreen parse device tree function.
+ *
+ * return:
+ *	n.a.
+ ******************************************************
+ */
+#ifdef CONFIG_OF
+static int32_t nvt_spi_parse_dt(struct device *dev)
+{
+	struct device_node *np = dev->of_node;
+	int32_t ret = 0;
+	struct nvt_spi_data_t *ts = nvt_spi_data;
+
+#if NVT_SPI_TOUCH_SUPPORT_HW_RST
+	ts->reset_gpio = of_get_named_gpio_flags(np, "novatek,reset-gpio", 0, &ts->reset_flags);
+	NVT_LOG("novatek,reset-gpio=%d\n", ts->reset_gpio);
+#endif
+	ts->irq_gpio = of_get_named_gpio_flags(np, "novatek,irq-gpio", 0, &ts->irq_flags);
+	NVT_LOG("novatek,irq-gpio=%d\n", ts->irq_gpio);
+
+	ts->pen_support = of_property_read_bool(np, "novatek,pen-support");
+	NVT_LOG("novatek,pen-support=%d\n", ts->pen_support);
+
+	ts->wgp_stylus = of_property_read_bool(np, "novatek,wgp-stylus");
+	NVT_LOG("novatek,wgp-stylus=%d\n", ts->wgp_stylus);
+
+	ret = of_property_read_u32(np, "novatek,swrst-n8-addr", &NVT_SPI_SWRST_N8_ADDR);
+	if (ret) {
+		NVT_ERR("error reading novatek,swrst-n8-addr. ret=%d\n", ret);
+		return ret;
+	}
+	NVT_LOG("SWRST_N8_ADDR=0x%06X\n", NVT_SPI_SWRST_N8_ADDR);
+
+	ret = of_property_read_u32(np, "novatek,spi-rd-fast-addr", &NVT_SPI_RD_FAST_ADDR);
+	if (ret) {
+		NVT_LOG("not support novatek,spi-rd-fast-addr\n");
+		NVT_SPI_RD_FAST_ADDR = 0;
+		ret = 0;
+	} else
+		NVT_LOG("SPI_RD_FAST_ADDR=0x%06X\n", NVT_SPI_RD_FAST_ADDR);
+
+	return ret;
+}
+#else
+static int32_t nvt_spi_parse_dt(struct device *dev)
+{
+	struct nvt_spi_data_t *ts = nvt_spi_data;
+
+#if NVT_SPI_TOUCH_SUPPORT_HW_RST
+	ts->reset_gpio = NVTTOUCH_RST_PIN;
+#endif
+	ts->irq_gpio = NVTTOUCH_INT_PIN;
+	return 0;
+}
+#endif
+
+/*
+ ******************************************************
+ * Description:
+ *	Novatek touchscreen config and request gpio
+ *
+ * return:
+ *	Executive outcomes. 0---succeed. not 0---failed.
+ ******************************************************
+ */
+static int nvt_spi_gpio_config(struct nvt_spi_data_t *ts)
+{
+	int32_t ret = 0;
+
+#if NVT_SPI_TOUCH_SUPPORT_HW_RST
+	/* request RST-pin (Output/High) */
+	if (gpio_is_valid(ts->reset_gpio)) {
+		ret = gpio_request_one(ts->reset_gpio, GPIOF_OUT_INIT_LOW, "NVT-tp-rst");
+		if (ret) {
+			NVT_ERR("Failed to request NVT-tp-rst GPIO\n");
+			goto err_request_reset_gpio;
+		}
+	}
+#endif
+
+	/* request INT-pin (Input) */
+	if (gpio_is_valid(ts->irq_gpio)) {
+		ret = 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:
+#if NVT_SPI_TOUCH_SUPPORT_HW_RST
+	gpio_free(ts->reset_gpio);
+err_request_reset_gpio:
+#endif
+	return ret;
+}
+
+/*
+ ******************************************************
+ * Description:
+ *	Novatek touchscreen deconfig gpio
+ *
+ * return:
+ *	n.a.
+ ******************************************************
+ */
+static void nvt_spi_gpio_deconfig(struct nvt_spi_data_t *ts)
+{
+	if (gpio_is_valid(ts->irq_gpio))
+		gpio_free(ts->irq_gpio);
+#if NVT_SPI_TOUCH_SUPPORT_HW_RST
+	if (gpio_is_valid(ts->reset_gpio))
+		gpio_free(ts->reset_gpio);
+#endif
+}
+
+static uint8_t nvt_spi_fw_recovery(uint8_t *point_data)
+{
+	uint8_t i = 0;
+	uint8_t detected = true;
+
+	/* check pattern */
+	for (i = 1; i < 7; i++) {
+		if (point_data[i] != 0x77) {
+			detected = false;
+			break;
+		}
+	}
+
+	return detected;
+}
+
+#if NVT_SPI_TOUCH_ESD_PROTECT
+void nvt_spi_esd_check_enable(uint8_t enable)
+{
+	/* update interrupt timer */
+	nvt_spi_irq_timer = jiffies;
+	/* clear esd_retry counter, if protect function is enabled */
+	nvt_spi_esd_retry = enable ? 0 : nvt_spi_esd_retry;
+	/* enable/disable esd check flag */
+	nvt_spi_esd_check = enable;
+}
+
+static void nvt_spi_esd_check_func(struct work_struct *work)
+{
+	unsigned int timer = jiffies_to_msecs(jiffies - nvt_spi_irq_timer);
+	struct nvt_spi_data_t *ts = nvt_spi_data;
+
+	//NVT_LOG("esd_check = %d (retry %d)\n", esd_check, esd_retry);
+
+	if ((timer > NVT_SPI_TOUCH_ESD_CHECK_PERIOD) && esd_check) {
+		mutex_lock(&ts->lock);
+		NVT_ERR("do ESD recovery, timer = %d, retry = %d\n", timer, nvt_spi_esd_retry);
+		/* do esd recovery, reload fw */
+		nvt_spi_update_firmware(NVT_SPI_BOOT_UPDATE_FIRMWARE_NAME);
+		mutex_unlock(&ts->lock);
+		/* update interrupt timer */
+		nvt_spi_irq_timer = jiffies;
+		/* update esd_retry counter */
+		nvt_spi_esd_retry++;
+	}
+
+	queue_delayed_work(nvt_spi_esd_check_wq, &nvt_spi_esd_check_work,
+			msecs_to_jiffies(NVT_SPI_TOUCH_ESD_CHECK_PERIOD));
+}
+#endif /* #if NVT_SPI_TOUCH_ESD_PROTECT */
+
+#define NVT_SPI_PEN_DATA_LEN 14
+#if NVT_SPI_CHECK_PEN_DATA_CHECKSUM
+static int32_t nvt_spi_pen_data_checksum(uint8_t *buf, uint8_t length)
+{
+	uint8_t checksum = 0;
+	int32_t i = 0;
+
+	// Calculate checksum
+	for (i = 0; i < length - 1; i++)
+		checksum += buf[i];
+	checksum = (~checksum + 1);
+
+	// Compare ckecksum and dump fail data
+	if (checksum != buf[length - 1]) {
+		NVT_ERR("pen packet checksum not match. (buf[%d]=0x%02X, checksum=0x%02X)\n",
+			length - 1, buf[length - 1], checksum);
+		//--- dump pen buf ---
+		for (i = 0; i < length; i++)
+			NVT_ERR("%02X ", buf[i]);
+
+		NVT_ERR("\n");
+
+		return -EIO;
+	}
+
+	return 0;
+}
+#endif // #if NVT_SPI_CHECK_PEN_DATA_CHECKSUM
+
+#if NVT_SPI_TOUCH_WDT_RECOVERY
+static uint8_t nvt_spi_recovery_cnt;
+static uint8_t nvt_spi_wdt_fw_recovery(uint8_t *point_data)
+{
+	uint32_t recovery_cnt_max = 10;
+	uint8_t recovery_enable = false;
+	uint8_t i = 0;
+
+	nvt_spi_recovery_cnt++;
+
+	/* check pattern */
+	for (i = 1 ; i < 7 ; i++) {
+		if ((point_data[i] != 0xFD) && (point_data[i] != 0xFE)) {
+			nvt_spi_recovery_cnt = 0;
+			break;
+		}
+	}
+
+	if (nvt_spi_recovery_cnt > recovery_cnt_max) {
+		recovery_enable = true;
+		nvt_spi_recovery_cnt = 0;
+	}
+
+	return recovery_enable;
+}
+#endif	/* #if NVT_SPI_TOUCH_WDT_RECOVERY */
+
+#if NVT_SPI_POINT_DATA_CHECKSUM
+static int32_t nvt_spi_point_data_checksum(uint8_t *buf, uint8_t length)
+{
+	uint8_t checksum = 0;
+	int32_t i = 0;
+
+	// Generate checksum
+	for (i = 0; i < length - 1; i++)
+		checksum += buf[i + 1];
+
+	checksum = (~checksum + 1);
+
+	// Compare ckecksum and dump fail data
+	if (checksum != buf[length]) {
+		NVT_ERR("packet checksum not match. (point_data[%d]=0x%02X, checksum=0x%02X)\n",
+			length, buf[length], checksum);
+
+		for (i = 0; i < 10; i++)
+			NVT_LOG("%02X %02X %02X %02X %02X %02X\n",
+				buf[1 + i * 6], buf[2 + i * 6], buf[3 + i * 6],
+				buf[4 + i * 6], buf[5 + i * 6], buf[6 + i * 6]);
+
+		NVT_LOG("%02X %02X %02X %02X %02X\n",
+			buf[61], buf[62], buf[63], buf[64], buf[65]);
+		return -EIO;
+	}
+
+	return 0;
+}
+#endif /* NVT_SPI_POINT_DATA_CHECKSUM */
+
+/*
+ ******************************************************
+ * Description:
+ *	Novatek touchscreen work function.
+ *
+ * return:
+ *	n.a.
+ ******************************************************
+ */
+#define NVT_SPI_POINT_DATA_LEN 65
+#define NVT_SPI_POINT (NVT_SPI_POINT_DATA_LEN + NVT_SPI_PEN_DATA_LEN + 1 + NVT_SPI_DUMMY_BYTES)
+static irqreturn_t nvt_spi_work_func(int irq, void *data)
+{
+	int32_t ret = -1;
+	uint8_t point_data[NVT_SPI_POINT] = {0};
+	uint32_t position = 0;
+	uint32_t input_x = 0;
+	uint32_t input_y = 0;
+	uint32_t input_w = 0;
+	uint32_t input_p = 0;
+	uint8_t input_id = 0;
+#if NVT_SPI_MT_PROTOCOL_B
+	uint8_t press_id[NVT_SPI_TOUCH_MAX_FINGER_NUM] = {0};
+#endif /* NVT_SPI_MT_PROTOCOL_B */
+	int32_t i = 0;
+	int32_t finger_cnt = 0;
+	uint8_t pen_format_id = 0;
+	uint32_t pen_x = 0;
+	uint32_t pen_y = 0;
+	uint32_t pen_pressure = 0;
+	uint32_t pen_distance = 0;
+	int8_t pen_tilt_x = 0;
+	int8_t pen_tilt_y = 0;
+	uint32_t pen_btn1 = 0;
+	uint32_t pen_btn2 = 0;
+	uint32_t pen_battery = 0;
+	struct nvt_spi_data_t *ts = nvt_spi_data;
+
+#if NVT_SPI_WAKEUP_GESTURE
+	if (bTouchIsAwake == 0)
+		pm_wakeup_event(&ts->input_dev->dev, 5000);
+
+#endif
+
+	mutex_lock(&ts->lock);
+
+	if (ts->pen_support)
+		ret = nvt_spi_read(point_data, NVT_SPI_POINT_DATA_LEN + NVT_SPI_PEN_DATA_LEN + 1);
+	else
+		ret = nvt_spi_read(point_data, NVT_SPI_POINT_DATA_LEN + 1);
+
+	if (ret < 0) {
+		NVT_ERR("nvt_spi_read failed.(%d)\n", ret);
+		goto XFER_ERROR;
+	}
+
+	//--- dump SPI buf ---
+/*
+ *	for (i = 0; i < 10; i++) {
+ *		printk("%02X %02X %02X %02X %02X %02X  ",
+ *			point_data[1+i*6], point_data[2+i*6], point_data[3+i*6],
+ *			point_data[4+i*6], point_data[5+i*6], point_data[6+i*6]);
+ *	}
+ *	printk("\n");
+ */
+
+#if NVT_SPI_TOUCH_WDT_RECOVERY
+	/* ESD protect by WDT */
+	if (nvt_spi_wdt_fw_recovery(point_data)) {
+		NVT_ERR("Recover for fw reset, %02X\n", point_data[1]);
+		nvt_spi_update_firmware(NVT_SPI_BOOT_UPDATE_FIRMWARE_NAME);
+		goto XFER_ERROR;
+	}
+#endif /* #if NVT_SPI_TOUCH_WDT_RECOVERY */
+
+	/* ESD protect by FW handshake */
+	if (nvt_spi_fw_recovery(point_data)) {
+#if NVT_SPI_TOUCH_ESD_PROTECT
+		nvt_spi_esd_check_enable(true);
+#endif /* #if NVT_SPI_TOUCH_ESD_PROTECT */
+		goto XFER_ERROR;
+	}
+
+#if NVT_SPI_POINT_DATA_CHECKSUM
+	if (NVT_SPI_POINT_DATA_LEN >= NVT_SPI_POINT_DATA_CHECKSUM_LEN) {
+		ret = nvt_spi_point_data_checksum(point_data, NVT_SPI_POINT_DATA_CHECKSUM_LEN);
+		if (ret)
+			goto XFER_ERROR;
+	}
+#endif /* NVT_SPI_POINT_DATA_CHECKSUM */
+
+#if NVT_SPI_WAKEUP_GESTURE
+	if (bTouchIsAwake == 0) {
+		input_id = (uint8_t)(point_data[1] >> 3);
+		nvt_spi_wakeup_gesture_report(input_id, point_data);
+		mutex_unlock(&ts->lock);
+		return IRQ_HANDLED;
+	}
+#endif
+
+	finger_cnt = 0;
+
+	for (i = 0; i < ts->max_touch_num; i++) {
+		position = 1 + 6 * i;
+		input_id = (uint8_t)(point_data[position + 0] >> 3);
+		if ((input_id == 0) || (input_id > ts->max_touch_num))
+			continue;
+
+		if (((point_data[position] & 0x07) == 0x01)
+				|| ((point_data[position] & 0x07) == 0x02)) {
+			//finger down (enter & moving)
+#if NVT_SPI_TOUCH_ESD_PROTECT
+			/* update interrupt timer */
+			nvt_spi_irq_timer = jiffies;
+#endif /* #if NVT_SPI_TOUCH_ESD_PROTECT */
+			input_x = (uint32_t)(point_data[position + 1] << 4);
+			input_x += (uint32_t) (point_data[position + 3] >> 4);
+
+			input_y = (uint32_t)(point_data[position + 2] << 4);
+			input_y += (uint32_t) (point_data[position + 3] & 0x0F);
+			if ((input_x < 0) || (input_y < 0))
+				continue;
+			if ((input_x > ts->abs_x_max) || (input_y > ts->abs_y_max))
+				continue;
+			input_w = (uint32_t)(point_data[position + 4]);
+			if (input_w == 0)
+				input_w = 1;
+			if (i < 2) {
+				input_p = (uint32_t)(point_data[position + 5]);
+				input_p += (uint32_t)(point_data[i + 63] << 8);
+				if (input_p > NVT_SPI_TOUCH_FORCE_NUM)
+					input_p = NVT_SPI_TOUCH_FORCE_NUM;
+			} else
+				input_p = (uint32_t)(point_data[position + 5]);
+
+			if (input_p == 0)
+				input_p = 1;
+
+#if NVT_SPI_MT_PROTOCOL_B
+			press_id[input_id - 1] = 1;
+			input_mt_slot(ts->input_dev, input_id - 1);
+			input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, true);
+#else /* NVT_SPI_MT_PROTOCOL_B */
+			input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, input_id - 1);
+			input_report_key(ts->input_dev, BTN_TOUCH, 1);
+#endif /* NVT_SPI_MT_PROTOCOL_B */
+
+			input_report_abs(ts->input_dev, ABS_MT_POSITION_X, input_x);
+			input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, input_y);
+			input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, input_w);
+			input_report_abs(ts->input_dev, ABS_MT_PRESSURE, input_p);
+
+#if NVT_SPI_MT_PROTOCOL_B
+#else /* NVT_SPI_MT_PROTOCOL_B */
+			input_mt_sync(ts->input_dev);
+#endif /* NVT_SPI_MT_PROTOCOL_B */
+
+			finger_cnt++;
+		}
+	}
+
+#if NVT_SPI_MT_PROTOCOL_B
+	for (i = 0; i < ts->max_touch_num; i++) {
+		if (press_id[i] != 1) {
+			input_mt_slot(ts->input_dev, i);
+			input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0);
+			input_report_abs(ts->input_dev, ABS_MT_PRESSURE, 0);
+			input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, false);
+		}
+	}
+
+	input_report_key(ts->input_dev, BTN_TOUCH, (finger_cnt > 0));
+#else /* NVT_SPI_MT_PROTOCOL_B */
+	if (finger_cnt == 0) {
+		input_report_key(ts->input_dev, BTN_TOUCH, 0);
+		input_mt_sync(ts->input_dev);
+	}
+#endif /* NVT_SPI_MT_PROTOCOL_B */
+
+#if NVT_SPI_TOUCH_KEY_NUM > 0
+	if (point_data[61] == 0xF8) {
+#if NVT_SPI_TOUCH_ESD_PROTECT
+		/* update interrupt timer */
+		nvt_spi_irq_timer = jiffies;
+#endif /* #if NVT_SPI_TOUCH_ESD_PROTECT */
+		for (i = 0; i < ts->max_button_num; i++)
+			input_report_key(ts->input_dev, nvt_spi_touch_key_array[i],
+					((point_data[62] >> i) & 0x01));
+
+	} else {
+		for (i = 0; i < ts->max_button_num; i++)
+			input_report_key(ts->input_dev, nvt_spi_touch_key_array[i], 0);
+	}
+#endif
+
+	input_sync(ts->input_dev);
+
+	if (ts->pen_support) {
+
+		//--- dump pen buf ---
+/*
+ *		printk("%02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n",
+ *			point_data[66], point_data[67], point_data[68], point_data[69],
+ *			point_data[70], point_data[71], point_data[72], point_data[73],
+ *			point_data[74], point_data[75], point_data[76], point_data[77],
+ *			point_data[78], point_data[79]);
+ */
+#if NVT_SPI_CHECK_PEN_DATA_CHECKSUM
+		if (nvt_spi_pen_data_checksum(&point_data[66], NVT_SPI_PEN_DATA_LEN)) {
+			// pen data packet checksum not match, skip it
+			goto XFER_ERROR;
+		}
+#endif // #if NVT_SPI_CHECK_PEN_DATA_CHECKSUM
+
+		// parse and handle pen report
+		pen_format_id = point_data[66];
+		if (pen_format_id != 0xFF) {
+			if (pen_format_id == 0x01) {
+				// report pen data
+				pen_x = (uint32_t)(point_data[67] << 8);
+				pen_x += (uint32_t)(point_data[68]);
+
+				pen_y = (uint32_t)(point_data[69] << 8);
+				pen_y += (uint32_t)(point_data[70]);
+
+				pen_pressure = (uint32_t)(point_data[71] << 8);
+				pen_pressure +=  (uint32_t)(point_data[72]);
+
+				pen_tilt_x = (int32_t)point_data[73];
+				pen_tilt_y = (int32_t)point_data[74];
+
+				pen_distance = (uint32_t)(point_data[75] << 8);
+				pen_distance += (uint32_t)(point_data[76]);
+
+				pen_btn1 = (uint32_t)(point_data[77] & 0x01);
+				pen_btn2 = (uint32_t)((point_data[77] >> 1) & 0x01);
+				pen_battery = (uint32_t)point_data[78];
+//				printk("x=%d,y=%d,p=%d,tx=%d,ty=%d,d=%d,b1=%d,b2=%d,bat=%d\n",
+//					pen_x, pen_y, pen_pressure, pen_tilt_x, pen_tilt_y,
+//					pen_distance, pen_btn1, pen_btn2, pen_battery);
+
+				input_report_abs(ts->pen_input_dev, ABS_X, pen_x);
+				input_report_abs(ts->pen_input_dev, ABS_Y, pen_y);
+				input_report_abs(ts->pen_input_dev, ABS_PRESSURE, pen_pressure);
+				input_report_key(ts->pen_input_dev, BTN_TOUCH, !!pen_pressure);
+				input_report_abs(ts->pen_input_dev, ABS_TILT_X, pen_tilt_x);
+				input_report_abs(ts->pen_input_dev, ABS_TILT_Y, pen_tilt_y);
+				input_report_abs(ts->pen_input_dev, ABS_DISTANCE, pen_distance);
+				input_report_key(ts->pen_input_dev, BTN_TOOL_PEN,
+						!!pen_distance || !!pen_pressure);
+				input_report_key(ts->pen_input_dev, BTN_STYLUS, pen_btn1);
+				input_report_key(ts->pen_input_dev, BTN_STYLUS2, pen_btn2);
+				// TBD: pen battery event report
+				// NVT_LOG("pen_battery=%d\n", pen_battery);
+			} else if (pen_format_id == 0xF0) {
+				// report Pen ID
+			} else {
+				NVT_ERR("Unknown pen format id!\n");
+				goto XFER_ERROR;
+			}
+		} else { // pen_format_id = 0xFF, i.e. no pen present
+			input_report_abs(ts->pen_input_dev, ABS_X, 0);
+			input_report_abs(ts->pen_input_dev, ABS_Y, 0);
+			input_report_abs(ts->pen_input_dev, ABS_PRESSURE, 0);
+			input_report_abs(ts->pen_input_dev, ABS_TILT_X, 0);
+			input_report_abs(ts->pen_input_dev, ABS_TILT_Y, 0);
+			input_report_abs(ts->pen_input_dev, ABS_DISTANCE, 0);
+			input_report_key(ts->pen_input_dev, BTN_TOUCH, 0);
+			input_report_key(ts->pen_input_dev, BTN_TOOL_PEN, 0);
+			input_report_key(ts->pen_input_dev, BTN_STYLUS, 0);
+			input_report_key(ts->pen_input_dev, BTN_STYLUS2, 0);
+		}
+
+		input_sync(ts->pen_input_dev);
+	} /* if (ts->pen_support) */
+
+XFER_ERROR:
+
+	mutex_unlock(&ts->lock);
+
+	return IRQ_HANDLED;
+}
+
+
+/*
+ *******************************************************
+ * Description:
+ *	Novatek touchscreen check chip version trim function.
+ *
+ * return:
+ *	Executive outcomes. 0---NVT IC. -1---not NVT IC.
+ ******************************************************
+ */
+static int8_t nvt_spi_check_chip_ver_trim(uint32_t chip_ver_trim_addr)
+{
+	uint8_t buf[8] = {0};
+	int32_t retry = 0;
+	int32_t list = 0;
+	int32_t i = 0;
+	int32_t found_nvt_chip = 0;
+	int32_t ret = -1;
+	struct nvt_spi_data_t *ts = nvt_spi_data;
+
+	//---Check for 5 times---
+	for (retry = 5; retry > 0; retry--) {
+
+		nvt_spi_bootloader_reset();
+
+		nvt_spi_set_page(chip_ver_trim_addr);
+
+		buf[0] = chip_ver_trim_addr & 0x7F;
+		buf[1] = 0x00;
+		buf[2] = 0x00;
+		buf[3] = 0x00;
+		buf[4] = 0x00;
+		buf[5] = 0x00;
+		buf[6] = 0x00;
+		nvt_spi_read(buf, 7);
+		NVT_LOG("buf[1]=0x%02X,[2]=0x%02X,[3]=0x%02X,[4]=0x%02X,[5]=0x%02X,[6]=0x%02X\n",
+			buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]);
+
+		// compare read chip id on supported list
+		for (list = 0; list < ARRAY_SIZE(nvt_spi_trim_id_table); list++) {
+			found_nvt_chip = 0;
+
+			// compare each byte
+			for (i = 0; i < NVT_SPI_ID_BYTE_MAX; i++) {
+				if (nvt_spi_trim_id_table[list].mask[i]) {
+					if (buf[i + 1] != nvt_spi_trim_id_table[list].id[i])
+						break;
+				}
+			}
+
+			if (i == NVT_SPI_ID_BYTE_MAX)
+				found_nvt_chip = 1;
+
+			if (found_nvt_chip) {
+				NVT_LOG("This is NVT touch IC\n");
+				ts->mmap = nvt_spi_trim_id_table[list].mmap;
+				ts->hw_crc = nvt_spi_trim_id_table[list].hwinfo->hw_crc;
+				ret = 0;
+				goto out;
+			} else {
+				ts->mmap = NULL;
+				ret = -1;
+			}
+		}
+
+		msleep(20);
+	}
+
+out:
+	return ret;
+}
+
+#if defined(CONFIG_DRM)
+static int nvt_spi_check_dt(struct device_node *np)
+{
+	int i;
+	int count;
+	struct device_node *node;
+	struct drm_panel *panel;
+
+	count = of_count_phandle_with_args(np, "panel", NULL);
+	if (count <= 0)
+		return 0;
+
+	for (i = 0; i < count; i++) {
+		node = of_parse_phandle(np, "panel", i);
+		panel = of_drm_find_panel(node);
+		of_node_put(node);
+		if (!IS_ERR(panel)) {
+			active_spi_panel = panel;
+			return 0;
+		}
+	}
+
+	return PTR_ERR(panel);
+}
+#endif
+
+static int32_t nvt_spi_late_probe(struct spi_device *client)
+{
+	int32_t ret = 0;
+
+	//---check chip version trim---
+	ret = nvt_spi_check_chip_ver_trim(NVT_SPI_CHIP_VER_TRIM_ADDR);
+	if (ret) {
+		NVT_LOG("try to check from old chip ver trim address\n");
+		ret = nvt_spi_check_chip_ver_trim(NVT_SPI_CHIP_VER_TRIM_OLD_ADDR);
+		if (ret) {
+			NVT_ERR("chip is not identified\n");
+			ret = -EINVAL;
+			return ret;
+		}
+	}
+
+	if (nvt_spi_update_firmware(NVT_SPI_BOOT_UPDATE_FIRMWARE_NAME))
+		NVT_ERR("download firmware failed, ignore check fw state\n");
+	else
+		nvt_spi_check_fw_reset_state(NVT_SPI_RESET_STATE_REK);
+
+	//---set device node---
+#if NVT_SPI_TOUCH_PROC
+	ret = nvt_spi_flash_proc_init();
+	if (ret != 0) {
+		NVT_ERR("nvt flash proc init failed. ret=%d\n", ret);
+		goto err_flash_proc_init_failed;
+	}
+#endif
+
+#if NVT_SPI_TOUCH_EXT_PROC
+	ret = nvt_spi_extra_proc_init();
+	if (ret != 0) {
+		NVT_ERR("nvt extra proc init failed. ret=%d\n", ret);
+		goto err_extra_proc_init_failed;
+	}
+#endif
+
+#if NVT_SPI_TOUCH_MP
+	ret = nvt_spi_mp_proc_init();
+	if (ret != 0) {
+		NVT_ERR("nvt mp proc init failed. ret=%d\n", ret);
+		goto err_mp_proc_init_failed;
+	}
+#endif
+
+	bTouchIsAwake = 1;
+	NVT_LOG("end\n");
+
+	nvt_spi_irq_enable(true);
+	return 0;
+
+#if NVT_SPI_TOUCH_MP
+	nvt_spi_mp_proc_deinit();
+err_mp_proc_init_failed:
+#endif
+#if NVT_SPI_TOUCH_EXT_PROC
+	nvt_spi_extra_proc_deinit();
+err_extra_proc_init_failed:
+#endif
+#if NVT_SPI_TOUCH_PROC
+	nvt_spi_flash_proc_deinit();
+err_flash_proc_init_failed:
+#endif
+	return 0;
+}
+
+/*
+ *******************************************************
+ * Description:
+ *	Novatek touchscreen driver probe function.
+ *
+ * return:
+ *	Executive outcomes. 0---succeed. negative---failed
+ ******************************************************
+ */
+static int32_t nvt_spi_probe(struct spi_device *client)
+{
+	int32_t ret = 0;
+#if defined(CONFIG_DRM)
+	struct device_node *dp = NULL;
+#endif
+#if ((NVT_SPI_TOUCH_KEY_NUM > 0) || NVT_SPI_WAKEUP_GESTURE)
+	int32_t retry = 0;
+#endif
+	struct nvt_spi_data_t *ts;
+
+#if defined(CONFIG_DRM)
+	dp = client->dev.of_node;
+
+	ret = nvt_spi_check_dt(dp);
+	if (ret == -EPROBE_DEFER)
+		return ret;
+
+	if (ret) {
+		ret = -ENODEV;
+		return ret;
+	}
+#endif
+
+	NVT_LOG("start\n");
+
+	ts = kzalloc(sizeof(struct nvt_spi_data_t), GFP_KERNEL);
+	if (ts == NULL) {
+		NVT_ERR("failed to allocated memory for nvt ts data\n");
+		return -ENOMEM;
+	}
+	nvt_spi_data = ts;
+
+	ts->xbuf = kzalloc(NVT_SPI_XBUF_LEN, GFP_KERNEL);
+	if (ts->xbuf == NULL) {
+		NVT_ERR("kzalloc for xbuf failed!\n");
+		ret = -ENOMEM;
+		goto err_malloc_xbuf;
+	}
+
+	ts->rbuf = kzalloc(NVT_SPI_READ_LEN, GFP_KERNEL);
+	if (ts->rbuf == NULL) {
+		NVT_ERR("kzalloc for rbuf failed!\n");
+		ret = -ENOMEM;
+		goto err_malloc_rbuf;
+	}
+
+	ts->client = client;
+	spi_set_drvdata(client, ts);
+
+	//---prepare for spi parameter---
+	if (ts->client->master->flags & SPI_MASTER_HALF_DUPLEX) {
+		NVT_ERR("Full duplex not supported by master\n");
+		ret = -EIO;
+		goto err_ckeck_full_duplex;
+	}
+	ts->client->bits_per_word = 8;
+	ts->client->mode = SPI_MODE_0;
+
+	ret = spi_setup(ts->client);
+	if (ret < 0) {
+		NVT_ERR("Failed to perform SPI setup\n");
+		goto err_spi_setup;
+	}
+
+	NVT_LOG("mode=%d, max_speed_hz=%d\n", ts->client->mode, ts->client->max_speed_hz);
+
+	//---parse dts---
+	ret = nvt_spi_parse_dt(&client->dev);
+	if (ret) {
+		NVT_ERR("parse dt error\n");
+		goto err_spi_setup;
+	}
+
+	//---request and config GPIOs---
+	ret = nvt_spi_gpio_config(ts);
+	if (ret) {
+		NVT_ERR("gpio config error!\n");
+		goto err_gpio_config_failed;
+	}
+
+	mutex_init(&ts->lock);
+	mutex_init(&ts->xbuf_lock);
+
+	//---eng reset before TP_RESX high
+	nvt_spi_eng_reset();
+
+#if NVT_SPI_TOUCH_SUPPORT_HW_RST
+	gpio_set_value(ts->reset_gpio, 1);
+#endif
+
+	// need 10ms delay after POR(power on reset)
+	msleep(20);
+
+	ts->abs_x_max = NVT_SPI_TOUCH_DEFAULT_MAX_WIDTH;
+	ts->abs_y_max = NVT_SPI_TOUCH_DEFAULT_MAX_HEIGHT;
+
+	//---allocate input device---
+	ts->input_dev = input_allocate_device();
+	if (ts->input_dev == NULL) {
+		NVT_ERR("allocate input device failed\n");
+		ret = -ENOMEM;
+		goto err_input_dev_alloc_failed;
+	}
+
+	ts->max_touch_num = NVT_SPI_TOUCH_MAX_FINGER_NUM;
+
+#if NVT_SPI_TOUCH_KEY_NUM > 0
+	ts->max_button_num = NVT_SPI_TOUCH_KEY_NUM;
+#endif
+
+	ts->int_trigger_type = NVT_SPI_INT_TRIGGER_TYPE;
+
+
+	//---set input device info.---
+	ts->input_dev->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+	ts->input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+	ts->input_dev->propbit[0] = BIT(INPUT_PROP_DIRECT);
+
+#if NVT_SPI_MT_PROTOCOL_B
+	input_mt_init_slots(ts->input_dev, ts->max_touch_num, 0);
+#endif
+
+	// pressure = NVT_SPI_TOUCH_FORCE_NUM
+	input_set_abs_params(ts->input_dev, ABS_MT_PRESSURE, 0, NVT_SPI_TOUCH_FORCE_NUM, 0, 0);
+
+#if NVT_SPI_TOUCH_MAX_FINGER_NUM > 1
+	// area = 255
+	input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
+
+	input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X, 0, ts->abs_x_max, 0, 0);
+	input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y, 0, ts->abs_y_max, 0, 0);
+#if NVT_SPI_MT_PROTOCOL_B
+	// no need to set ABS_MT_TRACKING_ID, input_mt_init_slots() already set it
+#else
+	input_set_abs_params(ts->input_dev, ABS_MT_TRACKING_ID, 0, ts->max_touch_num, 0, 0);
+#endif //NVT_SPI_MT_PROTOCOL_B
+#endif //NVT_SPI_TOUCH_MAX_FINGER_NUM > 1
+
+#if NVT_SPI_TOUCH_KEY_NUM > 0
+	for (retry = 0; retry < ts->max_button_num; retry++)
+		input_set_capability(ts->input_dev, EV_KEY, nvt_spi_touch_key_array[retry]);
+#endif
+
+#if NVT_SPI_WAKEUP_GESTURE
+	for (retry = 0; retry < ARRAY_SIZE(nvt_spi_gesture_key_array); retry++)
+		input_set_capability(ts->input_dev, EV_KEY, nvt_spi_gesture_key_array[retry]);
+#endif
+
+	snprintf(ts->phys, sizeof(ts->phys), "input/ts");
+	ts->input_dev->name = NVT_SPI_TS_NAME;
+	ts->input_dev->phys = ts->phys;
+	ts->input_dev->id.bustype = BUS_SPI;
+
+	//---register input device---
+	ret = input_register_device(ts->input_dev);
+	if (ret) {
+		NVT_ERR("register input device (%s) failed. ret=%d\n", ts->input_dev->name, ret);
+		goto err_input_register_device_failed;
+	}
+
+	if (ts->pen_support) {
+		//---allocate pen input device---
+		ts->pen_input_dev = input_allocate_device();
+		if (ts->pen_input_dev == NULL) {
+			NVT_ERR("allocate pen input device failed\n");
+			ret = -ENOMEM;
+			goto err_pen_input_dev_alloc_failed;
+		}
+
+		//---set pen input device info.---
+		ts->pen_input_dev->evbit[0] =
+				BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+		ts->pen_input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+		ts->pen_input_dev->keybit[BIT_WORD(BTN_TOOL_PEN)] |= BIT_MASK(BTN_TOOL_PEN);
+		//ts->pen_input_dev->keybit[BIT_WORD(BTN_TOOL_RUBBER)]
+		//		|= BIT_MASK(BTN_TOOL_RUBBER);
+		ts->pen_input_dev->keybit[BIT_WORD(BTN_STYLUS)] |= BIT_MASK(BTN_STYLUS);
+		ts->pen_input_dev->keybit[BIT_WORD(BTN_STYLUS2)] |= BIT_MASK(BTN_STYLUS2);
+		ts->pen_input_dev->propbit[0] = BIT(INPUT_PROP_DIRECT);
+
+		if (ts->wgp_stylus) {
+			input_set_abs_params(ts->pen_input_dev, ABS_X, 0,
+					ts->abs_x_max * 2, 0, 0);
+			input_set_abs_params(ts->pen_input_dev, ABS_Y, 0,
+					ts->abs_y_max * 2, 0, 0);
+		} else {
+			input_set_abs_params(ts->pen_input_dev, ABS_X, 0,
+					ts->abs_x_max, 0, 0);
+
+			input_set_abs_params(ts->pen_input_dev, ABS_Y, 0,
+					ts->abs_y_max, 0, 0);
+		}
+		input_set_abs_params(ts->pen_input_dev, ABS_PRESSURE, 0,
+				NVT_SPI_PEN_PRESSURE_MAX, 0, 0);
+		input_set_abs_params(ts->pen_input_dev, ABS_DISTANCE, 0,
+				NVT_SPI_PEN_DISTANCE_MAX, 0, 0);
+		input_set_abs_params(ts->pen_input_dev, ABS_TILT_X,
+				NVT_SPI_PEN_TILT_MIN, NVT_SPI_PEN_TILT_MAX, 0, 0);
+		input_set_abs_params(ts->pen_input_dev, ABS_TILT_Y,
+				NVT_SPI_PEN_TILT_MIN, NVT_SPI_PEN_TILT_MAX, 0, 0);
+
+		snprintf(ts->pen_phys, sizeof(ts->pen_phys), "input/pen");
+		ts->pen_input_dev->name = NVT_SPI_PEN_NAME;
+		ts->pen_input_dev->phys = ts->pen_phys;
+		ts->pen_input_dev->id.bustype = BUS_SPI;
+
+		//---register pen input device---
+		ret = input_register_device(ts->pen_input_dev);
+		if (ret) {
+			NVT_ERR("register pen input device (%s) failed. ret=%d\n",
+					ts->pen_input_dev->name, ret);
+			goto err_pen_input_register_device_failed;
+		}
+	} /* if (ts->pen_support) */
+
+	//---set int-pin & request irq---
+	client->irq = gpio_to_irq(ts->irq_gpio);
+	if (client->irq) {
+		NVT_LOG("int_trigger_type=%d\n", ts->int_trigger_type);
+		ts->irq_enabled = true;
+		ret = request_threaded_irq(client->irq, NULL, nvt_spi_work_func,
+				ts->int_trigger_type | IRQF_ONESHOT, NVT_SPI_NAME, ts);
+		if (ret != 0) {
+			NVT_ERR("request irq failed. ret=%d\n", ret);
+			goto err_int_request_failed;
+		} else {
+			nvt_spi_irq_enable(false);
+			NVT_LOG("request irq %d succeed\n", client->irq);
+		}
+	}
+
+#if NVT_SPI_WAKEUP_GESTURE
+	device_init_wakeup(&ts->input_dev->dev, 1);
+#endif
+
+#if NVT_SPI_BOOT_UPDATE_FIRMWARE
+	nvt_spi_fwu_wq = alloc_workqueue("nvt_spi_fwu_wq", WQ_UNBOUND | WQ_MEM_RECLAIM, 1);
+	if (!nvt_spi_fwu_wq) {
+		NVT_ERR("nvt_spi_fwu_wq create workqueue failed\n");
+		ret = -ENOMEM;
+		goto err_create_nvt_spi_fwu_wq_failed;
+	}
+	INIT_DELAYED_WORK(&ts->nvt_fwu_work, nvt_spi_update_firmware_work);
+
+	// please make sure boot update start after display reset(RESX) sequence
+	//queue_delayed_work(nvt_spi_fwu_wq, &ts->nvt_fwu_work, msecs_to_jiffies(14000));
+#endif
+
+	NVT_LOG("NVT_SPI_TOUCH_ESD_PROTECT is %d\n", NVT_SPI_TOUCH_ESD_PROTECT);
+#if NVT_SPI_TOUCH_ESD_PROTECT
+	INIT_DELAYED_WORK(&nvt_spi_esd_check_work, nvt_spi_esd_check_func);
+	nvt_spi_esd_check_wq = alloc_workqueue("nvt_spi_esd_check_wq", WQ_MEM_RECLAIM, 1);
+	if (!nvt_spi_esd_check_wq) {
+		NVT_ERR("nvt_spi_esd_check_wq create workqueue failed\n");
+		ret = -ENOMEM;
+		goto err_create_nvt_spi_esd_check_wq_failed;
+	}
+	queue_delayed_work(nvt_spi_esd_check_wq, &nvt_spi_esd_check_work,
+			msecs_to_jiffies(NVT_SPI_TOUCH_ESD_CHECK_PERIOD));
+#endif /* #if NVT_SPI_TOUCH_ESD_PROTECT */
+
+
+#if defined(CONFIG_DRM)
+	//if (!strcmp(ts->touch_environment, "pvm"))
+	nvt_spi_register_for_panel_events(client->dev.of_node, ts);
+#elif defined(_MSM_DRM_NOTIFY_H_)
+	ts->drm_notif.notifier_call = nvt_drm_notifier_callback;
+	ret = msm_drm_register_client(&ts->drm_notif);
+	if (ret) {
+		NVT_ERR("register drm_notifier failed. ret=%d\n", ret);
+		goto err_register_drm_notif_failed;
+	}
+#else
+	ts->fb_notif.notifier_call = nvt_fb_notifier_callback;
+	ret = fb_register_client(&ts->fb_notif);
+	if (ret) {
+		NVT_ERR("register fb_notifier failed. ret=%d\n", ret);
+		goto err_register_fb_notif_failed;
+	}
+#endif
+
+	NVT_LOG("end\n");
+	return 0;
+
+#if defined(CONFIG_DRM)
+
+#elif defined(_MSM_DRM_NOTIFY_H_)
+err_register_drm_notif_failed:
+#else
+err_register_fb_notif_failed:
+#endif
+
+#if NVT_SPI_TOUCH_MP
+	nvt_spi_mp_proc_deinit();
+#endif
+#if NVT_SPI_TOUCH_EXT_PROC
+	nvt_spi_extra_proc_deinit();
+
+#endif
+#if NVT_SPI_TOUCH_PROC
+	nvt_spi_flash_proc_deinit();
+
+#endif
+#if NVT_SPI_TOUCH_ESD_PROTECT
+	if (nvt_spi_esd_check_wq) {
+		cancel_delayed_work_sync(&nvt_spi_esd_check_work);
+		destroy_workqueue(nvt_spi_esd_check_wq);
+		nvt_spi_esd_check_wq = NULL;
+	}
+err_create_nvt_spi_esd_check_wq_failed:
+#endif
+#if NVT_SPI_BOOT_UPDATE_FIRMWARE
+	if (nvt_spi_fwu_wq) {
+		cancel_delayed_work_sync(&ts->nvt_fwu_work);
+		destroy_workqueue(nvt_spi_fwu_wq);
+		nvt_spi_fwu_wq = NULL;
+	}
+err_create_nvt_spi_fwu_wq_failed:
+#endif
+#if NVT_SPI_WAKEUP_GESTURE
+	device_init_wakeup(&ts->input_dev->dev, 0);
+#endif
+	free_irq(client->irq, ts);
+err_int_request_failed:
+	if (ts->pen_support) {
+		input_unregister_device(ts->pen_input_dev);
+		ts->pen_input_dev = NULL;
+	}
+err_pen_input_register_device_failed:
+	if (ts->pen_support) {
+		if (ts->pen_input_dev) {
+			input_free_device(ts->pen_input_dev);
+			ts->pen_input_dev = NULL;
+		}
+	}
+err_pen_input_dev_alloc_failed:
+	input_unregister_device(ts->input_dev);
+	ts->input_dev = NULL;
+err_input_register_device_failed:
+	if (ts->input_dev) {
+		input_free_device(ts->input_dev);
+		ts->input_dev = NULL;
+	}
+err_input_dev_alloc_failed:
+	mutex_destroy(&ts->xbuf_lock);
+	mutex_destroy(&ts->lock);
+	nvt_spi_gpio_deconfig(ts);
+err_gpio_config_failed:
+err_spi_setup:
+err_ckeck_full_duplex:
+	spi_set_drvdata(client, NULL);
+	kfree(ts->rbuf);
+	ts->rbuf = NULL;
+err_malloc_rbuf:
+	kfree(ts->xbuf);
+	ts->xbuf = NULL;
+err_malloc_xbuf:
+	kfree(ts);
+	ts = NULL;
+
+	return ret;
+}
+
+/*
+ *******************************************************
+ * Description:
+ *	Novatek touchscreen driver release function.
+ *
+ * return:
+ *	Executive outcomes. 0---succeed.
+ ******************************************************
+ */
+static int32_t nvt_spi_remove(struct spi_device *client)
+{
+	struct nvt_spi_data_t *ts = nvt_spi_data;
+
+	NVT_LOG("Removing driver...\n");
+
+#if defined(CONFIG_DRM)
+	if (active_spi_panel && ts->notifier_cookie)
+		panel_event_notifier_unregister(ts->notifier_cookie);
+#elif defined(_MSM_DRM_NOTIFY_H_)
+	if (msm_drm_unregister_client(&ts->drm_notif))
+		NVT_ERR("Error occurred while unregistering drm_notifier.\n");
+#else
+	if (fb_unregister_client(&ts->fb_notif))
+		NVT_ERR("Error occurred while unregistering fb_notifier.\n");
+#endif
+
+#if NVT_SPI_TOUCH_MP
+	nvt_spi_mp_proc_deinit();
+#endif
+#if NVT_SPI_TOUCH_EXT_PROC
+	nvt_spi_extra_proc_deinit();
+#endif
+#if NVT_SPI_TOUCH_PROC
+	nvt_spi_flash_proc_deinit();
+#endif
+
+#if NVT_SPI_TOUCH_ESD_PROTECT
+	if (nvt_spi_esd_check_wq) {
+		cancel_delayed_work_sync(&nvt_spi_esd_check_work);
+		nvt_spi_esd_check_enable(false);
+		destroy_workqueue(nvt_spi_esd_check_wq);
+		nvt_spi_esd_check_wq = NULL;
+	}
+#endif
+
+#if NVT_SPI_BOOT_UPDATE_FIRMWARE
+	if (nvt_spi_fwu_wq) {
+		cancel_delayed_work_sync(&ts->nvt_fwu_work);
+		destroy_workqueue(nvt_spi_fwu_wq);
+		nvt_spi_fwu_wq = NULL;
+	}
+#endif
+
+#if NVT_SPI_WAKEUP_GESTURE
+	device_init_wakeup(&ts->input_dev->dev, 0);
+#endif
+
+	nvt_spi_irq_enable(false);
+	free_irq(client->irq, ts);
+
+	mutex_destroy(&ts->xbuf_lock);
+	mutex_destroy(&ts->lock);
+
+	nvt_spi_gpio_deconfig(ts);
+
+	if (ts->pen_support) {
+		if (ts->pen_input_dev) {
+			input_unregister_device(ts->pen_input_dev);
+			ts->pen_input_dev = NULL;
+		}
+	}
+
+	if (ts->input_dev) {
+		input_unregister_device(ts->input_dev);
+		ts->input_dev = NULL;
+	}
+
+	spi_set_drvdata(client, NULL);
+
+	kfree(ts->xbuf);
+	ts->xbuf = NULL;
+
+	kfree(ts);
+	ts = NULL;
+
+	return 0;
+}
+
+static void nvt_spi_shutdown(struct spi_device *client)
+{
+	struct nvt_spi_data_t *ts = nvt_spi_data;
+
+	NVT_LOG("Shutdown driver...\n");
+
+	nvt_spi_irq_enable(false);
+
+#if defined(CONFIG_DRM)
+	if (active_spi_panel && ts->notifier_cookie)
+		panel_event_notifier_unregister(ts->notifier_cookie);
+#elif defined(_MSM_DRM_NOTIFY_H_)
+	if (msm_drm_unregister_client(&ts->drm_notif))
+		NVT_ERR("Error occurred while unregistering drm_notifier.\n");
+#else
+	if (fb_unregister_client(&ts->fb_notif))
+		NVT_ERR("Error occurred while unregistering fb_notifier.\n");
+#endif
+
+#if NVT_SPI_TOUCH_MP
+	nvt_spi_mp_proc_deinit();
+#endif
+#if NVT_SPI_TOUCH_EXT_PROC
+	nvt_spi_extra_proc_deinit();
+#endif
+#if NVT_SPI_TOUCH_PROC
+	nvt_spi_flash_proc_deinit();
+#endif
+
+#if NVT_SPI_TOUCH_ESD_PROTECT
+	if (nvt_spi_esd_check_wq) {
+		cancel_delayed_work_sync(&nvt_spi_esd_check_work);
+		nvt_spi_esd_check_enable(false);
+		destroy_workqueue(nvt_spi_esd_check_wq);
+		nvt_spi_esd_check_wq = NULL;
+	}
+#endif /* #if NVT_SPI_TOUCH_ESD_PROTECT */
+
+#if NVT_SPI_BOOT_UPDATE_FIRMWARE
+	if (nvt_spi_fwu_wq) {
+		cancel_delayed_work_sync(&ts->nvt_fwu_work);
+		destroy_workqueue(nvt_spi_fwu_wq);
+		nvt_spi_fwu_wq = NULL;
+	}
+#endif
+
+#if NVT_SPI_WAKEUP_GESTURE
+	device_init_wakeup(&ts->input_dev->dev, 0);
+#endif
+}
+
+/*
+ ******************************************************
+ * Description:
+ *	Novatek touchscreen driver suspend function.
+ *
+ * return:
+ *	Executive outcomes. 0---succeed.
+ ******************************************************
+ */
+static int32_t nvt_spi_suspend(struct device *dev)
+{
+	uint8_t buf[4] = {0};
+#if NVT_SPI_MT_PROTOCOL_B
+	uint32_t i = 0;
+#endif
+	struct nvt_spi_data_t *ts = nvt_spi_data;
+
+	if (!bTouchIsAwake) {
+		NVT_LOG("Touch is already suspend\n");
+		return 0;
+	}
+
+#if !NVT_SPI_WAKEUP_GESTURE
+	nvt_spi_irq_enable(false);
+#endif
+
+#if NVT_SPI_TOUCH_ESD_PROTECT
+	NVT_LOG("cancel delayed work sync\n");
+	cancel_delayed_work_sync(&nvt_spi_esd_check_work);
+	nvt_spi_esd_check_enable(false);
+#endif /* #if NVT_SPI_TOUCH_ESD_PROTECT */
+
+	mutex_lock(&ts->lock);
+
+	NVT_LOG("start\n");
+
+	bTouchIsAwake = 0;
+
+#if NVT_SPI_WAKEUP_GESTURE
+	//---write command to enter "wakeup gesture mode"---
+	buf[0] = NVT_SPI_EVENT_MAP_HOST_CMD;
+	buf[1] = 0x13;
+	nvt_spi_write(buf, 2);
+
+	enable_irq_wake(ts->client->irq);
+
+	NVT_LOG("Enabled touch wakeup gesture\n");
+
+#else // NVT_SPI_WAKEUP_GESTURE
+	//---write command to enter "deep sleep mode"---
+	buf[0] = NVT_SPI_EVENT_MAP_HOST_CMD;
+	buf[1] = 0x11;
+	nvt_spi_write(buf, 2);
+#endif // NVT_SPI_WAKEUP_GESTURE
+
+	mutex_unlock(&ts->lock);
+
+	/* release all touches */
+#if NVT_SPI_MT_PROTOCOL_B
+	for (i = 0; i < ts->max_touch_num; i++) {
+		input_mt_slot(ts->input_dev, i);
+		input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0);
+		input_report_abs(ts->input_dev, ABS_MT_PRESSURE, 0);
+		input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, 0);
+	}
+#endif
+	input_report_key(ts->input_dev, BTN_TOUCH, 0);
+#if !NVT_SPI_MT_PROTOCOL_B
+	input_mt_sync(ts->input_dev);
+#endif
+	input_sync(ts->input_dev);
+
+	/* release pen event */
+	if (ts->pen_support) {
+		input_report_abs(ts->pen_input_dev, ABS_X, 0);
+		input_report_abs(ts->pen_input_dev, ABS_Y, 0);
+		input_report_abs(ts->pen_input_dev, ABS_PRESSURE, 0);
+		input_report_abs(ts->pen_input_dev, ABS_TILT_X, 0);
+		input_report_abs(ts->pen_input_dev, ABS_TILT_Y, 0);
+		input_report_abs(ts->pen_input_dev, ABS_DISTANCE, 0);
+		input_report_key(ts->pen_input_dev, BTN_TOUCH, 0);
+		input_report_key(ts->pen_input_dev, BTN_TOOL_PEN, 0);
+		input_report_key(ts->pen_input_dev, BTN_STYLUS, 0);
+		input_report_key(ts->pen_input_dev, BTN_STYLUS2, 0);
+		input_sync(ts->pen_input_dev);
+	}
+
+	msleep(50);
+
+	NVT_LOG("end\n");
+
+	return 0;
+}
+
+/*
+ ******************************************************
+ * Description:
+ *	Novatek touchscreen driver resume function.
+ *
+ * return:
+ *	Executive outcomes. 0---succeed.
+ ******************************************************
+ */
+static int32_t nvt_spi_resume(struct device *dev)
+{
+	struct nvt_spi_data_t *ts = nvt_spi_data;
+
+	if (bTouchIsAwake || ts->fw_ver == 0) {
+		nvt_spi_late_probe(ts->client);
+		NVT_LOG("nvt_spi_late_probe\n");
+		return 0;
+	}
+
+	mutex_lock(&ts->lock);
+
+	NVT_LOG("start\n");
+
+	// please make sure display reset(RESX) sequence and mipi dsi cmds sent before this
+#if NVT_SPI_TOUCH_SUPPORT_HW_RST
+	gpio_set_value(ts->reset_gpio, 1);
+#endif
+
+	if (nvt_spi_update_firmware(NVT_SPI_BOOT_UPDATE_FIRMWARE_NAME))
+		NVT_ERR("download firmware failed, ignore check fw state\n");
+	else
+		nvt_spi_check_fw_reset_state(NVT_SPI_RESET_STATE_REK);
+
+#if !NVT_SPI_WAKEUP_GESTURE
+	nvt_spi_irq_enable(true);
+#endif
+
+#if NVT_SPI_TOUCH_ESD_PROTECT
+	nvt_spi_esd_check_enable(false);
+	queue_delayed_work(nvt_spi_esd_check_wq, &nvt_spi_esd_check_work,
+			msecs_to_jiffies(NVT_SPI_TOUCH_ESD_CHECK_PERIOD));
+#endif /* #if NVT_SPI_TOUCH_ESD_PROTECT */
+
+	bTouchIsAwake = 1;
+
+	mutex_unlock(&ts->lock);
+
+	NVT_LOG("end\n");
+
+	return 0;
+}
+
+#if defined(CONFIG_DRM)
+
+static void nvt_spi_panel_notifier_callback(enum panel_event_notifier_tag tag,
+		 struct panel_event_notification *notification, void *client_data)
+{
+	struct nvt_spi_data_t *ts = client_data;
+
+	if (!notification) {
+		pr_err("Invalid notification\n");
+		return;
+	}
+
+	NVT_LOG("Notification type:%d, early_trigger:%d",
+			notification->notif_type,
+			notification->notif_data.early_trigger);
+
+	switch (notification->notif_type) {
+	case DRM_PANEL_EVENT_UNBLANK:
+		if (notification->notif_data.early_trigger)
+			NVT_LOG("resume notification pre commit\n");
+		else
+			nvt_spi_resume(&ts->client->dev);
+		break;
+
+	case DRM_PANEL_EVENT_BLANK:
+		if (notification->notif_data.early_trigger)
+			nvt_spi_suspend(&ts->client->dev);
+		else
+			NVT_LOG("suspend notification post commit\n");
+		break;
+
+	case DRM_PANEL_EVENT_BLANK_LP:
+		NVT_LOG("received lp event\n");
+		break;
+
+	case DRM_PANEL_EVENT_FPS_CHANGE:
+		NVT_LOG("shashank:Received fps change old fps:%d new fps:%d\n",
+				notification->notif_data.old_fps,
+				notification->notif_data.new_fps);
+		break;
+	default:
+		NVT_LOG("notification serviced :%d\n",
+				notification->notif_type);
+		break;
+	}
+}
+
+#elif defined(_MSM_DRM_NOTIFY_H_)
+static int nvt_drm_notifier_callback(struct notifier_block *self, unsigned long event, void *data)
+{
+	struct msm_drm_notifier *evdata = data;
+	int *blank;
+	struct nvt_spi_data_t *ts =
+		container_of(self, struct nvt_spi_data_t, drm_notif);
+
+	if (!evdata || (evdata->id != 0))
+		return 0;
+
+	if (evdata->data && ts) {
+		blank = evdata->data;
+		if (event == MSM_DRM_EARLY_EVENT_BLANK) {
+			if (*blank == MSM_DRM_BLANK_POWERDOWN) {
+				NVT_LOG("event=%lu, *blank=%d\n", event, *blank);
+				nvt_spi_suspend(&ts->client->dev);
+			}
+		} else if (event == MSM_DRM_EVENT_BLANK) {
+			if (*blank == MSM_DRM_BLANK_UNBLANK) {
+				NVT_LOG("event=%lu, *blank=%d\n", event, *blank);
+				nvt_ts_resume(&ts->client->dev);
+			}
+		}
+	}
+
+	return 0;
+}
+#else
+static int nvt_fb_notifier_callback(struct notifier_block *self, unsigned long event, void *data)
+{
+	struct fb_event *evdata = data;
+	int *blank;
+	struct nvt_spi_data_t *ts =
+		container_of(self, struct nvt_spi_data_t, fb_notif);
+
+	if (evdata && evdata->data && event == FB_EARLY_EVENT_BLANK) {
+		blank = evdata->data;
+		if (*blank == FB_BLANK_POWERDOWN) {
+			NVT_LOG("event=%lu, *blank=%d\n", event, *blank);
+			nvt_spi_suspend(&ts->client->dev);
+		}
+	} else if (evdata && evdata->data && event == FB_EVENT_BLANK) {
+		blank = evdata->data;
+		if (*blank == FB_BLANK_UNBLANK) {
+			NVT_LOG("event=%lu, *blank=%d\n", event, *blank);
+			nvt_ts_resume(&ts->client->dev);
+		}
+	}
+
+	return 0;
+}
+#endif
+
+static const struct spi_device_id nvt_spi_id[] = {
+	{ NVT_SPI_NAME, 0 },
+	{ }
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id nvt_spi_match_table[] = {
+	{ .compatible = "novatek,NVT-ts",},
+	{ },
+};
+#endif
+
+static struct spi_driver nvt_spi_driver = {
+	.probe		= nvt_spi_probe,
+	.remove		= nvt_spi_remove,
+	.shutdown	= nvt_spi_shutdown,
+	.id_table	= nvt_spi_id,
+	.driver = {
+		.name	= NVT_SPI_NAME,
+		.owner	= THIS_MODULE,
+#ifdef CONFIG_OF
+		.of_match_table = nvt_spi_match_table,
+#endif
+	},
+};
+
+/*
+ ******************************************************
+ * Description:
+ *	Driver Install function.
+ *
+ * return:
+ *	Executive Outcomes. 0---succeed. not 0---failed.
+ *******************************************************
+ */
+static int32_t __init nvt_spi_driver_init(void)
+{
+	int32_t ret = 0;
+
+	NVT_LOG("start\n");
+
+	//---add spi driver---
+	ret = spi_register_driver(&nvt_spi_driver);
+	if (ret) {
+		NVT_ERR("failed to add spi driver");
+		goto err_driver;
+	}
+
+	NVT_LOG("finished\n");
+
+err_driver:
+	return ret;
+}
+
+/*
+ ******************************************************
+ * Description:
+ *	Driver uninstall function.
+ *
+ * return:
+ *	n.a.
+ *******************************************************
+ */
+static void __exit nvt_spi_driver_exit(void)
+{
+	spi_unregister_driver(&nvt_spi_driver);
+}
+
+module_init(nvt_spi_driver_init);
+module_exit(nvt_spi_driver_exit);
+
+MODULE_DESCRIPTION("Novatek Touchscreen Driver");
+MODULE_LICENSE("GPL v2");
+
+#endif

+ 386 - 0
qcom/opensource/touch-drivers/nt36xxx/nt36xxx.h

@@ -0,0 +1,386 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2010 - 2018 Novatek, Inc.
+ *
+ * $Revision: 47247 $
+ * $Date: 2019-07-10 10:41:36 +0800 (Wed, 10 Jul 2019) $
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+#ifndef _LINUX_NVT_TOUCH_H
+#define _LINUX_NVT_TOUCH_H
+
+#if !defined(NVT_NT36XXX_SPI) /* NT36XXX I2C */
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/uaccess.h>
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#include <linux/earlysuspend.h>
+#endif
+
+#include "nt36xxx_mem_map.h"
+
+#define NVT_DEBUG 1
+
+//---GPIO number---
+#define NVTTOUCH_RST_PIN 980
+#define NVTTOUCH_INT_PIN 943
+
+
+//---INT trigger mode---
+//#define IRQ_TYPE_EDGE_RISING 1
+//#define IRQ_TYPE_EDGE_FALLING 2
+#define INT_TRIGGER_TYPE IRQ_TYPE_EDGE_RISING
+
+
+//---I2C driver info.---
+#define NVT_I2C_NAME "NVT-ts"
+#define I2C_BLDR_Address 0x01
+#define I2C_FW_Address 0x01
+#define I2C_HW_Address 0x62
+
+#if NVT_DEBUG
+#define NVT_LOG(fmt, args...)    pr_err("[%s] %s %d: " fmt, NVT_I2C_NAME, __func__, __LINE__, ##args)
+#else
+#define NVT_LOG(fmt, args...)    pr_info("[%s] %s %d: " fmt, NVT_I2C_NAME, __func__, __LINE__, ##args)
+#endif
+#define NVT_ERR(fmt, args...)    pr_err("[%s] %s %d: " fmt, NVT_I2C_NAME, __func__, __LINE__, ##args)
+
+//---Input device info.---
+#define NVT_TS_NAME "NVTCapacitiveTouchScreen"
+
+
+//---Touch info.---
+#define TOUCH_DEFAULT_MAX_WIDTH 1080
+#define TOUCH_DEFAULT_MAX_HEIGHT 2408
+#define TOUCH_MAX_FINGER_NUM 10
+#define TOUCH_KEY_NUM 0
+#if TOUCH_KEY_NUM > 0
+extern const uint16_t touch_key_array[TOUCH_KEY_NUM];
+#endif
+#define TOUCH_FORCE_NUM 1000
+
+/* Enable only when module have tp reset pin and connected to host */
+#define NVT_TOUCH_SUPPORT_HW_RST 1
+
+//---Customerized func.---
+#define NVT_TOUCH_PROC 1
+#define NVT_TOUCH_EXT_PROC 1
+#define NVT_TOUCH_MP 1
+#define MT_PROTOCOL_B 1
+#define WAKEUP_GESTURE 0
+#if WAKEUP_GESTURE
+extern const uint16_t gesture_key_array[];
+#endif
+#define BOOT_UPDATE_FIRMWARE 1
+#define BOOT_UPDATE_FIRMWARE_NAME "novatek_ts_fw.bin"
+
+//---ESD Protect.---
+#define NVT_TOUCH_ESD_PROTECT 0
+#define NVT_TOUCH_ESD_CHECK_PERIOD 1500	/* ms */
+
+struct nvt_ts_data {
+	struct i2c_client *client;
+	struct input_dev *input_dev;
+	struct delayed_work nvt_fwu_work;
+	uint16_t addr;
+	int8_t phys[32];
+	const struct i2c_device_id *id;
+#if defined(CONFIG_DRM)
+	struct notifier_block drm_panel_notif;
+#elif defined(_MSM_DRM_NOTIFY_H_)
+	struct notifier_block drm_notif;
+#else
+	struct notifier_block fb_notif;
+
+#endif
+	uint8_t fw_ver;
+	uint8_t x_num;
+	uint8_t y_num;
+	uint16_t abs_x_max;
+	uint16_t abs_y_max;
+	uint8_t max_touch_num;
+	uint8_t max_button_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;
+	uint8_t carrier_system;
+	uint16_t nvt_pid;
+	uint8_t xbuf[1025];
+	struct mutex xbuf_lock;
+	bool irq_enabled;
+	void *notifier_cookie;
+};
+
+#if NVT_TOUCH_PROC
+struct nvt_flash_data{
+	rwlock_t lock;
+	struct i2c_client *client;
+};
+#endif
+
+typedef enum {
+	RESET_STATE_INIT = 0xA0,// IC reset
+	RESET_STATE_REK,        // ReK baseline
+	RESET_STATE_REK_FINISH, // baseline is ready
+	RESET_STATE_NORMAL_RUN, // normal run
+	RESET_STATE_MAX  = 0xAF
+} RST_COMPLETE_STATE;
+
+typedef enum {
+    EVENT_MAP_HOST_CMD                      = 0x50,
+    EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE   = 0x51,
+    EVENT_MAP_RESET_COMPLETE                = 0x60,
+    EVENT_MAP_FWINFO                        = 0x78,
+    EVENT_MAP_PROJECTID                     = 0x9A,
+} I2C_EVENT_MAP;
+
+//---extern structures---
+extern struct nvt_ts_data *ts;
+
+//---extern functions---
+extern int32_t CTP_I2C_READ(struct i2c_client *client, uint16_t address, uint8_t *buf, uint16_t len);
+extern int32_t CTP_I2C_WRITE(struct i2c_client *client, uint16_t address, uint8_t *buf, uint16_t len);
+extern void nvt_bootloader_reset(void);
+extern void nvt_sw_reset_idle(void);
+extern int32_t nvt_check_fw_reset_state(RST_COMPLETE_STATE check_reset_state);
+extern int32_t nvt_get_fw_info(void);
+extern int32_t nvt_clear_fw_status(void);
+extern int32_t nvt_check_fw_status(void);
+extern int32_t nvt_set_page(uint16_t i2c_addr, uint32_t addr);
+#if NVT_TOUCH_ESD_PROTECT
+extern void nvt_esd_check_enable(uint8_t enable);
+#endif /* #if NVT_TOUCH_ESD_PROTECT */
+extern void nvt_stop_crc_reboot(void);
+
+#else  /* NT36XXX_SPI */
+
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/of.h>
+#include <linux/spi/spi.h>
+#include <linux/uaccess.h>
+
+#include "nt36xxx_mem_map.h"
+
+#define NVT_SPI_DEBUG 0
+
+//---GPIO number---
+#define NVTTOUCH_RST_PIN 980
+#define NVTTOUCH_INT_PIN 943
+
+//---INT trigger mode---
+//#define NVT_SPI_IRQ_TYPE_EDGE_RISING 1
+//#define NVT_SPI_IRQ_TYPE_EDGE_FALLING 2
+#define NVT_SPI_INT_TRIGGER_TYPE IRQ_TYPE_EDGE_RISING
+
+//---SPI driver info.---
+#define NVT_SPI_NAME "NVT-SPI"
+
+#if NVT_SPI_DEBUG
+#define NVT_LOG(fmt, args...) pr_err("[%s] %s %d: " fmt, "NVT-SPI", __func__, __LINE__, ##args)
+#else
+#define NVT_LOG(fmt, args...) pr_debug("[%s] %s %d: " fmt, "NVT-SPI", __func__, __LINE__, ##args)
+#endif
+#define NVT_ERR(fmt, args...) pr_err("[%s] %s %d: " fmt, "NVT-SPI", __func__, __LINE__, ##args)
+
+//---Input device info.---
+#define NVT_SPI_TS_NAME "NVTCapacitiveTouchScreen"
+#define NVT_SPI_PEN_NAME "NVTCapacitivePen"
+
+//---Touch info.---
+#define NVT_SPI_TOUCH_DEFAULT_MAX_WIDTH 1080
+#define NVT_SPI_TOUCH_DEFAULT_MAX_HEIGHT 2400
+#define NVT_SPI_TOUCH_MAX_FINGER_NUM 10
+#define NVT_SPI_TOUCH_KEY_NUM 0
+#if NVT_SPI_TOUCH_KEY_NUM > 0
+extern const uint16_t nvt_spi_touch_key_array[NVT_SPI_TOUCH_KEY_NUM];
+#endif
+#define NVT_SPI_TOUCH_FORCE_NUM 1000
+//---for Pen---
+#define NVT_SPI_PEN_PRESSURE_MAX (4095)
+#define NVT_SPI_PEN_DISTANCE_MAX (1)
+#define NVT_SPI_PEN_TILT_MIN (-60)
+#define NVT_SPI_PEN_TILT_MAX (60)
+
+/* Enable only when module have tp reset pin and connected to host */
+#define NVT_SPI_TOUCH_SUPPORT_HW_RST 0
+
+//---Customerized func.---
+#define NVT_SPI_TOUCH_PROC 1
+#define NVT_SPI_TOUCH_EXT_PROC 1
+#define NVT_SPI_TOUCH_MP 0
+#define NVT_SPI_MT_PROTOCOL_B 1
+#define NVT_SPI_WAKEUP_GESTURE 0
+
+#if NVT_SPI_WAKEUP_GESTURE
+extern const uint16_t nvt_spi_gesture_key_array[];
+#endif
+#define NVT_SPI_BOOT_UPDATE_FIRMWARE 1
+#define NVT_SPI_BOOT_UPDATE_FIRMWARE_NAME "novatek_spi_fw.bin"
+#define NVT_SPI_MP_UPDATE_FIRMWARE_NAME   "novatek_ts_mp.bin"
+#define NVT_SPI_POINT_DATA_CHECKSUM 1
+#define NVT_SPI_POINT_DATA_CHECKSUM_LEN 65
+
+//---ESD Protect.---
+#define NVT_SPI_TOUCH_ESD_PROTECT 0
+#define NVT_SPI_TOUCH_ESD_CHECK_PERIOD 1500	/* ms */
+#define NVT_SPI_TOUCH_WDT_RECOVERY 1
+
+#define NVT_SPI_CHECK_PEN_DATA_CHECKSUM 0
+
+struct nvt_spi_data_t {
+	struct spi_device *client;
+	struct input_dev *input_dev;
+	struct delayed_work nvt_fwu_work;
+	uint16_t addr;
+	int8_t phys[32];
+
+#if defined(CONFIG_DRM)
+	struct notifier_block drm_panel_notif;
+#elif defined(_MSM_DRM_NOTIFY_H_)
+	struct notifier_block drm_notif;
+#else
+	struct notifier_block fb_notif;
+#endif
+
+	uint8_t fw_ver;
+	uint8_t x_num;
+	uint8_t y_num;
+	uint16_t abs_x_max;
+	uint16_t abs_y_max;
+	uint8_t max_touch_num;
+	uint8_t max_button_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_spi_mem_map *mmap;
+	uint8_t hw_crc;
+	uint16_t nvt_pid;
+	uint8_t *rbuf;
+	uint8_t *xbuf;
+	struct mutex xbuf_lock;
+	bool irq_enabled;
+	bool pen_support;
+	bool wgp_stylus;
+	uint8_t x_gang_num;
+	uint8_t y_gang_num;
+	struct input_dev *pen_input_dev;
+	int8_t pen_phys[32];
+
+	void *notifier_cookie;
+	const char *touch_environment;
+
+#ifdef CONFIG_NOVATEK_SPI_TRUSTED_TOUCH
+	struct trusted_touch_vm_info *vm_info;
+	struct mutex fts_clk_io_ctrl_mutex;
+	struct completion trusted_touch_powerdown;
+	struct clk *core_clk;
+	struct clk *iface_clk;
+	atomic_t trusted_touch_initialized;
+	atomic_t trusted_touch_enabled;
+	atomic_t trusted_touch_underway;
+	atomic_t trusted_touch_event;
+	atomic_t trusted_touch_abort_status;
+	atomic_t delayed_vm_probe_pending;
+	atomic_t trusted_touch_mode;
+#endif
+};
+
+#if NVT_SPI_TOUCH_PROC
+struct nvt_spi_flash_data {
+	rwlock_t lock;
+};
+#endif
+
+enum NVT_SPI_RST_COMPLETE_STATE {
+	NVT_SPI_RESET_STATE_INIT = 0xA0,// IC reset
+	NVT_SPI_RESET_STATE_REK,        // ReK baseline
+	NVT_SPI_RESET_STATE_REK_FINISH, // baseline is ready
+	NVT_SPI_RESET_STATE_NORMAL_RUN, // normal run
+	NVT_SPI_RESET_STATE_MAX  = 0xAF
+};
+
+enum NVT_SPI_EVENT_MAP {
+	NVT_SPI_EVENT_MAP_HOST_CMD                    = 0x50,
+	NVT_SPI_EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE = 0x51,
+	NVT_SPI_EVENT_MAP_RESET_COMPLETE              = 0x60,
+	NVT_SPI_EVENT_MAP_FWINFO                      = 0x78,
+	NVT_SPI_EVENT_MAP_PROJECTID                   = 0x9A,
+};
+
+//---SPI READ/WRITE---
+#define NVT_SPI_WRITE_MASK(a) (a | 0x80)
+#define NVT_SPI_READ_MASK(a)  (a & 0x7F)
+
+#define NVT_SPI_DUMMY_BYTES  (1)
+#define NVT_SPI_TRANSFER_LEN (63*1024)
+#define NVT_SPI_READ_LEN     (2*1024)
+#define NVT_SPI_XBUF_LEN     (NVT_SPI_TRANSFER_LEN+1+NVT_SPI_DUMMY_BYTES)
+
+enum NVT_SPI_RW {
+	NVT_SPI_WRITE = 0,
+	NVT_SPI_READ  = 1
+};
+
+//---extern structures---
+extern struct nvt_spi_data_t *nvt_spi_data;
+
+//---extern functions---
+int32_t nvt_spi_read(uint8_t *buf, uint16_t len);
+int32_t nvt_spi_write(uint8_t *buf, uint16_t len);
+void nvt_spi_bootloader_reset(void);
+void nvt_spi_eng_reset(void);
+void nvt_spi_sw_reset(void);
+void nvt_spi_sw_reset_idle(void);
+void nvt_spi_boot_ready(void);
+void nvt_spi_bld_crc_enable(void);
+void nvt_spi_fw_crc_enable(void);
+void nvt_spi_tx_auto_copy_mode(void);
+int32_t nvt_spi_update_firmware(char *firmware_name);
+void nvt_spi_update_firmware_work(struct work_struct *work);
+int32_t nvt_spi_check_fw_reset_state(enum NVT_SPI_RST_COMPLETE_STATE reset_state);
+int32_t nvt_spi_get_fw_info(void);
+int32_t nvt_spi_clear_fw_status(void);
+int32_t nvt_spi_check_fw_status(void);
+int32_t nvt_spi_check_spi_dma_tx_info(void);
+int32_t nvt_spi_set_page(uint32_t addr);
+int32_t nvt_spi_write_addr(uint32_t addr, uint8_t data);
+
+#if NVT_SPI_TOUCH_EXT_PROC
+int32_t nvt_spi_extra_proc_init(void);
+void nvt_spi_extra_proc_deinit(void);
+#endif
+
+#if NVT_SPI_TOUCH_MP
+extern int32_t nvt_spi_mp_proc_init(void);
+extern void nvt_spi_mp_proc_deinit(void);
+#endif
+
+#if NVT_SPI_TOUCH_ESD_PROTECT
+extern void nvt_spi_esd_check_enable(uint8_t enable);
+#endif /* #if NVT_SPI_TOUCH_ESD_PROTECT */
+
+
+#endif
+
+#endif /* _LINUX_NVT_TOUCH_H */

+ 1537 - 0
qcom/opensource/touch-drivers/nt36xxx/nt36xxx_ext_proc.c

@@ -0,0 +1,1537 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2010 - 2018 Novatek, Inc.
+ *
+ * $Revision: 47247 $
+ * $Date: 2019-07-10 10:41:36 +0800 (Wed, 10 Jul 2019) $
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#if !defined(NVT_NT36XXX_SPI) /* TOUCHSCREEN_NT36XXX I2C */
+
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+
+#include "nt36xxx.h"
+
+#if NVT_TOUCH_EXT_PROC
+#define NVT_FW_VERSION "nvt_fw_version"
+#define NVT_BASELINE "nvt_baseline"
+#define NVT_RAW "nvt_raw"
+#define NVT_DIFF "nvt_diff"
+
+#define BUS_TRANSFER_LENGTH  64
+
+#define NORMAL_MODE 0x00
+#define TEST_MODE_1 0x21
+#define TEST_MODE_2 0x22
+#define HANDSHAKING_HOST_READY 0xBB
+
+#define XDATA_SECTOR_SIZE   256
+
+static uint8_t xdata_tmp[2048] = {0};
+static int32_t xdata[2048] = {0};
+
+static struct proc_dir_entry *NVT_proc_fw_version_entry;
+static struct proc_dir_entry *NVT_proc_baseline_entry;
+static struct proc_dir_entry *NVT_proc_raw_entry;
+static struct proc_dir_entry *NVT_proc_diff_entry;
+
+/*******************************************************
+Description:
+	Novatek touchscreen change mode function.
+
+return:
+	n.a.
+*******************************************************/
+void nvt_change_mode(uint8_t mode)
+{
+	uint8_t buf[8] = {0};
+
+	//---set xdata index to EVENT BUF ADDR---
+	nvt_set_page(I2C_FW_Address, ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_HOST_CMD);
+
+	//---set mode---
+	buf[0] = EVENT_MAP_HOST_CMD;
+	buf[1] = mode;
+	CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 2);
+
+	if (mode == NORMAL_MODE) {
+		buf[0] = EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE;
+		buf[1] = HANDSHAKING_HOST_READY;
+		CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 2);
+		msleep(20);
+	}
+}
+
+/*******************************************************
+Description:
+	Novatek touchscreen get firmware pipe function.
+
+return:
+	Executive outcomes. 0---pipe 0. 1---pipe 1.
+*******************************************************/
+uint8_t nvt_get_fw_pipe(void)
+{
+	uint8_t buf[8]= {0};
+
+	//---set xdata index to EVENT BUF ADDR---
+	nvt_set_page(I2C_FW_Address, ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE);
+
+	//---read fw status---
+	buf[0] = EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE;
+	buf[1] = 0x00;
+	CTP_I2C_READ(ts->client, I2C_FW_Address, buf, 2);
+
+	//NVT_LOG("FW pipe=%d, buf[1]=0x%02X\n", (buf[1]&0x01), buf[1]);
+
+	return (buf[1] & 0x01);
+}
+
+/*******************************************************
+Description:
+	Novatek touchscreen read meta data function.
+
+return:
+	n.a.
+*******************************************************/
+void nvt_read_mdata(uint32_t xdata_addr, uint32_t xdata_btn_addr)
+{
+	int32_t i = 0;
+	int32_t j = 0;
+	int32_t k = 0;
+	uint8_t buf[BUS_TRANSFER_LENGTH + 1] = {0};
+	uint32_t head_addr = 0;
+	int32_t dummy_len = 0;
+	int32_t data_len = 0;
+	int32_t residual_len = 0;
+
+	//---set xdata sector address & length---
+	head_addr = xdata_addr - (xdata_addr % XDATA_SECTOR_SIZE);
+	dummy_len = xdata_addr - head_addr;
+	data_len = ts->x_num * ts->y_num * 2;
+	residual_len = (head_addr + dummy_len + data_len) % XDATA_SECTOR_SIZE;
+
+	//printk("head_addr=0x%05X, dummy_len=0x%05X, data_len=0x%05X, residual_len=0x%05X\n", head_addr, dummy_len, data_len, residual_len);
+
+	//read xdata : step 1
+	for (i = 0; i < ((dummy_len + data_len) / XDATA_SECTOR_SIZE); i++) {
+		//---change xdata index---
+		nvt_set_page(I2C_FW_Address, head_addr + XDATA_SECTOR_SIZE * i);
+
+		//---read xdata by BUS_TRANSFER_LENGTH
+		for (j = 0; j < (XDATA_SECTOR_SIZE / BUS_TRANSFER_LENGTH); j++) {
+			//---read data---
+			buf[0] = BUS_TRANSFER_LENGTH * j;
+			CTP_I2C_READ(ts->client, I2C_FW_Address, buf, BUS_TRANSFER_LENGTH + 1);
+
+			//---copy buf to xdata_tmp---
+			for (k = 0; k < BUS_TRANSFER_LENGTH; k++) {
+				xdata_tmp[XDATA_SECTOR_SIZE * i + BUS_TRANSFER_LENGTH * j + k] = buf[k + 1];
+				//printk("0x%02X, 0x%04X\n", buf[k+1], (XDATA_SECTOR_SIZE*i + BUS_TRANSFER_LENGTH*j + k));
+			}
+		}
+		//printk("addr=0x%05X\n", (head_addr+XDATA_SECTOR_SIZE*i));
+	}
+
+	//read xdata : step2
+	if (residual_len != 0) {
+		//---change xdata index---
+		nvt_set_page(I2C_FW_Address, xdata_addr + data_len - residual_len);
+
+		//---read xdata by BUS_TRANSFER_LENGTH
+		for (j = 0; j < (residual_len / BUS_TRANSFER_LENGTH + 1); j++) {
+			//---read data---
+			buf[0] = BUS_TRANSFER_LENGTH * j;
+			CTP_I2C_READ(ts->client, I2C_FW_Address, buf, BUS_TRANSFER_LENGTH + 1);
+
+			//---copy buf to xdata_tmp---
+			for (k = 0; k < BUS_TRANSFER_LENGTH; k++) {
+				xdata_tmp[(dummy_len + data_len - residual_len) + BUS_TRANSFER_LENGTH * j + k] = buf[k + 1];
+				//printk("0x%02X, 0x%04x\n", buf[k+1], ((dummy_len+data_len-residual_len) + BUS_TRANSFER_LENGTH*j + k));
+			}
+		}
+		//printk("addr=0x%05X\n", (xdata_addr+data_len-residual_len));
+	}
+
+	//---remove dummy data and 2bytes-to-1data---
+	for (i = 0; i < (data_len / 2); i++) {
+		xdata[i] = (int16_t)(xdata_tmp[dummy_len + i * 2] + 256 * xdata_tmp[dummy_len + i * 2 + 1]);
+	}
+
+#if TOUCH_KEY_NUM > 0
+	//read button xdata : step3
+	//---change xdata index---
+	nvt_set_page(I2C_FW_Address, xdata_btn_addr);
+
+	//---read data---
+	buf[0] = (xdata_btn_addr & 0xFF);
+	CTP_I2C_READ(ts->client, I2C_FW_Address, buf, (TOUCH_KEY_NUM * 2 + 1));
+
+	//---2bytes-to-1data---
+	for (i = 0; i < TOUCH_KEY_NUM; i++) {
+		xdata[ts->x_num * ts->y_num + i] = (int16_t)(buf[1 + i * 2] + 256 * buf[1 + i * 2 + 1]);
+	}
+#endif
+
+	//---set xdata index to EVENT BUF ADDR---
+	nvt_set_page(I2C_FW_Address, ts->mmap->EVENT_BUF_ADDR);
+}
+
+/*******************************************************
+Description:
+    Novatek touchscreen get meta data function.
+
+return:
+    n.a.
+*******************************************************/
+void nvt_get_mdata(int32_t *buf, uint8_t *m_x_num, uint8_t *m_y_num)
+{
+    *m_x_num = ts->x_num;
+    *m_y_num = ts->y_num;
+    memcpy(buf, xdata, ((ts->x_num * ts->y_num + TOUCH_KEY_NUM) * sizeof(int32_t)));
+}
+
+/*******************************************************
+Description:
+	Novatek touchscreen firmware version show function.
+
+return:
+	Executive outcomes. 0---succeed.
+*******************************************************/
+static int32_t c_fw_version_show(struct seq_file *m, void *v)
+{
+	seq_printf(m, "fw_ver=%d, x_num=%d, y_num=%d, button_num=%d\n", ts->fw_ver, ts->x_num, ts->y_num, ts->max_button_num);
+	return 0;
+}
+
+/*******************************************************
+Description:
+	Novatek touchscreen xdata sequence print show
+	function.
+
+return:
+	Executive outcomes. 0---succeed.
+*******************************************************/
+static int32_t c_show(struct seq_file *m, void *v)
+{
+	int32_t i = 0;
+	int32_t j = 0;
+
+	for (i = 0; i < ts->y_num; i++) {
+		for (j = 0; j < ts->x_num; j++) {
+			seq_printf(m, "%5d, ", xdata[i * ts->x_num + j]);
+		}
+		seq_puts(m, "\n");
+	}
+
+#if TOUCH_KEY_NUM > 0
+	for (i = 0; i < TOUCH_KEY_NUM; i++) {
+		seq_printf(m, "%5d, ", xdata[ts->x_num * ts->y_num + i]);
+	}
+	seq_puts(m, "\n");
+#endif
+
+	seq_printf(m, "\n\n");
+	return 0;
+}
+
+/*******************************************************
+Description:
+	Novatek touchscreen xdata sequence print start
+	function.
+
+return:
+	Executive outcomes. 1---call next function.
+	NULL---not call next function and sequence loop
+	stop.
+*******************************************************/
+static void *c_start(struct seq_file *m, loff_t *pos)
+{
+	return *pos < 1 ? (void *)1 : NULL;
+}
+
+/*******************************************************
+Description:
+	Novatek touchscreen xdata sequence print next
+	function.
+
+return:
+	Executive outcomes. NULL---no next and call sequence
+	stop function.
+*******************************************************/
+static void *c_next(struct seq_file *m, void *v, loff_t *pos)
+{
+	++*pos;
+	return NULL;
+}
+
+/*******************************************************
+Description:
+	Novatek touchscreen xdata sequence print stop
+	function.
+
+return:
+	n.a.
+*******************************************************/
+static void c_stop(struct seq_file *m, void *v)
+{
+	return;
+}
+
+const struct seq_operations nvt_fw_version_seq_ops = {
+	.start  = c_start,
+	.next   = c_next,
+	.stop   = c_stop,
+	.show   = c_fw_version_show
+};
+
+const struct seq_operations nvt_seq_ops = {
+	.start  = c_start,
+	.next   = c_next,
+	.stop   = c_stop,
+	.show   = c_show
+};
+
+/*******************************************************
+Description:
+	Novatek touchscreen /proc/nvt_fw_version open
+	function.
+
+return:
+	n.a.
+*******************************************************/
+static int32_t nvt_fw_version_open(struct inode *inode, struct file *file)
+{
+	if (mutex_lock_interruptible(&ts->lock)) {
+		return -ERESTARTSYS;
+	}
+
+	NVT_LOG("++\n");
+
+#if NVT_TOUCH_ESD_PROTECT
+	nvt_esd_check_enable(false);
+#endif /* #if NVT_TOUCH_ESD_PROTECT */
+
+	if (nvt_get_fw_info()) {
+		mutex_unlock(&ts->lock);
+		return -EAGAIN;
+	}
+
+	mutex_unlock(&ts->lock);
+
+	NVT_LOG("--\n");
+
+	return seq_open(file, &nvt_fw_version_seq_ops);
+}
+
+static const struct proc_ops nvt_fw_version_fops = {
+	.proc_open = nvt_fw_version_open,
+	.proc_read = seq_read,
+	.proc_lseek = seq_lseek,
+	.proc_release = seq_release,
+};
+
+/*******************************************************
+Description:
+	Novatek touchscreen /proc/nvt_baseline open function.
+
+return:
+	Executive outcomes. 0---succeed.
+*******************************************************/
+static int32_t nvt_baseline_open(struct inode *inode, struct file *file)
+{
+	if (mutex_lock_interruptible(&ts->lock)) {
+		return -ERESTARTSYS;
+	}
+
+	NVT_LOG("++\n");
+
+#if NVT_TOUCH_ESD_PROTECT
+	nvt_esd_check_enable(false);
+#endif /* #if NVT_TOUCH_ESD_PROTECT */
+
+	if (nvt_clear_fw_status()) {
+		mutex_unlock(&ts->lock);
+		return -EAGAIN;
+	}
+
+	nvt_change_mode(TEST_MODE_2);
+
+	if (nvt_check_fw_status()) {
+		mutex_unlock(&ts->lock);
+		return -EAGAIN;
+	}
+
+	if (nvt_get_fw_info()) {
+		mutex_unlock(&ts->lock);
+		return -EAGAIN;
+	}
+
+	nvt_read_mdata(ts->mmap->BASELINE_ADDR, ts->mmap->BASELINE_BTN_ADDR);
+
+	nvt_change_mode(NORMAL_MODE);
+
+	mutex_unlock(&ts->lock);
+
+	NVT_LOG("--\n");
+
+	return seq_open(file, &nvt_seq_ops);
+}
+
+static const struct proc_ops nvt_baseline_fops = {
+	.proc_open = nvt_baseline_open,
+	.proc_read = seq_read,
+	.proc_lseek = seq_lseek,
+	.proc_release = seq_release,
+};
+
+/*******************************************************
+Description:
+	Novatek touchscreen /proc/nvt_raw open function.
+
+return:
+	Executive outcomes. 0---succeed.
+*******************************************************/
+static int32_t nvt_raw_open(struct inode *inode, struct file *file)
+{
+	if (mutex_lock_interruptible(&ts->lock)) {
+		return -ERESTARTSYS;
+	}
+
+	NVT_LOG("++\n");
+
+#if NVT_TOUCH_ESD_PROTECT
+	nvt_esd_check_enable(false);
+#endif /* #if NVT_TOUCH_ESD_PROTECT */
+
+	if (nvt_clear_fw_status()) {
+		mutex_unlock(&ts->lock);
+		return -EAGAIN;
+	}
+
+	nvt_change_mode(TEST_MODE_2);
+
+	if (nvt_check_fw_status()) {
+		mutex_unlock(&ts->lock);
+		return -EAGAIN;
+	}
+
+	if (nvt_get_fw_info()) {
+		mutex_unlock(&ts->lock);
+		return -EAGAIN;
+	}
+
+	if (nvt_get_fw_pipe() == 0)
+		nvt_read_mdata(ts->mmap->RAW_PIPE0_ADDR, ts->mmap->RAW_BTN_PIPE0_ADDR);
+	else
+		nvt_read_mdata(ts->mmap->RAW_PIPE1_ADDR, ts->mmap->RAW_BTN_PIPE1_ADDR);
+
+	nvt_change_mode(NORMAL_MODE);
+
+	mutex_unlock(&ts->lock);
+
+	NVT_LOG("--\n");
+
+	return seq_open(file, &nvt_seq_ops);
+}
+
+static const struct proc_ops nvt_raw_fops = {
+	.proc_open = nvt_raw_open,
+	.proc_read = seq_read,
+	.proc_lseek = seq_lseek,
+	.proc_release = seq_release,
+};
+
+/*******************************************************
+Description:
+	Novatek touchscreen /proc/nvt_diff open function.
+
+return:
+	Executive outcomes. 0---succeed. negative---failed.
+*******************************************************/
+static int32_t nvt_diff_open(struct inode *inode, struct file *file)
+{
+	if (mutex_lock_interruptible(&ts->lock)) {
+		return -ERESTARTSYS;
+	}
+
+	NVT_LOG("++\n");
+
+#if NVT_TOUCH_ESD_PROTECT
+	nvt_esd_check_enable(false);
+#endif /* #if NVT_TOUCH_ESD_PROTECT */
+
+	if (nvt_clear_fw_status()) {
+		mutex_unlock(&ts->lock);
+		return -EAGAIN;
+	}
+
+	nvt_change_mode(TEST_MODE_2);
+
+	if (nvt_check_fw_status()) {
+		mutex_unlock(&ts->lock);
+		return -EAGAIN;
+	}
+
+	if (nvt_get_fw_info()) {
+		mutex_unlock(&ts->lock);
+		return -EAGAIN;
+	}
+
+	if (nvt_get_fw_pipe() == 0)
+		nvt_read_mdata(ts->mmap->DIFF_PIPE0_ADDR, ts->mmap->DIFF_BTN_PIPE0_ADDR);
+	else
+		nvt_read_mdata(ts->mmap->DIFF_PIPE1_ADDR, ts->mmap->DIFF_BTN_PIPE1_ADDR);
+
+	nvt_change_mode(NORMAL_MODE);
+
+	mutex_unlock(&ts->lock);
+
+	NVT_LOG("--\n");
+
+	return seq_open(file, &nvt_seq_ops);
+}
+
+static const struct proc_ops nvt_diff_fops = {
+	.proc_open = nvt_diff_open,
+	.proc_read = seq_read,
+	.proc_lseek = seq_lseek,
+	.proc_release = seq_release,
+};
+
+/*******************************************************
+Description:
+	Novatek touchscreen extra function proc. file node
+	initial function.
+
+return:
+	Executive outcomes. 0---succeed. -12---failed.
+*******************************************************/
+int32_t nvt_extra_proc_init(void)
+{
+	NVT_proc_fw_version_entry = proc_create(NVT_FW_VERSION, 0444, NULL,&nvt_fw_version_fops);
+	if (NVT_proc_fw_version_entry == NULL) {
+		NVT_ERR("create proc/%s Failed!\n", NVT_FW_VERSION);
+		return -ENOMEM;
+	} else {
+		NVT_LOG("create proc/%s Succeeded!\n", NVT_FW_VERSION);
+	}
+
+	NVT_proc_baseline_entry = proc_create(NVT_BASELINE, 0444, NULL,&nvt_baseline_fops);
+	if (NVT_proc_baseline_entry == NULL) {
+		NVT_ERR("create proc/%s Failed!\n", NVT_BASELINE);
+		return -ENOMEM;
+	} else {
+		NVT_LOG("create proc/%s Succeeded!\n", NVT_BASELINE);
+	}
+
+	NVT_proc_raw_entry = proc_create(NVT_RAW, 0444, NULL,&nvt_raw_fops);
+	if (NVT_proc_raw_entry == NULL) {
+		NVT_ERR("create proc/%s Failed!\n", NVT_RAW);
+		return -ENOMEM;
+	} else {
+		NVT_LOG("create proc/%s Succeeded!\n", NVT_RAW);
+	}
+
+	NVT_proc_diff_entry = proc_create(NVT_DIFF, 0444, NULL,&nvt_diff_fops);
+	if (NVT_proc_diff_entry == NULL) {
+		NVT_ERR("create proc/%s Failed!\n", NVT_DIFF);
+		return -ENOMEM;
+	} else {
+		NVT_LOG("create proc/%s Succeeded!\n", NVT_DIFF);
+	}
+
+	return 0;
+}
+
+/*******************************************************
+Description:
+	Novatek touchscreen extra function proc. file node
+	deinitial function.
+
+return:
+	n.a.
+*******************************************************/
+void nvt_extra_proc_deinit(void)
+{
+	if (NVT_proc_fw_version_entry != NULL) {
+		remove_proc_entry(NVT_FW_VERSION, NULL);
+		NVT_proc_fw_version_entry = NULL;
+		NVT_LOG("Removed /proc/%s\n", NVT_FW_VERSION);
+	}
+
+	if (NVT_proc_baseline_entry != NULL) {
+		remove_proc_entry(NVT_BASELINE, NULL);
+		NVT_proc_baseline_entry = NULL;
+		NVT_LOG("Removed /proc/%s\n", NVT_BASELINE);
+	}
+
+	if (NVT_proc_raw_entry != NULL) {
+		remove_proc_entry(NVT_RAW, NULL);
+		NVT_proc_raw_entry = NULL;
+		NVT_LOG("Removed /proc/%s\n", NVT_RAW);
+	}
+
+	if (NVT_proc_diff_entry != NULL) {
+		remove_proc_entry(NVT_DIFF, NULL);
+		NVT_proc_diff_entry = NULL;
+		NVT_LOG("Removed /proc/%s\n", NVT_DIFF);
+	}
+}
+#endif
+
+#else  /* NT36XXX_SPI */
+
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+
+#include "nt36xxx.h"
+
+#if NVT_SPI_TOUCH_EXT_PROC
+#define NVT_SPI_FW_VERSION "nvt_fw_version"
+#define NVT_SPI_BASELINE "nvt_baseline"
+#define NVT_SPI_RAW "nvt_raw"
+#define NVT_SPI_DIFF "nvt_diff"
+#define NVT_SPI_PEN_DIFF "nvt_pen_diff"
+
+#define NVT_SPI_BUS_TRANSFER_LENGTH 256
+
+#define NVT_SPI_NORMAL_MODE 0x00
+#define NVT_SPI_TEST_MODE_2 0x22
+#define NVT_SPI_HANDSHAKING_HOST_READY 0xBB
+
+#define NVT_SPI_XDATA_SECTOR_SIZE 256
+
+static uint8_t nvt_spi_xdata_tmp[5000] = {0};
+static int32_t nvt_spi_xdata[2500] = {0};
+static int32_t nvt_spi_xdata_pen_tip_x[256] = {0};
+static int32_t nvt_spi_xdata_pen_tip_y[256] = {0};
+static int32_t nvt_spi_xdata_pen_ring_x[256] = {0};
+static int32_t nvt_spi_xdata_pen_ring_y[256] = {0};
+
+static struct proc_dir_entry *nvt_spi_proc_fw_version_entry;
+static struct proc_dir_entry *nvt_spi_proc_baseline_entry;
+static struct proc_dir_entry *nvt_spi_proc_raw_entry;
+static struct proc_dir_entry *nvt_spi_proc_diff_entry;
+static struct proc_dir_entry *nvt_spi_proc_pen_diff_entry;
+
+/*
+ *******************************************************
+ * Description:
+ *	Novatek touchscreen change mode function.
+ *
+ * return:
+ *	n.a.
+ ******************************************************
+ */
+void nvt_spi_change_mode(uint8_t mode)
+{
+	uint8_t buf[8] = {0};
+	struct nvt_spi_data_t *ts = nvt_spi_data;
+
+	//---set xdata index to EVENT BUF ADDR---
+	nvt_spi_set_page(ts->mmap->EVENT_BUF_ADDR | NVT_SPI_EVENT_MAP_HOST_CMD);
+
+	//---set mode---
+	buf[0] = NVT_SPI_EVENT_MAP_HOST_CMD;
+	buf[1] = mode;
+	nvt_spi_write(buf, 2);
+
+	if (mode == NVT_SPI_NORMAL_MODE) {
+		usleep_range(20000, 21000);
+		buf[0] = NVT_SPI_EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE;
+		buf[1] = NVT_SPI_HANDSHAKING_HOST_READY;
+		nvt_spi_write(buf, 2);
+		usleep_range(20000, 21000);
+	}
+}
+
+static int32_t nvt_set_pen_inband_mode_1_spi(uint8_t freq_idx, uint8_t x_term)
+{
+	uint8_t buf[8] = {0};
+	int32_t i = 0;
+	const int32_t retry = 5;
+	struct nvt_spi_data_t *ts = nvt_spi_data;
+
+	//---set xdata index to EVENT BUF ADDR---
+	nvt_spi_set_page(ts->mmap->EVENT_BUF_ADDR | NVT_SPI_EVENT_MAP_HOST_CMD);
+
+	//---set mode---
+	buf[0] = NVT_SPI_EVENT_MAP_HOST_CMD;
+	buf[1] = 0xC1;
+	buf[2] = 0x02;
+	buf[3] = freq_idx;
+	buf[4] = x_term;
+	nvt_spi_write(buf, 5);
+
+	for (i = 0; i < retry; i++) {
+		buf[0] = NVT_SPI_EVENT_MAP_HOST_CMD;
+		buf[1] = 0xFF;
+		nvt_spi_read(buf, 2);
+
+		if (buf[1] == 0x00)
+			break;
+
+		usleep_range(10000, 11000);
+	}
+
+	if (i >= retry) {
+		NVT_ERR("failed, i=%d, buf[1]=0x%02X\n", i, buf[1]);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int32_t nvt_spi_set_pen_normal_mode(void)
+{
+	uint8_t buf[8] = {0};
+	int32_t i = 0;
+	const int32_t retry = 5;
+	struct nvt_spi_data_t *ts = nvt_spi_data;
+
+	//---set xdata index to EVENT BUF ADDR---
+	nvt_spi_set_page(ts->mmap->EVENT_BUF_ADDR | NVT_SPI_EVENT_MAP_HOST_CMD);
+
+	//---set mode---
+	buf[0] = NVT_SPI_EVENT_MAP_HOST_CMD;
+	buf[1] = 0xC1;
+	buf[2] = 0x04;
+	nvt_spi_write(buf, 3);
+
+	for (i = 0; i < retry; i++) {
+		buf[0] = NVT_SPI_EVENT_MAP_HOST_CMD;
+		buf[1] = 0xFF;
+		nvt_spi_read(buf, 2);
+
+		if (buf[1] == 0x00)
+			break;
+
+		usleep_range(10000, 11000);
+	}
+
+	if (i >= retry) {
+		NVT_ERR("failed, i=%d, buf[1]=0x%02X\n", i, buf[1]);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+/*
+ *******************************************************
+ * Description:
+ *	Novatek touchscreen get firmware pipe function.
+ *
+ * return:
+ *	Executive outcomes. 0---pipe 0. 1---pipe 1.
+ ******************************************************
+ */
+uint8_t nvt_spi_get_fw_pipe(void)
+{
+	uint32_t addr;
+	uint8_t buf[8] = {0};
+	struct nvt_spi_data_t *ts = nvt_spi_data;
+
+	//---set xdata index to EVENT BUF ADDR---
+	addr = ts->mmap->EVENT_BUF_ADDR;
+	nvt_spi_set_page(addr | NVT_SPI_EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE);
+
+	//---read fw status---
+	buf[0] = NVT_SPI_EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE;
+	buf[1] = 0x00;
+	nvt_spi_read(buf, 2);
+
+	//NVT_LOG("FW pipe=%d, buf[1]=0x%02X\n", (buf[1]&0x01), buf[1]);
+
+	return (buf[1] & 0x01);
+}
+
+/*
+ *******************************************************
+ * Description:
+ *	Novatek touchscreen read meta data function.
+ *
+ * return:
+ *	n.a.
+ ******************************************************
+ */
+static void nvt_spi_read_mdata(uint32_t xdata_addr, uint32_t xdata_btn_addr)
+{
+	int32_t i = 0;
+	int32_t j = 0;
+	int32_t k = 0;
+	uint8_t buf[NVT_SPI_BUS_TRANSFER_LENGTH + 2] = {0};
+	uint32_t head_addr = 0;
+	int32_t dummy_len = 0;
+	int32_t data_len = 0;
+	int32_t residual_len = 0;
+	int32_t index, data;
+	struct nvt_spi_data_t *ts = nvt_spi_data;
+
+	//---set xdata sector address & length---
+	head_addr = xdata_addr - (xdata_addr % NVT_SPI_XDATA_SECTOR_SIZE);
+	dummy_len = xdata_addr - head_addr;
+	data_len = ts->x_num * ts->y_num * 2;
+	residual_len = (head_addr + dummy_len + data_len) % NVT_SPI_XDATA_SECTOR_SIZE;
+
+	//printk("head_addr=0x%05X, dummy_len=0x%05X, data_len=0x%05X, residual_len=0x%05X\n",
+	//		head_addr, dummy_len, data_len, residual_len);
+
+	//read xdata : step 1
+	for (i = 0; i < ((dummy_len + data_len) / NVT_SPI_XDATA_SECTOR_SIZE); i++) {
+		//---change xdata index---
+		nvt_spi_set_page(head_addr + NVT_SPI_XDATA_SECTOR_SIZE * i);
+
+		//---read xdata by NVT_SPI_BUS_TRANSFER_LENGTH
+		for (j = 0; j < (NVT_SPI_XDATA_SECTOR_SIZE / NVT_SPI_BUS_TRANSFER_LENGTH); j++) {
+			//---read data---
+			buf[0] = NVT_SPI_BUS_TRANSFER_LENGTH * j;
+			nvt_spi_read(buf, NVT_SPI_BUS_TRANSFER_LENGTH + 1);
+
+			//---copy buf to nvt_spi_xdata_tmp---
+			for (k = 0; k < NVT_SPI_BUS_TRANSFER_LENGTH; k++) {
+				index = NVT_SPI_XDATA_SECTOR_SIZE * i;
+				index += NVT_SPI_BUS_TRANSFER_LENGTH * j + k;
+				nvt_spi_xdata_tmp[index] = buf[k + 1];
+				//printk("0x%02X, 0x%04X\n", buf[k+1], index);
+			}
+		}
+		//printk("addr=0x%05X\n", (head_addr+NVT_SPI_XDATA_SECTOR_SIZE*i));
+	}
+
+	//read xdata : step2
+	if (residual_len != 0) {
+		//---change xdata index---
+		nvt_spi_set_page(xdata_addr + data_len - residual_len);
+
+		//---read xdata by NVT_SPI_BUS_TRANSFER_LENGTH
+		for (j = 0; j < (residual_len / NVT_SPI_BUS_TRANSFER_LENGTH + 1); j++) {
+			//---read data---
+			buf[0] = NVT_SPI_BUS_TRANSFER_LENGTH * j;
+			nvt_spi_read(buf, NVT_SPI_BUS_TRANSFER_LENGTH + 1);
+
+			//---copy buf to nvt_spi_xdata_tmp---
+			for (k = 0; k < NVT_SPI_BUS_TRANSFER_LENGTH; k++) {
+				index = (dummy_len + data_len - residual_len);
+				index += NVT_SPI_BUS_TRANSFER_LENGTH * j + k;
+				nvt_spi_xdata_tmp[index] = buf[k + 1];
+				//printk("0x%02X, 0x%04x\n", buf[k+1], index);
+
+			}
+		}
+		//printk("addr=0x%05X\n", (xdata_addr+data_len-residual_len));
+	}
+
+	//---remove dummy data and 2bytes-to-1data---
+	for (i = 0; i < (data_len / 2); i++) {
+		data = nvt_spi_xdata_tmp[dummy_len + i * 2];
+		data += 256 * nvt_spi_xdata_tmp[dummy_len + i * 2 + 1];
+		nvt_spi_xdata[i] = (int16_t)data;
+	}
+
+#if NVT_SPI_TOUCH_KEY_NUM > 0
+	//read button xdata : step3
+	//---change xdata index---
+	nvt_spi_set_page(xdata_btn_addr);
+
+	//---read data---
+	buf[0] = (xdata_btn_addr & 0xFF);
+	nvt_spi_read(buf, (NVT_SPI_TOUCH_KEY_NUM * 2 + 1));
+
+	//---2bytes-to-1data---
+	for (i = 0; i < NVT_SPI_TOUCH_KEY_NUM; i++)
+		nvt_spi_xdata[ts->x_num * ts->y_num + i] =
+				(int16_t)(buf[1 + i * 2] + 256 * buf[1 + i * 2 + 1]);
+#endif
+
+	//---set xdata index to EVENT BUF ADDR---
+	nvt_spi_set_page(ts->mmap->EVENT_BUF_ADDR);
+}
+
+/*
+ *******************************************************
+ * Description:
+ *	Novatek touchscreen get meta data function.
+ *
+ * return:
+ *	n.a.
+ ******************************************************
+ */
+void nvt_spi_get_mdata(int32_t *buf, uint8_t *m_x_num, uint8_t *m_y_num)
+{
+	struct nvt_spi_data_t *ts = nvt_spi_data;
+
+	*m_x_num = ts->x_num;
+	*m_y_num = ts->y_num;
+	memcpy(buf, nvt_spi_xdata,
+		((ts->x_num * ts->y_num + NVT_SPI_TOUCH_KEY_NUM) * sizeof(int32_t)));
+}
+
+/*
+ *******************************************************
+ * Description:
+ *	Novatek touchscreen read and get number of meta data function.
+ *
+ * return:
+ *	n.a.
+ *******************************************************
+ */
+void nvt_spi_read_get_num_mdata(uint32_t xdata_addr, int32_t *buffer, uint32_t num)
+{
+	int32_t i = 0;
+	int32_t j = 0;
+	int32_t k = 0;
+	uint8_t buf[NVT_SPI_BUS_TRANSFER_LENGTH + 2] = {0};
+	uint32_t head_addr = 0;
+	int32_t dummy_len = 0;
+	int32_t data_len = 0;
+	int32_t residual_len = 0;
+	int32_t index, data;
+	struct nvt_spi_data_t *ts = nvt_spi_data;
+
+	//---set xdata sector address & length---
+	head_addr = xdata_addr - (xdata_addr % NVT_SPI_XDATA_SECTOR_SIZE);
+	dummy_len = xdata_addr - head_addr;
+	data_len = num * 2;
+	residual_len = (head_addr + dummy_len + data_len) % NVT_SPI_XDATA_SECTOR_SIZE;
+
+	//printk("head_addr=0x%05X, dummy_len=0x%05X, data_len=0x%05X, residual_len=0x%05X\n",
+	//	head_addr, dummy_len, data_len, residual_len);
+
+	//read xdata : step 1
+	for (i = 0; i < ((dummy_len + data_len) / NVT_SPI_XDATA_SECTOR_SIZE); i++) {
+		//---change xdata index---
+		nvt_spi_set_page(head_addr + NVT_SPI_XDATA_SECTOR_SIZE * i);
+
+		//---read xdata by NVT_SPI_BUS_TRANSFER_LENGTH
+		for (j = 0; j < (NVT_SPI_XDATA_SECTOR_SIZE / NVT_SPI_BUS_TRANSFER_LENGTH); j++) {
+			//---read data---
+			buf[0] = NVT_SPI_BUS_TRANSFER_LENGTH * j;
+			nvt_spi_read(buf, NVT_SPI_BUS_TRANSFER_LENGTH + 1);
+
+			//---copy buf to nvt_spi_xdata_tmp---
+			for (k = 0; k < NVT_SPI_BUS_TRANSFER_LENGTH; k++) {
+				index = NVT_SPI_XDATA_SECTOR_SIZE * i;
+				index += NVT_SPI_BUS_TRANSFER_LENGTH * j + k;
+				nvt_spi_xdata_tmp[index] = buf[k + 1];
+			}
+		}
+		//printk("addr=0x%05X\n", (head_addr+NVT_SPI_XDATA_SECTOR_SIZE*i));
+	}
+
+	//read xdata : step2
+	if (residual_len != 0) {
+		//---change xdata index---
+		nvt_spi_set_page(xdata_addr + data_len - residual_len);
+
+		//---read xdata by NVT_SPI_BUS_TRANSFER_LENGTH
+		for (j = 0; j < (residual_len / NVT_SPI_BUS_TRANSFER_LENGTH + 1); j++) {
+			//---read data---
+			buf[0] = NVT_SPI_BUS_TRANSFER_LENGTH * j;
+			nvt_spi_read(buf, NVT_SPI_BUS_TRANSFER_LENGTH + 1);
+
+			//---copy buf to nvt_spi_xdata_tmp---
+			for (k = 0; k < NVT_SPI_BUS_TRANSFER_LENGTH; k++) {
+				index = (dummy_len + data_len - residual_len);
+				index += NVT_SPI_BUS_TRANSFER_LENGTH * j + k;
+				nvt_spi_xdata_tmp[index] = buf[k + 1];
+				//printk("0x%02X, 0x%04x\n", buf[k+1], index));
+
+			}
+		}
+		//printk("addr=0x%05X\n", (xdata_addr+data_len-residual_len));
+	}
+
+	//---remove dummy data and 2bytes-to-1data---
+	for (i = 0; i < (data_len / 2); i++) {
+		data = nvt_spi_xdata_tmp[dummy_len + i * 2];
+		data += 256 * nvt_spi_xdata_tmp[dummy_len + i * 2 + 1];
+		buffer[i] = (int16_t)data;
+	}
+
+	//---set xdata index to EVENT BUF ADDR---
+	nvt_spi_set_page(ts->mmap->EVENT_BUF_ADDR);
+}
+
+/*
+ ******************************************************
+ * Description:
+ *	Novatek touchscreen firmware version show function.
+ *
+ * return:
+ *	Executive outcomes. 0---succeed.
+ ******************************************************
+ */
+static int32_t c_fw_version_show(struct seq_file *m, void *v)
+{
+	struct nvt_spi_data_t *ts = nvt_spi_data;
+
+	seq_printf(m, "fw_ver=%d, x_num=%d, y_num=%d, button_num=%d\n",
+		ts->fw_ver, ts->x_num, ts->y_num, ts->max_button_num);
+	return 0;
+}
+
+/*
+ *******************************************************
+ * Description:
+ *	Novatek touchscreen xdata sequence print show
+ *	function.
+ *
+ * return:
+ *	Executive outcomes. 0---succeed.
+ ******************************************************
+ */
+static int32_t c_show(struct seq_file *m, void *v)
+{
+	int32_t i = 0;
+	int32_t j = 0;
+	struct nvt_spi_data_t *ts = nvt_spi_data;
+
+	for (i = 0; i < ts->y_num; i++) {
+		for (j = 0; j < ts->x_num; j++)
+			seq_printf(m, "%5d, ", nvt_spi_xdata[i * ts->x_num + j]);
+
+		seq_puts(m, "\n");
+	}
+
+#if NVT_SPI_TOUCH_KEY_NUM > 0
+	for (i = 0; i < NVT_SPI_TOUCH_KEY_NUM; i++)
+		seq_printf(m, "%5d, ", nvt_spi_xdata[ts->x_num * ts->y_num + i]);
+
+	seq_puts(m, "\n");
+#endif
+
+	seq_puts(m, "\n\n");
+	return 0;
+}
+
+/*
+ *******************************************************
+ * Description:
+ *	Novatek pen 1D diff xdata sequence print show
+ *	function.
+ *
+ * return:
+ *	Executive outcomes. 0---succeed.
+ ******************************************************
+ */
+static int32_t c_pen_1d_diff_show(struct seq_file *m, void *v)
+{
+	int32_t i = 0;
+	struct nvt_spi_data_t *ts = nvt_spi_data;
+
+	seq_puts(m, "Tip X:\n");
+	for (i = 0; i < ts->x_num; i++)
+		seq_printf(m, "%5d, ", nvt_spi_xdata_pen_tip_x[i]);
+
+	seq_puts(m, "\n");
+	seq_puts(m, "Tip Y:\n");
+	for (i = 0; i < ts->y_num; i++)
+		seq_printf(m, "%5d, ", nvt_spi_xdata_pen_tip_y[i]);
+
+	seq_puts(m, "\n");
+	seq_puts(m, "Ring X:\n");
+	for (i = 0; i < ts->x_num; i++)
+		seq_printf(m, "%5d, ", nvt_spi_xdata_pen_ring_x[i]);
+
+	seq_puts(m, "\n");
+	seq_puts(m, "Ring Y:\n");
+	for (i = 0; i < ts->y_num; i++)
+		seq_printf(m, "%5d, ", nvt_spi_xdata_pen_ring_y[i]);
+
+	seq_puts(m, "\n");
+
+	seq_puts(m, "\n\n");
+	return 0;
+}
+
+/*
+ *******************************************************
+ * Description:
+ *	Novatek touchscreen xdata sequence print start
+ *	function.
+ *
+ * return:
+ *	Executive outcomes. 1---call next function.
+ *	NULL---not call next function and sequence loop
+ *	stop.
+ *******************************************************
+ */
+static void *c_start(struct seq_file *m, loff_t *pos)
+{
+	return *pos < 1 ? (void *)1 : NULL;
+}
+
+/*
+ *******************************************************
+ * Description:
+ *	Novatek touchscreen xdata sequence print next
+ *	function.
+ *
+ * return:
+ *	Executive outcomes. NULL---no next and call sequence
+ *	stop function.
+ ******************************************************
+ */
+static void *c_next(struct seq_file *m, void *v, loff_t *pos)
+{
+	++*pos;
+	return NULL;
+}
+
+/*
+ *******************************************************
+ * Description:
+ *	Novatek touchscreen xdata sequence print stop
+ *	function.
+ *
+ * return:
+ *	n.a.
+ ******************************************************
+ */
+static void c_stop(struct seq_file *m, void *v)
+{
+}
+
+const struct seq_operations nvt_spi_fw_version_seq_ops = {
+	.start = c_start,
+	.next  = c_next,
+	.stop  = c_stop,
+	.show  = c_fw_version_show
+};
+
+const struct seq_operations nvt_spi_seq_ops = {
+	.start = c_start,
+	.next  = c_next,
+	.stop  = c_stop,
+	.show  = c_show
+};
+
+const struct seq_operations nvt_spi_pen_diff_seq_ops = {
+	.start = c_start,
+	.next  = c_next,
+	.stop  = c_stop,
+	.show  = c_pen_1d_diff_show
+};
+
+/*
+ *******************************************************
+ * Description:
+ *	Novatek touchscreen /proc/nvt_fw_version open
+ *	function.
+ *
+ * return:
+ *	n.a.
+ ******************************************************
+ */
+static int32_t nvt_spi_fw_version_open(struct inode *inode, struct file *file)
+{
+	struct nvt_spi_data_t *ts = nvt_spi_data;
+
+	if (mutex_lock_interruptible(&ts->lock))
+		return -ERESTARTSYS;
+
+	NVT_LOG("++\n");
+
+#if NVT_SPI_TOUCH_ESD_PROTECT
+	nvt_spi_esd_check_enable(false);
+#endif /* #if NVT_SPI_TOUCH_ESD_PROTECT */
+
+	if (nvt_spi_get_fw_info()) {
+		mutex_unlock(&ts->lock);
+		return -EAGAIN;
+	}
+
+	mutex_unlock(&ts->lock);
+
+	NVT_LOG("--\n");
+
+	return seq_open(file, &nvt_spi_fw_version_seq_ops);
+}
+
+static const struct proc_ops nvt_spi_fw_version_fops = {
+	.proc_open = nvt_spi_fw_version_open,
+	.proc_read = seq_read,
+	.proc_lseek = seq_lseek,
+	.proc_release = seq_release,
+};
+
+/*
+ *******************************************************
+ * Description:
+ *	Novatek touchscreen /proc/nvt_baseline open function.
+ *
+ * return:
+ *	Executive outcomes. 0---succeed.
+ ******************************************************
+ */
+static int32_t nvt_spi_baseline_open(struct inode *inode, struct file *file)
+{
+	struct nvt_spi_data_t *ts = nvt_spi_data;
+
+	if (mutex_lock_interruptible(&ts->lock))
+		return -ERESTARTSYS;
+
+	NVT_LOG("++\n");
+
+#if NVT_SPI_TOUCH_ESD_PROTECT
+	nvt_spi_esd_check_enable(false);
+#endif /* #if NVT_SPI_TOUCH_ESD_PROTECT */
+
+	if (nvt_spi_clear_fw_status()) {
+		mutex_unlock(&ts->lock);
+		return -EAGAIN;
+	}
+
+	nvt_spi_change_mode(NVT_SPI_TEST_MODE_2);
+
+	if (nvt_spi_check_fw_status()) {
+		mutex_unlock(&ts->lock);
+		return -EAGAIN;
+	}
+
+	if (nvt_spi_get_fw_info()) {
+		mutex_unlock(&ts->lock);
+		return -EAGAIN;
+	}
+
+	nvt_spi_read_mdata(ts->mmap->BASELINE_ADDR, ts->mmap->BASELINE_BTN_ADDR);
+
+	nvt_spi_change_mode(NVT_SPI_NORMAL_MODE);
+
+	mutex_unlock(&ts->lock);
+
+	NVT_LOG("--\n");
+
+	return seq_open(file, &nvt_spi_seq_ops);
+}
+
+static const struct proc_ops nvt_spi_baseline_fops = {
+	.proc_open = nvt_spi_baseline_open,
+	.proc_read = seq_read,
+	.proc_lseek = seq_lseek,
+	.proc_release = seq_release,
+};
+
+/*
+ *******************************************************
+ * Description:
+ *	Novatek touchscreen /proc/nvt_raw open function.
+ *
+ * return:
+ *	Executive outcomes. 0---succeed.
+ ******************************************************
+ */
+static int32_t nvt_spi_raw_open(struct inode *inode, struct file *file)
+{
+	struct nvt_spi_data_t *ts = nvt_spi_data;
+
+	if (mutex_lock_interruptible(&ts->lock))
+		return -ERESTARTSYS;
+
+	NVT_LOG("++\n");
+
+#if NVT_SPI_TOUCH_ESD_PROTECT
+	nvt_spi_esd_check_enable(false);
+#endif /* #if NVT_SPI_TOUCH_ESD_PROTECT */
+
+	if (nvt_spi_clear_fw_status()) {
+		mutex_unlock(&ts->lock);
+		return -EAGAIN;
+	}
+
+	nvt_spi_change_mode(NVT_SPI_TEST_MODE_2);
+
+	if (nvt_spi_check_fw_status()) {
+		mutex_unlock(&ts->lock);
+		return -EAGAIN;
+	}
+
+	if (nvt_spi_get_fw_info()) {
+		mutex_unlock(&ts->lock);
+		return -EAGAIN;
+	}
+
+	if (nvt_spi_get_fw_pipe() == 0)
+		nvt_spi_read_mdata(ts->mmap->RAW_PIPE0_ADDR, ts->mmap->RAW_BTN_PIPE0_ADDR);
+	else
+		nvt_spi_read_mdata(ts->mmap->RAW_PIPE1_ADDR, ts->mmap->RAW_BTN_PIPE1_ADDR);
+
+	nvt_spi_change_mode(NVT_SPI_NORMAL_MODE);
+
+	mutex_unlock(&ts->lock);
+
+	NVT_LOG("--\n");
+
+	return seq_open(file, &nvt_spi_seq_ops);
+}
+
+static const struct proc_ops nvt_spi_raw_fops = {
+	.proc_open = nvt_spi_raw_open,
+	.proc_read = seq_read,
+	.proc_lseek = seq_lseek,
+	.proc_release = seq_release,
+};
+
+/*
+ ******************************************************
+ * Description:
+ *	Novatek touchscreen /proc/nvt_diff open function.
+ *
+ * return:
+ *	Executive outcomes. 0---succeed. negative---failed.
+ ******************************************************
+ */
+static int32_t nvt_spi_diff_open(struct inode *inode, struct file *file)
+{
+	struct nvt_spi_data_t *ts = nvt_spi_data;
+
+	if (mutex_lock_interruptible(&ts->lock))
+		return -ERESTARTSYS;
+
+	NVT_LOG("++\n");
+
+#if NVT_SPI_TOUCH_ESD_PROTECT
+	nvt_spi_esd_check_enable(false);
+#endif /* #if NVT_SPI_TOUCH_ESD_PROTECT */
+
+	if (nvt_spi_clear_fw_status()) {
+		mutex_unlock(&ts->lock);
+		return -EAGAIN;
+	}
+
+	nvt_spi_change_mode(NVT_SPI_TEST_MODE_2);
+
+	if (nvt_spi_check_fw_status()) {
+		mutex_unlock(&ts->lock);
+		return -EAGAIN;
+	}
+
+	if (nvt_spi_get_fw_info()) {
+		mutex_unlock(&ts->lock);
+		return -EAGAIN;
+	}
+
+	if (nvt_spi_get_fw_pipe() == 0)
+		nvt_spi_read_mdata(ts->mmap->DIFF_PIPE0_ADDR, ts->mmap->DIFF_BTN_PIPE0_ADDR);
+	else
+		nvt_spi_read_mdata(ts->mmap->DIFF_PIPE1_ADDR, ts->mmap->DIFF_BTN_PIPE1_ADDR);
+
+	nvt_spi_change_mode(NVT_SPI_NORMAL_MODE);
+
+	mutex_unlock(&ts->lock);
+
+	NVT_LOG("--\n");
+
+	return seq_open(file, &nvt_spi_seq_ops);
+}
+
+static const struct proc_ops nvt_spi_diff_fops = {
+	.proc_open = nvt_spi_diff_open,
+	.proc_read = seq_read,
+	.proc_lseek = seq_lseek,
+	.proc_release = seq_release,
+};
+
+/*
+ *******************************************************
+ * Description:
+ *	Novatek touchscreen /proc/nvt_pen_diff open function.
+ *
+ * return:
+ *	Executive outcomes. 0---succeed. negative---failed.
+ ******************************************************
+ */
+static int32_t nvt_spi_pen_diff_open(struct inode *inode, struct file *file)
+{
+	uint32_t addr;
+	struct nvt_spi_data_t *ts = nvt_spi_data;
+
+	if (mutex_lock_interruptible(&ts->lock))
+		return -ERESTARTSYS;
+
+	NVT_LOG("++\n");
+
+#if NVT_SPI_TOUCH_ESD_PROTECT
+	nvt_spi_esd_check_enable(false);
+#endif /* #if NVT_SPI_TOUCH_ESD_PROTECT */
+
+	if (nvt_set_pen_inband_mode_1_spi(0xFF, 0x00)) {
+		mutex_unlock(&ts->lock);
+		return -EAGAIN;
+	}
+
+	if (nvt_spi_check_fw_reset_state(NVT_SPI_RESET_STATE_NORMAL_RUN)) {
+		mutex_unlock(&ts->lock);
+		return -EAGAIN;
+	}
+
+	if (nvt_spi_clear_fw_status()) {
+		mutex_unlock(&ts->lock);
+		return -EAGAIN;
+	}
+
+	nvt_spi_change_mode(NVT_SPI_TEST_MODE_2);
+
+	if (nvt_spi_check_fw_status()) {
+		mutex_unlock(&ts->lock);
+		return -EAGAIN;
+	}
+
+	if (nvt_spi_get_fw_info()) {
+		mutex_unlock(&ts->lock);
+		return -EAGAIN;
+	}
+
+	addr = ts->mmap->PEN_1D_DIFF_TIP_X_ADDR;
+	nvt_spi_read_get_num_mdata(addr, nvt_spi_xdata_pen_tip_x, ts->x_num);
+
+	addr = ts->mmap->PEN_1D_DIFF_TIP_Y_ADDR;
+	nvt_spi_read_get_num_mdata(addr, nvt_spi_xdata_pen_tip_y, ts->y_num);
+
+	addr = ts->mmap->PEN_1D_DIFF_RING_X_ADDR;
+	nvt_spi_read_get_num_mdata(addr, nvt_spi_xdata_pen_ring_x, ts->x_num);
+
+	addr = ts->mmap->PEN_1D_DIFF_RING_Y_ADDR;
+	nvt_spi_read_get_num_mdata(addr, nvt_spi_xdata_pen_ring_y, ts->y_num);
+
+	nvt_spi_change_mode(NVT_SPI_NORMAL_MODE);
+
+	nvt_spi_set_pen_normal_mode();
+
+	nvt_spi_check_fw_reset_state(NVT_SPI_RESET_STATE_NORMAL_RUN);
+
+	mutex_unlock(&ts->lock);
+
+	NVT_LOG("--\n");
+
+	return seq_open(file, &nvt_spi_pen_diff_seq_ops);
+}
+
+static const struct proc_ops nvt_spi_pen_diff_fops = {
+	.proc_open = nvt_spi_pen_diff_open,
+	.proc_read = seq_read,
+	.proc_lseek = seq_lseek,
+	.proc_release = seq_release,
+};
+
+/*
+ *******************************************************
+ * Description:
+ *	Novatek touchscreen extra function proc. file node
+ *	initial function.
+ *
+ * return:
+ *	Executive outcomes. 0---succeed. -12---failed.
+ ******************************************************
+ */
+int32_t nvt_spi_extra_proc_init(void)
+{
+	struct nvt_spi_data_t *ts = nvt_spi_data;
+
+	nvt_spi_proc_fw_version_entry = proc_create(NVT_SPI_FW_VERSION, 0444, NULL,
+			&nvt_spi_fw_version_fops);
+	if (nvt_spi_proc_fw_version_entry == NULL) {
+		NVT_ERR("create proc/%s Failed!\n", NVT_SPI_FW_VERSION);
+		return -ENOMEM;
+	}
+	NVT_LOG("create proc/%s Succeeded!\n", NVT_SPI_FW_VERSION);
+
+	nvt_spi_proc_baseline_entry = proc_create(NVT_SPI_BASELINE, 0444, NULL,
+			&nvt_spi_baseline_fops);
+	if (nvt_spi_proc_baseline_entry == NULL) {
+		NVT_ERR("create proc/%s Failed!\n", NVT_SPI_BASELINE);
+		return -ENOMEM;
+	}
+	NVT_LOG("create proc/%s Succeeded!\n", NVT_SPI_BASELINE);
+
+	nvt_spi_proc_raw_entry = proc_create(NVT_SPI_RAW, 0444, NULL, &nvt_spi_raw_fops);
+	if (nvt_spi_proc_raw_entry == NULL) {
+		NVT_ERR("create proc/%s Failed!\n", NVT_SPI_RAW);
+		return -ENOMEM;
+	}
+	NVT_LOG("create proc/%s Succeeded!\n", NVT_SPI_RAW);
+
+	nvt_spi_proc_diff_entry = proc_create(NVT_SPI_DIFF, 0444, NULL, &nvt_spi_diff_fops);
+	if (nvt_spi_proc_diff_entry == NULL) {
+		NVT_ERR("create proc/%s Failed!\n", NVT_SPI_DIFF);
+		return -ENOMEM;
+	}
+	NVT_LOG("create proc/%s Succeeded!\n", NVT_SPI_DIFF);
+
+	if (ts->pen_support) {
+		nvt_spi_proc_pen_diff_entry = proc_create(NVT_SPI_PEN_DIFF, 0444, NULL,
+				&nvt_spi_pen_diff_fops);
+		if (nvt_spi_proc_pen_diff_entry == NULL) {
+			NVT_ERR("create proc/%s Failed!\n", NVT_SPI_PEN_DIFF);
+			return -ENOMEM;
+		}
+		NVT_LOG("create proc/%s Succeeded!\n", NVT_SPI_PEN_DIFF);
+	}
+
+	return 0;
+}
+
+/*
+ ******************************************************
+ * Description:
+ *	Novatek touchscreen extra function proc. file node
+ *	deinitial function.
+ *
+ * return:
+ *	n.a.
+ ******************************************************
+ */
+void nvt_spi_extra_proc_deinit(void)
+{
+	struct nvt_spi_data_t *ts = nvt_spi_data;
+
+	if (nvt_spi_proc_fw_version_entry != NULL) {
+		remove_proc_entry(NVT_SPI_FW_VERSION, NULL);
+		nvt_spi_proc_fw_version_entry = NULL;
+		NVT_LOG("Removed /proc/%s\n", NVT_SPI_FW_VERSION);
+	}
+
+	if (nvt_spi_proc_baseline_entry != NULL) {
+		remove_proc_entry(NVT_SPI_BASELINE, NULL);
+		nvt_spi_proc_baseline_entry = NULL;
+		NVT_LOG("Removed /proc/%s\n", NVT_SPI_BASELINE);
+	}
+
+	if (nvt_spi_proc_raw_entry != NULL) {
+		remove_proc_entry(NVT_SPI_RAW, NULL);
+		nvt_spi_proc_raw_entry = NULL;
+		NVT_LOG("Removed /proc/%s\n", NVT_SPI_RAW);
+	}
+
+	if (nvt_spi_proc_diff_entry != NULL) {
+		remove_proc_entry(NVT_SPI_DIFF, NULL);
+		nvt_spi_proc_diff_entry = NULL;
+		NVT_LOG("Removed /proc/%s\n", NVT_SPI_DIFF);
+	}
+
+	if (ts->pen_support) {
+		if (nvt_spi_proc_pen_diff_entry != NULL) {
+			remove_proc_entry(NVT_SPI_PEN_DIFF, NULL);
+			nvt_spi_proc_pen_diff_entry = NULL;
+			NVT_LOG("Removed /proc/%s\n", NVT_SPI_PEN_DIFF);
+		}
+	}
+}
+#endif
+
+
+
+#endif

+ 2002 - 0
qcom/opensource/touch-drivers/nt36xxx/nt36xxx_fw_update.c

@@ -0,0 +1,2002 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2010 - 2018 Novatek, Inc.
+ *
+ * $Revision: 47247 $
+ * $Date: 2019-07-10 10:41:36 +0800 (Wed, 10 Jul 2019) $
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#if !defined(NVT_NT36XXX_SPI) /* NT36XXX I2C */
+
+#include <linux/firmware.h>
+
+#include "nt36xxx.h"
+
+#if BOOT_UPDATE_FIRMWARE
+
+#define SIZE_4KB 4096
+#define FLASH_SECTOR_SIZE SIZE_4KB
+#define SIZE_64KB 65536
+#define BLOCK_64KB_NUM 4
+#define FW_BIN_VER_OFFSET (fw_need_write_size - SIZE_4KB)
+#define FW_BIN_VER_BAR_OFFSET (FW_BIN_VER_OFFSET + 1)
+
+#define NVT_FLASH_END_FLAG_LEN 3
+#define NVT_FLASH_END_FLAG_ADDR (fw_need_write_size - NVT_FLASH_END_FLAG_LEN)
+
+const struct firmware *fw_entry = NULL;
+static size_t fw_need_write_size = 0;
+
+static int32_t nvt_get_fw_need_write_size(const struct firmware *fw_entry)
+{
+	int32_t i = 0;
+	int32_t total_sectors_to_check = 0;
+
+	total_sectors_to_check = fw_entry->size / FLASH_SECTOR_SIZE;
+	/* printk("total_sectors_to_check = %d\n", total_sectors_to_check); */
+
+	for (i = total_sectors_to_check; i > 0; i--) {
+		/* printk("current end flag address checked = 0x%X\n", i * FLASH_SECTOR_SIZE - NVT_FLASH_END_FLAG_LEN); */
+		/* check if there is end flag "NVT" at the end of this sector */
+		if ((memcmp((const char *)&fw_entry->data[i * FLASH_SECTOR_SIZE -
+				NVT_FLASH_END_FLAG_LEN], "NVT", NVT_FLASH_END_FLAG_LEN) == 0) ||
+			(memcmp((const char *)&fw_entry->data[i * FLASH_SECTOR_SIZE -
+				NVT_FLASH_END_FLAG_LEN], "MOD", NVT_FLASH_END_FLAG_LEN) == 0)) {
+			fw_need_write_size = i * FLASH_SECTOR_SIZE;
+			NVT_LOG("fw_need_write_size = %zu(0x%zx)\n", fw_need_write_size, fw_need_write_size);
+			return 0;
+		}
+	}
+
+	NVT_ERR("end flag \"NVT\" not found!\n");
+	return -1;
+}
+
+/*******************************************************
+Description:
+	Novatek touchscreen request update firmware function.
+
+return:
+	Executive outcomes. 0---succeed. -1,-22---failed.
+*******************************************************/
+int32_t update_firmware_request(char *filename)
+{
+	int32_t ret = 0;
+
+	if (NULL == filename) {
+		return -1;
+	}
+
+	NVT_LOG("filename is %s\n", filename);
+
+	ret = request_firmware(&fw_entry, filename, &ts->client->dev);
+	if (ret) {
+		NVT_ERR("firmware load failed, ret=%d\n", ret);
+		return ret;
+	}
+
+	// check FW need to write size
+	if (nvt_get_fw_need_write_size(fw_entry)) {
+		NVT_ERR("get fw need to write size fail!\n");
+		return -EINVAL;
+	}
+
+	// check if FW version add FW version bar equals 0xFF
+	if (*(fw_entry->data + FW_BIN_VER_OFFSET) + *(fw_entry->data + FW_BIN_VER_BAR_OFFSET) != 0xFF) {
+		NVT_ERR("bin file FW_VER + FW_VER_BAR should be 0xFF!\n");
+		NVT_ERR("FW_VER=0x%02X, FW_VER_BAR=0x%02X\n", *(fw_entry->data+FW_BIN_VER_OFFSET), *(fw_entry->data+FW_BIN_VER_BAR_OFFSET));
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/*******************************************************
+Description:
+	Novatek touchscreen release update firmware function.
+
+return:
+	n.a.
+*******************************************************/
+void update_firmware_release(void)
+{
+	if (fw_entry) {
+		release_firmware(fw_entry);
+	}
+	fw_entry=NULL;
+}
+
+/*******************************************************
+Description:
+	Novatek touchscreen check firmware version function.
+
+return:
+	Executive outcomes. 0---need update. 1---need not
+	update.
+*******************************************************/
+int32_t Check_FW_Ver(void)
+{
+	uint8_t buf[16] = {0};
+	int32_t ret = 0;
+
+	//write i2c index to EVENT BUF ADDR
+	ret = nvt_set_page(I2C_BLDR_Address, ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_FWINFO);
+	if (ret < 0) {
+		NVT_ERR("i2c write error!(%d)\n", ret);
+		return ret;
+	}
+
+	//read Firmware Version
+	buf[0] = EVENT_MAP_FWINFO;
+	buf[1] = 0x00;
+	buf[2] = 0x00;
+	ret = CTP_I2C_READ(ts->client, I2C_BLDR_Address, buf, 3);
+	if (ret < 0) {
+		NVT_ERR("i2c read error!(%d)\n", ret);
+		return ret;
+	}
+
+	NVT_LOG("IC FW Ver = 0x%02X, FW Ver Bar = 0x%02X\n", buf[1], buf[2]);
+	NVT_LOG("Bin FW Ver = 0x%02X, FW ver Bar = 0x%02X\n",
+			fw_entry->data[FW_BIN_VER_OFFSET], fw_entry->data[FW_BIN_VER_BAR_OFFSET]);
+
+	// check IC FW_VER + FW_VER_BAR equals 0xFF or not, need to update if not
+	if ((buf[1] + buf[2]) != 0xFF) {
+		NVT_ERR("IC FW_VER + FW_VER_BAR not equals to 0xFF!\n");
+		return 0;
+	}
+
+	// compare IC and binary FW version
+	if (buf[1] > fw_entry->data[FW_BIN_VER_OFFSET])
+		return 1;
+	else
+		return 0;
+}
+
+/*******************************************************
+Description:
+	Novatek touchscreen resume from deep power down function.
+
+return:
+	Executive outcomes. 0---succeed. negative---failed.
+*******************************************************/
+int32_t Resume_PD(void)
+{
+	uint8_t buf[8] = {0};
+	int32_t ret = 0;
+	int32_t retry = 0;
+
+	// Resume Command
+	buf[0] = 0x00;
+	buf[1] = 0xAB;
+	ret = CTP_I2C_WRITE(ts->client, I2C_HW_Address, buf, 2);
+	if (ret < 0) {
+		NVT_ERR("Write Enable error!!(%d)\n", ret);
+		return ret;
+	}
+
+	// Check 0xAA (Resume Command)
+	retry = 0;
+	while(1) {
+		msleep(1);
+		buf[0] = 0x00;
+		buf[1] = 0x00;
+		ret = CTP_I2C_READ(ts->client, I2C_HW_Address, buf, 2);
+		if (ret < 0) {
+			NVT_ERR("Check 0xAA (Resume Command) error!!(%d)\n", ret);
+			return ret;
+		}
+		if (buf[1] == 0xAA) {
+			break;
+		}
+		retry++;
+		if (unlikely(retry > 20)) {
+			NVT_ERR("Check 0xAA (Resume Command) error!! status=0x%02X\n", buf[1]);
+			return -1;
+		}
+	}
+	msleep(10);
+
+	NVT_LOG("Resume PD OK\n");
+	return 0;
+}
+
+/*******************************************************
+Description:
+	Novatek touchscreen check firmware checksum function.
+
+return:
+	Executive outcomes. 0---checksum not match.
+	1---checksum match. -1--- checksum read failed.
+*******************************************************/
+int32_t Check_CheckSum(void)
+{
+	uint8_t buf[64] = {0};
+	uint32_t XDATA_Addr = ts->mmap->READ_FLASH_CHECKSUM_ADDR;
+	int32_t ret = 0;
+	int32_t i = 0;
+	int32_t k = 0;
+	uint16_t WR_Filechksum[BLOCK_64KB_NUM] = {0};
+	uint16_t RD_Filechksum[BLOCK_64KB_NUM] = {0};
+	size_t len_in_blk = 0;
+	int32_t retry = 0;
+
+	if (Resume_PD()) {
+		NVT_ERR("Resume PD error!!\n");
+		return -1;
+	}
+
+	for (i = 0; i < BLOCK_64KB_NUM; i++) {
+		if (fw_need_write_size > (i * SIZE_64KB)) {
+			// Calculate WR_Filechksum of each 64KB block
+			len_in_blk = min(fw_need_write_size - i * SIZE_64KB, (size_t)SIZE_64KB);
+			WR_Filechksum[i] = i + 0x00 + 0x00 + (((len_in_blk - 1) >> 8) & 0xFF) + ((len_in_blk - 1) & 0xFF);
+			for (k = 0; k < len_in_blk; k++) {
+				WR_Filechksum[i] += fw_entry->data[k + i * SIZE_64KB];
+			}
+			WR_Filechksum[i] = 65535 - WR_Filechksum[i] + 1;
+
+			// Fast Read Command
+			buf[0] = 0x00;
+			buf[1] = 0x07;
+			buf[2] = i;
+			buf[3] = 0x00;
+			buf[4] = 0x00;
+			buf[5] = ((len_in_blk - 1) >> 8) & 0xFF;
+			buf[6] = (len_in_blk - 1) & 0xFF;
+			ret = CTP_I2C_WRITE(ts->client, I2C_HW_Address, buf, 7);
+			if (ret < 0) {
+				NVT_ERR("Fast Read Command error!!(%d)\n", ret);
+				return ret;
+			}
+			// Check 0xAA (Fast Read Command)
+			retry = 0;
+			while (1) {
+				msleep(80);
+				buf[0] = 0x00;
+				buf[1] = 0x00;
+				ret = CTP_I2C_READ(ts->client, I2C_HW_Address, buf, 2);
+				if (ret < 0) {
+					NVT_ERR("Check 0xAA (Fast Read Command) error!!(%d)\n", ret);
+					return ret;
+				}
+				if (buf[1] == 0xAA) {
+					break;
+				}
+				retry++;
+				if (unlikely(retry > 5)) {
+					NVT_ERR("Check 0xAA (Fast Read Command) failed, buf[1]=0x%02X, retry=%d\n", buf[1], retry);
+					return -1;
+				}
+			}
+			// Read Checksum (write addr high byte & middle byte)
+			ret = nvt_set_page(I2C_BLDR_Address, XDATA_Addr);
+			if (ret < 0) {
+				NVT_ERR("Read Checksum (write addr high byte & middle byte) error!!(%d)\n", ret);
+				return ret;
+			}
+			// Read Checksum
+			buf[0] = (XDATA_Addr) & 0xFF;
+			buf[1] = 0x00;
+			buf[2] = 0x00;
+			ret = CTP_I2C_READ(ts->client, I2C_BLDR_Address, buf, 3);
+			if (ret < 0) {
+				NVT_ERR("Read Checksum error!!(%d)\n", ret);
+				return ret;
+			}
+
+			RD_Filechksum[i] = (uint16_t)((buf[2] << 8) | buf[1]);
+			if (WR_Filechksum[i] != RD_Filechksum[i]) {
+				NVT_ERR("RD_Filechksum[%d]=0x%04X, WR_Filechksum[%d]=0x%04X\n", i, RD_Filechksum[i], i, WR_Filechksum[i]);
+				NVT_ERR("firmware checksum not match!!\n");
+				return 0;
+			}
+		}
+	}
+
+	NVT_LOG("firmware checksum match\n");
+	return 1;
+}
+
+/*******************************************************
+Description:
+	Novatek touchscreen initial bootloader and flash
+	block function.
+
+return:
+	Executive outcomes. 0---succeed. negative---failed.
+*******************************************************/
+int32_t Init_BootLoader(void)
+{
+	uint8_t buf[64] = {0};
+	int32_t ret = 0;
+	int32_t retry = 0;
+
+	// SW Reset & Idle
+	nvt_sw_reset_idle();
+
+	// Initiate Flash Block
+	buf[0] = 0x00;
+	buf[1] = 0x00;
+	buf[2] = I2C_FW_Address;
+	ret = CTP_I2C_WRITE(ts->client, I2C_HW_Address, buf, 3);
+	if (ret < 0) {
+		NVT_ERR("Inittial Flash Block error!!(%d)\n", ret);
+		return ret;
+	}
+
+	// Check 0xAA (Initiate Flash Block)
+	retry = 0;
+	while(1) {
+		msleep(1);
+		buf[0] = 0x00;
+		buf[1] = 0x00;
+		ret = CTP_I2C_READ(ts->client, I2C_HW_Address, buf, 2);
+		if (ret < 0) {
+			NVT_ERR("Check 0xAA (Inittial Flash Block) error!!(%d)\n", ret);
+			return ret;
+		}
+		if (buf[1] == 0xAA) {
+			break;
+		}
+		retry++;
+		if (unlikely(retry > 20)) {
+			NVT_ERR("Check 0xAA (Inittial Flash Block) error!! status=0x%02X\n", buf[1]);
+			return -1;
+		}
+	}
+
+	NVT_LOG("Init OK \n");
+	msleep(20);
+
+	return 0;
+}
+
+/*******************************************************
+Description:
+	Novatek touchscreen erase flash sectors function.
+
+return:
+	Executive outcomes. 0---succeed. negative---failed.
+*******************************************************/
+int32_t Erase_Flash(void)
+{
+	uint8_t buf[64] = {0};
+	int32_t ret = 0;
+	int32_t count = 0;
+	int32_t i = 0;
+	int32_t Flash_Address = 0;
+	int32_t retry = 0;
+
+	// Write Enable
+	buf[0] = 0x00;
+	buf[1] = 0x06;
+	ret = CTP_I2C_WRITE(ts->client, I2C_HW_Address, buf, 2);
+	if (ret < 0) {
+		NVT_ERR("Write Enable (for Write Status Register) error!!(%d)\n", ret);
+		return ret;
+	}
+	// Check 0xAA (Write Enable)
+	retry = 0;
+	while (1) {
+		msleep(1);
+		buf[0] = 0x00;
+		buf[1] = 0x00;
+		ret = CTP_I2C_READ(ts->client, I2C_HW_Address, buf, 2);
+		if (ret < 0) {
+			NVT_ERR("Check 0xAA (Write Enable for Write Status Register) error!!(%d)\n", ret);
+			return ret;
+		}
+		if (buf[1] == 0xAA) {
+			break;
+		}
+		retry++;
+		if (unlikely(retry > 20)) {
+			NVT_ERR("Check 0xAA (Write Enable for Write Status Register) error!! status=0x%02X\n", buf[1]);
+			return -1;
+		}
+	}
+
+	// Write Status Register
+	buf[0] = 0x00;
+	buf[1] = 0x01;
+	buf[2] = 0x00;
+	ret = CTP_I2C_WRITE(ts->client, I2C_HW_Address, buf, 3);
+	if (ret < 0) {
+		NVT_ERR("Write Status Register error!!(%d)\n", ret);
+		return ret;
+	}
+	// Check 0xAA (Write Status Register)
+	retry = 0;
+	while (1) {
+		msleep(1);
+		buf[0] = 0x00;
+		buf[1] = 0x00;
+		ret = CTP_I2C_READ(ts->client, I2C_HW_Address, buf, 2);
+		if (ret < 0) {
+			NVT_ERR("Check 0xAA (Write Status Register) error!!(%d)\n", ret);
+			return ret;
+		}
+		if (buf[1] == 0xAA) {
+			break;
+		}
+		retry++;
+		if (unlikely(retry > 20)) {
+			NVT_ERR("Check 0xAA (Write Status Register) error!! status=0x%02X\n", buf[1]);
+			return -1;
+		}
+	}
+
+	// Read Status
+	retry = 0;
+	while (1) {
+		msleep(5);
+		buf[0] = 0x00;
+		buf[1] = 0x05;
+		ret = CTP_I2C_WRITE(ts->client, I2C_HW_Address, buf, 2);
+		if (ret < 0) {
+			NVT_ERR("Read Status (for Write Status Register) error!!(%d)\n", ret);
+			return ret;
+		}
+
+		// Check 0xAA (Read Status)
+		buf[0] = 0x00;
+		buf[1] = 0x00;
+		buf[2] = 0x00;
+		ret = CTP_I2C_READ(ts->client, I2C_HW_Address, buf, 3);
+		if (ret < 0) {
+			NVT_ERR("Check 0xAA (Read Status for Write Status Register) error!!(%d)\n", ret);
+			return ret;
+		}
+		if ((buf[1] == 0xAA) && (buf[2] == 0x00)) {
+			break;
+		}
+		retry++;
+		if (unlikely(retry > 100)) {
+			NVT_ERR("Check 0xAA (Read Status for Write Status Register) failed, buf[1]=0x%02X, buf[2]=0x%02X, retry=%d\n", buf[1], buf[2], retry);
+			return -1;
+		}
+	}
+
+	if (fw_need_write_size % FLASH_SECTOR_SIZE)
+		count = fw_need_write_size / FLASH_SECTOR_SIZE + 1;
+	else
+		count = fw_need_write_size / FLASH_SECTOR_SIZE;
+
+	for(i = 0; i < count; i++) {
+		// Write Enable
+		buf[0] = 0x00;
+		buf[1] = 0x06;
+		ret = CTP_I2C_WRITE(ts->client, I2C_HW_Address, buf, 2);
+		if (ret < 0) {
+			NVT_ERR("Write Enable error!!(%d,%d)\n", ret, i);
+			return ret;
+		}
+		// Check 0xAA (Write Enable)
+		retry = 0;
+		while (1) {
+			msleep(1);
+			buf[0] = 0x00;
+			buf[1] = 0x00;
+			ret = CTP_I2C_READ(ts->client, I2C_HW_Address, buf, 2);
+			if (ret < 0) {
+				NVT_ERR("Check 0xAA (Write Enable) error!!(%d,%d)\n", ret, i);
+				return ret;
+			}
+			if (buf[1] == 0xAA) {
+				break;
+			}
+			retry++;
+			if (unlikely(retry > 20)) {
+				NVT_ERR("Check 0xAA (Write Enable) error!! status=0x%02X\n", buf[1]);
+				return -1;
+			}
+		}
+
+		Flash_Address = i * FLASH_SECTOR_SIZE;
+
+		// Sector Erase
+		buf[0] = 0x00;
+		buf[1] = 0x20;    // Command : Sector Erase
+		buf[2] = ((Flash_Address >> 16) & 0xFF);
+		buf[3] = ((Flash_Address >> 8) & 0xFF);
+		buf[4] = (Flash_Address & 0xFF);
+		ret = CTP_I2C_WRITE(ts->client, I2C_HW_Address, buf, 5);
+		if (ret < 0) {
+			NVT_ERR("Sector Erase error!!(%d,%d)\n", ret, i);
+			return ret;
+		}
+		// Check 0xAA (Sector Erase)
+		retry = 0;
+		while (1) {
+			msleep(1);
+			buf[0] = 0x00;
+			buf[1] = 0x00;
+			ret = CTP_I2C_READ(ts->client, I2C_HW_Address, buf, 2);
+			if (ret < 0) {
+				NVT_ERR("Check 0xAA (Sector Erase) error!!(%d,%d)\n", ret, i);
+				return ret;
+			}
+			if (buf[1] == 0xAA) {
+				break;
+			}
+			retry++;
+			if (unlikely(retry > 20)) {
+				NVT_ERR("Check 0xAA (Sector Erase) failed, buf[1]=0x%02X, retry=%d\n", buf[1], retry);
+				return -1;
+			}
+		}
+
+		// Read Status
+		retry = 0;
+		while (1) {
+			msleep(5);
+			buf[0] = 0x00;
+			buf[1] = 0x05;
+			ret = CTP_I2C_WRITE(ts->client, I2C_HW_Address, buf, 2);
+			if (ret < 0) {
+				NVT_ERR("Read Status error!!(%d,%d)\n", ret, i);
+				return ret;
+			}
+
+			// Check 0xAA (Read Status)
+			buf[0] = 0x00;
+			buf[1] = 0x00;
+			buf[2] = 0x00;
+			ret = CTP_I2C_READ(ts->client, I2C_HW_Address, buf, 3);
+			if (ret < 0) {
+				NVT_ERR("Check 0xAA (Read Status) error!!(%d,%d)\n", ret, i);
+				return ret;
+			}
+			if ((buf[1] == 0xAA) && (buf[2] == 0x00)) {
+				break;
+			}
+			retry++;
+			if (unlikely(retry > 100)) {
+				NVT_ERR("Check 0xAA (Read Status) failed, buf[1]=0x%02X, buf[2]=0x%02X, retry=%d\n", buf[1], buf[2], retry);
+				return -1;
+			}
+		}
+	}
+
+	NVT_LOG("Erase OK \n");
+	return 0;
+}
+
+/*******************************************************
+Description:
+	Novatek touchscreen write flash sectors function.
+
+return:
+	Executive outcomes. 0---succeed. negative---failed.
+*******************************************************/
+int32_t Write_Flash(void)
+{
+	uint8_t buf[64] = {0};
+	uint32_t XDATA_Addr = ts->mmap->RW_FLASH_DATA_ADDR;
+	uint32_t Flash_Address = 0;
+	int32_t i = 0, j = 0, k = 0;
+	uint8_t tmpvalue = 0;
+	int32_t count = 0;
+	int32_t ret = 0;
+	int32_t retry = 0;
+	int32_t percent = 0;
+	int32_t previous_percent = -1;
+
+	// change I2C buffer index
+	ret = nvt_set_page(I2C_BLDR_Address, XDATA_Addr);
+	if (ret < 0) {
+		NVT_ERR("change I2C buffer index error!!(%d)\n", ret);
+		return ret;
+	}
+
+	if (fw_need_write_size % 256)
+		count = fw_need_write_size / 256 + 1;
+	else
+		count = fw_need_write_size / 256;
+
+	for (i = 0; i < count; i++) {
+		Flash_Address = i * 256;
+
+		// Write Enable
+		buf[0] = 0x00;
+		buf[1] = 0x06;
+		ret = CTP_I2C_WRITE(ts->client, I2C_HW_Address, buf, 2);
+		if (ret < 0) {
+			NVT_ERR("Write Enable error!!(%d)\n", ret);
+			return ret;
+		}
+		// Check 0xAA (Write Enable)
+		retry = 0;
+		while (1) {
+			udelay(100);
+			buf[0] = 0x00;
+			buf[1] = 0x00;
+			ret = CTP_I2C_READ(ts->client, I2C_HW_Address, buf, 2);
+			if (ret < 0) {
+				NVT_ERR("Check 0xAA (Write Enable) error!!(%d,%d)\n", ret, i);
+				return ret;
+			}
+			if (buf[1] == 0xAA) {
+				break;
+			}
+			retry++;
+			if (unlikely(retry > 20)) {
+				NVT_ERR("Check 0xAA (Write Enable) error!! status=0x%02X\n", buf[1]);
+				return -1;
+			}
+		}
+
+		// Write Page : 256 bytes
+		for (j = 0; j < min(fw_need_write_size - i * 256, (size_t)256); j += 32) {
+			buf[0] = (XDATA_Addr + j) & 0xFF;
+			for (k = 0; k < 32; k++) {
+				buf[1 + k] = fw_entry->data[Flash_Address + j + k];
+			}
+			ret = CTP_I2C_WRITE(ts->client, I2C_BLDR_Address, buf, 33);
+			if (ret < 0) {
+				NVT_ERR("Write Page error!!(%d), j=%d\n", ret, j);
+				return ret;
+			}
+		}
+		if (fw_need_write_size - Flash_Address >= 256)
+			tmpvalue=(Flash_Address >> 16) + ((Flash_Address >> 8) & 0xFF) + (Flash_Address & 0xFF) + 0x00 + (255);
+		else
+			tmpvalue=(Flash_Address >> 16) + ((Flash_Address >> 8) & 0xFF) + (Flash_Address & 0xFF) + 0x00 + (fw_need_write_size - Flash_Address - 1);
+
+		for (k = 0; k < min(fw_need_write_size - Flash_Address, (size_t)256); k++)
+			tmpvalue += fw_entry->data[Flash_Address + k];
+
+		tmpvalue = 255 - tmpvalue + 1;
+
+		// Page Program
+		buf[0] = 0x00;
+		buf[1] = 0x02;
+		buf[2] = ((Flash_Address >> 16) & 0xFF);
+		buf[3] = ((Flash_Address >> 8) & 0xFF);
+		buf[4] = (Flash_Address & 0xFF);
+		buf[5] = 0x00;
+		buf[6] = min(fw_need_write_size - Flash_Address, (size_t)256) - 1;
+		buf[7] = tmpvalue;
+		ret = CTP_I2C_WRITE(ts->client, I2C_HW_Address, buf, 8);
+		if (ret < 0) {
+			NVT_ERR("Page Program error!!(%d), i=%d\n", ret, i);
+			return ret;
+		}
+		// Check 0xAA (Page Program)
+		retry = 0;
+		while (1) {
+			msleep(1);
+			buf[0] = 0x00;
+			buf[1] = 0x00;
+			ret = CTP_I2C_READ(ts->client, I2C_HW_Address, buf, 2);
+			if (ret < 0) {
+				NVT_ERR("Page Program error!!(%d)\n", ret);
+				return ret;
+			}
+			if (buf[1] == 0xAA || buf[1] == 0xEA) {
+				break;
+			}
+			retry++;
+			if (unlikely(retry > 20)) {
+				NVT_ERR("Check 0xAA (Page Program) failed, buf[1]=0x%02X, retry=%d\n", buf[1], retry);
+				return -1;
+			}
+		}
+		if (buf[1] == 0xEA) {
+			NVT_ERR("Page Program error!! i=%d\n", i);
+			return -3;
+		}
+
+		// Read Status
+		retry = 0;
+		while (1) {
+			msleep(5);
+			buf[0] = 0x00;
+			buf[1] = 0x05;
+			ret = CTP_I2C_WRITE(ts->client, I2C_HW_Address, buf, 2);
+			if (ret < 0) {
+				NVT_ERR("Read Status error!!(%d)\n", ret);
+				return ret;
+			}
+
+			// Check 0xAA (Read Status)
+			buf[0] = 0x00;
+			buf[1] = 0x00;
+			buf[2] = 0x00;
+			ret = CTP_I2C_READ(ts->client, I2C_HW_Address, buf, 3);
+			if (ret < 0) {
+				NVT_ERR("Check 0xAA (Read Status) error!!(%d)\n", ret);
+				return ret;
+			}
+			if (((buf[1] == 0xAA) && (buf[2] == 0x00)) || (buf[1] == 0xEA)) {
+				break;
+			}
+			retry++;
+			if (unlikely(retry > 100)) {
+				NVT_ERR("Check 0xAA (Read Status) failed, buf[1]=0x%02X, buf[2]=0x%02X, retry=%d\n", buf[1], buf[2], retry);
+				return -1;
+			}
+		}
+		if (buf[1] == 0xEA) {
+			NVT_ERR("Page Program error!! i=%d\n", i);
+			return -4;
+		}
+
+		percent = ((i + 1) * 100) / count;
+		if (((percent % 10) == 0) && (percent != previous_percent)) {
+			NVT_LOG("Programming...%2d%%\n", percent);
+			previous_percent = percent;
+		}
+	}
+
+	NVT_LOG("Program OK         \n");
+	return 0;
+}
+
+/*******************************************************
+Description:
+	Novatek touchscreen verify checksum of written
+	flash function.
+
+return:
+	Executive outcomes. 0---succeed. negative---failed.
+*******************************************************/
+int32_t Verify_Flash(void)
+{
+	uint8_t buf[64] = {0};
+	uint32_t XDATA_Addr = ts->mmap->READ_FLASH_CHECKSUM_ADDR;
+	int32_t ret = 0;
+	int32_t i = 0;
+	int32_t k = 0;
+	uint16_t WR_Filechksum[BLOCK_64KB_NUM] = {0};
+	uint16_t RD_Filechksum[BLOCK_64KB_NUM] = {0};
+	size_t len_in_blk = 0;
+	int32_t retry = 0;
+
+	for (i = 0; i < BLOCK_64KB_NUM; i++) {
+		if (fw_need_write_size > (i * SIZE_64KB)) {
+			// Calculate WR_Filechksum of each 64KB block
+			len_in_blk = min(fw_need_write_size - i * SIZE_64KB, (size_t)SIZE_64KB);
+			WR_Filechksum[i] = i + 0x00 + 0x00 + (((len_in_blk - 1) >> 8) & 0xFF) + ((len_in_blk - 1) & 0xFF);
+			for (k = 0; k < len_in_blk; k++) {
+				WR_Filechksum[i] += fw_entry->data[k + i * SIZE_64KB];
+			}
+			WR_Filechksum[i] = 65535 - WR_Filechksum[i] + 1;
+
+			// Fast Read Command
+			buf[0] = 0x00;
+			buf[1] = 0x07;
+			buf[2] = i;
+			buf[3] = 0x00;
+			buf[4] = 0x00;
+			buf[5] = ((len_in_blk - 1) >> 8) & 0xFF;
+			buf[6] = (len_in_blk - 1) & 0xFF;
+			ret = CTP_I2C_WRITE(ts->client, I2C_HW_Address, buf, 7);
+			if (ret < 0) {
+				NVT_ERR("Fast Read Command error!!(%d)\n", ret);
+				return ret;
+			}
+			// Check 0xAA (Fast Read Command)
+			retry = 0;
+			while (1) {
+				msleep(80);
+				buf[0] = 0x00;
+				buf[1] = 0x00;
+				ret = CTP_I2C_READ(ts->client, I2C_HW_Address, buf, 2);
+				if (ret < 0) {
+					NVT_ERR("Check 0xAA (Fast Read Command) error!!(%d)\n", ret);
+					return ret;
+				}
+				if (buf[1] == 0xAA) {
+					break;
+				}
+				retry++;
+				if (unlikely(retry > 5)) {
+					NVT_ERR("Check 0xAA (Fast Read Command) failed, buf[1]=0x%02X, retry=%d\n", buf[1], retry);
+					return -1;
+				}
+			}
+			// Read Checksum (write addr high byte & middle byte)
+			ret = nvt_set_page(I2C_BLDR_Address, XDATA_Addr);
+			if (ret < 0) {
+				NVT_ERR("Read Checksum (write addr high byte & middle byte) error!!(%d)\n", ret);
+				return ret;
+			}
+			// Read Checksum
+			buf[0] = (XDATA_Addr) & 0xFF;
+			buf[1] = 0x00;
+			buf[2] = 0x00;
+			ret = CTP_I2C_READ(ts->client, I2C_BLDR_Address, buf, 3);
+			if (ret < 0) {
+				NVT_ERR("Read Checksum error!!(%d)\n", ret);
+				return ret;
+			}
+
+			RD_Filechksum[i] = (uint16_t)((buf[2] << 8) | buf[1]);
+			if (WR_Filechksum[i] != RD_Filechksum[i]) {
+				NVT_ERR("Verify Fail%d!!\n", i);
+				NVT_ERR("RD_Filechksum[%d]=0x%04X, WR_Filechksum[%d]=0x%04X\n", i, RD_Filechksum[i], i, WR_Filechksum[i]);
+				return -1;
+			}
+		}
+	}
+
+	NVT_LOG("Verify OK \n");
+	return 0;
+}
+
+/*******************************************************
+Description:
+	Novatek touchscreen update firmware function.
+
+return:
+	Executive outcomes. 0---succeed. negative---failed.
+*******************************************************/
+int32_t Update_Firmware(void)
+{
+	int32_t ret = 0;
+
+	//---Stop CRC check to prevent IC auto reboot---
+	nvt_stop_crc_reboot();
+
+	// Step 1 : initial bootloader
+	ret = Init_BootLoader();
+	if (ret) {
+		return ret;
+	}
+
+	// Step 2 : Resume PD
+	ret = Resume_PD();
+	if (ret) {
+		return ret;
+	}
+
+	// Step 3 : Erase
+	ret = Erase_Flash();
+	if (ret) {
+		return ret;
+	}
+
+	// Step 4 : Program
+	ret = Write_Flash();
+	if (ret) {
+		return ret;
+	}
+
+	// Step 5 : Verify
+	ret = Verify_Flash();
+	if (ret) {
+		return ret;
+	}
+
+	//Step 6 : Bootloader Reset
+	nvt_bootloader_reset();
+	nvt_check_fw_reset_state(RESET_STATE_INIT);
+	nvt_get_fw_info();
+
+	return ret;
+}
+
+/*******************************************************
+Description:
+	Novatek touchscreen check flash end flag function.
+
+return:
+	Executive outcomes. 0---succeed. 1,negative---failed.
+*******************************************************/
+int32_t nvt_check_flash_end_flag(void)
+{
+	uint8_t buf[8] = {0};
+	uint8_t nvt_end_flag[NVT_FLASH_END_FLAG_LEN + 1] = {0};
+	int32_t ret = 0;
+
+	// Step 1 : initial bootloader
+	ret = Init_BootLoader();
+	if (ret) {
+		return ret;
+	}
+
+	// Step 2 : Resume PD
+	ret = Resume_PD();
+	if (ret) {
+		return ret;
+	}
+
+	// Step 3 : unlock
+	buf[0] = 0x00;
+	buf[1] = 0x35;
+	ret = CTP_I2C_WRITE(ts->client, I2C_HW_Address, buf, 2);
+	if (ret < 0) {
+		NVT_ERR("write unlock error!!(%d)\n", ret);
+		return ret;
+	}
+	msleep(10);
+
+	//Step 4 : Flash Read Command
+	buf[0] = 0x00;
+	buf[1] = 0x03;
+	buf[2] = (NVT_FLASH_END_FLAG_ADDR >> 16) & 0xFF; //Addr_H
+	buf[3] = (NVT_FLASH_END_FLAG_ADDR >> 8) & 0xFF; //Addr_M
+	buf[4] = NVT_FLASH_END_FLAG_ADDR & 0xFF; //Addr_L
+	buf[5] = (NVT_FLASH_END_FLAG_LEN >> 8) & 0xFF; //Len_H
+	buf[6] = NVT_FLASH_END_FLAG_LEN & 0xFF; //Len_L
+	ret = CTP_I2C_WRITE(ts->client, I2C_HW_Address, buf, 7);
+	if (ret < 0) {
+		NVT_ERR("write Read Command error!!(%d)\n", ret);
+		return ret;
+	}
+	msleep(10);
+
+	// Check 0xAA (Read Command)
+	buf[0] = 0x00;
+	buf[1] = 0x00;
+	ret = CTP_I2C_READ(ts->client, I2C_HW_Address, buf, 2);
+	if (ret < 0) {
+		NVT_ERR("Check 0xAA (Read Command) error!!(%d)\n", ret);
+		return ret;
+	}
+	if (buf[1] != 0xAA) {
+		NVT_ERR("Check 0xAA (Read Command) error!! status=0x%02X\n", buf[1]);
+		return -1;
+	}
+
+	msleep(10);
+
+	//Step 5 : Read Flash Data
+	ret = nvt_set_page(I2C_BLDR_Address, ts->mmap->READ_FLASH_CHECKSUM_ADDR);
+	if (ret < 0) {
+		NVT_ERR("change index error!! (%d)\n", ret);
+		return ret;
+	}
+	msleep(10);
+
+	// Read Back
+	buf[0] = ts->mmap->READ_FLASH_CHECKSUM_ADDR & 0xFF;
+	ret = CTP_I2C_READ(ts->client, I2C_BLDR_Address, buf, 6);
+	if (ret < 0) {
+		NVT_ERR("Read Back error!! (%d)\n", ret);
+		return ret;
+	}
+
+	//buf[3:5] => NVT End Flag
+	strlcpy(nvt_end_flag, &buf[3], sizeof(nvt_end_flag));
+	NVT_LOG("nvt_end_flag=%s (%02X %02X %02X)\n", nvt_end_flag, buf[3], buf[4], buf[5]);
+
+	if ((memcmp(nvt_end_flag, "NVT", NVT_FLASH_END_FLAG_LEN) == 0) ||
+		(memcmp(nvt_end_flag, "MOD", NVT_FLASH_END_FLAG_LEN) == 0)) {
+		return 0;
+	} else {
+		NVT_ERR("\"NVT\" end flag not found!\n");
+		return 1;
+	}
+}
+
+/*******************************************************
+Description:
+	Novatek touchscreen update firmware when booting
+	function.
+
+return:
+	n.a.
+*******************************************************/
+void Boot_Update_Firmware(struct work_struct *work)
+{
+	int32_t ret = 0;
+
+	char firmware_name[256] = "";
+
+	snprintf(firmware_name, sizeof(firmware_name),
+			BOOT_UPDATE_FIRMWARE_NAME);
+
+	// request bin file in "/etc/firmware"
+	ret = update_firmware_request(firmware_name);
+	if (ret) {
+		NVT_ERR("update_firmware_request failed. (%d)\n", ret);
+		return;
+	}
+
+	mutex_lock(&ts->lock);
+
+#if NVT_TOUCH_ESD_PROTECT
+	nvt_esd_check_enable(false);
+#endif /* #if NVT_TOUCH_ESD_PROTECT */
+
+	nvt_sw_reset_idle();
+
+	ret = Check_CheckSum();
+
+	if (ret < 0) {	// read firmware checksum failed
+		NVT_ERR("read firmware checksum failed\n");
+		Update_Firmware();
+	} else if ((ret == 0) && (Check_FW_Ver() == 0)) { // (fw checksum not match) && (bin fw version >= ic fw version)
+		NVT_LOG("firmware version not match\n");
+		Update_Firmware();
+	} else if (nvt_check_flash_end_flag()) {
+		NVT_LOG("check flash end flag failed\n");
+		Update_Firmware();
+	} else {
+		// Bootloader Reset
+		nvt_bootloader_reset();
+		ret = nvt_check_fw_reset_state(RESET_STATE_INIT);
+		if (ret) {
+			NVT_LOG("check fw reset state failed\n");
+			Update_Firmware();
+		}
+	}
+
+	mutex_unlock(&ts->lock);
+
+	update_firmware_release();
+}
+#endif /* BOOT_UPDATE_FIRMWARE */
+
+#else  /* NT36XXX_SPI */
+
+#include <linux/firmware.h>
+#include <linux/gpio.h>
+
+#include "nt36xxx.h"
+
+#if NVT_SPI_BOOT_UPDATE_FIRMWARE
+
+#define SIZE_4KB 4096
+#define FLASH_SECTOR_SIZE SIZE_4KB
+#define FW_BIN_VER_OFFSET (nvt_spi_fw_need_write_size - SIZE_4KB)
+#define FW_BIN_VER_BAR_OFFSET (FW_BIN_VER_OFFSET + 1)
+#define NVT_FLASH_END_FLAG_LEN 3
+#define NVT_FLASH_END_FLAG_ADDR (nvt_spi_fw_need_write_size - NVT_FLASH_END_FLAG_LEN)
+
+#define NVT_DUMP_PARTITION      (0)
+#define NVT_DUMP_PARTITION_LEN  (1024)
+#define NVT_DUMP_PARTITION_PATH "/data/local/tmp"
+
+static ktime_t nvt_spi_start, nvt_spi_end;
+static const struct firmware *nvt_spi_fw_entry;
+static size_t nvt_spi_fw_need_write_size;
+static uint8_t *nvt_spi_fwbuf;
+
+struct nvt_spi_bin_map_t {
+	char name[12];
+	uint32_t BIN_addr;
+	uint32_t SRAM_addr;
+	uint32_t size;
+	uint32_t crc;
+};
+
+static struct nvt_spi_bin_map_t *nvt_spi_bin_map;
+
+static int32_t nvt_spi_get_fw_need_write_size(const struct firmware *fw_entry)
+{
+	int32_t i = 0;
+	int32_t total_sectors_to_check = 0;
+
+	total_sectors_to_check = fw_entry->size / FLASH_SECTOR_SIZE;
+	/* printk("total_sectors_to_check = %d\n", total_sectors_to_check); */
+
+	for (i = total_sectors_to_check; i > 0; i--) {
+		// printk("current end flag address checked = 0x%X\n",
+		//			i * FLASH_SECTOR_SIZE - NVT_FLASH_END_FLAG_LEN);
+		/* check if there is end flag "NVT" at the end of this sector */
+		if (memcmp(&fw_entry->data[i * FLASH_SECTOR_SIZE - NVT_FLASH_END_FLAG_LEN],
+			"NVT", NVT_FLASH_END_FLAG_LEN) == 0) {
+			nvt_spi_fw_need_write_size = i * FLASH_SECTOR_SIZE;
+			NVT_LOG("fw_need_write_size = %zu(0x%zx), NVT end flag\n",
+				nvt_spi_fw_need_write_size, nvt_spi_fw_need_write_size);
+			return 0;
+		}
+
+		/* check if there is end flag "MOD" at the end of this sector */
+		if (memcmp(&fw_entry->data[i * FLASH_SECTOR_SIZE - NVT_FLASH_END_FLAG_LEN],
+			"MOD", NVT_FLASH_END_FLAG_LEN) == 0) {
+			nvt_spi_fw_need_write_size = i * FLASH_SECTOR_SIZE;
+			NVT_LOG("fw_need_write_size = %zu(0x%zx), MOD end flag\n",
+				nvt_spi_fw_need_write_size, nvt_spi_fw_need_write_size);
+			return 0;
+		}
+	}
+
+	NVT_ERR("end flag \"NVT\" \"MOD\" not found!\n");
+	return -EINVAL;
+}
+
+/*
+ *******************************************************
+ * Description:
+ *	Novatek touchscreen init variable and allocate buffer
+ * for download firmware function.
+ *
+ * return:
+ *	n.a.
+ ******************************************************
+ */
+static int32_t nvt_spi_download_init(void)
+{
+	uint8_t *buf;
+	/* allocate buffer for transfer firmware */
+	//NVT_LOG("NVT_TRANSFER_LEN = 0x%06X\n", NVT_SPI_TRANSFER_LEN);
+
+	if (nvt_spi_fwbuf == NULL) {
+		buf = kzalloc((NVT_SPI_TRANSFER_LEN + 1 + NVT_SPI_DUMMY_BYTES), GFP_KERNEL);
+		if (buf == NULL) {
+			NVT_ERR("kzalloc for fwbuf failed!\n");
+			return -ENOMEM;
+		}
+		nvt_spi_fwbuf = buf;
+	}
+
+	return 0;
+}
+
+/*
+ ******************************************************
+ * Description:
+ *	Novatek touchscreen checksum function. Calculate bin
+ * file checksum for comparison.
+ *
+ * return:
+ *	n.a.
+ ******************************************************
+ */
+static uint32_t CheckSum(const u8 *data, size_t len)
+{
+	uint32_t i = 0;
+	uint32_t checksum = 0;
+
+	for (i = 0 ; i < len + 1; i++)
+		checksum += data[i];
+
+	checksum += len;
+	checksum = ~checksum + 1;
+
+	return checksum;
+}
+
+static uint32_t byte_to_word(const uint8_t *data)
+{
+	return data[0] + (data[1] << 8) + (data[2] << 16) + (data[3] << 24);
+}
+
+/*
+ ******************************************************
+ * Description:
+ *	Novatek touchscreen parsing bin header function.
+ *
+ * return:
+ *	n.a.
+ ******************************************************
+ */
+static uint32_t nvt_spi_partition;
+static uint8_t nvt_spi_ilm_dlm_num = 2;
+static uint8_t nvt_spi_cascade_2nd_header_info;
+static int32_t nvt_spi_bin_header_parser(const u8 *fwdata, size_t fwsize)
+{
+	uint32_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 find_bin_header = 0;
+	struct nvt_spi_bin_map_t *bin_map = NULL;
+	struct nvt_spi_data_t *ts = nvt_spi_data;
+
+	/* Find the header size */
+	end = fwdata[0] + (fwdata[1] << 8) + (fwdata[2] << 16) + (fwdata[3] << 24);
+
+	/* check cascade next header */
+	nvt_spi_cascade_2nd_header_info = (fwdata[0x20] & 0x02) >> 1;
+	NVT_LOG("cascade_2nd_header_info = %d\n", nvt_spi_cascade_2nd_header_info);
+
+	if (nvt_spi_cascade_2nd_header_info) {
+		pos = 0x30;	// info section start at 0x30 offset
+		while (pos < (end / 2)) {
+			info_sec_num++;
+			pos += 0x10;	/* each header info is 16 bytes */
+		}
+
+		info_sec_num = info_sec_num + 1; //next header section
+	} else {
+		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
+	 */
+	nvt_spi_partition = nvt_spi_ilm_dlm_num + ovly_sec_num + info_sec_num;
+	NVT_LOG("ovly_info=%d, ilm_dlm_num=%d, ovly_sec_num=%d, info_sec_num=%d, partition=%d\n",
+		ovly_info, nvt_spi_ilm_dlm_num, ovly_sec_num, info_sec_num, nvt_spi_partition);
+
+
+	/* allocated memory for header info */
+	bin_map = kzalloc((nvt_spi_partition+1) * sizeof(struct nvt_spi_bin_map_t), GFP_KERNEL);
+	if (bin_map == NULL) {
+		NVT_ERR("kzalloc for bin_map failed!\n");
+		return -ENOMEM;
+	}
+	nvt_spi_bin_map = bin_map;
+
+	for (list = 0; list < nvt_spi_partition; list++) {
+		/*
+		 * [1] parsing ILM & DLM header info
+		 * BIN_addr : SRAM_addr : size (12-bytes)
+		 * crc located at 0x18 & 0x1C
+		 */
+		if (list < nvt_spi_ilm_dlm_num) {
+			bin_map[list].BIN_addr = byte_to_word(&fwdata[0 + list * 12]);
+			bin_map[list].SRAM_addr = byte_to_word(&fwdata[4 + list * 12]);
+			bin_map[list].size = byte_to_word(&fwdata[8 + list * 12]);
+			if (ts->hw_crc)
+				bin_map[list].crc = byte_to_word(&fwdata[0x18 + list * 4]);
+			else { //ts->hw_crc
+				if ((bin_map[list].BIN_addr + bin_map[list].size) < fwsize)
+					bin_map[list].crc = CheckSum(
+							&fwdata[bin_map[list].BIN_addr],
+							bin_map[list].size);
+				else {
+					NVT_ERR("access range (0x%08X to 0x%08X) is too large!\n",
+						bin_map[list].BIN_addr,
+						bin_map[list].BIN_addr + bin_map[list].size);
+					return -EINVAL;
+				}
+			} //ts->hw_crc
+			if (list == 0)
+				snprintf(bin_map[list].name, sizeof(bin_map[list].name), "ILM");
+			else if (list == 1)
+				snprintf(bin_map[list].name, sizeof(bin_map[list].name), "DLM");
+		}
+
+		/*
+		 * [2] parsing others header info
+		 * SRAM_addr : size : BIN_addr : crc (16-bytes)
+		 */
+		if ((list >= nvt_spi_ilm_dlm_num)
+			&& (list < (nvt_spi_ilm_dlm_num + info_sec_num))) {
+
+			if (find_bin_header == 0) {
+				/* others partition located at 0x30 offset */
+				pos = 0x30 + (0x10 * (list - nvt_spi_ilm_dlm_num));
+			} else if (find_bin_header && nvt_spi_cascade_2nd_header_info) {
+				/* cascade 2nd header info */
+				pos = end - 0x10;
+			}
+
+			bin_map[list].SRAM_addr = byte_to_word(&fwdata[pos]);
+			bin_map[list].size = byte_to_word(&fwdata[pos + 4]);
+			bin_map[list].BIN_addr = byte_to_word(&fwdata[pos + 8]);
+			if (ts->hw_crc)
+				bin_map[list].crc = byte_to_word(&fwdata[pos + 12]);
+			else { //ts->hw_crc
+				if ((bin_map[list].BIN_addr + bin_map[list].size) < fwsize)
+					bin_map[list].crc = CheckSum(
+							&fwdata[bin_map[list].BIN_addr],
+							bin_map[list].size);
+				else {
+					NVT_ERR("access range (0x%08X to 0x%08X) is too large!\n",
+						bin_map[list].BIN_addr,
+						bin_map[list].BIN_addr + bin_map[list].size);
+					return -EINVAL;
+				}
+			} //ts->hw_crc
+			/* detect header end to protect parser function */
+			if ((bin_map[list].BIN_addr < end) && (bin_map[list].size != 0)) {
+				snprintf(bin_map[list].name, sizeof(bin_map[list].name),
+					"Header");
+				find_bin_header = 1;
+			} else
+				snprintf(bin_map[list].name, sizeof(bin_map[list].name),
+					"Info-%d", (list - nvt_spi_ilm_dlm_num));
+		}
+
+		/*
+		 * [3] parsing overlay section header info
+		 * SRAM_addr : size : BIN_addr : crc (16-bytes)
+		 */
+		if (list >= (nvt_spi_ilm_dlm_num + info_sec_num)) {
+			/* overlay info located at DLM (list = 1) start addr */
+			pos = bin_map[1].BIN_addr;
+			pos += (0x10 * (list - nvt_spi_ilm_dlm_num - info_sec_num));
+
+			bin_map[list].SRAM_addr = byte_to_word(&fwdata[pos]);
+			bin_map[list].size = byte_to_word(&fwdata[pos + 4]);
+			bin_map[list].BIN_addr = byte_to_word(&fwdata[pos + 8]);
+			if (ts->hw_crc)
+				bin_map[list].crc = byte_to_word(&fwdata[pos + 12]);
+			else { //ts->hw_crc
+				if ((bin_map[list].BIN_addr + bin_map[list].size) < fwsize)
+					bin_map[list].crc = CheckSum(
+								&fwdata[bin_map[list].BIN_addr],
+								bin_map[list].size);
+				else {
+					NVT_ERR("access range (0x%08X to 0x%08X) is too large!\n",
+						bin_map[list].BIN_addr,
+						bin_map[list].BIN_addr + bin_map[list].size);
+					return -EINVAL;
+				}
+			} //ts->hw_crc
+			snprintf(bin_map[list].name, sizeof(bin_map[list].name),
+					"Overlay-%d",
+					(list - nvt_spi_ilm_dlm_num - info_sec_num));
+		}
+
+		/* BIN size error detect */
+		if ((bin_map[list].BIN_addr + bin_map[list].size) > fwsize) {
+			NVT_ERR("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;
+		}
+
+//		NVT_LOG("[%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;
+}
+
+/*
+ *******************************************************
+ * Description:
+ *	Novatek touchscreen release update firmware function.
+ *
+ * return:
+ *	n.a.
+ ******************************************************
+ */
+static void nvt_spi_update_firmware_release(void)
+{
+	if (nvt_spi_fw_entry)
+		release_firmware(nvt_spi_fw_entry);
+
+	nvt_spi_fw_entry = NULL;
+}
+
+/*
+ ******************************************************
+ * Description:
+ *	Novatek touchscreen request update firmware function.
+ *
+ * return:
+ *	Executive outcomes. 0---succeed. -1,-22---failed.
+ ******************************************************
+ */
+static int32_t nvt_spi_update_firmware_request(char *filename)
+{
+	uint8_t retry = 0;
+	int32_t ret = 0;
+	struct nvt_spi_data_t *ts = nvt_spi_data;
+	uint8_t ver;
+
+	if (filename == NULL)
+		return -ENOENT;
+
+	while (1) {
+		NVT_LOG("filename is %s\n", filename);
+
+		ret = request_firmware(&nvt_spi_fw_entry, filename, &ts->client->dev);
+		if (ret) {
+			NVT_ERR("firmware load failed, ret=%d\n", ret);
+			goto request_fail;
+		}
+
+		// check FW need to write size
+		if (nvt_spi_get_fw_need_write_size(nvt_spi_fw_entry)) {
+			NVT_ERR("get fw need to write size fail!\n");
+			ret = -EINVAL;
+			goto invalid;
+		}
+
+		// check if FW version add FW version bar equals 0xFF
+		ver = *(nvt_spi_fw_entry->data + FW_BIN_VER_OFFSET);
+		if (ver + *(nvt_spi_fw_entry->data + FW_BIN_VER_BAR_OFFSET) != 0xFF) {
+			NVT_ERR("bin file FW_VER + FW_VER_BAR should be 0xFF!\n");
+			NVT_ERR("FW_VER=0x%02X, FW_VER_BAR=0x%02X\n",
+					*(nvt_spi_fw_entry->data+FW_BIN_VER_OFFSET),
+					*(nvt_spi_fw_entry->data+FW_BIN_VER_BAR_OFFSET));
+			ret = -ENOEXEC;
+			goto invalid;
+		}
+
+		/* BIN Header Parser */
+		ret = nvt_spi_bin_header_parser(nvt_spi_fw_entry->data, nvt_spi_fw_entry->size);
+		if (ret) {
+			NVT_ERR("bin header parser failed\n");
+			goto invalid;
+		} else
+			break;
+
+invalid:
+		nvt_spi_update_firmware_release();
+		if (!IS_ERR_OR_NULL(nvt_spi_bin_map)) {
+			kfree(nvt_spi_bin_map);
+			nvt_spi_bin_map = NULL;
+		}
+
+request_fail:
+		retry++;
+		if (unlikely(retry > 2)) {
+			NVT_ERR("error, retry=%d\n", retry);
+			break;
+		}
+	}
+
+	return ret;
+}
+
+/*
+ *******************************************************
+ * Description:
+ *	Novatek touchscreen write data to sram function.
+ *
+ * - fwdata   : The buffer is written
+ * - SRAM_addr: The sram destination address
+ * - size     : Number of data bytes in @fwdata being written
+ * - BIN_addr : The transferred data offset of @fwdata
+ *
+ * return:
+ *	Executive outcomes. 0---succeed. else---fail.
+ *******************************************************
+ */
+static int32_t nvt_spi_write_sram(const u8 *fwdata,
+		uint32_t SRAM_addr, uint32_t size, uint32_t BIN_addr)
+{
+	int32_t ret = 0;
+	uint32_t i = 0;
+	uint16_t len = 0;
+	int32_t count = 0;
+
+	if (size % NVT_SPI_TRANSFER_LEN)
+		count = (size / NVT_SPI_TRANSFER_LEN) + 1;
+	else
+		count = (size / NVT_SPI_TRANSFER_LEN);
+
+	for (i = 0 ; i < count ; i++) {
+		len = (size < NVT_SPI_TRANSFER_LEN) ? size : NVT_SPI_TRANSFER_LEN;
+
+		//---set xdata index to start address of SRAM---
+		ret = nvt_spi_set_page(SRAM_addr);
+		if (ret) {
+			NVT_ERR("set page failed, ret = %d\n", ret);
+			return ret;
+		}
+
+		//---write data into SRAM---
+		nvt_spi_fwbuf[0] = SRAM_addr & 0x7F;	//offset
+		memcpy(nvt_spi_fwbuf+1, &fwdata[BIN_addr], len);	//payload
+		ret = nvt_spi_write(nvt_spi_fwbuf, len+1);
+		if (ret) {
+			NVT_ERR("write to sram failed, ret = %d\n", ret);
+			return ret;
+		}
+
+		SRAM_addr += NVT_SPI_TRANSFER_LEN;
+		BIN_addr += NVT_SPI_TRANSFER_LEN;
+		size -= NVT_SPI_TRANSFER_LEN;
+	}
+
+	return ret;
+}
+
+/*
+ *******************************************************
+ * Description:
+ *	Novatek touchscreen nvt_spi_write_firmware function to write
+ *	firmware into each partition.
+ *
+ * return:
+ *	n.a.
+ ******************************************************
+ */
+static int32_t nvt_spi_write_firmware(const u8 *fwdata, size_t fwsize)
+{
+	uint32_t list = 0;
+	char *name;
+	uint32_t BIN_addr, SRAM_addr, size;
+	int32_t ret = 0;
+
+	memset(nvt_spi_fwbuf, 0, (NVT_SPI_TRANSFER_LEN+1));
+
+	for (list = 0; list < nvt_spi_partition; list++) {
+		/* initialize variable */
+		SRAM_addr = nvt_spi_bin_map[list].SRAM_addr;
+		size = nvt_spi_bin_map[list].size;
+		BIN_addr = nvt_spi_bin_map[list].BIN_addr;
+		name = nvt_spi_bin_map[list].name;
+
+//		NVT_LOG("[%d][%s] SRAM (0x%08X), SIZE (0x%08X), BIN (0x%08X)\n",
+//				list, name, SRAM_addr, size, BIN_addr);
+
+		/* Check data size */
+		if ((BIN_addr + size) > fwsize) {
+			NVT_ERR("access range (0x%08X to 0x%08X) is larger than bin size!\n",
+					BIN_addr, BIN_addr + size);
+			ret = -EINVAL;
+			goto out;
+		}
+
+		/* ignore reserved partition (Reserved Partition size is zero) */
+		if (!size)
+			continue;
+		else
+			size = size + 1;
+
+		/* write data to SRAM */
+		ret = nvt_spi_write_sram(fwdata, SRAM_addr, size, BIN_addr);
+		if (ret) {
+			NVT_ERR("sram program failed, ret = %d\n", ret);
+			goto out;
+		}
+	}
+
+out:
+	return ret;
+}
+
+/*
+ *******************************************************
+ * Description:
+ *	Novatek touchscreen check checksum function.
+ * This function will compare file checksum and fw checksum.
+ *
+ * return:
+ *	n.a.
+ *******************************************************
+ */
+static int32_t nvt_spi_check_fw_checksum(void)
+{
+	uint32_t fw_checksum = 0;
+	uint32_t len = nvt_spi_partition * 4;
+	uint32_t list = 0;
+	int32_t ret = 0;
+	struct nvt_spi_data_t *ts = nvt_spi_data;
+
+	memset(nvt_spi_fwbuf, 0, (len+1));
+
+	//---set xdata index to checksum---
+	nvt_spi_set_page(ts->mmap->R_ILM_CHECKSUM_ADDR);
+
+	/* read checksum */
+	nvt_spi_fwbuf[0] = (ts->mmap->R_ILM_CHECKSUM_ADDR) & 0x7F;
+	ret = nvt_spi_read(nvt_spi_fwbuf, len+1);
+	if (ret) {
+		NVT_ERR("Read fw checksum failed\n");
+		return ret;
+	}
+
+	/*
+	 * Compare each checksum from fw
+	 * ILM + DLM + Overlay + Info
+	 * nvt_spi_ilm_dlm_num (ILM & DLM) + ovly_sec_num + info_sec_num
+	 */
+	for (list = 0; list < nvt_spi_partition; list++) {
+		fw_checksum = byte_to_word(&nvt_spi_fwbuf[1+list*4]);
+
+		/* ignore reserved partition (Reserved Partition size is zero) */
+		if (!nvt_spi_bin_map[list].size)
+			continue;
+
+		if (nvt_spi_bin_map[list].crc != fw_checksum) {
+			NVT_ERR("[%d] BIN_checksum=0x%08X, FW_checksum=0x%08X\n",
+				list, nvt_spi_bin_map[list].crc, fw_checksum);
+			ret = -EIO;
+		}
+	}
+
+	return ret;
+}
+
+/*
+ *******************************************************
+ * Description:
+ *	Novatek touchscreen set bootload crc reg bank function.
+ * This function will set hw crc reg before enable crc function.
+ *
+ * return:
+ *	n.a.
+ ******************************************************
+ */
+static void nvt_spi_set_bld_crc_bank(uint32_t DES_ADDR, uint32_t SRAM_ADDR,
+		uint32_t LENGTH_ADDR, uint32_t size,
+		uint32_t G_CHECKSUM_ADDR, uint32_t crc)
+{
+	struct nvt_spi_data_t *ts = nvt_spi_data;
+
+	/* write destination address */
+	nvt_spi_set_page(DES_ADDR);
+	nvt_spi_fwbuf[0] = DES_ADDR & 0x7F;
+	nvt_spi_fwbuf[1] = (SRAM_ADDR) & 0xFF;
+	nvt_spi_fwbuf[2] = (SRAM_ADDR >> 8) & 0xFF;
+	nvt_spi_fwbuf[3] = (SRAM_ADDR >> 16) & 0xFF;
+	nvt_spi_write(nvt_spi_fwbuf, 4);
+
+	/* write length */
+	//nvt_spi_set_page(LENGTH_ADDR);
+	nvt_spi_fwbuf[0] = LENGTH_ADDR & 0x7F;
+	nvt_spi_fwbuf[1] = (size) & 0xFF;
+	nvt_spi_fwbuf[2] = (size >> 8) & 0xFF;
+	nvt_spi_fwbuf[3] = (size >> 16) & 0x01;
+	if (ts->hw_crc == 1)
+		nvt_spi_write(nvt_spi_fwbuf, 3);
+	else if (ts->hw_crc > 1)
+		nvt_spi_write(nvt_spi_fwbuf, 4);
+
+	/* write golden dlm checksum */
+	//nvt_spi_set_page(G_CHECKSUM_ADDR);
+	nvt_spi_fwbuf[0] = G_CHECKSUM_ADDR & 0x7F;
+	nvt_spi_fwbuf[1] = (crc) & 0xFF;
+	nvt_spi_fwbuf[2] = (crc >> 8) & 0xFF;
+	nvt_spi_fwbuf[3] = (crc >> 16) & 0xFF;
+	nvt_spi_fwbuf[4] = (crc >> 24) & 0xFF;
+	nvt_spi_write(nvt_spi_fwbuf, 5);
+}
+
+/*
+ *********************************************************
+ * Description:
+ *	Novatek touchscreen set BLD hw crc function.
+ * This function will set ILM and DLM crc information to register.
+ *
+ * return:
+ *	n.a.
+ *********************************************************
+ */
+static void nvt_spi_set_bld_hw_crc(void)
+{
+	struct nvt_spi_data_t *ts = nvt_spi_data;
+
+	/* [0] ILM */
+	/* write register bank */
+	nvt_spi_set_bld_crc_bank(ts->mmap->ILM_DES_ADDR, nvt_spi_bin_map[0].SRAM_addr,
+			ts->mmap->ILM_LENGTH_ADDR, nvt_spi_bin_map[0].size,
+			ts->mmap->G_ILM_CHECKSUM_ADDR, nvt_spi_bin_map[0].crc);
+
+	/* [1] DLM */
+	/* write register bank */
+	nvt_spi_set_bld_crc_bank(ts->mmap->DLM_DES_ADDR, nvt_spi_bin_map[1].SRAM_addr,
+			ts->mmap->DLM_LENGTH_ADDR, nvt_spi_bin_map[1].size,
+			ts->mmap->G_DLM_CHECKSUM_ADDR, nvt_spi_bin_map[1].crc);
+}
+
+/*
+ *******************************************************
+ * Description:
+ *	Novatek touchscreen read BLD hw crc info function.
+ * This function will check crc results from register.
+ *
+ * return:
+ *	n.a.
+ ******************************************************
+ */
+static void nvt_spi_read_bld_hw_crc(void)
+{
+	uint8_t buf[8] = {0};
+	uint32_t g_crc = 0, r_crc = 0;
+	struct nvt_spi_data_t *ts = nvt_spi_data;
+
+	/* CRC Flag */
+	nvt_spi_set_page(ts->mmap->BLD_ILM_DLM_CRC_ADDR);
+	buf[0] = ts->mmap->BLD_ILM_DLM_CRC_ADDR & 0x7F;
+	buf[1] = 0x00;
+	nvt_spi_read(buf, 2);
+	NVT_ERR("crc_done = %d, ilm_crc_flag = %d, dlm_crc_flag = %d\n",
+			(buf[1] >> 2) & 0x01, (buf[1] >> 0) & 0x01, (buf[1] >> 1) & 0x01);
+
+	/* ILM CRC */
+	nvt_spi_set_page(ts->mmap->G_ILM_CHECKSUM_ADDR);
+	buf[0] = ts->mmap->G_ILM_CHECKSUM_ADDR & 0x7F;
+	buf[1] = 0x00;
+	buf[2] = 0x00;
+	buf[3] = 0x00;
+	buf[4] = 0x00;
+	nvt_spi_read(buf, 5);
+	g_crc = buf[1] | (buf[2] << 8) | (buf[3] << 16) | (buf[4] << 24);
+
+	nvt_spi_set_page(ts->mmap->R_ILM_CHECKSUM_ADDR);
+	buf[0] = ts->mmap->R_ILM_CHECKSUM_ADDR & 0x7F;
+	buf[1] = 0x00;
+	buf[2] = 0x00;
+	buf[3] = 0x00;
+	buf[4] = 0x00;
+	nvt_spi_read(buf, 5);
+	r_crc = buf[1] | (buf[2] << 8) | (buf[3] << 16) | (buf[4] << 24);
+
+	NVT_ERR("ilm: bin crc = 0x%08X, golden = 0x%08X, result = 0x%08X\n",
+			nvt_spi_bin_map[0].crc, g_crc, r_crc);
+
+	/* DLM CRC */
+	nvt_spi_set_page(ts->mmap->G_DLM_CHECKSUM_ADDR);
+	buf[0] = ts->mmap->G_DLM_CHECKSUM_ADDR & 0x7F;
+	buf[1] = 0x00;
+	buf[2] = 0x00;
+	buf[3] = 0x00;
+	buf[4] = 0x00;
+	nvt_spi_read(buf, 5);
+	g_crc = buf[1] | (buf[2] << 8) | (buf[3] << 16) | (buf[4] << 24);
+
+	nvt_spi_set_page(ts->mmap->R_DLM_CHECKSUM_ADDR);
+	buf[0] = ts->mmap->R_DLM_CHECKSUM_ADDR & 0x7F;
+	buf[1] = 0x00;
+	buf[2] = 0x00;
+	buf[3] = 0x00;
+	buf[4] = 0x00;
+	nvt_spi_read(buf, 5);
+	r_crc = buf[1] | (buf[2] << 8) | (buf[3] << 16) | (buf[4] << 24);
+
+	NVT_ERR("dlm: bin crc = 0x%08X, golden = 0x%08X, result = 0x%08X\n",
+			nvt_spi_bin_map[1].crc, g_crc, r_crc);
+
+}
+
+/*
+ ******************************************************
+ * Description:
+ *	Novatek touchscreen Download_Firmware with HW CRC
+ * function. It's complete download firmware flow.
+ *
+ * return:
+ *	Executive outcomes. 0---succeed. else---fail.
+ ******************************************************
+ */
+static int32_t nvt_spi_download_firmware_hw_crc(void)
+{
+	uint8_t retry = 0;
+	int32_t ret = 0;
+	const struct firmware *fw = nvt_spi_fw_entry;
+
+	nvt_spi_start = ktime_get();
+
+	while (1) {
+		/* bootloader reset to reset MCU */
+		nvt_spi_bootloader_reset();
+
+		/* set ilm & dlm reg bank */
+		nvt_spi_set_bld_hw_crc();
+
+		/* Start to write firmware process */
+		if (nvt_spi_cascade_2nd_header_info) {
+			/* for cascade */
+			nvt_spi_tx_auto_copy_mode();
+
+			ret = nvt_spi_write_firmware(fw->data, fw->size);
+			if (ret) {
+				NVT_ERR("Write_Firmware failed. (%d)\n", ret);
+				goto fail;
+			}
+
+			ret = nvt_spi_check_spi_dma_tx_info();
+			if (ret) {
+				NVT_ERR("spi dma tx info failed. (%d)\n", ret);
+				goto fail;
+			}
+		} else {
+			ret = nvt_spi_write_firmware(fw->data, fw->size);
+			if (ret) {
+				NVT_ERR("Write_Firmware failed. (%d)\n", ret);
+				goto fail;
+			}
+		}
+
+#if NVT_DUMP_PARTITION
+		ret = nvt_dump_partition();
+		if (ret)
+			NVT_ERR("nvt_dump_partition failed, ret = %d\n", ret);
+#endif
+
+		/* enable hw bld crc function */
+		nvt_spi_bld_crc_enable();
+
+		/* clear fw reset status & enable fw crc check */
+		nvt_spi_fw_crc_enable();
+
+		/* Set Boot Ready Bit */
+		nvt_spi_boot_ready();
+
+		ret = nvt_spi_check_fw_reset_state(NVT_SPI_RESET_STATE_INIT);
+		if (ret) {
+			NVT_ERR("nvt_check_fw_reset_state failed. (%d)\n", ret);
+			goto fail;
+		} else {
+			break;
+		}
+
+fail:
+		retry++;
+		if (unlikely(retry > 2)) {
+			NVT_ERR("error, retry=%d\n", retry);
+			nvt_spi_read_bld_hw_crc();
+			break;
+		}
+	}
+
+	nvt_spi_end = ktime_get();
+
+	return ret;
+}
+
+/*
+ ********************************************************
+ * Description:
+ *	Novatek touchscreen Download_Firmware function. It's
+ * complete download firmware flow.
+ *
+ * return:
+ *	n.a.
+ ******************************************************
+ */
+static int32_t nvt_spi_download_firmware(void)
+{
+	uint32_t addr;
+	uint8_t retry = 0;
+	int32_t ret = 0;
+	struct nvt_spi_data_t *ts = nvt_spi_data;
+
+	nvt_spi_start = ktime_get();
+
+	while (1) {
+		/*
+		 * Send eng reset cmd before download FW
+		 * Keep TP_RESX low when send eng reset cmd
+		 */
+#if NVT_SPI_TOUCH_SUPPORT_HW_RST
+		gpio_set_value(ts->reset_gpio, 0);
+		mdelay(1);	//wait 1ms
+#endif
+		nvt_spi_eng_reset();
+#if NVT_SPI_TOUCH_SUPPORT_HW_RST
+		gpio_set_value(ts->reset_gpio, 1);
+		mdelay(10);	//wait tRT2BRST after TP_RST
+#endif
+		nvt_spi_bootloader_reset();
+
+		addr = ts->mmap->EVENT_BUF_ADDR;
+		/* clear fw reset status */
+		nvt_spi_write_addr(addr | NVT_SPI_EVENT_MAP_RESET_COMPLETE, 0x00);
+
+		/* Start to write firmware process */
+		ret = nvt_spi_write_firmware(nvt_spi_fw_entry->data, nvt_spi_fw_entry->size);
+		if (ret) {
+			NVT_ERR("Write_Firmware failed. (%d)\n", ret);
+			goto fail;
+		}
+
+#if NVT_DUMP_PARTITION
+		ret = nvt_dump_partition();
+		if (ret)
+			NVT_ERR("nvt_dump_partition failed, ret = %d\n", ret);
+#endif
+
+		/* Set Boot Ready Bit */
+		nvt_spi_boot_ready();
+
+		ret = nvt_spi_check_fw_reset_state(NVT_SPI_RESET_STATE_INIT);
+		if (ret) {
+			NVT_ERR("nvt_check_fw_reset_state failed. (%d)\n", ret);
+			goto fail;
+		}
+
+		/* check fw checksum result */
+		ret = nvt_spi_check_fw_checksum();
+		if (ret) {
+			NVT_ERR("firmware checksum not match, retry=%d\n", retry);
+			goto fail;
+		} else
+			break;
+
+fail:
+		retry++;
+		if (unlikely(retry > 2)) {
+			NVT_ERR("error, retry=%d\n", retry);
+			break;
+		}
+	}
+
+	nvt_spi_end = ktime_get();
+
+	return ret;
+}
+
+/*
+ ******************************************************
+ * Description:
+ *	Novatek touchscreen update firmware main function.
+ *
+ * return:
+ *	n.a.
+ ******************************************************
+ */
+int32_t nvt_spi_update_firmware(char *firmware_name)
+{
+	int32_t ret = 0;
+	struct nvt_spi_data_t *ts = nvt_spi_data;
+
+	// request bin file in "/etc/firmware"
+	ret = nvt_spi_update_firmware_request(firmware_name);
+	if (ret) {
+		NVT_ERR("update_firmware_request failed. (%d)\n", ret);
+		goto request_firmware_fail;
+	}
+
+	/* initial buffer and variable */
+	ret = nvt_spi_download_init();
+	if (ret) {
+		NVT_ERR("Download Init failed. (%d)\n", ret);
+		goto download_fail;
+	}
+
+	/* download firmware process */
+	if (ts->hw_crc)
+		ret = nvt_spi_download_firmware_hw_crc();
+	else
+		ret = nvt_spi_download_firmware();
+	if (ret) {
+		NVT_ERR("Download Firmware failed. (%d)\n", ret);
+		goto download_fail;
+	}
+
+	NVT_LOG("Update firmware success! <%ld us>\n",
+			(long) ktime_us_delta(nvt_spi_end, nvt_spi_start));
+
+	/* Get FW Info */
+	ret = nvt_spi_get_fw_info();
+	if (ret)
+		NVT_ERR("nvt_get_fw_info failed. (%d)\n", ret);
+
+download_fail:
+	if (!IS_ERR_OR_NULL(nvt_spi_bin_map)) {
+		kfree(nvt_spi_bin_map);
+		nvt_spi_bin_map = NULL;
+	}
+
+	nvt_spi_update_firmware_release();
+request_firmware_fail:
+
+	return ret;
+}
+
+/*
+ *******************************************************
+ * Description:
+ *	Novatek touchscreen update firmware when booting
+ *	function.
+ *
+ * return:
+ *	n.a.
+ ******************************************************
+ */
+void nvt_spi_update_firmware_work(struct work_struct *work)
+{
+	struct nvt_spi_data_t *ts = nvt_spi_data;
+
+	mutex_lock(&ts->lock);
+	nvt_spi_update_firmware(NVT_SPI_BOOT_UPDATE_FIRMWARE_NAME);
+	mutex_unlock(&ts->lock);
+}
+#endif
+
+#endif

+ 607 - 0
qcom/opensource/touch-drivers/nt36xxx/nt36xxx_mem_map.h

@@ -0,0 +1,607 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2010 - 2018 Novatek, Inc.
+ *
+ * $Revision: 48764 $
+ * $Date: 2019-08-08 14:52:12 +0800 (Thu, 08 Aug 2019) $
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+#define CHIP_VER_TRIM_ADDR 0x3F004
+#define CHIP_VER_TRIM_OLD_ADDR 0x1F64E
+
+#if !defined(NVT_NT36XXX_SPI) /* NT36XXX I2C */
+
+struct nvt_ts_mem_map {
+	uint32_t EVENT_BUF_ADDR;
+	uint32_t RAW_PIPE0_ADDR;
+	uint32_t RAW_PIPE1_ADDR;
+	uint32_t BASELINE_ADDR;
+	uint32_t BASELINE_BTN_ADDR;
+	uint32_t DIFF_PIPE0_ADDR;
+	uint32_t DIFF_PIPE1_ADDR;
+	uint32_t RAW_BTN_PIPE0_ADDR;
+	uint32_t RAW_BTN_PIPE1_ADDR;
+	uint32_t DIFF_BTN_PIPE0_ADDR;
+	uint32_t DIFF_BTN_PIPE1_ADDR;
+	uint32_t READ_FLASH_CHECKSUM_ADDR;
+	uint32_t RW_FLASH_DATA_ADDR;
+};
+
+struct nvt_ts_hw_info {
+	uint8_t carrier_system;
+	uint8_t hw_crc;
+};
+
+static const struct nvt_ts_mem_map NT36526_memory_map = {
+	.EVENT_BUF_ADDR           = 0x22D00,
+	.RAW_PIPE0_ADDR           = 0x24000,
+	.RAW_PIPE1_ADDR           = 0x24000,
+	.BASELINE_ADDR            = 0x21758,
+	.BASELINE_BTN_ADDR        = 0,
+	.DIFF_PIPE0_ADDR          = 0x20AB0,
+	.DIFF_PIPE1_ADDR          = 0x24AB0,
+	.RAW_BTN_PIPE0_ADDR       = 0,
+	.RAW_BTN_PIPE1_ADDR       = 0,
+	.DIFF_BTN_PIPE0_ADDR      = 0,
+	.DIFF_BTN_PIPE1_ADDR      = 0,
+	.READ_FLASH_CHECKSUM_ADDR = 0x24000,
+	.RW_FLASH_DATA_ADDR       = 0x24002,
+};
+
+static const struct nvt_ts_mem_map NT36675_memory_map = {
+	.EVENT_BUF_ADDR           = 0x22D00,
+	.RAW_PIPE0_ADDR           = 0x24000,
+	.RAW_PIPE1_ADDR           = 0x24000,
+	.BASELINE_ADDR            = 0x21B90,
+	.BASELINE_BTN_ADDR        = 0,
+	.DIFF_PIPE0_ADDR          = 0x20C60,
+	.DIFF_PIPE1_ADDR          = 0x24C60,
+	.RAW_BTN_PIPE0_ADDR       = 0,
+	.RAW_BTN_PIPE1_ADDR       = 0,
+	.DIFF_BTN_PIPE0_ADDR      = 0,
+	.DIFF_BTN_PIPE1_ADDR      = 0,
+	.READ_FLASH_CHECKSUM_ADDR = 0x24000,
+	.RW_FLASH_DATA_ADDR       = 0x24002,
+};
+
+static const struct nvt_ts_mem_map NT36672A_memory_map = {
+	.EVENT_BUF_ADDR           = 0x21C00,
+	.RAW_PIPE0_ADDR           = 0x20000,
+	.RAW_PIPE1_ADDR           = 0x23000,
+	.BASELINE_ADDR            = 0x20BFC,
+	.BASELINE_BTN_ADDR        = 0x23BFC,
+	.DIFF_PIPE0_ADDR          = 0x206DC,
+	.DIFF_PIPE1_ADDR          = 0x236DC,
+	.RAW_BTN_PIPE0_ADDR       = 0x20510,
+	.RAW_BTN_PIPE1_ADDR       = 0x23510,
+	.DIFF_BTN_PIPE0_ADDR      = 0x20BF0,
+	.DIFF_BTN_PIPE1_ADDR      = 0x23BF0,
+	.READ_FLASH_CHECKSUM_ADDR = 0x24000,
+	.RW_FLASH_DATA_ADDR       = 0x24002,
+};
+
+static const struct nvt_ts_mem_map NT36772_memory_map = {
+	.EVENT_BUF_ADDR           = 0x11E00,
+	.RAW_PIPE0_ADDR           = 0x10000,
+	.RAW_PIPE1_ADDR           = 0x12000,
+	.BASELINE_ADDR            = 0x10E70,
+	.BASELINE_BTN_ADDR        = 0x12E70,
+	.DIFF_PIPE0_ADDR          = 0x10830,
+	.DIFF_PIPE1_ADDR          = 0x12830,
+	.RAW_BTN_PIPE0_ADDR       = 0x10E60,
+	.RAW_BTN_PIPE1_ADDR       = 0x12E60,
+	.DIFF_BTN_PIPE0_ADDR      = 0x10E68,
+	.DIFF_BTN_PIPE1_ADDR      = 0x12E68,
+	.READ_FLASH_CHECKSUM_ADDR = 0x14000,
+	.RW_FLASH_DATA_ADDR       = 0x14002,
+};
+
+static const struct nvt_ts_mem_map NT36525_memory_map = {
+	.EVENT_BUF_ADDR           = 0x11A00,
+	.RAW_PIPE0_ADDR           = 0x10000,
+	.RAW_PIPE1_ADDR           = 0x12000,
+	.BASELINE_ADDR            = 0x10B08,
+	.BASELINE_BTN_ADDR        = 0x12B08,
+	.DIFF_PIPE0_ADDR          = 0x1064C,
+	.DIFF_PIPE1_ADDR          = 0x1264C,
+	.RAW_BTN_PIPE0_ADDR       = 0x10634,
+	.RAW_BTN_PIPE1_ADDR       = 0x12634,
+	.DIFF_BTN_PIPE0_ADDR      = 0x10AFC,
+	.DIFF_BTN_PIPE1_ADDR      = 0x12AFC,
+	.READ_FLASH_CHECKSUM_ADDR = 0x14000,
+	.RW_FLASH_DATA_ADDR       = 0x14002,
+};
+
+static const struct nvt_ts_mem_map NT36676F_memory_map = {
+	.EVENT_BUF_ADDR           = 0x11A00,
+	.RAW_PIPE0_ADDR           = 0x10000,
+	.RAW_PIPE1_ADDR           = 0x12000,
+	.BASELINE_ADDR            = 0x10B08,
+	.BASELINE_BTN_ADDR        = 0x12B08,
+	.DIFF_PIPE0_ADDR          = 0x1064C,
+	.DIFF_PIPE1_ADDR          = 0x1264C,
+	.RAW_BTN_PIPE0_ADDR       = 0x10634,
+	.RAW_BTN_PIPE1_ADDR       = 0x12634,
+	.DIFF_BTN_PIPE0_ADDR      = 0x10AFC,
+	.DIFF_BTN_PIPE1_ADDR      = 0x12AFC,
+	.READ_FLASH_CHECKSUM_ADDR = 0x14000,
+	.RW_FLASH_DATA_ADDR       = 0x14002,
+};
+
+static struct nvt_ts_hw_info NT36526_hw_info = {
+	.carrier_system = 2,
+	.hw_crc         = 2,
+};
+
+static struct nvt_ts_hw_info NT36675_hw_info = {
+	.carrier_system = 2,
+	.hw_crc         = 2,
+};
+
+static struct nvt_ts_hw_info NT36672A_hw_info = {
+	.carrier_system = 0,
+	.hw_crc         = 1,
+};
+
+static struct nvt_ts_hw_info NT36772_hw_info = {
+	.carrier_system = 0,
+	.hw_crc         = 0,
+};
+
+static struct nvt_ts_hw_info NT36525_hw_info = {
+	.carrier_system = 0,
+	.hw_crc         = 0,
+};
+
+static struct nvt_ts_hw_info NT36676F_hw_info = {
+	.carrier_system = 0,
+	.hw_crc         = 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[] = {
+	{.id = {0x20, 0xFF, 0xFF, 0x72, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1},
+		.mmap = &NT36675_memory_map,  .hwinfo = &NT36675_hw_info},
+	{.id = {0x00, 0xFF, 0xFF, 0x80, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1},
+		.mmap = &NT36675_memory_map,  .hwinfo = &NT36675_hw_info},
+	{.id = {0x0C, 0xFF, 0xFF, 0x25, 0x65, 0x03}, .mask = {1, 0, 0, 1, 1, 1},
+		.mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info},
+	{.id = {0x0E, 0xFF, 0xFF, 0x72, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1},
+		.mmap = &NT36675_memory_map,  .hwinfo = &NT36675_hw_info},
+	{.id = {0x0C, 0xFF, 0xFF, 0x72, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1},
+		.mmap = &NT36675_memory_map,  .hwinfo = &NT36675_hw_info},
+	{.id = {0xFF, 0xFF, 0xFF, 0x26, 0x65, 0x03}, .mask = {0, 0, 0, 1, 1, 1},
+		.mmap = &NT36526_memory_map,  .hwinfo = &NT36526_hw_info},
+	{.id = {0xFF, 0xFF, 0xFF, 0x75, 0x66, 0x03}, .mask = {0, 0, 0, 1, 1, 1},
+		.mmap = &NT36675_memory_map,  .hwinfo = &NT36675_hw_info},
+	{.id = {0x0B, 0xFF, 0xFF, 0x72, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1},
+		.mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info},
+	{.id = {0x0B, 0xFF, 0xFF, 0x82, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1},
+		.mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info},
+	{.id = {0x0B, 0xFF, 0xFF, 0x25, 0x65, 0x03}, .mask = {1, 0, 0, 1, 1, 1},
+		.mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info},
+	{.id = {0x0A, 0xFF, 0xFF, 0x72, 0x65, 0x03}, .mask = {1, 0, 0, 1, 1, 1},
+		.mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info},
+	{.id = {0x0A, 0xFF, 0xFF, 0x72, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1},
+		.mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info},
+	{.id = {0x0A, 0xFF, 0xFF, 0x82, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1},
+		.mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info},
+	{.id = {0x0A, 0xFF, 0xFF, 0x70, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1},
+		.mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info},
+	{.id = {0x0B, 0xFF, 0xFF, 0x70, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1},
+		.mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info},
+	{.id = {0x0A, 0xFF, 0xFF, 0x72, 0x67, 0x03}, .mask = {1, 0, 0, 1, 1, 1},
+		.mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info},
+	{.id = {0x55, 0x00, 0xFF, 0x00, 0x00, 0x00}, .mask = {1, 1, 0, 1, 1, 1},
+		.mmap = &NT36772_memory_map,  .hwinfo = &NT36772_hw_info},
+	{.id = {0x55, 0x72, 0xFF, 0x00, 0x00, 0x00}, .mask = {1, 1, 0, 1, 1, 1},
+		.mmap = &NT36772_memory_map,  .hwinfo = &NT36772_hw_info},
+	{.id = {0xAA, 0x00, 0xFF, 0x00, 0x00, 0x00}, .mask = {1, 1, 0, 1, 1, 1},
+		.mmap = &NT36772_memory_map,  .hwinfo = &NT36772_hw_info},
+	{.id = {0xAA, 0x72, 0xFF, 0x00, 0x00, 0x00}, .mask = {1, 1, 0, 1, 1, 1},
+		.mmap = &NT36772_memory_map,  .hwinfo = &NT36772_hw_info},
+	{.id = {0xFF, 0xFF, 0xFF, 0x72, 0x67, 0x03}, .mask = {0, 0, 0, 1, 1, 1},
+		.mmap = &NT36772_memory_map,  .hwinfo = &NT36772_hw_info},
+	{.id = {0xFF, 0xFF, 0xFF, 0x70, 0x66, 0x03}, .mask = {0, 0, 0, 1, 1, 1},
+		.mmap = &NT36772_memory_map,  .hwinfo = &NT36772_hw_info},
+	{.id = {0xFF, 0xFF, 0xFF, 0x70, 0x67, 0x03}, .mask = {0, 0, 0, 1, 1, 1},
+		.mmap = &NT36772_memory_map,  .hwinfo = &NT36772_hw_info},
+	{.id = {0xFF, 0xFF, 0xFF, 0x72, 0x66, 0x03}, .mask = {0, 0, 0, 1, 1, 1},
+		.mmap = &NT36772_memory_map,  .hwinfo = &NT36772_hw_info},
+	{.id = {0xFF, 0xFF, 0xFF, 0x25, 0x65, 0x03}, .mask = {0, 0, 0, 1, 1, 1},
+		.mmap = &NT36525_memory_map,  .hwinfo = &NT36525_hw_info},
+	{.id = {0xFF, 0xFF, 0xFF, 0x76, 0x66, 0x03}, .mask = {0, 0, 0, 1, 1, 1},
+		.mmap = &NT36676F_memory_map, .hwinfo = &NT36676F_hw_info}
+};
+
+#else  /* NT36XXX_SPI */
+
+#define NVT_SPI_CHIP_VER_TRIM_ADDR 0x3F004
+#define NVT_SPI_CHIP_VER_TRIM_OLD_ADDR 0x1F64E
+
+struct nvt_spi_mem_map {
+	uint32_t EVENT_BUF_ADDR;
+	uint32_t RAW_PIPE0_ADDR;
+	uint32_t RAW_PIPE1_ADDR;
+	uint32_t BASELINE_ADDR;
+	uint32_t BASELINE_BTN_ADDR;
+	uint32_t DIFF_PIPE0_ADDR;
+	uint32_t DIFF_PIPE1_ADDR;
+	uint32_t RAW_BTN_PIPE0_ADDR;
+	uint32_t RAW_BTN_PIPE1_ADDR;
+	uint32_t DIFF_BTN_PIPE0_ADDR;
+	uint32_t DIFF_BTN_PIPE1_ADDR;
+	uint32_t PEN_2D_BL_TIP_X_ADDR;
+	uint32_t PEN_2D_BL_TIP_Y_ADDR;
+	uint32_t PEN_2D_BL_RING_X_ADDR;
+	uint32_t PEN_2D_BL_RING_Y_ADDR;
+	uint32_t PEN_2D_DIFF_TIP_X_ADDR;
+	uint32_t PEN_2D_DIFF_TIP_Y_ADDR;
+	uint32_t PEN_2D_DIFF_RING_X_ADDR;
+	uint32_t PEN_2D_DIFF_RING_Y_ADDR;
+	uint32_t PEN_2D_RAW_TIP_X_ADDR;
+	uint32_t PEN_2D_RAW_TIP_Y_ADDR;
+	uint32_t PEN_2D_RAW_RING_X_ADDR;
+	uint32_t PEN_2D_RAW_RING_Y_ADDR;
+	uint32_t PEN_1D_DIFF_TIP_X_ADDR;
+	uint32_t PEN_1D_DIFF_TIP_Y_ADDR;
+	uint32_t PEN_1D_DIFF_RING_X_ADDR;
+	uint32_t PEN_1D_DIFF_RING_Y_ADDR;
+	uint32_t READ_FLASH_CHECKSUM_ADDR;
+	uint32_t RW_FLASH_DATA_ADDR;
+	/* Phase 2 Host Download */
+	uint32_t BOOT_RDY_ADDR;
+	uint32_t POR_CD_ADDR;
+	uint32_t TX_AUTO_COPY_EN;
+	uint32_t SPI_DMA_TX_INFO;
+	/* BLD CRC */
+	uint32_t BLD_LENGTH_ADDR;
+	uint32_t ILM_LENGTH_ADDR;
+	uint32_t DLM_LENGTH_ADDR;
+	uint32_t BLD_DES_ADDR;
+	uint32_t ILM_DES_ADDR;
+	uint32_t DLM_DES_ADDR;
+	uint32_t G_ILM_CHECKSUM_ADDR;
+	uint32_t G_DLM_CHECKSUM_ADDR;
+	uint32_t R_ILM_CHECKSUM_ADDR;
+	uint32_t R_DLM_CHECKSUM_ADDR;
+	uint32_t BLD_CRC_EN_ADDR;
+	uint32_t DMA_CRC_EN_ADDR;
+	uint32_t BLD_ILM_DLM_CRC_ADDR;
+	uint32_t DMA_CRC_FLAG_ADDR;
+};
+
+struct nvt_spi_hw_info {
+	uint8_t hw_crc;
+};
+
+static const struct nvt_spi_mem_map NT36523_memory_map = {
+	.EVENT_BUF_ADDR           = 0x2FE00,
+	.RAW_PIPE0_ADDR           = 0x30FA0,
+	.RAW_PIPE1_ADDR           = 0x30FA0,
+	.BASELINE_ADDR            = 0x36510,
+	.BASELINE_BTN_ADDR        = 0,
+	.DIFF_PIPE0_ADDR          = 0x373E8,
+	.DIFF_PIPE1_ADDR          = 0x38068,
+	.RAW_BTN_PIPE0_ADDR       = 0,
+	.RAW_BTN_PIPE1_ADDR       = 0,
+	.DIFF_BTN_PIPE0_ADDR      = 0,
+	.DIFF_BTN_PIPE1_ADDR      = 0,
+	.PEN_2D_BL_TIP_X_ADDR     = 0x2988A,
+	.PEN_2D_BL_TIP_Y_ADDR     = 0x29A1A,
+	.PEN_2D_BL_RING_X_ADDR    = 0x29BAA,
+	.PEN_2D_BL_RING_Y_ADDR    = 0x29D3A,
+	.PEN_2D_DIFF_TIP_X_ADDR   = 0x29ECA,
+	.PEN_2D_DIFF_TIP_Y_ADDR   = 0x2A05A,
+	.PEN_2D_DIFF_RING_X_ADDR  = 0x2A1EA,
+	.PEN_2D_DIFF_RING_Y_ADDR  = 0x2A37A,
+	.PEN_2D_RAW_TIP_X_ADDR    = 0x2A50A,
+	.PEN_2D_RAW_TIP_Y_ADDR    = 0x2A69A,
+	.PEN_2D_RAW_RING_X_ADDR   = 0x2A82A,
+	.PEN_2D_RAW_RING_Y_ADDR   = 0x2A9BA,
+	.PEN_1D_DIFF_TIP_X_ADDR   = 0x2AB4A,
+	.PEN_1D_DIFF_TIP_Y_ADDR   = 0x2ABAE,
+	.PEN_1D_DIFF_RING_X_ADDR  = 0x2AC12,
+	.PEN_1D_DIFF_RING_Y_ADDR  = 0x2AC76,
+	.READ_FLASH_CHECKSUM_ADDR = 0x24000,
+	.RW_FLASH_DATA_ADDR       = 0x24002,
+	/* Phase 2 Host Download */
+	.BOOT_RDY_ADDR            = 0x3F10D,
+	.TX_AUTO_COPY_EN          = 0x3F7E8,
+	.SPI_DMA_TX_INFO          = 0x3F7F1,
+	/* BLD CRC */
+	.BLD_LENGTH_ADDR          = 0x3F138,	//0x3F138 ~ 0x3F13A	(3 bytes)
+	.ILM_LENGTH_ADDR          = 0x3F118,	//0x3F118 ~ 0x3F11A	(3 bytes)
+	.DLM_LENGTH_ADDR          = 0x3F130,	//0x3F130 ~ 0x3F132	(3 bytes)
+	.BLD_DES_ADDR             = 0x3F114,	//0x3F114 ~ 0x3F116	(3 bytes)
+	.ILM_DES_ADDR             = 0x3F128,	//0x3F128 ~ 0x3F12A	(3 bytes)
+	.DLM_DES_ADDR             = 0x3F12C,	//0x3F12C ~ 0x3F12E	(3 bytes)
+	.G_ILM_CHECKSUM_ADDR      = 0x3F100,	//0x3F100 ~ 0x3F103	(4 bytes)
+	.G_DLM_CHECKSUM_ADDR      = 0x3F104,	//0x3F104 ~ 0x3F107	(4 bytes)
+	.R_ILM_CHECKSUM_ADDR      = 0x3F120,	//0x3F120 ~ 0x3F123 (4 bytes)
+	.R_DLM_CHECKSUM_ADDR      = 0x3F124,	//0x3F124 ~ 0x3F127 (4 bytes)
+	.BLD_CRC_EN_ADDR          = 0x3F30E,
+	.DMA_CRC_EN_ADDR          = 0x3F136,
+	.BLD_ILM_DLM_CRC_ADDR     = 0x3F133,
+	.DMA_CRC_FLAG_ADDR        = 0x3F134,
+};
+
+static const struct nvt_spi_mem_map NT36526_memory_map = {
+	.EVENT_BUF_ADDR           = 0x22D00,
+	.RAW_PIPE0_ADDR           = 0x24000,
+	.RAW_PIPE1_ADDR           = 0x24000,
+	.BASELINE_ADDR            = 0x21758,
+	.BASELINE_BTN_ADDR        = 0,
+	.DIFF_PIPE0_ADDR          = 0x20AB0,
+	.DIFF_PIPE1_ADDR          = 0x24AB0,
+	.RAW_BTN_PIPE0_ADDR       = 0,
+	.RAW_BTN_PIPE1_ADDR       = 0,
+	.DIFF_BTN_PIPE0_ADDR      = 0,
+	.DIFF_BTN_PIPE1_ADDR      = 0,
+	.READ_FLASH_CHECKSUM_ADDR = 0x24000,
+	.RW_FLASH_DATA_ADDR       = 0x24002,
+	/* Phase 2 Host Download */
+	.BOOT_RDY_ADDR            = 0x3F10D,
+	/* BLD CRC */
+	.BLD_LENGTH_ADDR          = 0x3F138,	//0x3F138 ~ 0x3F13A	(3 bytes)
+	.ILM_LENGTH_ADDR          = 0x3F118,	//0x3F118 ~ 0x3F11A	(3 bytes)
+	.DLM_LENGTH_ADDR          = 0x3F130,	//0x3F130 ~ 0x3F132	(3 bytes)
+	.BLD_DES_ADDR             = 0x3F114,	//0x3F114 ~ 0x3F116	(3 bytes)
+	.ILM_DES_ADDR             = 0x3F128,	//0x3F128 ~ 0x3F12A	(3 bytes)
+	.DLM_DES_ADDR             = 0x3F12C,	//0x3F12C ~ 0x3F12E	(3 bytes)
+	.G_ILM_CHECKSUM_ADDR      = 0x3F100,	//0x3F100 ~ 0x3F103	(4 bytes)
+	.G_DLM_CHECKSUM_ADDR      = 0x3F104,	//0x3F104 ~ 0x3F107	(4 bytes)
+	.R_ILM_CHECKSUM_ADDR      = 0x3F120,	//0x3F120 ~ 0x3F123 (4 bytes)
+	.R_DLM_CHECKSUM_ADDR      = 0x3F124,	//0x3F124 ~ 0x3F127 (4 bytes)
+	.BLD_CRC_EN_ADDR          = 0x3F30E,
+	.DMA_CRC_EN_ADDR          = 0x3F136,
+	.BLD_ILM_DLM_CRC_ADDR     = 0x3F133,
+	.DMA_CRC_FLAG_ADDR        = 0x3F134,
+};
+
+
+static const struct nvt_spi_mem_map NT36675_memory_map = {
+	.EVENT_BUF_ADDR           = 0x22D00,
+	.RAW_PIPE0_ADDR           = 0x24000,
+	.RAW_PIPE1_ADDR           = 0x24000,
+	.BASELINE_ADDR            = 0x21B90,
+	.BASELINE_BTN_ADDR        = 0,
+	.DIFF_PIPE0_ADDR          = 0x20C60,
+	.DIFF_PIPE1_ADDR          = 0x24C60,
+	.RAW_BTN_PIPE0_ADDR       = 0,
+	.RAW_BTN_PIPE1_ADDR       = 0,
+	.DIFF_BTN_PIPE0_ADDR      = 0,
+	.DIFF_BTN_PIPE1_ADDR      = 0,
+	.READ_FLASH_CHECKSUM_ADDR = 0x24000,
+	.RW_FLASH_DATA_ADDR       = 0x24002,
+	/* Phase 2 Host Download */
+	.BOOT_RDY_ADDR            = 0x3F10D,
+	/* BLD CRC */
+	.BLD_LENGTH_ADDR          = 0x3F138,	//0x3F138 ~ 0x3F13A	(3 bytes)
+	.ILM_LENGTH_ADDR          = 0x3F118,	//0x3F118 ~ 0x3F11A	(3 bytes)
+	.DLM_LENGTH_ADDR          = 0x3F130,	//0x3F130 ~ 0x3F132	(3 bytes)
+	.BLD_DES_ADDR             = 0x3F114,	//0x3F114 ~ 0x3F116	(3 bytes)
+	.ILM_DES_ADDR             = 0x3F128,	//0x3F128 ~ 0x3F12A	(3 bytes)
+	.DLM_DES_ADDR             = 0x3F12C,	//0x3F12C ~ 0x3F12E	(3 bytes)
+	.G_ILM_CHECKSUM_ADDR      = 0x3F100,	//0x3F100 ~ 0x3F103	(4 bytes)
+	.G_DLM_CHECKSUM_ADDR      = 0x3F104,	//0x3F104 ~ 0x3F107	(4 bytes)
+	.R_ILM_CHECKSUM_ADDR      = 0x3F120,	//0x3F120 ~ 0x3F123 (4 bytes)
+	.R_DLM_CHECKSUM_ADDR      = 0x3F124,	//0x3F124 ~ 0x3F127 (4 bytes)
+	.BLD_CRC_EN_ADDR          = 0x3F30E,
+	.DMA_CRC_EN_ADDR          = 0x3F136,
+	.BLD_ILM_DLM_CRC_ADDR     = 0x3F133,
+	.DMA_CRC_FLAG_ADDR        = 0x3F134,
+};
+
+static const struct nvt_spi_mem_map NT36672A_memory_map = {
+	.EVENT_BUF_ADDR           = 0x21C00,
+	.RAW_PIPE0_ADDR           = 0x20000,
+	.RAW_PIPE1_ADDR           = 0x23000,
+	.BASELINE_ADDR            = 0x20BFC,
+	.BASELINE_BTN_ADDR        = 0x23BFC,
+	.DIFF_PIPE0_ADDR          = 0x206DC,
+	.DIFF_PIPE1_ADDR          = 0x236DC,
+	.RAW_BTN_PIPE0_ADDR       = 0x20510,
+	.RAW_BTN_PIPE1_ADDR       = 0x23510,
+	.DIFF_BTN_PIPE0_ADDR      = 0x20BF0,
+	.DIFF_BTN_PIPE1_ADDR      = 0x23BF0,
+	.READ_FLASH_CHECKSUM_ADDR = 0x24000,
+	.RW_FLASH_DATA_ADDR       = 0x24002,
+	/* Phase 2 Host Download */
+	.BOOT_RDY_ADDR            = 0x3F10D,
+	/* BLD CRC */
+	.BLD_LENGTH_ADDR          = 0x3F10E,	//0x3F10E ~ 0x3F10F	(2 bytes)
+	.ILM_LENGTH_ADDR          = 0x3F118,	//0x3F118 ~ 0x3F119	(2 bytes)
+	.DLM_LENGTH_ADDR          = 0x3F130,	//0x3F130 ~ 0x3F131	(2 bytes)
+	.BLD_DES_ADDR             = 0x3F114,	//0x3F114 ~ 0x3F116	(3 bytes)
+	.ILM_DES_ADDR             = 0x3F128,	//0x3F128 ~ 0x3F12A	(3 bytes)
+	.DLM_DES_ADDR             = 0x3F12C,	//0x3F12C ~ 0x3F12E	(3 bytes)
+	.G_ILM_CHECKSUM_ADDR      = 0x3F100,	//0x3F100 ~ 0x3F103	(4 bytes)
+	.G_DLM_CHECKSUM_ADDR      = 0x3F104,	//0x3F104 ~ 0x3F107	(4 bytes)
+	.R_ILM_CHECKSUM_ADDR      = 0x3F120,	//0x3F120 ~ 0x3F123 (4 bytes)
+	.R_DLM_CHECKSUM_ADDR      = 0x3F124,	//0x3F124 ~ 0x3F127 (4 bytes)
+	.BLD_CRC_EN_ADDR          = 0x3F30E,
+	.DMA_CRC_EN_ADDR          = 0x3F132,
+	.BLD_ILM_DLM_CRC_ADDR     = 0x3F133,
+	.DMA_CRC_FLAG_ADDR        = 0x3F134,
+};
+
+static const struct nvt_spi_mem_map NT36772_memory_map = {
+	.EVENT_BUF_ADDR           = 0x11E00,
+	.RAW_PIPE0_ADDR           = 0x10000,
+	.RAW_PIPE1_ADDR           = 0x12000,
+	.BASELINE_ADDR            = 0x10E70,
+	.BASELINE_BTN_ADDR        = 0x12E70,
+	.DIFF_PIPE0_ADDR          = 0x10830,
+	.DIFF_PIPE1_ADDR          = 0x12830,
+	.RAW_BTN_PIPE0_ADDR       = 0x10E60,
+	.RAW_BTN_PIPE1_ADDR       = 0x12E60,
+	.DIFF_BTN_PIPE0_ADDR      = 0x10E68,
+	.DIFF_BTN_PIPE1_ADDR      = 0x12E68,
+	.READ_FLASH_CHECKSUM_ADDR = 0x14000,
+	.RW_FLASH_DATA_ADDR       = 0x14002,
+	/* Phase 2 Host Download */
+	.BOOT_RDY_ADDR            = 0x1F141,
+	.POR_CD_ADDR              = 0x1F61C,
+	/* BLD CRC */
+	.R_ILM_CHECKSUM_ADDR      = 0x1BF00,
+};
+
+static const struct nvt_spi_mem_map NT36525_memory_map = {
+	.EVENT_BUF_ADDR           = 0x11A00,
+	.RAW_PIPE0_ADDR           = 0x10000,
+	.RAW_PIPE1_ADDR           = 0x12000,
+	.BASELINE_ADDR            = 0x10B08,
+	.BASELINE_BTN_ADDR        = 0x12B08,
+	.DIFF_PIPE0_ADDR          = 0x1064C,
+	.DIFF_PIPE1_ADDR          = 0x1264C,
+	.RAW_BTN_PIPE0_ADDR       = 0x10634,
+	.RAW_BTN_PIPE1_ADDR       = 0x12634,
+	.DIFF_BTN_PIPE0_ADDR      = 0x10AFC,
+	.DIFF_BTN_PIPE1_ADDR      = 0x12AFC,
+	.READ_FLASH_CHECKSUM_ADDR = 0x14000,
+	.RW_FLASH_DATA_ADDR       = 0x14002,
+	/* Phase 2 Host Download */
+	.BOOT_RDY_ADDR            = 0x1F141,
+	.POR_CD_ADDR              = 0x1F61C,
+	/* BLD CRC */
+	.R_ILM_CHECKSUM_ADDR      = 0x1BF00,
+};
+
+static const struct nvt_spi_mem_map NT36676F_memory_map = {
+	.EVENT_BUF_ADDR           = 0x11A00,
+	.RAW_PIPE0_ADDR           = 0x10000,
+	.RAW_PIPE1_ADDR           = 0x12000,
+	.BASELINE_ADDR            = 0x10B08,
+	.BASELINE_BTN_ADDR        = 0x12B08,
+	.DIFF_PIPE0_ADDR          = 0x1064C,
+	.DIFF_PIPE1_ADDR          = 0x1264C,
+	.RAW_BTN_PIPE0_ADDR       = 0x10634,
+	.RAW_BTN_PIPE1_ADDR       = 0x12634,
+	.DIFF_BTN_PIPE0_ADDR      = 0x10AFC,
+	.DIFF_BTN_PIPE1_ADDR      = 0x12AFC,
+	.READ_FLASH_CHECKSUM_ADDR = 0x14000,
+	.RW_FLASH_DATA_ADDR       = 0x14002,
+};
+
+static struct nvt_spi_hw_info NT36523_hw_info = {
+	.hw_crc         = 2,
+};
+
+static struct nvt_spi_hw_info NT36526_hw_info = {
+	.hw_crc         = 2,
+};
+
+static struct nvt_spi_hw_info NT36675_hw_info = {
+	.hw_crc         = 2,
+};
+
+static struct nvt_spi_hw_info NT36672A_hw_info = {
+	.hw_crc         = 1,
+};
+
+static struct nvt_spi_hw_info NT36772_hw_info = {
+	.hw_crc         = 0,
+};
+
+static struct nvt_spi_hw_info NT36525_hw_info = {
+	.hw_crc         = 0,
+};
+
+static struct nvt_spi_hw_info NT36676F_hw_info = {
+	.hw_crc         = 0,
+};
+
+#define NVT_SPI_ID_BYTE_MAX 6
+struct nvt_spi_trim_id_table_t {
+	uint8_t id[NVT_SPI_ID_BYTE_MAX];
+	uint8_t mask[NVT_SPI_ID_BYTE_MAX];
+	const struct nvt_spi_mem_map *mmap;
+	const struct nvt_spi_hw_info *hwinfo;
+};
+
+static const struct nvt_spi_trim_id_table_t nvt_spi_trim_id_table[] = {
+	{.id = {0x0D, 0xFF, 0xFF, 0x72, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1},
+		.mmap = &NT36675_memory_map,  .hwinfo = &NT36675_hw_info},
+	{.id = {0x20, 0xFF, 0xFF, 0x72, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1},
+		.mmap = &NT36675_memory_map,  .hwinfo = &NT36675_hw_info},
+	{.id = {0x00, 0xFF, 0xFF, 0x80, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1},
+		.mmap = &NT36675_memory_map,  .hwinfo = &NT36675_hw_info},
+	{.id = {0x0C, 0xFF, 0xFF, 0x25, 0x65, 0x03}, .mask = {1, 0, 0, 1, 1, 1},
+		.mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info},
+	{.id = {0x0E, 0xFF, 0xFF, 0x72, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1},
+		.mmap = &NT36675_memory_map,  .hwinfo = &NT36675_hw_info},
+	{.id = {0x20, 0xFF, 0xFF, 0x23, 0x65, 0x03}, .mask = {1, 0, 0, 1, 1, 1},
+		.mmap = &NT36523_memory_map,  .hwinfo = &NT36523_hw_info},
+	{.id = {0x0C, 0xFF, 0xFF, 0x23, 0x65, 0x03}, .mask = {1, 0, 0, 1, 1, 1},
+		.mmap = &NT36523_memory_map,  .hwinfo = &NT36523_hw_info},
+	{.id = {0x0B, 0xFF, 0xFF, 0x23, 0x65, 0x03}, .mask = {1, 0, 0, 1, 1, 1},
+		.mmap = &NT36523_memory_map,  .hwinfo = &NT36523_hw_info},
+	{.id = {0x0A, 0xFF, 0xFF, 0x23, 0x65, 0x03}, .mask = {1, 0, 0, 1, 1, 1},
+		.mmap = &NT36523_memory_map,  .hwinfo = &NT36523_hw_info},
+	{.id = {0xFF, 0xFF, 0xFF, 0x23, 0x65, 0x03}, .mask = {0, 0, 0, 1, 1, 1},
+		.mmap = &NT36523_memory_map,  .hwinfo = &NT36523_hw_info},
+	{.id = {0x0C, 0xFF, 0xFF, 0x72, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1},
+		.mmap = &NT36675_memory_map,  .hwinfo = &NT36675_hw_info},
+	{.id = {0xFF, 0xFF, 0xFF, 0x26, 0x65, 0x03}, .mask = {0, 0, 0, 1, 1, 1},
+		.mmap = &NT36526_memory_map,  .hwinfo = &NT36526_hw_info},
+	{.id = {0xFF, 0xFF, 0xFF, 0x75, 0x66, 0x03}, .mask = {0, 0, 0, 1, 1, 1},
+		.mmap = &NT36675_memory_map,  .hwinfo = &NT36675_hw_info},
+	{.id = {0x0B, 0xFF, 0xFF, 0x72, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1},
+		.mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info},
+	{.id = {0x0B, 0xFF, 0xFF, 0x82, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1},
+		.mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info},
+	{.id = {0x0B, 0xFF, 0xFF, 0x25, 0x65, 0x03}, .mask = {1, 0, 0, 1, 1, 1},
+		.mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info},
+	{.id = {0x0A, 0xFF, 0xFF, 0x72, 0x65, 0x03}, .mask = {1, 0, 0, 1, 1, 1},
+		.mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info},
+	{.id = {0x0A, 0xFF, 0xFF, 0x72, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1},
+		.mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info},
+	{.id = {0x0A, 0xFF, 0xFF, 0x82, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1},
+		.mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info},
+	{.id = {0x0A, 0xFF, 0xFF, 0x70, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1},
+		.mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info},
+	{.id = {0x0B, 0xFF, 0xFF, 0x70, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1},
+		.mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info},
+	{.id = {0x0A, 0xFF, 0xFF, 0x72, 0x67, 0x03}, .mask = {1, 0, 0, 1, 1, 1},
+		.mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info},
+	{.id = {0x55, 0x00, 0xFF, 0x00, 0x00, 0x00}, .mask = {1, 1, 0, 1, 1, 1},
+		.mmap = &NT36772_memory_map,  .hwinfo = &NT36772_hw_info},
+	{.id = {0x55, 0x72, 0xFF, 0x00, 0x00, 0x00}, .mask = {1, 1, 0, 1, 1, 1},
+		.mmap = &NT36772_memory_map,  .hwinfo = &NT36772_hw_info},
+	{.id = {0xAA, 0x00, 0xFF, 0x00, 0x00, 0x00}, .mask = {1, 1, 0, 1, 1, 1},
+		.mmap = &NT36772_memory_map,  .hwinfo = &NT36772_hw_info},
+	{.id = {0xAA, 0x72, 0xFF, 0x00, 0x00, 0x00}, .mask = {1, 1, 0, 1, 1, 1},
+		.mmap = &NT36772_memory_map,  .hwinfo = &NT36772_hw_info},
+	{.id = {0xFF, 0xFF, 0xFF, 0x72, 0x67, 0x03}, .mask = {0, 0, 0, 1, 1, 1},
+		.mmap = &NT36772_memory_map,  .hwinfo = &NT36772_hw_info},
+	{.id = {0xFF, 0xFF, 0xFF, 0x70, 0x66, 0x03}, .mask = {0, 0, 0, 1, 1, 1},
+		.mmap = &NT36772_memory_map,  .hwinfo = &NT36772_hw_info},
+	{.id = {0xFF, 0xFF, 0xFF, 0x70, 0x67, 0x03}, .mask = {0, 0, 0, 1, 1, 1},
+		.mmap = &NT36772_memory_map,  .hwinfo = &NT36772_hw_info},
+	{.id = {0xFF, 0xFF, 0xFF, 0x72, 0x66, 0x03}, .mask = {0, 0, 0, 1, 1, 1},
+		.mmap = &NT36772_memory_map,  .hwinfo = &NT36772_hw_info},
+	{.id = {0xFF, 0xFF, 0xFF, 0x25, 0x65, 0x03}, .mask = {0, 0, 0, 1, 1, 1},
+		.mmap = &NT36525_memory_map,  .hwinfo = &NT36525_hw_info},
+	{.id = {0xFF, 0xFF, 0xFF, 0x76, 0x66, 0x03}, .mask = {0, 0, 0, 1, 1, 1},
+		.mmap = &NT36676F_memory_map, .hwinfo = &NT36676F_hw_info}
+};
+
+#endif

+ 1481 - 0
qcom/opensource/touch-drivers/nt36xxx/nt36xxx_mp_ctrlram.c

@@ -0,0 +1,1481 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2010 - 2018 Novatek, Inc.
+ *
+ * $Revision: 47247 $
+ * $Date: 2019-07-10 10:41:36 +0800 (Wed, 10 Jul 2019) $
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+
+#include "nt36xxx.h"
+#include "nt36xxx_mp_ctrlram.h"
+
+#if NVT_TOUCH_MP
+
+#define NORMAL_MODE 0x00
+#define TEST_MODE_1 0x21
+#define TEST_MODE_2 0x22
+#define MP_MODE_CC 0x41
+#define FREQ_HOP_DISABLE 0x66
+#define FREQ_HOP_ENABLE 0x65
+
+#define SHORT_TEST_CSV_FILE "/data/local/tmp/ShortTest.csv"
+#define OPEN_TEST_CSV_FILE "/data/local/tmp/OpenTest.csv"
+#define FW_RAWDATA_CSV_FILE "/data/local/tmp/FWMutualTest.csv"
+#define FW_CC_CSV_FILE "/data/local/tmp/FWCCTest.csv"
+#define NOISE_TEST_CSV_FILE "/data/local/tmp/NoiseTest.csv"
+
+#define nvt_mp_seq_printf(m, fmt, args...) do {	\
+	seq_printf(m, fmt, ##args);	\
+	if (!nvt_mp_test_result_printed)	\
+		printk(fmt, ##args);	\
+} while (0)
+
+static uint8_t *RecordResult_Short = NULL;
+static uint8_t *RecordResult_Open = NULL;
+static uint8_t *RecordResult_FWMutual = NULL;
+static uint8_t *RecordResult_FW_CC = NULL;
+static uint8_t *RecordResult_FW_DiffMax = NULL;
+static uint8_t *RecordResult_FW_DiffMin = NULL;
+
+static int32_t TestResult_Short = 0;
+static int32_t TestResult_Open = 0;
+static int32_t TestResult_FW_Rawdata = 0;
+static int32_t TestResult_FWMutual = 0;
+static int32_t TestResult_FW_CC = 0;
+static int32_t TestResult_Noise = 0;
+static int32_t TestResult_FW_DiffMax = 0;
+static int32_t TestResult_FW_DiffMin = 0;
+
+static int32_t *RawData_Short = NULL;
+static int32_t *RawData_Open = NULL;
+static int32_t *RawData_Diff = NULL;
+static int32_t *RawData_Diff_Min = NULL;
+static int32_t *RawData_Diff_Max = NULL;
+static int32_t *RawData_FWMutual = NULL;
+static int32_t *RawData_FW_CC = NULL;
+
+static struct proc_dir_entry *NVT_proc_selftest_entry = NULL;
+static int8_t nvt_mp_test_result_printed = 0;
+static uint8_t fw_ver = 0;
+unsigned char mpcriteria[PAGE_SIZE]; //novatek-mp-criteria-default
+
+extern void nvt_change_mode(uint8_t mode);
+extern uint8_t nvt_get_fw_pipe(void);
+extern void nvt_read_mdata(uint32_t xdata_addr, uint32_t xdata_btn_addr);
+extern void nvt_get_mdata(int32_t *buf, uint8_t *m_x_num, uint8_t *m_y_num);
+int32_t nvt_mp_parse_dt(struct device_node *root, const char *node_compatible);
+
+/*******************************************************
+Description:
+	Novatek touchscreen allocate buffer for mp selftest.
+
+return:
+	Executive outcomes. 0---succeed. -12---Out of memory
+*******************************************************/
+static int nvt_mp_buffer_init(void)
+{
+	size_t RecordResult_BufSize = IC_X_CFG_SIZE * IC_Y_CFG_SIZE + IC_KEY_CFG_SIZE;
+	size_t RawData_BufSize = (IC_X_CFG_SIZE * IC_Y_CFG_SIZE + IC_KEY_CFG_SIZE) * sizeof(int32_t);
+
+	RecordResult_Short = kzalloc(RecordResult_BufSize, GFP_KERNEL);
+	if (!RecordResult_Short) {
+		NVT_ERR("kzalloc for RecordResult_Short failed!\n");
+		return -ENOMEM;
+	}
+
+	RecordResult_Open = kzalloc(RecordResult_BufSize, GFP_KERNEL);
+	if (!RecordResult_Open) {
+		NVT_ERR("kzalloc for RecordResult_Open failed!\n");
+		return -ENOMEM;
+	}
+
+	RecordResult_FWMutual = kzalloc(RecordResult_BufSize, GFP_KERNEL);
+	if (!RecordResult_FWMutual) {
+		NVT_ERR("kzalloc for RecordResult_FWMutual failed!\n");
+		return -ENOMEM;
+	}
+
+	RecordResult_FW_CC = kzalloc(RecordResult_BufSize, GFP_KERNEL);
+	if (!RecordResult_FW_CC) {
+		NVT_ERR("kzalloc for RecordResult_FW_CC failed!\n");
+		return -ENOMEM;
+	}
+
+	RecordResult_FW_DiffMax = kzalloc(RecordResult_BufSize, GFP_KERNEL);
+	if (!RecordResult_FW_DiffMax) {
+		NVT_ERR("kzalloc for RecordResult_FW_DiffMax failed!\n");
+		return -ENOMEM;
+	}
+
+	RecordResult_FW_DiffMin = kzalloc(RecordResult_BufSize, GFP_KERNEL);
+	if (!RecordResult_FW_DiffMin) {
+		NVT_ERR("kzalloc for RecordResult_FW_DiffMin failed!\n");
+		return -ENOMEM;
+	}
+
+	RawData_Short = kzalloc(RawData_BufSize, GFP_KERNEL);
+	if (!RawData_Short) {
+		NVT_ERR("kzalloc for RawData_Short failed!\n");
+		return -ENOMEM;
+	}
+
+	RawData_Open = kzalloc(RawData_BufSize, GFP_KERNEL);
+	if (!RawData_Open) {
+		NVT_ERR("kzalloc for RawData_Open failed!\n");
+		return -ENOMEM;
+	}
+
+	RawData_Diff = kzalloc(RawData_BufSize, GFP_KERNEL);
+	if (!RawData_Diff) {
+		NVT_ERR("kzalloc for RawData_Diff failed!\n");
+		return -ENOMEM;
+	}
+
+	RawData_Diff_Min = kzalloc(RawData_BufSize, GFP_KERNEL);
+	if (!RawData_Diff_Min) {
+		NVT_ERR("kzalloc for RawData_Diff_Min failed!\n");
+		return -ENOMEM;
+	}
+
+	RawData_Diff_Max = kzalloc(RawData_BufSize, GFP_KERNEL);
+	if (!RawData_Diff_Max) {
+		NVT_ERR("kzalloc for RawData_Diff_Max failed!\n");
+		return -ENOMEM;
+	}
+
+	RawData_FWMutual = kzalloc(RawData_BufSize, GFP_KERNEL);
+	if (!RawData_FWMutual) {
+		NVT_ERR("kzalloc for RawData_FWMutual failed!\n");
+		return -ENOMEM;
+	}
+
+	RawData_FW_CC = kzalloc(RawData_BufSize, GFP_KERNEL);
+	if (!RawData_FW_CC) {
+		NVT_ERR("kzalloc for RawData_FW_CC failed!\n");
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+/*******************************************************
+Description:
+	Novatek touchscreen free buffer for mp selftest.
+
+return:
+	n.a.
+*******************************************************/
+static void nvt_mp_buffer_deinit(void)
+{
+	if (RecordResult_Short) {
+		kfree(RecordResult_Short);
+		RecordResult_Short = NULL;
+	}
+
+	if (RecordResult_Open) {
+		kfree(RecordResult_Open);
+		RecordResult_Open = NULL;
+	}
+
+	if (RecordResult_FWMutual) {
+		kfree(RecordResult_FWMutual);
+		RecordResult_FWMutual = NULL;
+	}
+
+	if (RecordResult_FW_CC) {
+		kfree(RecordResult_FW_CC);
+		RecordResult_FW_CC = NULL;
+	}
+
+	if (RecordResult_FW_DiffMax) {
+		kfree(RecordResult_FW_DiffMax);
+		RecordResult_FW_DiffMax = NULL;
+	}
+
+	if (RecordResult_FW_DiffMin) {
+		kfree(RecordResult_FW_DiffMin);
+		RecordResult_FW_DiffMin = NULL;
+	}
+
+	if (RawData_Short) {
+		kfree(RawData_Short);
+		RawData_Short = NULL;
+	}
+
+	if (RawData_Open) {
+		kfree(RawData_Open);
+		RawData_Open = NULL;
+	}
+
+	if (RawData_Diff) {
+		kfree(RawData_Diff);
+		RawData_Diff = NULL;
+	}
+
+	if (RawData_Diff_Min) {
+		kfree(RawData_Diff_Min);
+		RawData_Diff_Min = NULL;
+	}
+
+	if (RawData_Diff_Max) {
+		kfree(RawData_Diff_Max);
+		RawData_Diff_Max = NULL;
+	}
+
+	if (RawData_FWMutual) {
+		kfree(RawData_FWMutual);
+		RawData_FWMutual = NULL;
+	}
+
+	if (RawData_FW_CC) {
+		kfree(RawData_FW_CC);
+		RawData_FW_CC = NULL;
+	}
+}
+
+static void nvt_print_data_log_in_one_line(int32_t *data, int32_t data_num)
+{
+	char *tmp_log = NULL;
+	int32_t i = 0;
+	int32_t count = data_num * 7 + 1;
+
+	tmp_log = kzalloc(count, GFP_KERNEL);
+	if (!tmp_log) {
+		NVT_ERR("kzalloc for tmp_log failed!\n ");
+		return;
+	}
+
+	for (i = 0; i < data_num; i++) {
+		snprintf(tmp_log + i * 7, count - i * 7, "%5d, ", data[i]);
+	}
+	tmp_log[data_num * 7] = '\0';
+	printk("%s", tmp_log);
+	if (tmp_log) {
+		kfree(tmp_log);
+		tmp_log = NULL;
+	}
+
+	return;
+}
+
+static void nvt_print_result_log_in_one_line(uint8_t *result, int32_t result_num)
+{
+	char *tmp_log = NULL;
+	int32_t i = 0;
+	int32_t count = 0;
+
+	count = result_num * 6 + 1;
+	tmp_log = kzalloc(count, GFP_KERNEL);
+	if (!tmp_log) {
+		NVT_ERR("kzalloc for tmp_log failed!\n ");
+		return;
+	}
+
+	for (i = 0; i < result_num; i++) {
+		snprintf(tmp_log + i * 6, count - i * 6, "0x%02X, ", result[i]);
+	}
+	tmp_log[result_num * 6] = '\0';
+	printk("%s", tmp_log);
+	if (tmp_log) {
+		kfree(tmp_log);
+		tmp_log = NULL;
+	}
+
+	return;
+}
+
+/*******************************************************
+Description:
+	Novatek touchscreen self-test criteria print function.
+
+return:
+	n.a.
+*******************************************************/
+static void nvt_print_lmt_array(int32_t *array, int32_t x_ch, int32_t y_ch)
+{
+	int32_t j = 0;
+
+	for (j = 0; j < y_ch; j++) {
+		nvt_print_data_log_in_one_line(array + j * x_ch, x_ch);
+		printk("\n");
+	}
+#if TOUCH_KEY_NUM > 0
+	nvt_print_data_log_in_one_line(array + y_ch * x_ch, Key_Channel);
+	printk("\n");
+#endif /* #if TOUCH_KEY_NUM > 0 */
+}
+
+static void nvt_print_criteria(void)
+{
+	NVT_LOG("++\n");
+
+	//---PS_Config_Lmt_Short_Rawdata---
+	printk("PS_Config_Lmt_Short_Rawdata_P:\n");
+	nvt_print_lmt_array(PS_Config_Lmt_Short_Rawdata_P, X_Channel, Y_Channel);
+	printk("PS_Config_Lmt_Short_Rawdata_N:\n");
+	nvt_print_lmt_array(PS_Config_Lmt_Short_Rawdata_N, X_Channel, Y_Channel);
+
+	//---PS_Config_Lmt_Open_Rawdata---
+	printk("PS_Config_Lmt_Open_Rawdata_P:\n");
+	nvt_print_lmt_array(PS_Config_Lmt_Open_Rawdata_P, X_Channel, Y_Channel);
+	printk("PS_Config_Lmt_Open_Rawdata_N:\n");
+	nvt_print_lmt_array(PS_Config_Lmt_Open_Rawdata_N, X_Channel, Y_Channel);
+
+	//---PS_Config_Lmt_FW_Rawdata---
+	printk("PS_Config_Lmt_FW_Rawdata_P:\n");
+	nvt_print_lmt_array(PS_Config_Lmt_FW_Rawdata_P, X_Channel, Y_Channel);
+	printk("PS_Config_Lmt_FW_Rawdata_N:\n");
+	nvt_print_lmt_array(PS_Config_Lmt_FW_Rawdata_N, X_Channel, Y_Channel);
+
+	//---PS_Config_Lmt_FW_CC---
+	printk("PS_Config_Lmt_FW_CC_P:\n");
+	nvt_print_lmt_array(PS_Config_Lmt_FW_CC_P, X_Channel, Y_Channel);
+	printk("PS_Config_Lmt_FW_CC_N:\n");
+	nvt_print_lmt_array(PS_Config_Lmt_FW_CC_N, X_Channel, Y_Channel);
+
+	//---PS_Config_Lmt_FW_Diff---
+	printk("PS_Config_Lmt_FW_Diff_P:\n");
+	nvt_print_lmt_array(PS_Config_Lmt_FW_Diff_P, X_Channel, Y_Channel);
+	printk("PS_Config_Lmt_FW_Diff_N:\n");
+	nvt_print_lmt_array(PS_Config_Lmt_FW_Diff_N, X_Channel, Y_Channel);
+
+	NVT_LOG("--\n");
+}
+
+static int32_t nvt_polling_hand_shake_status(void)
+{
+	uint8_t buf[8] = {0};
+	int32_t i = 0;
+	const int32_t retry = 70;
+
+	for (i = 0; i < retry; i++) {
+		//---set xdata index to EVENT BUF ADDR---
+		nvt_set_page(I2C_FW_Address, ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE);
+
+		//---read fw status---
+		buf[0] = EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE;
+		buf[1] = 0x00;
+		CTP_I2C_READ(ts->client, I2C_FW_Address, buf, 2);
+
+		if ((buf[1] == 0xA0) || (buf[1] == 0xA1))
+			break;
+
+		usleep_range(10000, 10000);
+	}
+
+	if (i >= retry) {
+		NVT_ERR("polling hand shake status failed, buf[1]=0x%02X\n", buf[1]);
+
+		// Read back 5 bytes from offset EVENT_MAP_HOST_CMD for debug check
+		nvt_set_page(I2C_FW_Address, ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_HOST_CMD);
+
+		buf[0] = EVENT_MAP_HOST_CMD;
+		buf[1] = 0x00;
+		buf[2] = 0x00;
+		buf[3] = 0x00;
+		buf[4] = 0x00;
+		buf[5] = 0x00;
+		CTP_I2C_READ(ts->client, I2C_FW_Address, buf, 6);
+		NVT_ERR("Read back 5 bytes from offset EVENT_MAP_HOST_CMD: 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X\n", buf[1], buf[2], buf[3], buf[4], buf[5]);
+
+		return -1;
+	} else {
+		return 0;
+	}
+}
+
+static int8_t nvt_switch_FreqHopEnDis(uint8_t FreqHopEnDis)
+{
+	uint8_t buf[8] = {0};
+	uint8_t retry = 0;
+	int8_t ret = 0;
+
+	NVT_LOG("++\n");
+
+	for (retry = 0; retry < 20; retry++) {
+		//---set xdata index to EVENT BUF ADDR---
+		nvt_set_page(I2C_FW_Address, ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_HOST_CMD);
+
+		//---switch FreqHopEnDis---
+		buf[0] = EVENT_MAP_HOST_CMD;
+		buf[1] = FreqHopEnDis;
+		CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 2);
+
+		msleep(35);
+
+		buf[0] = EVENT_MAP_HOST_CMD;
+		buf[1] = 0xFF;
+		CTP_I2C_READ(ts->client, I2C_FW_Address, buf, 2);
+
+		if (buf[1] == 0x00)
+			break;
+	}
+
+	if (unlikely(retry == 20)) {
+		NVT_ERR("switch FreqHopEnDis 0x%02X failed, buf[1]=0x%02X\n", FreqHopEnDis, buf[1]);
+		ret = -1;
+	}
+
+	NVT_LOG("--\n");
+
+	return ret;
+}
+
+static int32_t nvt_read_baseline(int32_t *xdata)
+{
+	uint8_t x_num = 0;
+	uint8_t y_num = 0;
+	uint32_t x = 0;
+	uint32_t y = 0;
+	int32_t iArrayIndex = 0;
+#if TOUCH_KEY_NUM > 0
+	int32_t k = 0;
+#endif /* #if TOUCH_KEY_NUM > 0 */
+
+	NVT_LOG("++\n");
+
+	nvt_read_mdata(ts->mmap->BASELINE_ADDR, ts->mmap->BASELINE_BTN_ADDR);
+
+	nvt_get_mdata(xdata, &x_num, &y_num);
+
+	for (y = 0; y < y_num; y++) {
+		for (x = 0; x < x_num; x++) {
+			iArrayIndex = y * x_num + x;
+			xdata[iArrayIndex] = (int16_t)xdata[iArrayIndex];
+		}
+	}
+#if TOUCH_KEY_NUM > 0
+	for (k = 0; k < Key_Channel; k++) {
+		iArrayIndex = Y_Channel * X_Channel + k;
+		xdata[iArrayIndex] = (int16_t)xdata[iArrayIndex];
+	}
+#endif /* #if TOUCH_KEY_NUM > 0 */
+
+	NVT_LOG("--\n");
+
+	return 0;
+}
+
+static int32_t nvt_read_CC(int32_t *xdata)
+{
+	uint8_t x_num = 0;
+	uint8_t y_num = 0;
+	uint32_t x = 0;
+	uint32_t y = 0;
+	int32_t iArrayIndex = 0;
+#if TOUCH_KEY_NUM > 0
+	int32_t k = 0;
+#endif /* #if TOUCH_KEY_NUM > 0 */
+
+	NVT_LOG("++\n");
+
+	if (nvt_get_fw_pipe() == 0)
+		nvt_read_mdata(ts->mmap->DIFF_PIPE1_ADDR, ts->mmap->DIFF_BTN_PIPE1_ADDR);
+	else
+		nvt_read_mdata(ts->mmap->DIFF_PIPE0_ADDR, ts->mmap->DIFF_BTN_PIPE0_ADDR);
+
+	nvt_get_mdata(xdata, &x_num, &y_num);
+
+	for (y = 0; y < y_num; y++) {
+		for (x = 0; x < x_num; x++) {
+			iArrayIndex = y * x_num + x;
+			xdata[iArrayIndex] = (int16_t)xdata[iArrayIndex];
+		}
+	}
+#if TOUCH_KEY_NUM > 0
+	for (k = 0; k < Key_Channel; k++) {
+		iArrayIndex = Y_Channel * X_Channel + k;
+		xdata[iArrayIndex] = (int16_t)xdata[iArrayIndex];
+	}
+#endif /* #if TOUCH_KEY_NUM > 0 */
+
+	NVT_LOG("--\n");
+
+	return 0;
+}
+
+static void nvt_enable_noise_collect(int32_t frame_num)
+{
+	uint8_t buf[8] = {0};
+
+	//---set xdata index to EVENT BUF ADDR---
+	nvt_set_page(I2C_FW_Address, ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_HOST_CMD);
+
+	//---enable noise collect---
+	buf[0] = EVENT_MAP_HOST_CMD;
+	buf[1] = 0x47;
+	buf[2] = 0xAA;
+	buf[3] = frame_num;
+	buf[4] = 0x00;
+	CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 5);
+}
+
+static int32_t nvt_read_fw_noise(int32_t *xdata)
+{
+	uint8_t x_num = 0;
+	uint8_t y_num = 0;
+	uint32_t x = 0;
+	uint32_t y = 0;
+	int32_t iArrayIndex = 0;
+	int32_t frame_num = 0;
+#if TOUCH_KEY_NUM > 0
+	int32_t k = 0;
+#endif /* #if TOUCH_KEY_NUM > 0 */
+
+	NVT_LOG("++\n");
+
+	//---Enter Test Mode---
+	if (nvt_clear_fw_status()) {
+		return -EAGAIN;
+	}
+
+	frame_num = PS_Config_Diff_Test_Frame / 10;
+	if (frame_num <= 0)
+		frame_num = 1;
+	printk("%s: frame_num=%d\n", __func__, frame_num);
+	nvt_enable_noise_collect(frame_num);
+	// need wait PS_Config_Diff_Test_Frame * 8.3ms
+	msleep(frame_num * 83);
+
+	if (nvt_polling_hand_shake_status()) {
+		return -EAGAIN;
+	}
+
+	if (nvt_get_fw_info()) {
+		return -EAGAIN;
+	}
+
+	if (nvt_get_fw_pipe() == 0)
+		nvt_read_mdata(ts->mmap->DIFF_PIPE0_ADDR, ts->mmap->DIFF_BTN_PIPE0_ADDR);
+	else
+		nvt_read_mdata(ts->mmap->DIFF_PIPE1_ADDR, ts->mmap->DIFF_BTN_PIPE1_ADDR);
+
+	nvt_get_mdata(xdata, &x_num, &y_num);
+
+	for (y = 0; y < y_num; y++) {
+		for (x = 0; x < x_num; x++) {
+			iArrayIndex = y * x_num + x;
+			RawData_Diff_Max[iArrayIndex] = (int8_t)((xdata[iArrayIndex] >> 8) & 0xFF);
+			RawData_Diff_Min[iArrayIndex] = (int8_t)(xdata[iArrayIndex] & 0xFF);
+		}
+	}
+#if TOUCH_KEY_NUM > 0
+	for (k = 0; k < Key_Channel; k++) {
+		iArrayIndex = Y_Channel * X_Channel + k;
+		RawData_Diff_Max[iArrayIndex] = (int8_t)((xdata[iArrayIndex] >> 8) & 0xFF);
+		RawData_Diff_Min[iArrayIndex] = (int8_t)(xdata[iArrayIndex] & 0xFF);
+	}
+#endif /* #if TOUCH_KEY_NUM > 0 */
+
+	//---Leave Test Mode---
+	nvt_change_mode(NORMAL_MODE);
+
+	NVT_LOG("--\n");
+
+	return 0;
+}
+
+static void nvt_enable_open_test(void)
+{
+	uint8_t buf[8] = {0};
+
+	//---set xdata index to EVENT BUF ADDR---
+	nvt_set_page(I2C_FW_Address, ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_HOST_CMD);
+
+	//---enable open test---
+	buf[0] = EVENT_MAP_HOST_CMD;
+	buf[1] = 0x45;
+	buf[2] = 0xAA;
+	buf[3] = 0x02;
+	buf[4] = 0x00;
+	CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 5);
+}
+
+static void nvt_enable_short_test(void)
+{
+	uint8_t buf[8] = {0};
+
+	//---set xdata index to EVENT BUF ADDR---
+	nvt_set_page(I2C_FW_Address, ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_HOST_CMD);
+
+	//---enable short test---
+	buf[0] = EVENT_MAP_HOST_CMD;
+	buf[1] = 0x43;
+	buf[2] = 0xAA;
+	buf[3] = 0x02;
+	buf[4] = 0x00;
+	CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 5);
+}
+
+static int32_t nvt_read_fw_open(int32_t *xdata)
+{
+	uint32_t raw_pipe_addr = 0;
+	uint8_t *rawdata_buf = NULL;
+	uint32_t x = 0;
+	uint32_t y = 0;
+	uint8_t buf[128] = {0};
+#if TOUCH_KEY_NUM > 0
+	uint32_t raw_btn_pipe_addr = 0;
+	int32_t k = 0;
+#endif /* #if TOUCH_KEY_NUM > 0 */
+
+	NVT_LOG("++\n");
+
+	//---Enter Test Mode---
+	if (nvt_clear_fw_status()) {
+		return -EAGAIN;
+	}
+
+	nvt_enable_open_test();
+
+	if (nvt_polling_hand_shake_status()) {
+		return -EAGAIN;
+	}
+
+#if TOUCH_KEY_NUM > 0
+	rawdata_buf = kzalloc((IC_X_CFG_SIZE * IC_Y_CFG_SIZE + IC_KEY_CFG_SIZE) * 2, GFP_KERNEL);
+#else
+	rawdata_buf = kzalloc(IC_X_CFG_SIZE * IC_Y_CFG_SIZE * 2, GFP_KERNEL);
+#endif /* #if TOUCH_KEY_NUM > 0 */
+	if (!rawdata_buf) {
+		NVT_ERR("kzalloc for rawdata_buf failed!\n");
+		return -ENOMEM;
+	}
+
+	if (nvt_get_fw_pipe() == 0)
+		raw_pipe_addr = ts->mmap->RAW_PIPE0_ADDR;
+	else
+		raw_pipe_addr = ts->mmap->RAW_PIPE1_ADDR;
+
+	for (y = 0; y < IC_Y_CFG_SIZE; y++) {
+		//---change xdata index---
+		nvt_set_page(I2C_FW_Address, raw_pipe_addr + y * IC_X_CFG_SIZE * 2);
+		buf[0] = (uint8_t)((raw_pipe_addr + y * IC_X_CFG_SIZE * 2) & 0xFF);
+		CTP_I2C_READ(ts->client, I2C_FW_Address, buf, IC_X_CFG_SIZE * 2 + 1);
+		memcpy(rawdata_buf + y * IC_X_CFG_SIZE * 2, buf + 1, IC_X_CFG_SIZE * 2);
+	}
+#if TOUCH_KEY_NUM > 0
+	if (nvt_get_fw_pipe() == 0)
+		raw_btn_pipe_addr = ts->mmap->RAW_BTN_PIPE0_ADDR;
+	else
+		raw_btn_pipe_addr = ts->mmap->RAW_BTN_PIPE1_ADDR;
+
+	//---change xdata index---
+	nvt_set_page(I2C_FW_Address, raw_btn_pipe_addr);
+	buf[0] = (uint8_t)(raw_btn_pipe_addr & 0xFF);
+	CTP_I2C_READ(ts->client, I2C_FW_Address, buf, IC_KEY_CFG_SIZE * 2 + 1);
+	memcpy(rawdata_buf + IC_Y_CFG_SIZE * IC_X_CFG_SIZE * 2, buf + 1, IC_KEY_CFG_SIZE * 2);
+#endif /* #if TOUCH_KEY_NUM > 0 */
+
+	for (y = 0; y < IC_Y_CFG_SIZE; y++) {
+		for (x = 0; x < IC_X_CFG_SIZE; x++) {
+			if ((AIN_Y[y] != 0xFF) && (AIN_X[x] != 0xFF)) {
+				xdata[AIN_Y[y] * X_Channel + AIN_X[x]] = (int16_t)((rawdata_buf[(y * IC_X_CFG_SIZE + x) * 2] + 256 * rawdata_buf[(y * IC_X_CFG_SIZE + x) * 2 + 1]));
+			}
+		}
+	}
+#if TOUCH_KEY_NUM > 0
+	for (k = 0; k < IC_KEY_CFG_SIZE; k++) {
+		if (AIN_KEY[k] != 0xFF)
+			xdata[Y_Channel * X_Channel + AIN_KEY[k]] = (int16_t)(rawdata_buf[(IC_Y_CFG_SIZE * IC_X_CFG_SIZE + k) * 2] + 256 * rawdata_buf[(IC_Y_CFG_SIZE * IC_X_CFG_SIZE + k) * 2 + 1]);
+	}
+#endif /* #if TOUCH_KEY_NUM > 0 */
+
+	if (rawdata_buf) {
+		kfree(rawdata_buf);
+		rawdata_buf = NULL;
+	}
+
+	//---Leave Test Mode---
+	nvt_change_mode(NORMAL_MODE);
+
+	NVT_LOG("--\n");
+
+	return 0;
+}
+
+static int32_t nvt_read_fw_short(int32_t *xdata)
+{
+	uint32_t raw_pipe_addr = 0;
+	uint8_t *rawdata_buf = NULL;
+	uint32_t x = 0;
+	uint32_t y = 0;
+	uint8_t buf[128] = {0};
+	int32_t iArrayIndex = 0;
+#if TOUCH_KEY_NUM > 0
+	uint32_t raw_btn_pipe_addr = 0;
+	int32_t k = 0;
+#endif /* #if TOUCH_KEY_NUM > 0 */
+
+	NVT_LOG("++\n");
+
+	//---Enter Test Mode---
+	if (nvt_clear_fw_status()) {
+		return -EAGAIN;
+	}
+
+	nvt_enable_short_test();
+
+	if (nvt_polling_hand_shake_status()) {
+		return -EAGAIN;
+	}
+
+#if TOUCH_KEY_NUM > 0
+    rawdata_buf = kzalloc((X_Channel * Y_Channel + Key_Channel) * 2, GFP_KERNEL);
+#else
+    rawdata_buf = kzalloc(X_Channel * Y_Channel * 2, GFP_KERNEL);
+#endif /* #if TOUCH_KEY_NUM > 0 */
+	if (!rawdata_buf) {
+		NVT_ERR("kzalloc for rawdata_buf failed!\n");
+		return -ENOMEM;
+	}
+
+	if (nvt_get_fw_pipe() == 0)
+		raw_pipe_addr = ts->mmap->RAW_PIPE0_ADDR;
+	else
+		raw_pipe_addr = ts->mmap->RAW_PIPE1_ADDR;
+
+	for (y = 0; y < Y_Channel; y++) {
+		//---change xdata index---
+		nvt_set_page(I2C_FW_Address, raw_pipe_addr + y * X_Channel * 2);
+		buf[0] = (uint8_t)((raw_pipe_addr + y * X_Channel * 2) & 0xFF);
+		CTP_I2C_READ(ts->client, I2C_FW_Address, buf, X_Channel * 2 + 1);
+		memcpy(rawdata_buf + y * X_Channel * 2, buf + 1, X_Channel * 2);
+	}
+#if TOUCH_KEY_NUM > 0
+	if (nvt_get_fw_pipe() == 0)
+		raw_btn_pipe_addr = ts->mmap->RAW_BTN_PIPE0_ADDR;
+	else
+		raw_btn_pipe_addr = ts->mmap->RAW_BTN_PIPE1_ADDR;
+
+    //---change xdata index---
+	nvt_set_page(I2C_FW_Address, raw_btn_pipe_addr);
+	buf[0] = (uint8_t)(raw_btn_pipe_addr & 0xFF);
+	CTP_I2C_READ(ts->client, I2C_FW_Address, buf, Key_Channel * 2 + 1);
+	memcpy(rawdata_buf + Y_Channel * X_Channel * 2, buf + 1, Key_Channel * 2);
+#endif /* #if TOUCH_KEY_NUM > 0 */
+
+	for (y = 0; y < Y_Channel; y++) {
+		for (x = 0; x < X_Channel; x++) {
+			iArrayIndex = y * X_Channel + x;
+			xdata[iArrayIndex] = (int16_t)(rawdata_buf[iArrayIndex * 2] + 256 * rawdata_buf[iArrayIndex * 2 + 1]);
+		}
+	}
+#if TOUCH_KEY_NUM > 0
+	for (k = 0; k < Key_Channel; k++) {
+		iArrayIndex = Y_Channel * X_Channel + k;
+		xdata[iArrayIndex] = (int16_t)(rawdata_buf[iArrayIndex * 2] + 256 * rawdata_buf[iArrayIndex * 2 + 1]);
+	}
+#endif /* #if TOUCH_KEY_NUM > 0 */
+
+	if (rawdata_buf) {
+		kfree(rawdata_buf);
+		rawdata_buf = NULL;
+	}
+
+	//---Leave Test Mode---
+	nvt_change_mode(NORMAL_MODE);
+
+	NVT_LOG("--\n");
+
+	return 0;
+}
+
+/*******************************************************
+Description:
+	Novatek touchscreen raw data test for each single point function.
+
+return:
+	Executive outcomes. 0---passed. negative---failed.
+*******************************************************/
+static int32_t RawDataTest_SinglePoint_Sub(int32_t rawdata[], uint8_t RecordResult[], uint8_t x_ch, uint8_t y_ch, int32_t Rawdata_Limit_Postive[], int32_t Rawdata_Limit_Negative[])
+{
+	int32_t i = 0;
+	int32_t j = 0;
+#if TOUCH_KEY_NUM > 0
+    int32_t k = 0;
+#endif /* #if TOUCH_KEY_NUM > 0 */
+	int32_t iArrayIndex = 0;
+	bool isPass = true;
+
+	for (j = 0; j < y_ch; j++) {
+		for (i = 0; i < x_ch; i++) {
+			iArrayIndex = j * x_ch + i;
+
+			RecordResult[iArrayIndex] = 0x00; // default value for PASS
+
+			if(rawdata[iArrayIndex] > Rawdata_Limit_Postive[iArrayIndex])
+				RecordResult[iArrayIndex] |= 0x01;
+
+			if(rawdata[iArrayIndex] < Rawdata_Limit_Negative[iArrayIndex])
+				RecordResult[iArrayIndex] |= 0x02;
+		}
+	}
+#if TOUCH_KEY_NUM > 0
+	for (k = 0; k < Key_Channel; k++) {
+		iArrayIndex = y_ch * x_ch + k;
+
+		RecordResult[iArrayIndex] = 0x00; // default value for PASS
+
+		if(rawdata[iArrayIndex] > Rawdata_Limit_Postive[iArrayIndex])
+			RecordResult[iArrayIndex] |= 0x01;
+
+		if(rawdata[iArrayIndex] < Rawdata_Limit_Negative[iArrayIndex])
+			RecordResult[iArrayIndex] |= 0x02;
+	}
+#endif /* #if TOUCH_KEY_NUM > 0 */
+
+	//---Check RecordResult---
+	for (j = 0; j < y_ch; j++) {
+		for (i = 0; i < x_ch; i++) {
+			if (RecordResult[j * x_ch + i] != 0) {
+				isPass = false;
+				break;
+			}
+		}
+	}
+#if TOUCH_KEY_NUM > 0
+	for (k = 0; k < Key_Channel; k++) {
+		iArrayIndex = y_ch * x_ch + k;
+		if (RecordResult[iArrayIndex] != 0) {
+			isPass = false;
+			break;
+		}
+	}
+#endif /* #if TOUCH_KEY_NUM > 0 */
+
+	if (!isPass) {
+		return -1; // FAIL
+	} else {
+		return 0; // PASS
+	}
+}
+
+/*******************************************************
+Description:
+	Novatek touchscreen print self-test result function.
+
+return:
+	n.a.
+*******************************************************/
+void print_selftest_result(struct seq_file *m, int32_t TestResult, uint8_t RecordResult[], int32_t rawdata[], uint8_t x_len, uint8_t y_len)
+{
+	int32_t i = 0;
+	int32_t j = 0;
+	int32_t iArrayIndex = 0;
+#if TOUCH_KEY_NUM > 0
+	int32_t k = 0;
+#endif /* #if TOUCH_KEY_NUM > 0 */
+
+	switch (TestResult) {
+		case 0:
+			nvt_mp_seq_printf(m, " PASS!\n");
+			break;
+
+		case 1:
+			nvt_mp_seq_printf(m, " ERROR! Read Data FAIL!\n");
+			break;
+
+		case -1:
+			nvt_mp_seq_printf(m, " FAIL!\n");
+			nvt_mp_seq_printf(m, "RecordResult:\n");
+			for (i = 0; i < y_len; i++) {
+				for (j = 0; j < x_len; j++) {
+					iArrayIndex = i * x_len + j;
+					seq_printf(m, "0x%02X, ", RecordResult[iArrayIndex]);
+				}
+				if (!nvt_mp_test_result_printed)
+					nvt_print_result_log_in_one_line(RecordResult + i * x_len, x_len);
+				nvt_mp_seq_printf(m, "\n");
+			}
+#if TOUCH_KEY_NUM > 0
+			for (k = 0; k < Key_Channel; k++) {
+				iArrayIndex = y_len * x_len + k;
+				seq_printf(m, "0x%02X, ", RecordResult[iArrayIndex]);
+			}
+			if (!nvt_mp_test_result_printed)
+				nvt_print_result_log_in_one_line(RecordResult + y_len * x_len, Key_Channel);
+			nvt_mp_seq_printf(m, "\n");
+#endif /* #if TOUCH_KEY_NUM > 0 */
+			nvt_mp_seq_printf(m, "ReadData:\n");
+			for (i = 0; i < y_len; i++) {
+				for (j = 0; j < x_len; j++) {
+					iArrayIndex = i * x_len + j;
+					seq_printf(m, "%5d, ", rawdata[iArrayIndex]);
+				}
+				if (!nvt_mp_test_result_printed)
+					nvt_print_data_log_in_one_line(rawdata + i * x_len, x_len);
+				nvt_mp_seq_printf(m, "\n");
+			}
+#if TOUCH_KEY_NUM > 0
+			for (k = 0; k < Key_Channel; k++) {
+				iArrayIndex = y_len * x_len + k;
+				seq_printf(m, "%5d, ", rawdata[iArrayIndex]);
+			}
+			if (!nvt_mp_test_result_printed)
+				nvt_print_data_log_in_one_line(rawdata + y_len * x_len, Key_Channel);
+			nvt_mp_seq_printf(m, "\n");
+#endif /* #if TOUCH_KEY_NUM > 0 */
+			break;
+	}
+	nvt_mp_seq_printf(m, "\n");
+}
+
+/*******************************************************
+Description:
+	Novatek touchscreen self-test sequence print show
+	function.
+
+return:
+	Executive outcomes. 0---succeed.
+*******************************************************/
+static int32_t c_show_selftest(struct seq_file *m, void *v)
+{
+	NVT_LOG("++\n");
+
+	nvt_mp_seq_printf(m, "FW Version: %d\n\n", fw_ver);
+
+	nvt_mp_seq_printf(m, "Short Test");
+
+	print_selftest_result(m, TestResult_Short, RecordResult_Short, RawData_Short, X_Channel, Y_Channel);
+
+	nvt_mp_seq_printf(m, "Open Test");
+	print_selftest_result(m, TestResult_Open, RecordResult_Open, RawData_Open, X_Channel, Y_Channel);
+
+	nvt_mp_seq_printf(m, "FW Rawdata Test");
+	if ((TestResult_FW_Rawdata == 0) || (TestResult_FW_Rawdata == 1)) {
+		 print_selftest_result(m, TestResult_FWMutual, RecordResult_FWMutual, RawData_FWMutual, X_Channel, Y_Channel);
+	} else { // TestResult_FW_Rawdata is -1
+		nvt_mp_seq_printf(m, " FAIL!\n");
+		if (TestResult_FWMutual == -1) {
+			nvt_mp_seq_printf(m, "FW Mutual");
+			print_selftest_result(m, TestResult_FWMutual, RecordResult_FWMutual, RawData_FWMutual, X_Channel, Y_Channel);
+		}
+		if (TestResult_FW_CC == -1) {
+			nvt_mp_seq_printf(m, "FW CC");
+			print_selftest_result(m, TestResult_FW_CC, RecordResult_FW_CC, RawData_FW_CC, X_Channel, Y_Channel);
+		}
+	}
+
+	nvt_mp_seq_printf(m, "Noise Test");
+	if ((TestResult_Noise == 0) || (TestResult_Noise == 1)) {
+		print_selftest_result(m, TestResult_FW_DiffMax, RecordResult_FW_DiffMax, RawData_Diff_Max, X_Channel, Y_Channel);
+	} else { // TestResult_Noise is -1
+		nvt_mp_seq_printf(m, " FAIL!\n");
+
+		if (TestResult_FW_DiffMax == -1) {
+			nvt_mp_seq_printf(m, "FW Diff Max");
+			print_selftest_result(m, TestResult_FW_DiffMax, RecordResult_FW_DiffMax, RawData_Diff_Max, X_Channel, Y_Channel);
+		}
+		if (TestResult_FW_DiffMin == -1) {
+			nvt_mp_seq_printf(m, "FW Diff Min");
+			print_selftest_result(m, TestResult_FW_DiffMin, RecordResult_FW_DiffMin, RawData_Diff_Min, X_Channel, Y_Channel);
+		}
+	}
+
+	nvt_mp_test_result_printed = 1;
+
+	NVT_LOG("--\n");
+
+    return 0;
+}
+
+/*******************************************************
+Description:
+	Novatek touchscreen self-test sequence print start
+	function.
+
+return:
+	Executive outcomes. 1---call next function.
+	NULL---not call next function and sequence loop
+	stop.
+*******************************************************/
+static void *c_start(struct seq_file *m, loff_t *pos)
+{
+	return *pos < 1 ? (void *)1 : NULL;
+}
+
+/*******************************************************
+Description:
+	Novatek touchscreen self-test sequence print next
+	function.
+
+return:
+	Executive outcomes. NULL---no next and call sequence
+	stop function.
+*******************************************************/
+static void *c_next(struct seq_file *m, void *v, loff_t *pos)
+{
+	++*pos;
+	return NULL;
+}
+
+/*******************************************************
+Description:
+	Novatek touchscreen self-test sequence print stop
+	function.
+
+return:
+	n.a.
+*******************************************************/
+static void c_stop(struct seq_file *m, void *v)
+{
+	return;
+}
+
+const struct seq_operations nvt_selftest_seq_ops = {
+	.start  = c_start,
+	.next   = c_next,
+	.stop   = c_stop,
+	.show   = c_show_selftest
+};
+
+/*******************************************************
+Description:
+	Novatek touchscreen /proc/nvt_selftest open function.
+
+return:
+	Executive outcomes. 0---succeed. negative---failed.
+*******************************************************/
+static int32_t nvt_selftest_open(struct inode *inode, struct file *file)
+{
+	struct device_node *np = ts->client->dev.of_node;
+
+	memset(mpcriteria, 0, PAGE_SIZE * sizeof(mpcriteria[0]));
+	TestResult_Short = 0;
+	TestResult_Open = 0;
+	TestResult_FW_Rawdata = 0;
+	TestResult_FWMutual = 0;
+	TestResult_FW_CC = 0;
+	TestResult_Noise = 0;
+	TestResult_FW_DiffMax = 0;
+	TestResult_FW_DiffMin = 0;
+
+	NVT_LOG("++\n");
+
+	if (mutex_lock_interruptible(&ts->lock)) {
+		return -ERESTARTSYS;
+	}
+
+#if NVT_TOUCH_ESD_PROTECT
+	nvt_esd_check_enable(false);
+#endif /* #if NVT_TOUCH_ESD_PROTECT */
+
+	if (nvt_get_fw_info()) {
+		mutex_unlock(&ts->lock);
+		NVT_ERR("get fw info failed!\n");
+		return -EAGAIN;
+	}
+
+	fw_ver = ts->fw_ver;
+
+	/* Parsing criteria from dts */
+	if(of_property_read_bool(np, "novatek,mp-support-dt")) {
+		/*
+		 * Parsing Criteria by Novatek PID
+		 * The string rule is "novatek-mp-criteria-<nvt_pid>"
+		 * nvt_pid is 2 bytes (show hex).
+		 *
+		 * Ex. nvt_pid = 500A
+		 *     mpcriteria = "novatek-mp-criteria-500A"
+		 */
+		snprintf(mpcriteria, PAGE_SIZE, "novatek-mp-criteria-%04X", ts->nvt_pid);
+
+		if (nvt_mp_parse_dt(np, mpcriteria)) {
+			mutex_unlock(&ts->lock);
+			NVT_ERR("mp parse device tree failed!\n");
+			return -EINVAL;
+		}
+	} else {
+		NVT_LOG("Not found novatek,mp-support-dt, use default setting\n");
+		//---Print Test Criteria---
+		nvt_print_criteria();
+	}
+
+	if (nvt_switch_FreqHopEnDis(FREQ_HOP_DISABLE)) {
+		mutex_unlock(&ts->lock);
+		NVT_ERR("switch frequency hopping disable failed!\n");
+		return -EAGAIN;
+	}
+
+	if (nvt_check_fw_reset_state(RESET_STATE_NORMAL_RUN)) {
+		mutex_unlock(&ts->lock);
+		NVT_ERR("check fw reset state failed!\n");
+		return -EAGAIN;
+	}
+
+	msleep(100);
+
+	//---Enter Test Mode---
+	if (nvt_clear_fw_status()) {
+		mutex_unlock(&ts->lock);
+		NVT_ERR("clear fw status failed!\n");
+		return -EAGAIN;
+	}
+
+	nvt_change_mode(MP_MODE_CC);
+
+	if (nvt_check_fw_status()) {
+		mutex_unlock(&ts->lock);
+		NVT_ERR("check fw status failed!\n");
+		return -EAGAIN;
+	}
+
+	//---FW Rawdata Test---
+	if (nvt_read_baseline(RawData_FWMutual) != 0) {
+		TestResult_FWMutual = 1;
+	} else {
+		TestResult_FWMutual = RawDataTest_SinglePoint_Sub(RawData_FWMutual, RecordResult_FWMutual, X_Channel, Y_Channel,
+						PS_Config_Lmt_FW_Rawdata_P, PS_Config_Lmt_FW_Rawdata_N);
+	}
+	if (nvt_read_CC(RawData_FW_CC) != 0) {
+		TestResult_FW_CC = 1;
+	} else {
+		TestResult_FW_CC = RawDataTest_SinglePoint_Sub(RawData_FW_CC, RecordResult_FW_CC, X_Channel, Y_Channel,
+						PS_Config_Lmt_FW_CC_P, PS_Config_Lmt_FW_CC_N);
+	}
+
+	if ((TestResult_FWMutual == 1) || (TestResult_FW_CC == 1)) {
+		TestResult_FW_Rawdata = 1;
+	} else {
+		if ((TestResult_FWMutual == -1) || (TestResult_FW_CC == -1))
+			TestResult_FW_Rawdata = -1;
+		else
+			TestResult_FW_Rawdata = 0;
+	}
+
+	//---Leave Test Mode---
+	nvt_change_mode(NORMAL_MODE);
+
+	//---Noise Test---
+	if (nvt_read_fw_noise(RawData_Diff) != 0) {
+		TestResult_Noise = 1;	// 1: ERROR
+		TestResult_FW_DiffMax = 1;
+		TestResult_FW_DiffMin = 1;
+	} else {
+		TestResult_FW_DiffMax = RawDataTest_SinglePoint_Sub(RawData_Diff_Max, RecordResult_FW_DiffMax, X_Channel, Y_Channel,
+						PS_Config_Lmt_FW_Diff_P, PS_Config_Lmt_FW_Diff_N);
+
+		TestResult_FW_DiffMin = RawDataTest_SinglePoint_Sub(RawData_Diff_Min, RecordResult_FW_DiffMin, X_Channel, Y_Channel,
+						PS_Config_Lmt_FW_Diff_P, PS_Config_Lmt_FW_Diff_N);
+
+		if ((TestResult_FW_DiffMax == -1) || (TestResult_FW_DiffMin == -1))
+			TestResult_Noise = -1;
+		else
+			TestResult_Noise = 0;
+	}
+
+	//--Short Test---
+	if (nvt_read_fw_short(RawData_Short) != 0) {
+		TestResult_Short = 1; // 1:ERROR
+	} else {
+		//---Self Test Check --- // 0:PASS, -1:FAIL
+		TestResult_Short = RawDataTest_SinglePoint_Sub(RawData_Short, RecordResult_Short, X_Channel, Y_Channel,
+						PS_Config_Lmt_Short_Rawdata_P, PS_Config_Lmt_Short_Rawdata_N);
+	}
+
+	//---Open Test---
+	if (nvt_read_fw_open(RawData_Open) != 0) {
+		TestResult_Open = 1;    // 1:ERROR
+	} else {
+		//---Self Test Check --- // 0:PASS, -1:FAIL
+		TestResult_Open = RawDataTest_SinglePoint_Sub(RawData_Open, RecordResult_Open, X_Channel, Y_Channel,
+						PS_Config_Lmt_Open_Rawdata_P, PS_Config_Lmt_Open_Rawdata_N);
+	}
+
+	//---Reset IC---
+	nvt_bootloader_reset();
+
+	mutex_unlock(&ts->lock);
+
+	NVT_LOG("--\n");
+
+	nvt_mp_test_result_printed = 0;
+
+	return seq_open(file, &nvt_selftest_seq_ops);
+}
+
+static const struct proc_ops nvt_selftest_fops = {
+	.proc_open = nvt_selftest_open,
+	.proc_read = seq_read,
+	.proc_lseek = seq_lseek,
+	.proc_release = seq_release,
+};
+
+#ifdef CONFIG_OF
+/*******************************************************
+Description:
+	Novatek touchscreen parse AIN setting for array type.
+
+return:
+	n.a.
+*******************************************************/
+int32_t nvt_mp_parse_ain(struct device_node *np, const char *name, uint8_t *array, int32_t size)
+{
+	struct property *data;
+	int32_t len, ret;
+	int32_t tmp[40];
+	int32_t i;
+
+	data = of_find_property(np, name, &len);
+	len /= sizeof(u32);
+	if ((!data) || (!len) || (len != size)) {
+		NVT_ERR("error find %s. len=%d\n", name, len);
+		return -1;
+	} else {
+		NVT_LOG("%s. len=%d\n", name, len);
+		ret = of_property_read_u32_array(np, name, tmp, len);
+		if (ret) {
+			NVT_ERR("error reading %s. ret=%d\n", name, ret);
+			return -1;
+		}
+
+		for (i = 0; i < len; i++)
+			array[i] = tmp[i];
+
+#if NVT_DEBUG
+		printk("[NVT-ts] %s = ", name);
+		nvt_print_result_log_in_one_line(array, len);
+		printk("\n");
+#endif
+	}
+
+	return 0;
+}
+
+/*******************************************************
+Description:
+	Novatek touchscreen parse criterion for u32 type.
+
+return:
+	n.a.
+*******************************************************/
+int32_t nvt_mp_parse_u32(struct device_node *np, const char *name, int32_t *para)
+{
+	int32_t ret;
+
+	ret = of_property_read_u32(np, name, para);
+	if (ret) {
+		NVT_ERR("error reading %s. ret=%d\n", name, ret);
+		return -1;
+	} else {
+#if NVT_DEBUG
+		NVT_LOG("%s=%d\n", name, *para);
+#endif
+	}
+
+	return 0;
+}
+
+/*******************************************************
+Description:
+	Novatek touchscreen parse criterion for array type.
+
+return:
+	n.a.
+*******************************************************/
+int32_t nvt_mp_parse_array(struct device_node *np, const char *name, int32_t *array,
+		int32_t size)
+{
+	struct property *data;
+	int32_t len, ret;
+#if NVT_DEBUG
+	int32_t j = 0;
+#endif
+
+	data = of_find_property(np, name, &len);
+	len /= sizeof(u32);
+	if ((!data) || (!len) || (len < size)) {
+		NVT_ERR("error find %s. len=%d\n", name, len);
+		return -1;
+	} else {
+		NVT_LOG("%s. len=%d\n", name, len);
+		ret = of_property_read_u32_array(np, name, array, len);
+		if (ret) {
+			NVT_ERR("error reading %s. ret=%d\n", name, ret);
+			return -1;
+		}
+
+#if NVT_DEBUG
+		NVT_LOG("%s =\n", name);
+		for (j = 0; j < Y_Channel; j++) {
+			nvt_print_data_log_in_one_line(array + j * X_Channel, X_Channel);
+			printk("\n");
+		}
+#if TOUCH_KEY_NUM > 0
+		nvt_print_data_log_in_one_line(array + Y_Channel * X_Channel, Key_Channel);
+		printk("\n");
+#endif
+#endif
+	}
+
+	return 0;
+}
+
+/*******************************************************
+Description:
+	Novatek touchscreen parse device tree mp function.
+
+return:
+	n.a.
+*******************************************************/
+int32_t nvt_mp_parse_dt(struct device_node *root, const char *node_compatible)
+{
+	struct device_node *np = root;
+	struct device_node *child = NULL;
+
+	NVT_LOG("Parse mp criteria for node %s\n", node_compatible);
+
+	/* find each MP sub-nodes */
+	for_each_child_of_node(root, child) {
+		/* find the specified node */
+		if (of_device_is_compatible(child, node_compatible)) {
+			NVT_LOG("found child node %s\n", node_compatible);
+			np = child;
+			break;
+		}
+	}
+	if (child == NULL) {
+		NVT_ERR("Not found compatible node %s!\n", node_compatible);
+		return -1;
+	}
+
+	/* MP Config*/
+	if (nvt_mp_parse_u32(np, "IC_X_CFG_SIZE", &IC_X_CFG_SIZE))
+		return -1;
+
+	if (nvt_mp_parse_u32(np, "IC_Y_CFG_SIZE", &IC_Y_CFG_SIZE))
+		return -1;
+
+#if TOUCH_KEY_NUM > 0
+	if (nvt_mp_parse_u32(np, "IC_KEY_CFG_SIZE", &IC_KEY_CFG_SIZE))
+		return -1;
+#endif
+
+	if (nvt_mp_parse_u32(np, "X_Channel", &X_Channel))
+		return -1;
+
+	if (nvt_mp_parse_u32(np, "Y_Channel", &Y_Channel))
+		return -1;
+
+	if (nvt_mp_parse_ain(np, "AIN_X", AIN_X, IC_X_CFG_SIZE))
+		return -1;
+
+	if (nvt_mp_parse_ain(np, "AIN_Y", AIN_Y, IC_Y_CFG_SIZE))
+		return -1;
+
+#if TOUCH_KEY_NUM > 0
+	if (nvt_mp_parse_ain(np, "AIN_KEY", AIN_KEY, IC_KEY_CFG_SIZE))
+		return -1;
+#endif
+
+	/* MP Criteria */
+	if (nvt_mp_parse_array(np, "PS_Config_Lmt_Short_Rawdata_P", PS_Config_Lmt_Short_Rawdata_P,
+			X_Channel * Y_Channel + Key_Channel))
+		return -1;
+
+	if (nvt_mp_parse_array(np, "PS_Config_Lmt_Short_Rawdata_N", PS_Config_Lmt_Short_Rawdata_N,
+			X_Channel * Y_Channel + Key_Channel))
+		return -1;
+
+	if (nvt_mp_parse_array(np, "PS_Config_Lmt_Open_Rawdata_P", PS_Config_Lmt_Open_Rawdata_P,
+			X_Channel * Y_Channel + Key_Channel))
+		return -1;
+
+	if (nvt_mp_parse_array(np, "PS_Config_Lmt_Open_Rawdata_N", PS_Config_Lmt_Open_Rawdata_N,
+			X_Channel * Y_Channel + Key_Channel))
+		return -1;
+
+	if (nvt_mp_parse_array(np, "PS_Config_Lmt_FW_Rawdata_P", PS_Config_Lmt_FW_Rawdata_P,
+			X_Channel * Y_Channel + Key_Channel))
+		return -1;
+
+	if (nvt_mp_parse_array(np, "PS_Config_Lmt_FW_Rawdata_N", PS_Config_Lmt_FW_Rawdata_N,
+			X_Channel * Y_Channel + Key_Channel))
+		return -1;
+
+	if (nvt_mp_parse_array(np, "PS_Config_Lmt_FW_CC_P", PS_Config_Lmt_FW_CC_P,
+			X_Channel * Y_Channel + Key_Channel))
+		return -1;
+
+	if (nvt_mp_parse_array(np, "PS_Config_Lmt_FW_CC_N", PS_Config_Lmt_FW_CC_N,
+			X_Channel * Y_Channel + Key_Channel))
+		return -1;
+
+	if (nvt_mp_parse_array(np, "PS_Config_Lmt_FW_Diff_P", PS_Config_Lmt_FW_Diff_P,
+			X_Channel * Y_Channel + Key_Channel))
+		return -1;
+
+	if (nvt_mp_parse_array(np, "PS_Config_Lmt_FW_Diff_N", PS_Config_Lmt_FW_Diff_N,
+			X_Channel * Y_Channel + Key_Channel))
+		return -1;
+
+	if (nvt_mp_parse_u32(np, "PS_Config_Diff_Test_Frame", &PS_Config_Diff_Test_Frame))
+		return -1;
+
+	NVT_LOG("Parse mp criteria done!\n");
+
+	return 0;
+}
+#endif /* #ifdef CONFIG_OF */
+
+/*******************************************************
+Description:
+	Novatek touchscreen MP function proc. file node
+	initial function.
+
+return:
+	Executive outcomes. 0---succeed. -1---failed.
+*******************************************************/
+int32_t nvt_mp_proc_init(void)
+{
+	NVT_proc_selftest_entry = proc_create("nvt_selftest", 0444, NULL, &nvt_selftest_fops);
+	if (NVT_proc_selftest_entry == NULL) {
+		NVT_ERR("create /proc/nvt_selftest Failed!\n");
+		return -1;
+	} else {
+		if(nvt_mp_buffer_init()) {
+			NVT_ERR("Allocate mp memory failed\n");
+			return -1;
+		}
+		else {
+			NVT_LOG("create /proc/nvt_selftest Succeeded!\n");
+		}
+		return 0;
+	}
+}
+
+/*******************************************************
+Description:
+	Novatek touchscreen MP function proc. file node
+	deinitial function.
+
+return:
+	n.a.
+*******************************************************/
+void nvt_mp_proc_deinit(void)
+{
+	nvt_mp_buffer_deinit();
+
+	if (NVT_proc_selftest_entry != NULL) {
+		remove_proc_entry("nvt_selftest", NULL);
+		NVT_proc_selftest_entry = NULL;
+		NVT_LOG("Removed /proc/%s\n", "nvt_selftest");
+	}
+}
+#endif /* #if NVT_TOUCH_MP */

+ 460 - 0
qcom/opensource/touch-drivers/nt36xxx/nt36xxx_mp_ctrlram.h

@@ -0,0 +1,460 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2010 - 2018 Novatek, Inc.
+ *
+ * $Revision: 46179 $
+ * $Date: 2019-06-14 13:47:17 +0800 (Fri, 14 Jun 2019) $
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#if NVT_TOUCH_MP
+
+static uint32_t IC_X_CFG_SIZE = 18;
+static uint32_t IC_Y_CFG_SIZE = 36;
+static uint32_t IC_KEY_CFG_SIZE = 4;
+static uint32_t X_Channel = 18;
+static uint32_t Y_Channel = 36;
+static uint32_t Key_Channel = TOUCH_KEY_NUM;
+static uint8_t AIN_X[40] =
+	{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17};
+static uint8_t AIN_Y[40] =
+	{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
+	18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35};
+
+#if TOUCH_KEY_NUM > 0
+static uint8_t AIN_KEY[8] = {0, 1, 2, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+#endif /* #if TOUCH_KEY_NUM > 0 */
+
+static int32_t PS_Config_Lmt_Short_Rawdata_P[40 * 40] = {
+	14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,
+	14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,
+	14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,
+	14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,
+	14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,
+	14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,
+	14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,
+	14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,
+	14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,
+	14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,
+	14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,
+	14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,
+	14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,
+	14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,
+	14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,
+	14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,
+	14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,
+	14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,
+	14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,
+	14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,
+	14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,
+	14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,
+	14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,
+	14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,
+	14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,
+	14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,
+	14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,
+	14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,
+	14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,
+	14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,
+	14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,
+	14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,
+	14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,
+	14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,
+	14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,
+	14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,
+#if TOUCH_KEY_NUM > 0
+	14008,14008,14008,
+#endif /* #if TOUCH_KEY_NUM > 0 */
+};
+
+static int32_t PS_Config_Lmt_Short_Rawdata_N[40 * 40] = {
+	10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,
+	10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,
+	10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,
+	10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,
+	10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,
+	10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,
+	10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,
+	10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,
+	10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,
+	10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,
+	10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,
+	10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,
+	10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,
+	10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,
+	10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,
+	10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,
+	10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,
+	10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,
+	10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,
+	10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,
+	10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,
+	10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,
+	10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,
+	10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,
+	10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,
+	10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,
+	10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,
+	10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,
+	10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,
+	10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,
+	10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,
+	10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,
+	10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,
+	10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,
+	10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,
+	10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,
+#if TOUCH_KEY_NUM > 0
+	10000,10000,10000,
+#endif /* #if TOUCH_KEY_NUM > 0 */
+};
+
+static int32_t PS_Config_Lmt_Open_Rawdata_P[40 * 40] = {
+	5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,
+	5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,
+	5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,
+	5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,
+	5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,
+	5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,
+	5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,
+	5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,
+	5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,
+	5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,
+	5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,
+	5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,
+	5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,
+	5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,
+	5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,
+	5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,
+	5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,
+	5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,
+	5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,
+	5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,
+	5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,
+	5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,
+	5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,
+	5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,
+	5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,
+	5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,
+	5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,
+	5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,
+	5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,
+	5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,
+	5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,
+	5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,
+	5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,
+	5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,
+	5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,
+	5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,
+#if TOUCH_KEY_NUM > 0
+	5120,5120,5120,
+#endif /* #if TOUCH_KEY_NUM > 0 */
+};
+
+static int32_t PS_Config_Lmt_Open_Rawdata_N[40 * 40] = {
+	50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,
+	50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,
+	50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,
+	50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,
+	50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,
+	50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,
+	50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,
+	50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,
+	50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,
+	50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,
+	50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,
+	50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,
+	50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,
+	50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,
+	50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,
+	50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,
+	50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,
+	50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,
+	50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,
+	50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,
+	50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,
+	50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,
+	50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,
+	50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,
+	50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,
+	50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,
+	50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,
+	50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,
+	50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,
+	50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,
+	50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,
+	50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,
+	50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,
+	50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,
+	50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,
+	50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,
+#if TOUCH_KEY_NUM > 0
+	50,50,50,
+#endif /* #if TOUCH_KEY_NUM > 0 */
+};
+
+static int32_t PS_Config_Lmt_FW_Rawdata_P[40 * 40] = {
+	2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,
+	2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,
+	2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,
+	2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,
+	2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,
+	2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,
+	2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,
+	2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,
+	2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,
+	2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,
+	2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,
+	2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,
+	2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,
+	2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,
+	2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,
+	2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,
+	2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,
+	2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,
+	2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,
+	2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,
+	2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,
+	2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,
+	2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,
+	2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,
+	2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,
+	2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,
+	2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,
+	2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,
+	2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,
+	2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,
+	2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,
+	2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,
+	2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,
+	2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,
+	2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,
+	2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,
+#if TOUCH_KEY_NUM > 0
+	2560,2560,2560,
+#endif /* #if TOUCH_KEY_NUM > 0 */
+};
+
+static int32_t PS_Config_Lmt_FW_Rawdata_N[40 * 40] = {
+	240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,
+	240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,
+	240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,
+	240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,
+	240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,
+	240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,
+	240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,
+	240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,
+	240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,
+	240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,
+	240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,
+	240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,
+	240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,
+	240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,
+	240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,
+	240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,
+	240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,
+	240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,
+	240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,
+	240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,
+	240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,
+	240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,
+	240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,
+	240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,
+	240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,
+	240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,
+	240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,
+	240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,
+	240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,
+	240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,
+	240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,
+	240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,
+	240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,
+	240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,
+	240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,
+	240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,
+#if TOUCH_KEY_NUM > 0
+	240,240,240,
+#endif /* #if TOUCH_KEY_NUM > 0 */
+};
+
+static int32_t PS_Config_Lmt_FW_CC_P[40 * 40] = {
+	314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,
+	314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,
+	314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,
+	314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,
+	314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,
+	314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,
+	314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,
+	314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,
+	314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,
+	314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,
+	314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,
+	314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,
+	314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,
+	314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,
+	314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,
+	314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,
+	314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,
+	314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,
+	314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,
+	314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,
+	314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,
+	314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,
+	314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,
+	314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,
+	314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,
+	314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,
+	314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,
+	314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,
+	314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,
+	314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,
+	314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,
+	314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,
+	314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,
+	314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,
+	314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,
+	314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,
+#if TOUCH_KEY_NUM > 0
+	314,314,314,
+#endif /* #if TOUCH_KEY_NUM > 0 */
+};
+
+static int32_t PS_Config_Lmt_FW_CC_N[40 * 40] = {
+	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+#if TOUCH_KEY_NUM > 0
+	0,0,0,
+#endif /* #if TOUCH_KEY_NUM > 0 */
+};
+
+static int32_t PS_Config_Lmt_FW_Diff_P[40 * 40] = {
+	75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,
+	75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,
+	75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,
+	75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,
+	75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,
+	75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,
+	75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,
+	75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,
+	75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,
+	75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,
+	75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,
+	75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,
+	75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,
+	75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,
+	75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,
+	75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,
+	75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,
+	75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,
+	75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,
+	75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,
+	75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,
+	75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,
+	75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,
+	75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,
+	75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,
+	75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,
+	75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,
+	75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,
+	75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,
+	75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,
+	75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,
+	75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,
+	75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,
+	75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,
+	75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,
+	75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,
+#if TOUCH_KEY_NUM > 0
+	75,75,75,
+#endif /* #if TOUCH_KEY_NUM > 0 */
+};
+
+static int32_t PS_Config_Lmt_FW_Diff_N[40 *40] = {
+	-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,
+	-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,
+	-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,
+	-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,
+	-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,
+	-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,
+	-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,
+	-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,
+	-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,
+	-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,
+	-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,
+	-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,
+	-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,
+	-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,
+	-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,
+	-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,
+	-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,
+	-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,
+	-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,
+	-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,
+	-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,
+	-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,
+	-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,
+	-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,
+	-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,
+	-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,
+	-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,
+	-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,
+	-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,
+	-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,
+	-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,
+	-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,
+	-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,
+	-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,
+	-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,
+	-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,
+#if TOUCH_KEY_NUM > 0
+	-75,-75,-75,
+#endif /* #if TOUCH_KEY_NUM > 0 */
+};
+
+static int32_t PS_Config_Diff_Test_Frame = 50;
+
+#endif /* #if NVT_TOUCH_MP */

+ 8 - 0
qcom/opensource/touch-drivers/nt36xxx/nt36xxx_spi.c

@@ -0,0 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2021 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#define NVT_NT36XXX_SPI
+
+#include "nt36xxx.c"

+ 8 - 0
qcom/opensource/touch-drivers/nt36xxx/nt36xxx_spi_ext_proc.c

@@ -0,0 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2021 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#define NVT_NT36XXX_SPI
+
+#include "nt36xxx_ext_proc.c"

+ 8 - 0
qcom/opensource/touch-drivers/nt36xxx/nt36xxx_spi_fw_update.c

@@ -0,0 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2021 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#define NVT_NT36XXX_SPI
+
+#include "nt36xxx_fw_update.c"

+ 53 - 0
qcom/opensource/touch-drivers/pt/Makefile

@@ -0,0 +1,53 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for the touchscreen drivers.
+#
+
+# Each configuration option enables a list of files.
+obj-$(CONFIG_TOUCHSCREEN_PARADE)      += pt.o
+pt-y := pt_core.o pt_mt_common.o
+pt-$(CONFIG_TOUCHSCREEN_PARADE_MT_A) += pt_mta.o
+pt-$(CONFIG_TOUCHSCREEN_PARADE_MT_B) += pt_mtb.o
+pt-$(CONFIG_TOUCHSCREEN_PARADE_BUTTON) += pt_btn.o
+pt-$(CONFIG_TOUCHSCREEN_PARADE_PROXIMITY) += pt_proximity.o
+obj-$(CONFIG_TOUCHSCREEN_PARADE_DEVICETREE_SUPPORT) += pt_devtree.o
+ifdef CONFIG_TOUCHSCREEN_PARADE
+obj-y += pt_platform.o
+endif
+obj-$(CONFIG_TOUCHSCREEN_PARADE_I2C)  += pt_i2c.o
+obj-$(CONFIG_TOUCHSCREEN_PARADE_SPI)  += pt_spi.o
+obj-$(CONFIG_TOUCHSCREEN_PARADE_DEBUG_MDL)    += pt_debug.o
+obj-$(CONFIG_TOUCHSCREEN_PARADE_LOADER)       += pt_loader.o
+obj-$(CONFIG_TOUCHSCREEN_PARADE_DEVICE_ACCESS)          += pt_device_access.o
+
+ifeq ($(CONFIG_TOUCHSCREEN_PARADE_DEBUG),y)
+CFLAGS_pt_core.o += -DDEBUG
+CFLAGS_pt_i2c.o += -DDEBUG
+CFLAGS_pt_spi.o += -DDEBUG
+CFLAGS_pt_mta.o += -DDEBUG
+CFLAGS_pt_mtb.o += -DDEBUG
+CFLAGS_pt_mt_common.o += -DDEBUG
+CFLAGS_pt_btn.o += -DDEBUG
+CFLAGS_pt_proximity.o += -DDEBUG
+CFLAGS_pt_device_access.o += -DDEBUG
+CFLAGS_pt_loader.o += -DDEBUG
+CFLAGS_pt_debug.o += -DDEBUG
+CFLAGS_pt_devtree.o += -DDEBUG
+CFLAGS_pt_platform.o += -DDEBUG
+endif
+
+ifeq ($(CONFIG_TOUCHSCREEN_PARADE_VDEBUG),y)
+CFLAGS_pt_core.o += -DVERBOSE_DEBUG
+CFLAGS_pt_i2c.o += -DVERBOSE_DEBUG
+CFLAGS_pt_spi.o += -DVERBOSE_DEBUG
+CFLAGS_pt_mta.o += -DVERBOSE_DEBUG
+CFLAGS_pt_mtb.o += -DVERBOSE_DEBUG
+CFLAGS_pt_mt_common.o += -DVERBOSE_DEBUG
+CFLAGS_pt_btn.o += -DVERBOSE_DEBUG
+CFLAGS_pt_proximity.o += -DVERBOSE_DEBUG
+CFLAGS_pt_device_access.o += -DVERBOSE_DEBUG
+CFLAGS_pt_loader.o += -DVERBOSE_DEBUG
+CFLAGS_pt_debug.o += -DVERBOSE_DEBUG
+CFLAGS_pt_devtree.o += -DVERBOSE_DEBUG
+CFLAGS_pt_platform.o += -DVERBOSE_DEBUG
+endif

+ 522 - 0
qcom/opensource/touch-drivers/pt/pt_btn.c

@@ -0,0 +1,522 @@
+/*
+ * pt_btn.c
+ * Parade TrueTouch(TM) Standard Product CapSense Reports Module.
+ * For use with Parade touchscreen controllers.
+ * Supported parts include:
+ * TMA5XX
+ * TMA448
+ * TMA445A
+ * TT21XXX
+ * TT31XXX
+ * TT4XXXX
+ * TT7XXX
+ * TC3XXX
+ *
+ * Copyright (C) 2015-2020 Parade Technologies
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * Contact Parade Technologies at www.paradetech.com <[email protected]>
+ */
+
+#include "pt_regs.h"
+
+/*******************************************************************************
+ * FUNCTION: pt_btn_key_action
+ *
+ * SUMMARY: Reports key event
+ *
+ * PARAMETERS:
+ *     *bd        - pointer to button data structure
+ *      btn_no    - number of button
+ *      btn_state - state of button
+ ******************************************************************************/
+static inline void pt_btn_key_action(struct pt_btn_data *bd,
+	int btn_no, int btn_state)
+{
+	struct device *dev = bd->dev;
+	struct pt_sysinfo *si = bd->si;
+
+	if (!si->btn[btn_no].enabled ||
+			si->btn[btn_no].state == btn_state)
+		return;
+
+	si->btn[btn_no].state = btn_state;
+	input_report_key(bd->input, si->btn[btn_no].key_code, btn_state);
+	input_sync(bd->input);
+
+	pt_debug(dev, DL_INFO, "%s: btn=%d key_code=%d %s\n",
+		__func__, btn_no, si->btn[btn_no].key_code,
+		btn_state == PT_BTN_PRESSED ?
+			"PRESSED" : "RELEASED");
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_get_btn_touches
+ *
+ * SUMMARY: Parse and report key event
+ *
+ * PARAMETERS:
+ *     *bd  - pointer to button data structure
+ ******************************************************************************/
+static void pt_get_btn_touches(struct pt_btn_data *bd)
+{
+	struct pt_sysinfo *si = bd->si;
+	int num_btns = si->num_btns;
+	int cur_btn;
+	int cur_btn_state;
+
+	for (cur_btn = 0; cur_btn < num_btns; cur_btn++) {
+		/* Get current button state */
+		cur_btn_state = (si->xy_data[0] >> (cur_btn * PT_BITS_PER_BTN))
+				& PT_NUM_BTN_EVENT_ID;
+
+		pt_btn_key_action(bd, cur_btn, cur_btn_state);
+	}
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_btn_lift_all
+ *
+ * SUMMARY: Reports button liftoff action
+ *
+ * PARAMETERS:
+ *     *bd  - pointer to button data structure
+ ******************************************************************************/
+static void pt_btn_lift_all(struct pt_btn_data *bd)
+{
+	struct pt_sysinfo *si = bd->si;
+	int i;
+
+	if (!si || si->num_btns == 0)
+		return;
+
+	for (i = 0; i < si->num_btns; i++)
+		pt_btn_key_action(bd, i, PT_BTN_RELEASED);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_xy_worker
+ *
+ * SUMMARY: Read xy_data for all current CapSense button touches
+ *
+ * RETURN:
+ *	 0 = success
+ *
+ * PARAMETERS:
+ *     *bd  - pointer to button data structure
+ ******************************************************************************/
+static int pt_xy_worker(struct pt_btn_data *bd)
+{
+	struct pt_sysinfo *si = bd->si;
+
+	/* extract button press/release touch information */
+	if (si->num_btns > 0)
+		pt_get_btn_touches(bd);
+
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_btn_attention
+ *
+ * SUMMARY: Wrapper function for pt_xy_worker() that register to TTDL attention
+ *  list.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *     *dev - pointer to device structure
+ ******************************************************************************/
+static int pt_btn_attention(struct device *dev)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_btn_data *bd = &cd->bd;
+	int rc;
+
+	if (bd->si->xy_mode[2] != bd->si->desc.btn_report_id)
+		return 0;
+
+	/* core handles handshake */
+	mutex_lock(&bd->btn_lock);
+	rc = pt_xy_worker(bd);
+	mutex_unlock(&bd->btn_lock);
+	if (rc < 0)
+		pt_debug(dev, DL_ERROR,
+			"%s: xy_worker error r=%d\n", __func__, rc);
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_startup_attention
+ *
+ * SUMMARY: Wrapper function for pt_btn_lift_all() that register to TTDL
+ *  attention list.
+ *
+ * RETURN:
+ *	 0 = success
+ *
+ * PARAMETERS:
+ *     *dev - pointer to device structure
+ ******************************************************************************/
+static int pt_startup_attention(struct device *dev)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_btn_data *bd = &cd->bd;
+
+	mutex_lock(&bd->btn_lock);
+	pt_btn_lift_all(bd);
+	mutex_unlock(&bd->btn_lock);
+
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_btn_suspend_attention
+ *
+ * SUMMARY: Function for button to enter suspend state that as following steps:
+ *          1) Lift all button
+ *          2) Set flag with suspend state
+ *          3) Decrese pm system count
+ *
+ * RETURN:
+ *	 0 = success
+ *
+ * PARAMETERS:
+ *     *dev - pointer to device structure
+ ******************************************************************************/
+static int pt_btn_suspend_attention(struct device *dev)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_btn_data *bd = &cd->bd;
+
+	mutex_lock(&bd->btn_lock);
+	pt_btn_lift_all(bd);
+	bd->is_suspended = true;
+	mutex_unlock(&bd->btn_lock);
+
+	pm_runtime_put(dev);
+
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_btn_resume_attention
+ *
+ * SUMMARY: Function for button to leave suspend state that as following steps:
+ *          1) Increse pm system count
+ *          2) Clear suspend state flag
+ *
+ * RETURN:
+ *	 0 = success
+ *
+ * PARAMETERS:
+ *     *dev - pointer to device structure
+ ******************************************************************************/
+static int pt_btn_resume_attention(struct device *dev)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_btn_data *bd = &cd->bd;
+
+	pm_runtime_get(dev);
+
+	mutex_lock(&bd->btn_lock);
+	bd->is_suspended = false;
+	mutex_unlock(&bd->btn_lock);
+
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_btn_open
+ *
+ * SUMMARY: Open method for input device(button) that sets up call back
+ *  functions to TTDL attention list
+ *
+ * RETURN:
+ *	 0 = success
+ *
+ * PARAMETERS:
+ *     *input - pointer to input_dev structure
+ ******************************************************************************/
+static int pt_btn_open(struct input_dev *input)
+{
+	struct device *dev = input->dev.parent;
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_btn_data *bd = &cd->bd;
+
+	pm_runtime_get_sync(dev);
+
+	mutex_lock(&bd->btn_lock);
+	bd->is_suspended = false;
+	mutex_unlock(&bd->btn_lock);
+
+	pt_debug(dev, DL_INFO, "%s: setup subscriptions\n", __func__);
+
+	/* set up touch call back */
+	_pt_subscribe_attention(dev, PT_ATTEN_IRQ, PT_BTN_NAME,
+		pt_btn_attention, PT_MODE_OPERATIONAL);
+
+	/* set up startup call back */
+	_pt_subscribe_attention(dev, PT_ATTEN_STARTUP, PT_BTN_NAME,
+		pt_startup_attention, 0);
+
+	/* set up suspend call back */
+	_pt_subscribe_attention(dev, PT_ATTEN_SUSPEND, PT_BTN_NAME,
+		pt_btn_suspend_attention, 0);
+
+	/* set up resume call back */
+	_pt_subscribe_attention(dev, PT_ATTEN_RESUME, PT_BTN_NAME,
+		pt_btn_resume_attention, 0);
+
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_btn_close
+ *
+ * SUMMARY: Close method for input device(button) that clears call back
+ *  functions from TTDL attention list.
+ *
+ * PARAMETERS:
+ *     *input - pointer to input_dev structure
+ ******************************************************************************/
+static void pt_btn_close(struct input_dev *input)
+{
+	struct device *dev = input->dev.parent;
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_btn_data *bd = &cd->bd;
+
+	_pt_unsubscribe_attention(dev, PT_ATTEN_IRQ, PT_BTN_NAME,
+		pt_btn_attention, PT_MODE_OPERATIONAL);
+
+	_pt_unsubscribe_attention(dev, PT_ATTEN_STARTUP, PT_BTN_NAME,
+		pt_startup_attention, 0);
+
+	_pt_unsubscribe_attention(dev, PT_ATTEN_SUSPEND, PT_BTN_NAME,
+		pt_btn_suspend_attention, 0);
+
+	_pt_unsubscribe_attention(dev, PT_ATTEN_RESUME, PT_BTN_NAME,
+		pt_btn_resume_attention, 0);
+
+	mutex_lock(&bd->btn_lock);
+	if (!bd->is_suspended) {
+		pm_runtime_put(dev);
+		bd->is_suspended = true;
+	}
+	mutex_unlock(&bd->btn_lock);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_setup_input_device
+ *
+ * SUMMARY: Set up resolution, event signal capabilities and register input
+ *  device for button.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *     *dev - pointer to device structure
+ ******************************************************************************/
+static int pt_setup_input_device(struct device *dev)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_btn_data *bd = &cd->bd;
+	int i;
+	int rc;
+
+	pt_debug(dev, DL_INFO, "%s: Initialize event signals\n",
+		__func__);
+	__set_bit(EV_KEY, bd->input->evbit);
+	pt_debug(dev, DL_INFO, "%s: Number of buttons %d\n",
+		__func__, bd->si->num_btns);
+	for (i = 0; i < bd->si->num_btns; i++) {
+		pt_debug(dev, DL_INFO, "%s: btn:%d keycode:%d\n",
+			__func__, i, bd->si->btn[i].key_code);
+		__set_bit(bd->si->btn[i].key_code, bd->input->keybit);
+	}
+
+	rc = input_register_device(bd->input);
+	if (rc < 0)
+		pt_debug(dev, DL_ERROR,
+			"%s: Error, failed register input device r=%d\n",
+			__func__, rc);
+	else
+		bd->input_device_registered = true;
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_setup_input_attention
+ *
+ * SUMMARY: Wrapper function for pt_setup_input_device() register to TTDL
+ *  attention list.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *     *dev - pointer to device structure
+ ******************************************************************************/
+static int pt_setup_input_attention(struct device *dev)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_btn_data *bd = &cd->bd;
+	int rc;
+
+	bd->si = _pt_request_sysinfo(dev);
+	if (!bd->si)
+		return -EPERM;
+
+	rc = pt_setup_input_device(dev);
+
+	_pt_unsubscribe_attention(dev, PT_ATTEN_STARTUP, PT_BTN_NAME,
+		pt_setup_input_attention, 0);
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_btn_probe
+ *
+ * SUMMARY: The probe function for button input device
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*dev   - pointer to device structure
+ ******************************************************************************/
+int pt_btn_probe(struct device *dev)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_btn_data *bd = &cd->bd;
+	struct pt_platform_data *pdata = dev_get_platdata(dev);
+	struct pt_btn_platform_data *btn_pdata;
+	int rc = 0;
+
+	if (!pdata || !pdata->btn_pdata) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Missing platform data\n", __func__);
+		rc = -ENODEV;
+		goto error_no_pdata;
+	}
+	btn_pdata = pdata->btn_pdata;
+
+	mutex_init(&bd->btn_lock);
+	bd->dev = dev;
+	bd->pdata = btn_pdata;
+
+	/* Create the input device and register it. */
+	pt_debug(dev, DL_INFO,
+		"%s: Create the input device and register it\n", __func__);
+	bd->input = input_allocate_device();
+	if (!bd->input) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Error, failed to allocate input device\n",
+			__func__);
+		rc = -ENODEV;
+		goto error_alloc_failed;
+	} else
+		bd->input_device_allocated = true;
+
+	if (bd->pdata->inp_dev_name)
+		bd->input->name = bd->pdata->inp_dev_name;
+	else
+		bd->input->name = PT_BTN_NAME;
+	scnprintf(bd->phys, sizeof(bd->phys), "%s/input%d", dev_name(dev),
+			cd->phys_num++);
+	bd->input->phys = bd->phys;
+	bd->input->dev.parent = bd->dev;
+	bd->input->open = pt_btn_open;
+	bd->input->close = pt_btn_close;
+	input_set_drvdata(bd->input, bd);
+
+	/* get sysinfo */
+	bd->si = _pt_request_sysinfo(dev);
+
+	if (bd->si) {
+		rc = pt_setup_input_device(dev);
+		if (rc)
+			goto error_init_input;
+	} else {
+		pt_debug(dev, DL_ERROR,
+			"%s: Fail get sysinfo pointer from core p=%p\n",
+			__func__, bd->si);
+		_pt_subscribe_attention(dev, PT_ATTEN_STARTUP,
+			PT_BTN_NAME, pt_setup_input_attention, 0);
+	}
+
+	return 0;
+
+error_init_input:
+	input_free_device(bd->input);
+	bd->input_device_allocated = false;
+error_alloc_failed:
+error_no_pdata:
+	pt_debug(dev, DL_ERROR, "%s failed.\n", __func__);
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_btn_release
+ *
+ * SUMMARY: The release function for button input device
+ *
+ * RETURN:
+ *	 0 = success
+ *
+ * PARAMETERS:
+ *	*dev   - pointer to device structure
+ ******************************************************************************/
+int pt_btn_release(struct device *dev)
+{
+	struct pt_core_data *cd;
+	struct pt_btn_data *bd;
+
+	/* Ensure valid pointers before de-referencing them */
+	if (dev) {
+		cd = dev_get_drvdata(dev);
+		if (cd)
+			bd = &cd->bd;
+		else
+			return 0;
+	} else {
+		return 0;
+	}
+
+	/*
+	 * Second call this function may cause kernel panic if probe fail.
+	 * Use input_device_registered & input_device_allocated variable to
+	 * avoid unregister or free unavailable devive.
+	 */
+	if (bd && bd->input_device_registered) {
+		bd->input_device_registered = false;
+		input_unregister_device(bd->input);
+		/* Unregistering device will free the device too */
+		bd->input_device_allocated = false;
+	} else if (bd && bd->input_device_allocated) {
+		bd->input_device_allocated = false;
+		input_free_device(bd->input);
+		_pt_unsubscribe_attention(dev, PT_ATTEN_STARTUP,
+			PT_BTN_NAME, pt_setup_input_attention, 0);
+	}
+
+	return 0;
+}

+ 18570 - 0
qcom/opensource/touch-drivers/pt/pt_core.c

@@ -0,0 +1,18570 @@
+/*
+ * pt_core.c
+ * Parade TrueTouch(TM) Standard Product Core Module.
+ * For use with Parade touchscreen controllers.
+ * Supported parts include:
+ * TMA5XX
+ * TMA448
+ * TMA445A
+ * TT21XXX
+ * TT31XXX
+ * TT4XXXX
+ * TT7XXX
+ * TC3XXX
+ *
+ * Copyright (C) 2015-2020 Parade Technologies
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * Contact Parade Technologies at www.paradetech.com <[email protected]>
+ */
+
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/kthread.h>
+#include <linux/suspend.h>
+#include <glink_interface.h>
+#include <linux/remoteproc/qcom_rproc.h>
+#include "pt_regs.h"
+#if defined(CONFIG_PANEL_NOTIFIER)
+#include <linux/soc/qcom/panel_event_notifier.h>
+#endif
+
+#define PINCTRL_STATE_ACTIVE    "pmx_ts_active"
+#define PINCTRL_STATE_SUSPEND   "pmx_ts_suspend"
+#define PINCTRL_STATE_RELEASE   "pmx_ts_release"
+
+#define FT_VTG_MIN_UV           2800000
+#define FT_VTG_MAX_UV           2800000
+#define FT_I2C_VTG_MIN_UV       1800000
+#define FT_I2C_VTG_MAX_UV       1800000
+
+#define PWR_SUSPEND_LOAD_UA  165
+#define I2C_SUSPEND_LOAD_UA  100
+#define PWR_ACTIVE_LOAD_MA  12000
+#define I2C_ACTIVE_LOAD_MA  30000
+
+#define PT_CORE_STARTUP_RETRY_COUNT		3
+
+#define PT_STATUS_STR_LEN (50)
+
+#define PT_DATA_SIZE  (2 * 256)
+
+
+#if defined(CONFIG_DRM) || defined(CONFIG_PANEL_NOTIFIER)
+static struct drm_panel *active_panel;
+#endif
+
+MODULE_FIRMWARE(PT_FW_FILE_NAME);
+
+#define ENABLE_I2C_REG_ONLY
+
+enum core_states {
+		STATE_NONE,
+		STATE_RESUME,
+		STATE_SUSPEND
+};
+#ifdef ENABLE_I2C_REG_ONLY
+static int pt_enable_i2c_regulator(struct pt_core_data *cd, bool en);
+#endif
+
+#define PT_AMBIENT_MODE
+
+#ifdef PT_AMBIENT_MODE
+static int pt_device_exit(struct i2c_client *client);
+int pt_device_entry(struct device *dev,
+		u16 irq, size_t xfer_buf_size);
+#endif
+
+static const char *pt_driver_core_name = PT_CORE_NAME;
+static const char *pt_driver_core_version = PT_DRIVER_VERSION;
+static const char *pt_driver_core_date = PT_DRIVER_DATE;
+enum core_states pt_core_state = STATE_NONE;
+
+uint32_t pt_slate_resp_ack;
+struct pt_hid_field {
+	int report_count;
+	int report_size;
+	int size; /* report_count * report_size */
+	int offset;
+	int data_type;
+	int logical_min;
+	int logical_max;
+	/* Usage Page (Hi 16 bit) + Usage (Lo 16 bit) */
+	u32 usage_page;
+	u32 collection_usage_pages[PT_HID_MAX_COLLECTIONS];
+	struct pt_hid_report *report;
+	bool record_field;
+};
+
+struct pt_hid_report {
+	u8 id;
+	u8 type;
+	int size;
+	struct pt_hid_field *fields[PT_HID_MAX_FIELDS];
+	int num_fields;
+	int record_field_index;
+	int header_size;
+	int record_size;
+	u32 usage_page;
+};
+
+struct atten_node {
+	struct list_head node;
+	char *id;
+	struct device *dev;
+
+	int (*func)(struct device *dev);
+	int mode;
+};
+
+struct param_node {
+	struct list_head node;
+	u8 id;
+	u32 value;
+	u8 size;
+};
+
+struct module_node {
+	struct list_head node;
+	struct pt_module *module;
+	void *data;
+};
+
+struct pt_hid_cmd {
+	u8 opcode;
+	u8 report_type;
+	union {
+		u8 report_id;
+		u8 power_state;
+	};
+	u8 has_data_register;
+	size_t write_length;
+	u8 *write_buf;
+	u8 *read_buf;
+	u8 wait_interrupt;
+	u8 reset_cmd;
+	u16 timeout_ms;
+};
+
+struct pt_hid_output {
+	u8 cmd_type;
+	u16 length;
+	u8 command_code;
+	size_t write_length;
+	u8 *write_buf;
+	u8 novalidate;
+	u8 reset_expected;
+	u16 timeout_ms;
+};
+
+#define SET_CMD_OPCODE(byte, opcode) SET_CMD_LOW(byte, opcode)
+#define SET_CMD_REPORT_TYPE(byte, type) SET_CMD_HIGH(byte, ((type) << 4))
+#define SET_CMD_REPORT_ID(byte, id) SET_CMD_LOW(byte, id)
+
+#define CREATE_PIP1_FW_CMD(command) \
+	.cmd_type = PIP1_CMD_TYPE_FW, \
+	.command_code = command
+
+#define CREATE_PIP1_BL_CMD(command) \
+	.cmd_type = PIP1_CMD_TYPE_BL, \
+	.command_code = command
+
+#define PT_MAX_PR_BUF_SIZE  2048
+/*******************************************************************************
+ * FUNCTION: pt_pr_buf
+ *
+ * SUMMARY: Print out the contents of a buffer to kmsg based on the debug level
+ *
+ * RETURN: Void
+ *
+ * PARAMETERS:
+ *	*dev         - pointer to Device structure
+ *	 debug_level - requested debug level to print at
+ *	*buf         - pointer to buffer to print
+ *	 buf_len     - size of buf
+ *	*data_name   - Descriptive name of data prefixed to data
+ ******************************************************************************/
+void pt_pr_buf(struct device *dev, u8 debug_level, u8 *buf,
+	u16 buf_len, const char *data_name)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	int i;
+	ssize_t pr_buf_index = 0;
+	int max_size;
+
+	/* only proceed if valid debug level and there is data to print */
+	if (debug_level <= cd->debug_level && buf_len > 0) {
+		char *pr_buf = kzalloc(PT_MAX_PR_BUF_SIZE, GFP_KERNEL);
+
+		if (!pr_buf)
+			return;
+
+		/*
+		 * With a space each printed char takes 3 bytes, subtract
+		 * the length of the data_name prefix as well as 11 bytes
+		 * for the " [0..xxx]: " printed before the data.
+		 */
+		max_size = (PT_MAX_PR_BUF_SIZE - sizeof(data_name) - 11) / 3;
+
+		/* Ensure pr_buf_index stays within the 1018 size */
+		pr_buf_index += scnprintf(pr_buf, PT_MAX_PR_BUF_SIZE, "%s [0..%d]: ",
+			data_name);
+		for (i = 0; i < buf_len && i < max_size; i++)
+			pr_buf_index += scnprintf(pr_buf + pr_buf_index,
+				PT_MAX_PR_BUF_SIZE, "%02X ", buf[i]);
+
+		pt_debug(dev, debug_level, "%s\n", pr_buf);
+		kfree(pr_buf);
+	}
+}
+EXPORT_SYMBOL_GPL(pt_pr_buf);
+
+#ifdef TTHE_TUNER_SUPPORT
+/*******************************************************************************
+ * FUNCTION: tthe_print
+ *
+ * SUMMARY: Format data name and time stamp as the header and format the
+ *  content of input buffer with hex base to "tthe_buf". And then wake up event
+ *  semaphore for tthe debugfs node.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *cd         - pointer to core data
+ *  *buf        - pointer to input buffer
+ *   buf_len    - size of input buffer
+ *  *data_name  - pointer to data name
+ ******************************************************************************/
+static int tthe_print(struct pt_core_data *cd, u8 *buf, int buf_len,
+		const u8 *data_name)
+{
+	int name_len = strlen(data_name);
+	int i, n;
+	u8 *p;
+	int remain;
+	u8 data_name_with_time_stamp[100];
+
+	/* Prepend timestamp, if requested, to data_name */
+	if (cd->show_timestamp) {
+		scnprintf(data_name_with_time_stamp,
+			sizeof(data_name_with_time_stamp),
+			"[%u] %s", pt_get_time_stamp(), data_name);
+		data_name = data_name_with_time_stamp;
+		name_len = strlen(data_name);
+	}
+
+	mutex_lock(&cd->tthe_lock);
+	if (!cd->tthe_buf)
+		goto exit;
+
+	/* Add 1 due to the '\n' that is appended at the end */
+	if (cd->tthe_buf_len + name_len + buf_len + 1 > cd->tthe_buf_size)
+		goto exit;
+
+	if (name_len + buf_len == 0)
+		goto exit;
+
+	remain = cd->tthe_buf_size - cd->tthe_buf_len;
+	if (remain < name_len)
+		name_len = remain;
+
+	p = cd->tthe_buf + cd->tthe_buf_len;
+	memcpy(p, data_name, name_len);
+	cd->tthe_buf_len += name_len;
+	p += name_len;
+	remain -= name_len;
+
+	*p = 0;
+	for (i = 0; i < buf_len; i++) {
+		n = scnprintf(p, remain, "%02X ", buf[i]);
+		if (n <= 0)
+			break;
+		p += n;
+		remain -= n;
+		cd->tthe_buf_len += n;
+	}
+
+	n = scnprintf(p, remain, "\n");
+	cd->tthe_buf_len += n;
+exit:
+	wake_up(&cd->wait_q);
+	mutex_unlock(&cd->tthe_lock);
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_request_tthe_print
+ *
+ * SUMMARY: Function pointer included in core_cmds to allow other modules
+ *	to request to print data to the "tthe_buffer".
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *dev - pointer to device structure
+ ******************************************************************************/
+static int _pt_request_tthe_print(struct device *dev, u8 *buf,
+		int buf_len, const u8 *data_name)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+
+	return tthe_print(cd, buf, buf_len, data_name);
+}
+#endif
+
+/*******************************************************************************
+ * FUNCTION: pt_platform_detect_read
+ *
+ * SUMMARY: To be passed to platform dectect function to perform a read
+ *  operation.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *dev  - pointer to Device structure
+ *  *buf  - pointer to buffer where the data read will be stored
+ *   size - size to be read
+ ******************************************************************************/
+static int pt_platform_detect_read(struct device *dev, void *buf, int size)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+
+	return pt_adap_read_default(cd, buf, size);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_add_parameter
+ *
+ * SUMMARY: Adds a parameter that has been altered to the parameter linked list.
+ *	On every reset of the DUT this linked list is traversed and all
+ *	parameters in it are restored to the DUT.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd          - pointer to core data
+ *	 param_id    - parameter ID to add
+ *	 param_value - Value corresponding to the ID
+ *	 param_size  - Size of param_value
+ ******************************************************************************/
+static int pt_add_parameter(struct pt_core_data *cd,
+		u8 param_id, u32 param_value, u8 param_size)
+{
+	struct param_node *param, *param_new;
+
+	/* Check if parameter already exists in the list */
+	spin_lock(&cd->spinlock);
+
+	list_for_each_entry(param, &cd->param_list, node) {
+		if (param->id == param_id) {
+			/* Update parameter */
+			param->value = param_value;
+			pt_debug(cd->dev, DL_INFO,
+				"%s: Update parameter id:%d value:%d size:%d\n",
+				 __func__, param_id, param_value, param_size);
+			goto exit_unlock;
+		}
+	}
+	spin_unlock(&cd->spinlock);
+
+	param_new = kzalloc(sizeof(*param_new), GFP_KERNEL);
+	if (!param_new)
+		return -ENOMEM;
+
+	param_new->id = param_id;
+	param_new->value = param_value;
+	param_new->size = param_size;
+
+	pt_debug(cd->dev, DL_INFO,
+		"%s: Add parameter id:%d value:%d size:%d\n",
+		__func__, param_id, param_value, param_size);
+
+	spin_lock(&cd->spinlock);
+	list_add(&param_new->node, &cd->param_list);
+exit_unlock:
+	spin_unlock(&cd->spinlock);
+
+	return 0;
+}
+
+#ifdef TTDL_DIAGNOSTICS
+/*******************************************************************************
+ * FUNCTION: pt_erase_parameter_list
+ *
+ * SUMMARY: Empty out the entire parameter linked list of all parameter/value
+ *	pairs. In some test cases this functionality is needed to ensure DUT
+ *	returns to a virgin state after a reset and no parameters are restored.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd - pointer to core data
+ ******************************************************************************/
+static int pt_erase_parameter_list(struct pt_core_data *cd)
+{
+	struct param_node *pos, *temp;
+
+	spin_lock(&cd->spinlock);
+	list_for_each_entry_safe(pos, temp, &cd->param_list, node) {
+		pt_debug(cd->dev, DL_INFO,
+			"%s: Parameter Restore List - remove 0x%02x\n",
+			__func__, pos->id);
+		list_del(&pos->node);
+		kfree(pos);
+	}
+	spin_unlock(&cd->spinlock);
+
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_count_parameter_list
+ *
+ * SUMMARY: Count the items in the RAM parameter restor list
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd - pointer to core data
+ ******************************************************************************/
+static int pt_count_parameter_list(struct pt_core_data *cd)
+{
+	struct param_node *pos, *temp;
+	int entries = 0;
+
+	spin_lock(&cd->spinlock);
+	list_for_each_entry_safe(pos, temp, &cd->param_list, node)
+		entries++;
+	spin_unlock(&cd->spinlock);
+
+	return entries;
+}
+#endif /* TTDL_DIAGNOSTICS */
+
+/*******************************************************************************
+ * FUNCTION: request_exclusive
+ *
+ * SUMMARY: Request exclusive access to the DUT
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd         - pointer to core data
+ *	*ownptr     - pointer to device
+ *	 timeout_ms - Timeout value
+ ******************************************************************************/
+int request_exclusive(struct pt_core_data *cd, void *ownptr,
+		int timeout_ms)
+{
+	int t = msecs_to_jiffies(timeout_ms);
+	bool with_timeout = (timeout_ms != 0);
+
+	pt_debug(cd->dev, DL_INFO, "%s: Attempt to Request EXCLUSIVE t=%d\n",
+		__func__, timeout_ms);
+
+	mutex_lock(&cd->system_lock);
+	if (!cd->exclusive_dev && cd->exclusive_waits == 0) {
+		cd->exclusive_dev = ownptr;
+		goto exit;
+	}
+
+	cd->exclusive_waits++;
+wait:
+	mutex_unlock(&cd->system_lock);
+	if (with_timeout) {
+		t = wait_event_timeout(cd->wait_q, !cd->exclusive_dev, t);
+		if (IS_TMO(t)) {
+			pt_debug(cd->dev, DL_ERROR,
+				"%s: tmo waiting exclusive access\n", __func__);
+			return -ETIME;
+		}
+	} else {
+		wait_event(cd->wait_q, !cd->exclusive_dev);
+	}
+	mutex_lock(&cd->system_lock);
+	if (cd->exclusive_dev)
+		goto wait;
+	cd->exclusive_dev = ownptr;
+	cd->exclusive_waits--;
+exit:
+	mutex_unlock(&cd->system_lock);
+	pt_debug(cd->dev, DL_DEBUG, "%s: request exclusive ok=%p\n",
+		__func__, ownptr);
+
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: release_exclusive_
+ *
+ * SUMMARY: Release exclusive access to the DUT
+ *
+ * RETURN:
+ *	 0 = success
+ *
+ * PARAMETERS:
+ *	*cd         - pointer to core data
+ *	*ownptr     - pointer to device
+ ******************************************************************************/
+static int release_exclusive_(struct pt_core_data *cd, void *ownptr)
+{
+	pt_debug(cd->dev, DL_INFO, "%s: Attempt to Release EXCLUSIVE\n",
+		__func__);
+	if (cd->exclusive_dev != ownptr)
+		return -EINVAL;
+
+	pt_debug(cd->dev, DL_DEBUG, "%s: exclusive_dev %p freed\n",
+		__func__, cd->exclusive_dev);
+	cd->exclusive_dev = NULL;
+	wake_up(&cd->wait_q);
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: release_exclusive
+ *
+ * SUMMARY: Protected wrapper to release_exclusive_()
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd         - pointer to core data
+ *	*ownptr     - pointer to device
+ ******************************************************************************/
+int release_exclusive(struct pt_core_data *cd, void *ownptr)
+{
+	int rc;
+
+	mutex_lock(&cd->system_lock);
+	rc = release_exclusive_(cd, ownptr);
+	mutex_unlock(&cd->system_lock);
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_hid_exec_cmd_
+ *
+ * SUMMARY: Send the HID command to the DUT
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd      - pointer to core data
+ *	*hid_cmd - pointer to the HID command to send
+ ******************************************************************************/
+static int pt_hid_exec_cmd_(struct pt_core_data *cd,
+		struct pt_hid_cmd *hid_cmd)
+{
+	int rc = 0;
+	u8 *cmd;
+	u16 cmd_length;
+	u8 cmd_offset = 0;
+
+	cmd_length = 2 /* command register */
+		+ 2    /* command */
+		+ (hid_cmd->report_id >= 0XF ? 1 : 0)   /* Report ID */
+		+ (hid_cmd->has_data_register ? 2 : 0)	/* Data register */
+		+ hid_cmd->write_length;                /* Data length */
+
+	if (cmd_length < 4 || cmd_length > PT_MAX_PIP1_MSG_SIZE)
+		return -EPROTO;
+
+	cmd = kzalloc(cmd_length, GFP_KERNEL);
+	if (!cmd)
+		return -ENOMEM;
+
+	/* Set Command register */
+	memcpy(&cmd[cmd_offset], &cd->hid_desc.command_register,
+			sizeof(cd->hid_desc.command_register));
+	cmd_offset += sizeof(cd->hid_desc.command_register);
+
+	/* Set Command */
+	SET_CMD_REPORT_TYPE(cmd[cmd_offset], hid_cmd->report_type);
+
+	if (hid_cmd->report_id >= 0XF)
+		SET_CMD_REPORT_ID(cmd[cmd_offset], 0xF);
+	else
+		SET_CMD_REPORT_ID(cmd[cmd_offset], hid_cmd->report_id);
+	cmd_offset++;
+
+	SET_CMD_OPCODE(cmd[cmd_offset], hid_cmd->opcode);
+	cmd_offset++;
+
+	if (hid_cmd->report_id >= 0XF) {
+		cmd[cmd_offset] = hid_cmd->report_id;
+		cmd_offset++;
+	}
+
+	/* Set Data register */
+	if (hid_cmd->has_data_register) {
+		memcpy(&cmd[cmd_offset], &cd->hid_desc.data_register,
+				sizeof(cd->hid_desc.data_register));
+		cmd_offset += sizeof(cd->hid_desc.data_register);
+	}
+
+	/* Set Data */
+	if (hid_cmd->write_length && hid_cmd->write_buf) {
+		memcpy(&cmd[cmd_offset], hid_cmd->write_buf,
+				hid_cmd->write_length);
+		cmd_offset += hid_cmd->write_length;
+	}
+
+	pt_debug(cd->dev, DL_INFO,
+		">>> %s: Write Buffer Size[%d] Cmd[0x%02X]\n",
+		__func__, cmd_length, hid_cmd->report_id);
+
+	pt_pr_buf(cd->dev, DL_DEBUG, cmd, cmd_length, ">>> CMD");
+	rc = pt_adap_write_read_specific(cd, cmd_length, cmd,
+			hid_cmd->read_buf);
+	if (rc)
+		pt_debug(cd->dev, DL_ERROR,
+		"%s: Fail pt_adap_transfer\n", __func__);
+
+	kfree(cmd);
+	return rc;
+}
+#ifdef TTDL_DIAGNOSTICS
+/*******************************************************************************
+ * FUNCTION: pt_toggle_err_gpio
+ *
+ * SUMMARY: Toggles the pre-defined error GPIO
+ *
+ * RETURN: n/a
+ *
+ * PARAMETERS:
+ *	*cd   - pointer to core data
+ *	 type - type of err that occurred
+ ******************************************************************************/
+void pt_toggle_err_gpio(struct pt_core_data *cd, u8 type)
+{
+	pt_debug(cd->dev, DL_DEBUG, "%s called with type = %d\n",
+		__func__, type);
+	if (cd->err_gpio && type == cd->err_gpio_type) {
+		pt_debug(cd->dev, DL_WARN, "%s: Toggle ERR GPIO\n", __func__);
+		gpio_direction_output(cd->err_gpio,
+			!gpio_get_value(cd->err_gpio));
+	}
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_request_toggle_err_gpio
+ *
+ * SUMMARY: Function pointer included in core_cmds to allow other modules
+ *	to request to toggle the err_gpio
+ *
+ * RETURN: n/a
+ *
+ * PARAMETERS:
+ *	*cd   - pointer to core data
+ *	 type - type of err that occurred
+ ******************************************************************************/
+void _pt_request_toggle_err_gpio(struct device *dev, u8 type)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+
+	pt_toggle_err_gpio(cd, type);
+}
+#endif /* TTDL_DIAGNOSTICS */
+
+/*******************************************************************************
+ * FUNCTION: pt_hid_exec_cmd_and_wait_
+ *
+ * SUMMARY: Send the HID command to the DUT and wait for the response
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd      - pointer to core data
+ *	*hid_cmd - pointer to the HID command to send
+ ******************************************************************************/
+static int pt_hid_exec_cmd_and_wait_(struct pt_core_data *cd,
+		struct pt_hid_cmd *hid_cmd)
+{
+	int rc = 0;
+	int t;
+	u16 timeout_ms;
+	int *cmd_state;
+
+	if (hid_cmd->reset_cmd)
+		cmd_state = &cd->hid_reset_cmd_state;
+	else
+		cmd_state = &cd->hid_cmd_state;
+
+	if (hid_cmd->wait_interrupt) {
+		mutex_lock(&cd->system_lock);
+		*cmd_state = 1;
+		mutex_unlock(&cd->system_lock);
+	}
+
+	rc = pt_hid_exec_cmd_(cd, hid_cmd);
+	if (rc) {
+		if (hid_cmd->wait_interrupt)
+			goto error;
+
+		goto exit;
+	}
+
+	if (!hid_cmd->wait_interrupt)
+		goto exit;
+
+	if (hid_cmd->timeout_ms)
+		timeout_ms = hid_cmd->timeout_ms;
+	else
+		timeout_ms = PT_HID_CMD_DEFAULT_TIMEOUT;
+
+	t = wait_event_timeout(cd->wait_q, (*cmd_state == 0),
+			msecs_to_jiffies(timeout_ms));
+	if (IS_TMO(t)) {
+#ifdef TTDL_DIAGNOSTICS
+		cd->bus_transmit_error_count++;
+		pt_toggle_err_gpio(cd, PT_ERR_GPIO_I2C_TRANS);
+#endif /* TTDL_DIAGNOSTICS */
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: HID output cmd execution timed out\n",
+			__func__);
+		rc = -ETIME;
+		goto error;
+	}
+
+	goto exit;
+
+error:
+	mutex_lock(&cd->system_lock);
+	*cmd_state = 0;
+	mutex_unlock(&cd->system_lock);
+
+exit:
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_hid_cmd_reset_
+ *
+ * SUMMARY: Send the HID RESET command to the DUT
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd      - pointer to core data
+ ******************************************************************************/
+static int pt_hid_cmd_reset_(struct pt_core_data *cd)
+{
+	struct pt_hid_cmd hid_cmd = {
+		.opcode = HID_CMD_RESET,
+		.wait_interrupt = 1,
+		.reset_cmd = 1,
+		.timeout_ms = PT_HID_CMD_DEFAULT_TIMEOUT,
+	};
+
+	return pt_hid_exec_cmd_and_wait_(cd, &hid_cmd);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_hid_cmd_reset
+ *
+ * SUMMARY: Wrapper function for pt_hid_cmd_reset_ that guarantees exclusive
+ *	access.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd      - pointer to core data
+ ******************************************************************************/
+static int pt_hid_cmd_reset(struct pt_core_data *cd)
+{
+	int rc = 0;
+
+	rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
+	if (rc < 0) {
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: fail get exclusive ex=%p own=%p\n",
+			__func__, cd->exclusive_dev, cd->dev);
+		return rc;
+	}
+
+	pt_debug(cd->dev, DL_INFO, "%s: Send HID Reset command\n", __func__);
+	rc = pt_hid_cmd_reset_(cd);
+
+	if (release_exclusive(cd, cd->dev) < 0)
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: fail to release exclusive\n", __func__);
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_hid_cmd_set_power_
+ *
+ * SUMMARY: Send hid cmd to set power state for the DUT and wait for response
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *cd           - pointer to core data
+ *   power_state  - power state to set(HID_POWER_ON/HID_POWER_SLEEP)
+ ******************************************************************************/
+static int pt_hid_cmd_set_power_(struct pt_core_data *cd,
+		u8 power_state)
+{
+	int rc = 0;
+	struct pt_hid_cmd hid_cmd = {
+		.opcode = HID_CMD_SET_POWER,
+		.wait_interrupt = 1,
+		.timeout_ms = PT_HID_CMD_DEFAULT_TIMEOUT,
+	};
+	hid_cmd.power_state = power_state;
+
+	/* The chip won't give response if goes to Deep Standby */
+	if (power_state == HID_POWER_STANDBY) {
+		rc =  pt_hid_exec_cmd_(cd, &hid_cmd);
+		if (rc)
+			pt_debug(cd->dev, DL_ERROR,
+				"%s: Failed to set power to state:%d\n",
+				__func__, power_state);
+		else
+			cd->fw_sys_mode_in_standby_state = true;
+		return rc;
+	}
+	cd->fw_sys_mode_in_standby_state = false;
+
+	rc =  pt_hid_exec_cmd_and_wait_(cd, &hid_cmd);
+	if (rc) {
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: Failed to set power to state:%d\n",
+			__func__, power_state);
+		return rc;
+	}
+
+	/* validate */
+	if ((cd->response_buf[2] != HID_RESPONSE_REPORT_ID)
+			|| ((cd->response_buf[3] & 0x3) != power_state)
+			|| ((cd->response_buf[4] & 0xF) != HID_CMD_SET_POWER))
+		rc = -EINVAL;
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_hid_cmd_set_power
+ *
+ * SUMMARY: Wrapper function for pt_hid_cmd_set_power_ that guarantees
+ *  exclusive access.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *cd           - pointer to core data
+ *   power_state  - power state to set(HID_POWER_ON/HID_POWER_SLEEP)
+ ******************************************************************************/
+static int pt_hid_cmd_set_power(struct pt_core_data *cd,
+		u8 power_state)
+{
+	int rc;
+
+	rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
+	if (rc < 0) {
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: fail get exclusive ex=%p own=%p\n",
+			__func__, cd->exclusive_dev, cd->dev);
+		return rc;
+	}
+
+	rc = pt_hid_cmd_set_power_(cd, power_state);
+
+	if (release_exclusive(cd, cd->dev) < 0)
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: fail to release exclusive\n", __func__);
+
+	return rc;
+}
+
+static const u16 crc_table[16] = {
+	0x0000, 0x1021, 0x2042, 0x3063,
+	0x4084, 0x50a5, 0x60c6, 0x70e7,
+	0x8108, 0x9129, 0xa14a, 0xb16b,
+	0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
+};
+
+/*******************************************************************************
+ * FUNCTION: _pt_compute_crc
+ *
+ * SUMMARY: Calculate CRC by CRC table.
+ *
+ * RETURN:
+ *   CRC calculation result
+ *
+ * PARAMETERS:
+ *  *buf   - pointer to the data array to be calculated
+ *   size  - size of data array
+ ******************************************************************************/
+static u16 _pt_compute_crc(u8 *buf, u32 size)
+{
+	u16 remainder = 0xFFFF;
+	u16 xor_mask = 0x0000;
+	u32 index;
+	u32 byte_value;
+	u32 table_index;
+	u32 crc_bit_width = sizeof(u16) * 8;
+
+	/* Divide the message by polynomial, via the table. */
+	for (index = 0; index < size; index++) {
+		byte_value = buf[index];
+		table_index = ((byte_value >> 4) & 0x0F)
+			^ (remainder >> (crc_bit_width - 4));
+		remainder = crc_table[table_index] ^ (remainder << 4);
+		table_index = (byte_value & 0x0F)
+			^ (remainder >> (crc_bit_width - 4));
+		remainder = crc_table[table_index] ^ (remainder << 4);
+	}
+
+	/* Perform the final remainder CRC. */
+	return remainder ^ xor_mask;
+}
+
+u16 ccitt_Table[] = {
+	0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7,
+	0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF,
+	0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6,
+	0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE,
+	0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485,
+	0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D,
+	0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4,
+	0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC,
+	0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
+	0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B,
+	0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12,
+	0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A,
+	0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41,
+	0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49,
+	0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70,
+	0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78,
+	0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F,
+	0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
+	0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E,
+	0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256,
+	0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D,
+	0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
+	0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C,
+	0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634,
+	0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB,
+	0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3,
+	0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
+	0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92,
+	0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9,
+	0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1,
+	0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8,
+	0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0,
+};
+
+/*******************************************************************************
+ * FUNCTION: crc_ccitt_calculate
+ *
+ * SUMMARY: Calculate CRC with ccitt standard by CRC table.
+ *
+ * RETURN:
+ *   CRC calculation result
+ *
+ * PARAMETERS:
+ *  *q    - pointer to the data array to be calculated
+ *   len  - size of data array
+ ******************************************************************************/
+static unsigned short crc_ccitt_calculate(unsigned char *q, int len)
+{
+	unsigned short crc = 0xffff;
+
+	while (len-- > 0)
+		crc = ccitt_Table[(crc >> 8 ^ *q++) & 0xff] ^ (crc << 8);
+	return crc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pip2_cmd_calculate_crc
+ *
+ * SUMMARY: Calculate the CRC of a command packet
+ *
+ * RETURN: void
+ *
+ * PARAMETERS:
+ *	*cmd         - pointer to command data
+ *	 extra_bytes - Extra bytes included in command length
+ ******************************************************************************/
+static void pt_pip2_cmd_calculate_crc(struct pip2_cmd_structure *cmd,
+	u8 extra_bytes)
+{
+	u8 buf[PT_MAX_PIP2_MSG_SIZE + 1] = {0};
+	unsigned short crc;
+
+	buf[0] = cmd->len & 0xff;
+	buf[1] = (cmd->len & 0xff00) >> 8;
+	buf[2] = cmd->seq;
+	buf[3] = cmd->id;
+	memcpy(&buf[4], cmd->data, cmd->len - extra_bytes);
+	/* Calculate the CRC for the first 4 bytes above and the data payload */
+	crc = crc_ccitt_calculate(buf, 4 + (cmd->len - extra_bytes));
+	cmd->crc[0] = (crc & 0xff00) >> 8;
+	cmd->crc[1] = (crc & 0xff);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pip2_get_next_cmd_seq
+ *
+ * SUMMARY: Gets the next sequence number for a PIP2 command. The sequence
+ *	number is a 3 bit value (bits [0-2]) but because TTDL will always have
+ *	the TAG bit set (bit 3), the counter starts at 0x08 and goes to 0x0F.
+ *	If the "force_pip2_seq" holds a valid seq value (0x08-0x0F) then do not
+ *	increment, just use the forced value.
+ *
+ * RETURN: Next command sequence number [0x08-0x0F]
+ *
+ * PARAMETERS:
+ *	*cd - pointer to core data
+ ******************************************************************************/
+static u8 pt_pip2_get_next_cmd_seq(struct pt_core_data *cd)
+{
+#ifdef TTDL_DIAGNOSTICS
+	if (cd->force_pip2_seq <= 0x07) {
+		cd->pip2_cmd_tag_seq++;
+		if (cd->pip2_cmd_tag_seq > 0x0F)
+			cd->pip2_cmd_tag_seq = 0x08;
+	} else {
+		cd->pip2_cmd_tag_seq = cd->force_pip2_seq;
+	}
+#else
+	cd->pip2_cmd_tag_seq++;
+	if (cd->pip2_cmd_tag_seq > 0x0F)
+		cd->pip2_cmd_tag_seq = 0x08;
+#endif
+	return cd->pip2_cmd_tag_seq;
+}
+
+/*
+ * Following macros are to define the response time (the interval between PIP2
+ * command finishes sending and INT pin falls). The unit is in microsecond.
+ * It has different time settings between the solution GPIO polling and Bus
+ * polling due to the considration for system load.
+ */
+#ifdef PT_POLL_RESP_BY_BUS
+#define POLL_RETRY_DEFAULT_INTERVAL    50
+#define PIP2_RESP_DEFAULT_TIME_MIN     50
+#define PIP2_RESP_DEFAULT_TIME_MAX     (PT_PIP_CMD_DEFAULT_TIMEOUT * 1000)
+#define PIP2_RESP_FILE_WRITE_TIME_MIN  220
+#define PIP2_RESP_FILE_IOCTL_TIME_MAX  (PT_PIP2_CMD_FILE_ERASE_TIMEOUT * 1000)
+#else
+#define POLL_RETRY_DEFAULT_INTERVAL    20
+#define PIP2_RESP_DEFAULT_TIME_MIN     20
+#define PIP2_RESP_DEFAULT_TIME_MAX     (PT_PIP_CMD_DEFAULT_TIMEOUT * 1000)
+#define PIP2_RESP_FILE_WRITE_TIME_MIN  20
+#define PIP2_RESP_FILE_IOCTL_TIME_MAX  (PT_PIP2_CMD_FILE_ERASE_TIMEOUT * 1000)
+#endif
+/*
+ * id: the command id defined in PIP2
+ * response_len: the (maximum) length of response.
+ * response_time_min: minimum response time in microsecond
+ * response_time_max: maximum response time in microsecond
+ */
+static const struct pip2_cmd_response_structure pip2_cmd_response[] = {
+	{.id = PIP2_CMD_ID_PING,
+	 .response_len = 255,
+	 .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN,
+	 .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX},
+	{.id = PIP2_CMD_ID_STATUS,
+	 .response_len = PIP2_EXTRA_BYTES_NUM + 5,
+	 .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN,
+	 .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX},
+	{.id = PIP2_CMD_ID_CTRL,
+	 .response_len = PIP2_EXTRA_BYTES_NUM + 1,
+	 .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN,
+	 .response_time_max = PT_PIP2_CMD_FILE_ERASE_TIMEOUT},
+	{.id = PIP2_CMD_ID_CONFIG,
+	 .response_len = PIP2_EXTRA_BYTES_NUM + 1,
+	 .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN,
+	 .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX},
+	{.id = PIP2_CMD_ID_CLEAR,
+	 .response_len = PIP2_EXTRA_BYTES_NUM + 0,
+	 .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN,
+	 .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX},
+	{.id = PIP2_CMD_ID_RESET,
+	 .response_len = PIP2_EXTRA_BYTES_NUM + 1,
+	 .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN,
+	 .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX},
+	{.id = PIP2_CMD_ID_VERSION,
+	 .response_len = PIP2_EXTRA_BYTES_NUM + 23,
+	 .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN,
+	 .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX},
+	{.id = PIP2_CMD_ID_FILE_OPEN,
+	 .response_len = PIP2_EXTRA_BYTES_NUM + 2,
+	 .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN,
+	 .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX},
+	{.id = PIP2_CMD_ID_FILE_CLOSE,
+	 .response_len = PIP2_EXTRA_BYTES_NUM + 1,
+	 .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN,
+	 .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX},
+	{.id = PIP2_CMD_ID_FILE_READ,
+	 .response_len = 255,
+	 .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN,
+	 .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX},
+	{.id = PIP2_CMD_ID_FILE_WRITE,
+	 .response_len = PIP2_EXTRA_BYTES_NUM + 1,
+	 .response_time_min = PIP2_RESP_FILE_WRITE_TIME_MIN,
+	 .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX},
+	{.id = PIP2_CMD_ID_FILE_IOCTL,
+	 .response_len = PIP2_EXTRA_BYTES_NUM + 10,
+	 .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN,
+	 .response_time_max = PIP2_RESP_FILE_IOCTL_TIME_MAX},
+	{.id = PIP2_CMD_ID_FLASH_INFO,
+	 .response_len = PIP2_EXTRA_BYTES_NUM + 17,
+	 .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN,
+	 .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX},
+	{.id = PIP2_CMD_ID_EXECUTE,
+	 .response_len = PIP2_EXTRA_BYTES_NUM + 1,
+	 .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN,
+	 .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX},
+	{.id = PIP2_CMD_ID_GET_LAST_ERRNO,
+	 .response_len = PIP2_EXTRA_BYTES_NUM + 3,
+	 .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN,
+	 .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX},
+	 {.id = PIP2_CMD_ID_EXIT_HOST_MODE,
+	 .response_len = PIP2_EXTRA_BYTES_NUM + 1,
+	 .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN,
+	 .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX},
+	{.id = PIP2_CMD_ID_READ_GPIO,
+	 .response_len = PIP2_EXTRA_BYTES_NUM + 5,
+	 .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN,
+	 .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX},
+	{.id = PIP2_CMD_EXECUTE_SCAN,
+	 .response_len = PIP2_EXTRA_BYTES_NUM + 1,
+	 .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN,
+	 .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX},
+	{.id = PIP2_CMD_SET_PARAMETER,
+	 .response_len = PIP2_EXTRA_BYTES_NUM + 1,
+	 .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN,
+	 .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX},
+	{.id = PIP2_CMD_GET_PARAMETER,
+	 .response_len = PIP2_EXTRA_BYTES_NUM + 7,
+	 .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN,
+	 .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX},
+	{.id = PIP2_CMD_SET_DDI_REG,
+	 .response_len = PIP2_EXTRA_BYTES_NUM + 1,
+	 .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN,
+	 .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX},
+	{.id = PIP2_CMD_GET_DDI_REG,
+	 .response_len = PIP2_EXTRA_BYTES_NUM + 249,
+	 .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN,
+	 .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX},
+	{.id = PIP2_CMD_ID_END,
+	 .response_len = 255,
+	 .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN,
+	 .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX}
+};
+
+/*******************************************************************************
+ * FUNCTION: pt_pip2_get_cmd_response_len
+ *
+ * SUMMARY: Gets the expected response length based on the command ID
+ *
+ * RETURN: Expected response length
+ *
+ * PARAMETERS:
+ *	id - Command ID (-1 means input ID is not in list of PIP2 command)
+ ******************************************************************************/
+static int pt_pip2_get_cmd_response_len(u8 id)
+{
+	const struct pip2_cmd_response_structure *p = pip2_cmd_response;
+
+	while ((p->id != id) && (p->id != PIP2_CMD_ID_END))
+		p++;
+
+	if (p->id != PIP2_CMD_ID_END)
+		return p->response_len;
+	else
+		return -EPERM;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pip2_get_cmd_resp_time_min
+ *
+ * SUMMARY: Gets the minimum response time (the interval between PIP2 command
+ * finishes sending and INT pin falls) based on the command ID
+ *
+ * RETURN: Estimated minimum response time in microsecond
+ *
+ * PARAMETERS:
+ *	id - Command ID
+ ******************************************************************************/
+static u32 pt_pip2_get_cmd_resp_time_min(u8 id)
+{
+	const struct pip2_cmd_response_structure *p = pip2_cmd_response;
+
+	while ((p->id != id) && (p->id != PIP2_CMD_ID_END))
+		p++;
+
+	if (p->id != PIP2_CMD_ID_END)
+		return p->response_time_min;
+	else
+		return PIP2_RESP_DEFAULT_TIME_MIN;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pip2_get_cmd_resp_time_max
+ *
+ * SUMMARY: Gets the maximum response time (the interval between PIP2 command
+ * finishes sending and INT pin falls) based on the command ID
+ *
+ * RETURN: Estimated maximum response time in microsecond
+ *
+ * PARAMETERS:
+ *	id - Command ID
+ ******************************************************************************/
+static u32 pt_pip2_get_cmd_resp_time_max(u8 id)
+{
+	const struct pip2_cmd_response_structure *p = pip2_cmd_response;
+
+	while ((p->id != id) && (p->id != PIP2_CMD_ID_END))
+		p++;
+
+	if (p->id != PIP2_CMD_ID_END)
+		return p->response_time_max;
+	else
+		return PIP2_RESP_DEFAULT_TIME_MAX;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pip2_validate_response
+ *
+ * SUMMARY: Validate the response of PIP2 command.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *cd              - pointer to core data
+ *	*pip2_cmd        - pointer to PIP2 command to send
+ *	*read_buf        - pointer to response buffer
+ *	 actual_read_len - actual read length of the response
+ ******************************************************************************/
+static int pt_pip2_validate_response(struct pt_core_data *cd,
+		struct pip2_cmd_structure *pip2_cmd, u8 *read_buf,
+		u16 actual_read_len)
+{
+	int rc = 0;
+	u8 response_seq = 0;
+	u8 reserved_bits = 0;
+	u8 cmd_id = 0;
+	u8 response_bit = 0;
+	unsigned short calc_crc = 0;
+	unsigned short resp_crc = 0;
+
+	/* Verify the length of response buffer */
+	if (actual_read_len < PT_MIN_PIP2_PACKET_SIZE) {
+		pt_debug(cd->dev, DL_ERROR,
+			"%s cmd[0x%02X] read length ERR: read_len = %d\n",
+			__func__, pip2_cmd->id, actual_read_len);
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	/* Verify the CRC */
+	calc_crc = crc_ccitt_calculate(read_buf, actual_read_len - 2);
+	resp_crc  = read_buf[actual_read_len - 2] << 8;
+	resp_crc |= read_buf[actual_read_len - 1];
+	if (resp_crc != calc_crc) {
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: cmd[0x%02X] CRC ERR: calc=0x%04X rsp=0x%04X\n",
+			__func__, pip2_cmd->id, calc_crc, resp_crc);
+#ifdef TTDL_DIAGNOSTICS
+		cd->pip2_crc_error_count++;
+#endif /* TTDL_DIAGNOSTICS */
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	/* Verify the response bit is set */
+	response_bit = read_buf[PIP2_RESP_REPORT_ID_OFFSET] & 0x80;
+	if (!response_bit) {
+		pt_debug(cd->dev, DL_ERROR,
+			"%s cmd[0x%02X] response bit ERR: response_bit = %d\n",
+			__func__, pip2_cmd->id, response_bit);
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	/* Verify the command ID matches from command to response */
+	cmd_id = read_buf[PIP2_RESP_REPORT_ID_OFFSET] & 0x7F;
+	if (cmd_id != pip2_cmd->id) {
+		pt_debug(cd->dev, DL_ERROR,
+			"%s cmd[0x%02X] command ID ERR: cmd_id = 0x%02X\n",
+			__func__, pip2_cmd->id, cmd_id);
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	/* Verify the SEQ number matches from command to response */
+	response_seq = read_buf[PIP2_RESP_SEQUENCE_OFFSET] & 0x0F;
+	if ((pip2_cmd->seq & 0x0F) != response_seq) {
+		pt_debug(cd->dev, DL_ERROR,
+			"%s cmd[0x%02X] send_seq = 0x%02X, resp_seq = 0x%02X\n",
+			__func__, pip2_cmd->id,
+			pip2_cmd->seq, response_seq);
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	/* Verify the reserved bits are 0 */
+	reserved_bits = read_buf[PIP2_RESP_SEQUENCE_OFFSET] & 0xF0;
+	if (reserved_bits)
+		pt_debug(cd->dev, DL_WARN,
+			"%s cmd[0x%02X] reserved_bits = 0x%02X\n",
+			__func__, pip2_cmd->id, reserved_bits);
+
+exit:
+	if (rc)
+		pt_pr_buf(cd->dev, DL_WARN, cd->input_buf, actual_read_len,
+				"PIP RSP:");
+	return rc;
+}
+
+
+/*******************************************************************************
+ * FUNCTION: pt_hid_output_validate_bl_response
+ *
+ * SUMMARY: Validate the response of bootloader command.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *cd          - pointer to core data
+ *  *hid_output  - pointer to hid output data structure
+ ******************************************************************************/
+static int pt_hid_output_validate_bl_response(
+		struct pt_core_data *cd,
+		struct pt_hid_output *hid_output)
+{
+	u16 size;
+	u16 crc;
+	u8 status;
+
+	size = get_unaligned_le16(&cd->response_buf[0]);
+
+	if (hid_output->reset_expected && !size)
+		return 0;
+
+	if (cd->response_buf[PIP1_RESP_REPORT_ID_OFFSET]
+			!= PT_PIP_BL_RESPONSE_REPORT_ID) {
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: BL output response, wrong report_id\n", __func__);
+		return -EPROTO;
+	}
+
+	if (cd->response_buf[4] != PIP1_BL_SOP) {
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: BL output response, wrong SOP\n", __func__);
+		return -EPROTO;
+	}
+
+	if (cd->response_buf[size - 1] != PIP1_BL_EOP) {
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: BL output response, wrong EOP\n", __func__);
+		return -EPROTO;
+	}
+
+	crc = _pt_compute_crc(&cd->response_buf[4], size - 7);
+	if (cd->response_buf[size - 3] != LOW_BYTE(crc)
+			|| cd->response_buf[size - 2] != HI_BYTE(crc)) {
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: BL output response, wrong CRC 0x%X\n",
+			__func__, crc);
+		return -EPROTO;
+	}
+
+	status = cd->response_buf[5];
+	if (status) {
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: BL output response, ERROR:%d\n",
+			__func__, status);
+		return -EPROTO;
+	}
+
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_hid_output_validate_app_response
+ *
+ * SUMMARY: Validate the response of application command.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *cd          - pointer to core data
+ *  *hid_output  - pointer to hid output data structure
+ ******************************************************************************/
+static int pt_hid_output_validate_app_response(
+		struct pt_core_data *cd,
+		struct pt_hid_output *hid_output)
+{
+	int command_code;
+	u16 size;
+
+	size = get_unaligned_le16(&cd->response_buf[0]);
+
+	if (hid_output->reset_expected && !size)
+		return 0;
+
+	if (cd->response_buf[PIP1_RESP_REPORT_ID_OFFSET]
+			!= PT_PIP_NON_HID_RESPONSE_ID) {
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: APP output response, wrong report_id\n", __func__);
+		return -EPROTO;
+	}
+
+	command_code = cd->response_buf[PIP1_RESP_COMMAND_ID_OFFSET]
+		& PIP1_RESP_COMMAND_ID_MASK;
+	if (command_code != hid_output->command_code) {
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: APP output response, wrong command_code:%X\n",
+			__func__, command_code);
+		return -EPROTO;
+	}
+
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_check_set_parameter
+ *
+ * SUMMARY: Check command input and response for Set Parameter command.And
+ *  store the parameter to the list for resume work if pass the check.
+ *
+ * PARAMETERS:
+ *  *cd          - pointer to core data
+ *  *hid_output  - pointer to hid output data structure
+ *   raw         - flag to show if output cmd is user cmd(1:user cmd)
+ ******************************************************************************/
+static void pt_check_set_parameter(struct pt_core_data *cd,
+		struct pt_hid_output *hid_output, bool raw)
+{
+	u8 *param_buf;
+	u32 param_value = 0;
+	u8 param_size;
+	u8 param_id;
+	int i = 0;
+
+	if (!(cd->cpdata->flags & PT_CORE_FLAG_RESTORE_PARAMETERS))
+		return;
+
+	/* Check command input for Set Parameter command */
+	if (raw && hid_output->length >= 10 && hid_output->length <= 13
+			&& !memcmp(&hid_output->write_buf[0],
+					&cd->hid_desc.output_register,
+					sizeof(cd->hid_desc.output_register))
+			&& hid_output->write_buf[4] ==
+					PT_PIP_NON_HID_COMMAND_ID
+			&& hid_output->write_buf[6] ==
+					PIP1_CMD_ID_SET_PARAM)
+		param_buf = &hid_output->write_buf[7];
+	else if (!raw && hid_output->cmd_type == PIP1_CMD_TYPE_FW
+			&& hid_output->command_code == PIP1_CMD_ID_SET_PARAM
+			&& hid_output->write_length >= 3
+			&& hid_output->write_length <= 6)
+		param_buf = &hid_output->write_buf[0];
+	else
+		return;
+
+	/* Get parameter ID, size and value */
+	param_id = param_buf[0];
+	param_size = param_buf[1];
+	if (param_size > 4) {
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: Invalid parameter size\n", __func__);
+		return;
+	}
+
+	param_buf = &param_buf[2];
+	while (i < param_size)
+		param_value += *(param_buf++) << (8 * i++);
+
+	/* Check command response for Set Parameter command */
+	if (cd->response_buf[2] != PT_PIP_NON_HID_RESPONSE_ID
+			|| (cd->response_buf[4] &
+			    PIP1_RESP_COMMAND_ID_MASK) !=
+				PIP1_CMD_ID_SET_PARAM
+			|| cd->response_buf[5] != param_id
+			|| cd->response_buf[6] != param_size) {
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: Set Parameter command not successful\n",
+			__func__);
+		return;
+	}
+
+	pt_add_parameter(cd, param_id, param_value, param_size);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_check_command
+ *
+ * SUMMARY: Check the output command. The function pt_check_set_parameter() is
+ *  called here to check output command and store parameter to the list.
+ *
+ * PARAMETERS:
+ *  *cd          - pointer to core data
+ *  *hid_output  - pointer to hid output data structure
+ *   raw         - flag to show if output cmd is user cmd(1:user cmd)
+ ******************************************************************************/
+static void pt_check_command(struct pt_core_data *cd,
+		struct pt_hid_output *hid_output, bool raw)
+{
+	pt_check_set_parameter(cd, hid_output, raw);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_hid_output_validate_response
+ *
+ * SUMMARY: Validate the response of application or bootloader command.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *cd          - pointer to core data
+ *  *hid_output  - pointer to hid output data structure
+ ******************************************************************************/
+static int pt_hid_output_validate_response(struct pt_core_data *cd,
+		struct pt_hid_output *hid_output)
+{
+	if (hid_output->cmd_type == PIP1_CMD_TYPE_BL)
+		return pt_hid_output_validate_bl_response(cd, hid_output);
+
+	return pt_hid_output_validate_app_response(cd, hid_output);
+
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_hid_send_output_user_
+ *
+ * SUMMARY: Blindly send user data to the DUT.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd         - pointer to core data
+ *	*hid_output - pointer to the command to send
+ ******************************************************************************/
+static int pt_hid_send_output_user_(struct pt_core_data *cd,
+		struct pt_hid_output *hid_output)
+{
+	int rc = 0;
+	int cmd;
+
+	if (!hid_output->length || !hid_output->write_buf)
+		return -EINVAL;
+
+	if (cd->pip2_prot_active) {
+		cmd = hid_output->write_buf[PIP2_CMD_COMMAND_ID_OFFSET];
+		cmd &= PIP2_CMD_COMMAND_ID_MASK;
+	} else
+		cmd = hid_output->write_buf[PIP1_CMD_COMMAND_ID_OFFSET];
+
+	pt_debug(cd->dev, DL_INFO,
+		">>> %s: Write Buffer Size[%d] Cmd[0x%02X]\n",
+		__func__, hid_output->length, cmd);
+
+	pt_pr_buf(cd->dev, DL_DEBUG, hid_output->write_buf,
+		hid_output->length, ">>> User CMD");
+
+	rc = pt_adap_write_read_specific(cd, hid_output->length,
+			hid_output->write_buf, NULL);
+	if (rc)
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: Fail pt_adap_transfer\n", __func__);
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_hid_send_output_user_and_wait_
+ *
+ * SUMMARY: Blindly send user data to the DUT and wait for the response.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd         - pointer to core data
+ *	*hid_output - pointer to the command to send
+ ******************************************************************************/
+static int pt_hid_send_output_user_and_wait_(struct pt_core_data *cd,
+		struct pt_hid_output *hid_output)
+{
+	int rc = 0;
+	int t;
+
+	mutex_lock(&cd->system_lock);
+	cd->hid_cmd_state = PIP1_CMD_ID_USER_CMD + 1;
+	mutex_unlock(&cd->system_lock);
+
+	rc = pt_hid_send_output_user_(cd, hid_output);
+	if (rc)
+		goto error;
+
+	t = wait_event_timeout(cd->wait_q, (cd->hid_cmd_state == 0),
+		msecs_to_jiffies(cd->pip_cmd_timeout));
+	if (IS_TMO(t)) {
+#ifdef TTDL_DIAGNOSTICS
+		cd->bus_transmit_error_count++;
+		pt_toggle_err_gpio(cd, PT_ERR_GPIO_I2C_TRANS);
+#endif /* TTDL_DIAGNOSTICS */
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: HID output cmd execution timed out\n",
+			__func__);
+		rc = -ETIME;
+		goto error;
+	}
+	pt_check_command(cd, hid_output, true);
+	goto exit;
+
+error:
+	mutex_lock(&cd->system_lock);
+	cd->hid_cmd_state = 0;
+	mutex_unlock(&cd->system_lock);
+
+exit:
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_check_irq_asserted
+ *
+ * SUMMARY: Checks if the IRQ GPIO is asserted or not. There are times when
+ *	the FW can hold the INT line low ~150us after the read is complete.
+ *	NOTE: if irq_stat is not defined this function will return false
+ *
+ * RETURN:
+ *	true  = IRQ asserted
+ *	false = IRQ not asserted
+ *
+ * PARAMETERS:
+ *      *cd - pointer to core data
+ ******************************************************************************/
+static bool pt_check_irq_asserted(struct pt_core_data *cd)
+{
+#ifdef ENABLE_WORKAROUND_FOR_GLITCH_AFTER_BL_LAUNCH_APP
+	/*
+	 * Workaround for FW defect, CDT165308
+	 * bl_launch app creates a glitch in IRQ line
+	 */
+	if (cd->hid_cmd_state == PIP1_BL_CMD_ID_LAUNCH_APP + 1
+		&& cd->cpdata->irq_stat) {
+		/*
+		 * in X1S panel and GC1546 panel, the width for the INT
+		 * glitch is about 4us,the normal INT width of response
+		 * will last more than 200us, so use 10us delay
+		 * for distinguish the glitch the normal INT is enough.
+		 */
+		udelay(10);
+	}
+#endif
+	if (cd->cpdata->irq_stat) {
+		if (cd->cpdata->irq_stat(cd->cpdata, cd->dev)
+		    == PT_IRQ_ASSERTED_VALUE) {
+			/* Debounce to allow FW to release INT */
+			usleep_range(100, 200);
+		}
+		if (cd->cpdata->irq_stat(cd->cpdata, cd->dev)
+		    == PT_IRQ_ASSERTED_VALUE)
+			return true;
+		else
+			return false;
+	}
+	return true;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_flush_bus
+ *
+ * SUMMARY: Force flushing the bus by reading len bytes or forced 255 bytes
+ *	Used if IRQ is found to be stuck low
+ *
+ * RETURN: Length of bytes read from bus
+ *
+ * PARAMETERS:
+ *      *cd         - pointer to core data
+ *	 flush_type - type of flush
+ *			- PT_FLUSH_BUS_BASED_ON_LEN (two reads)
+ *			- PT_FLUSH_BUS_FULL_256_READ
+ *      *read_buf  - pointer to store read data
+ ******************************************************************************/
+static ssize_t pt_flush_bus(struct pt_core_data *cd,
+		u8 flush_type, u8 *read_buf)
+{
+	u8 buf[PT_MAX_PIP2_MSG_SIZE];
+	u16 pip_len;
+	int bytes_read;
+	int rc = 0;
+
+	if (flush_type == PT_FLUSH_BUS_BASED_ON_LEN) {
+		rc = pt_adap_read_default(cd, buf, 2);
+		if (rc) {
+			bytes_read = 0;
+			goto exit;
+		}
+
+		pip_len = get_unaligned_le16(&buf[0]);
+
+		if (pip_len == 2 || pip_len >= PT_PIP_1P7_EMPTY_BUF) {
+#ifdef TTDL_DIAGNOSTICS
+			pt_toggle_err_gpio(cd, PT_ERR_GPIO_EMPTY_PACKET);
+#endif
+			bytes_read = 2;
+			pt_debug(cd->dev, DL_INFO,
+				"%s: Empty buf detected - len=0x%04X\n",
+				__func__, pip_len);
+		} else if (pip_len == 0) {
+			bytes_read = 0;
+			pt_debug(cd->dev, DL_INFO,
+				"%s: Sentinel detected\n", __func__);
+		} else if (pip_len > PT_MAX_PIP2_MSG_SIZE) {
+			pt_debug(cd->dev, DL_ERROR,
+				"%s: Illegal len=0x%04x, force %d byte read\n",
+				__func__, pip_len, PT_MAX_PIP2_MSG_SIZE);
+			rc = pt_adap_read_default(cd, buf,
+				PT_MAX_PIP2_MSG_SIZE);
+			if (!rc)
+				bytes_read = PT_MAX_PIP2_MSG_SIZE;
+			else
+				bytes_read = 0;
+		} else {
+			pt_debug(cd->dev, DL_INFO,
+				"%s: Flush read of %d bytes...\n",
+				__func__, pip_len);
+
+			rc = pt_adap_read_default(cd, buf, pip_len);
+			if (!rc)
+				bytes_read = pip_len;
+			else
+				bytes_read = 0;
+		}
+	} else {
+		pt_debug(cd->dev, DL_INFO,
+			"%s: Forced flush of max %d bytes...\n",
+			__func__, PT_MAX_PIP2_MSG_SIZE);
+		rc = pt_adap_read_default(cd, buf, PT_MAX_PIP2_MSG_SIZE);
+		if (!rc)
+			bytes_read = PT_MAX_PIP2_MSG_SIZE;
+		else
+			bytes_read = 0;
+	}
+
+	if (read_buf && (bytes_read > 3))
+		memcpy(read_buf, buf, bytes_read);
+
+exit:
+	return bytes_read;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_flush_bus_if_irq_asserted
+ *
+ * SUMMARY: This function will flush the active bus if the INT is found to be
+ *	asserted.
+ *
+ * RETURN: bytes cleared from bus
+ *
+ * PARAMETERS:
+ *	*cd         - pointer the core data structure
+ *	 flush_type - type of flush
+ *			- PT_FLUSH_BUS_BASED_ON_LEN
+ *			- PT_FLUSH_BUS_FULL_256_READ
+ ******************************************************************************/
+static int pt_flush_bus_if_irq_asserted(struct pt_core_data *cd, u8 flush_type)
+{
+	int count = 0;
+	int bytes_read = 0;
+
+	while (pt_check_irq_asserted(cd) && count < 5) {
+		count++;
+		bytes_read = pt_flush_bus(cd, flush_type, NULL);
+		if (bytes_read) {
+			pt_debug(cd->dev, DL_WARN,
+				"%s: Cleared %d bytes off bus\n",
+				__func__, bytes_read);
+		}
+	}
+	if (pt_check_irq_asserted(cd)) {
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: IRQ still asserted, %d bytes read\n",
+			__func__, bytes_read);
+	} else {
+		pt_debug(cd->dev, DL_INFO,
+			"%s: IRQ cleared, %d bytes read\n",
+			__func__, bytes_read);
+	}
+	return bytes_read;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_hid_send_output_
+ *
+ * SUMMARY: Send a touch application command to the DUT
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd         - pointer to core data
+ *	*hid_output - pointer to the command to send
+ ******************************************************************************/
+static int pt_hid_send_output_(struct pt_core_data *cd,
+		struct pt_hid_output *hid_output)
+{
+	int rc = 0;
+	u8 *cmd;
+	u16 length;
+	u16 crc;
+	u8 report_id;
+	u8 cmd_offset = 0;
+	u8 cmd_allocated = 0;
+
+	switch (hid_output->cmd_type) {
+	case PIP1_CMD_TYPE_FW:
+		report_id = PT_PIP_NON_HID_COMMAND_ID;
+		length = 5;
+		break;
+	case PIP1_CMD_TYPE_BL:
+		report_id = PT_PIP_BL_COMMAND_REPORT_ID;
+		length = 11 /* 5 + SOP + LEN(2) + CRC(2) + EOP */;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	length += hid_output->write_length;
+
+	if (length + 2 > PT_PREALLOCATED_CMD_BUFFER) {
+		cmd = kzalloc(length + 2, GFP_KERNEL);
+		if (!cmd)
+			return -ENOMEM;
+		cmd_allocated = 1;
+	} else {
+		cmd = cd->cmd_buf;
+	}
+
+	/* Set Output register */
+	memcpy(&cmd[cmd_offset], &cd->hid_desc.output_register,
+			sizeof(cd->hid_desc.output_register));
+	cmd_offset += sizeof(cd->hid_desc.output_register);
+
+	cmd[cmd_offset++] = LOW_BYTE(length);
+	cmd[cmd_offset++] = HI_BYTE(length);
+	cmd[cmd_offset++] = report_id;
+	cmd[cmd_offset++] = 0x0; /* reserved */
+	if (hid_output->cmd_type == PIP1_CMD_TYPE_BL)
+		cmd[cmd_offset++] = PIP1_BL_SOP;
+	cmd[cmd_offset++] = hid_output->command_code;
+
+	/* Set Data Length for bootloader */
+	if (hid_output->cmd_type == PIP1_CMD_TYPE_BL) {
+		cmd[cmd_offset++] = LOW_BYTE(hid_output->write_length);
+		cmd[cmd_offset++] = HI_BYTE(hid_output->write_length);
+	}
+	/* Set Data */
+	if (hid_output->write_length && hid_output->write_buf) {
+		memcpy(&cmd[cmd_offset], hid_output->write_buf,
+				hid_output->write_length);
+		cmd_offset += hid_output->write_length;
+	}
+	if (hid_output->cmd_type == PIP1_CMD_TYPE_BL) {
+		crc = _pt_compute_crc(&cmd[6],
+				hid_output->write_length + 4);
+		cmd[cmd_offset++] = LOW_BYTE(crc);
+		cmd[cmd_offset++] = HI_BYTE(crc);
+		cmd[cmd_offset++] = PIP1_BL_EOP;
+	}
+
+	pt_debug(cd->dev, DL_INFO,
+		">>> %s: Write Buffer Size[%d] Cmd[0x%02X]\n",
+		__func__, length + 2, hid_output->command_code);
+
+	pt_pr_buf(cd->dev, DL_DEBUG, cmd, length + 2, ">>> CMD");
+	rc = pt_adap_write_read_specific(cd, length + 2, cmd, NULL);
+
+	if (rc)
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: Fail pt_adap_transfer rc=%d\n", __func__, rc);
+
+	if (cmd_allocated)
+		kfree(cmd);
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pip1_send_output_and_wait_
+ *
+ * SUMMARY: Send valid PIP1 command to the DUT and wait for the response.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd         - pointer to core data
+ *	*hid_output - pointer to the command to send
+ ******************************************************************************/
+static int pt_pip1_send_output_and_wait_(struct pt_core_data *cd,
+		struct pt_hid_output *hid_output)
+{
+	int rc = 0;
+	int t;
+	u16 timeout_ms;
+
+	mutex_lock(&cd->system_lock);
+	cd->hid_cmd_state = hid_output->command_code + 1;
+	mutex_unlock(&cd->system_lock);
+
+	if (hid_output->timeout_ms)
+		timeout_ms = hid_output->timeout_ms;
+	else
+		timeout_ms = PT_PIP1_CMD_DEFAULT_TIMEOUT;
+
+	rc = pt_hid_send_output_(cd, hid_output);
+	if (rc)
+		goto error;
+
+	t = wait_event_timeout(cd->wait_q, (cd->hid_cmd_state == 0),
+			msecs_to_jiffies(timeout_ms));
+	if (IS_TMO(t)) {
+#ifdef TTDL_DIAGNOSTICS
+		cd->bus_transmit_error_count++;
+		pt_toggle_err_gpio(cd, PT_ERR_GPIO_I2C_TRANS);
+#endif /* TTDL_DIAGNOSTICS */
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: HID output cmd execution timed out (%dms)\n",
+			__func__, timeout_ms);
+		rc = -ETIME;
+		goto error;
+	}
+
+	if (!hid_output->novalidate)
+		rc = pt_hid_output_validate_response(cd, hid_output);
+
+	pt_check_command(cd, hid_output, false);
+	goto exit;
+
+error:
+	mutex_lock(&cd->system_lock);
+	cd->hid_cmd_state = 0;
+	mutex_unlock(&cd->system_lock);
+exit:
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_hid_output_user_cmd_
+ *
+ * SUMMARY: Load the write buffer into a HID structure and send it as a HID cmd
+ *	to the DUT waiting for the response and loading it into the read buffer
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd              - pointer to core data
+ *	 read_len        - expected read length of the response
+ *	*read_buf        - pointer to where the response will be loaded
+ *	 write_len       - length of the write buffer
+ *	*write_buf       - pointer to the write buffer
+ *	*actual_read_len - pointer to the actual amount of data read back
+ ******************************************************************************/
+static int pt_hid_output_user_cmd_(struct pt_core_data *cd,
+		u16 read_len, u8 *read_buf, u16 write_len, u8 *write_buf,
+		u16 *actual_read_len)
+{
+	int rc = 0;
+	u16 size;
+	struct pt_hid_output hid_output = {
+		.length = write_len,
+		.write_buf = write_buf,
+	};
+#ifdef TTHE_TUNER_SUPPORT
+	if (!cd->pip2_send_user_cmd) {
+		int command_code = 0;
+		int len;
+
+		/* Print up to cmd ID */
+		len = PIP1_CMD_COMMAND_ID_OFFSET + 1;
+		if (write_len < len)
+			len = write_len;
+		else
+			command_code = write_buf[PIP1_CMD_COMMAND_ID_OFFSET]
+				& PIP1_CMD_COMMAND_ID_MASK;
+
+		/* Don't print EXEC_PANEL_SCAN & RETRIEVE_PANEL_SCAN commands */
+		if (command_code != PIP1_CMD_ID_EXEC_PANEL_SCAN &&
+		    command_code != PIP1_CMD_ID_RETRIEVE_PANEL_SCAN)
+			tthe_print(cd, write_buf, len, "CMD=");
+	}
+#endif
+	rc = pt_hid_send_output_user_and_wait_(cd, &hid_output);
+	if (rc)
+		return rc;
+
+	/* Get the response size from the first 2 bytes in the response */
+	size = get_unaligned_le16(&cd->response_buf[0]);
+
+	/* Ensure size is not greater than max buffer size */
+	if (size > PT_MAX_PIP2_MSG_SIZE)
+		size = PT_MAX_PIP2_MSG_SIZE;
+
+	/* Minimum size to read is the 2 byte len field */
+	if (size == 0)
+		size = 2;
+
+	if (size > read_len) {
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: PIP2 len field=%d, requested read_len=%d\n",
+			__func__, size, read_len);
+		*actual_read_len = 0;
+		return -EIO;
+	}
+
+	memcpy(read_buf, cd->response_buf, size);
+	*actual_read_len = size;
+
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_hid_output_user_cmd
+ *
+ * SUMMARY: Protected call to pt_hid_output_user_cmd_ by exclusive access to
+ *  the DUT.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd              - pointer to core data
+ *	 read_len        - length of data to read
+ *	*read_buf        - pointer to store read data
+ *	 write_len       - length of data to write
+ *	*write_buf       - pointer to buffer to write
+ *	*actual_read_len - pointer to store data length actually read
+ ******************************************************************************/
+static int pt_hid_output_user_cmd(struct pt_core_data *cd,
+		u16 read_len, u8 *read_buf, u16 write_len, u8 *write_buf,
+		u16 *actual_read_len)
+{
+	int rc;
+
+	rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
+	if (rc < 0) {
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: fail get exclusive ex=%p own=%p\n",
+			__func__, cd->exclusive_dev, cd->dev);
+		return rc;
+	}
+
+	rc = pt_hid_output_user_cmd_(cd, read_len, read_buf,
+			write_len, write_buf, actual_read_len);
+
+	if (release_exclusive(cd, cd->dev) < 0)
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: fail to release exclusive\n", __func__);
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_request_pip2_send_cmd
+ *
+ * SUMMARY: Writes a PIP2 command packet to DUT, then waits for the
+ *	interrupt and reads response data to read_buf
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*dev             - pointer to device structure
+ *	 protect         - flag to run in protected mode
+ *	 id              - ID of PIP command
+ *	*data            - pointer to PIP data payload
+ *	 report_body_len - report length
+ *	*read_buf        - pointer to response buffer
+ *	*actual_read_len - pointer to response buffer length
+ ******************************************************************************/
+static int _pt_request_pip2_send_cmd(struct device *dev,
+	int protect, u8 id, u8 *data, u16 report_body_len, u8 *read_buf,
+	u16 *actual_read_len)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pip2_cmd_structure pip2_cmd;
+	int rc = 0;
+	u16 i = 0;
+	u16 j = 0;
+	u16 write_len;
+	u8 *write_buf = NULL;
+	u16 read_len;
+	u8 extra_bytes;
+
+	if (report_body_len > 247)
+		return -EPROTO;
+
+	memset(&pip2_cmd, 0, sizeof(pip2_cmd));
+	/* Hard coded register for PIP2.x */
+	pip2_cmd.reg[0] = 0x01;
+	pip2_cmd.reg[1] = 0x01;
+
+	/*
+	 * For PIP2.1+ the length field value includes itself:
+	 * ADD 6: 2 (LEN) + 1 (SEQ) + 1 (REPORT ID) + 2 (CRC)
+	 *
+	 * The overall write length must include only the register:
+	 * ADD 2: 2 (Register)
+	 */
+	extra_bytes = 6;
+	write_len = 2;
+
+	/* PIP2 the CMD ID is a 7bit field */
+	if (id > PIP2_CMD_ID_END) {
+		pt_debug(dev, DL_WARN, "%s: Invalid PIP2 CMD ID 0x%02X\n",
+			__func__, id);
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	pip2_cmd.len  = report_body_len + extra_bytes;
+	pip2_cmd.id   = id & PIP2_CMD_COMMAND_ID_MASK;
+	pip2_cmd.seq  = pt_pip2_get_next_cmd_seq(cd);
+	pip2_cmd.data = data;
+
+	pt_pip2_cmd_calculate_crc(&pip2_cmd, extra_bytes);
+
+	/* Add the command length to the extra bytes based on PIP version */
+	write_len += pip2_cmd.len;
+
+	pt_debug(dev, DL_INFO, "%s Length Field: %d, Write Len: %d",
+		__func__, pip2_cmd.len, write_len);
+
+	write_buf = kzalloc(write_len, GFP_KERNEL);
+
+	if (write_buf == NULL) {
+		rc = -ENOMEM;
+		goto exit;
+	}
+
+	write_buf[i++] = pip2_cmd.reg[0];
+	write_buf[i++] = pip2_cmd.reg[1];
+	write_buf[i++] = pip2_cmd.len & 0xff;
+	write_buf[i++] = (pip2_cmd.len & 0xff00) >> 8;
+	write_buf[i++] = pip2_cmd.seq;
+	write_buf[i++] = pip2_cmd.id;
+
+	for (j = i; j < i + pip2_cmd.len - extra_bytes; j++)
+		write_buf[j] = pip2_cmd.data[j-i];
+	write_buf[j++] = pip2_cmd.crc[0];
+	write_buf[j++] = pip2_cmd.crc[1];
+
+	read_len = pt_pip2_get_cmd_response_len(pip2_cmd.id);
+	if (read_len < 0)
+		read_len = 255;
+
+	pt_debug(dev, DL_INFO,
+		"%s cmd_id[0x%02X] expected response length:%d ",
+		__func__, pip2_cmd.id, read_len);
+
+	/*
+	 * All PIP2 commands come through this function.
+	 * Set flag for PIP2.x interface to allow response parsing to know
+	 * how to decode the protocol header.
+	 */
+	mutex_lock(&cd->system_lock);
+	cd->pip2_prot_active = true;
+	cd->pip2_send_user_cmd = true;
+	mutex_unlock(&cd->system_lock);
+
+	if (protect == PT_CORE_CMD_PROTECTED)
+		rc = pt_hid_output_user_cmd(cd, read_len, read_buf,
+			write_len, write_buf, actual_read_len);
+	else {
+		rc = pt_hid_output_user_cmd_(cd, read_len, read_buf,
+			write_len, write_buf, actual_read_len);
+	}
+	if (rc) {
+		pt_debug(dev, DL_ERROR,
+			"%s: nonhid_cmd->user_cmd() Error = %d\n",
+			__func__, rc);
+		goto exit;
+	}
+	rc = pt_pip2_validate_response(cd, &pip2_cmd, read_buf,
+		*actual_read_len);
+exit:
+	mutex_lock(&cd->system_lock);
+	cd->pip2_prot_active = false;
+	cd->pip2_send_user_cmd = false;
+	mutex_unlock(&cd->system_lock);
+	kfree(write_buf);
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_pip2_send_cmd_no_int
+ *
+ * SUMMARY: Writes a PIP2 command packet to DUT, then poll the response and
+ *	reads response data to read_buf if response is available.
+ *
+ * NOTE:
+ *  Interrupt MUST be disabled before to call this function.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*dev             - pointer to device structure
+ *	 protect         - flag to run in protected mode
+ *	 id              - ID of PIP command
+ *	*data            - pointer to PIP data payload
+ *	 report_body_len - report length
+ *	*read_buf        - pointer to response buffer
+ *	*actual_read_len - pointer to response buffer length
+ ******************************************************************************/
+static int _pt_pip2_send_cmd_no_int(struct device *dev,
+	int protect, u8 id, u8 *data, u16 report_body_len, u8 *read_buf,
+	u16 *actual_read_len)
+{
+	int max_retry = 0;
+	int retry = 0;
+	int rc = 0;
+	u16 i = 0;
+	u16 j = 0;
+	u16 write_len;
+	u8 *write_buf = NULL;
+	u16 read_len;
+	u16 size = 0;
+	u8 response_seq = 0;
+	u8 extra_bytes;
+	u32 retry_interval = 0;
+	u32 retry_total_time = 0;
+	u32 resp_time_min = pt_pip2_get_cmd_resp_time_min(id);
+	u32 resp_time_max = pt_pip2_get_cmd_resp_time_max(id);
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pip2_cmd_structure pip2_cmd;
+
+	if (report_body_len > 247)
+		return -EPROTO;
+
+	if (protect == PT_CORE_CMD_PROTECTED) {
+		rc = request_exclusive(cd,
+			cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
+		if (rc < 0) {
+			pt_debug(cd->dev, DL_ERROR,
+				"%s: fail get exclusive ex=%p own=%p\n",
+				__func__, cd->exclusive_dev, cd->dev);
+			return rc;
+		}
+	}
+
+	memset(&pip2_cmd, 0, sizeof(pip2_cmd));
+
+	/* Hard coded register for PIP2.x */
+	pip2_cmd.reg[0] = 0x01;
+	pip2_cmd.reg[1] = 0x01;
+
+	/*
+	 * For PIP2.1+ the length field value includes itself:
+	 * ADD 6: 2 (LEN) + 1 (SEQ) + 1 (REPORT ID) + 2 (CRC)
+	 *
+	 * The overall write length must include only the register:
+	 * ADD 2: 2 (Register)
+	 */
+	extra_bytes = 6;
+	write_len = 2;
+
+	pip2_cmd.len  = report_body_len + extra_bytes;
+	pip2_cmd.id   = id;
+	pip2_cmd.seq  = pt_pip2_get_next_cmd_seq(cd);
+	pip2_cmd.data = data;
+	pt_pip2_cmd_calculate_crc(&pip2_cmd, extra_bytes);
+
+	/* Add the command length to the extra bytes based on PIP version */
+	write_len += pip2_cmd.len;
+
+	write_buf = kzalloc(write_len, GFP_KERNEL);
+	if (write_buf == NULL) {
+		rc = -ENOMEM;
+		goto exit;
+	}
+
+	write_buf[i++] = pip2_cmd.reg[0];
+	write_buf[i++] = pip2_cmd.reg[1];
+	write_buf[i++] = pip2_cmd.len & 0xff;
+	write_buf[i++] = (pip2_cmd.len & 0xff00) >> 8;
+	write_buf[i++] = pip2_cmd.seq;
+	write_buf[i++] = pip2_cmd.id;
+
+	for (j = i; j < i + pip2_cmd.len - extra_bytes; j++)
+		write_buf[j] = pip2_cmd.data[j-i];
+	write_buf[j++] = pip2_cmd.crc[0];
+	write_buf[j++] = pip2_cmd.crc[1];
+
+	read_len = pt_pip2_get_cmd_response_len(pip2_cmd.id);
+	if (read_len < 0)
+		read_len = 255;
+	pt_debug(dev, DL_INFO,
+		"%s: ATM - cmd_id[0x%02X] expected response length:%d ",
+		__func__, pip2_cmd.id, read_len);
+
+	pt_pr_buf(cd->dev, DL_DEBUG, write_buf, write_len, ">>> NO_INT CMD");
+
+	rc = pt_adap_write_read_specific(cd, write_len, write_buf, NULL);
+	if (rc) {
+		pt_debug(dev, DL_ERROR,
+			"%s: SPI write Error = %d\n",
+			__func__, rc);
+		goto exit;
+	}
+
+#ifdef PT_POLL_RESP_BY_BUS
+	/*
+	 * Frequent bus read can increase system load obviously. The expected
+	 * first bus read should be valid and timely. The tollerance for
+	 * usleep_range should be limited. The minimum response delay (between
+	 * command finishes sending and INT pin falls) is less than 50
+	 * microseconds. So the 10 microseconds should be maximum tollerance
+	 * with the consideration that the unit to calculate the response delay
+	 * is 10 microseconds and more precise is not necessary. Every
+	 * additional 10 microseconds only contribute less than 3 milliseconds
+	 * for whole BL.
+	 */
+	usleep_range(resp_time_min, resp_time_min+10);
+	max_retry = resp_time_max / POLL_RETRY_DEFAULT_INTERVAL;
+	while ((retry < max_retry) && (retry_total_time < resp_time_max)) {
+		rc = pt_adap_read_default(cd, read_buf, read_len);
+		if (rc) {
+			pt_debug(dev, DL_ERROR, "%s: SPI read Error = %d\n",
+				__func__, rc);
+			break;
+		}
+		response_seq = read_buf[PIP2_RESP_SEQUENCE_OFFSET];
+		size = get_unaligned_le16(&read_buf[0]);
+		if ((size <= read_len) &&
+			(size >= PIP2_EXTRA_BYTES_NUM)  &&
+			(pip2_cmd.seq & 0x07) == (response_seq & 0x07)) {
+			break;
+		}
+
+		/*
+		 * To reduce the bus and system load, increase the sleep
+		 * step gradually:
+		 * 1  ~ 19 : step=50 us, sleep_us=[50, 100, 150, 200, ..950]
+		 * 20 ~ 39 : step=1000 us, sleep_us=[1950, 2950, ...20950]
+		 * 40 ~ MAX: step=50 ms, sleep_ms=[71, 121, 191,..]
+		 */
+		retry++;
+		if (retry < 20) {
+			retry_interval += POLL_RETRY_DEFAULT_INTERVAL;
+			usleep_range(retry_interval,
+				retry_interval + POLL_RETRY_DEFAULT_INTERVAL);
+		} else if (retry < 40) {
+			retry_interval += 1000;
+			usleep_range(retry_interval, retry_interval + 1000);
+		} else {
+			retry_interval += 50000;
+			msleep(retry_interval/1000);
+		}
+		retry_total_time += retry_interval;
+	}
+#else
+	/*
+	 * Frequent GPIO read will not increase CPU/system load heavily if the
+	 * interval is longer than 10 us, so it is safe to poll GPIO with a
+	 * fixed interval: 20 us.
+	 */
+	usleep_range(resp_time_min, resp_time_min+10);
+	max_retry = resp_time_max / POLL_RETRY_DEFAULT_INTERVAL;
+	while ((retry < max_retry) && (retry_total_time < resp_time_max)) {
+		if (!gpio_get_value(cd->cpdata->irq_gpio)) {
+			rc = pt_adap_read_default(cd, read_buf, read_len);
+			size = get_unaligned_le16(&read_buf[0]);
+			if (rc)
+				pt_debug(dev, DL_ERROR,
+					"%s: SPI read Error = %d\n",
+					__func__, rc);
+			else if (size > read_len) {
+				pt_debug(cd->dev, DL_ERROR,
+					"%s: PIP2 len field=%d, requested read_len=%d\n",
+					__func__, size, read_len);
+				rc = -EIO;
+			}
+			break;
+		}
+		/*
+		 * Poll GPIO with fixed interval 20 us, and tollerance is
+		 * limited to 10 us to speed up the process.
+		 */
+		retry_interval = POLL_RETRY_DEFAULT_INTERVAL;
+		usleep_range(retry_interval, retry_interval+10);
+		retry_total_time += retry_interval;
+	}
+#endif
+
+	*actual_read_len = size;
+
+	if (rc || (retry >= max_retry) || (retry_total_time >= resp_time_max)) {
+		pt_debug(dev, DL_ERROR,
+			"%s cmd[0x%02X] timed out, send_seq=0x%02X, resp_seq=0x%02X\n",
+			__func__, pip2_cmd.id, pip2_cmd.seq, response_seq);
+		*actual_read_len = 0;
+		rc = -EINVAL;
+	}
+
+	pt_pr_buf(cd->dev, DL_DEBUG, read_buf, *actual_read_len,
+		"<<< NO_INT Read");
+
+exit:
+	kfree(write_buf);
+	if (protect == PT_CORE_CMD_PROTECTED) {
+		if (release_exclusive(cd, cd->dev) < 0)
+			pt_debug(cd->dev, DL_ERROR,
+				"%s: fail to release exclusive\n", __func__);
+	}
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pip_null_
+ *
+ * SUMMARY: Send the PIP "ping"(0x00) command to the DUT and wait for response.
+ *  This function is used by watchdog to check if the fw corrupts.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *cd  - pointer to core data
+ ******************************************************************************/
+static int pt_pip_null_(struct pt_core_data *cd)
+{
+	struct pt_hid_output hid_output = {
+		CREATE_PIP1_FW_CMD(PIP1_CMD_ID_NULL),
+	};
+	return pt_pip1_send_output_and_wait_(cd, &hid_output);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pip_null
+ *
+ * SUMMARY: Wrapper function for pt_pip_null_ that guarantees exclusive access.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *cd  - pointer to core data
+ ******************************************************************************/
+static int pt_pip_null(struct pt_core_data *cd)
+{
+	int rc;
+
+	rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
+	if (rc < 0) {
+		pt_debug(cd->dev, DL_ERROR,
+				"%s: fail get exclusive ex=%p own=%p\n",
+				__func__, cd->exclusive_dev, cd->dev);
+		return rc;
+	}
+
+	rc = pt_pip_null_(cd);
+
+	if (release_exclusive(cd, cd->dev) < 0)
+		pt_debug(cd->dev, DL_ERROR,
+				"%s: fail to release exclusive\n", __func__);
+
+	return rc;
+}
+
+static void pt_stop_wd_timer(struct pt_core_data *cd);
+/*******************************************************************************
+ * FUNCTION: pt_pip_start_bootloader_
+ *
+ * SUMMARY: Sends the HID command start_bootloader [PIP cmd 0x01] to the DUT
+ *
+ *	NOTE: The WD MUST be stopped/restarted by the calling Function. Having
+ *        the WD active could cause this function to fail!
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd - pointer to core data
+ ******************************************************************************/
+static int pt_pip_start_bootloader_(struct pt_core_data *cd)
+{
+	int rc = 0;
+
+	struct pt_hid_output hid_output = {
+		CREATE_PIP1_FW_CMD(PIP1_CMD_ID_START_BOOTLOADER),
+		.timeout_ms = PT_PIP1_START_BOOTLOADER_TIMEOUT,
+		.reset_expected = 1,
+	};
+	if (cd->watchdog_enabled) {
+		pt_debug(cd->dev, DL_WARN,
+			"%s: watchdog isn't stopped before enter bl\n",
+			__func__);
+		goto exit;
+	}
+
+	/* Reset startup status after entering BL, new DUT enum required */
+	cd->startup_status = STARTUP_STATUS_START;
+	pt_debug(cd->dev, DL_DEBUG, "%s: Startup Status Reset\n", __func__);
+	rc = pt_pip1_send_output_and_wait_(cd, &hid_output);
+	if (rc) {
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: Start BL PIP cmd failed. rc = %d\n",
+			__func__, rc);
+	}
+
+exit:
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pip_start_bootloader
+ *
+ * SUMMARY: Protected function to force DUT to enter the BL
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *      *cd - pointer to core data structure
+ ******************************************************************************/
+static int pt_pip_start_bootloader(struct pt_core_data *cd)
+{
+	int rc;
+
+	rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
+	if (rc < 0) {
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: fail get exclusive ex=%p own=%p\n",
+			__func__, cd->exclusive_dev, cd->dev);
+		return rc;
+	}
+
+	rc = pt_pip_start_bootloader_(cd);
+
+	if (release_exclusive(cd, cd->dev) < 0)
+		pt_debug(cd->dev, DL_ERROR,
+				"%s: fail to release exclusive\n", __func__);
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_request_pip_start_bl
+ *
+ * SUMMARY: Function pointer included in core_nonhid_cmds to allow other
+ *	modules to request the DUT to enter the BL
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *      *dev     - pointer to device structure
+ *	 protect - flag to run in protected mode
+ ******************************************************************************/
+static int _pt_request_pip_start_bl(struct device *dev, int protect)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+
+	if (protect)
+		return pt_pip_start_bootloader(cd);
+
+	return pt_pip_start_bootloader_(cd);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pip2_ver_load_ttdata
+ *
+ * SUMMARY: Function to load the Version information from the PIP2 VERSION
+ *	command into the core data struct.
+ *
+ * RETURN: n/a
+ *
+ * PARAMETERS:
+ *      *cd  - pointer to core data structure
+ *	 len - Length of data in response_buf
+ ******************************************************************************/
+static void pt_pip2_ver_load_ttdata(struct pt_core_data *cd, u16 len)
+{
+	struct pt_ttdata *ttdata = &cd->sysinfo.ttdata;
+	struct pt_pip2_version_full *full_ver;
+	struct pt_pip2_version      *ver;
+
+	/*
+	 * The PIP2 VERSION command can return different lengths of data.
+	 * The additional LOT fields are included when the packet
+	 * size is >= 29 bytes. Older FW sends a reduced packet size.
+	 * NOTE:
+	 *  - The FW would swap the BL and FW versions when reporting
+	 * the small packet.
+	 *  - Sub Lot bytes 16 and 17 are reserved.
+	 */
+	if (len >= 0x1D) {
+		full_ver = (struct pt_pip2_version_full *)
+			&cd->response_buf[PIP2_RESP_STATUS_OFFSET];
+
+		ttdata->pip_ver_major = full_ver->pip2_version_msb;
+		ttdata->pip_ver_minor = full_ver->pip2_version_lsb;
+		ttdata->bl_ver_major  = full_ver->bl_version_msb;
+		ttdata->bl_ver_minor  = full_ver->bl_version_lsb;
+		ttdata->fw_ver_major  = full_ver->fw_version_msb;
+		ttdata->fw_ver_minor  = full_ver->fw_version_lsb;
+		/*
+		 * BL PIP 2.02 and greater the version fields are
+		 * swapped
+		 */
+		if (ttdata->pip_ver_major >= 2 && ttdata->pip_ver_minor >= 2) {
+			ttdata->chip_rev =
+			    get_unaligned_le16(&full_ver->chip_rev);
+			ttdata->chip_id =
+			    get_unaligned_le16(&full_ver->chip_id);
+		} else {
+			ttdata->chip_rev =
+			    get_unaligned_le16(&full_ver->chip_id);
+			ttdata->chip_id =
+			    get_unaligned_le16(&full_ver->chip_rev);
+		}
+		memcpy(ttdata->uid, full_ver->uid, PT_UID_SIZE);
+
+		pt_pr_buf(cd->dev, DL_INFO, (u8 *)full_ver,
+			sizeof(struct pt_pip2_version_full),
+			"PIP2 VERSION FULL");
+	} else {
+		ver = (struct pt_pip2_version *)
+			&cd->response_buf[PIP2_RESP_STATUS_OFFSET];
+
+		ttdata->pip_ver_major = ver->pip2_version_msb;
+		ttdata->pip_ver_minor = ver->pip2_version_lsb;
+		ttdata->bl_ver_major  = ver->bl_version_msb;
+		ttdata->bl_ver_minor  = ver->bl_version_lsb;
+		ttdata->fw_ver_major  = ver->fw_version_msb;
+		ttdata->fw_ver_minor  = ver->fw_version_lsb;
+		ttdata->chip_rev = get_unaligned_le16(&ver->chip_rev);
+		ttdata->chip_id  = get_unaligned_le16(&ver->chip_id);
+
+		pt_pr_buf(cd->dev, DL_INFO, (u8 *)ver,
+			sizeof(struct pt_pip2_version), "PIP2 VERSION");
+	}
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_si_get_ttdata
+ *
+ * SUMMARY: Function to load the version information from the system information
+ *	PIP command into the core data struct.
+ *
+ * RETURN: n/a
+ *
+ * PARAMETERS:
+ *      *cd - pointer to core data structure
+ ******************************************************************************/
+static void pt_si_get_ttdata(struct pt_core_data *cd)
+{
+	struct pt_ttdata *ttdata = &cd->sysinfo.ttdata;
+	struct pt_ttdata_dev *ttdata_dev =
+		(struct pt_ttdata_dev *)
+		&cd->response_buf[PIP1_SYSINFO_TTDATA_OFFSET];
+
+	ttdata->pip_ver_major = ttdata_dev->pip_ver_major;
+	ttdata->pip_ver_minor = ttdata_dev->pip_ver_minor;
+	ttdata->bl_ver_major = ttdata_dev->bl_ver_major;
+	ttdata->bl_ver_minor = ttdata_dev->bl_ver_minor;
+	ttdata->fw_ver_major = ttdata_dev->fw_ver_major;
+	ttdata->fw_ver_minor = ttdata_dev->fw_ver_minor;
+
+	ttdata->fw_pid = get_unaligned_le16(&ttdata_dev->fw_pid);
+	ttdata->fw_ver_conf = get_unaligned_le16(&ttdata_dev->fw_ver_conf);
+	ttdata->post_code = get_unaligned_le16(&ttdata_dev->post_code);
+	ttdata->revctrl = get_unaligned_le32(&ttdata_dev->revctrl);
+	ttdata->jtag_id_l = get_unaligned_le16(&ttdata_dev->jtag_si_id_l);
+	ttdata->jtag_id_h = get_unaligned_le16(&ttdata_dev->jtag_si_id_h);
+
+	memcpy(ttdata->mfg_id, ttdata_dev->mfg_id, PT_NUM_MFGID);
+
+	pt_pr_buf(cd->dev, DL_INFO, (u8 *)ttdata_dev,
+		sizeof(struct pt_ttdata_dev), "sysinfo_ttdata");
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_si_get_sensing_conf_data
+ *
+ * SUMMARY: Function to load the sensing information from the system information
+ *	PIP command into the core data struct.
+ *
+ * RETURN: n/a
+ *
+ * PARAMETERS:
+ *      *cd - pointer to core data structure
+ ******************************************************************************/
+static void pt_si_get_sensing_conf_data(struct pt_core_data *cd)
+{
+	struct pt_sensing_conf_data *scd = &cd->sysinfo.sensing_conf_data;
+	struct pt_sensing_conf_data_dev *scd_dev =
+		(struct pt_sensing_conf_data_dev *)
+		&cd->response_buf[PIP1_SYSINFO_SENSING_OFFSET];
+
+	scd->electrodes_x = scd_dev->electrodes_x;
+	scd->electrodes_y = scd_dev->electrodes_y;
+	scd->origin_x = scd_dev->origin_x;
+	scd->origin_y = scd_dev->origin_y;
+
+	/* PIP 1.4 (001-82649 *Q) add X_IS_TX bit in X_ORG */
+	if (scd->origin_x & 0x02) {
+		scd->tx_num = scd->electrodes_x;
+		scd->rx_num = scd->electrodes_y;
+	} else {
+		scd->tx_num = scd->electrodes_y;
+		scd->rx_num = scd->electrodes_x;
+	}
+	/*
+	 * When the Panel ID is coming from an XY pin and not a dedicated
+	 * GPIO, store the PID in pid_for_loader. This cannot be done for all
+	 * other DUTs as the loader will use cd->pid_for_loader to generate
+	 * the bin file name but will ignore it if pid_for_loader is still
+	 * set to PANEL_ID_NOT_ENABLED
+	 */
+	if (cd->panel_id_support &
+		(PT_PANEL_ID_BY_BL | PT_PANEL_ID_BY_SYS_INFO)) {
+		mutex_lock(&cd->system_lock);
+		cd->pid_for_loader = scd_dev->panel_id;
+		mutex_unlock(&cd->system_lock);
+	}
+
+	scd->panel_id = scd_dev->panel_id;
+	scd->btn = scd_dev->btn;
+	scd->scan_mode = scd_dev->scan_mode;
+	scd->max_tch = scd_dev->max_num_of_tch_per_refresh_cycle;
+
+	scd->res_x = get_unaligned_le16(&scd_dev->res_x);
+	scd->res_y = get_unaligned_le16(&scd_dev->res_y);
+	scd->max_z = get_unaligned_le16(&scd_dev->max_z);
+	scd->len_x = get_unaligned_le16(&scd_dev->len_x);
+	scd->len_y = get_unaligned_le16(&scd_dev->len_y);
+
+	pt_pr_buf(cd->dev, DL_INFO, (u8 *)scd_dev,
+		sizeof(struct pt_sensing_conf_data_dev),
+		"sensing_conf_data");
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_si_setup
+ *
+ * SUMMARY: Setup the xy_data and xy_mode by allocating the needed memory
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *      *cd - pointer to core data structure
+ ******************************************************************************/
+static int pt_si_setup(struct pt_core_data *cd)
+{
+	struct pt_sysinfo *si = &cd->sysinfo;
+	int max_tch = si->sensing_conf_data.max_tch;
+
+	if (!si->xy_data)
+		si->xy_data = kzalloc(max_tch * si->desc.tch_record_size,
+				GFP_KERNEL);
+	if (!si->xy_data)
+		return -ENOMEM;
+
+	if (!si->xy_mode)
+		si->xy_mode = kzalloc(si->desc.tch_header_size, GFP_KERNEL);
+	if (!si->xy_mode) {
+		kfree(si->xy_data);
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_si_get_btn_data
+ *
+ * SUMMARY: Setup the core data button information based on the response of the
+ *	System Information PIP command.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *      *cd - pointer to core data structure
+ ******************************************************************************/
+static int pt_si_get_btn_data(struct pt_core_data *cd)
+{
+	struct pt_sysinfo *si = &cd->sysinfo;
+	int num_btns = 0;
+	int num_defined_keys;
+	u16 *key_table;
+	int btn;
+	int i;
+	int rc = 0;
+	unsigned int btns = cd->response_buf[PIP1_SYSINFO_BTN_OFFSET]
+		& PIP1_SYSINFO_BTN_MASK;
+	size_t btn_keys_size;
+
+	pt_debug(cd->dev, DL_INFO, "%s: get btn data\n", __func__);
+
+	for (i = 0; i < PIP1_SYSINFO_MAX_BTN; i++) {
+		if (btns & (1 << i))
+			num_btns++;
+	}
+	si->num_btns = num_btns;
+
+	if (num_btns) {
+		btn_keys_size = num_btns * sizeof(struct pt_btn);
+		if (!si->btn)
+			si->btn = kzalloc(btn_keys_size, GFP_KERNEL);
+		if (!si->btn)
+			return -ENOMEM;
+
+		if (cd->cpdata->sett[PT_IC_GRPNUM_BTN_KEYS] == NULL)
+			num_defined_keys = 0;
+		else if (cd->cpdata->sett[PT_IC_GRPNUM_BTN_KEYS]->data == NULL)
+			num_defined_keys = 0;
+		else
+			num_defined_keys = cd->cpdata->sett
+				[PT_IC_GRPNUM_BTN_KEYS]->size;
+
+		for (btn = 0; btn < num_btns && btn < num_defined_keys; btn++) {
+			key_table = (u16 *)cd->cpdata->sett
+				[PT_IC_GRPNUM_BTN_KEYS]->data;
+			si->btn[btn].key_code = key_table[btn];
+			si->btn[btn].enabled = true;
+		}
+		for (; btn < num_btns; btn++) {
+			si->btn[btn].key_code = KEY_RESERVED;
+			si->btn[btn].enabled = true;
+		}
+
+		return rc;
+	}
+
+	kfree(si->btn);
+	si->btn = NULL;
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_si_put_log_data
+ *
+ * SUMMARY: Prints all sys info data to kmsg log
+ *
+ * RETURN: n/a
+ *
+ * PARAMETERS:
+ *	*cd - pointer to core data structure
+ ******************************************************************************/
+static void pt_si_put_log_data(struct pt_core_data *cd)
+{
+	struct pt_sysinfo *si = &cd->sysinfo;
+	struct pt_ttdata *ttdata = &si->ttdata;
+	struct pt_sensing_conf_data *scd = &si->sensing_conf_data;
+	int i;
+
+	pt_debug(cd->dev, DL_DEBUG, "%s: pip_ver_major = 0x%02X (%d)\n",
+		__func__, ttdata->pip_ver_major, ttdata->pip_ver_major);
+	pt_debug(cd->dev, DL_DEBUG, "%s: pip_ver_minor = 0x%02X (%d)\n",
+		__func__, ttdata->pip_ver_minor, ttdata->pip_ver_minor);
+	pt_debug(cd->dev, DL_DEBUG, "%s: fw_pid = 0x%04X (%d)\n",
+		__func__, ttdata->fw_pid, ttdata->fw_pid);
+	pt_debug(cd->dev, DL_DEBUG, "%s: fw_ver_major = 0x%02X (%d)\n",
+		__func__, ttdata->fw_ver_major, ttdata->fw_ver_major);
+	pt_debug(cd->dev, DL_DEBUG, "%s: fw_ver_minor = 0x%02X (%d)\n",
+		__func__, ttdata->fw_ver_minor, ttdata->fw_ver_minor);
+	pt_debug(cd->dev, DL_DEBUG, "%s: revctrl = 0x%08X (%d)\n",
+		__func__, ttdata->revctrl, ttdata->revctrl);
+	pt_debug(cd->dev, DL_DEBUG, "%s: fw_ver_conf = 0x%04X (%d)\n",
+		__func__, ttdata->fw_ver_conf, ttdata->fw_ver_conf);
+	pt_debug(cd->dev, DL_DEBUG, "%s: bl_ver_major = 0x%02X (%d)\n",
+		__func__, ttdata->bl_ver_major, ttdata->bl_ver_major);
+	pt_debug(cd->dev, DL_DEBUG, "%s: bl_ver_minor = 0x%02X (%d)\n",
+		__func__, ttdata->bl_ver_minor, ttdata->bl_ver_minor);
+	pt_debug(cd->dev, DL_DEBUG, "%s: jtag_id_h = 0x%04X (%d)\n",
+		__func__, ttdata->jtag_id_h, ttdata->jtag_id_h);
+	pt_debug(cd->dev, DL_DEBUG, "%s: jtag_id_l = 0x%04X (%d)\n",
+		__func__, ttdata->jtag_id_l, ttdata->jtag_id_l);
+
+	for (i = 0; i < PT_NUM_MFGID; i++)
+		pt_debug(cd->dev, DL_DEBUG,
+			"%s: mfg_id[%d] = 0x%02X (%d)\n",
+			__func__, i, ttdata->mfg_id[i],
+			ttdata->mfg_id[i]);
+
+	pt_debug(cd->dev, DL_DEBUG, "%s: post_code = 0x%04X (%d)\n",
+		__func__, ttdata->post_code, ttdata->post_code);
+	pt_debug(cd->dev, DL_DEBUG, "%s: electrodes_x = 0x%02X (%d)\n",
+		__func__, scd->electrodes_x, scd->electrodes_x);
+	pt_debug(cd->dev, DL_DEBUG, "%s: electrodes_y = 0x%02X (%d)\n",
+		__func__, scd->electrodes_y, scd->electrodes_y);
+	pt_debug(cd->dev, DL_DEBUG, "%s: len_x = 0x%04X (%d)\n",
+		__func__, scd->len_x, scd->len_x);
+	pt_debug(cd->dev, DL_DEBUG, "%s: len_y = 0x%04X (%d)\n",
+		__func__, scd->len_y, scd->len_y);
+	pt_debug(cd->dev, DL_DEBUG, "%s: res_x = 0x%04X (%d)\n",
+		__func__, scd->res_x, scd->res_x);
+	pt_debug(cd->dev, DL_DEBUG, "%s: res_y = 0x%04X (%d)\n",
+		__func__, scd->res_y, scd->res_y);
+	pt_debug(cd->dev, DL_DEBUG, "%s: max_z = 0x%04X (%d)\n",
+		__func__, scd->max_z, scd->max_z);
+	pt_debug(cd->dev, DL_DEBUG, "%s: origin_x = 0x%02X (%d)\n",
+		__func__, scd->origin_x, scd->origin_x);
+	pt_debug(cd->dev, DL_DEBUG, "%s: origin_y = 0x%02X (%d)\n",
+		__func__, scd->origin_y, scd->origin_y);
+	pt_debug(cd->dev, DL_DEBUG, "%s: panel_id = 0x%02X (%d)\n",
+		__func__, scd->panel_id, scd->panel_id);
+	pt_debug(cd->dev, DL_DEBUG, "%s: btn =0x%02X (%d)\n",
+		__func__, scd->btn, scd->btn);
+	pt_debug(cd->dev, DL_DEBUG, "%s: scan_mode = 0x%02X (%d)\n",
+		__func__, scd->scan_mode, scd->scan_mode);
+	pt_debug(cd->dev, DL_DEBUG,
+		"%s: max_num_of_tch_per_refresh_cycle = 0x%02X (%d)\n",
+		__func__, scd->max_tch, scd->max_tch);
+	pt_debug(cd->dev, DL_DEBUG, "%s: xy_mode = %p\n",
+		__func__, si->xy_mode);
+	pt_debug(cd->dev, DL_DEBUG, "%s: xy_data = %p\n",
+		__func__, si->xy_data);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_get_sysinfo_regs
+ *
+ * SUMMARY: Setup all the core data System information based on the response
+ *	of the System Information PIP command.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *      *cd - pointer to core data structure
+ ******************************************************************************/
+static int pt_get_sysinfo_regs(struct pt_core_data *cd)
+{
+	struct pt_sysinfo *si = &cd->sysinfo;
+	int rc;
+
+	rc = pt_si_get_btn_data(cd);
+	if (rc < 0)
+		return rc;
+
+	pt_si_get_ttdata(cd);
+
+	pt_si_get_sensing_conf_data(cd);
+
+	pt_si_setup(cd);
+
+	pt_si_put_log_data(cd);
+
+	si->ready = true;
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_free_si_ptrs
+ *
+ * SUMMARY: Frees all memory associated with the System Information within
+ *	core data
+ *
+ * RETURN: n/a
+ *
+ * PARAMETERS:
+ *      *cd - pointer to core data structure
+ ******************************************************************************/
+static void pt_free_si_ptrs(struct pt_core_data *cd)
+{
+	struct pt_sysinfo *si = &cd->sysinfo;
+
+	kfree(si->btn);
+	kfree(si->xy_mode);
+	kfree(si->xy_data);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_hid_output_get_sysinfo_
+ *
+ * SUMMARY: Sends the PIP Get SYS INFO command to the DUT and waits for the
+ *	response.
+ *
+ * RETURN::
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd - pointer to core data structure
+ ******************************************************************************/
+static int pt_hid_output_get_sysinfo_(struct pt_core_data *cd)
+{
+	int rc = 0;
+	struct pt_hid_output hid_output = {
+		CREATE_PIP1_FW_CMD(PIP1_CMD_ID_GET_SYSINFO),
+		.timeout_ms = PT_PIP1_CMD_GET_SYSINFO_TIMEOUT,
+	};
+
+	rc = pt_pip1_send_output_and_wait_(cd, &hid_output);
+	if (rc)
+		return rc;
+
+	/* Parse the sysinfo data */
+	rc = pt_get_sysinfo_regs(cd);
+	if (rc)
+		pt_free_si_ptrs(cd);
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_hid_output_get_sysinfo
+ *
+ * SUMMARY: Protected call to pt_hid_output_get_sysinfo_
+ *
+ * RETURN::
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd - pointer to core data structure
+ ******************************************************************************/
+static int pt_hid_output_get_sysinfo(struct pt_core_data *cd)
+{
+	int rc;
+
+	rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
+	if (rc < 0) {
+		pt_debug(cd->dev, DL_ERROR,
+				"%s: fail get exclusive ex=%p own=%p\n",
+				__func__, cd->exclusive_dev, cd->dev);
+		return rc;
+	}
+
+	rc = pt_hid_output_get_sysinfo_(cd);
+
+	if (release_exclusive(cd, cd->dev) < 0)
+		pt_debug(cd->dev, DL_ERROR,
+				"%s: fail to release exclusive\n", __func__);
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pip_suspend_scanning_
+ *
+ * SUMMARY: Sends the PIP Suspend Scanning command to the DUT
+ *
+ * RETURN::
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd - pointer to core data structure
+ ******************************************************************************/
+static int pt_pip_suspend_scanning_(struct pt_core_data *cd)
+{
+	int rc = 0;
+	struct pt_hid_output hid_output = {
+		CREATE_PIP1_FW_CMD(PIP1_CMD_ID_SUSPEND_SCANNING),
+	};
+
+	rc = pt_pip1_send_output_and_wait_(cd, &hid_output);
+	if (rc) {
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: Suspend Scan PIP cmd failed. rc = %d\n",
+			__func__, rc);
+	}
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pip_suspend_scanning
+ *
+ * SUMMARY: Protected wrapper for calling pt_hid_output_suspend_scanning_
+ *
+ * RETURN::
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd - pointer to core data structure
+ ******************************************************************************/
+static int pt_pip_suspend_scanning(struct pt_core_data *cd)
+{
+	int rc;
+
+	rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
+	if (rc < 0) {
+		pt_debug(cd->dev, DL_ERROR,
+				"%s: fail get exclusive ex=%p own=%p\n",
+				__func__, cd->exclusive_dev, cd->dev);
+		return rc;
+	}
+
+	rc = pt_pip_suspend_scanning_(cd);
+
+	if (release_exclusive(cd, cd->dev) < 0)
+		pt_debug(cd->dev, DL_ERROR,
+				"%s: fail to release exclusive\n", __func__);
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_request_pip_suspend_scanning
+ *
+ * SUMMARY: Function pointer included in core_nonhid_cmd struct for external
+ *	calls to the protected or unprotected call to pt_pip_suspend_scanning
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*dev - pointer to device structure
+ *	 protect - 0 = call non-protected function
+ *		   1 = call protected function
+ ******************************************************************************/
+static int _pt_request_pip_suspend_scanning(struct device *dev,
+		int protect)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+
+	if (protect)
+		return pt_pip_suspend_scanning(cd);
+
+	return pt_pip_suspend_scanning_(cd);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pip_resume_scanning_
+ *
+ * SUMMARY: Sends the PIP Resume Scanning command to the DUT
+ *
+ * RETURN::
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd - pointer to core data structure
+ ******************************************************************************/
+static int pt_pip_resume_scanning_(struct pt_core_data *cd)
+{
+	int rc = 0;
+	struct pt_hid_output hid_output = {
+		CREATE_PIP1_FW_CMD(PIP1_CMD_ID_RESUME_SCANNING),
+	};
+
+	rc = pt_pip1_send_output_and_wait_(cd, &hid_output);
+	if (rc) {
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: Resume Scan PIP cmd failed. rc = %d\n",
+			__func__, rc);
+	}
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pip_resume_scanning
+ *
+ * SUMMARY: Protected wrapper for calling pt_pip_resume_scanning_
+ *
+ * RETURN::
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd - pointer to core data structure
+ ******************************************************************************/
+static int pt_pip_resume_scanning(struct pt_core_data *cd)
+{
+	int rc;
+
+	rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
+	if (rc < 0) {
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: fail get exclusive ex=%p own=%p\n",
+			__func__, cd->exclusive_dev, cd->dev);
+		return rc;
+	}
+
+	rc = pt_pip_resume_scanning_(cd);
+
+	if (release_exclusive(cd, cd->dev) < 0)
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: fail to release exclusive\n", __func__);
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_request_pip_resume_scanning
+ *
+ * SUMMARY: Function pointer included in core_nonhid_cmd struct for external
+ *	calls to the protected or unprotected call to pt_pip_resume_scanning
+ *
+ * RETURN::
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*dev - pointer to device structure
+ *	 protect - 0 = call non-protected function
+ *		   1 = call protected function
+ ******************************************************************************/
+static int _pt_request_pip_resume_scanning(struct device *dev,
+		int protect)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+
+	if (protect)
+		return pt_pip_resume_scanning(cd);
+
+	return pt_pip_resume_scanning_(cd);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pip_get_param_
+ *
+ * SUMMARY: Sends a PIP command 0x05 Get Parameter to the DUT and returns
+ *	the 32bit parameter value
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd       - pointer to core data
+ *	 param_id - parameter ID to retrieve
+ *	*value    - value of DUT parameter
+ ******************************************************************************/
+static int pt_pip_get_param_(struct pt_core_data *cd,
+		u8 param_id, u32 *value)
+{
+	int write_length = 1;
+	u8 param[1] = { param_id };
+	u8 read_param_id;
+	int param_size;
+	u8 *ptr;
+	int rc = 0;
+	int i;
+	struct pt_hid_output hid_output = {
+		CREATE_PIP1_FW_CMD(PIP1_CMD_ID_GET_PARAM),
+		.write_length = write_length,
+		.write_buf = param,
+	};
+
+	rc = pt_pip1_send_output_and_wait_(cd, &hid_output);
+	if (rc)
+		return rc;
+
+	read_param_id = cd->response_buf[5];
+	if (read_param_id != param_id)
+		return -EPROTO;
+
+	param_size = cd->response_buf[6];
+	ptr = &cd->response_buf[7];
+	*value = 0;
+	for (i = 0; i < param_size; i++)
+		*value += ptr[i] << (i * 8);
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pip_get_param
+ *
+ * SUMMARY: Protected call to pt_hid_output_get_param_ by a request exclusive
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd       - pointer to core data
+ *	 param_id - parameter ID to retrieve
+ *	*value    - value of DUT parameter
+ ******************************************************************************/
+static int pt_pip_get_param(struct pt_core_data *cd,
+		u8 param_id, u32 *value)
+{
+	int rc;
+
+	rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
+	if (rc < 0) {
+		pt_debug(cd->dev, DL_ERROR,
+				"%s: fail get exclusive ex=%p own=%p\n",
+				__func__, cd->exclusive_dev, cd->dev);
+		return rc;
+	}
+
+	rc = pt_pip_get_param_(cd, param_id, value);
+
+	if (release_exclusive(cd, cd->dev) < 0)
+		pt_debug(cd->dev, DL_ERROR,
+				"%s: fail to release exclusive\n", __func__);
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_request_pip_get_param
+ *
+ * SUMMARY: Function pointer included in core_nonhid_cmd struct for external
+ *	calls to the protected or unprotected call to pt_pip_get_param
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*dev      - pointer to device structure
+ *	 protect  - flag to call protected or non protected function
+ *	 param_id - parameter ID to retrieve
+ *	*value    - value of DUT parameter
+ ******************************************************************************/
+int _pt_request_pip_get_param(struct device *dev,
+		int protect, u8 param_id, u32 *value)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+
+	if (protect)
+		return pt_pip_get_param(cd, param_id, value);
+
+	return pt_pip_get_param_(cd, param_id, value);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pip_set_param_
+ *
+ * SUMMARY: Sends a PIP command 0x06 Set Parameter to the DUT writing the
+ *	passed in value to flash
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd       - pointer to core data
+ *	 param_id - parameter ID to set
+ *	 value    - value to write
+ *	 size     - size to write
+ ******************************************************************************/
+static int pt_pip_set_param_(struct pt_core_data *cd,
+		u8 param_id, u32 value, u8 size)
+{
+	u8 write_buf[6];
+	u8 *ptr = &write_buf[2];
+	int rc = 0;
+	int i;
+	struct pt_hid_output hid_output = {
+		CREATE_PIP1_FW_CMD(PIP1_CMD_ID_SET_PARAM),
+		.write_buf = write_buf,
+	};
+	write_buf[0] = param_id;
+	write_buf[1] = size;
+	for (i = 0; i < size; i++) {
+		ptr[i] = value & 0xFF;
+		value = value >> 8;
+	}
+
+	hid_output.write_length = 2 + size;
+
+	rc = pt_pip1_send_output_and_wait_(cd, &hid_output);
+	if (rc)
+		return rc;
+
+	if (param_id != cd->response_buf[5] || size != cd->response_buf[6])
+		return -EPROTO;
+
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pip_set_param
+ *
+ * SUMMARY: Protected call to pt_hid_output_set_param_ by a request exclusive
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd       - pointer to core data
+ *	 param_id - parameter ID to set
+ *	 value    - value to write
+ *	 size     - size to write
+ ******************************************************************************/
+static int pt_pip_set_param(struct pt_core_data *cd,
+		u8 param_id, u32 value, u8 size)
+{
+	int rc;
+
+	rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
+	if (rc < 0) {
+		pt_debug(cd->dev, DL_ERROR,
+				"%s: fail get exclusive ex=%p own=%p\n",
+				__func__, cd->exclusive_dev, cd->dev);
+		return rc;
+	}
+
+	rc = pt_pip_set_param_(cd, param_id, value, size);
+
+	if (release_exclusive(cd, cd->dev) < 0)
+		pt_debug(cd->dev, DL_ERROR,
+				"%s: fail to release exclusive\n", __func__);
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_request_pip_set_param
+ *
+ * SUMMARY: Function pointer included in core_nonhid_cmd struct for external
+ *	calls to the protected or unprotected call to pt_pip_set_param
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*dev      - pointer to device structure
+ *	 protect  - flag to call protected or non-protected
+ *	 param_id - parameter ID to set
+ *	 value    - value to write
+ *	 size     - size to write
+ ******************************************************************************/
+int _pt_request_pip_set_param(struct device *dev, int protect,
+	u8 param_id, u32 value, u8 size)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+
+	if (protect)
+		return pt_pip_set_param(cd, param_id, value, size);
+
+	return pt_pip_set_param_(cd, param_id, value, size);
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_pip_enter_easywake_state_
+ *
+ * SUMMARY: Sends a PIP command 0x09 Enter EasyWake State to the DUT
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*dev         - pointer to device structure
+ *	 data        - easywake guesture (Only used for PIP1.6 and earlier)
+ *	*return_data - return status if easywake was entered
+ ******************************************************************************/
+static int pt_hid_output_enter_easywake_state_(
+		struct pt_core_data *cd, u8 data, u8 *return_data)
+{
+	int write_length = 1;
+	u8 param[1] = { data };
+	int rc = 0;
+	struct pt_hid_output hid_output = {
+		CREATE_PIP1_FW_CMD(PIP1_CMD_ID_ENTER_EASYWAKE_STATE),
+		.write_length = write_length,
+		.write_buf = param,
+	};
+	rc = pt_pip1_send_output_and_wait_(cd, &hid_output);
+	if (rc)
+		return rc;
+
+	*return_data = cd->response_buf[5];
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pip_verify_config_block_crc_
+ *
+ * SUMMARY: Sends the PIP "Verify Data Block CRC" (0x20) command to the DUT
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd             - pointer the core data structure
+ *	 ebid           - enumerated block ID
+ *	*status         - PIP command status
+ *	 calculated_crc - calculated CRC
+ *	 stored_crc     - stored CRC in config area
+ ******************************************************************************/
+static int pt_pip_verify_config_block_crc_(
+		struct pt_core_data *cd, u8 ebid, u8 *status,
+		u16 *calculated_crc, u16 *stored_crc)
+{
+	int write_length = 1;
+	u8 param[1] = { ebid };
+	u8 *ptr;
+	int rc = 0;
+	struct pt_hid_output hid_output = {
+		CREATE_PIP1_FW_CMD(PIP1_CMD_ID_VERIFY_CONFIG_BLOCK_CRC),
+		.write_length = write_length,
+		.write_buf = param,
+	};
+	rc = pt_pip1_send_output_and_wait_(cd, &hid_output);
+	if (rc)
+		return rc;
+
+	ptr = &cd->response_buf[5];
+	*status = ptr[0];
+	*calculated_crc = get_unaligned_le16(&ptr[1]);
+	*stored_crc = get_unaligned_le16(&ptr[3]);
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pip_verify_config_block_crc
+ *
+ * SUMMARY: Protected call to pt_hid_output_verify_config_block_crc_() within
+ *	an exclusive access to the DUT.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd             - pointer the core data structure
+ *	 ebid           - enumerated block ID
+ *	*status         - PIP command status
+ *	 calculated_crc - calculated CRC
+ *	 stored_crc     - stored CRC in config area
+ ******************************************************************************/
+static int pt_pip_verify_config_block_crc(
+		struct pt_core_data *cd, u8 ebid, u8 *status,
+		u16 *calculated_crc, u16 *stored_crc)
+{
+	int rc;
+
+	rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
+	if (rc < 0) {
+		pt_debug(cd->dev, DL_ERROR,
+				"%s: fail get exclusive ex=%p own=%p\n",
+				__func__, cd->exclusive_dev, cd->dev);
+		return rc;
+	}
+
+	rc = pt_pip_verify_config_block_crc_(cd, ebid, status,
+			calculated_crc, stored_crc);
+
+	if (release_exclusive(cd, cd->dev) < 0)
+		pt_debug(cd->dev, DL_ERROR,
+				"%s: fail to release exclusive\n", __func__);
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_request_pip_verify_config_block_crc
+ *
+ * SUMMARY: Function pointer included in core_nonhid_cmd struct for external
+ *	calls to the protected or unprotected call to
+ *	pt_pip_verify_config_block_crc_
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*dev            - pointer to device structure
+ *	 protect        - flag to call protected or non-protected
+ *	 ebid           - enumerated block ID
+ *	*status         - PIP command status
+ *	 calculated_crc - calculated CRC
+ *	 stored_crc     - stored CRC in config area
+ ******************************************************************************/
+static int _pt_request_pip_verify_config_block_crc(
+		struct device *dev, int protect, u8 ebid, u8 *status,
+		u16 *calculated_crc, u16 *stored_crc)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+
+	if (protect)
+		return pt_pip_verify_config_block_crc(cd, ebid,
+				status, calculated_crc, stored_crc);
+
+	return pt_pip_verify_config_block_crc_(cd, ebid,
+			status, calculated_crc, stored_crc);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pip_get_config_row_size_
+ *
+ * SUMMARY: Sends the PIP "Get Data Row Size" (0x21) command to the DUT
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd       - pointer to core data
+ *	 protect  - flag to call protected or non-protected
+ *	*row_size - pointer to store the retrieved row size
+ ******************************************************************************/
+static int pt_pip_get_config_row_size_(struct pt_core_data *cd,
+		u16 *row_size)
+{
+	int rc = 0;
+	struct pt_hid_output hid_output = {
+		CREATE_PIP1_FW_CMD(PIP1_CMD_ID_GET_CONFIG_ROW_SIZE),
+	};
+
+	rc = pt_pip1_send_output_and_wait_(cd, &hid_output);
+	if (rc)
+		return rc;
+
+	*row_size = get_unaligned_le16(&cd->response_buf[5]);
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pip_get_config_row_size
+ *
+ * SUMMARY: Protected call to pt_hid_output_get_config_row_size_ within
+ *	an exclusive access to the DUT.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd       - pointer to core data
+ *	 protect  - flag to call protected or non-protected
+ *	*row_size - pointer to store the retrieved row size
+ ******************************************************************************/
+static int pt_pip_get_config_row_size(struct pt_core_data *cd,
+		u16 *row_size)
+{
+	int rc;
+
+	rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
+	if (rc < 0) {
+		pt_debug(cd->dev, DL_ERROR,
+				"%s: fail get exclusive ex=%p own=%p\n",
+				__func__, cd->exclusive_dev, cd->dev);
+		return rc;
+	}
+
+	rc = pt_pip_get_config_row_size_(cd, row_size);
+
+	if (release_exclusive(cd, cd->dev) < 0)
+		pt_debug(cd->dev, DL_ERROR,
+				"%s: fail to release exclusive\n", __func__);
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_request_pip_get_config_row_size
+ *
+ * SUMMARY: Function pointer included in core_nonhid_cmd struct for external
+ *	calls to the protected or unprotected call to
+ *	pt_pip_get_config_row_size_
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*dev      - pointer to device structure
+ *	 protect  - flag to call protected or non-protected
+ *	*row_size - pointer to store the retrieved row size
+ ******************************************************************************/
+static int _pt_request_pip_get_config_row_size(struct device *dev,
+		int protect, u16 *row_size)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+
+	if (protect)
+		return pt_pip_get_config_row_size(cd, row_size);
+
+	return pt_pip_get_config_row_size_(cd, row_size);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pip1_read_data_block_
+ *
+ * SUMMARY: Sends the PIP "Read Data Block" (0x22) command to the DUT and print
+ *  output data to the "read_buf" and update "crc".
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd              - pointer to core data
+ *	 row_number      - row number
+ *	 length          - length of data to read
+ *	 ebid            - block id
+ *	*actual_read_len - Actual data length read
+ *	*read_buf        - pointer to the buffer to store read data
+ *	 read_buf_size   - size of read_buf
+ *	*crc             - pointer to store CRC of row data
+ ******************************************************************************/
+static int pt_pip1_read_data_block_(struct pt_core_data *cd,
+		u16 row_number, u16 length, u8 ebid, u16 *actual_read_len,
+		u8 *read_buf, u16 read_buf_size, u16 *crc)
+{
+	int read_ebid;
+	int status;
+	int rc = 0;
+	int write_length = 5;
+	u8 write_buf[5];
+	u8 cmd_offset = 0;
+	u16 calc_crc;
+	struct pt_hid_output hid_output = {
+		CREATE_PIP1_FW_CMD(PIP1_CMD_ID_READ_DATA_BLOCK),
+		.write_length = write_length,
+		.write_buf = write_buf,
+	};
+
+	write_buf[cmd_offset++] = LOW_BYTE(row_number);
+	write_buf[cmd_offset++] = HI_BYTE(row_number);
+	write_buf[cmd_offset++] = LOW_BYTE(length);
+	write_buf[cmd_offset++] = HI_BYTE(length);
+	write_buf[cmd_offset++] = ebid;
+
+	rc = pt_pip1_send_output_and_wait_(cd, &hid_output);
+	if (rc)
+		return rc;
+
+	status = cd->response_buf[5];
+	if (status)
+		return status;
+
+	read_ebid = cd->response_buf[6];
+	if ((read_ebid != ebid) || (cd->response_buf[9] != 0))
+		return -EPROTO;
+
+	*actual_read_len = get_unaligned_le16(&cd->response_buf[7]);
+	if (length == 0 || *actual_read_len == 0)
+		return 0;
+
+	if (read_buf_size >= *actual_read_len &&
+		*actual_read_len < PT_MAX_PIP2_MSG_SIZE)
+		memcpy(read_buf, &cd->response_buf[10], *actual_read_len);
+	else
+		return -EPROTO;
+
+	*crc = get_unaligned_le16(&cd->response_buf[*actual_read_len + 10]);
+
+	/* Validate Row Data CRC */
+	calc_crc = _pt_compute_crc(read_buf, *actual_read_len);
+	if (*crc == calc_crc) {
+		return 0;
+	} else {
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: CRC Mismatch packet=0x%04X calc=0x%04X\n",
+			__func__, *crc, calc_crc);
+		return -EPROTO;
+	}
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_request_pip_read_data_block
+ *
+ * SUMMARY: Function pointer included in core_nonhid_cmd struct for external
+ *	calls to pt_pip1_read_data_block_
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*dev             - pointer to device structure
+ *	 row_number      - row number
+ *	 length          - length of data to read
+ *	 ebid            - block id
+ *	*actual_read_len - Actual data length read
+ *	*read_buf        - pointer to the buffer to store read data
+ *	*crc             - pointer to store CRC of row data
+ ******************************************************************************/
+static int _pt_request_pip_read_data_block(struct device *dev,
+		u16 row_number, u16 length, u8 ebid, u16 *actual_read_len,
+		u8 *read_buf, u16 read_buf_size, u16 *crc)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+
+	return pt_pip1_read_data_block_(cd, row_number, length,
+			ebid, actual_read_len, read_buf, read_buf_size, crc);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pip1_write_data_block_
+ *
+ * SUMMARY: Sends the PIP "Write Data Block" (0x23) command to the DUT and
+ *  write data to the data block.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *cd               - pointer to core data
+ *   row_number       - row in config block to write to
+ *   write_length     - length of data to write
+ *   ebid             - enumerated block ID
+ *  *write_buf        - pointer to buffer to write
+ *  *security_key     - pointer to security key to allow write
+ *  *actual_write_len - pointer to store data length actually written
+ ******************************************************************************/
+static int pt_pip1_write_data_block_(struct pt_core_data *cd,
+		u16 row_number, u16 write_length, u8 ebid, u8 *write_buf,
+		u8 *security_key, u16 *actual_write_len)
+{
+	/* row_number + write_len + ebid + security_key + crc */
+	u16 full_write_length = 2 + 2 + 1 + write_length + 8 + 2;
+	u8 *full_write_buf;
+	u8 cmd_offset = 0;
+	u16 crc;
+	int status;
+	int rc = 0;
+	int read_ebid;
+	u8 *data;
+	struct pt_hid_output hid_output = {
+		CREATE_PIP1_FW_CMD(PIP1_CMD_ID_WRITE_DATA_BLOCK),
+		.write_length = full_write_length,
+		.timeout_ms = PT_PIP1_CMD_WRITE_CONF_BLOCK_TIMEOUT,
+	};
+
+	if (write_length > PT_CAL_DATA_ROW_SIZE)
+		return -EINVAL;
+
+	full_write_buf = kzalloc(full_write_length, GFP_KERNEL);
+	if (!full_write_buf)
+		return -ENOMEM;
+
+	hid_output.write_buf = full_write_buf;
+	full_write_buf[cmd_offset++] = LOW_BYTE(row_number);
+	full_write_buf[cmd_offset++] = HI_BYTE(row_number);
+	full_write_buf[cmd_offset++] = LOW_BYTE(write_length);
+	full_write_buf[cmd_offset++] = HI_BYTE(write_length);
+	full_write_buf[cmd_offset++] = ebid;
+	data = &full_write_buf[cmd_offset];
+	memcpy(data, write_buf, write_length);
+	cmd_offset += write_length;
+	memcpy(&full_write_buf[cmd_offset], security_key, 8);
+	cmd_offset += 8;
+	crc = _pt_compute_crc(data, write_length);
+	full_write_buf[cmd_offset++] = LOW_BYTE(crc);
+	full_write_buf[cmd_offset++] = HI_BYTE(crc);
+
+	rc = pt_pip1_send_output_and_wait_(cd, &hid_output);
+	if (rc)
+		goto exit;
+
+	status = cd->response_buf[5];
+	if (status) {
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	read_ebid = cd->response_buf[6];
+	if (read_ebid != ebid) {
+		rc = -EPROTO;
+		goto exit;
+	}
+
+	*actual_write_len = get_unaligned_le16(&cd->response_buf[7]);
+
+exit:
+	kfree(full_write_buf);
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_request_pip_write_data_block
+ *
+ * SUMMARY: Function pointer included in core_nonhid_cmd struct for external
+ *	calls to the protected or unprotected call to pt_pip1_write_data_block_
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*dev              - pointer to device structure
+ *	 protect          - flag to call protected or non-protected
+ *	 row_number       - row in config block to write to
+ *	 write_length     - length of data to write
+ *	 ebid             - enumerated block ID
+ *	*write_buf        - pointer to buffer to write
+ *	*security_key     - pointer to security key to allow write
+ *	*actual_write_len - pointer to store data length actually written
+ ******************************************************************************/
+static int _pt_request_pip_write_data_block(struct device *dev,
+		u16 row_number, u16 write_length, u8 ebid,
+		u8 *write_buf, u8 *security_key, u16 *actual_write_len)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+
+	return pt_pip1_write_data_block_(cd, row_number,
+			write_length, ebid, write_buf, security_key,
+			actual_write_len);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pip_get_data_structure_
+ *
+ * SUMMARY: Sends the PIP "Retrieve Data Structure" (0x24) command to the DUT
+ *	returning a structure of data defined by data_id
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd              - pointer to core data
+ *	 read_offset     - read pointer offset
+ *	 read_length     - length of data to read
+ *	 data_id         - data ID to read
+ *	*status          - pointer to store the read response status
+ *	*data_format     - pointer to store format of data read
+ *	*actual_read_len - pointer to store data length actually read
+ *	*data            - pointer to store data read
+ ******************************************************************************/
+static int pt_pip_get_data_structure_(
+		struct pt_core_data *cd, u16 read_offset, u16 read_length,
+		u8 data_id, u8 *status, u8 *data_format, u16 *actual_read_len,
+		u8 *data)
+{
+	int rc = 0;
+	u16 total_read_len = 0;
+	u16 read_len;
+	u16 off_buf = 0;
+	u8 write_buf[5];
+	u8 read_data_id;
+	struct pt_hid_output hid_output = {
+		CREATE_PIP1_FW_CMD(PIP1_CMD_ID_GET_DATA_STRUCTURE),
+		.write_length = 5,
+		.write_buf = write_buf,
+	};
+
+again:
+	write_buf[0] = LOW_BYTE(read_offset);
+	write_buf[1] = HI_BYTE(read_offset);
+	write_buf[2] = LOW_BYTE(read_length);
+	write_buf[3] = HI_BYTE(read_length);
+	write_buf[4] = data_id;
+
+	rc = pt_pip1_send_output_and_wait_(cd, &hid_output);
+	if (rc)
+		return rc;
+
+	if (cd->response_buf[5] != PT_CMD_STATUS_SUCCESS)
+		goto set_status;
+
+	read_data_id = cd->response_buf[6];
+	if (read_data_id != data_id)
+		return -EPROTO;
+
+	read_len = get_unaligned_le16(&cd->response_buf[7]);
+	if (read_len && data) {
+		memcpy(&data[off_buf], &cd->response_buf[10], read_len);
+
+		total_read_len += read_len;
+
+		if (read_len < read_length) {
+			read_offset += read_len;
+			off_buf += read_len;
+			read_length -= read_len;
+			goto again;
+		}
+	}
+
+	if (data_format)
+		*data_format = cd->response_buf[9];
+	if (actual_read_len)
+		*actual_read_len = total_read_len;
+set_status:
+	if (status)
+		*status = cd->response_buf[5];
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pip_get_data_structure
+ *
+ * SUMMARY: Protected call to pt_hid_output_get_data_structure within
+ *	an exclusive access to the DUT.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd              - pointer to core data
+ *	 read_offset     - read pointer offset
+ *	 read_length     - length of data to read
+ *	 data_id         - data ID to read
+ *	*status          - pointer to store the read response status
+ *	*data_format     - pointer to store format of data read
+ *	*actual_read_len - pointer to store data length actually read
+ *	*data            - pointer to store data read
+ ******************************************************************************/
+static int pt_pip_get_data_structure(
+		struct pt_core_data *cd, u16 read_offset, u16 read_length,
+		u8 data_id, u8 *status, u8 *data_format, u16 *actual_read_len,
+		u8 *data)
+{
+	int rc;
+
+	rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
+	if (rc < 0) {
+		pt_debug(cd->dev, DL_ERROR,
+				"%s: fail get exclusive ex=%p own=%p\n",
+				__func__, cd->exclusive_dev, cd->dev);
+		return rc;
+	}
+
+	rc = pt_pip_get_data_structure_(cd, read_offset,
+			read_length, data_id, status, data_format,
+			actual_read_len, data);
+
+	if (release_exclusive(cd, cd->dev) < 0)
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: fail to release exclusive\n", __func__);
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_request_pip_get_data_structure
+ *
+ * SUMMARY: Function pointer included in core_nonhid_cmd struct for external
+ *	calls to the protected or unprotected call to
+ *	pt_pip_get_data_structure
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*dev             - pointer to device structure
+ *	 protect         - flag to call protected or non-protected
+ *	 read_offset     - read pointer offset
+ *	 read_length     - length of data to read
+ *	 data_id         - data ID to read
+ *	*status          - pointer to store the read response status
+ *	*data_format     - pointer to store format of data read
+ *	*actual_read_len - pointer to store data length actually read
+ *	*data            - pointer to store data read
+ ******************************************************************************/
+static int _pt_request_pip_get_data_structure(struct device *dev,
+		int protect, u16 read_offset, u16 read_length, u8 data_id,
+		u8 *status, u8 *data_format, u16 *actual_read_len, u8 *data)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+
+	if (protect)
+		return pt_pip_get_data_structure(cd,
+				read_offset, read_length, data_id, status,
+				data_format, actual_read_len, data);
+
+	return pt_pip_get_data_structure_(cd,
+			read_offset, read_length, data_id, status,
+			data_format, actual_read_len, data);
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_manage_local_cal_data
+ *
+ * SUMMARY: This function manages storing or restoring a copy of the Firmware
+ *	CALIBRATION data. It stores it in a local static array and can be
+ *	cleared, loaded or used to restore the CAL data back to the running FW.
+ *	The CAL data is read or restored by use of the PIP1 commands:
+ *	   - READ_DATA_BLOCK  (0x22)
+ *	   - WRITE_DATA_BLOCK (0x23)
+ *
+ * RETURN:
+ *       0 = success
+ *      !0 = failure
+ *
+ * PARAMETERS:
+ *      *dev    - pointer to device structure
+ *       action - One of the following actions:
+ *                  - PT_CAL_DATA_SAVE
+ *                  - PT_CAL_DATA_RESTORE
+ *                  - PT_CAL_DATA_CLEAR
+ *                  - PT_CAL_DATA_SIZE
+ *      *size   - pointer to the number of bytes transferred
+ *	*crc    - pointer to Chip ID CRC that the CAL data was retrieved from
+ ******************************************************************************/
+static int _pt_manage_local_cal_data(struct device *dev, u8 action, u16 *size,
+	unsigned short *crc)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_ttdata *ttdata = &cd->sysinfo.ttdata;
+	unsigned short calc_id_crc = 0;
+	static u8 *cal_cache_data;
+	static u16 cal_cache_len;
+	static unsigned short cal_cache_chip_id;
+	int rc = 0;
+	u8 *tmp_data = NULL;
+	u8 row_number = 0;
+	u8 prefix[20];
+	u16 cal_size = 0;
+	u16 transfer_size;
+	u16 act_trans_len = 0;
+	u16 byte_offset = 0;
+	u16 cal_blk_size;
+	u16 total_rows;
+	u16 remain_bytes;
+	u16 data_block_crc;
+	u16 buf_size = 12;
+
+	pt_debug(dev, DL_INFO, "%s: ATM - CAL Cache action=%d\n",
+		__func__, action);
+	switch (action) {
+	case PT_CAL_DATA_SAVE:
+		/* Read the size of the CAL block and calculate # rows */
+		tmp_data = kzalloc(buf_size, GFP_KERNEL);
+		if (!tmp_data) {
+			rc = -ENOMEM;
+			goto exit;
+		}
+		/*
+		 * Don't check rc as doing a read size will give a false
+		 * error on the CRC check.
+		 */
+		rc = pt_pip1_read_data_block_(cd, row_number, 0, PT_CAL_EBID,
+			&act_trans_len, tmp_data, buf_size, &data_block_crc);
+		cal_blk_size = act_trans_len;
+		kfree(tmp_data);
+
+		pt_debug(dev, DL_INFO,
+			"%s: CAL Cache size=%d FW CAL Size=%d\n",
+			__func__, cal_cache_len, cal_blk_size);
+
+		/* Safety net to ensure we didn't read incorrect size */
+		if (cal_blk_size > PT_CAL_DATA_MAX_SIZE) {
+			pt_debug(dev, DL_ERROR, "%s: Alloc struct Failed\n",
+				__func__);
+			rc = 1;
+			goto exit;
+		}
+
+		/* Panels could have diff CAL sizes, Re-allocate the cache */
+		if (cal_blk_size != cal_cache_len) {
+			kfree(cal_cache_data);
+			cal_cache_data = kzalloc(cal_blk_size + 2,
+						GFP_KERNEL);
+			if (!cal_cache_data) {
+				rc = -ENOMEM;
+				goto exit;
+			}
+
+			pt_debug(dev, DL_INFO, "%s: CAL Cache Allocated\n",
+				__func__);
+		}
+		memset(&cal_cache_data[0], 0, cal_blk_size + 2);
+
+		/* Calculate how many rows [0-n] (PIP Transactions) */
+		total_rows = (cal_blk_size / PT_CAL_DATA_ROW_SIZE) - 1;
+		remain_bytes = cal_blk_size % PT_CAL_DATA_ROW_SIZE;
+		/* Add row if we have a last partial row */
+		if (remain_bytes > 0)
+			total_rows++;
+		pt_debug(dev, DL_INFO,
+			"%s: CAL size=%d rows=[0-%d] partial row bytes=%d\n",
+			__func__, cal_blk_size, total_rows, remain_bytes);
+
+		/* Read all rows unless an error occurs */
+		rc = 0;
+		while (rc == 0 && row_number <= total_rows) {
+			act_trans_len = 0;
+			if (remain_bytes > 0 && row_number == total_rows)
+				transfer_size = remain_bytes;
+			else
+				transfer_size = PT_CAL_DATA_ROW_SIZE;
+
+			rc = pt_pip1_read_data_block_(cd, row_number,
+				transfer_size, PT_CAL_EBID,
+				&act_trans_len,
+				&cal_cache_data[byte_offset], cal_blk_size + 2,
+				&data_block_crc);
+			if (rc) {
+				/* Error occurred, exit loop */
+				cal_size = 0;
+				break;
+			}
+
+			pt_debug(dev, DL_INFO,
+				"%s: CAL read rc=%d actual read len=%d\n",
+				__func__, rc, act_trans_len);
+
+			byte_offset += act_trans_len;
+			cal_size = byte_offset;
+
+			scnprintf(prefix, sizeof(prefix), "%s[%d]", "CAL DATA ROW", row_number);
+			pt_pr_buf(dev, DL_INFO,
+				&cal_cache_data[byte_offset - act_trans_len],
+				act_trans_len, prefix);
+
+			row_number++;
+		}
+
+		if (cal_size > 0) {
+			/* Save a CRC of the chip info the CAL was saved from */
+			calc_id_crc = crc_ccitt_calculate(
+				(u8 *)&ttdata->chip_rev, 4 + PT_UID_SIZE);
+			cal_cache_chip_id = calc_id_crc;
+			cal_cache_len = cal_size;
+			pt_debug(dev, DL_INFO,
+				"%s: CAL Cache: CRC=0x%04X Total Size=%d\n",
+				__func__, calc_id_crc, cal_size);
+		}
+
+		*size = cal_size;
+		*crc = calc_id_crc;
+		break;
+	case PT_CAL_DATA_RESTORE:
+		cal_size = cal_cache_len;
+		while ((rc == 0) && (byte_offset < cal_size)) {
+			if (cal_size - byte_offset > PT_CAL_DATA_ROW_SIZE)
+				transfer_size = PT_CAL_DATA_ROW_SIZE;
+			else
+				transfer_size = cal_size - byte_offset;
+
+			rc = pt_pip1_write_data_block_(cd, row_number,
+				transfer_size, PT_CAL_EBID,
+				&cal_cache_data[byte_offset],
+				(u8 *)pt_data_block_security_key,
+				&act_trans_len);
+
+			byte_offset += act_trans_len;
+			pt_debug(dev, DL_INFO, "%s: CAL write byte offset=%d\n",
+				__func__, byte_offset);
+
+			scnprintf(prefix, sizeof(prefix), "%s[%d]", "CAL DATA ROW", row_number);
+			pt_pr_buf(dev, DL_INFO,
+				&cal_cache_data[byte_offset - act_trans_len],
+				act_trans_len, prefix);
+
+
+			if ((byte_offset > cal_size) ||
+			    (act_trans_len != transfer_size))
+				rc = -EIO;
+			row_number++;
+		}
+		*size = byte_offset;
+		*crc = cal_cache_chip_id;
+		break;
+	case PT_CAL_DATA_CLEAR:
+		if (cal_cache_data)
+			memset(&cal_cache_data[0], 0, cal_cache_len);
+		cal_cache_len = 0;
+		cal_cache_chip_id = 0;
+		*size = 0;
+		*crc = 0;
+		break;
+	case PT_CAL_DATA_INFO:
+	default:
+		*size = cal_cache_len;
+		*crc = cal_cache_chip_id;
+		pt_debug(dev, DL_INFO,
+			"%s: CAL Cache: CRC=%04X Total Size=%d\n",
+			__func__, cal_cache_chip_id,
+			cal_cache_len);
+		break;
+	}
+
+exit:
+	pt_debug(dev, DL_INFO,
+		"%s: CAL Cache exit: rc=%d CRC=0x%04X Total Size=%d\n",
+		__func__, rc, *crc, *size);
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pip_run_selftest_
+ *
+ * SUMMARY: Sends the PIP "Run Self Test" (0x26) command to the DUT
+ *	to execute a FW built in self test
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd                   - pointer to core data
+ *	 test_id              - enumerated test ID to run
+ *	 write_idacs_to_flash - flag whether to write new IDACS to flash
+ *	*status               - pointer to store the read response status
+ *	*summary_results      - pointer to store the results summary
+ *	*results_available    - pointer to store if results are available
+ *****************************************************************************/
+static int pt_pip_run_selftest_(
+		struct pt_core_data *cd, u8 test_id,
+		u8 write_idacs_to_flash, u8 *status, u8 *summary_result,
+		u8 *results_available)
+{
+	int rc = 0;
+	u8 write_buf[2];
+	struct pt_hid_output hid_output = {
+		CREATE_PIP1_FW_CMD(PIP1_CMD_ID_RUN_SELF_TEST),
+		.write_length = 2,
+		.write_buf = write_buf,
+		.timeout_ms = PT_PIP1_CMD_RUN_SELF_TEST_TIMEOUT,
+	};
+
+	write_buf[0] = test_id;
+	write_buf[1] = write_idacs_to_flash;
+
+	if (cd->active_dut_generation == DUT_PIP2_CAPABLE)
+		hid_output.write_length = 1;
+
+	rc = pt_pip1_send_output_and_wait_(cd, &hid_output);
+	if (rc)
+		return rc;
+
+	if (status)
+		*status = cd->response_buf[5];
+	if (summary_result)
+		*summary_result = cd->response_buf[6];
+	/* results_available only available before PIP 1.03 */
+	if (cd->sysinfo.ready && !IS_PIP_VER_GE(&cd->sysinfo, 1, 3)) {
+		if (results_available)
+			*results_available = cd->response_buf[7];
+	}
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pip_run_selftest
+ *
+ * SUMMARY: Protected call to pt_hid_output_run_selftest within
+ *	an exclusive access to the DUT.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd                   - pointer to core data
+ *	 test_id              - enumerated test ID to run
+ *	 write_idacs_to_flash - flag whether to write new IDACS to flash
+ *	*status               - pointer to store the read response status
+ *	*summary_results      - pointer to store the results summary
+ *	*results_available    - pointer to store if results are available
+ ******************************************************************************/
+static int pt_pip_run_selftest(
+		struct pt_core_data *cd, u8 test_id,
+		u8 write_idacs_to_flash, u8 *status, u8 *summary_result,
+		u8 *results_available)
+{
+	int rc;
+
+	rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
+	if (rc < 0) {
+		pt_debug(cd->dev, DL_ERROR,
+				"%s: fail get exclusive ex=%p own=%p\n",
+				__func__, cd->exclusive_dev, cd->dev);
+		return rc;
+	}
+
+	rc = pt_pip_run_selftest_(cd, test_id,
+			write_idacs_to_flash, status, summary_result,
+			results_available);
+
+	if (release_exclusive(cd, cd->dev) < 0)
+		pt_debug(cd->dev, DL_ERROR,
+				"%s: fail to release exclusive\n", __func__);
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_request_pip_run_selftest
+ *
+ * SUMMARY: Function pointer included in core_nonhid_cmd struct for external
+ *	calls to the protected or unprotected call to pt_pip_run_selftest
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*dev                  - pointer to device structure
+ *	 protect              - flag to call protected or non-protected
+ *	 test_id              - enumerated test ID to run
+ *	 write_idacs_to_flash - flag whether to write new IDACS to flash
+ *	*status               - pointer to store the read response status
+ *	*summary_results      - pointer to store the results summary
+ *	*results_available    - pointer to store if results are available
+ ******************************************************************************/
+static int _pt_request_pip_run_selftest(struct device *dev,
+		int protect, u8 test_id, u8 write_idacs_to_flash, u8 *status,
+		u8 *summary_result, u8 *results_available)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+
+	if (protect)
+		return pt_pip_run_selftest(cd, test_id,
+				write_idacs_to_flash, status, summary_result,
+				results_available);
+
+	return pt_pip_run_selftest_(cd, test_id,
+			write_idacs_to_flash, status, summary_result,
+			results_available);
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_pip_get_selftest_result_
+ *
+ * SUMMARY: Sends the PIP "Get Self Test Results" (0x27) command to the DUT
+ *	to retrieve the self test results from the self test already executed
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd              - pointer to core data
+ *	 read_offset     - read pointer offset
+ *	 read_length     - length of data to read
+ *	 test_id         - enumerated test ID to read selftest results from
+ *	*status          - pointer to store the read response status
+ *	*actual_read_len - pointer to store data length actually read
+ *	*status          - pointer to where the cmd response statas is stored
+ ******************************************************************************/
+static int pt_pip_get_selftest_result_(
+		struct pt_core_data *cd, u16 read_offset, u16 read_length,
+		u8 test_id, u8 *status, u16 *actual_read_len, u8 *data)
+{
+	int rc = 0;
+	u16 total_read_len = 0;
+	u16 read_len;
+	u16 off_buf = 0;
+	u8 write_buf[5];
+	u8 read_test_id;
+	bool repeat;
+	struct pt_hid_output hid_output = {
+		CREATE_PIP1_FW_CMD(PIP1_CMD_ID_GET_SELF_TEST_RESULT),
+		.write_length = 5,
+		.write_buf = write_buf,
+	};
+
+	/*
+	 * Do not repeat reading for Auto Shorts test
+	 * when PIP version < 1.3
+	 */
+	repeat = IS_PIP_VER_GE(&cd->sysinfo, 1, 3)
+			|| test_id != PT_ST_ID_AUTOSHORTS;
+
+again:
+	write_buf[0] = LOW_BYTE(read_offset);
+	write_buf[1] = HI_BYTE(read_offset);
+	write_buf[2] = LOW_BYTE(read_length);
+	write_buf[3] = HI_BYTE(read_length);
+	write_buf[4] = test_id;
+
+	rc = pt_pip1_send_output_and_wait_(cd, &hid_output);
+	if (rc)
+		return rc;
+
+	if (cd->response_buf[5] != PT_CMD_STATUS_SUCCESS)
+		goto set_status;
+
+	read_test_id = cd->response_buf[6];
+	if (read_test_id != test_id)
+		return -EPROTO;
+
+	read_len = get_unaligned_le16(&cd->response_buf[7]);
+	if (read_len && data) {
+		memcpy(&data[off_buf], &cd->response_buf[10], read_len);
+
+		total_read_len += read_len;
+
+		if (repeat && read_len < read_length) {
+			read_offset += read_len;
+			off_buf += read_len;
+			read_length -= read_len;
+			goto again;
+		}
+	}
+
+	if (actual_read_len)
+		*actual_read_len = total_read_len;
+set_status:
+	if (status)
+		*status = cd->response_buf[5];
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_pip_get_selftest_result
+ *
+ * SUMMARY: Protected call to pt_hid_output_get_selftest_result by exclusive
+ *	access to the DUT
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd              - pointer to core data
+ *	 read_offset     - read pointer offset
+ *	 read_length     - length of data to read
+ *	 test_id         - enumerated test ID to read selftest results from
+ *	*status          - pointer to store the read response status
+ *	*actual_read_len - pointer to store data length actually read
+ *	*status          - pointer to where the cmd response statas is stored
+ ******************************************************************************/
+static int pt_pip_get_selftest_result(
+		struct pt_core_data *cd, u16 read_offset, u16 read_length,
+		u8 test_id, u8 *status, u16 *actual_read_len, u8 *data)
+{
+	int rc;
+
+	rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
+	if (rc < 0) {
+		pt_debug(cd->dev, DL_ERROR,
+				"%s: fail get exclusive ex=%p own=%p\n",
+				__func__, cd->exclusive_dev, cd->dev);
+		return rc;
+	}
+
+	rc = pt_pip_get_selftest_result_(cd, read_offset,
+		read_length, test_id, status, actual_read_len, data);
+
+	if (release_exclusive(cd, cd->dev) < 0)
+		pt_debug(cd->dev, DL_ERROR,
+				"%s: fail to release exclusive\n", __func__);
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_request_pip_get_selftest_result
+ *
+ * SUMMARY: Function pointer included in core_nonhid_cmd struct for external
+ *	calls to the protected or unprotected call to pt_pip_get_selftest_result
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*dev             - pointer to device structure
+ *	 protect         - flag to call protected or non-protected
+ *	 read_offset     - read pointer offset
+ *	 read_length     - length of data to read
+ *	 test_id         - enumerated test ID to read selftest results from
+ *	*status          - pointer to store the read response status
+ *	*actual_read_len - pointer to store data length actually read
+ *	*data            - pointer to where the data read is stored
+ ******************************************************************************/
+static int _pt_request_pip_get_selftest_result(struct device *dev,
+		int protect, u16 read_offset, u16 read_length, u8 test_id,
+		u8 *status, u16 *actual_read_len, u8 *data)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+
+	if (protect)
+		return pt_pip_get_selftest_result(cd, read_offset,
+				read_length, test_id, status, actual_read_len,
+				data);
+
+	return pt_pip_get_selftest_result_(cd, read_offset,
+			read_length, test_id, status, actual_read_len,
+			data);
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_pip_load_self_test_param
+ *
+ * SUMMARY: Sends the PIP "Load Self Test Parameters" (0x25) command to the DUT
+ *	to load parameters needed by a self test
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd           - pointer to core data
+ *	 self_test_id - enumerated test ID for which the parmeters belong
+ *	 load_offset  - mem offset to where to load parameters
+ *	 load_length  - length of parameter data to load
+ *	*parameters   - pointer to list of parameter data
+ *	*status       - pointer to store the response status
+ *	*ret_test_id  - pointer to returned test id the parameters were stored
+ *	*act_load_len - pointer to store the actual load length that was writen
+ ******************************************************************************/
+static int pt_pip_load_self_test_param_(struct pt_core_data *cd,
+		u8 self_test_id, u16 load_offset, u16 load_length,
+		u8 *parameters, u8 *status, u8 *ret_test_id, u16 *act_load_len)
+{
+	int rc = 0;
+	int i;
+	u8 write_buf[PT_MAX_PIP1_MSG_SIZE];
+	struct pt_hid_output hid_output = {
+		CREATE_PIP1_FW_CMD(PIP1_CMD_ID_LOAD_SELF_TEST_PARAM),
+		.write_length = 5 + load_length,
+		.write_buf = write_buf,
+		.timeout_ms = PT_PIP1_CMD_DEFAULT_TIMEOUT,
+	};
+
+	write_buf[0] = LOW_BYTE(load_offset);
+	write_buf[1] = HI_BYTE(load_offset);
+	write_buf[2] = LOW_BYTE(load_length);
+	write_buf[3] = HI_BYTE(load_length);
+	write_buf[4] = self_test_id;
+	for (i = 0; i < load_length; i++)
+		write_buf[i + 5] = parameters[i];
+
+	rc = pt_pip1_send_output_and_wait_(cd, &hid_output);
+	if (rc)
+		return rc;
+
+	if (status)
+		*status = cd->response_buf[5];
+	if (ret_test_id)
+		*ret_test_id = cd->response_buf[6];
+	if (act_load_len)
+		*act_load_len = get_unaligned_le16(&cd->response_buf[7]);
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pip_load_self_test_param
+ *
+ * SUMMARY: Protected call to pt_pip_load_self_test_param_ within an exclusive
+ *	access to the DUT.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd           - pointer to core data
+ *	 self_test_id - enumerated test ID for which the parmeters belong
+ *	 load_offset  - mem offset to where to load parameters
+ *	 load_length  - length of parameter data to load
+ *	*parameters   - pointer to list of parameter data
+ *	*status       - pointer to store the response status
+ *	*ret_test_id  - pointer to returned test id the parameters were stored
+ *	*act_load_len - pointer to store the actual load length that was writen
+ ******************************************************************************/
+static int pt_pip_load_self_test_param(struct pt_core_data *cd,
+		u8 self_test_id, u16 load_offset, u16 load_length,
+		u8 *parameters, u8 *status, u8 *ret_test_id, u16 *act_load_len)
+{
+	int rc;
+
+	rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
+	if (rc < 0) {
+		pt_debug(cd->dev, DL_ERROR,
+				"%s: fail get exclusive ex=%p own=%p\n",
+				__func__, cd->exclusive_dev, cd->dev);
+		return rc;
+	}
+
+	rc = pt_pip_load_self_test_param_(cd, self_test_id, load_offset,
+		load_length, parameters, status, ret_test_id, act_load_len);
+
+	if (release_exclusive(cd, cd->dev) < 0)
+		pt_debug(cd->dev, DL_ERROR,
+				"%s: fail to release exclusive\n", __func__);
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_request_pip_load_self_test_param
+ *
+ * SUMMARY: Function pointer included in core_nonhid_cmd struct for external
+ *	calls to the protected or unprotected call to
+ *	pt_pip_load_self_test_param
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*dev          - pointer to device structure
+ *	 protect      - flag to call protected or non-protected
+ *	 self_test_id - enumerated test ID for which the parmeters belong
+ *	 load_offset  - mem offset to where to load parameters
+ *	 load_length  - length of parameter data to load
+ *	*parameters   - pointer to list of parameter data
+ *	*status       - pointer to store the response status
+ *	*ret_test_id  - pointer to returned test id the parameters were stored
+ *	*act_load_len - pointer to store the actual load length that was writen
+ ******************************************************************************/
+static int _pt_request_pip_load_self_test_param(struct device *dev,
+		int protect, u8 self_test_id, u16 load_offset, u16 load_length,
+		u8 *parameters, u8 *status, u8 *ret_test_id, u16 *act_load_len)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+
+	if (protect)
+		return pt_pip_load_self_test_param(cd, self_test_id,
+		load_offset, load_length, parameters, status, ret_test_id,
+		act_load_len);
+
+	return pt_pip_load_self_test_param_(cd, self_test_id, load_offset,
+		load_length, parameters, status, ret_test_id, act_load_len);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pip_calibrate_ext_
+ *
+ * SUMMARY: Send the PIP1 Extended Calibrate command (0x30) to the DUT waiting
+ *	for the response
+ *
+ * NOTE: This calibrate command requires the DUT to support PIP version >= 1.10
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd       - pointer to core data
+ *	*cal_data - pointer to extended calibration data structure
+ *	*status   - pointer to where the command response status is stored
+ ******************************************************************************/
+static int pt_pip_calibrate_ext_(struct pt_core_data *cd,
+		struct pt_cal_ext_data *cal_data, u8 *status)
+{
+	int rc = 0;
+	int write_length = 4;
+	u8 write_buf[4];
+	u16 size = 0;
+	unsigned short crc = 0;
+
+	struct pt_hid_output hid_output = {
+		CREATE_PIP1_FW_CMD(PIP1_CMD_ID_CALIBRATE_DEVICE_EXTENDED),
+		.write_length = write_length,
+		.write_buf = write_buf,
+		.timeout_ms = PT_PIP1_CMD_CALIBRATE_EXT_TIMEOUT,
+	};
+
+	if (cal_data == NULL)
+		return -EINVAL;
+
+	memcpy(write_buf, cal_data, sizeof(struct pt_cal_ext_data));
+
+	rc =  pt_pip1_send_output_and_wait_(cd, &hid_output);
+	if (rc)
+		return rc;
+
+	if (status)
+		*status = cd->response_buf[5];
+
+	/*
+	 * When doing a calibration on a flashless DUT, save CAL data in
+	 * the TTDL cache on any successful calibration
+	 */
+	if (cd->response_buf[5] == 0 && cd->cal_cache_in_host) {
+		pt_debug(cd->dev, DL_INFO, "%s: Retrieve and Save CAL\n",
+			__func__);
+		rc = _pt_manage_local_cal_data(cd->dev, PT_CAL_DATA_SAVE,
+			&size, &crc);
+		if (rc)
+			pt_debug(cd->dev, DL_ERROR,
+				"%s: Error Saving CAL rc=%d\n", __func__, rc);
+		else
+			pt_debug(cd->dev, DL_INFO,
+				"%s: Saved CAL: chip ID=0x%04X size=%d\n",
+				__func__, crc, size);
+	}
+
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pip_calibrate_ext
+ *
+ * SUMMARY: Protected call to pt_pip_calibrate_ext_ by exclusive access to the
+ *	DUT
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd       - pointer to core data
+ *	*cal_data - pointer to extended calibration data structure
+ *	*status   - pointer to where the command response status is stored
+ ******************************************************************************/
+static int pt_pip_calibrate_ext(struct pt_core_data *cd,
+		struct pt_cal_ext_data *cal_data, u8 *status)
+{
+	int rc;
+
+	rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
+	if (rc < 0) {
+		pt_debug(cd->dev, DL_ERROR,
+				"%s: fail get exclusive ex=%p own=%p\n",
+				__func__, cd->exclusive_dev, cd->dev);
+		return rc;
+	}
+
+	rc = pt_pip_calibrate_ext_(cd, cal_data, status);
+
+	if (release_exclusive(cd, cd->dev) < 0)
+		pt_debug(cd->dev, DL_ERROR,
+				"%s: fail to release exclusive\n", __func__);
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_request_pip_calibrate_ext
+ *
+ * SUMMARY: Function pointer included in core_nonhid_cmd struct for external
+ *	calls to the protected or unprotected call to pt_pip_calibrate_ext
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*dev     -  pointer to device structure
+ *	 protect -  flag to call protected or non-protected
+ *	*cal_data - pointer to extended calibration data structure
+ *	*status   - pointer to where the command response status is stored
+ ******************************************************************************/
+static int _pt_request_pip_calibrate_ext(struct device *dev,
+		int protect, struct pt_cal_ext_data *cal_data, u8 *status)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+
+	if (protect)
+		return pt_pip_calibrate_ext(cd, cal_data, status);
+
+	return pt_pip_calibrate_ext_(cd, cal_data, status);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pip_calibrate_idacs_
+ *
+ * SUMMARY: Send the PIP Calibrate IDACs command (0x28) to the DUT waiting
+ *	for the response
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd     - pointer to core data
+ *	 mode   - sense mode to calibrate (0-5)
+ *	*status - pointer to where the command response status is stored
+ ******************************************************************************/
+static int pt_pip_calibrate_idacs_(struct pt_core_data *cd,
+		u8 mode, u8 *status)
+{
+	int rc = 0;
+	int write_length = 1;
+	u8 write_buf[1];
+	u8 cmd_offset = 0;
+	struct pt_hid_output hid_output = {
+		CREATE_PIP1_FW_CMD(PIP1_CMD_ID_CALIBRATE_IDACS),
+		.write_length = write_length,
+		.write_buf = write_buf,
+		.timeout_ms = PT_PIP1_CMD_CALIBRATE_IDAC_TIMEOUT,
+	};
+	write_buf[cmd_offset++] = mode;
+	rc =  pt_pip1_send_output_and_wait_(cd, &hid_output);
+	if (rc)
+		return rc;
+
+	*status = cd->response_buf[5];
+	if (*status)
+		return -EINVAL;
+
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pip_calibrate_idacs
+ *
+ * SUMMARY: Protected call to pt_hid_output_calibrate_idacs_ by exclusive
+ *	access to the DUT
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd     - pointer to core data
+ *	 mode   - sense mode to calibrate (0-5)
+ *	*status - pointer to where the command response status is stored
+ ******************************************************************************/
+static int pt_pip_calibrate_idacs(struct pt_core_data *cd,
+		u8 mode, u8 *status)
+{
+	int rc;
+
+	rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
+	if (rc < 0) {
+		pt_debug(cd->dev, DL_ERROR,
+				"%s: fail get exclusive ex=%p own=%p\n",
+				__func__, cd->exclusive_dev, cd->dev);
+		return rc;
+	}
+
+	rc = pt_pip_calibrate_idacs_(cd, mode, status);
+
+	if (release_exclusive(cd, cd->dev) < 0)
+		pt_debug(cd->dev, DL_ERROR,
+				"%s: fail to release exclusive\n", __func__);
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_request_pip_calibrate_idacs
+ *
+ * SUMMARY: Function pointer included in core_nonhid_cmd struct for external
+ *	calls to the protected or unprotected call to pt_pip_calibrate_idacs
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*dev     - pointer to device structure
+ *	 protect - flag to call protected or non-protected
+ *	 mode    - sense mode to calibrate (0-5)
+ *	*status  - pointer to where the command response status is stored
+ ******************************************************************************/
+static int _pt_request_pip_calibrate_idacs(struct device *dev,
+		int protect, u8 mode, u8 *status)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+
+	if (protect)
+		return pt_pip_calibrate_idacs(cd, mode, status);
+
+	return pt_pip_calibrate_idacs_(cd, mode, status);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_hid_output_initialize_baselines_
+ *
+ * SUMMARY: Send the PIP "Initialize Baselines" command (0x29) to the DUT
+ *  waiting for the response.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *cd       - pointer to core data
+ *   test_id  - bit type flag to allow initialize baseline MUT,BTN,SELG
+ *              each or together with a single command.
+ *  *status   - pointer to where the command response status is stored
+ ******************************************************************************/
+static int pt_hid_output_initialize_baselines_(
+		struct pt_core_data *cd, u8 test_id, u8 *status)
+{
+	int rc = 0;
+	int write_length = 1;
+	u8 write_buf[1];
+	u8 cmd_offset = 0;
+	struct pt_hid_output hid_output = {
+		CREATE_PIP1_FW_CMD(PIP1_CMD_ID_INITIALIZE_BASELINES),
+		.write_length = write_length,
+		.write_buf = write_buf,
+	};
+	write_buf[cmd_offset++] = test_id;
+
+	rc = pt_pip1_send_output_and_wait_(cd, &hid_output);
+	if (rc)
+		return rc;
+
+	*status = cd->response_buf[5];
+	if (*status)
+		return -EINVAL;
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_hid_output_initialize_baselines
+ *
+ * SUMMARY: Protected call to pt_hid_output_initialize_baselines_ by exclusive
+ *  access to the DUT
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *cd       - pointer to core data
+ *   test_id  - enumerated ID against which to initialize the baseline
+ *  *status   - pointer to where the command response status is stored
+ ******************************************************************************/
+static int pt_hid_output_initialize_baselines(struct pt_core_data *cd,
+		u8 test_id, u8 *status)
+{
+	int rc;
+
+	rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
+	if (rc < 0) {
+		pt_debug(cd->dev, DL_ERROR,
+				"%s: fail get exclusive ex=%p own=%p\n",
+				__func__, cd->exclusive_dev, cd->dev);
+		return rc;
+	}
+
+	rc = pt_hid_output_initialize_baselines_(cd, test_id, status);
+
+	if (release_exclusive(cd, cd->dev) < 0)
+		pt_debug(cd->dev, DL_ERROR,
+				"%s: fail to release exclusive\n", __func__);
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_request_pip_initialize_baselines
+ *
+ * SUMMARY: Function pointer included in core_nonhid_cmd struct for external
+ *	calls to the protected or unprotected call to
+ *	pt_pip_initialize_baselines
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*dev     - pointer to device structure
+ *	 protect - flag to call protected or non-protected
+ *	 test_id - enumerated ID against which to initialize the baseline
+ *	*status  - pointer to where the command response status is stored
+ ******************************************************************************/
+static int _pt_request_pip_initialize_baselines(struct device *dev,
+		int protect, u8 test_id, u8 *status)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+
+	if (protect)
+		return pt_hid_output_initialize_baselines(cd, test_id,
+				status);
+
+	return pt_hid_output_initialize_baselines_(cd, test_id, status);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_hid_output_exec_panel_scan_
+ *
+ * SUMMARY: Sends the PIP "Execute Panel Scan" (0x2A) to the DUT and waits for
+ *	the response
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd        - pointer to core data
+ ******************************************************************************/
+static int pt_hid_output_exec_panel_scan_(struct pt_core_data *cd)
+{
+	struct pt_hid_output hid_output = {
+		CREATE_PIP1_FW_CMD(PIP1_CMD_ID_EXEC_PANEL_SCAN),
+	};
+
+	return pt_pip1_send_output_and_wait_(cd, &hid_output);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_hid_output_exec_panel_scan
+ *
+ * SUMMARY: Protected call to pt_hid_output_exec_panel_scan_ by exclusive
+ *	access to the DUT.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd        - pointer to core data
+ ******************************************************************************/
+static int pt_hid_output_exec_panel_scan(struct pt_core_data *cd)
+{
+	int rc;
+
+	rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
+	if (rc < 0) {
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: fail get exclusive ex=%p own=%p\n",
+			__func__, cd->exclusive_dev, cd->dev);
+		return rc;
+	}
+
+	rc = pt_hid_output_exec_panel_scan_(cd);
+
+	if (release_exclusive(cd, cd->dev) < 0)
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: fail to release exclusive\n", __func__);
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pip2_exec_panel_scan_
+ *
+ * SUMMARY: Send the PIP2 "Execute Panel Scan" (0x21) to the DUT and waits for
+ *	the response
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd        - pointer to core data
+ *	 scan_type - type of panel scan to perform (PIP2 only)
+ ******************************************************************************/
+static int pt_pip2_exec_panel_scan_(struct pt_core_data *cd, u8 scan_type)
+{
+	int rc = 0;
+	u8  data[2];
+	u8  read_buf[10];
+	u16 actual_read_len;
+
+	pt_debug(cd->dev, DL_DEBUG, "%s: PIP2 Execute Scan %d\n",
+		__func__, scan_type);
+	data[0] = scan_type;
+	rc = _pt_request_pip2_send_cmd(cd->dev,
+		PT_CORE_CMD_UNPROTECTED, PIP2_CMD_EXECUTE_SCAN,
+		data, 1, read_buf, &actual_read_len);
+	if (rc) {
+		pt_debug(cd->dev, DL_ERROR,
+			"%s EXECUTE_SCAN command for type %d failed. rc=%d\n",
+			__func__, scan_type, rc);
+	}
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pip2_exec_panel_scan
+ *
+ * SUMMARY: Protected call to pt_pip2_exec_panel_scan_ by exclusive
+ *	access to the DUT.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd        - pointer to core data
+ *	 scan_type - type of panel scan to perform (PIP2 only)
+ ******************************************************************************/
+static int pt_pip2_exec_panel_scan(struct pt_core_data *cd, u8 scan_type)
+{
+	int rc;
+
+	rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
+	if (rc < 0) {
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: fail get exclusive ex=%p own=%p\n",
+			__func__, cd->exclusive_dev, cd->dev);
+		return rc;
+	}
+
+	rc = pt_pip2_exec_panel_scan_(cd, scan_type);
+
+	if (release_exclusive(cd, cd->dev) < 0)
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: fail to release exclusive\n", __func__);
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_request_pip_exec_panel_scan
+ *
+ * SUMMARY: Function pointer included in core_nonhid_cmd struct for external
+ *	calls to the protected or unprotected call to
+ *	pt_pip2_exec_panel_scan or pt_hid_output_exec_panel_scan
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*dev       - pointer to device structure
+ *	 protect   - flag to call protected or non-protected
+ *	 scan_type - type of panel scan to perform (PIP2 only)
+ ******************************************************************************/
+static int _pt_request_pip_exec_panel_scan(struct device *dev,
+		int protect, u8 scan_type)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+
+	if (cd->sysinfo.ready && IS_PIP_VER_GE(&cd->sysinfo, 1, 12)) {
+		if (protect)
+			return pt_pip2_exec_panel_scan(cd, scan_type);
+
+		return pt_pip2_exec_panel_scan_(cd, scan_type);
+	}
+
+	if (protect)
+		return pt_hid_output_exec_panel_scan(cd);
+
+	return pt_hid_output_exec_panel_scan_(cd);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_hid_output_retrieve_panel_scan_
+ *
+ * SUMMARY: Sends the PIP "Retrieve Panel Scan" (0x2B) command to the DUT
+ *  to retrieve the specified data type for a the last successful Execute
+ *  Panel Scan command.
+ *
+ * RETURN:
+ *    0 = success
+ *   !0 = failure
+ *
+ * PARAMETERS:
+ *  *dev             - pointer to device structure
+ *   protect         - flag to call protected or non-protected
+ *   read_offset     - read pointer offset
+ *   read_count      - length of data to read
+ *   data_id         - enumerated test ID to read selftest results from
+ *  *response        - pointer to store the read response status
+ *  *config          - pointer to store config data
+ *  *actual_read_len - pointer to store data length actually read
+ *  *read_buf        - pointer to the read buffer
+ ******************************************************************************/
+static int pt_hid_output_retrieve_panel_scan_(
+		struct pt_core_data *cd, u16 read_offset, u16 read_count,
+		u8 data_id, u8 *response, u8 *config, u16 *actual_read_len,
+		u8 *read_buf)
+{
+	int status;
+	u8 read_data_id;
+	int rc = 0;
+	int write_length = 5;
+	u8 write_buf[5];
+	u8 cmd_offset = 0;
+	u8 data_elem_size;
+	int size;
+	int data_size;
+	struct pt_hid_output hid_output = {
+		CREATE_PIP1_FW_CMD(PIP1_CMD_ID_RETRIEVE_PANEL_SCAN),
+		.write_length = write_length,
+		.write_buf = write_buf,
+	};
+
+	write_buf[cmd_offset++] = LOW_BYTE(read_offset);
+	write_buf[cmd_offset++] = HI_BYTE(read_offset);
+	write_buf[cmd_offset++] = LOW_BYTE(read_count);
+	write_buf[cmd_offset++] = HI_BYTE(read_count);
+	write_buf[cmd_offset++] = data_id;
+
+	rc = pt_pip1_send_output_and_wait_(cd, &hid_output);
+	if (rc)
+		return rc;
+
+	status = cd->response_buf[5];
+	if (status)
+		return -EINVAL;
+
+	read_data_id = cd->response_buf[6];
+	if (read_data_id != data_id)
+		return -EPROTO;
+
+	size = get_unaligned_le16(&cd->response_buf[0]);
+	*actual_read_len = get_unaligned_le16(&cd->response_buf[7]);
+	*config = cd->response_buf[9];
+
+	data_elem_size = *config & 0x07;
+	data_size = *actual_read_len * data_elem_size;
+
+	if (read_buf)
+		memcpy(read_buf, &cd->response_buf[10], data_size);
+	if (response)
+		memcpy(response, cd->response_buf, size);
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_hid_output_retrieve_panel_scan
+ *
+ * SUMMARY: Protected call to pt_hid_output_retrieve_panel_scan_ by exclusive
+ *  access to the DUT.
+ *
+ * RETURN:
+ *    0 = success
+ *   !0 = failure
+ *
+ * PARAMETERS:
+ *  *dev             - pointer to device structure
+ *   protect         - flag to call protected or non-protected
+ *   read_offset     - read pointer offset
+ *   read_count      - length of data to read
+ *   data_id         - enumerated test ID to read selftest results from
+ *  *response        - pointer to store the read response status
+ *  *config          - pointer to store config data
+ *  *actual_read_len - pointer to store data length actually read
+ *  *read_buf        - pointer to the read buffer
+ ******************************************************************************/
+static int pt_hid_output_retrieve_panel_scan(
+		struct pt_core_data *cd, u16 read_offset, u16 read_count,
+		u8 data_id, u8 *response, u8 *config, u16 *actual_read_len,
+		u8 *read_buf)
+{
+	int rc;
+
+	rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
+	if (rc < 0) {
+		pt_debug(cd->dev, DL_ERROR,
+				"%s: fail get exclusive ex=%p own=%p\n",
+				__func__, cd->exclusive_dev, cd->dev);
+		return rc;
+	}
+
+	rc = pt_hid_output_retrieve_panel_scan_(cd, read_offset,
+			read_count, data_id, response, config,
+			actual_read_len, read_buf);
+
+	if (release_exclusive(cd, cd->dev) < 0)
+		pt_debug(cd->dev, DL_ERROR,
+				"%s: fail to release exclusive\n", __func__);
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_request_pip_retrieve_panel_scan
+ *
+ * SUMMARY: Function pointer included in core_nonhid_cmd struct for external
+ *	calls to the protected or unprotected call to
+ *	pt_hid_output_retrieve_panel_scan
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*dev             - pointer to device structure
+ *	 protect         - flag to call protected or non-protected
+ *	 read_offset     - read pointer offset
+ *	 read_count      - length of data to read
+ *	 data_id         - enumerated test ID to read selftest results from
+ *	*response        - pointer to store the read response status
+ *	*config          - pointer to store config data
+ *	*actual_read_len - pointer to store data length actually read
+ *	*read_buf        - pointer to the read buffer
+ ******************************************************************************/
+static int _pt_request_pip_retrieve_panel_scan(struct device *dev,
+		int protect, u16 read_offset, u16 read_count, u8 data_id,
+		u8 *response, u8 *config, u16 *actual_read_len, u8 *read_buf)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+
+	if (protect)
+		return pt_hid_output_retrieve_panel_scan(cd,
+				read_offset, read_count, data_id, response,
+				config, actual_read_len, read_buf);
+
+	return pt_hid_output_retrieve_panel_scan_(cd,
+			read_offset, read_count, data_id, response,
+			config, actual_read_len, read_buf);
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_request_pip_user_cmd
+ *
+ * SUMMARY: Function pointer included in core_nonhid_cmd struct for external
+ *	calls to the protected or unprotected call to
+ *	pt_hid_output_user_cmd
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*dev             - pointer to device structure
+ *	 protect         - flag to call protected or non-protected
+ *	 read_len        - length of data to read
+ *	*read_buf        - pointer to store read data
+ *	 write_len       - length of data to write
+ *	*write_buf       - pointer to buffer to write
+ *	*actual_read_len - pointer to store data length actually read
+ ******************************************************************************/
+static int _pt_request_pip_user_cmd(struct device *dev,
+		int protect, u16 read_len, u8 *read_buf, u16 write_len,
+		u8 *write_buf, u16 *actual_read_len)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+
+	if (protect)
+		return pt_hid_output_user_cmd(cd, read_len, read_buf,
+				write_len, write_buf, actual_read_len);
+
+	return pt_hid_output_user_cmd_(cd, read_len, read_buf,
+			write_len, write_buf, actual_read_len);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_hid_output_bl_get_information_
+ *
+ * SUMMARY: Sends the PIP "Get Bootloader Information" (0x38) command to the
+ *  DUT to retrieve bootloader version and chip identification information.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *cd           - pointer to core data
+ *  *return_data  - pointer to store the return data
+ *****************************************************************************/
+static int pt_hid_output_bl_get_information_(struct pt_core_data *cd,
+		u8 *return_data)
+{
+	int rc;
+	int data_len;
+	struct pt_hid_output hid_output = {
+		CREATE_PIP1_BL_CMD(PIP1_BL_CMD_ID_GET_INFO),
+	};
+
+	rc = pt_pip1_send_output_and_wait_(cd, &hid_output);
+	if (rc)
+		return rc;
+
+	data_len = get_unaligned_le16(&cd->input_buf[6]);
+	if (!data_len)
+		return -EPROTO;
+
+	memcpy(return_data, &cd->response_buf[8], data_len);
+
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_hid_output_bl_get_information
+ *
+ * SUMMARY: Protected call to pt_hid_output_bl_get_information_ by exclusive
+ *  access to the DUT.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *cd           - pointer to core data
+ *  *return_data  - pointer to store the return data
+ ******************************************************************************/
+static int pt_hid_output_bl_get_information(struct pt_core_data *cd,
+		u8 *return_data)
+{
+	int rc;
+
+	rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
+	if (rc < 0) {
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: fail get exclusive ex=%p own=%p\n",
+			__func__, cd->exclusive_dev, cd->dev);
+		return rc;
+	}
+
+	rc = pt_hid_output_bl_get_information_(cd, return_data);
+
+	if (release_exclusive(cd, cd->dev) < 0)
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: fail to release exclusive\n", __func__);
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_request_pip_bl_get_information
+ *
+ * SUMMARY: Function pointer included in core_nonhid_cmd struct for external
+ *	calls to the protected or unprotected call to
+ *	pt_hid_output_bl_get_information
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*dev         - pointer to device structure
+ *	 protect     - flag to call protected or non-protected
+ *	*return_data - pointer to store bl data
+ ******************************************************************************/
+static int _pt_request_pip_bl_get_information(struct device *dev,
+		int protect, u8 *return_data)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+
+	if (protect)
+		return pt_hid_output_bl_get_information(cd, return_data);
+
+	return pt_hid_output_bl_get_information_(cd, return_data);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_hid_output_bl_initiate_bl_
+ *
+ * SUMMARY: Sends the PIP "Get Bootloader Information" (0x48) command to the
+ *  DUT to erases the entire TrueTouch application, Configuration Data block,
+ *  and Design Data block in flash and enables the host to execute the Program
+ *  and Verify Row command to bootload the application image and data.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *cd               - pointer to core data
+ *   protect          - flag to call protected or non-protected
+ *   key_size         - size of key
+ *  *key_buf          - pointer to key data to allow operation
+ *   row_size         - size of the meta data row
+ *  *metadata_row_buf - pointer to meta data to write
+ ******************************************************************************/
+static int pt_hid_output_bl_initiate_bl_(struct pt_core_data *cd,
+		u16 key_size, u8 *key_buf, u16 row_size, u8 *metadata_row_buf)
+{
+	u16 write_length = key_size + row_size;
+	u8 *write_buf;
+	int rc = 0;
+	struct pt_hid_output hid_output = {
+		CREATE_PIP1_BL_CMD(PIP1_BL_CMD_ID_INITIATE_BL),
+		.write_length = write_length,
+		.timeout_ms = PT_PIP1_CMD_INITIATE_BL_TIMEOUT,
+	};
+
+	write_buf = kzalloc(write_length, GFP_KERNEL);
+	if (!write_buf)
+		return -ENOMEM;
+
+	hid_output.write_buf = write_buf;
+
+	if (key_size)
+		memcpy(write_buf, key_buf, key_size);
+
+	if (row_size)
+		memcpy(&write_buf[key_size], metadata_row_buf, row_size);
+
+	rc =  pt_pip1_send_output_and_wait_(cd, &hid_output);
+
+	kfree(write_buf);
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_hid_output_bl_initiate_bl
+ *
+ * SUMMARY: Protected call to pt_hid_output_bl_initiate_bl_ by exclusive
+ *  access to the DUT.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *cd               - pointer to core data
+ *   key_size         - size of key
+ *  *key_buf          - pointer to key data to allow operation
+ *   row_size         - size of the meta data row
+ *  *metadata_row_buf - pointer to meta data to write
+ ******************************************************************************/
+static int pt_hid_output_bl_initiate_bl(struct pt_core_data *cd,
+		u16 key_size, u8 *key_buf, u16 row_size, u8 *metadata_row_buf)
+{
+	int rc;
+
+	rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
+	if (rc < 0) {
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: fail get exclusive ex=%p own=%p\n",
+			__func__, cd->exclusive_dev, cd->dev);
+		return rc;
+	}
+
+	rc = pt_hid_output_bl_initiate_bl_(cd, key_size, key_buf,
+			row_size, metadata_row_buf);
+
+	if (release_exclusive(cd, cd->dev) < 0)
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: fail to release exclusive\n", __func__);
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_request_pip_bl_initiate_bl
+ *
+ * SUMMARY: Function pointer included in core_nonhid_cmd struct for external
+ *	calls to the protected or unprotected call to
+ *	pt_hid_output_bl_initiate_bl
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*dev              - pointer to device structure
+ *	 protect          - flag to call protected or non-protected
+ *	 key_size         - size of key
+ *	*key_buf          - pointer to key data to allow operation
+ *	 row_size         - size of the meta data row
+ *	*metadata_row_buf - pointer to meta data to write
+ ******************************************************************************/
+static int _pt_request_pip_bl_initiate_bl(struct device *dev,
+		int protect, u16 key_size, u8 *key_buf, u16 row_size,
+		u8 *metadata_row_buf)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+
+	if (protect)
+		return pt_hid_output_bl_initiate_bl(cd, key_size, key_buf,
+				row_size, metadata_row_buf);
+
+	return pt_hid_output_bl_initiate_bl_(cd, key_size, key_buf,
+			row_size, metadata_row_buf);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_hid_output_bl_program_and_verify_
+ *
+ * SUMMARY: Sends the PIP "Get Bootloader Information" (0x39) command to upload
+ *  and program a 128-byte row into the flash, and then verifies written data.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *cd       - pointer to core data
+ *   data_len - length of data_buf
+ *  *data_buf - firmware image to program
+ ******************************************************************************/
+static int pt_hid_output_bl_program_and_verify_(
+		struct pt_core_data *cd, u16 data_len, u8 *data_buf)
+{
+	struct pt_hid_output hid_output = {
+		CREATE_PIP1_BL_CMD(PIP1_BL_CMD_ID_PROGRAM_AND_VERIFY),
+		.write_length = data_len,
+		.write_buf = data_buf,
+		.timeout_ms = PT_PIP1_CMD_PROGRAM_AND_VERIFY_TIMEOUT,
+	};
+
+	return pt_pip1_send_output_and_wait_(cd, &hid_output);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_hid_output_bl_program_and_verify
+ *
+ * SUMMARY: Protected call to pt_hid_output_bl_program_and_verify_ by exclusive
+ *  access to the DUT.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *cd       - pointer to core data
+ *   data_len - length of data_buf
+ *  *data_buf - firmware image to program
+ ******************************************************************************/
+static int pt_hid_output_bl_program_and_verify(
+		struct pt_core_data *cd, u16 data_len, u8 *data_buf)
+{
+	int rc;
+
+	rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
+	if (rc < 0) {
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: fail get exclusive ex=%p own=%p\n",
+			__func__, cd->exclusive_dev, cd->dev);
+		return rc;
+	}
+
+	rc = pt_hid_output_bl_program_and_verify_(cd, data_len, data_buf);
+
+	if (release_exclusive(cd, cd->dev) < 0)
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: fail to release exclusive\n", __func__);
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_request_pip_bl_program_and_verify
+ *
+ * SUMMARY: Function pointer included in core_nonhid_cmds to allow other modules
+ *	to request to have the BL program and verify a FW image
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *      *dev      - pointer to device structure
+ *	 protect  - boolean to determine to call the protected function
+ *	 data_len - length of data_buf
+ *	*data_buf - firmware image to program
+ ******************************************************************************/
+static int _pt_request_pip_bl_program_and_verify(
+		struct device *dev, int protect, u16 data_len, u8 *data_buf)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+
+	if (protect)
+		return pt_hid_output_bl_program_and_verify(cd, data_len,
+				data_buf);
+
+	return pt_hid_output_bl_program_and_verify_(cd, data_len,
+			data_buf);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_hid_output_bl_verify_app_integrity_
+ *
+ * SUMMARY: Sends the PIP "Get Bootloader Information" (0x31) command to
+ *  perform a full verification of the application integrity by calculating the
+ *  CRC of the image in flash and compare it to the expected CRC stored in the
+ *  Metadata row.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *cd      - pointer to core data
+ *  *result  - pointer to store result
+ ******************************************************************************/
+static int pt_hid_output_bl_verify_app_integrity_(
+		struct pt_core_data *cd, u8 *result)
+{
+	int rc;
+	struct pt_hid_output hid_output = {
+		CREATE_PIP1_BL_CMD(PIP1_BL_CMD_ID_VERIFY_APP_INTEGRITY),
+	};
+
+	rc = pt_pip1_send_output_and_wait_(cd, &hid_output);
+	if (rc) {
+		*result = 0;
+		return rc;
+	}
+
+	*result = cd->response_buf[8];
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_hid_output_bl_verify_app_integrity
+ *
+ * SUMMARY: Protected call to pt_hid_output_bl_verify_app_integrity_ by
+ *  exclusive access to the DUT.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *cd      - pointer to core data
+ *  *result  - pointer to store result
+ ******************************************************************************/
+static int pt_hid_output_bl_verify_app_integrity(
+		struct pt_core_data *cd, u8 *result)
+{
+	int rc;
+
+	rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
+	if (rc < 0) {
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: fail get exclusive ex=%p own=%p\n",
+			__func__, cd->exclusive_dev, cd->dev);
+		return rc;
+	}
+
+	rc = pt_hid_output_bl_verify_app_integrity_(cd, result);
+
+	if (release_exclusive(cd, cd->dev) < 0)
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: fail to release exclusive\n", __func__);
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_request_pip_bl_verify_app_integrity
+ *
+ * SUMMARY: Function pointer included in core_nonhid_cmds to allow other modules
+ *	to request to have the BL verify the application integrity (PIP1.x only)
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *      *dev     - pointer to device structure
+ *	 protect - boolean to determine to call the protected function
+ *	*result  - pointer to store result
+ ******************************************************************************/
+static int _pt_request_pip_bl_verify_app_integrity(
+		struct device *dev, int protect, u8 *result)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+
+	if (protect)
+		return pt_hid_output_bl_verify_app_integrity(cd, result);
+
+	return pt_hid_output_bl_verify_app_integrity_(cd, result);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_hid_output_bl_launch_app_
+ *
+ * SUMMARY: Sends the PIP "Launch Application" (0x3B) command to launch the
+ *  application from bootloader (PIP1.x only).
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *cd  - pointer to core data
+ ******************************************************************************/
+static int pt_hid_output_bl_launch_app_(struct pt_core_data *cd)
+{
+	struct pt_hid_output hid_output = {
+		CREATE_PIP1_BL_CMD(PIP1_BL_CMD_ID_LAUNCH_APP),
+		.reset_expected = 1,
+	};
+
+	return pt_pip1_send_output_and_wait_(cd, &hid_output);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_hid_output_bl_launch_app
+ *
+ * SUMMARY: Protected call to pt_hid_output_bl_launch_app_ by exclusive access
+ *  to the DUT.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *cd  - pointer to core data
+ ******************************************************************************/
+static int pt_hid_output_bl_launch_app(struct pt_core_data *cd)
+{
+	int rc;
+
+	rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
+	if (rc < 0) {
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: fail get exclusive ex=%p own=%p\n",
+			__func__, cd->exclusive_dev, cd->dev);
+		return rc;
+	}
+
+	rc = pt_hid_output_bl_launch_app_(cd);
+
+	if (release_exclusive(cd, cd->dev) < 0)
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: fail to release exclusive\n", __func__);
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_request_pip_launch_app
+ *
+ * SUMMARY: Function pointer included in core_nonhid_cmds to allow other modules
+ *	to request to have the BL launch the application. (PIP1.x only)
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *      *dev      - pointer to device structure
+ *	 protect  - boolean to determine to call the protected function
+ ******************************************************************************/
+static int _pt_request_pip_launch_app(struct device *dev,
+		int protect)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+
+	if (protect)
+		return pt_hid_output_bl_launch_app(cd);
+
+	return pt_hid_output_bl_launch_app_(cd);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_hid_output_bl_get_panel_id_
+ *
+ * SUMMARY: Sends the PIP "Get Panel ID" (0x3E) command to return the Panel ID
+ *  value store in the System Information.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *cd       - pointer to core data
+ *  *panel_id - pointer to where the panel ID will be stored
+ ******************************************************************************/
+static int pt_hid_output_bl_get_panel_id_(
+		struct pt_core_data *cd, u8 *panel_id)
+{
+	int rc;
+	struct pt_hid_output hid_output = {
+		CREATE_PIP1_BL_CMD(PIP1_BL_CMD_ID_GET_PANEL_ID),
+	};
+
+	rc = pt_pip1_send_output_and_wait_(cd, &hid_output);
+	if (rc == -EPROTO && cd->response_buf[5] == ERROR_COMMAND) {
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: Get Panel ID command not supported\n",
+			__func__);
+		*panel_id = PANEL_ID_NOT_ENABLED;
+		return 0;
+	} else if (rc < 0) {
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: Error on Get Panel ID command\n", __func__);
+		return rc;
+	}
+
+	*panel_id = cd->response_buf[8];
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_hid_output_bl_get_panel_id
+ *
+ * SUMMARY: Protected call to pt_hid_output_bl_get_panel_id_ by exclusive access
+ *  to the DUT.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *cd       - pointer to core data
+ *  *panel_id - pointer to where the panel ID will be stored
+ ******************************************************************************/
+static int pt_hid_output_bl_get_panel_id(
+		struct pt_core_data *cd, u8 *panel_id)
+{
+	int rc;
+
+	rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
+	if (rc < 0) {
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: fail get exclusive ex=%p own=%p\n",
+			__func__, cd->exclusive_dev, cd->dev);
+		return rc;
+	}
+
+	rc = pt_hid_output_bl_get_panel_id_(cd, panel_id);
+
+	if (release_exclusive(cd, cd->dev) < 0)
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: fail to release exclusive\n", __func__);
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_request_pip_bl_get_panel_id
+ *
+ * SUMMARY: Function pointer included in core_nonhid_cmds to allow other modules
+ *	to have the BL retrieve the panel ID
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*dev      - pointer to device structure
+ *	 protect  - flag to run in protected mode
+ *	*panel_id - pointer to where the panel ID will be stored
+ ******************************************************************************/
+static int _pt_request_pip_bl_get_panel_id(
+		struct device *dev, int protect, u8 *panel_id)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+
+	if (protect)
+		return pt_hid_output_bl_get_panel_id(cd, panel_id);
+
+	return pt_hid_output_bl_get_panel_id_(cd, panel_id);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pip2_get_mode_sysmode_
+ *
+ * SUMMARY: Determine the current mode and system mode of the DUT by use of the
+ *	PIP2 STATUS command.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd       - pointer to core data structure
+ *	*mode     - pointer to store the retrieved mode
+ *	*sys_mode - pointer to store the FW system mode
+ ******************************************************************************/
+static int pt_pip2_get_mode_sysmode_(struct pt_core_data *cd,
+		u8 *mode, u8 *sys_mode)
+{
+	u16 actual_read_len;
+	u8 read_buf[12];
+	u8 status, boot;
+	int rc = 0;
+
+	rc = _pt_request_pip2_send_cmd(cd->dev, PT_CORE_CMD_UNPROTECTED,
+		PIP2_CMD_ID_STATUS, NULL, 0, read_buf, &actual_read_len);
+
+	pt_debug(cd->dev, DL_INFO, "%s: PIP2 STATUS command rc = %d\n",
+		__func__, rc);
+
+	if (!rc) {
+		pt_pr_buf(cd->dev, DL_DEBUG, read_buf, actual_read_len,
+			"PIP2 STATUS");
+		status = read_buf[PIP2_RESP_STATUS_OFFSET];
+		boot = read_buf[PIP2_RESP_BODY_OFFSET] & 0x01;
+		if (sys_mode) {
+			if (status == PIP2_RSP_ERR_NONE &&
+			    boot == PIP2_STATUS_APP_EXEC)
+				*sys_mode = read_buf[PIP2_RESP_BODY_OFFSET + 1];
+			else
+				*sys_mode = FW_SYS_MODE_UNDEFINED;
+		}
+		if (mode) {
+			if (status == PIP2_RSP_ERR_NONE &&
+			    boot == PIP2_STATUS_BOOT_EXEC)
+				*mode = PT_MODE_BOOTLOADER;
+			else if (status == PIP2_RSP_ERR_NONE &&
+				 boot == PIP2_STATUS_APP_EXEC)
+				*mode = PT_MODE_OPERATIONAL;
+			else
+				*mode = PT_MODE_UNKNOWN;
+		}
+	} else {
+		if (mode)
+			*mode = PT_MODE_UNKNOWN;
+		if (sys_mode)
+			*sys_mode = FW_SYS_MODE_UNDEFINED;
+		pt_debug(cd->dev, DL_WARN,
+			"%s: Mode and sys_mode could not be determined\n",
+			__func__);
+	}
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pip2_get_mode_sysmode
+ *
+ * SUMMARY: Protected call to pt_pip2_get_mode_sysmode_ by exclusive
+ *	access to the DUT.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd       - pointer to core data structure
+ *	*mode     - pointer to store the retrieved mode
+ *	*sys_mode - pointer to store the FW system mode
+ ******************************************************************************/
+static int pt_pip2_get_mode_sysmode(struct pt_core_data *cd,
+			u8 *mode, u8 *sys_mode)
+{
+	int rc;
+
+	rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
+	if (rc < 0) {
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: fail get exclusive ex=%p own=%p\n",
+			__func__, cd->exclusive_dev, cd->dev);
+		return rc;
+	}
+
+	rc = pt_pip2_get_mode_sysmode_(cd, mode, sys_mode);
+
+	if (release_exclusive(cd, cd->dev) < 0)
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: fail to release exclusive\n", __func__);
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_request_pip2_get_mode_sysmode
+ *
+ * SUMMARY: Function pointer included in core_commands struct for external
+ *	calls to the protected or unprotected call to
+ *	pt_pip2_get_mode_sysmode
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*dev       - pointer to device structure
+ *	*mode     - pointer to store the retrieved mode
+ *	*sys_mode - pointer to store the FW system mode
+ ******************************************************************************/
+static int _pt_request_pip2_get_mode_sysmode(struct device *dev,
+	int protect, u8 *mode, u8 *sys_mode)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+
+	if (protect)
+		return pt_pip2_get_mode_sysmode(cd, mode, sys_mode);
+
+	return pt_pip2_get_mode_sysmode_(cd, mode, sys_mode);
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_poll_for_fw_exit_boot_mode
+ *
+ * SUMMARY: Verify and or poll for the FW to exit BOOT mode. During the FW BOOT
+ *	mode only the following PIP commands will be serviced, any other PIP
+ *	command the FW will respond with an "Invalid PIP Command" response.
+ *		- Get HID Descriptor (Register 0x0001, no command ID)
+ *		- Reset (Register 0x0005, RESET HID request)
+ *		- Ping (Register 0x0004, Command ID 0x00
+ *		- Get System Information (Register 0x0004, Command ID 0x02)
+ *		- PIP2 Status (Register 0x0101, Command ID 0x01)
+ *		- PIP2 Version (Register 0x0101, Command ID 0x07)
+ *	This function will loop on the results of the STATUS command until
+ *	the FW reports it is out of BOOT mode.
+ *
+ *	NOTE:
+ *	- This function will update cd->fw_system_mode
+ *	- The STATUS cmd only supports this functionality for PIP 1.11+
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd         - pointer to core data
+ *	timeout     - max time (ms) to wait for FW to exit BOOT mode
+ *	actual_wait - pointer to actual time waited for FW to exit BOOT mode
+ ******************************************************************************/
+static int _pt_poll_for_fw_exit_boot_mode(struct pt_core_data *cd, int timeout,
+	int *actual_wait)
+{
+	int loop = 0;
+	u8 sys_mode = cd->fw_system_mode;
+	u8 pause = 10; /* in ms */
+	int rc = 0;
+	int max_loop = (timeout / pause) + 1; /* Add 1 due to int math */
+
+	if (cd->sysinfo.ready && !IS_PIP_VER_GE(&cd->sysinfo, 1, 11)) {
+		/*
+		 * For PIP <1.11, no support for polling wait so do a hard
+		 * coded wait and assume the FW is out of BOOT. Added 1 to
+		 * timeout to make it clear in kmsg if non polling was done.
+		 */
+		*actual_wait = PT_FW_EXIT_BOOT_MODE_TIMEOUT + 1;
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: PIP %d.%d no support for ext STATUS, sleep %d\n",
+			__func__,
+			cd->sysinfo.ttdata.pip_ver_major,
+			cd->sysinfo.ttdata.pip_ver_minor, *actual_wait);
+		msleep(*actual_wait);
+		sys_mode = FW_SYS_MODE_SCANNING;
+	}
+
+	if (sys_mode == FW_SYS_MODE_BOOT) {
+		while (!rc && loop <= max_loop &&
+		      (sys_mode == FW_SYS_MODE_BOOT)) {
+			loop++;
+			usleep_range(9000, pause * 1000);
+			rc = pt_pip2_get_mode_sysmode_(cd, NULL, &sys_mode);
+			pt_debug(cd->dev, DL_DEBUG,
+				"%s: FW in BOOT mode-sleep %dms, sys_mode=%d\n",
+				__func__, loop * pause, sys_mode);
+		}
+		*actual_wait = (int)(loop * pause);
+		pt_debug(cd->dev, DL_WARN,
+			"%s: FW exited BOOT mode in %dms, sys_mode=%d\n",
+			__func__, *actual_wait, sys_mode);
+
+		if (rc)
+			sys_mode = FW_SYS_MODE_UNDEFINED;
+		else if (sys_mode == FW_SYS_MODE_BOOT ||
+			 sys_mode == FW_SYS_MODE_UNDEFINED)
+			rc = -EBUSY;
+	}
+
+	mutex_lock(&cd->system_lock);
+	cd->fw_system_mode = sys_mode;
+	mutex_unlock(&cd->system_lock);
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_poll_for_fw_exit_boot_mode
+ *
+ * SUMMARY: Protected call to _pt_poll_for_fw_exit_boot_mode by exclusive
+ *	access to the DUT.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd         - pointer to core data
+ *	timeout     - max time (ms) to wait for FW to exit BOOT mode
+ *	actual_wait - pointer to actual time waited for FW to exit BOOT mode
+ ******************************************************************************/
+static int pt_poll_for_fw_exit_boot_mode(struct pt_core_data *cd, int timeout,
+	int *actual_wait)
+{
+	int rc;
+
+	rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
+	if (rc < 0) {
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: fail get exclusive ex=%p own=%p\n",
+			__func__, cd->exclusive_dev, cd->dev);
+		return rc;
+	}
+
+	rc = _pt_poll_for_fw_exit_boot_mode(cd, timeout, actual_wait);
+
+	if (release_exclusive(cd, cd->dev) < 0)
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: fail to release exclusive\n", __func__);
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_get_fw_sys_mode
+ *
+ * SUMMARY: Determine the FW system mode. For PIP 1.11+ the
+ * PIP2 STATUS command is used to directly query the FW system mode. For older
+ * PIP versions, there is no direct PIP commamnd that will directly provide this
+ * information but any PIP command above 0x1F requires scanning to be disabled
+ * before it will be operational. If scanning was not disabled before sending
+ * these PIP commands the FW will respond with a 6 byte error response. So to
+ * safely determine the scanning state, a PIP message that will not affect the
+ * operation of the FW was chosen:
+ * "Verify Data Block CRC (ID 0x20)" is sent and if a 6 byte error code is
+ * received scanning is active.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *      *cd        - pointer to core data
+ *      *sys_mode  - pointer to FW System mode
+ *      *mode      - pointer to mode (BL/FW)
+ ******************************************************************************/
+static int _pt_get_fw_sys_mode(struct pt_core_data *cd, u8 *sys_mode, u8 *mode)
+{
+	int write_length = 1;
+	int report_length;
+	int rc = 0;
+	u8 tmp_sys_mode = FW_SYS_MODE_UNDEFINED;
+	u8 tmp_mode = PT_MODE_UNKNOWN;
+	u8 param[1] = { PT_TCH_PARM_EBID };
+	struct pt_hid_output hid_output = {
+		CREATE_PIP1_FW_CMD(PIP1_CMD_ID_VERIFY_CONFIG_BLOCK_CRC),
+		.write_length = write_length,
+		.write_buf = param,
+		.novalidate = true,
+	};
+
+	/* AFter PIP1.11 the preferred method is using STATUS cmd */
+	if (IS_PIP_VER_GE(&cd->sysinfo, 1, 11)) {
+		rc = pt_pip2_get_mode_sysmode_(cd, &tmp_mode, &tmp_sys_mode);
+		pt_debug(cd->dev, DL_DEBUG, "%s: tmp_sys_mode=%d tmp_mode=%d\n",
+			__func__, tmp_sys_mode, tmp_mode);
+		if (!rc) {
+			if (tmp_mode != PT_MODE_OPERATIONAL)
+				tmp_sys_mode = FW_SYS_MODE_UNDEFINED;
+		}
+		goto exit;
+	}
+
+	/* Older systems use PIP1 CONFIG_BLOCK_CRC to best determine sys_mode */
+	if (cd->mode != PT_MODE_OPERATIONAL) {
+		tmp_mode = cd->mode;
+		goto exit;
+	}
+
+	rc = pt_pip1_send_output_and_wait_(cd, &hid_output);
+	if (rc)
+		goto exit;
+
+	report_length = (cd->response_buf[1] << 8) | (cd->response_buf[0]);
+	if ((report_length == 0x06) &&
+	    ((cd->response_buf[4] & PIP1_RESP_COMMAND_ID_MASK) == 0x00) &&
+	    (cd->response_buf[5] == PIP1_CMD_ID_VERIFY_CONFIG_BLOCK_CRC)) {
+		tmp_mode = PIP2_STATUS_APP_EXEC;
+		tmp_sys_mode = FW_SYS_MODE_SCANNING;
+	} else if ((report_length == 0x0A) &&
+		   ((cd->response_buf[4] & PIP1_RESP_COMMAND_ID_MASK) ==
+		    PIP1_CMD_ID_VERIFY_CONFIG_BLOCK_CRC)) {
+		tmp_mode = PIP2_STATUS_APP_EXEC;
+		tmp_sys_mode = FW_SYS_MODE_TEST;
+	}
+
+exit:
+	if (mode)
+		*mode = tmp_mode;
+	if (sys_mode)
+		*sys_mode = tmp_sys_mode;
+	pt_debug(cd->dev, DL_INFO, "%s: Return Mode=%d sys_mode=%d\n",
+		__func__, tmp_mode, tmp_sys_mode);
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_get_fw_sys_mode
+ *
+ * SUMMARY: Protected call to _pt_get_fw_sys_mode() to determine if FW scanning
+ * is active or not.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd       - pointer to core data
+ *	*sys_mode - pointer to fw system mode
+ *	*mode     - pointer to mode
+ ******************************************************************************/
+static int pt_get_fw_sys_mode(struct pt_core_data *cd, u8 *sys_mode, u8 *mode)
+{
+	int rc;
+
+	rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
+	if (rc < 0) {
+		pt_debug(cd->dev, DL_ERROR,
+				"%s: fail get exclusive ex=%p own=%p\n",
+				__func__, cd->exclusive_dev, cd->dev);
+		return rc;
+	}
+
+	rc = _pt_get_fw_sys_mode(cd, sys_mode, mode);
+
+	if (release_exclusive(cd, cd->dev) < 0)
+		pt_debug(cd->dev, DL_ERROR,
+				"%s: fail to release exclusive\n", __func__);
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_request_get_fw_sys_mode
+ *
+ * SUMMARY: Function pointer included in core_cmds to allow other modules
+ *	to request to get scan state
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*dev      - pointer to device structure
+ *	 protect  - flag to call protected or non-protected
+ *	*sys_mode - pointer to FW system mode
+ *	*mode     - pointer to mode
+ ******************************************************************************/
+static int _pt_request_get_fw_sys_mode(struct device *dev, int protect,
+		u8 *sys_mode, u8 *mode)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+
+	if (protect)
+		return pt_get_fw_sys_mode(cd, sys_mode, mode);
+
+	return _pt_get_fw_sys_mode(cd, sys_mode, mode);
+}
+
+/* Default hid descriptor to provide basic register map */
+const struct pt_hid_desc hid_desc_default = {
+	230, /* hid_desc_len */
+	HID_APP_REPORT_ID, /* packet_id */
+	0x00, /* reserved_byte */
+	0x0100, /* bcd_version */
+	0x00EC, /* report_desc_len */
+	0x0002, /* report_desc_register */
+	0x0003, /* input_register */
+	0x00FE, /* max_input_len */
+	0x0004, /* output_register */
+	0x00FE, /* max_output_len */
+	0x0005, /* command_register */
+	0x0006, /* data_register */
+	0x04B4, /* vendor_id */
+	0xC101, /* product_id */
+	0x0100, /* version_id */
+	{0x00, 0x00, 0x00, 0x00} /* reserved[4] */
+};
+
+/*******************************************************************************
+ * FUNCTION: pt_init_hid_descriptor
+ *
+ * SUMMARY: Setup default values for HID descriptor structure
+ *
+ *
+ * PARAMETERS:
+ *	*desc - pointer to the HID descriptor data read back from DUT
+ ******************************************************************************/
+static inline void pt_init_hid_descriptor(struct pt_hid_desc *desc)
+{
+	memcpy(desc, &hid_desc_default, sizeof(hid_desc_default));
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_get_hid_descriptor_
+ *
+ * SUMMARY: Send the get HID descriptor command to the DUT and load the response
+ *	into the HID descriptor structure
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd   - pointer to core data
+ *	*desc - pointer to the HID descriptor data read back from DUT
+ ******************************************************************************/
+static int pt_get_hid_descriptor_(struct pt_core_data *cd,
+		struct pt_hid_desc *desc)
+{
+	struct device *dev = cd->dev;
+	int rc = 0;
+	int t;
+	u8 cmd[2];
+
+	/*
+	 * During startup the HID descriptor is required for all future
+	 * processing. If IRQ is already asserted due to an early touch report
+	 * the report must be cleared before sending command.
+	 */
+	pt_flush_bus_if_irq_asserted(cd, PT_FLUSH_BUS_BASED_ON_LEN);
+
+	/* Read HID descriptor length and version */
+	mutex_lock(&cd->system_lock);
+	cd->hid_cmd_state = 1;
+	mutex_unlock(&cd->system_lock);
+
+	/* Set HID descriptor register */
+	memcpy(cmd, &cd->hid_core.hid_desc_register,
+		sizeof(cd->hid_core.hid_desc_register));
+
+	pt_debug(cd->dev, DL_INFO, ">>> %s: Write Buffer [%zu]",
+		__func__, sizeof(cmd));
+	pt_pr_buf(cd->dev, DL_DEBUG, cmd, sizeof(cmd), ">>> Get HID Desc");
+	rc = pt_adap_write_read_specific(cd, 2, cmd, NULL);
+	if (rc) {
+		pt_debug(dev, DL_ERROR,
+			"%s: failed to get HID descriptor, rc=%d\n",
+			__func__, rc);
+		goto error;
+	}
+
+	t = wait_event_timeout(cd->wait_q, (cd->hid_cmd_state == 0),
+		msecs_to_jiffies(PT_GET_HID_DESCRIPTOR_TIMEOUT));
+
+	if (IS_TMO(t)) {
+#ifdef TTDL_DIAGNOSTICS
+		cd->bus_transmit_error_count++;
+		pt_toggle_err_gpio(cd, PT_ERR_GPIO_I2C_TRANS);
+#endif /* TTDL_DIAGNOSTICS */
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: HID get descriptor timed out\n", __func__);
+		rc = -ETIME;
+		goto error;
+	} else {
+		cd->hw_detected = true;
+	}
+
+	/* Load the HID descriptor including all registers */
+	memcpy((u8 *)desc, cd->response_buf, sizeof(struct pt_hid_desc));
+
+	/* Check HID descriptor length and version */
+	pt_debug(dev, DL_INFO, "%s: HID len:%X HID ver:%X\n", __func__,
+		le16_to_cpu(desc->hid_desc_len),
+		le16_to_cpu(desc->bcd_version));
+
+	if (le16_to_cpu(desc->hid_desc_len) != sizeof(*desc) ||
+		le16_to_cpu(desc->bcd_version) != HID_VERSION) {
+		pt_debug(dev, DL_ERROR, "%s: Unsupported HID version\n",
+			__func__);
+		return -ENODEV;
+	}
+
+	goto exit;
+
+error:
+	mutex_lock(&cd->system_lock);
+	cd->hid_cmd_state = 0;
+	mutex_unlock(&cd->system_lock);
+exit:
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_get_hid_descriptor
+ *
+ * SUMMARY: Protected call to pt_get_hid_descriptor_()
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd   - pointer to core data
+ *	*desc - pointer to the HID descriptor data read back from DUT
+ ******************************************************************************/
+static int pt_get_hid_descriptor(struct pt_core_data *cd,
+		struct pt_hid_desc *desc)
+{
+	int rc;
+
+	rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
+	if (rc < 0) {
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: fail get exclusive ex=%p own=%p\n",
+			__func__, cd->exclusive_dev, cd->dev);
+		return rc;
+	}
+
+	rc = pt_get_hid_descriptor_(cd, desc);
+
+	if (release_exclusive(cd, cd->dev) < 0)
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: fail to release exclusive\n", __func__);
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pip2_get_version_
+ *
+ * SUMMARY: Sends a PIP2 VERSION command to the DUT and stores the data in
+ *	cd-ttdata
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd - pointer to core data
+ ******************************************************************************/
+static int pt_pip2_get_version_(struct pt_core_data *cd)
+{
+	int rc = 0;
+	int status;
+	u8 read_buf[64];
+	u16 actual_read_len;
+
+	rc = _pt_request_pip2_send_cmd(cd->dev,
+		PT_CORE_CMD_UNPROTECTED, PIP2_CMD_ID_VERSION,
+		NULL, 0, read_buf, &actual_read_len);
+	if (rc) {
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: Error Sending PIP2 VERSION Cmd rc=%d\n",
+			__func__, rc);
+		return rc;
+	}
+
+	status = read_buf[PIP2_RESP_STATUS_OFFSET];
+	if (status == 0) {
+		/* Parse the PIP2 VERSION response into ttdata */
+		pt_pip2_ver_load_ttdata(cd, actual_read_len);
+	} else {
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: Error in PIP2 VERSION Cmd status=%d\n",
+			__func__, status);
+		return status;
+	}
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pip2_get_version
+ *
+ * SUMMARY: Protected call to pt_pip2_get_version_ by exclusive
+ *	access to the DUT.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd       - pointer to core data structure
+ ******************************************************************************/
+static int pt_pip2_get_version(struct pt_core_data *cd)
+{
+	int rc;
+
+	rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
+	if (rc < 0) {
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: fail get exclusive ex=%p own=%p\n",
+			__func__, cd->exclusive_dev, cd->dev);
+		return rc;
+	}
+
+	rc = pt_pip2_get_version_(cd);
+
+	if (release_exclusive(cd, cd->dev) < 0)
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: fail to release exclusive\n", __func__);
+
+	return rc;
+}
+
+
+/*******************************************************************************
+ * FUNCTION: _pt_request_active_pip_protocol
+ *
+ * SUMMARY: Get active PIP protocol version using the PIP2 version command.
+ *	Function will return PIP version of BL or application based on
+ *	when it's called.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*dev               - pointer to device structure
+ *	 protect           - flag to run in protected mode
+ *	*pip_version_major - pointer to store PIP major version
+ *	*pip_version_minor - pointer to store PIP minor version
+ ******************************************************************************/
+int _pt_request_active_pip_protocol(struct device *dev, int protect,
+	u8 *pip_version_major, u8 *pip_version_minor)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_ttdata *ttdata = &cd->sysinfo.ttdata;
+	int rc = 0;
+	struct pt_hid_output sys_info = {
+		CREATE_PIP1_FW_CMD(PIP1_CMD_ID_GET_SYSINFO),
+		.timeout_ms = PT_PIP1_CMD_GET_SYSINFO_TIMEOUT,
+	};
+
+	pt_flush_bus_if_irq_asserted(cd, PT_FLUSH_BUS_BASED_ON_LEN);
+
+	/* Skip PIP2 command if DUT generation is confirmed */
+	if (cd->active_dut_generation == DUT_PIP1_ONLY)
+		goto skip_pip2_command;
+
+	rc = pt_pip2_get_version_(cd);
+	if (!rc) {
+		*pip_version_major = ttdata->pip_ver_major;
+		*pip_version_minor = ttdata->pip_ver_minor;
+		pt_debug(dev, DL_INFO,
+			"%s: pip_version = %d.%d\n", __func__,
+			*pip_version_major, *pip_version_minor);
+	} else {
+		/*
+		 * Legacy products do not support the pip2 protocol to get
+		 * pip version. However, they do support the "get sysinfo"
+		 * command to get pip version from FW, but the bootloader
+		 * does not support it. This function will try "get sysinfo"
+		 * command if the pip2 command failed but this cmd could also
+		 * fail if DUT is stuck in bootloader mode.
+		 */
+		pt_debug(dev, DL_INFO,
+			"%s: PIP2 no response rc = %d, try legacy cmd\n",
+			__func__, rc);
+
+skip_pip2_command:
+		rc = pt_pip1_send_output_and_wait_(cd, &sys_info);
+		if (!rc) {
+			*pip_version_minor =
+			   cd->response_buf[PIP1_SYSINFO_TTDATA_OFFSET + 1];
+			*pip_version_major =
+			   cd->response_buf[PIP1_SYSINFO_TTDATA_OFFSET];
+
+			pt_debug(dev, DL_INFO,
+				"%s: pip_version = %d.%d\n", __func__,
+				*pip_version_major, *pip_version_minor);
+		} else {
+			*pip_version_major = 0;
+			*pip_version_minor = 0;
+			pt_debug(dev, DL_ERROR,
+				"%s: pip_version Not Detected\n", __func__);
+		}
+	}
+
+	return rc;
+}
+EXPORT_SYMBOL_GPL(_pt_request_active_pip_protocol);
+
+/*******************************************************************************
+ * FUNCTION: _pt_detect_dut_generation
+ *
+ * SUMMARY: Determine the generation of device that we are communicating with:
+ *		DUT_PIP1_ONLY (Gen5 or Gen6)
+ *		DUT_PIP2_CAPABLE (TC33xx or TT7xxx)
+ *	The HID_DESC command is supported in Gen5/6 BL and FW as well as
+ *	TT/TC FW. The packet ID in the descriptor, however, is unique when
+ *	coming form the BL or the FW:
+ *		Packet_ID in BL = HID_BL_REPORT_ID (0xFF)
+ *		Packet_ID in FW = HID_APP_REPORT_ID (0xF7)
+ *	This function will return a modified status if it detects the DUT
+ *	is in the BL. In the case of a Gen5/6 BL, which also sends out a FW
+ *	reset sentinel, the status is "corrected" from a FW to BL sentinel.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *      *dev     - pointer to device structure
+ *      *status  - pointer to status bitmask
+ *      *dut_gen - pointer to store the dut_generation
+ *      *mode    - pointer to store the PT_MODE
+ ******************************************************************************/
+static int _pt_detect_dut_generation(struct device *dev,
+	u32 *status, u8 *dut_gen, enum pt_mode *mode)
+{
+	int rc = 0;
+	u8 dut_gen_tmp = DUT_UNKNOWN;
+	u8 mode_tmp = PT_MODE_UNKNOWN;
+	u8 attempt = 1;
+	u32 status_tmp = STARTUP_STATUS_START;
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_hid_desc hid_desc;
+
+	memset(&hid_desc, 0, sizeof(hid_desc));
+	rc = pt_get_hid_descriptor_(cd, &hid_desc);
+
+	while (rc && attempt < 3) {
+		attempt++;
+		usleep_range(2000, 5000);
+		rc = pt_get_hid_descriptor_(cd, &hid_desc);
+	}
+
+	if (!rc && hid_desc.packet_id == HID_BL_REPORT_ID) {
+		dut_gen_tmp = DUT_PIP1_ONLY; /* Gen5/6 BL */
+		mode_tmp = PT_MODE_BOOTLOADER;
+		status_tmp = STARTUP_STATUS_BL_RESET_SENTINEL;
+	} else if (!rc && hid_desc.packet_id == HID_APP_REPORT_ID) {
+		rc = pt_pip2_get_version_(cd);
+		if (!rc)
+			dut_gen_tmp = DUT_PIP2_CAPABLE; /* TT/TC FW */
+		else
+			dut_gen_tmp = DUT_PIP1_ONLY; /* Gen5/6 FW */
+		mode_tmp = PT_MODE_OPERATIONAL;
+		status_tmp = STARTUP_STATUS_FW_RESET_SENTINEL;
+		rc = 0; /* To return success instead of error code */
+	} else if (rc) {
+		rc = pt_pip2_get_version_(cd);
+		if (!rc) {
+			dut_gen_tmp = DUT_PIP2_CAPABLE; /* TT/TC BL */
+			mode_tmp = PT_MODE_BOOTLOADER;
+			status_tmp = STARTUP_STATUS_BL_RESET_SENTINEL;
+		}
+	}
+
+	mutex_lock(&cd->system_lock);
+	if (dut_gen)
+		*dut_gen = dut_gen_tmp;
+	if (mode)
+		*mode = mode_tmp;
+	if (status)
+		*status = status_tmp;
+	mutex_unlock(&cd->system_lock);
+
+#ifdef TTDL_DIAGNOSTICS
+	pt_debug(cd->dev, DL_INFO, "%s: Generation=%d Mode=%d\n",
+		__func__, dut_gen_tmp, mode_tmp);
+#endif /* TTDL_DIAGNOSTICS */
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_request_dut_generation
+ *
+ * SUMMARY: Function pointer included in core_cmds to allow other modules
+ *	to get current dut generation.
+ *
+ * NOTE: This function WILL NOT try to determine dut generation.
+ *
+ * RETURN:
+ *	The current dut generation.
+ *
+ * PARAMETERS:
+ *      *dev            - pointer to device structure
+ ******************************************************************************/
+static int _pt_request_dut_generation(struct device *dev)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+
+	return cd->active_dut_generation;
+}
+
+#define HW_VERSION_LEN_MAX 13
+/*******************************************************************************
+ * FUNCTION: _legacy_generate_hw_version
+ *
+ * SUMMARY: Format chip information from struct ttdata (maintained by PIP1
+ *   SYSINFO command) or struct bl_info (maintained by PIP1 BL INFORMATION
+ *   command) to the hw_version.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd         - pointer to core data
+ *	*hw_version - pointer to store the hardware version
+ ******************************************************************************/
+static int _legacy_generate_hw_version(struct pt_core_data *cd,
+				       char *hw_version)
+{
+	struct pt_ttdata *ttdata = &cd->sysinfo.ttdata;
+
+	if (cd->sysinfo.ready) {
+		scnprintf(hw_version, HW_VERSION_LEN_MAX, "%04X.FFFF.%02X",
+			 ttdata->jtag_id_h, cd->pid_for_loader);
+		return 0;
+	} else if (cd->bl_info.ready) {
+		scnprintf(hw_version, HW_VERSION_LEN_MAX, "%04X.FFFF.%02X",
+			 cd->bl_info.chip_id, cd->pid_for_loader);
+		return 0;
+	} else {
+		snprintf(hw_version, HW_VERSION_LEN_MAX, "FFFF.FFFF.FF");
+		pt_debug(cd->dev, DL_ERROR,
+			 "%s: SYSINFO and BL_INFO are not ready\n", __func__);
+		return -ENODATA;
+	}
+}
+
+/*******************************************************************************
+ * FUNCTION: _pip2_generate_hw_version
+ *
+ * SUMMARY: Format chip information from struct ttdata (maintained by PIP2
+ *   VERSION command) to the hw_version.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd         - pointer to core data
+ *	*hw_version - pointer to store the hardware version
+ ******************************************************************************/
+static int _pip2_generate_hw_version(struct pt_core_data *cd, char *hw_version)
+{
+	struct pt_ttdata *ttdata = &cd->sysinfo.ttdata;
+
+	if (cd->app_pip_ver_ready | cd->bl_pip_ver_ready) {
+		snprintf(hw_version, HW_VERSION_LEN_MAX, "%04X.%04X.%02X",
+			 ttdata->chip_id, ttdata->chip_rev, cd->pid_for_loader);
+		return 0;
+	} else {
+		snprintf(hw_version, HW_VERSION_LEN_MAX, "FFFF.FFFF.FF");
+		pt_debug(cd->dev, DL_ERROR,
+			 "%s: PIP Version are not ready\n", __func__);
+		return -ENODATA;
+	}
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_generate_hw_version
+ *
+ * SUMMARY: Wraaper function for both legacy and TT/TC products generate the
+ *   hw_version from static data.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd         - pointer to core data
+ *	*hw_version - pointer to store the hardware version
+ ******************************************************************************/
+static int pt_generate_hw_version(struct pt_core_data *cd, char *hw_version)
+{
+	int rc = 0;
+
+	if (!hw_version)
+		return -ENOMEM;
+
+	if (cd->active_dut_generation == DUT_PIP1_ONLY)
+		rc = _legacy_generate_hw_version(cd, hw_version);
+	else if (cd->active_dut_generation == DUT_PIP2_CAPABLE)
+		rc = _pip2_generate_hw_version(cd, hw_version);
+	else {
+		snprintf(hw_version, HW_VERSION_LEN_MAX, "FFFF.FFFF.FF");
+		rc = -ENODATA;
+	}
+
+	return rc;
+}
+/*******************************************************************************
+ * SUMMARY: Attempt to retrieve the HW version of the connected DUT
+ *
+ * NOTE: The calling function must ensure to free *hw_version
+ *
+ * RETURN:
+ *	0  = success
+ *	!0 = Failure
+ *
+ * PARAMETERS:
+ *      *dev        - pointer to device structure
+ *	*hw_version - pointer to where the hw_version string will be stored
+ ******************************************************************************/
+static int _pt_request_hw_version(struct device *dev, char *hw_version)
+{
+	int rc = 0;
+	u16 actual_read_len;
+	u16 pip_ver;
+	u8 rd_buf[256];
+	u8 status;
+	u8 index = PIP2_RESP_STATUS_OFFSET;
+	u8 return_data[8];
+	u8 panel_id;
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_ttdata *ttdata = &cd->sysinfo.ttdata;
+
+	if (!hw_version)
+		return -ENOMEM;
+
+	if (!cd->hw_detected) {
+		/* No HW detected */
+		rc = -ENODEV;
+		pt_debug(dev, DL_ERROR, "%s: no hardware is detected!\n",
+			 __func__);
+		goto exit_error;
+	}
+
+	memset(return_data, 0, ARRAY_SIZE(return_data));
+	/* For Parade TC or TT parts */
+	if (cd->active_dut_generation == DUT_PIP2_CAPABLE) {
+		rc = _pt_request_pip2_send_cmd(dev,
+			PT_CORE_CMD_UNPROTECTED, PIP2_CMD_ID_VERSION,
+			NULL, 0, rd_buf, &actual_read_len);
+
+		if (rc) {
+			pt_debug(dev, DL_ERROR,
+				"%s: Failed to send PIP2 VERSION cmd\n",
+				__func__);
+			goto exit_error;
+		}
+
+		status = rd_buf[index];
+		if (status == 0) {
+			pip_ver = 256 * rd_buf[index + 2] + rd_buf[index + 1];
+
+			/*
+			 * BL PIP 2.02 and greater the version fields are
+			 * swapped
+			 */
+			if (pip_ver >= 0x0202) {
+				snprintf(hw_version, HW_VERSION_LEN_MAX,
+					"%02X%02X.%02X%02X.FF",
+					rd_buf[index + 10], rd_buf[index + 9],
+					rd_buf[index + 8], rd_buf[index + 7]);
+			} else {
+				snprintf(hw_version, HW_VERSION_LEN_MAX,
+					"%02X%02X.%02X%02X.FF",
+					rd_buf[index + 8], rd_buf[index + 7],
+					rd_buf[index + 10], rd_buf[index + 9]);
+			}
+			return STATUS_SUCCESS;
+		} else {
+			rc = status;
+			pt_debug(dev, DL_WARN,
+				"%s: PIP2 VERSION cmd response error\n",
+				__func__);
+		}
+	} else if (cd->active_dut_generation == DUT_PIP1_ONLY) {
+		/*
+		 * For Parade/Cypress legacy parts the RevID and FamilyID are
+		 * hard coded to FFFF
+		 */
+		if (cd->mode == PT_MODE_OPERATIONAL) {
+			rc = pt_hid_output_get_sysinfo(cd);
+			if (!rc) {
+				panel_id =
+					cd->sysinfo.sensing_conf_data.panel_id;
+			} else {
+				panel_id = PANEL_ID_NOT_ENABLED;
+			}
+			/* In FW - simply retrieve from ttdata struct */
+			snprintf(hw_version, HW_VERSION_LEN_MAX,
+				"%04X.FFFF.%02X",
+				ttdata->jtag_id_h,
+				panel_id);
+			return STATUS_SUCCESS;
+		} else {
+			/*
+			 * Return the stored value if PT_PANEL_ID_BY_BL
+			 * is not supported while other feature is.
+			 */
+			if (cd->panel_id_support & PT_PANEL_ID_BY_BL) {
+				rc = pt_hid_output_bl_get_information(
+				    cd, return_data);
+				if (!rc) {
+					rc = pt_hid_output_bl_get_panel_id(
+					    cd, &panel_id);
+				}
+			} else
+				panel_id = cd->pid_for_loader;
+			if (!rc) {
+				snprintf(hw_version,
+					HW_VERSION_LEN_MAX,
+					"%02X%02X.FFFF.%02X",
+					return_data[3], return_data[2],
+					panel_id);
+				return STATUS_SUCCESS;
+			}
+		}
+	} else {
+		/* Unknown generation */
+		rc = -ENODEV;
+		pt_debug(dev, DL_ERROR, "%s: generation is unknown!\n",
+			 __func__);
+	}
+
+exit_error:
+	snprintf(hw_version, HW_VERSION_LEN_MAX, "FFFF.FFFF.FF");
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_start_wd_timer
+ *
+ * SUMMARY: Starts the TTDL watchdog timer if the timer interval is > 0
+ *
+ * RETURN: void
+ *
+ * PARAMETERS:
+ *      *cd - pointer to core data
+ ******************************************************************************/
+static void pt_start_wd_timer(struct pt_core_data *cd)
+{
+	if (cd->watchdog_interval < 100) {
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: WARNING: Invalid watchdog interval: %d\n",
+			__func__, cd->watchdog_interval);
+		return;
+	}
+
+	if (cd->watchdog_force_stop) {
+		pt_debug(cd->dev, DL_INFO,
+			"%s: TTDL WD Forced stop\n", __func__);
+		return;
+	}
+
+	mod_timer(&cd->watchdog_timer, jiffies +
+		msecs_to_jiffies(cd->watchdog_interval));
+	cd->watchdog_enabled = 1;
+	pt_debug(cd->dev, DL_INFO, "%s: TTDL WD Started\n", __func__);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_stop_wd_timer
+ *
+ * SUMMARY: Stops the TTDL watchdog timer if the timer interval is > 0
+ *
+ * RETURN: void
+ *
+ * PARAMETERS:
+ *      *cd - pointer to core data
+ ******************************************************************************/
+static void pt_stop_wd_timer(struct pt_core_data *cd)
+{
+	if (!cd->watchdog_interval)
+		return;
+
+	/*
+	 * Ensure we wait until the watchdog timer
+	 * running on a different CPU finishes
+	 */
+	del_timer_sync(&cd->watchdog_timer);
+	cancel_work_sync(&cd->watchdog_work);
+	del_timer_sync(&cd->watchdog_timer);
+	cd->watchdog_enabled = 0;
+	pt_debug(cd->dev, DL_INFO, "%s: TTDL WD Stopped\n", __func__);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_hw_soft_reset
+ *
+ * SUMMARY: Sends a PIP reset command to the DUT. Disable/re-enable the
+ *	TTDL watchdog around the reset to ensure the WD doesn't happen to
+ *	schedule an enum if it fires when the DUT is being reset.
+ *	This can cause a double reset.
+ *
+ *  NOTE: The WD MUST be stopped/restarted by the calling Function. Having
+ *       the WD active could cause this function to fail!
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd      - pointer to core data struct
+ *	 protect - flag to call protected or non-protected
+ ******************************************************************************/
+static int pt_hw_soft_reset(struct pt_core_data *cd, int protect)
+{
+	int rc = 0;
+
+	mutex_lock(&cd->system_lock);
+	cd->startup_status = STARTUP_STATUS_START;
+	pt_debug(cd->dev, DL_DEBUG, "%s: Startup Status Reset\n", __func__);
+	mutex_unlock(&cd->system_lock);
+	if (protect)
+		rc = pt_hid_cmd_reset(cd);
+	else
+		rc = pt_hid_cmd_reset_(cd);
+	if (rc < 0) {
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: FAILED to execute SOFT reset\n", __func__);
+		return rc;
+	}
+	pt_debug(cd->dev, DL_INFO, "%s: SOFT reset successful\n",
+		__func__);
+
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_hw_hard_reset
+ *
+ * SUMMARY: Calls the platform xres function if it exists to perform a hard
+ *	reset on the DUT by toggling the XRES gpio. Disable/re-enable the
+ *	TTDL watchdog around the reset to ensure the WD doesn't happen to
+ *	schedule an enum if it fires when the DUT is being reset.
+ *	This can cause a double reset.
+ *
+ * NOTE: The WD MUST be stopped/restarted by the calling Function. Having
+ *       the WD active could cause this function to fail!
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd - pointer to core data struct
+ ******************************************************************************/
+static int pt_hw_hard_reset(struct pt_core_data *cd)
+{
+	if (cd->cpdata->xres) {
+		cd->startup_status = STARTUP_STATUS_START;
+		pt_debug(cd->dev, DL_DEBUG, "%s: Startup Status Reset\n",
+			__func__);
+		cd->cpdata->xres(cd->cpdata, cd->dev);
+		pt_debug(cd->dev, DL_WARN, "%s: executed HARD reset\n",
+			__func__);
+		return 0;
+	}
+	pt_debug(cd->dev, DL_ERROR,
+		"%s: FAILED to execute HARD reset\n", __func__);
+
+	return -ENODEV;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_dut_reset
+ *
+ * SUMMARY: Attempts to reset the DUT by a hard reset and if that fails a
+ *	soft reset.
+ *
+ * NOTE: The WD MUST be stopped/restarted by the calling Function. Having
+ *       the WD active could cause this function to fail!
+ * NOTE: "protect" flag is only used for soft reset.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd      - pointer to core data structure
+ *	 protect - flag to call protected or non-protected
+ ******************************************************************************/
+static int pt_dut_reset(struct pt_core_data *cd, int protect)
+{
+	int rc = 0;
+
+	pt_debug(cd->dev, DL_INFO, "%s: reset hw...\n", __func__);
+	mutex_lock(&cd->system_lock);
+	cd->hid_reset_cmd_state = 1;
+	rc = pt_hw_hard_reset(cd);
+	mutex_unlock(&cd->system_lock);
+
+	if (rc == -ENODEV) {
+		mutex_lock(&cd->system_lock);
+		cd->hid_reset_cmd_state = 0;
+		mutex_unlock(&cd->system_lock);
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: Hard reset failed, try soft reset\n", __func__);
+		rc = pt_hw_soft_reset(cd, protect);
+	}
+
+	if (rc)
+		pt_debug(cd->dev, DL_ERROR, "%s: %s dev='%s' r=%d\n",
+			__func__, "Fail hw reset", dev_name(cd->dev), rc);
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_dut_reset_and_wait
+ *
+ * SUMMARY: Wrapper function for pt_dut_reset that waits for the reset to
+ *	complete
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *      *cd - pointer to core data structure
+ ******************************************************************************/
+static int pt_dut_reset_and_wait(struct pt_core_data *cd)
+{
+	int rc = 0;
+	int t;
+
+	rc = pt_dut_reset(cd, PT_CORE_CMD_UNPROTECTED);
+	if (rc < 0)
+		goto exit;
+
+	t = wait_event_timeout(cd->wait_q,
+		(cd->hid_reset_cmd_state == 0),
+		msecs_to_jiffies(PT_HID_CMD_DEFAULT_TIMEOUT));
+	if (IS_TMO(t)) {
+#ifdef TTDL_DIAGNOSTICS
+		cd->bus_transmit_error_count++;
+		pt_toggle_err_gpio(cd, PT_ERR_GPIO_I2C_TRANS);
+#endif /* TTDL_DIAGNOSTICS */
+		pt_debug(cd->dev, DL_ERROR, "%s: reset timed out\n",
+			__func__);
+		rc = -ETIME;
+		goto exit;
+	}
+
+exit:
+	return rc;
+}
+
+/*
+ * touch default parameters (from report descriptor) to resolve protocol for
+ * touch report
+ */
+const struct pt_tch_abs_params tch_hdr_default[PT_TCH_NUM_HDR] = {
+	/* byte offset, size, min, max, bit offset, report */
+	{0x00, 0x02, 0x00, 0x10000, 0x00, 0x01},	/* SCAN TIME */
+	{0x02, 0x01, 0x00, 0x20,    0x00, 0x01},	/* NUMBER OF RECORDS */
+	{0x02, 0x01, 0x00, 0x02,    0x05, 0x01},	/* LARGE OBJECT */
+	{0x03, 0x01, 0x00, 0x08,    0x00, 0x01},	/* NOISE EFFECT */
+	{0x03, 0x01, 0x00, 0x04,    0x06, 0x01},	/* REPORT_COUNTER */
+};
+
+/*
+ * button default parameters (from report descriptor) to resolve protocol for
+ * button report
+ */
+const struct pt_tch_abs_params tch_abs_default[PT_TCH_NUM_ABS] = {
+	/* byte offset, size, min, max, bit offset, report */
+	{0x02, 0x02, 0x00, 0x10000, 0x00, 0x01},	/* X */
+	{0x04, 0x02, 0x00, 0x10000, 0x00, 0x01},	/* Y */
+	{0x06, 0x01, 0x00, 0x100,   0x00, 0x01},	/* P (Z) */
+	{0x01, 0x01, 0x00, 0x20,    0x00, 0x01},	/* TOUCH ID */
+	{0x01, 0x01, 0x00, 0x04,    0x05, 0x01},	/* EVENT ID */
+	{0x00, 0x01, 0x00, 0x08,    0x00, 0x01},	/* OBJECT ID */
+	{0x01, 0x01, 0x00, 0x02,    0x07, 0x01},	/* LIFTOFF */
+	{0x07, 0x01, 0x00, 0x100,   0x00, 0x01},	/* TOUCH_MAJOR */
+	{0x08, 0x01, 0x00, 0x100,   0x00, 0x01},	/* TOUCH_MINOR */
+	{0x09, 0x01, 0x00, 0x100,   0x00, 0x01},	/* ORIENTATION */
+};
+
+/*******************************************************************************
+ * FUNCTION: pt_init_pip_report_fields
+ *
+ * SUMMARY: Setup default values for touch/button report parsing.
+ *
+ * PARAMETERS:
+ *  *cd  - pointer to core data structure
+ ******************************************************************************/
+static void pt_init_pip_report_fields(struct pt_core_data *cd)
+{
+	struct pt_sysinfo *si = &cd->sysinfo;
+
+	memcpy(si->tch_hdr, tch_hdr_default, sizeof(tch_hdr_default));
+	memcpy(si->tch_abs, tch_abs_default, sizeof(tch_abs_default));
+
+	si->desc.tch_report_id = PT_PIP_TOUCH_REPORT_ID;
+	si->desc.tch_record_size = TOUCH_REPORT_SIZE;
+	si->desc.tch_header_size = TOUCH_INPUT_HEADER_SIZE;
+	si->desc.btn_report_id = PT_PIP_CAPSENSE_BTN_REPORT_ID;
+
+	cd->features.easywake = 1;
+	cd->features.noise_metric = 1;
+	cd->features.tracking_heatmap = 1;
+	cd->features.sensor_data = 1;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_get_mode
+ *
+ * SUMMARY: Determine the current mode from the contents of a HID descriptor
+ *	message
+ *
+ * RETURN: Enum of the current mode
+ *
+ * PARAMETERS:
+ *      *cd      - pointer to the Core Data structure
+ *       protect - run command in protected mode
+ *      *mode    - pointer to store the retrieved mode
+ ******************************************************************************/
+static int pt_get_mode(struct pt_core_data *cd, struct pt_hid_desc *desc)
+{
+	if (desc->packet_id == HID_APP_REPORT_ID)
+		return PT_MODE_OPERATIONAL;
+	else if (desc->packet_id == HID_BL_REPORT_ID)
+		return PT_MODE_BOOTLOADER;
+
+	return PT_MODE_UNKNOWN;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_request_get_mode
+ *
+ * SUMMARY: Function pointer included in core_cmds to allow other modules
+ *	to determine the current mode of the DUT by use of the Get HID
+ *	Descriptor command.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *      *dev      - pointer to device structure
+ *       protect  - run command in protected mode
+ *      *mode     - pointer to store the retrieved mode
+ ******************************************************************************/
+static int _pt_request_get_mode(struct device *dev, int protect, u8 *mode)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_hid_desc hid_desc;
+	int rc = 0;
+
+	memset(&hid_desc, 0, sizeof(hid_desc));
+
+	if (protect)
+		rc = pt_get_hid_descriptor(cd, &hid_desc);
+	else
+		rc = pt_get_hid_descriptor_(cd, &hid_desc);
+
+	if (rc)
+		*mode = PT_MODE_UNKNOWN;
+	else
+		*mode = pt_get_mode(cd, &hid_desc);
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_queue_enum_
+ *
+ * SUMMARY: Queues a TTDL enum by scheduling work with the pt_enum_with_dut()
+ *	function. It won't try to add/delete sysfs node or modules.
+ *
+ * RETURN: void
+ *
+ * PARAMETERS:
+ *      *cd - pointer to core data
+ ******************************************************************************/
+static void pt_queue_enum_(struct pt_core_data *cd)
+{
+	if (cd->startup_state == STARTUP_NONE) {
+		cd->startup_state = STARTUP_QUEUED;
+
+#ifdef TTDL_DIAGNOSTICS
+		if (!cd->bridge_mode)
+			schedule_work(&cd->enum_work);
+		else
+			cd->startup_state = STARTUP_NONE;
+#else
+		schedule_work(&cd->enum_work);
+#endif
+		pt_debug(cd->dev, DL_INFO,
+			"%s: enum_work queued\n", __func__);
+	} else {
+		pt_debug(cd->dev, DL_WARN,
+			"%s: Enum not queued - startup_state = %d\n",
+			__func__, cd->startup_state);
+	}
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_queue_enum
+ *
+ * SUMMARY: Queues a TTDL enum within a mutex lock
+ *
+ * RETURN: void
+ *
+ * PARAMETERS:
+ *      *cd - pointer to core data
+ ******************************************************************************/
+static void pt_queue_enum(struct pt_core_data *cd)
+{
+	mutex_lock(&cd->system_lock);
+	pt_queue_enum_(cd);
+	mutex_unlock(&cd->system_lock);
+}
+
+static void remove_sysfs_and_modules(struct device *dev);
+/*******************************************************************************
+ * FUNCTION: pt_queue_restart
+ *
+ * SUMMARY: Queues a TTDL restart within a mutex lock
+ *
+ * RETURN: void
+ *
+ * PARAMETERS:
+ *                 *cd - pointer to core data
+ * remove_sysfs_module - True: remove all DUT relative sysfs nodes and modules
+ *                      False: will not perform remove action
+ ******************************************************************************/
+static void pt_queue_restart(struct pt_core_data *cd)
+{
+	mutex_lock(&cd->system_lock);
+	if (cd->startup_state == STARTUP_NONE) {
+		cd->startup_state = STARTUP_QUEUED;
+
+		schedule_work(&cd->ttdl_restart_work);
+		pt_debug(cd->dev, DL_INFO,
+			"%s: pt_ttdl_restart queued\n", __func__);
+	} else {
+		pt_debug(cd->dev, DL_INFO, "%s: startup_state = %d\n",
+			__func__, cd->startup_state);
+	}
+	mutex_unlock(&cd->system_lock);
+}
+
+/*******************************************************************************
+ * FUNCTION: call_atten_cb
+ *
+ * SUMMARY: Iterate over attention list call the function that registered.
+ *
+ * RETURN: void
+ *
+ * PARAMETERS:
+ *  *cd    - pointer to core data
+ *   type  - type of attention list
+ *   mode  - condition for execution
+ ******************************************************************************/
+static void call_atten_cb(struct pt_core_data *cd,
+		enum pt_atten_type type, int mode)
+{
+	struct atten_node *atten, *atten_n;
+
+	pt_debug(cd->dev, DL_DEBUG, "%s: check list type=%d mode=%d\n",
+		__func__, type, mode);
+	spin_lock(&cd->spinlock);
+	list_for_each_entry_safe(atten, atten_n,
+			&cd->atten_list[type], node) {
+		if (!mode || atten->mode & mode) {
+			spin_unlock(&cd->spinlock);
+			pt_debug(cd->dev, DL_DEBUG,
+				"%s: attention for '%s'",
+				__func__, dev_name(atten->dev));
+			atten->func(atten->dev);
+			spin_lock(&cd->spinlock);
+		}
+	}
+	spin_unlock(&cd->spinlock);
+}
+
+/*******************************************************************************
+ * FUNCTION: start_fw_upgrade
+ *
+ * SUMMARY: Calling "PT_ATTEN_LOADER" attention list that loader registered to
+ *  start firmware upgrade.
+ *
+ * RETURN:
+ *   0 = success
+ *
+ * PARAMETERS:
+ *  *data  - pointer to core data
+ ******************************************************************************/
+static int start_fw_upgrade(void *data)
+{
+	struct pt_core_data *cd = (struct pt_core_data *)data;
+
+	call_atten_cb(cd, PT_ATTEN_LOADER, 0);
+
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_put_device_into_easy_wakeup_
+ *
+ * SUMMARY: Call the enter_easywake_state function and set the device into easy
+ *  wake up state.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *cd  - pointer to core data
+ ******************************************************************************/
+static int pt_put_device_into_easy_wakeup_(struct pt_core_data *cd)
+{
+	int rc = 0;
+	u8 status = 0;
+
+
+	rc = pt_hid_output_enter_easywake_state_(cd,
+		cd->easy_wakeup_gesture, &status);
+	if (rc || status == 0)
+		return -EBUSY;
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_put_device_into_deep_sleep_
+ *
+ * SUMMARY: Call the set_power function and set the DUT to deep sleep
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = error
+ *
+ * PARAMETERS:
+ *      *cd - pointer to core data
+ ******************************************************************************/
+static int pt_put_device_into_deep_sleep_(struct pt_core_data *cd)
+{
+	int rc = 0;
+
+	rc = pt_hid_cmd_set_power_(cd, HID_POWER_SLEEP);
+	if (rc)
+		rc = -EBUSY;
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_put_device_into_deep_standby_
+ *
+ * SUMMARY: Call the set_power function and set the DUT to Deep Standby
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = error
+ *
+ * PARAMETERS:
+ *      *cd - pointer to core data
+ ******************************************************************************/
+static int pt_put_device_into_deep_standby_(struct pt_core_data *cd)
+{
+	int rc = 0;
+
+	rc = pt_hid_cmd_set_power_(cd, HID_POWER_STANDBY);
+	if (rc)
+		rc = -EBUSY;
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_core_poweroff_device_
+ *
+ * SUMMARY: Disable IRQ and HW power down the device.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *cd  - pointer to core data
+ ******************************************************************************/
+static int pt_core_poweroff_device_(struct pt_core_data *cd)
+{
+	int rc;
+
+	if (cd->irq_enabled) {
+		cd->irq_enabled = false;
+		disable_irq_nosync(cd->irq);
+	}
+
+	rc = cd->cpdata->power(cd->cpdata, 0, cd->dev, 0);
+	if (rc < 0)
+		pt_debug(cd->dev, DL_ERROR, "%s: HW Power down fails r=%d\n",
+			__func__, rc);
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_core_sleep_
+ *
+ * SUMMARY: Suspend the device with power off or deep sleep based on the
+ *  configuration in the core platform data structure.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *cd  - pointer to core data
+ ******************************************************************************/
+static int pt_core_sleep_(struct pt_core_data *cd)
+{
+	int rc = 0;
+
+	mutex_lock(&cd->system_lock);
+	pt_debug(cd->dev, DL_INFO, "%s - sleep_state %d\n", __func__, cd->sleep_state);
+	if (cd->sleep_state == SS_SLEEP_ON || cd->sleep_state == SS_SLEEPING) {
+		mutex_unlock(&cd->system_lock);
+		pt_debug(cd->dev, DL_INFO,
+			"%s - Skip slee[ state %d\n", __func__, cd->sleep_state);
+			return 0;
+	} else {
+		cd->sleep_state = SS_SLEEPING;
+	}
+	mutex_unlock(&cd->system_lock);
+
+	/* Ensure watchdog and startup works stopped */
+	pt_stop_wd_timer(cd);
+	cancel_work_sync(&cd->enum_work);
+	pt_stop_wd_timer(cd);
+
+	if (cd->cpdata->flags & PT_CORE_FLAG_POWEROFF_ON_SLEEP) {
+		pt_debug(cd->dev, DL_INFO, "%s: Entering into power off mode:\n", __func__);
+		rc = pt_core_poweroff_device_(cd);
+		if (rc)
+			pr_err("%s: Power off error detected :rc=%d\n", __func__, rc);
+	} else if (cd->cpdata->flags & PT_CORE_FLAG_DEEP_STANDBY) {
+		pt_debug(cd->dev, DL_INFO,
+			"%s: Entering into deep standby mode:\n", __func__);
+		rc = pt_put_device_into_deep_standby_(cd);
+		if (rc)
+			pr_err("%s: Deep standby error detected :rc=%d\n", __func__, rc);
+	} else {
+		pt_debug(cd->dev, DL_INFO,
+			"%s: Entering into deep sleep mode:\n", __func__);
+		rc = pt_put_device_into_deep_sleep_(cd);
+		if (rc)
+			pr_err("%s: Deep sleep error detected :rc=%d\n", __func__, rc);
+	}
+
+	mutex_lock(&cd->system_lock);
+	cd->sleep_state = SS_SLEEP_ON;
+	mutex_unlock(&cd->system_lock);
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_core_easywake_on_
+ *
+ * SUMMARY: Suspend the device with easy wake on the
+ *  configuration in the core platform data structure.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *cd  - pointer to core data
+ ******************************************************************************/
+static int pt_core_easywake_on_(struct pt_core_data *cd)
+{
+	int rc = 0;
+
+	mutex_lock(&cd->system_lock);
+
+	if (cd->sleep_state == SS_EASY_WAKING_ON) {
+		mutex_unlock(&cd->system_lock);
+		pt_debug(cd->dev, DL_INFO, "%s - Skip sleep state %d\n",
+			__func__, cd->sleep_state);
+		return 0;
+	}
+	mutex_unlock(&cd->system_lock);
+
+	/* Ensure watchdog and startup works stopped */
+	pt_stop_wd_timer(cd);
+	cancel_work_sync(&cd->enum_work);
+	pt_stop_wd_timer(cd);
+
+	if (IS_EASY_WAKE_CONFIGURED(cd->easy_wakeup_gesture)) {
+		rc = pt_put_device_into_easy_wakeup_(cd);
+		pt_debug(cd->dev, DL_INFO, "%s :Entering into easywakeup: rc=%d\n", __func__, rc);
+		if (rc)
+			pr_err("%s: Easy wakeup error detected :rc=%d\n", __func__, rc);
+	}
+	mutex_lock(&cd->system_lock);
+	cd->sleep_state = SS_EASY_WAKING_ON;
+	mutex_unlock(&cd->system_lock);
+
+	return rc;
+}
+
+
+/*******************************************************************************
+ * FUNCTION: pt_core_easywake_on
+ *
+ * SUMMARY: Protected call to pt_core_easywake_on_ by exclusive access to the DUT.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *cd  - pointer to core data
+ ******************************************************************************/
+static int pt_core_easywake_on(struct pt_core_data *cd)
+{
+	int rc = 0;
+
+	rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
+	if (rc < 0) {
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: fail get exclusive ex=%p own=%p\n",
+			__func__, cd->exclusive_dev, cd->dev);
+		return rc;
+	}
+
+	rc = pt_core_easywake_on_(cd);
+
+	if (release_exclusive(cd, cd->dev) < 0)
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: fail to release exclusive\n", __func__);
+	else
+		pt_debug(cd->dev, DL_DEBUG, "%s: pass release exclusive\n",
+			__func__);
+
+	return rc;
+}
+
+
+/*******************************************************************************
+ * FUNCTION: pt_core_sleep
+ *
+ * SUMMARY: Protected call to pt_core_sleep_ by exclusive access to the DUT.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *cd  - pointer to core data
+ ******************************************************************************/
+static int pt_core_sleep(struct pt_core_data *cd)
+{
+	int rc = 0;
+
+	rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
+	if (rc < 0) {
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: fail get exclusive ex=%p own=%p\n",
+			__func__, cd->exclusive_dev, cd->dev);
+		return rc;
+	}
+
+	rc = pt_core_sleep_(cd);
+
+	if (release_exclusive(cd, cd->dev) < 0)
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: fail to release exclusive\n", __func__);
+	else
+		pt_debug(cd->dev, DL_DEBUG, "%s: pass release exclusive\n",
+			__func__);
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_wakeup_host
+ *
+ * SUMMARY: Check wake up report and call the PT_ATTEN_WAKE attention list.
+ *
+ * NOTE: TSG5 EasyWake and TSG6 EasyWake use different protocol.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *cd  - pointer to core data
+ ******************************************************************************/
+static int pt_wakeup_host(struct pt_core_data *cd)
+{
+#ifndef EASYWAKE_TSG6
+	/* TSG5 EasyWake */
+	int rc = 0;
+	int event_id;
+	int size = get_unaligned_le16(&cd->input_buf[0]);
+
+	/* Validate report */
+	if (size != 4 || cd->input_buf[2] != 4)
+		rc = -EINVAL;
+
+	event_id = cd->input_buf[3];
+
+	pt_debug(cd->dev, DL_INFO, "%s: e=%d, rc=%d\n",
+		__func__, event_id, rc);
+
+	if (rc) {
+		pt_core_sleep_(cd);
+		goto exit;
+	}
+
+	/* attention WAKE */
+	call_atten_cb(cd, PT_ATTEN_WAKE, 0);
+exit:
+	return rc;
+#else
+	/* TSG6 FW1.3 EasyWake */
+	int rc = 0;
+	int i = 0;
+	int report_length;
+
+	/* Validate report */
+	if (cd->input_buf[2] != PT_PIP_WAKEUP_REPORT_ID) {
+		rc = -EINVAL;
+		pt_core_sleep_(cd);
+		goto exit;
+	}
+
+	/* Get gesture id and gesture data length */
+	cd->gesture_id = cd->input_buf[3];
+	report_length = (cd->input_buf[1] << 8) | (cd->input_buf[0]);
+	cd->gesture_data_length = report_length - 4;
+
+	pt_debug(cd->dev, DL_INFO,
+		"%s: gesture_id = %d, gesture_data_length = %d\n",
+		__func__, cd->gesture_id, cd->gesture_data_length);
+
+	for (i = 0; i < cd->gesture_data_length; i++)
+		cd->gesture_data[i] = cd->input_buf[4 + i];
+
+	/* attention WAKE */
+	call_atten_cb(cd, PT_ATTEN_WAKE, 0);
+exit:
+	return rc;
+#endif
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_get_touch_axis
+ *
+ * SUMMARY: Function to calculate touch axis
+ *
+ * PARAMETERS:
+ *     *cd      - pointer to core data structure
+ *     *axis    - pointer to axis calculation result
+ *      size    - size in bytes
+ *      max     - max value of result
+ *     *xy_data - pointer to input data to be parsed
+ *      bofs    - bit offset
+ ******************************************************************************/
+static void pt_get_touch_axis(struct pt_core_data *cd,
+	int *axis, int size, int max, u8 *data, int bofs)
+{
+	int nbyte;
+	int next;
+
+	for (nbyte = 0, *axis = 0, next = 0; nbyte < size; nbyte++) {
+		*axis = *axis + ((data[next] >> bofs) << (nbyte * 8));
+		next++;
+	}
+
+	*axis &= max - 1;
+}
+
+/*******************************************************************************
+ * FUNCTION: move_tracking_heatmap_data
+ *
+ * SUMMARY: Move the valid tracking heatmap data from the input buffer into the
+ *	system information structure, xy_mode and xy_data.
+ *	- If TTHE_TUNER_SUPPORT is defined print the raw sensor data into
+ *	the tthe_tuner sysfs node under the label "THM"
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd - pointer to core data
+ *	*si - pointer to the system information structure
+ ******************************************************************************/
+static int move_tracking_heatmap_data(struct pt_core_data *cd,
+	struct pt_sysinfo *si)
+{
+#ifdef TTHE_TUNER_SUPPORT
+	int size = get_unaligned_le16(&cd->input_buf[0]);
+
+	if (size)
+		tthe_print(cd, cd->input_buf, size, "THM=");
+#endif
+	memcpy(si->xy_mode, cd->input_buf, SENSOR_HEADER_SIZE);
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: move_sensor_data
+ *
+ * SUMMARY: Move the valid sensor data from the input buffer into the system
+ *	information structure, xy_mode and xy_data.
+ *	- If TTHE_TUNER_SUPPORT is defined print the raw sensor data into
+ *	the tthe_tuner sysfs node under the label "sensor_monitor"
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd - pointer to core data
+ *	*si - pointer to the system information structure
+ ******************************************************************************/
+static int move_sensor_data(struct pt_core_data *cd,
+	struct pt_sysinfo *si)
+{
+#ifdef TTHE_TUNER_SUPPORT
+	int size = get_unaligned_le16(&cd->input_buf[0]);
+
+	if (size)
+		tthe_print(cd, cd->input_buf, size, "sensor_monitor=");
+#endif
+	memcpy(si->xy_mode, cd->input_buf, SENSOR_HEADER_SIZE);
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: move_button_data
+ *
+ * SUMMARY: Move the valid button data from the input buffer into the system
+ *	information structure, xy_mode and xy_data.
+ *	- If TTHE_TUNER_SUPPORT is defined print the raw button data into
+ *	the tthe_tuner sysfs node under the label "OpModeData"
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd - pointer to core data
+ *	*si - pointer to the system information structure
+ ******************************************************************************/
+static int move_button_data(struct pt_core_data *cd,
+	struct pt_sysinfo *si)
+{
+#ifdef TTHE_TUNER_SUPPORT
+	int size = get_unaligned_le16(&cd->input_buf[0]);
+
+	if (size)
+		tthe_print(cd, cd->input_buf, size, "OpModeData=");
+#endif
+	memcpy(si->xy_mode, cd->input_buf, BTN_INPUT_HEADER_SIZE);
+	pt_pr_buf(cd->dev, DL_INFO, (u8 *)si->xy_mode, BTN_INPUT_HEADER_SIZE,
+		"xy_mode");
+
+	memcpy(si->xy_data, &cd->input_buf[BTN_INPUT_HEADER_SIZE],
+		BTN_REPORT_SIZE);
+	pt_pr_buf(cd->dev, DL_INFO, (u8 *)si->xy_data, BTN_REPORT_SIZE,
+		"xy_data");
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: move_touch_data
+ *
+ * SUMMARY: Move the valid touch data from the input buffer into the system
+ *	information structure, xy_mode and xy_data.
+ *	- If TTHE_TUNER_SUPPORT is defined print the raw touch data into
+ *	the tthe_tuner sysfs node under the label "OpModeData"
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd - pointer to core data
+ *	*si - pointer to the system information structure
+ ******************************************************************************/
+static int move_touch_data(struct pt_core_data *cd, struct pt_sysinfo *si)
+{
+	int max_tch = si->sensing_conf_data.max_tch;
+	int num_cur_tch;
+	int length;
+	struct pt_tch_abs_params *tch = &si->tch_hdr[PT_TCH_NUM];
+#ifdef TTHE_TUNER_SUPPORT
+	int size = get_unaligned_le16(&cd->input_buf[0]);
+
+	if (size)
+		tthe_print(cd, cd->input_buf, size, "OpModeData=");
+#endif
+
+	memcpy(si->xy_mode, cd->input_buf, si->desc.tch_header_size);
+	pt_pr_buf(cd->dev, DL_INFO, (u8 *)si->xy_mode,
+		si->desc.tch_header_size, "xy_mode");
+
+	pt_get_touch_axis(cd, &num_cur_tch, tch->size,
+			tch->max, si->xy_mode + 3 + tch->ofs, tch->bofs);
+	if (unlikely(num_cur_tch > max_tch))
+		num_cur_tch = max_tch;
+
+	length = num_cur_tch * si->desc.tch_record_size;
+
+	memcpy(si->xy_data, &cd->input_buf[si->desc.tch_header_size], length);
+	pt_pr_buf(cd->dev, DL_INFO, (u8 *)si->xy_data, length, "xy_data");
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: move_hid_pen_data
+ *
+ * SUMMARY: TODO Move the valid pen data from the input buffer into the system
+ *	information structure, xy_mode and xy_data.
+ *	- If TTHE_TUNER_SUPPORT is defined print the raw pen data into
+ *	the tthe_tuner sysfs node under the label "HID" starting with the
+ *	report ID.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd - pointer to core data
+ *	*si - pointer to the system information structure
+ ******************************************************************************/
+static int move_hid_pen_data(struct pt_core_data *cd, struct pt_sysinfo *si)
+{
+#ifdef TTHE_TUNER_SUPPORT
+	int size = get_unaligned_le16(&cd->input_buf[0]);
+
+	if (size) {
+		/*
+		 * HID over USB does not require the two byte length field, so
+		 * this should print from input_buf[2] but to keep both finger
+		 * and pen reports the same the length is included
+		 */
+		if (cd->tthe_hid_usb_format == PT_FEATURE_ENABLE)
+			tthe_print(cd, &(cd->input_buf[2]), size - 2,
+				"HID-USB=");
+		else
+			tthe_print(cd, &(cd->input_buf[0]), size,
+				"HID-I2C=");
+	}
+#endif
+	pt_pr_buf(cd->dev, DL_INFO, (u8 *)&(cd->input_buf[0]), size, "HID Pen");
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: parse_touch_input
+ *
+ * SUMMARY: Parse the touch report and take action based on the touch
+ *	report_id.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd   - pointer to core data
+ *	 size - size of touch record
+ ******************************************************************************/
+static int parse_touch_input(struct pt_core_data *cd, int size)
+{
+	struct pt_sysinfo *si = &cd->sysinfo;
+	int report_id = cd->input_buf[2];
+	int rc = -EINVAL;
+
+	pt_debug(cd->dev, DL_DEBUG, "%s: Received touch report\n",
+		__func__);
+	if (!si->ready) {
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: Need system information to parse touches\n",
+			__func__);
+		return 0;
+	}
+
+	if (!si->xy_mode || !si->xy_data)
+		return rc;
+
+	if (report_id == PT_PIP_TOUCH_REPORT_ID)
+		rc = move_touch_data(cd, si);
+	else if (report_id == PT_HID_PEN_REPORT_ID)
+		rc = move_hid_pen_data(cd, si);
+	else if (report_id == PT_PIP_CAPSENSE_BTN_REPORT_ID)
+		rc = move_button_data(cd, si);
+	else if (report_id == PT_PIP_SENSOR_DATA_REPORT_ID)
+		rc = move_sensor_data(cd, si);
+	else if (report_id == PT_PIP_TRACKING_HEATMAP_REPORT_ID)
+		rc = move_tracking_heatmap_data(cd, si);
+
+	if (rc)
+		return rc;
+
+	/* attention IRQ */
+	call_atten_cb(cd, PT_ATTEN_IRQ, cd->mode);
+
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: parse_command_input
+ *
+ * SUMMARY: Move the response data from the input buffer to the response buffer
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd   - pointer to core data
+ *	 size - size of response data
+ ******************************************************************************/
+static int parse_command_input(struct pt_core_data *cd, int size)
+{
+	pt_debug(cd->dev, DL_DEBUG, "%s: Received cmd interrupt\n",
+		__func__);
+
+	memcpy(cd->response_buf, cd->input_buf, size);
+#if defined(TTHE_TUNER_SUPPORT) && defined(TTDL_DIAGNOSTICS)
+	if (size && cd->show_tt_data) {
+		if (cd->pip2_prot_active)
+			tthe_print(cd, cd->input_buf, size, "TT_DATA_PIP2=");
+		else
+			tthe_print(cd, cd->input_buf, size, "TT_DATA=");
+	}
+#endif
+
+	mutex_lock(&cd->system_lock);
+	cd->hid_cmd_state = 0;
+	mutex_unlock(&cd->system_lock);
+	wake_up(&cd->wait_q);
+
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_allow_enumeration
+ *
+ * SUMMARY: Determine if an enumeration or fully re-probe should perform when
+ *  FW sentinel is seen.
+ *
+ * RETURN:
+ *	true  = allow enumeration or fully re-probe
+ *	false = skip enumeration and fully re-probe
+ *
+ * PARAMETERS:
+ *	*cd - pointer to core data
+ ******************************************************************************/
+static inline bool pt_allow_enumeration(struct pt_core_data *cd)
+{
+	if ((cd->panel_id_support & PT_PANEL_ID_BY_SYS_INFO) &&
+		(!cd->hid_reset_cmd_state) &&
+	    (cd->core_probe_complete) &&
+	    (cd->hid_cmd_state != PIP1_CMD_ID_START_BOOTLOADER + 1) &&
+	    (cd->hid_cmd_state != PIP1_BL_CMD_ID_LAUNCH_APP + 1) &&
+	    (cd->mode == PT_MODE_OPERATIONAL)) {
+		return true;
+	}
+
+	if ((!cd->hid_reset_cmd_state) &&
+	    (cd->core_probe_complete) &&
+	    (cd->hid_cmd_state != PIP1_CMD_ID_START_BOOTLOADER + 1) &&
+	    (cd->hid_cmd_state != PIP1_BL_CMD_ID_LAUNCH_APP + 1) &&
+	    (cd->active_dut_generation != DUT_PIP1_ONLY)) {
+		return true;
+	}
+
+	pt_debug(cd->dev, DL_INFO,
+		"%s: Dissallow - %s=%d %s=%d %s=0x%02X %s=%d\n",
+		__func__,
+		"hid_reset_cmd_state(0)", cd->hid_reset_cmd_state,
+		"core_probe_complete(1)", cd->core_probe_complete,
+		"hid_cmd_state(Not 0x02 or 0x39)", cd->hid_cmd_state,
+		"active_dut_gen(0,2)", cd->active_dut_generation);
+	return false;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_is_touch_report
+ *
+ * SUMMARY: Determine if a report ID should be treated as a touch report
+ *
+ * RETURN:
+ *	true  = report ID is a touch report
+ *	false = report ID is not a touch report
+ *
+ * PARAMETERS:
+ *	id - Report ID
+ ******************************************************************************/
+static bool pt_is_touch_report(int id)
+{
+	if (id == PT_PIP_TOUCH_REPORT_ID ||
+	    id == PT_HID_PEN_REPORT_ID ||
+	    id == PT_PIP_CAPSENSE_BTN_REPORT_ID ||
+	    id == PT_PIP_SENSOR_DATA_REPORT_ID ||
+	    id == PT_PIP_TRACKING_HEATMAP_REPORT_ID)
+		return true;
+	else
+		return false;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_parse_input
+ *
+ * SUMMARY: Parse the input data read from DUT due to IRQ. Handle data based
+ *	on if its a response to a command or asynchronous touch data / reset
+ *	sentinel. PIP2.x messages have additional error checking that is
+ *	parsed (SEQ match from cmd to rsp, CRC valid).
+ *	Look for special packets based on unique lengths:
+ *		0 bytes  - APP(FW) reset sentinel or Gen5/6 BL sentinel
+ *		2 bytes  - Empty buffer (PIP 1.7 and earlier)
+ *		11 bytes - possible PIP2.x reset sentinel (TAG and SEQ must = 0)
+ *		0xFFXX   - Empty buffer (PIP 1.7+)
+ *	Queue a startup after any asynchronous FW reset sentinel is seen, unless
+ *	the initial probe has not yet been done.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd - pointer to core data
+ ******************************************************************************/
+static int pt_parse_input(struct pt_core_data *cd)
+{
+	int report_id;
+	int cmd_id;
+	int is_command = 0;
+	int size;
+	int print_size;
+	bool touch_report = true;
+	unsigned short calc_crc;
+	unsigned short resp_crc;
+
+	cd->fw_sys_mode_in_standby_state = false;
+	size = get_unaligned_le16(&cd->input_buf[0]);
+	print_size = size;
+	pt_debug(cd->dev, DL_DEBUG, "<<< %s: IRQ Triggered, read len [%d]\n",
+			__func__, print_size);
+	if (print_size <= PT_MAX_INPUT)
+		pt_pr_buf(cd->dev, DL_DEBUG, cd->input_buf, print_size,
+			"<<< Read buf");
+	if (size == 0 ||
+	   (size == 11 &&
+	   (cd->input_buf[PIP2_RESP_SEQUENCE_OFFSET] &
+	    PIP2_RESP_SEQUENCE_MASK) == 0 &&
+	   (cd->input_buf[PIP2_RESP_REPORT_ID_OFFSET] &
+	    PIP2_CMD_COMMAND_ID_MASK) ==
+	   PIP2_CMD_ID_STATUS)) {
+		touch_report = false;
+		cd->hw_detected = true;
+		cd->bl_pip_ver_ready = false;
+		cd->app_pip_ver_ready = false;
+		if (size == 0) {
+			mutex_lock(&cd->system_lock);
+			cd->pip2_prot_active = false;
+			if (cd->active_dut_generation == DUT_PIP1_ONLY) {
+				/*
+				 * For Gen5/6 this sentinel could be from
+				 * the BL or FW. Attempt to set the correct
+				 * mode based on the previous PIP command.
+				 */
+				if (cd->hid_cmd_state ==
+				    PIP1_BL_CMD_ID_LAUNCH_APP + 1) {
+					cd->mode = PT_MODE_OPERATIONAL;
+					cd->startup_status =
+					    STARTUP_STATUS_FW_RESET_SENTINEL;
+				} else if (cd->hid_cmd_state ==
+					PIP1_CMD_ID_START_BOOTLOADER + 1 ||
+					cd->hid_reset_cmd_state) {
+					cd->mode = PT_MODE_BOOTLOADER;
+					cd->startup_status =
+					    STARTUP_STATUS_BL_RESET_SENTINEL;
+				} else {
+					cd->mode = PT_MODE_UNKNOWN;
+					cd->startup_status =
+						STARTUP_STATUS_START;
+				}
+				cd->fw_system_mode = FW_SYS_MODE_UNDEFINED;
+				pt_debug(cd->dev, DL_INFO,
+					"%s: ATM Gen5/6 %s sentinel received\n",
+					__func__,
+					(cd->mode == PT_MODE_OPERATIONAL ?
+					"FW" :
+					(cd->mode == PT_MODE_BOOTLOADER ?
+					"BL" : "Unknown")));
+			} else {
+				cd->mode = PT_MODE_OPERATIONAL;
+				cd->fw_system_mode = FW_SYS_MODE_BOOT;
+				cd->startup_status =
+					STARTUP_STATUS_FW_RESET_SENTINEL;
+				pt_debug(cd->dev, DL_INFO,
+					"%s: ATM PT/TT FW sentinel received\n",
+					__func__);
+			}
+			mutex_unlock(&cd->system_lock);
+			if (pt_allow_enumeration(cd)) {
+				if (cd->active_dut_generation == DUT_UNKNOWN) {
+					pt_debug(cd->dev, DL_INFO,
+					"%s: Queue Restart\n", __func__);
+					pt_queue_restart(cd);
+				} else {
+					pt_debug(cd->dev, DL_INFO,
+					"%s: Queue Enum\n", __func__);
+					pt_queue_enum(cd);
+				}
+			} else {
+				pt_debug(cd->dev, DL_INFO,
+					"%s: Sentinel - No Queued Action\n",
+					__func__);
+			}
+
+		} else {
+			/* Sentinel must be from TT/TC BL */
+			mutex_lock(&cd->system_lock);
+			cd->pip2_prot_active = true;
+			cd->startup_status = STARTUP_STATUS_BL_RESET_SENTINEL;
+			cd->mode = PT_MODE_BOOTLOADER;
+			cd->sysinfo.ready = false;
+			mutex_unlock(&cd->system_lock);
+			pt_debug(cd->dev, DL_INFO,
+				"%s: BL Reset sentinel received\n", __func__);
+			if (cd->flashless_dut &&
+			    cd->flashless_auto_bl == PT_ALLOW_AUTO_BL) {
+				pt_debug(cd->dev, DL_INFO,
+					"%s: BL to RAM for flashless DUT\n",
+					__func__);
+				kthread_run(start_fw_upgrade, cd, "pt_loader");
+			}
+		}
+		mutex_lock(&cd->system_lock);
+		memcpy(cd->response_buf, cd->input_buf, 2);
+		if (!cd->hid_reset_cmd_state && !cd->hid_cmd_state) {
+			mutex_unlock(&cd->system_lock);
+			pt_debug(cd->dev, DL_WARN,
+				"%s: Device Initiated Reset\n", __func__);
+			wake_up(&cd->wait_q);
+			return 0;
+		}
+
+		cd->hid_reset_cmd_state = 0;
+		if (cd->hid_cmd_state == PIP1_CMD_ID_START_BOOTLOADER + 1 ||
+		    cd->hid_cmd_state == PIP1_BL_CMD_ID_LAUNCH_APP + 1 ||
+		    cd->hid_cmd_state == PIP1_CMD_ID_USER_CMD + 1)
+			cd->hid_cmd_state = 0;
+		wake_up(&cd->wait_q);
+		mutex_unlock(&cd->system_lock);
+		return 0;
+	} else if (size == 2 || size >= PT_PIP_1P7_EMPTY_BUF) {
+		/*
+		 * This debug message below is used by PBATS to calculate the
+		 * time from the last lift off IRQ to when FW exits LFT mode.
+		 */
+		touch_report = false;
+		pt_debug(cd->dev, DL_WARN,
+			"%s: DUT - Empty buffer detected\n", __func__);
+		return 0;
+	} else if (size > PT_MAX_INPUT || size < 0) {
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: DUT - Unexpected len field in active bus data!\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	if (cd->pip2_prot_active) {
+		pt_debug(cd->dev, DL_DEBUG,
+			"%s: Decode PIP2.x Response\n", __func__);
+
+		/* PIP2 does not have a report id, hard code it */
+		report_id = 0x00;
+		cmd_id = cd->input_buf[PIP2_RESP_COMMAND_ID_OFFSET];
+		calc_crc = crc_ccitt_calculate(cd->input_buf, size - 2);
+		resp_crc  = cd->input_buf[size - 2] << 8;
+		resp_crc |= cd->input_buf[size - 1];
+		if ((cd->pip2_cmd_tag_seq !=
+			(cd->input_buf[PIP2_RESP_SEQUENCE_OFFSET] & 0x0F)) &&
+			(resp_crc != calc_crc) &&
+			((cd->input_buf[PIP1_RESP_REPORT_ID_OFFSET]
+				== PT_PIP_TOUCH_REPORT_ID) ||
+			(cd->input_buf[PIP1_RESP_REPORT_ID_OFFSET]
+				== PT_PIP_CAPSENSE_BTN_REPORT_ID))) {
+			pt_debug(cd->dev, DL_WARN,
+				"%s: %s %d %s\n",
+				__func__,
+				"Received PIP1 report id =",
+				cd->input_buf[PIP1_RESP_REPORT_ID_OFFSET],
+				"when expecting a PIP2 report - IGNORE report");
+			return 0;
+		}
+		is_command = 1;
+		touch_report = false;
+	} else {
+		report_id = cd->input_buf[PIP1_RESP_REPORT_ID_OFFSET];
+		cmd_id = cd->input_buf[PIP1_RESP_COMMAND_ID_OFFSET];
+	}
+
+#ifdef TTDL_DIAGNOSTICS
+	pt_debug(cd->dev, DL_DEBUG,
+		"%s: pip2 = %d report_id: 0x%02X, cmd_code: 0x%02X\n",
+		__func__, cd->pip2_prot_active, report_id,
+		(cmd_id & PIP2_CMD_COMMAND_ID_MASK));
+#endif /* TTDL_DIAGNOSTICS */
+
+	if (report_id == PT_PIP_WAKEUP_REPORT_ID) {
+		pt_wakeup_host(cd);
+#ifdef TTHE_TUNER_SUPPORT
+		tthe_print(cd, cd->input_buf, size, "TT_WAKEUP=");
+#endif
+		return 0;
+	}
+	mod_timer_pending(&cd->watchdog_timer, jiffies +
+			msecs_to_jiffies(cd->watchdog_interval));
+
+	/*
+	 * If it is PIP2 response, the report_id has been set to 0,
+	 * so it will not be parsed as a touch packet.
+	 */
+	if (!pt_is_touch_report(report_id)) {
+		is_command = 1;
+		touch_report = false;
+	}
+	if (unlikely(is_command)) {
+		parse_command_input(cd, size);
+		return 0;
+	}
+	if (touch_report)
+		parse_touch_input(cd, size);
+
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_read_input
+ *
+ * SUMMARY: Reads incoming data off of the active bus
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *      *cd - pointer to core data
+ ******************************************************************************/
+static int pt_read_input(struct pt_core_data *cd)
+{
+	struct device *dev = cd->dev;
+	int rc = 0;
+	int t;
+	int retry = PT_BUS_READ_INPUT_RETRY_COUNT;
+
+	/*
+	 * Workaround for easywake failure
+	 * Interrupt for easywake, wait for bus controller to wake
+	 */
+	mutex_lock(&cd->system_lock);
+
+	if (IS_EASY_WAKE_CONFIGURED(cd->easy_wakeup_gesture)) {
+		if (cd->sleep_state == SS_SLEEP_ON || cd->sleep_state == SS_EASY_WAKING_ON) {
+			mutex_unlock(&cd->system_lock);
+			if (!dev->power.is_suspended)
+				goto read;
+			t = wait_event_timeout(cd->wait_q,
+				(cd->wait_until_wake == 1),
+				msecs_to_jiffies(2000));
+#ifdef TTDL_DIAGNOSTICS
+			if (IS_TMO(t)) {
+				cd->bus_transmit_error_count++;
+				pt_toggle_err_gpio(cd, PT_ERR_GPIO_I2C_TRANS);
+				pt_debug(dev, DL_ERROR,
+					"%s: !!!I2C Transmission Error %d\n",
+					__func__,
+					cd->bus_transmit_error_count);
+			}
+#else
+			if (IS_TMO(t))
+				pt_queue_enum(cd);
+#endif /* TTDL_DIAGNOSTICS */
+			goto read;
+		}
+	}
+	mutex_unlock(&cd->system_lock);
+
+read:
+	/* Try reading up to 'retry' times */
+	while (retry-- != 0) {
+		rc = pt_adap_read_default_nosize(cd, cd->input_buf,
+			PT_MAX_INPUT);
+		if (!rc) {
+			pt_debug(dev, DL_DEBUG,
+				"%s: Read input successfully\n", __func__);
+			goto read_exit;
+		}
+		usleep_range(1000, 2000);
+	}
+	pt_debug(dev, DL_ERROR,
+		"%s: Error getting report, rc=%d\n", __func__, rc);
+
+read_exit:
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_irq
+ *
+ * SUMMARY: Process all detected interrupts
+ *
+ * RETURN:
+ *	IRQ_HANDLED - Finished processing the interrupt
+ *
+ * PARAMETERS:
+ *       irq    - IRQ number
+ *	*handle - pointer to core data struct
+ ******************************************************************************/
+irqreturn_t pt_irq(int irq, void *handle)
+{
+	struct pt_core_data *cd = handle;
+	int rc = 0;
+
+	if (!pt_check_irq_asserted(cd))
+		return IRQ_HANDLED;
+
+	rc = pt_read_input(cd);
+#ifdef TTDL_DIAGNOSTICS
+	cd->irq_count++;
+
+	/* Used to calculate T-Refresh */
+	if (cd->t_refresh_active) {
+		if (cd->t_refresh_count == 0) {
+			cd->t_refresh_time = jiffies;
+			cd->t_refresh_count++;
+		} else if (cd->t_refresh_count < cd->t_refresh_total) {
+			cd->t_refresh_count++;
+		} else {
+			cd->t_refresh_active = 0;
+			cd->t_refresh_time =
+			jiffies_to_msecs(jiffies - cd->t_refresh_time);
+		}
+	}
+#endif /* TTDL_DIAGNOSTICS */
+
+	if (!rc)
+		pt_parse_input(cd);
+
+	return IRQ_HANDLED;
+}
+
+
+/*******************************************************************************
+ * FUNCTION: _pt_subscribe_attention
+ *
+ * SUMMARY: Function pointer included in core_cmds to allow other modules
+ *	to subscribe themselves into the TTDL attention list
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *      *dev  - pointer to device structure
+ *	 type - attention type enum
+ *	*id   - ID of the module calling this function
+ *	*func - callback function pointer to be called when notified
+ *	 mode - attention mode
+ ******************************************************************************/
+int _pt_subscribe_attention(struct device *dev,
+	enum pt_atten_type type, char *id, int (*func)(struct device *),
+	int mode)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct atten_node *atten, *atten_new;
+
+	atten_new = kzalloc(sizeof(*atten_new), GFP_KERNEL);
+	if (!atten_new)
+		return -ENOMEM;
+
+	pt_debug(cd->dev, DL_INFO, "%s from '%s'\n", __func__,
+		dev_name(cd->dev));
+
+	spin_lock(&cd->spinlock);
+	list_for_each_entry(atten, &cd->atten_list[type], node) {
+		if (atten->id == id && atten->mode == mode) {
+			spin_unlock(&cd->spinlock);
+			kfree(atten_new);
+			pt_debug(cd->dev, DL_INFO, "%s: %s=%p %s=%d\n",
+				 __func__,
+				 "already subscribed attention",
+				 dev, "mode", mode);
+
+			return 0;
+		}
+	}
+
+	atten_new->id = id;
+	atten_new->dev = dev;
+	atten_new->mode = mode;
+	atten_new->func = func;
+
+	list_add(&atten_new->node, &cd->atten_list[type]);
+	spin_unlock(&cd->spinlock);
+
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_unsubscribe_attention
+ *
+ * SUMMARY: Function pointer included in core_cmds to allow other modules
+ *	to unsubscribe themselves from the TTDL attention list
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *      *dev  - pointer to device structure
+ *	 type - attention type enum
+ *	*id   - ID of the module calling this function
+ *	*func - function pointer
+ *	 mode - attention mode
+ ******************************************************************************/
+int _pt_unsubscribe_attention(struct device *dev,
+	enum pt_atten_type type, char *id, int (*func)(struct device *),
+	int mode)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct atten_node *atten, *atten_n;
+
+	spin_lock(&cd->spinlock);
+	list_for_each_entry_safe(atten, atten_n, &cd->atten_list[type], node) {
+		if (atten->id == id && atten->mode == mode) {
+			list_del(&atten->node);
+			spin_unlock(&cd->spinlock);
+			pt_debug(cd->dev, DL_DEBUG, "%s: %s=%p %s=%d\n",
+				__func__,
+				"unsub for atten->dev", atten->dev,
+				"atten->mode", atten->mode);
+			kfree(atten);
+			return 0;
+		}
+	}
+	spin_unlock(&cd->spinlock);
+
+	return -ENODEV;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_request_exclusive
+ *
+ * SUMMARY: Function pointer included in core_cmds to allow other modules
+ *	to request exclusive access
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *      *dev        - pointer to device structure
+ *	 timeout_ms - timeout to wait for exclusive access
+ ******************************************************************************/
+static int _pt_request_exclusive(struct device *dev,
+		int timeout_ms)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+
+	return request_exclusive(cd, (void *)dev, timeout_ms);
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_release_exclusive
+ *
+ * SUMMARY: Function pointer included in core_cmds to allow other modules
+ *	to release exclusive access
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *      *dev - pointer to device structure
+ ******************************************************************************/
+static int _pt_release_exclusive(struct device *dev)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+
+	return release_exclusive(cd, (void *)dev);
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_request_reset
+ *
+ * SUMMARY: Function pointer included in core_cmds to allow other modules
+ *	to request the DUT to be reset. Function returns err if refused or
+ *	timeout occurs (Note: core uses fixed timeout period).
+ *
+ * NOTE: Function blocks until ISR occurs.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *      *dev     - pointer to device structure
+ *	 protect - flag to call protected or non-protected
+ ******************************************************************************/
+static int _pt_request_reset(struct device *dev, int protect)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	int rc;
+
+	rc = pt_dut_reset(cd, protect);
+	if (rc < 0) {
+		pt_debug(cd->dev, DL_ERROR, "%s: Error on h/w reset r=%d\n",
+			__func__, rc);
+	}
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_request_enum
+ *
+ * SUMMARY: Function pointer included in core_cmds to allow other modules
+ *	to request TTDL to queue an enum. This function will return err
+ *	if refused; if no error then enum has completed and system is in
+ *	normal operation mode.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *      *dev - pointer to device structure
+ *	 wait - boolean to determine if to wait for startup event
+ ******************************************************************************/
+static int _pt_request_enum(struct device *dev, bool wait)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+
+	pt_queue_enum(cd);
+
+	if (wait)
+		wait_event_timeout(cd->wait_q,
+			cd->startup_state == STARTUP_NONE,
+			msecs_to_jiffies(PT_REQUEST_ENUM_TIMEOUT));
+
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_request_sysinfo
+ *
+ * SUMMARY: Function pointer included in core_cmds to allow other modules
+ *	to request the pointer to the system information structure. This
+ *	function will return NULL if sysinfo has not been acquired from the
+ *	DUT yet.
+ *
+ * RETURN: Pointer to the system information struct
+ *
+ * PARAMETERS:
+ *      *dev - pointer to device structure
+ ******************************************************************************/
+struct pt_sysinfo *_pt_request_sysinfo(struct device *dev)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	int rc = 0;
+
+	/* Attempt to get sysinfo if not ready and panel_id is from XY pin */
+	if ((cd->panel_id_support & PT_PANEL_ID_BY_SYS_INFO) &&
+			!cd->sysinfo.ready) {
+		rc = pt_hid_output_get_sysinfo_(cd);
+		if (rc) {
+			pt_debug(cd->dev, DL_ERROR,
+				"%s: Error getting sysinfo rc=%d\n",
+				__func__, rc);
+		}
+	}
+
+	if (cd->sysinfo.ready)
+		return &cd->sysinfo;
+
+	return NULL;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_request_loader_pdata
+ *
+ * SUMMARY: Function pointer included in core_cmds to allow other modules
+ *	to request the pointer to the loader platform data
+ *
+ * RETURN: Pointer to the loader platform data struct
+ *
+ * PARAMETERS:
+ *      *dev - pointer to device structure
+ ******************************************************************************/
+static struct pt_loader_platform_data *_pt_request_loader_pdata(
+		struct device *dev)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+
+	return cd->pdata->loader_pdata;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_request_start_wd
+ *
+ * SUMMARY: Function pointer included in core_cmds to allow other modules
+ *	to request to start the TTDL watchdog
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *      *dev - pointer to device structure
+ ******************************************************************************/
+static int _pt_request_start_wd(struct device *dev)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+
+	pt_start_wd_timer(cd);
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_request_stop_wd
+ *
+ * SUMMARY: Function pointer included in core_cmds to allow other modules
+ *	to request to stop the TTDL watchdog
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *      *dev - pointer to device structure
+ ******************************************************************************/
+static int _pt_request_stop_wd(struct device *dev)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+
+	pt_stop_wd_timer(cd);
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pip2_launch_app
+ *
+ * SUMMARY: Sends the PIP2 EXECUTE command to launch the APP and then wait for
+ *	the FW reset sentinel to indicate the function succeeded.
+ *
+ * NOTE: Calling this function when the DUT is in Application mode WILL result
+ *  in a timeout delay and with the DUT being reset with an XRES.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *      *cd - pointer to core data structure
+ *	 protect - flag to call protected or non-protected
+ ******************************************************************************/
+static int pt_pip2_launch_app(struct device *dev, int protect)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	u16 actual_read_len;
+	u16 tmp_startup_status = cd->startup_status;
+	u8 read_buf[12];
+	u8 status;
+	int time = 0;
+	int rc = 0;
+
+	mutex_lock(&cd->system_lock);
+	cd->startup_status = STARTUP_STATUS_START;
+	pt_debug(dev, DL_DEBUG, "%s: Startup Status Reset\n", __func__);
+	mutex_unlock(&cd->system_lock);
+
+	rc = _pt_request_pip2_send_cmd(dev, protect,
+		PIP2_CMD_ID_EXECUTE, NULL, 0, read_buf,
+		&actual_read_len);
+	if (rc) {
+		pt_debug(dev, DL_ERROR,
+			"%s: PIP2 EXECUTE cmd failed rc = %d\n",
+			__func__, rc);
+	} else {
+		status = read_buf[PIP2_RESP_STATUS_OFFSET];
+
+		/* Test for no or invalid image in FLASH, no point to reset */
+		if (status == PIP2_RSP_ERR_INVALID_IMAGE) {
+			rc = status;
+			goto exit;
+		}
+
+		/* Any other boot failure */
+		if (status != 0) {
+			pt_debug(dev, DL_ERROR,
+				"%s: FW did not EXECUTE, status = %d\n",
+				__func__, status);
+			rc = status;
+		}
+	}
+
+	if (rc) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Failed to launch APP, XRES DUT rc = %d\n",
+			__func__, rc);
+		goto exit;
+	}
+
+	while ((cd->startup_status == STARTUP_STATUS_START) && time < 240) {
+		msleep(20);
+		pt_debug(cd->dev, DL_INFO, "%s: wait for %d for enum=0x%04X\n",
+			__func__, time, cd->startup_status);
+		time += 20;
+	}
+	if (cd->startup_status == STARTUP_STATUS_START) {
+		pt_debug(cd->dev, DL_WARN,
+			"%s: TMO waiting for FW reset sentinel\n", __func__);
+		rc = -ETIME;
+	}
+
+exit:
+	if (cd->startup_status == STARTUP_STATUS_START) {
+		/* Reset to original state because we could be stuck in BL */
+		mutex_lock(&cd->system_lock);
+		cd->startup_status = tmp_startup_status;
+		mutex_unlock(&cd->system_lock);
+	}
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_request_pip2_launch_app
+ *
+ * SUMMARY: Calls pt_pip2_launch_app() when configured to. A small delay is
+ *	inserted to ensure the reset has allowed the BL reset sentinel to be
+ *	consumed.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *      *cd - pointer to core data structure
+ ******************************************************************************/
+static int _pt_request_pip2_launch_app(struct device *dev, int protect)
+{
+	return pt_pip2_launch_app(dev, protect);
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_request_wait_for_enum_state
+ *
+ * SUMMARY: Loops for up to timeout waiting for the startup_status to reach
+ *	the state passed in or STARTUP_STATUS_COMPLETE whichever comes first
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *      *cd      - pointer to core data structure
+ *	 timeout - timeout for how long to wait
+ *	 state   - enum state to wait for
+ ******************************************************************************/
+static int _pt_request_wait_for_enum_state(struct device *dev, int timeout,
+	int state)
+{
+	int rc = 0;
+	int t;
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+
+	t = wait_event_timeout(cd->wait_q,
+		(cd->startup_status & state) || (cd->startup_status & 0x0100),
+		msecs_to_jiffies(timeout));
+	if (IS_TMO(t)) {
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: TMO waiting for enum state 0x%04X in %dms\n",
+			__func__, state, timeout);
+		pt_debug(cd->dev, DL_WARN,
+			"%s: enum state reached 0x%04X\n",
+			__func__, cd->startup_status);
+		rc = -ETIME;
+	} else if (cd->startup_status & state) {
+		pt_debug(cd->dev, DL_INFO,
+			 "%s: Enum state reached: enum=0x%04X in %dms\n",
+			 __func__, cd->startup_status,
+			 (t == 1) ? timeout : (timeout - jiffies_to_msecs(t)));
+	} else {
+		if (t == 1) {
+			pt_debug(
+			    cd->dev, DL_ERROR,
+			    "%s: TMO waiting for enum state 0x%04X in %dms\n",
+			    __func__, state, timeout);
+			rc = -ETIME;
+		} else {
+			pt_debug(
+			    cd->dev, DL_ERROR,
+			    "%s: Enum state 0x%04X not reached in %dms\n",
+			    __func__, state, timeout - jiffies_to_msecs(t));
+			rc = -EINVAL;
+		}
+		pt_debug(cd->dev, DL_INFO, "%s: enum state reached 0x%04X\n",
+			 __func__, cd->startup_status);
+	}
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_core_wake_device_from_deep_sleep_
+ *
+ * SUMMARY: Call the set_power function and set the DUT to wake up from
+ *  deep sleep.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = error
+ *
+ * PARAMETERS:
+ *      *cd - pointer to core data
+ ******************************************************************************/
+static int pt_core_wake_device_from_deep_sleep_(
+		struct pt_core_data *cd)
+{
+	int rc;
+
+	rc = pt_hid_cmd_set_power_(cd, HID_POWER_ON);
+	if (rc)
+		rc =  -EAGAIN;
+
+	/* Prevent failure on sequential wake/sleep requests from OS */
+	msleep(20);
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_core_wake_device_from_easy_wake_
+ *
+ * SUMMARY: Wake up device from Easy-Wake state.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *cd  - pointer to core data
+ ******************************************************************************/
+static int pt_core_wake_device_from_easy_wake_(struct pt_core_data *cd)
+{
+	int rc = 0;
+
+
+	rc = pt_core_wake_device_from_deep_sleep_(cd);
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_restore_parameters_
+ *
+ * SUMMARY: This function sends all RAM parameters stored in the linked list
+ *	back to the DUT
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd   - pointer the core data structure
+ ******************************************************************************/
+static int pt_restore_parameters_(struct pt_core_data *cd)
+{
+	struct param_node *param;
+	int rc = 0;
+
+	if (!(cd->cpdata->flags & PT_CORE_FLAG_RESTORE_PARAMETERS))
+		goto exit;
+
+	spin_lock(&cd->spinlock);
+	list_for_each_entry(param, &cd->param_list, node) {
+		spin_unlock(&cd->spinlock);
+		pt_debug(cd->dev, DL_INFO, "%s: Parameter id:%d value:%d\n",
+			 __func__, param->id, param->value);
+		rc = pt_pip_set_param_(cd, param->id,
+			param->value, param->size);
+		if (rc)
+			goto exit;
+		spin_lock(&cd->spinlock);
+	}
+	spin_unlock(&cd->spinlock);
+exit:
+	return rc;
+}
+
+
+/*******************************************************************************
+ * FUNCTION: pt_pip2_exit_bl_
+ *
+ * SUMMARY: Attempt to exit the BL and run the application, taking into account
+ *	a DUT that may not have flash and will need FW to be loaded into RAM
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd         - pointer the core data structure
+ *	*status_str - pointer to optional status string buffer
+ *	 buf_size   - size of status_str buffer
+ ******************************************************************************/
+int pt_pip2_exit_bl_(struct pt_core_data *cd, u8 *status_str, int buf_size)
+{
+	int rc;
+	int wait_time = 0;
+	u8 mode = PT_MODE_UNKNOWN;
+	bool load_status_str = false;
+
+	/*
+	 * Below function has protective call to ensure no enum is still on
+	 * going, while this kind of protection should be applied widely in
+	 * future (TODO).
+	 */
+	rc = pt_pip2_get_mode_sysmode(cd, &mode, NULL);
+
+	if (status_str && buf_size <= 50)
+		load_status_str = true;
+
+	if (mode == PT_MODE_BOOTLOADER) {
+		if (cd->flashless_dut == 1) {
+			rc = pt_hw_hard_reset(cd);
+		} else {
+			rc = pt_pip2_launch_app(cd->dev,
+				PT_CORE_CMD_UNPROTECTED);
+			if (rc == PIP2_RSP_ERR_INVALID_IMAGE) {
+				pt_debug(cd->dev, DL_ERROR, "%s: %s = %d\n",
+				__func__, "Invalid image in FLASH rc", rc);
+			} else if (rc) {
+				pt_debug(cd->dev, DL_ERROR, "%s: %s = %d\n",
+				__func__, "Failed to launch app rc", rc);
+			}
+		}
+
+		if (!rc) {
+			if (cd->flashless_dut == 1) {
+				/* Wait for BL to complete before enum */
+				rc = _pt_request_wait_for_enum_state(cd->dev,
+					4000, STARTUP_STATUS_FW_RESET_SENTINEL);
+				if (rc && load_status_str) {
+					strlcpy(status_str, "No FW sentinel after BL",
+						sizeof(*status_str)*PT_STATUS_STR_LEN);
+					goto exit;
+				}
+			}
+
+			/*
+			 * If the host wants to interact with the FW or do a
+			 * forced calibration, the FW must be out of BOOT mode
+			 * and the system information must have been retrieved.
+			 * Reaching the FW_OUT_OF_BOOT state guarantees both.
+			 * If, however, the enumeration does not reach this
+			 * point, the DUT may still be in APP mode so test
+			 * for all conditions.
+			 */
+			rc = _pt_request_wait_for_enum_state(cd->dev, 4500,
+				STARTUP_STATUS_FW_OUT_OF_BOOT);
+			if (!rc || cd->startup_status >=
+			    STARTUP_STATUS_FW_RESET_SENTINEL) {
+				mutex_lock(&cd->system_lock);
+				cd->mode = PT_MODE_OPERATIONAL;
+				mutex_unlock(&cd->system_lock);
+			}
+			if (rc) {
+				pt_debug(cd->dev, DL_WARN, "%s: %s: 0x%04X\n",
+					__func__, "Failed to enum with DUT",
+					cd->startup_status);
+				if (load_status_str && !(cd->startup_status &
+				    STARTUP_STATUS_FW_OUT_OF_BOOT)) {
+					strlcpy(status_str, "FW Stuck in Boot mode",
+						sizeof(*status_str)*PT_STATUS_STR_LEN);
+					goto exit;
+				}
+			}
+
+			/*
+			 * The coming FW sentinel could wake up the event
+			 * queue, which has chance to be taken by next command
+			 * wrongly. Following delay is a workaround to avoid
+			 * this issue for most situations.
+			 */
+			msleep(20);
+
+			pt_start_wd_timer(cd);
+		}
+		if (load_status_str) {
+			if (rc == PIP2_RSP_ERR_INVALID_IMAGE)
+				strlcpy(status_str, "Failed - Invalid image in FLASH",
+					sizeof(*status_str)*PT_STATUS_STR_LEN);
+			else if (!rc)
+				strlcpy(status_str, "Entered APP from BL mode",
+					sizeof(*status_str)*PT_STATUS_STR_LEN);
+			else
+				strlcpy(status_str, "Failed to enter APP from BL mode",
+					sizeof(*status_str)*PT_STATUS_STR_LEN);
+		}
+	} else if (mode == PT_MODE_OPERATIONAL) {
+		mutex_lock(&cd->system_lock);
+		cd->mode = mode;
+		mutex_unlock(&cd->system_lock);
+		rc = pt_poll_for_fw_exit_boot_mode(cd, 1500, &wait_time);
+		if (load_status_str) {
+			if (!rc)
+				strlcpy(status_str, "Already in APP mode",
+					sizeof(*status_str)*PT_STATUS_STR_LEN);
+			else
+				strlcpy(status_str, "Already in APP mode - FW stuck in Boot mode",
+					sizeof(*status_str)*PT_STATUS_STR_LEN);
+		}
+	} else if (rc || mode == PT_MODE_UNKNOWN) {
+		mutex_lock(&cd->system_lock);
+		cd->mode = mode;
+		mutex_unlock(&cd->system_lock);
+		if (load_status_str)
+			strlcpy(status_str, "Failed to determine active mode",
+				sizeof(*status_str)*PT_STATUS_STR_LEN);
+	}
+
+exit:
+	if (!rc)
+		pt_start_wd_timer(cd);
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pip2_exit_bl
+ *
+ * SUMMARY: Wrapper function for _pt_pip2_exit_bl that guarantees exclusive
+ *	access.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd         - pointer the core data structure
+ *	*status_str - pointer to optional status string buffer
+ *	 buf_size   - size of status_str buffer
+ ******************************************************************************/
+int pt_pip2_exit_bl(struct pt_core_data *cd, u8 *status_str, int buf_size)
+{
+	int rc;
+
+	rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
+	if (rc < 0) {
+		pt_debug(cd->dev, DL_ERROR,
+			 "%s: fail get exclusive ex=%p own=%p\n", __func__,
+			 cd->exclusive_dev, cd->dev);
+		return rc;
+	}
+
+	rc = pt_pip2_exit_bl_(cd, status_str, buf_size);
+
+	if (release_exclusive(cd, cd->dev) < 0)
+		pt_debug(cd->dev, DL_ERROR, "%s: fail to release exclusive\n",
+			 __func__);
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: _fast_startup
+ *
+ * SUMMARY: Perform fast startup after resume device by power on/off stratergy.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *cd   - pointer the core data structure
+ ******************************************************************************/
+static int _fast_startup(struct pt_core_data *cd)
+{
+	int retry = PT_CORE_STARTUP_RETRY_COUNT;
+	int rc = 0;
+	u8 mode = PT_MODE_UNKNOWN;
+	struct pt_hid_desc hid_desc;
+	int wait_time = 0;
+
+	memset(&hid_desc, 0, sizeof(hid_desc));
+reset:
+	if (retry != PT_CORE_STARTUP_RETRY_COUNT)
+		pt_debug(cd->dev, DL_INFO, "%s: Retry %d\n", __func__,
+			 PT_CORE_STARTUP_RETRY_COUNT - retry);
+
+	if (cd->active_dut_generation == DUT_PIP1_ONLY) {
+		pt_debug(cd->dev, DL_INFO, "%s: PIP1 Enumeration start\n",
+			 __func__);
+		pt_flush_bus_if_irq_asserted(cd, PT_FLUSH_BUS_BASED_ON_LEN);
+
+		rc = pt_get_hid_descriptor_(cd, &hid_desc);
+		if (rc < 0) {
+			pt_debug(cd->dev, DL_ERROR,
+				 "%s: Error on getting HID descriptor r=%d\n",
+				 __func__, rc);
+			if (retry--)
+				goto reset;
+			goto exit;
+		}
+		cd->mode = pt_get_mode(cd, &hid_desc);
+
+		if (cd->mode == PT_MODE_BOOTLOADER) {
+			pt_debug(cd->dev, DL_INFO, "%s: Bootloader mode\n",
+				 __func__);
+			rc = pt_hid_output_bl_launch_app_(cd);
+			if (rc < 0) {
+				pt_debug(cd->dev, DL_ERROR,
+					 "%s: Error on launch app r=%d\n",
+					 __func__, rc);
+				if (retry--)
+					goto reset;
+				goto exit;
+			}
+			rc = pt_get_hid_descriptor_(cd, &hid_desc);
+			if (rc < 0) {
+				pt_debug(cd->dev, DL_ERROR,
+					 "%s: Error on getting HID descriptor r=%d\n",
+					 __func__, rc);
+				if (retry--)
+					goto reset;
+				goto exit;
+			}
+			cd->mode = pt_get_mode(cd, &hid_desc);
+			if (cd->mode == PT_MODE_BOOTLOADER) {
+				if (retry--)
+					goto reset;
+				goto exit;
+			}
+		}
+
+		cd->startup_status |= STARTUP_STATUS_GET_DESC;
+		cd->startup_status |= STARTUP_STATUS_FW_OUT_OF_BOOT;
+	} else {
+		pt_debug(cd->dev, DL_INFO, "%s: PIP2 Enumeration start\n",
+			 __func__);
+
+		if (retry == PT_CORE_STARTUP_RETRY_COUNT) {
+			/* Wait for any sentinel before first try */
+			rc = _pt_request_wait_for_enum_state(
+				cd->dev, 150,
+				STARTUP_STATUS_BL_RESET_SENTINEL |
+				STARTUP_STATUS_FW_RESET_SENTINEL);
+			if (rc)
+				pt_debug(cd->dev, DL_ERROR,
+					 "%s: No Sentinel detected rc = %d\n",
+					 __func__, rc);
+		} else
+			pt_flush_bus_if_irq_asserted(cd,
+						     PT_FLUSH_BUS_BASED_ON_LEN);
+
+		rc = pt_pip2_get_mode_sysmode_(cd, &mode, NULL);
+		if (rc) {
+			pt_debug(cd->dev, DL_ERROR,
+				 "%s: Get mode failed, mode unknown\n",
+				 __func__);
+			mode = PT_MODE_UNKNOWN;
+		}
+		cd->mode = mode;
+
+		if (cd->mode == PT_MODE_BOOTLOADER) {
+			pt_debug(cd->dev, DL_INFO, "%s: Bootloader mode\n",
+				 __func__);
+			rc = pt_pip2_exit_bl_(cd, NULL, 0);
+			if (rc) {
+				pt_debug(cd->dev, DL_ERROR,
+					 "%s Failed to exit bootloader\n",
+					 __func__);
+				msleep(50);
+				rc = -ENODEV;
+				if (retry--)
+					goto reset;
+				goto exit;
+			} else {
+				pt_debug(cd->dev, DL_INFO,
+					 "%s: Exit bootloader successfully\n",
+					 __func__);
+			}
+
+			if (cd->mode != PT_MODE_OPERATIONAL) {
+				pt_debug(cd->dev, DL_WARN,
+					 "%s: restore mode failure mode = %d\n",
+					 __func__, cd->mode);
+				if (retry--)
+					goto reset;
+				goto exit;
+			}
+		}
+		cd->startup_status |= STARTUP_STATUS_GET_DESC;
+	}
+
+	/* FW cannot handle most PIP cmds when it is still in BOOT mode */
+	rc = _pt_poll_for_fw_exit_boot_mode(cd, 500, &wait_time);
+	if (!rc) {
+		cd->startup_status |= STARTUP_STATUS_FW_OUT_OF_BOOT;
+		pt_debug(cd->dev, DL_WARN,
+			"%s: Exit FW BOOT Mode after %dms\n",
+			__func__, wait_time);
+	} else {
+		pt_debug(cd->dev, DL_WARN,
+			"%s: FW stuck in BOOT Mode after %dms\n",
+			__func__, wait_time);
+		goto exit;
+	}
+
+	if (!cd->sysinfo.ready) {
+		rc = pt_hid_output_get_sysinfo_(cd);
+		if (rc) {
+			pt_debug(cd->dev, DL_ERROR,
+				"%s: Error on getting sysinfo r=%d\n",
+				__func__, rc);
+			if (retry--)
+				goto reset;
+			goto exit;
+		}
+	}
+	cd->startup_status |= STARTUP_STATUS_GET_SYS_INFO;
+
+	rc = pt_restore_parameters_(cd);
+	if (rc)
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: failed to restore parameters rc=%d\n",
+			__func__, rc);
+	else
+		cd->startup_status |= STARTUP_STATUS_RESTORE_PARM;
+
+exit:
+	cd->startup_status |= STARTUP_STATUS_COMPLETE;
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_core_poweron_device_
+ *
+ * SUMMARY: Power on device, enable IRQ, and then perform a fast startup.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *cd  - pointer to core data
+ ******************************************************************************/
+static int pt_core_poweron_device_(struct pt_core_data *cd)
+{
+	struct device *dev = cd->dev;
+	int rc = 0;
+
+	/*
+	 * After power on action, the chip can general FW sentinel. It can
+	 * trigger an enumeration without hid_reset_cmd_state flag. Since the
+	 * _fast_startup() can perform a quick enumeration too, here doesn't
+	 * need another enumeration.
+	 */
+	mutex_lock(&cd->system_lock);
+	cd->startup_status = STARTUP_STATUS_START;
+	cd->hid_reset_cmd_state = 1;
+	mutex_unlock(&cd->system_lock);
+
+	rc = cd->cpdata->power(cd->cpdata, 1, dev, 0);
+	if (rc < 0) {
+		pt_debug(dev, DL_ERROR, "%s: HW Power up fails r=%d\n",
+			__func__, rc);
+		goto exit;
+	}
+
+	if (!cd->irq_enabled) {
+		cd->irq_enabled = true;
+		enable_irq(cd->irq);
+	}
+
+	/* TBD: following function doesn't update startup_status */
+	rc = _fast_startup(cd);
+exit:
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_core_wake_device_from_deep_standby_
+ *
+ * SUMMARY: Reset device, and then trigger a full enumeration.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *cd  - pointer to core data
+ ******************************************************************************/
+static int pt_core_wake_device_from_deep_standby_(struct pt_core_data *cd)
+{
+	int rc;
+
+	rc = pt_dut_reset_and_wait(cd);
+	if (rc < 0) {
+		pt_debug(cd->dev, DL_ERROR, "%s: Error on h/w reset r=%d\n",
+			__func__, rc);
+		goto exit;
+	}
+
+	rc = _fast_startup(cd);
+exit:
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_core_easywake_off_
+ *
+ * SUMMARY: Resume the device with a power on or wake from deep sleep based on
+ *  the configuration in the core platform data structure.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *cd  - pointer to core data
+ ******************************************************************************/
+static int pt_core_easywake_off_(struct pt_core_data *cd)
+{
+	int rc = 0;
+
+	mutex_lock(&cd->system_lock);
+	if (cd->sleep_state == SS_EASY_WAKING_OFF) {
+		mutex_unlock(&cd->system_lock);
+		pt_debug(cd->dev, DL_INFO,
+			"%s - %d skip wakeoff\n", __func__, cd->sleep_state);
+		return 0;
+	}
+	mutex_unlock(&cd->system_lock);
+
+	if (!(cd->cpdata->flags & PT_CORE_FLAG_SKIP_RESUME)) {
+		if (IS_EASY_WAKE_CONFIGURED(cd->easy_wakeup_gesture))
+			rc = pt_core_wake_device_from_easy_wake_(cd);
+		if (rc < 0)
+			pt_debug(cd->dev, DL_ERROR,
+				"%s - %d failed %d\n", __func__, rc);
+	}
+
+	mutex_lock(&cd->system_lock);
+	cd->sleep_state = SS_EASY_WAKING_OFF;
+	mutex_unlock(&cd->system_lock);
+	pt_start_wd_timer(cd);
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_core_easywake_off
+ *
+ * SUMMARY: Protected call to pt_core_easywake_off by exclusive access to the DUT.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *cd  - pointer to core data
+ ******************************************************************************/
+static int pt_core_easywake_off(struct pt_core_data *cd)
+{
+	int rc;
+
+	rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
+	if (rc < 0) {
+		pt_debug(cd->dev, DL_ERROR,
+				"%s: fail get exclusive ex=%p own=%p\n",
+				__func__, cd->exclusive_dev, cd->dev);
+		return rc;
+	}
+
+	rc = pt_core_easywake_off_(cd);
+
+	if (release_exclusive(cd, cd->dev) < 0)
+		pt_debug(cd->dev, DL_ERROR, "%s: fail to release exclusive\n",
+			__func__);
+	else
+		pt_debug(cd->dev, DL_DEBUG, "%s: pass release exclusive\n",
+			__func__);
+
+	return rc;
+}
+
+
+/*******************************************************************************
+ * FUNCTION: pt_core_wake_
+ *
+ * SUMMARY: Resume the device with a power on or wake from deep sleep based on
+ *  the configuration in the core platform data structure.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *cd  - pointer to core data
+ ******************************************************************************/
+static int pt_core_wake_(struct pt_core_data *cd)
+{
+	int rc = 0;
+
+	mutex_lock(&cd->system_lock);
+
+	if (cd->sleep_state == SS_SLEEP_OFF || cd->sleep_state == SS_WAKING) {
+		mutex_unlock(&cd->system_lock);
+		pt_debug(cd->dev, DL_INFO,
+			"%s - skip wake sleep state %d\n", __func__, cd->sleep_state);
+		return 0;
+	} else {
+		cd->sleep_state = SS_WAKING;
+	}
+	mutex_unlock(&cd->system_lock);
+
+	if (!(cd->cpdata->flags & PT_CORE_FLAG_SKIP_RESUME)) {
+		if (cd->cpdata->flags & PT_CORE_FLAG_POWEROFF_ON_SLEEP) {
+			pt_debug(cd->dev, DL_INFO,
+				"%s: Entering into poweron mode:\n", __func__);
+			rc = pt_core_poweron_device_(cd);
+			if (rc < 0)
+				pr_err("%s: Poweron error detected: rc=%d\n",
+					__func__, rc);
+		}
+		else if (cd->cpdata->flags & PT_CORE_FLAG_DEEP_STANDBY)
+			rc = pt_core_wake_device_from_deep_standby_(cd);
+		else /* Default action to exit DeepSleep */
+			rc = pt_core_wake_device_from_deep_sleep_(cd);
+	}
+
+	mutex_lock(&cd->system_lock);
+	cd->sleep_state = SS_SLEEP_OFF;
+	mutex_unlock(&cd->system_lock);
+
+	pt_start_wd_timer(cd);
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_core_wake_
+ *
+ * SUMMARY: Protected call to pt_core_wake_ by exclusive access to the DUT.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *cd  - pointer to core data
+ ******************************************************************************/
+static int pt_core_wake(struct pt_core_data *cd)
+{
+	int rc = 0;
+
+	rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
+	if (rc < 0) {
+		pt_debug(cd->dev, DL_ERROR,
+				"%s: fail get exclusive ex=%p own=%p\n",
+				__func__, cd->exclusive_dev, cd->dev);
+		return rc;
+	}
+
+	rc = pt_core_wake_(cd);
+
+	if (release_exclusive(cd, cd->dev) < 0)
+		pt_debug(cd->dev, DL_ERROR, "%s: fail to release exclusive\n",
+			__func__);
+	else
+		pt_debug(cd->dev, DL_DEBUG, "%s: pass release exclusive\n",
+			__func__);
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_get_ic_crc_
+ *
+ * SUMMARY: This function retrieves the config block CRC
+ *
+ * NOTE: The post condition of calling this function will be that the DUT will
+ *  be in SCANNINING mode if no failures occur
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd   - pointer the core data structure
+ *	 ebid - enumerated block ID
+ ******************************************************************************/
+static int pt_get_ic_crc_(struct pt_core_data *cd, u8 ebid)
+{
+	struct pt_sysinfo *si = &cd->sysinfo;
+	int rc = 0;
+	u8 status;
+	u16 calculated_crc = 0;
+	u16 stored_crc = 0;
+
+	rc = pt_pip_suspend_scanning_(cd);
+	if (rc)
+		goto error;
+
+	rc = pt_pip_verify_config_block_crc_(cd, ebid, &status,
+			&calculated_crc, &stored_crc);
+	if (rc)
+		goto exit;
+
+	if (status) {
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	si->ttconfig.crc = stored_crc;
+
+exit:
+	pt_pip_resume_scanning_(cd);
+error:
+	pt_debug(cd->dev, DL_INFO,
+		"%s: CRC: ebid:%d, calc:0x%04X, stored:0x%04X, rc=%d\n",
+		__func__, ebid, calculated_crc, stored_crc, rc);
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pip2_read_gpio
+ *
+ * SUMMARY: Sends a PIP2 READ_GPIO command to the DUT and stores the 32 gpio
+ *	bits into the passed in variable
+ *
+ * NOTE: PIP2 READ_GPIO command is only supported in bootloader
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*dev    - pointer to device structure
+ *	*status - pointer to where the command response status is stored
+ *	*gpio   - pointer to device attributes structure
+ ******************************************************************************/
+static int pt_pip2_read_gpio(struct device *dev, u8 *status, u32 *gpio)
+{
+	int rc = 0;
+	u16 actual_read_len;
+	u8 read_buf[12];
+	u8 tmp_status = 0;
+	u8 index = PIP2_RESP_STATUS_OFFSET;
+
+	memset(read_buf, 0, ARRAY_SIZE(read_buf));
+	rc = _pt_request_pip2_send_cmd(dev,
+		PT_CORE_CMD_UNPROTECTED, PIP2_CMD_ID_READ_GPIO,
+		NULL, 0, read_buf, &actual_read_len);
+
+	if (rc) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Failed to send PIP2 READ_GPIO cmd\n", __func__);
+		rc = -ECOMM;
+	} else {
+		tmp_status = read_buf[index];
+	}
+
+	if (status)
+		*status = tmp_status;
+
+	if (!rc && gpio && (tmp_status == 0)) {
+		*gpio = ((read_buf[index + 4] << 24) |
+			 (read_buf[index + 3] << 16) |
+			 (read_buf[index + 2] << 8) |
+			 (read_buf[index + 1]));
+	}
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_pip2_get_panel_id_by_gpio
+ *
+ * SUMMARY: Wrapper function to call pt_pip2_read_gpio() to get panel ID
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd    - pointer the core data structure
+ *	*pid   - pointer to store panel ID
+ ******************************************************************************/
+static int _pt_pip2_get_panel_id_by_gpio(struct pt_core_data *cd, u8 *pid)
+{
+	u32 gpio_value = 0;
+	u8 status = 0;
+	u8 panel_id = PANEL_ID_NOT_ENABLED;
+	int rc = 0;
+
+	if (!pid)
+		return -ENOMEM;
+
+	rc = pt_pip2_read_gpio(cd->dev, &status, &gpio_value);
+	if (!rc) {
+		if (status == 0) {
+			panel_id = (gpio_value & PT_PANEL_ID_BITMASK) >>
+			       PT_PANEL_ID_SHIFT;
+			pt_debug(cd->dev, DL_INFO, "%s: %s=0x%02X %s=0x%08X\n",
+				 __func__,
+				 "BL mode PID", panel_id, "gpio", gpio_value);
+			*pid = panel_id;
+		} else {
+			pt_debug(cd->dev, DL_ERROR, "%s: %s=%d\n",
+				 __func__,
+				 "BL read gpio failed status", status);
+		}
+	} else {
+		pt_debug(cd->dev, DL_ERROR, "%s: %s=%d\n",
+			 __func__,
+			 "BL read gpio failed status", status);
+	}
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_enum_with_dut_
+ *
+ * SUMMARY: This function does the full enumeration of the DUT with TTDL.
+ *	The core data (cd) startup_status will store, as a bitmask, each
+ *	state of the enumeration process. The startup will be attempted
+ *	PT_CORE_STARTUP_RETRY_COUNT times before giving up.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd    - pointer the core data structure
+ *	 reset - Flag to reset the DUT before attempting to enumerate
+ *	*status - poionter to store the enum status bitmask flags
+ ******************************************************************************/
+static int pt_enum_with_dut_(struct pt_core_data *cd, bool reset,
+	u32 *enum_status)
+{
+	int try = 1;
+	int rc = 0;
+	int wait_time = 0;
+	bool detected = false;
+	u8 return_data[8];
+	u8 mode = PT_MODE_UNKNOWN;
+	u8 pid = PANEL_ID_NOT_ENABLED;
+	u8 sys_mode = FW_SYS_MODE_UNDEFINED;
+	struct pt_hid_desc hid_desc;
+
+	memset(&hid_desc, 0, sizeof(hid_desc));
+#ifdef TTHE_TUNER_SUPPORT
+	tthe_print(cd, NULL, 0, "enter startup");
+#endif
+	pt_debug(cd->dev, DL_INFO, "%s: Start enum... 0x%04X, reset=%d\n",
+		__func__, cd->startup_status, reset);
+	pt_stop_wd_timer(cd);
+reset:
+	if (try > 1)
+		pt_debug(cd->dev, DL_WARN, "%s: DUT Enum Attempt %d\n",
+			__func__, try);
+	pt_flush_bus_if_irq_asserted(cd, PT_FLUSH_BUS_BASED_ON_LEN);
+	if (cd->active_dut_generation == DUT_PIP1_ONLY) {
+		pt_debug(cd->dev, DL_INFO,
+			"%s: PIP1 Enumeration start\n", __func__);
+
+		/* Only reset the DUT after the first try */
+		if (reset || (try > 1)) {
+			/*
+			 * Reset hardware only for Legacy parts. Skip for TT/TC
+			 * parts because if the FW image was loaded directly
+			 * to SRAM issueing a reset ill wipe out what was just
+			 * loaded.
+			 */
+			rc = pt_dut_reset_and_wait(cd);
+			if (rc < 0) {
+				pt_debug(cd->dev, DL_ERROR,
+					"%s: Error on h/w reset r=%d\n",
+					__func__, rc);
+				if (try++ < PT_CORE_STARTUP_RETRY_COUNT)
+					goto reset;
+				goto exit;
+			}
+			/* sleep to allow FW to be launched if available */
+			msleep(120);
+		}
+		rc = pt_get_hid_descriptor_(cd, &hid_desc);
+		if (rc < 0) {
+			pt_debug(cd->dev, DL_ERROR,
+				"%s: Error getting HID Descriptor r=%d\n",
+				__func__, rc);
+			if (try++ < PT_CORE_STARTUP_RETRY_COUNT)
+				goto reset;
+			rc = -EIO;
+			goto exit;
+		}
+		detected = true;
+		cd->mode = pt_get_mode(cd, &hid_desc);
+		/*
+		 * Most systems do not use an XY pin as the panel_id and so
+		 * the BL is used to retrieve the panel_id, however for
+		 * systems that do use an XY pin, the panel_id MUST be
+		 * retrieved from the system information when running FW
+		 * (done below) and so this section of code is skipped.
+		 * Entering the BL here is only needed on XY_PIN systems.
+		 */
+		if (cd->panel_id_support & PT_PANEL_ID_BY_BL) {
+			if (cd->mode == PT_MODE_OPERATIONAL) {
+				rc = pt_pip_start_bootloader_(cd);
+				if (rc < 0) {
+					pt_debug(cd->dev, DL_ERROR,
+						"%s: Error on start bootloader r=%d\n",
+						__func__, rc);
+					if (try++ < PT_CORE_STARTUP_RETRY_COUNT)
+						goto reset;
+					goto exit;
+				}
+				cd->mode = PT_MODE_BOOTLOADER;
+				pt_debug(cd->dev, DL_INFO, "%s: Bootloader mode\n",
+					__func__);
+			}
+			rc = pt_hid_output_bl_get_information_(cd, return_data);
+			if (!rc) {
+				cd->bl_info.ready = true;
+				cd->bl_info.chip_id =
+				    get_unaligned_le16(&return_data[2]);
+				pt_debug(cd->dev, DL_INFO, "%s: chip ID %04X\n",
+					 __func__, cd->bl_info.chip_id);
+			} else {
+				pt_debug(cd->dev, DL_ERROR,
+					 "%s: failed to get chip ID, r=%d\n",
+					 __func__, rc);
+			}
+			rc = pt_hid_output_bl_get_panel_id_(cd, &pid);
+			mutex_lock(&cd->system_lock);
+			if (!rc) {
+				cd->pid_for_loader = pid;
+				pt_debug(cd->dev, DL_INFO, "%s: Panel ID: 0x%02X\n",
+					__func__, cd->pid_for_loader);
+			} else {
+				cd->pid_for_loader = PANEL_ID_NOT_ENABLED;
+				pt_debug(cd->dev, DL_WARN,
+					"%s: Read Failed, disable Panel ID: 0x%02X\n",
+					__func__, cd->pid_for_loader);
+			}
+			mutex_unlock(&cd->system_lock);
+		}
+
+		/* Exit BL due to XY_PIN case or any other cause to be in BL */
+		if (cd->mode == PT_MODE_BOOTLOADER) {
+			rc = pt_hid_output_bl_launch_app_(cd);
+			if (rc < 0) {
+				pt_debug(cd->dev, DL_ERROR,
+					"%s: Error on launch app r=%d\n",
+					__func__, rc);
+				if (try++ < PT_CORE_STARTUP_RETRY_COUNT)
+					goto reset;
+				rc = -ENODEV;
+				goto exit;
+			}
+
+			/* sleep to allow FW to be launched if available */
+			msleep(120);
+
+			/* Ensure the DUT is now in Application mode */
+			rc = pt_get_hid_descriptor_(cd, &hid_desc);
+			if (rc < 0) {
+				pt_debug(cd->dev, DL_ERROR,
+					"%s: Error getting HID Desc r=%d\n",
+					__func__, rc);
+				if (try++ < PT_CORE_STARTUP_RETRY_COUNT)
+					goto reset;
+				rc = -EIO;
+				goto exit;
+			}
+
+			cd->mode = pt_get_mode(cd, &hid_desc);
+			if (cd->mode == PT_MODE_BOOTLOADER) {
+				pt_debug(cd->dev, DL_WARN,
+					"%s: Error confiming exit BL\n",
+					__func__);
+				if (try++ < PT_CORE_STARTUP_RETRY_COUNT)
+					goto reset;
+				rc = -ENODEV;
+				goto exit;
+			}
+		}
+		pt_debug(cd->dev, DL_INFO, "%s: Operational mode\n", __func__);
+		cd->mode = PT_MODE_OPERATIONAL;
+		*enum_status |= STARTUP_STATUS_GET_DESC;
+		*enum_status |= STARTUP_STATUS_FW_OUT_OF_BOOT;
+	} else {
+		/* Generation is PIP2 Capable */
+		pt_debug(cd->dev, DL_INFO,
+			"%s: PIP2 Enumeration start\n", __func__);
+
+		rc = pt_pip2_get_mode_sysmode_(cd, &mode, NULL);
+		if (rc) {
+			pt_debug(cd->dev, DL_ERROR,
+				"%s: Get mode failed, mode unknown\n",
+				__func__);
+			mode = PT_MODE_UNKNOWN;
+		} else
+			detected = true;
+
+		cd->mode = mode;
+		switch (cd->mode) {
+		case PT_MODE_OPERATIONAL:
+			pt_debug(cd->dev, DL_INFO,
+				"%s: Operational mode\n", __func__);
+			if (cd->app_pip_ver_ready == false) {
+				rc = pt_pip2_get_version_(cd);
+				if (!rc)
+					cd->app_pip_ver_ready = true;
+				else {
+					if (try++ < PT_CORE_STARTUP_RETRY_COUNT)
+						goto reset;
+					goto exit;
+				}
+			}
+			break;
+		case PT_MODE_BOOTLOADER:
+			pt_debug(cd->dev, DL_INFO,
+				"%s: Bootloader mode\n", __func__);
+			if (cd->panel_id_support & PT_PANEL_ID_BY_BL) {
+				rc = _pt_pip2_get_panel_id_by_gpio(cd, &pid);
+				mutex_lock(&cd->system_lock);
+				if (!rc) {
+					cd->pid_for_loader = pid;
+					pt_debug(cd->dev, DL_INFO,
+						 "%s: Panel ID: 0x%02X\n",
+						 __func__, cd->pid_for_loader);
+				} else {
+					cd->pid_for_loader =
+					    PANEL_ID_NOT_ENABLED;
+					pt_debug(cd->dev, DL_WARN,
+						 "%s: Read Failed, disable Panel ID: 0x%02X\n",
+						 __func__, cd->pid_for_loader);
+				}
+				mutex_unlock(&cd->system_lock);
+			}
+
+			if (cd->bl_pip_ver_ready == false) {
+				rc = pt_pip2_get_version_(cd);
+				if (!rc)
+					cd->bl_pip_ver_ready = true;
+				else {
+					if (try++ < PT_CORE_STARTUP_RETRY_COUNT)
+						goto reset;
+					goto exit;
+				}
+			}
+
+			/*
+			 * Launch app command will fail in flashless mode.
+			 * Skip launch app command here to save time for
+			 * enumeration flow.
+			 */
+			if (cd->flashless_dut)
+				goto exit;
+
+			/*
+			 * pt_pip2_launch_app() is needed here instead of
+			 * pt_pip2_exit_bl() because exit_bl will cause several
+			 * hw_resets to occur and the auto BL on a flashless
+			 * DUT will fail.
+			 */
+			rc = pt_pip2_launch_app(cd->dev,
+				PT_CORE_CMD_UNPROTECTED);
+			if (rc) {
+				pt_debug(cd->dev, DL_ERROR,
+					"%s: Error on launch app r=%d\n",
+					__func__, rc);
+				msleep(50);
+				if (try++ < PT_CORE_STARTUP_RETRY_COUNT)
+					goto reset;
+				goto exit;
+			}
+
+			/*
+			 * IRQ thread can be delayed if the serial log print is
+			 * enabled. It causes next command to get wrong response
+			 * Here the delay is to ensure pt_parse_input() to be
+			 * finished.
+			 */
+			msleep(60);
+			/* Check and update the mode */
+			rc = pt_pip2_get_mode_sysmode_(cd, &mode, NULL);
+			if (rc) {
+				pt_debug(cd->dev, DL_ERROR,
+					"%s: Get mode failed, mode unknown\n",
+					__func__);
+				mode = PT_MODE_UNKNOWN;
+			}
+
+			cd->mode = mode;
+			if (cd->mode == PT_MODE_OPERATIONAL) {
+				pt_debug(cd->dev, DL_INFO,
+					 "%s: Launched to Operational mode\n",
+					 __func__);
+			} else if (cd->mode == PT_MODE_BOOTLOADER) {
+				pt_debug(cd->dev, DL_ERROR,
+					 "%s: Launch failed, Bootloader mode\n",
+					 __func__);
+				goto exit;
+			} else if (cd->mode == PT_MODE_UNKNOWN) {
+				pt_debug(cd->dev, DL_ERROR,
+					 "%s: Launch failed, Unknown mode\n",
+					 __func__);
+				if (try++ < PT_CORE_STARTUP_RETRY_COUNT)
+					goto reset;
+				goto exit;
+			}
+			if (cd->app_pip_ver_ready == false) {
+				rc = pt_pip2_get_version_(cd);
+				if (!rc)
+					cd->app_pip_ver_ready = true;
+				else {
+					if (try++ < PT_CORE_STARTUP_RETRY_COUNT)
+						goto reset;
+					goto exit;
+				}
+			}
+			break;
+		default:
+			pt_debug(cd->dev, DL_ERROR,
+				"%s: Unknown mode\n", __func__);
+			if (try++ < PT_CORE_STARTUP_RETRY_COUNT)
+				goto reset;
+			goto exit;
+		}
+		*enum_status |= STARTUP_STATUS_GET_DESC;
+	}
+
+	pt_init_pip_report_fields(cd);
+	*enum_status |= STARTUP_STATUS_GET_RPT_DESC;
+	if (!cd->features.easywake)
+		cd->easy_wakeup_gesture = PT_CORE_EWG_NONE;
+
+	pt_debug(cd->dev, DL_INFO, "%s: Reading sysinfo\n", __func__);
+	rc = pt_hid_output_get_sysinfo_(cd);
+	if (rc) {
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: Error on getting sysinfo r=%d\n", __func__, rc);
+		if (try++ < PT_CORE_STARTUP_RETRY_COUNT)
+			goto reset;
+		goto exit;
+	}
+	*enum_status |= STARTUP_STATUS_GET_SYS_INFO;
+	/* FW cannot handle most PIP cmds when it is still in BOOT mode */
+	rc = _pt_poll_for_fw_exit_boot_mode(cd, 10000, &wait_time);
+	if (!rc) {
+		*enum_status |= STARTUP_STATUS_FW_OUT_OF_BOOT;
+		pt_debug(cd->dev, DL_WARN,
+			"%s: Exit FW BOOT Mode after %dms\n",
+			__func__, wait_time);
+	} else {
+		pt_debug(cd->dev, DL_WARN,
+			"%s: FW stuck in BOOT Mode after %dms\n",
+			__func__, wait_time);
+		goto exit;
+	}
+
+	pt_debug(cd->dev, DL_INFO, "%s pt Prot Version: %d.%d\n",
+			__func__,
+			cd->sysinfo.ttdata.pip_ver_major,
+			cd->sysinfo.ttdata.pip_ver_minor);
+	rc = pt_get_ic_crc_(cd, PT_TCH_PARM_EBID);
+	if (rc) {
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: DUT Config block CRC failure rc=%d\n",
+			__func__, rc);
+		if (try++ < PT_CORE_STARTUP_RETRY_COUNT)
+			goto reset;
+	} else {
+		_pt_get_fw_sys_mode(cd, &sys_mode, NULL);
+		if (sys_mode != FW_SYS_MODE_SCANNING) {
+			pt_debug(cd->dev, DL_ERROR,
+				"%s: scan state: %d, retry: %d\n",
+				__func__, sys_mode, try);
+			if (try++ < PT_CORE_STARTUP_RETRY_COUNT)
+				goto reset;
+		} else
+			*enum_status |= STARTUP_STATUS_GET_CFG_CRC;
+	}
+	rc = pt_restore_parameters_(cd);
+	if (rc) {
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: Failed to restore parameters rc=%d\n",
+			__func__, rc);
+	} else
+		*enum_status |= STARTUP_STATUS_RESTORE_PARM;
+	call_atten_cb(cd, PT_ATTEN_STARTUP, 0);
+	cd->watchdog_interval = PT_WATCHDOG_TIMEOUT;
+	cd->startup_retry_count = 0;
+
+exit:
+	/* Generate the HW Version of the connected DUT and store in cd */
+	pt_generate_hw_version(cd, cd->hw_version);
+	pt_debug(cd->dev, DL_WARN, "%s: HW Version: %s\n", __func__,
+		cd->hw_version);
+
+	pt_start_wd_timer(cd);
+
+	if (!detected)
+		rc = -ENODEV;
+
+#ifdef TTHE_TUNER_SUPPORT
+	tthe_print(cd, NULL, 0, "exit startup");
+#endif
+
+	pt_debug(cd->dev, DL_WARN,
+		"%s: Completed Enumeration rc=%d On Attempt %d\n",
+		__func__, rc, try);
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_enum_with_dut
+ *
+ * SUMMARY: This is the safe function wrapper for pt_enum_with_dut_() by
+ *	requesting exclusive access around the call.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd    - pointer the core data structure
+ *	 reset - Flag to reset the DUT before attempting to enumerate
+ *	*status - pointer to store the enum status bitmask flags
+ ******************************************************************************/
+static int pt_enum_with_dut(struct pt_core_data *cd, bool reset, u32 *status)
+{
+	int rc = 0;
+
+	mutex_lock(&cd->system_lock);
+	cd->startup_state = STARTUP_RUNNING;
+	mutex_unlock(&cd->system_lock);
+
+	rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
+	if (rc) {
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: fail get exclusive ex=%p own=%p\n",
+			__func__, cd->exclusive_dev, cd->dev);
+		goto exit;
+	}
+
+	rc = pt_enum_with_dut_(cd, reset, status);
+
+	if (release_exclusive(cd, cd->dev) < 0)
+		/* Don't return fail code, mode is already changed. */
+		pt_debug(cd->dev, DL_ERROR, "%s: fail to release exclusive\n",
+			__func__);
+	else
+		pt_debug(cd->dev, DL_DEBUG, "%s: pass release exclusive\n",
+			__func__);
+exit:
+	mutex_lock(&cd->system_lock);
+	/* Clear startup state for any tasks waiting for startup completion */
+	cd->startup_state = STARTUP_NONE;
+	mutex_unlock(&cd->system_lock);
+
+	/* Set STATUS_COMPLETE bit to indicate the status is ready to be read */
+	*status |= STARTUP_STATUS_COMPLETE;
+
+	/* Wake the waiters for end of startup */
+	wake_up(&cd->wait_q);
+
+	return rc;
+}
+
+static int add_sysfs_interfaces(struct device *dev);
+static void remove_sysfs_interfaces(struct device *dev);
+static void remove_sysfs_and_modules(struct device *dev);
+static void pt_release_modules(struct pt_core_data *cd);
+static void pt_probe_modules(struct pt_core_data *cd);
+/*******************************************************************************
+ * FUNCTION: _pt_ttdl_restart
+ *
+ * SUMMARY: Restarts TTDL enumeration with the DUT and re-probes all modules
+ *
+ * NOTE: The function DOSE NOT remove sysfs and modules. Trying to create
+ * existing sysfs nodes will produce an error.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *      *dev  - pointer to core device
+ ******************************************************************************/
+static int _pt_ttdl_restart(struct device *dev)
+{
+	int rc = 0;
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+#ifdef CONFIG_TOUCHSCREEN_PARADE_I2C
+	struct i2c_client *client =
+		(struct i2c_client *)container_of(dev, struct i2c_client, dev);
+#endif
+	/*
+	 * Make sure the device is awake, pt_mt_release function will
+	 * cause pm sleep function and lead to deadlock.
+	 */
+	pm_runtime_get_sync(dev);
+	/* Use ttdl_restart_lock to avoid reentry */
+	mutex_lock(&cd->ttdl_restart_lock);
+
+	remove_sysfs_and_modules(cd->dev);
+
+#ifdef CONFIG_TOUCHSCREEN_PARADE_I2C
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+		pt_debug(dev, DL_ERROR,
+			"%s I2C functionality not Supported\n", __func__);
+		rc = -EIO;
+		goto ttdl_no_error;
+	}
+#endif
+
+	if (cd->active_dut_generation == DUT_UNKNOWN) {
+		rc = _pt_detect_dut_generation(cd->dev,
+			&cd->startup_status, &cd->active_dut_generation,
+			&cd->mode);
+		if ((cd->active_dut_generation == DUT_UNKNOWN) || (rc)) {
+			pt_debug(dev, DL_ERROR,
+				"%s: Error, Unknown DUT Generation rc=%d\n",
+				__func__, rc);
+		}
+	}
+
+	rc = add_sysfs_interfaces(cd->dev);
+	if (rc < 0)
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: Error, failed sysfs nodes rc=%d\n",
+			__func__, rc);
+
+	if (!(cd->startup_status & STARTUP_STATUS_BL_RESET_SENTINEL)) {
+		pt_debug(dev, DL_INFO, "%s: Call pt_enum_with_dut\n", __func__);
+		rc = pt_enum_with_dut(cd, true, &cd->startup_status);
+		if (rc < 0)
+			pt_debug(dev, DL_ERROR,
+				"%s: Error, Failed to Enumerate\n", __func__);
+	}
+
+	rc = pt_mt_probe(dev);
+	if (rc < 0) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Error, fail mt probe\n", __func__);
+	}
+
+	rc = pt_btn_probe(dev);
+	if (rc < 0) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Error, fail btn probe\n", __func__);
+	}
+
+	pt_probe_modules(cd);
+
+	pt_debug(cd->dev, DL_WARN,
+		"%s: Well Done! TTDL Restart Completed\n", __func__);
+	rc = 0;
+
+#ifdef CONFIG_TOUCHSCREEN_PARADE_I2C
+ttdl_no_error:
+#endif
+	mutex_unlock(&cd->ttdl_restart_lock);
+
+	mutex_lock(&cd->system_lock);
+	cd->startup_status |= STARTUP_STATUS_COMPLETE;
+	cd->startup_state = STARTUP_NONE;
+	mutex_unlock(&cd->system_lock);
+
+	pm_runtime_put(dev);
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_restart_work_function
+ *
+ * SUMMARY: This is the wrapper function placed in a work queue to call
+ *	_pt_ttdl_restart()
+ *
+ * RETURN: none
+ *
+ * PARAMETERS:
+ *	*work - pointer to the work_struct
+ ******************************************************************************/
+static void pt_restart_work_function(struct work_struct *work)
+{
+	struct pt_core_data *cd =  container_of(work,
+		struct pt_core_data, ttdl_restart_work);
+	int rc = 0;
+
+	rc = _pt_ttdl_restart(cd->dev);
+	if (rc < 0)
+		pt_debug(cd->dev, DL_ERROR, "%s: Fail queued startup r=%d\n",
+			__func__, rc);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_enum_work_function
+ *
+ * SUMMARY: This is the wrapper function placed in a work queue to call
+ *	pt_enum_with_dut()
+ *
+ * RETURN: none
+ *
+ * PARAMETERS:
+ *	*work - pointer to the work_struct
+ ******************************************************************************/
+static void pt_enum_work_function(struct work_struct *work)
+{
+	struct pt_core_data *cd =  container_of(work,
+		struct pt_core_data, enum_work);
+	int rc;
+
+	rc = pt_enum_with_dut(cd, false, &cd->startup_status);
+	if (rc < 0)
+		pt_debug(cd->dev, DL_ERROR, "%s: Fail queued startup r=%d\n",
+			__func__, rc);
+}
+
+static int pt_get_regulator(struct pt_core_data *cd, bool get)
+{
+	int rc;
+
+	if (!get) {
+		rc = 0;
+		goto regulator_put;
+	}
+
+	cd->vdd = regulator_get(cd->dev, "vdd");
+	if (IS_ERR(cd->vdd)) {
+		rc = PTR_ERR(cd->vdd);
+		dev_err(cd->dev,
+			"Regulator get failed vdd rc=%d\n", rc);
+		goto regulator_put;
+	}
+
+	cd->vcc_i2c = regulator_get(cd->dev, "vcc_i2c");
+	if (IS_ERR(cd->vcc_i2c)) {
+		rc = PTR_ERR(cd->vcc_i2c);
+		dev_err(cd->dev,
+			"Regulator get failed vcc_i2c rc=%d\n", rc);
+		goto regulator_put;
+	}
+
+	return 0;
+
+regulator_put:
+	if (cd->vdd) {
+		regulator_put(cd->vdd);
+		cd->vdd = NULL;
+	}
+
+	if (cd->vcc_i2c) {
+		regulator_put(cd->vcc_i2c);
+		cd->vcc_i2c = NULL;
+	}
+
+	return rc;
+}
+
+#ifdef ENABLE_I2C_REG_ONLY
+static int pt_enable_i2c_regulator(struct pt_core_data *cd, bool en)
+{
+	int rc = 0;
+
+	pt_debug(cd->dev, DL_INFO, "%s: Enter flag = %d\n", __func__, en);
+	if (!en) {
+		rc = 0;
+		goto disable_vcc_i2c_reg_only;
+	}
+
+	if (cd->vcc_i2c) {
+		rc = regulator_set_load(cd->vcc_i2c, I2C_ACTIVE_LOAD_MA);
+		if (rc < 0)
+			pt_debug(cd->dev, DL_INFO,
+				"%s: I2c unable to set active current rc = %d\n", __func__, rc);
+
+		pt_debug(cd->dev, DL_INFO, "%s: i2c set I2C_ACTIVE_LOAD_MA rc = %d\n",
+			__func__, rc);
+	}
+
+	return 0;
+
+disable_vcc_i2c_reg_only:
+	if (cd->vcc_i2c) {
+		rc = regulator_set_load(cd->vcc_i2c, I2C_SUSPEND_LOAD_UA);
+		if (rc < 0)
+			pt_debug(cd->dev, DL_INFO, "%s: i2c unable to set 0 uAm rc = %d\n",
+				__func__, rc);
+
+	}
+	pt_debug(cd->dev, DL_INFO, "%s: Exit rc = %d I2C_SUSPEND_LOAD_UA\n", __func__, rc);
+
+	return rc;
+}
+
+#endif
+
+static int pt_enable_regulator(struct pt_core_data *cd, bool en)
+{
+	int rc;
+
+	if (!en) {
+		rc = 0;
+		goto disable_vcc_i2c_reg;
+	}
+
+	if (cd->vdd) {
+		if (regulator_count_voltages(cd->vdd) > 0) {
+			rc = regulator_set_voltage(cd->vdd, FT_VTG_MIN_UV,
+						FT_VTG_MAX_UV);
+			if (rc) {
+				dev_err(cd->dev,
+					"Regulator set_vtg failed vdd rc=%d\n", rc);
+				goto exit;
+			}
+		}
+
+		rc = regulator_enable(cd->vdd);
+		if (rc) {
+			dev_err(cd->dev,
+				"Regulator vdd enable failed rc=%d\n", rc);
+			goto exit;
+		}
+		dev_info(cd->dev, "%s: VDD regulator enabled:\n", __func__);
+	}
+
+	if (cd->vcc_i2c) {
+		if (regulator_count_voltages(cd->vcc_i2c) > 0) {
+			rc = regulator_set_voltage(cd->vcc_i2c, FT_I2C_VTG_MIN_UV,
+							FT_I2C_VTG_MAX_UV);
+			if (rc) {
+				dev_err(cd->dev,
+					"Regulator set_vtg failed vcc_i2c rc=%d\n", rc);
+				goto disable_vdd_reg;
+			}
+		}
+
+		rc = regulator_enable(cd->vcc_i2c);
+		if (rc) {
+			dev_err(cd->dev,
+				"Regulator vcc_i2c enable failed rc=%d\n", rc);
+			goto disable_vdd_reg;
+		}
+		dev_info(cd->dev, "%s: VCC I2C regulator enabled:\n", __func__);
+	}
+
+	return 0;
+
+disable_vcc_i2c_reg:
+	if (cd->vcc_i2c) {
+		if (regulator_count_voltages(cd->vcc_i2c) > 0)
+			regulator_set_voltage(cd->vcc_i2c, FT_I2C_VTG_MIN_UV,
+						FT_I2C_VTG_MAX_UV);
+
+		regulator_disable(cd->vcc_i2c);
+		dev_info(cd->dev, "%s: VCC I2C regulator disabled:\n", __func__);
+
+	}
+
+disable_vdd_reg:
+	if (cd->vdd) {
+		if (regulator_count_voltages(cd->vdd) > 0)
+			regulator_set_voltage(cd->vdd, FT_VTG_MIN_UV,
+						FT_VTG_MAX_UV);
+
+		regulator_disable(cd->vdd);
+		dev_info(cd->dev, "%s: VDD regulator disabled:\n", __func__);
+	}
+
+exit:
+	return rc;
+}
+
+
+#if (KERNEL_VERSION(3, 19, 0) <= LINUX_VERSION_CODE)
+#define KERNEL_VER_GT_3_19
+#endif
+
+#if defined(CONFIG_PM_RUNTIME) || defined(KERNEL_VER_GT_3_19)
+/* CONFIG_PM_RUNTIME option is removed in 3.19.0 */
+#if defined(CONFIG_PM_SLEEP)
+/*******************************************************************************
+ * FUNCTION: pt_core_rt_suspend
+ *
+ * SUMMARY: Wrapper function with PM Runtime stratergy to call pt_core_sleep.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *      *dev  - pointer to core device
+ ******************************************************************************/
+static int pt_core_rt_suspend(struct device *dev)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+
+	pt_debug(dev, DL_DEBUG, "%s Skip - probe state %d\n",
+		__func__, cd->core_probe_complete);
+
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_core_rt_resume
+ *
+ * SUMMARY: Wrapper function with PM Runtime stratergy to call pt_core_wake.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *      *dev  - pointer to core device
+ ******************************************************************************/
+static int pt_core_rt_resume(struct device *dev)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	pt_debug(dev, DL_DEBUG, "%s Skip - probe state %d\n",
+		__func__, cd->core_probe_complete);
+
+	return 0;
+}
+#endif /* CONFIG_PM_SLEEP */
+#endif /* CONFIG_PM_RUNTIME || LINUX_VERSION_CODE */
+
+#if defined(CONFIG_PM_SLEEP)
+/*******************************************************************************
+ * FUNCTION: pt_core_suspend_
+ *
+ * SUMMARY: Wrapper function with device suspend/resume stratergy to call
+ *  pt_core_sleep. This function may disable IRQ during sleep state.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *      *dev  - pointer to core device
+ ******************************************************************************/
+static int pt_core_suspend_(struct device *dev)
+{
+	int rc;
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+
+	pt_debug(dev, DL_INFO, "%s: Enter\n", __func__);
+	rc = pt_core_sleep(cd);
+	if (rc) {
+		pt_debug(dev, DL_ERROR, "%s: Error on sleep rc =%d\n",
+			__func__, rc);
+		return -EAGAIN;
+	}
+
+	rc = pt_enable_regulator(cd, false);
+	if (!IS_EASY_WAKE_CONFIGURED(cd->easy_wakeup_gesture))
+		return 0;
+
+	/* Required to prevent interrupts before bus awake */
+	disable_irq(cd->irq);
+	cd->irq_disabled = 1;
+
+	if (device_may_wakeup(dev)) {
+		pt_debug(dev, DL_WARN, "%s Device MAY wakeup\n",
+			__func__);
+		if (!enable_irq_wake(cd->irq))
+			cd->irq_wake = 1;
+	} else {
+		pt_debug(dev, DL_WARN, "%s Device MAY NOT wakeup\n",
+			__func__);
+	}
+
+	pt_debug(dev, DL_INFO, "%s: Exit :\n", __func__);
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_core_suspend
+ *
+ * SUMMARY: Wrapper function of pt_core_suspend_() to help avoid TP from being
+ *  woke up or put to sleep based on Kernel power state even when the display
+ *  is off based on the check of TTDL core platform flag.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *      *dev  - pointer to core device
+ ******************************************************************************/
+static int pt_core_suspend(struct device *dev)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	int rc = 0;
+
+	if (cd->drv_debug_suspend || (cd->cpdata->flags & PT_CORE_FLAG_SKIP_SYS_SLEEP))
+		return 0;
+
+	if (pt_core_state == STATE_SUSPEND)
+	{
+		pt_debug(cd->dev, DL_INFO, "%s Already in Suspend state\n", __func__);
+		return 0;
+	}
+
+	pt_debug(cd->dev, DL_INFO, "%s Suspend start\n", __func__);
+	cancel_work_sync(&cd->resume_work);
+	cancel_work_sync(&cd->suspend_work);
+
+	mutex_lock(&cd->system_lock);
+	cd->wait_until_wake = 0;
+	mutex_unlock(&cd->system_lock);
+
+	if (pm_suspend_via_firmware() || cd->touch_offload) {
+		rc = pt_core_suspend_(cd->dev);
+		cd->quick_boot = true;
+	} else {
+		rc = pt_enable_i2c_regulator(cd, false);
+		if (rc < 0)
+			pt_debug(cd->dev, DL_ERROR,
+				"%s: Error on disabling i2c regulator\n", __func__);
+	}
+	pt_debug(cd->dev, DL_INFO, "%s Suspend exit - rc = %d\n", __func__, rc);
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_core_resume_
+ *
+ * SUMMARY: Wrapper function with device suspend/resume stratergy to call
+ *  pt_core_wake. This function may enable IRQ before wake up.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *      *dev  - pointer to core device
+ ******************************************************************************/
+static int pt_core_resume_(struct device *dev)
+{
+	int rc = 0;
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+
+	dev_info(dev, "%s: Entering into resume mode:\n",
+		__func__);
+	rc = pt_enable_regulator(cd, true);
+	dev_info(dev, "%s: Voltage regulator enabled: rc=%d\n",
+		__func__, rc);
+
+	if (!IS_EASY_WAKE_CONFIGURED(cd->easy_wakeup_gesture))
+		goto exit;
+
+	/*
+	 * Bus pm does not call suspend if device runtime suspended
+	 * This flag is covers that case
+	 */
+	if (cd->irq_disabled) {
+		enable_irq(cd->irq);
+		cd->irq_disabled = 0;
+	}
+
+	if (device_may_wakeup(dev)) {
+		pt_debug(dev, DL_WARN, "%s Device MAY wakeup\n",
+			__func__);
+		if (cd->irq_wake) {
+			disable_irq_wake(cd->irq);
+			cd->irq_wake = 0;
+		}
+	} else {
+		pt_debug(dev, DL_WARN, "%s Device MAY NOT wakeup\n",
+			__func__);
+	}
+
+exit:
+	rc = pt_core_wake(cd);
+	if (rc) {
+		dev_err(dev, "%s: Failed to wake up: rc=%d\n",
+			__func__, rc);
+		return -EAGAIN;
+	}
+
+	return rc;
+}
+/*******************************************************************************
+ * FUNCTION: pt_core_restore
+ *
+ * SUMMARY: Wrapper function with device suspend/resume stratergy to call
+ *  pt_core_wake. This function may enable IRQ before wake up.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *      *dev  - pointer to core device
+ ******************************************************************************/
+static int pt_core_restore(struct device *dev)
+{
+	int rc = 0;
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+
+	dev_info(dev, "%s: Entering into resume mode:\n",
+		__func__);
+
+	queue_work(cd->pt_workqueue, &cd->resume_offload_work);
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: suspend_offload_work
+ *
+ * SUMMARY: Wrapper function of pt_core_suspend() to avoid TP to be waken/slept
+ *  along with kernel power state even the display is off based on the check of
+ *  TTDL core platform flag.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *      *dev  - pointer to core device
+ ******************************************************************************/
+static void pt_suspend_offload_work(struct work_struct *work)
+
+{
+	int rc = 0;
+	struct pt_core_data *cd = container_of(work, struct pt_core_data,
+				suspend_offload_work);
+
+	pt_debug(cd->dev, DL_INFO, "%s start\n", __func__);
+	if (cd->cpdata->flags & PT_CORE_FLAG_SKIP_SYS_SLEEP)
+		return;
+
+	rc = pt_core_suspend_(cd->dev);
+	pt_debug(cd->dev, DL_WARN, "%s Exit - rc = %d\n", __func__, rc);
+}
+
+/*******************************************************************************
+ * FUNCTION: resume_offload_work
+ *
+ * SUMMARY: Wrapper function of pt_core_resume_() to avoid TP to be waken/slept
+ *  along with kernel power state even the display is off based on the check of
+ *  TTDL core platform flag.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *      *dev  - pointer to core device
+ ******************************************************************************/
+static void pt_resume_offload_work(struct work_struct *work)
+
+{
+	int rc = 0;
+	int retry_count = 1000;
+	struct pt_core_data *pt_data = container_of(work, struct pt_core_data,
+					resume_offload_work);
+
+	pt_debug(pt_data->dev, DL_INFO, "%s start\n", __func__);
+	do {
+		retry_count--;
+	rc = pt_core_resume_(pt_data->dev);
+	if (rc < 0)
+		pt_debug(pt_data->dev, DL_ERROR,
+			"%s: Error on wake\n", __func__);
+	} while (retry_count && rc < 0);
+
+	if (rc < 0){
+		pt_debug(pt_data->dev, DL_ERROR, "%s: Error on wake\n", __func__);
+		return;
+	}
+
+#ifdef TOUCH_TO_WAKE_POWER_FEATURE_WORK_AROUND
+	rc = pt_core_easywake_on(pt_data);
+	if (rc < 0) {
+		pt_debug(pt_data->dev, DL_ERROR,
+			"%s: Error on enable touch to wake key\n",
+			__func__);
+		return;
+	}
+	pt_data->fb_state = FB_OFF;
+	pt_debug(pt_data->dev, DL_INFO, "%s End\n", __func__);
+#endif
+	pt_data->quick_boot = false;
+	pt_debug(pt_data->dev, DL_INFO, "%s Exit\n", __func__);
+}
+
+
+/*******************************************************************************
+ * FUNCTION: pt_core_resume
+ *
+ * SUMMARY: Wrapper function of pt_core_resume_() to avoid TP to be waken/slept
+ *  along with kernel power state even the display is off based on the check of
+ *  TTDL core platform flag.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *      *dev  - pointer to core device
+ ******************************************************************************/
+static int pt_core_resume(struct device *dev)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	int rc = 0;
+
+	if (cd->drv_debug_suspend || (cd->cpdata->flags & PT_CORE_FLAG_SKIP_SYS_SLEEP))
+		return 0;
+
+
+	if (pm_suspend_via_firmware() || cd->touch_offload) {
+			rc = pt_core_restore(cd->dev);
+	} else {
+		pt_debug(cd->dev, DL_INFO, "%s start\n", __func__);
+		rc = pt_enable_i2c_regulator(cd, true);
+		pt_debug(cd->dev, DL_INFO, "%s i2c regulator rc %d\n", __func__, rc);
+	}
+
+	mutex_lock(&cd->system_lock);
+	cd->wait_until_wake = 1;
+	mutex_unlock(&cd->system_lock);
+	wake_up(&cd->wait_q);
+
+	pt_debug(cd->dev, DL_INFO, "%s End rc = %d\n", __func__, rc);
+	return rc;
+}
+#endif
+
+#ifdef NEED_SUSPEND_NOTIFIER
+/*******************************************************************************
+ * FUNCTION: pt_pm_notifier
+ *
+ * SUMMARY: This function is registered to notifier chain and will perform
+ *  suspend operation if match event PM_SUSPEND_PREPARE.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *  *nb     - pointer to notifier_block structure
+ *   action - notifier event type
+ *  *data   - void pointer
+ ******************************************************************************/
+static int pt_pm_notifier(struct notifier_block *nb,
+		unsigned long action, void *data)
+{
+	struct pt_core_data *cd = container_of(nb,
+			struct pt_core_data, pm_notifier);
+	if (action == PM_SUSPEND_PREPARE) {
+		pt_debug(cd->dev, DL_INFO, "%s: Suspend prepare\n",
+			__func__);
+
+		/*
+		 * If PM runtime is not suspended, either call runtime
+		 * PM suspend callback or wait until it finishes
+		 */
+		if (!pm_runtime_suspended(cd->dev))
+			pm_runtime_suspend(cd->dev);
+
+		(void) pt_core_suspend(cd->dev);
+	}
+
+	return NOTIFY_DONE;
+}
+#endif
+
+const struct dev_pm_ops pt_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(pt_core_suspend, pt_core_resume)
+	.freeze  = pt_core_suspend,
+	.restore = pt_core_restore,
+	SET_RUNTIME_PM_OPS(pt_core_rt_suspend, pt_core_rt_resume,
+			NULL)
+};
+EXPORT_SYMBOL_GPL(pt_pm_ops);
+
+/*******************************************************************************
+ * FUNCTION: _pt_request_pip2_enter_bl
+ *
+ * SUMMARY: Force the DUT to enter the BL by resetting the DUT by use of the
+ *	XRES pin or a soft reset.
+ *
+ *	NOTE: The WD MUST be stopped/restarted by the calling Function. Having
+ *        the WD active could cause this function to fail!
+ *	NOTE: If start_mode is passed in as PT_MODE_IGNORE, this function
+ *	      will not try to determine the current mode but will proceed with
+ *	      resetting the DUT and entering the BL.
+ *
+ *	NOTE: The definition of result code:
+ *	      PT_ENTER_BL_PASS                (0)
+ *	      PT_ENTER_BL_ERROR               (1)
+ *	      PT_ENTER_BL_RESET_FAIL          (2)
+ *	      PT_ENTER_BL_HID_START_BL_FAIL   (3)
+ *	      PT_ENTER_BL_CONFIRM_FAIL        (4)
+ *	      PT_ENTER_BL_GET_FLASH_INFO_FAIL (5)
+ *
+ * RETURNS:
+ *	 0 = success
+ *	!0 = failure
+ *
+ *
+ * PARAMETERS:
+ *	*dev        - pointer to device structure
+ *	*start_mode - pointer to the mode the DUT was in when this function
+ *	              starts
+ *	*result     - pointer to store the result when to enter BL
+ ******************************************************************************/
+int _pt_request_pip2_enter_bl(struct device *dev, u8 *start_mode, int *result)
+{
+	int rc = 0;
+	int t;
+	int tmp_result = PT_ENTER_BL_ERROR;
+	int flash_info_retry = 2;
+	u8 mode = PT_MODE_UNKNOWN;
+	u8 sys_mode = FW_SYS_MODE_UNDEFINED;
+	u8 read_buf[32];
+	u16 actual_read_len;
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	u8 host_mode_cmd[4] = {0xA5, 0xA5, 0xA5, 0xA5};
+	u8 time = 0;
+	u8 saved_flashless_auto_bl_mode = cd->flashless_auto_bl;
+
+	if (cd->watchdog_enabled) {
+		pt_debug(dev, DL_WARN,
+			"%s: Watchdog must be stopped before entering BL\n",
+			__func__);
+		goto exit;
+	}
+
+	cancel_work_sync(&cd->enum_work);
+	cancel_work_sync(&cd->watchdog_work);
+
+	/* if undefined assume operational/test to bypass all checks */
+	if (*start_mode == PT_MODE_IGNORE) {
+		mode = PT_MODE_OPERATIONAL;
+		sys_mode = FW_SYS_MODE_TEST;
+		pt_debug(dev, DL_INFO, "%s: Assume Mode = %d", __func__, mode);
+	} else if (*start_mode == PT_MODE_UNKNOWN) {
+		rc = pt_pip2_get_mode_sysmode_(cd, &mode, &sys_mode);
+		if (rc) {
+			pt_debug(dev, DL_ERROR,
+				"%s: Get mode failed, mode unknown\n",
+				__func__);
+		}
+		*start_mode = mode;
+		pt_debug(dev, DL_INFO, "%s: Get Mode = %d", __func__, mode);
+	} else if (*start_mode == PT_MODE_OPERATIONAL) {
+		/* Assume SCANNIING mode to avoid doing an extra get_mode */
+		sys_mode = FW_SYS_MODE_SCANNING;
+	}
+
+_retry:
+	/* For Flashless DUTs - Suppress auto BL on next BL sentinel */
+	pt_debug(dev, DL_INFO, "%s: Flashless Auto_BL - SUPPRESS\n", __func__);
+	cd->flashless_auto_bl = PT_SUPPRESS_AUTO_BL;
+
+	switch (mode) {
+	case PT_MODE_UNKNOWN:
+		/*
+		 * When the mode could not be determined the DUT could be
+		 * in App mode running corrupted FW or FW that is not
+		 * responding to the mode request, assume no communication
+		 * and do a hard reset
+		 */
+		mutex_lock(&cd->system_lock);
+		cd->startup_status = STARTUP_STATUS_START;
+		pt_debug(dev, DL_DEBUG, "%s: Startup Status Reset\n", __func__);
+		mutex_unlock(&cd->system_lock);
+
+		rc = pt_dut_reset(cd, PT_CORE_CMD_UNPROTECTED);
+		if (rc) {
+			tmp_result = PT_ENTER_BL_RESET_FAIL;
+			goto exit;
+		}
+		break;
+
+	case PT_MODE_OPERATIONAL:
+		if (sys_mode == FW_SYS_MODE_SCANNING) {
+			pt_debug(dev, DL_INFO, "%s: Suspend Scanning\n",
+				__func__);
+			rc = pt_pip_suspend_scanning_(cd);
+			if (rc) {
+				/*
+				 * Print to log but don't exit, the FW could be
+				 * running but be hung or fail to respond to
+				 * this request
+				 */
+				pt_debug(dev, DL_ERROR,
+					"%s Suspend Scan Failed\n", __func__);
+			}
+			/* sleep to allow the suspend scan to be processed */
+			usleep_range(1000, 2000);
+		}
+
+		mutex_lock(&cd->system_lock);
+		cd->startup_status = STARTUP_STATUS_START;
+		pt_debug(dev, DL_DEBUG, "%s: Startup Status Reset\n", __func__);
+		mutex_unlock(&cd->system_lock);
+
+		/* Reset device to enter the BL */
+		rc = pt_dut_reset(cd, PT_CORE_CMD_UNPROTECTED);
+		if (rc) {
+			tmp_result = PT_ENTER_BL_RESET_FAIL;
+			goto exit;
+		}
+		break;
+
+	case PT_MODE_BOOTLOADER:
+		/* Do nothing as we are already in the BL */
+		tmp_result = PT_ENTER_BL_PASS;
+		goto exit;
+
+	default:
+		/* Should NEVER get here */
+		tmp_result = PT_ENTER_BL_ERROR;
+		pt_debug(dev, DL_ERROR, "%s: Unknown mode code\n", __func__);
+		goto exit;
+	}
+
+	if (!cd->flashless_dut &&
+	    (mode == PT_MODE_UNKNOWN || mode == PT_MODE_OPERATIONAL)) {
+		/*
+		 * Sending the special "Host Mode" command will instruct the
+		 * BL to not execute the FW it has loaded into RAM.
+		 * The command must be sent within a 40ms window from releasing
+		 * the XRES pin. If the messages is sent too early it will NAK,
+		 * so keep sending it every 2ms until it is accepted by the BL.
+		 * A no-flash DUT does not require this command as there is no
+		 * FW for the BL to load and execute.
+		 */
+		usleep_range(4000, 6000);
+		pt_debug(cd->dev, DL_INFO,
+			">>> %s: Write Buffer Size[%d] Stay in BL\n",
+			__func__, (int)sizeof(host_mode_cmd));
+
+		pt_pr_buf(cd->dev, DL_DEBUG, host_mode_cmd,
+			(int)sizeof(host_mode_cmd), ">>> User CMD");
+		rc = 1;
+		while (rc && time < 34) {
+			rc = pt_adap_write_read_specific(cd, 4,
+				host_mode_cmd, NULL);
+			usleep_range(1800, 2000);
+			time += 2;
+		}
+
+		/* Sleep to allow the BL to come up */
+		usleep_range(1000, 2000);
+	}
+
+	/*
+	 * To avoid the case that next PIP command can be confused by BL/FW
+	 * sentinel's "wakeup" event, chekcing hid_reset_cmd_state  which is
+	 * followed by "wakeup event" function can lower the failure rate.
+	 */
+	t = wait_event_timeout(cd->wait_q,
+		((cd->startup_status != STARTUP_STATUS_START)
+		 && (cd->hid_reset_cmd_state == 0)),
+		msecs_to_jiffies(300));
+	if (IS_TMO(t)) {
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: TMO waiting for BL sentinel\n", __func__);
+	}
+
+	/* Check if device is now in BL mode */
+	rc = pt_pip2_get_mode_sysmode_(cd, &mode, NULL);
+	pt_debug(dev, DL_INFO, "%s: Mode=%d, Status=0x%04X\n", __func__, mode,
+		cd->startup_status);
+	if (!rc && mode == PT_MODE_BOOTLOADER) {
+		pt_debug(dev, DL_INFO, "%s In bootloader mode now\n", __func__);
+		mutex_lock(&cd->system_lock);
+		cd->pip2_prot_active = true;
+		cd->mode = PT_MODE_BOOTLOADER;
+		mutex_unlock(&cd->system_lock);
+		tmp_result = PT_ENTER_BL_PASS;
+	} else {
+		/*
+		 * If the device doesn't enter BL mode as expected and rc is
+		 * tested pass by above function pt_pip2_get_mode_sysmode_(),
+		 * the function should return an error code to indicate this
+		 * failure PT_ENTER_BL_CONFIRM_FAIL.
+		 */
+		if (!rc)
+			rc = -EINVAL;
+		tmp_result = PT_ENTER_BL_CONFIRM_FAIL;
+		mutex_lock(&cd->system_lock);
+		cd->mode = mode;
+		mutex_unlock(&cd->system_lock);
+		pt_debug(dev, DL_ERROR,
+			"%s ERROR: Not in BL as expected", __func__);
+	}
+
+exit:
+	if (!cd->flashless_dut && (tmp_result == PT_ENTER_BL_PASS)) {
+		/* Check to get (buffered) flash information */
+		rc = _pt_request_pip2_send_cmd(dev, PT_CORE_CMD_UNPROTECTED,
+					       PIP2_CMD_ID_FLASH_INFO, NULL, 0,
+					       read_buf, &actual_read_len);
+		if (!rc) {
+			if (read_buf[PIP2_RESP_BODY_OFFSET] == 0) {
+				pt_debug(
+				    dev, DL_WARN,
+				    "%s Unavailable Manufacturer ID: 0x%02x\n",
+				    __func__,
+				    read_buf[PIP2_RESP_BODY_OFFSET]);
+				/*
+				 * If the BL was unable to cache the correct
+				 * values when entering the first time due to
+				 * the Flash part not having been powered up
+				 * long enough, re-enter the BL to trigger the
+				 * BL to re-attempt to cache the values.
+				 */
+				if (flash_info_retry-- > 0) {
+					mode = PT_MODE_UNKNOWN;
+					pt_debug(dev, DL_WARN,
+						 "%s Assume mode to UNKNOWN to enter BL again, retry=%d\n",
+						 __func__, flash_info_retry);
+					goto _retry;
+				} else {
+					pt_debug(dev, DL_WARN,
+						"%s Manufacturer ID Unknown\n",
+						__func__);
+					tmp_result = PT_ENTER_BL_PASS;
+				}
+			}
+		} else {
+			tmp_result = PT_ENTER_BL_GET_FLASH_INFO_FAIL;
+			pt_debug(
+			    dev, DL_ERROR,
+			    "%s: Failed to send PIP2 READ_FLASH_INFO cmd\n",
+			    __func__);
+		}
+	}
+
+	pt_debug(dev, DL_INFO, "%s Flashless Auto_BL - %s\n", __func__,
+		saved_flashless_auto_bl_mode == PT_ALLOW_AUTO_BL ? "ALLOW" :
+		"SUPPRESS");
+	cd->flashless_auto_bl = saved_flashless_auto_bl_mode;
+	if (result)
+		*result = tmp_result;
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_pip2_file_open
+ *
+ * SUMMARY: Using the BL PIP2 commands open a file and return the file handle
+ *
+ *	NOTE: The DUT must be in BL mode for this command to work
+ *
+ * RETURNS:
+ *	<0 = Error
+ *	>0 = file handle opened
+ *
+ * PARAMETERS:
+ *	*dev     - pointer to device structure
+ *	 file_no - PIP2 file number to open
+ ******************************************************************************/
+int _pt_pip2_file_open(struct device *dev, u8 file_no)
+{
+	int ret = 0;
+	u16 status;
+	u16 actual_read_len;
+	u8  file_handle;
+	u8  data[2];
+	u8  read_buf[10];
+
+	pt_debug(dev, DL_DEBUG, "%s: OPEN file %d\n", __func__, file_no);
+	data[0] = file_no;
+	ret = _pt_request_pip2_send_cmd(dev,
+		PT_CORE_CMD_UNPROTECTED, PIP2_CMD_ID_FILE_OPEN,
+		data, 1, read_buf, &actual_read_len);
+	if (ret) {
+		pt_debug(dev, DL_ERROR,
+			"%s ROM BL FILE_OPEN timeout for file = %d\n",
+			__func__, file_no);
+		return -PIP2_RSP_ERR_NOT_OPEN;
+	}
+
+	status = read_buf[PIP2_RESP_STATUS_OFFSET];
+	file_handle = read_buf[PIP2_RESP_BODY_OFFSET];
+	if (ret || ((status != 0x00) && (status != 0x03)) ||
+	    (file_handle != file_no)) {
+		pt_debug(dev, DL_ERROR,
+			"%s ROM BL FILE_OPEN failure: 0x%02X for file = %d\n",
+			__func__, status, file_handle);
+		return -status;
+	}
+	return file_handle;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_pip2_file_close
+ *
+ * SUMMARY: Using the BL PIP2 commands close a file
+ *
+ *	NOTE: The DUT must be in BL mode for this command to work
+ *
+ * RETURNS:
+ *	<0 = Error
+ *	>0 = file handle closed
+ *
+ * PARAMETERS:
+ *	*dev         - pointer to device structure
+ *	 file_handle - handle to the file to be closed
+ ******************************************************************************/
+int _pt_pip2_file_close(struct device *dev, u8 file_handle)
+{
+	int ret = 0;
+	u16 status;
+	u16 actual_read_len;
+	u8  data[2];
+	u8 read_buf[32];
+
+	pt_debug(dev, DL_DEBUG, "%s: CLOSE file %d\n", __func__, file_handle);
+	data[0] = file_handle;
+	ret = _pt_request_pip2_send_cmd(dev,
+		PT_CORE_CMD_UNPROTECTED, PIP2_CMD_ID_FILE_CLOSE,
+		data, 1, read_buf, &actual_read_len);
+	if (ret) {
+		pt_debug(dev, DL_ERROR,
+			"%s ROM BL FILE_CLOSE timeout for file = %d\n",
+			__func__, file_handle);
+		return -ETIME;
+	}
+
+	status = read_buf[PIP2_RESP_STATUS_OFFSET];
+	if (status != 0x00) {
+		pt_debug(dev, DL_ERROR,
+			"%s ROM BL FILE_CLOSE failure: 0x%02X for file = %d\n",
+			__func__, status, file_handle);
+		return -status;
+	}
+	return file_handle;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_pip2_file_erase
+ *
+ * SUMMARY: Using the BL PIP2 commands erase a file
+ *
+ *	NOTE: The DUT must be in BL mode for this command to work
+ *	NOTE: Some FLASH parts can time out while erasing one or more sectors,
+ *		one retry is attempted for each sector in a file.
+ *
+ * RETURNS:
+ *	<0 = Error
+ *	>0 = file handle closed
+ *
+ * PARAMETERS:
+ *	*dev         - pointer to device structure
+ *	 file_handle - handle to the file to be erased
+ *	 *status     - PIP2 erase status code
+ ******************************************************************************/
+static int _pt_pip2_file_erase(struct device *dev, u8 file_handle, int *status)
+{
+	int ret = 0;
+	int max_retry = PT_PIP2_MAX_FILE_SIZE/PT_PIP2_FILE_SECTOR_SIZE;
+	int retry = 1;
+	u16 actual_read_len;
+	u8  data[2];
+	u8  read_buf[10];
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+
+	pt_debug(dev, DL_DEBUG, "%s: ERASE file %d\n", __func__, file_handle);
+	data[0] = file_handle;
+	data[1] = PIP2_FILE_IOCTL_CODE_ERASE_FILE;
+	*status = PIP2_RSP_ERR_TIMEOUT;
+
+	/* Increase waiting time for large file erase */
+	mutex_lock(&cd->system_lock);
+	cd->pip_cmd_timeout = PT_PIP2_CMD_FILE_ERASE_TIMEOUT;
+	mutex_unlock(&cd->system_lock);
+	while (*status == PIP2_RSP_ERR_TIMEOUT && retry < max_retry) {
+		ret = _pt_request_pip2_send_cmd(dev,
+			PT_CORE_CMD_UNPROTECTED, PIP2_CMD_ID_FILE_IOCTL,
+			data, 2, read_buf, &actual_read_len);
+		if (ret)
+			break;
+		*status = read_buf[PIP2_RESP_STATUS_OFFSET];
+		if (*status == PIP2_RSP_ERR_TIMEOUT) {
+#ifdef TTDL_DIAGNOSTICS
+			cd->file_erase_timeout_count++;
+#endif
+			pt_debug(dev, DL_WARN,
+				"%s: ERASE timeout %d for file = %d\n",
+				__func__, retry, file_handle);
+		}
+		retry++;
+	}
+	mutex_lock(&cd->system_lock);
+	cd->pip_cmd_timeout = cd->pip_cmd_timeout_default;
+	mutex_unlock(&cd->system_lock);
+	if (ret) {
+		pt_debug(dev, DL_ERROR,
+			"%s ROM FILE_ERASE cmd failure: %d for file = %d\n",
+			__func__, ret, file_handle);
+		return -EIO;
+	}
+
+	if (*status != 0x00) {
+		pt_debug(dev, DL_ERROR,
+			"%s ROM FILE_ERASE failure: 0x%02X for file = %d\n",
+			__func__, *status, file_handle);
+		return -EIO;
+	}
+	return file_handle;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_pip2_file_read
+ *
+ * SUMMARY: Using the BL PIP2 commands read n bytes from a already opened file
+ *
+ *	NOTE: The DUT must be in BL mode for this command to work
+ *
+ * RETURNS:
+ *	<0 = Error
+ *	>0 = number of bytes read
+ *
+ * PARAMETERS:
+ *	*dev         - pointer to device structure
+ *	 file_handle - File handle to read from
+ *	 num_bytes   - number of bytes to read
+ ******************************************************************************/
+int _pt_pip2_file_read(struct device *dev, u8 file_handle, u16 num_bytes,
+	u8 *read_buf)
+{
+	int ret = 0;
+	u16 status;
+	u16 actual_read_len;
+	u8  data[3];
+
+	data[0] = file_handle;
+	data[1] = (num_bytes & 0x00FF);
+	data[2] = (num_bytes & 0xFF00) >> 8;
+
+	ret = _pt_request_pip2_send_cmd(dev,
+		PT_CORE_CMD_UNPROTECTED, PIP2_CMD_ID_FILE_READ,
+		data, 3, read_buf, &actual_read_len);
+	status = read_buf[PIP2_RESP_STATUS_OFFSET];
+	if (ret || ((status != 0x00) && (status != 0x03))) {
+		pt_debug(dev, DL_ERROR,
+			"%s File open failure with error code = %d\n",
+			__func__, status);
+		return -EPERM;
+	}
+	ret = num_bytes;
+
+	return ret;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_read_us_file
+ *
+ * SUMMARY: Open a user space file and read 'size' bytes into buf. If size = 0
+ *	then read the entire file.
+ *      NOTE: The file size must be less than PT_PIP2_MAX_FILE_SIZE
+ *
+ * RETURN:
+ *       0 = success
+ *      !0 = failure
+ *
+ * PARAMETERS:
+ *      *file_path - pointer to the file path
+ *      *buf       - pointer to the buffer to store the file contents
+ *      *size      - pointer to the size of the file
+ ******************************************************************************/
+int _pt_read_us_file(struct device *dev, u8 *file_path, u8 *buf, int *size)
+{
+	struct file *filp = NULL;
+	struct inode *inode = NULL;
+	unsigned int file_len = 0;
+	unsigned int read_len = 0;
+	mm_segment_t oldfs;
+	int rc = 0;
+
+	if (file_path == NULL || buf == NULL) {
+		pt_debug(dev, DL_ERROR, "%s: path || buf is NULL.\n", __func__);
+		return -EINVAL;
+	}
+	pt_debug(dev, DL_WARN, "%s: path = %s\n", __func__, file_path);
+
+	oldfs = force_uaccess_begin();
+	filp = filp_open_block(file_path, O_RDONLY, 0400);
+
+	if (IS_ERR(filp)) {
+		pt_debug(dev, DL_ERROR, "%s: Failed to open %s\n", __func__,
+			file_path);
+		rc = -ENOENT;
+		goto err;
+	}
+
+	if (filp->f_op == NULL) {
+		pt_debug(dev, DL_ERROR, "%s: File Operation Method Error\n",
+			__func__);
+		rc = -EINVAL;
+		goto exit;
+	}
+	inode = filp->f_path.dentry->d_inode;
+	if (inode == NULL) {
+		pt_debug(dev, DL_ERROR, "%s: Get inode from filp failed\n",
+			__func__);
+		rc = -EINVAL;
+		goto exit;
+	}
+	file_len = i_size_read(inode->i_mapping->host);
+	if (file_len == 0) {
+		pt_debug(dev, DL_ERROR, "%s: file size error,file_len = %d\n",
+			__func__, file_len);
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	if (*size == 0)
+		read_len = file_len;
+	else
+		read_len = *size;
+
+	if (read_len > PT_PIP2_MAX_FILE_SIZE) {
+		pt_debug(dev, DL_ERROR, "%s: file size ( %d ) exception.\n",
+			__func__, read_len);
+		rc = -EINVAL;
+		goto exit;
+	}
+	filp->private_data = inode->i_private;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0))
+	if (filp->f_op->read(filp, buf, read_len, &(filp->f_pos)) != read_len) {
+#else
+	if (vfs_read(filp, buf, read_len, &(filp->f_pos)) != read_len) {
+#endif
+		pt_debug(dev, DL_ERROR, "%s: file read error.\n", __func__);
+		rc = -EINVAL;
+		goto exit;
+	}
+	*size = read_len;
+
+exit:
+	if (filp_close(filp, NULL) != 0)
+		pt_debug(dev, DL_ERROR, "%s: file close error.\n", __func__);
+err:
+	force_uaccess_end(oldfs);
+	return rc;
+
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_request_pip2_bin_hdr
+ *
+ * SUMMARY: Read the stored bin file header from Flash or the User Space file
+ *	in the case of a flashless DUT, and parse the contents
+ *
+ * RETURNS:
+ *	 0 = Success
+ *	!0 = Error condition
+ *
+ * PARAMETERS:
+ *	*dev - pointer to device structure
+ ******************************************************************************/
+int _pt_request_pip2_bin_hdr(struct device *dev, struct pt_bin_file_hdr *hdr)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	u8  file_handle;
+	u8  read_buf[255];
+	u8  hdr_len = 0;
+	u8  i;
+	int bytes_read;
+	int read_size;
+	int ret = 0;
+	int rc = 0;
+	bool load_hdr_struct = false;
+
+	if (cd->flashless_dut) {
+		read_size = sizeof(read_buf);
+		rc = _pt_read_us_file(dev, cd->pip2_us_file_path,
+			read_buf, &read_size);
+		if (rc) {
+			ret = rc;
+			pt_debug(dev, DL_ERROR,
+				"%s Failed to read fw image from US, rc=%d\n",
+				__func__, rc);
+			goto exit;
+		}
+		load_hdr_struct = true;
+		hdr_len = read_buf[0];
+		i = 0;
+	} else {
+		if (cd->mode != PT_MODE_BOOTLOADER) {
+			ret = -EPERM;
+			goto exit;
+		}
+
+		/* Open the bin file in Flash */
+		pt_debug(dev, DL_INFO, "%s Open File 1\n", __func__);
+		file_handle = _pt_pip2_file_open(dev, PIP2_FW_FILE);
+		if (file_handle != PIP2_FW_FILE) {
+			ret = -ENOENT;
+			pt_debug(dev, DL_ERROR,
+				"%s Failed to open bin file\n", __func__);
+			goto exit;
+		}
+
+		/* Read the header length from the file */
+		pt_debug(dev, DL_INFO, "%s Read length of header\n", __func__);
+		read_size = 1;
+		bytes_read = _pt_pip2_file_read(dev, file_handle, read_size,
+				read_buf);
+		if (bytes_read != read_size) {
+			ret = -EX_ERR_FREAD;
+			pt_debug(dev, DL_ERROR,
+				"%s Failed to bin file header len\n", __func__);
+			goto exit_close_file;
+		}
+		hdr_len = read_buf[PIP2_RESP_BODY_OFFSET];
+		if (hdr_len == 0xFF) {
+			ret = -EX_ERR_FLEN;
+			pt_debug(dev, DL_ERROR,
+				"%s Bin header len is invalid\n", __func__);
+			goto exit_close_file;
+		}
+
+		/* Read the rest of the header from the bin file */
+		pt_debug(dev, DL_INFO, "%s Read bin file header\n", __func__);
+		memset(read_buf, 0, sizeof(read_buf));
+		bytes_read = _pt_pip2_file_read(dev, file_handle, hdr_len,
+				read_buf);
+		if (bytes_read != hdr_len) {
+			ret = -EX_ERR_FREAD;
+			pt_debug(dev, DL_ERROR,
+				"%s Failed to read bin file\n", __func__);
+			goto exit_close_file;
+		}
+		load_hdr_struct = true;
+
+exit_close_file:
+		/* Close the file */
+		if (file_handle != _pt_pip2_file_close(dev, file_handle)) {
+			ret = -EX_ERR_FCLOSE;
+			pt_debug(dev, DL_ERROR,
+				"%s Failed to close bin file\n", __func__);
+		}
+
+		/*
+		 * The length was already read so subtract 1 to make the rest of
+		 * the offsets match the spec
+		 */
+		i = PIP2_RESP_BODY_OFFSET - 1;
+	}
+
+	if (load_hdr_struct) {
+		hdr->length      = hdr_len;
+		hdr->ttpid       = (read_buf[i+1] << 8) | read_buf[i+2];
+		hdr->fw_major    = (read_buf[i+3]);
+		hdr->fw_minor    = (read_buf[i+4]);
+		hdr->fw_crc      = (read_buf[i+5] << 24) |
+				   (read_buf[i+6] << 16) |
+				   (read_buf[i+7] << 8)  |
+				   (read_buf[i+8]);
+		hdr->fw_rev_ctrl = (read_buf[i+9]  << 24) |
+				   (read_buf[i+10] << 16) |
+				   (read_buf[i+11] << 8)  |
+				   (read_buf[i+12]);
+		hdr->si_rev      = (read_buf[i+14] << 8)  | (read_buf[i+13]);
+		hdr->si_id       = (read_buf[i+16] << 8)  | (read_buf[i+15]);
+		hdr->config_ver  = (read_buf[i+17] << 8)  | (read_buf[i+18]);
+		if (hdr_len >= 22) {
+			hdr->hex_file_size = (read_buf[i+19] << 24) |
+					     (read_buf[i+20] << 16) |
+					     (read_buf[i+21] << 8) |
+					     (read_buf[i+22]);
+		} else {
+			hdr->hex_file_size = 0;
+		}
+	}
+
+exit:
+	return ret;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_pip2_file_get_stats
+ *
+ * SUMMARY: Using the BL PIP2 commands get file information from an already
+ *  opened file
+ *
+ *	NOTE: The DUT must be in BL mode for this command to work
+ *
+ * RETURNS:
+ *	!0 = Error
+ *	 0 = Success
+ *
+ * PARAMETERS:
+ *	*dev         - pointer to device structure
+ *	 file_handle - File handle to read from
+ *	*address     - pointer to store address of file
+ *	*file_size   _ pointer to store size of file
+ ******************************************************************************/
+int _pt_pip2_file_get_stats(struct device *dev, u8 file_handle, u32 *address,
+	u32 *file_size)
+{
+	int ret = 1;
+	u16 status;
+	u16 actual_read_len;
+	u8  data[2];
+	u8  read_buf[16];
+
+	data[0] = file_handle;
+	data[1] = PIP2_FILE_IOCTL_CODE_FILE_STATS;
+
+	ret = _pt_request_pip2_send_cmd(dev,
+		PT_CORE_CMD_UNPROTECTED, PIP2_CMD_ID_FILE_IOCTL,
+		data, 2, read_buf, &actual_read_len);
+
+	status = read_buf[PIP2_RESP_STATUS_OFFSET];
+	if (ret || (status != 0x00)) {
+		pt_debug(dev, DL_ERROR,
+			"%s ROM FILE_STATS failure: 0x%02X for file = %d, ret = %d\n",
+			__func__, status, file_handle, ret);
+		ret = -EIO;
+		goto exit;
+	}
+
+	pt_debug(dev, DL_DEBUG,
+		"%s --- FILE %d Information ---\n", __func__, file_handle);
+
+	if (address) {
+		*address = read_buf[PIP2_RESP_BODY_OFFSET] +
+			(read_buf[PIP2_RESP_BODY_OFFSET + 1] << 8) +
+			(read_buf[PIP2_RESP_BODY_OFFSET + 2] << 16) +
+			(read_buf[PIP2_RESP_BODY_OFFSET + 3] << 24);
+		pt_debug(dev, DL_DEBUG, "%s Address: 0x%08x\n",
+			__func__, *address);
+	}
+
+	if (file_size) {
+		*file_size = read_buf[PIP2_RESP_BODY_OFFSET + 4] +
+			(read_buf[PIP2_RESP_BODY_OFFSET + 5] << 8) +
+			(read_buf[PIP2_RESP_BODY_OFFSET + 6] << 16) +
+			(read_buf[PIP2_RESP_BODY_OFFSET + 7] << 24);
+		pt_debug(dev, DL_DEBUG, "%s Size   : 0x%08x\n",
+			__func__, *file_size);
+	}
+
+exit:
+	return ret;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_pip2_file_seek_offset
+ *
+ * SUMMARY: Using the BL PIP2 commands seek read/write offset for an already
+ *  opened file
+ *
+ *	NOTE: The DUT must be in BL mode for this command to work
+ *	NOTE: File open/erase command can reset the offset
+ *
+ * RETURNS:
+ *	!0 = Error
+ *	 0 = Success
+ *
+ * PARAMETERS:
+ *	*dev          - pointer to device structure
+ *	 file_handle  - File handle to read from
+ *	 read_offset  - read offset of file
+ *	 write_offset - write offset of file
+ ******************************************************************************/
+int _pt_pip2_file_seek_offset(struct device *dev, u8 file_handle,
+	u32 read_offset, u32 write_offset)
+{
+	int ret = 1;
+	u16 status;
+	u16 actual_read_len;
+	u8  data[10];
+	u8  read_buf[16];
+
+	data[0] = file_handle;
+	data[1] = PIP2_FILE_IOCTL_CODE_SEEK_POINTER;
+	data[2] = 0xff & read_offset;
+	data[3] = 0xff & (read_offset >> 8);
+	data[4] = 0xff & (read_offset >> 16);
+	data[5] = 0xff & (read_offset >> 24);
+	data[6] = 0xff & write_offset;
+	data[7] = 0xff & (write_offset >> 8);
+	data[8] = 0xff & (write_offset >> 16);
+	data[9] = 0xff & (write_offset >> 24);
+
+	ret = _pt_request_pip2_send_cmd(dev,
+		PT_CORE_CMD_UNPROTECTED, PIP2_CMD_ID_FILE_IOCTL,
+		data, 10, read_buf, &actual_read_len);
+
+	status = read_buf[PIP2_RESP_STATUS_OFFSET];
+	if (ret || (status != 0x00)) {
+		pt_debug(dev, DL_ERROR,
+			"%s ROM FILE_SEEK failure: 0x%02X for file = %d, ret = %d\n",
+			__func__, status, ret, file_handle);
+		ret = -EIO;
+	}
+	return ret;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_pip2_file_crc
+ *
+ * SUMMARY: Using the BL PIP2 commands to calculate CRC for a file or portion of
+ *   the file.
+ *
+ *	NOTE: The DUT must be in BL mode for this command to work
+ *	NOTE: This command only can be used for BL version 1.8 or greater.
+ *        BL version 1.8 added this change according to PGV-173.
+ *
+ * RETURNS:
+ *	!0 = Error
+ *	 0 = Success
+ *
+ * PARAMETERS:
+ *	*dev          - pointer to device structure
+ *	 file_handle  - File handle to read from
+ *	 offset       - start offset for CRC calculation
+ *	 length       - number of bytes to calculate CRC over
+ *   read_buf     - pointer to the read buffer
+ ******************************************************************************/
+int _pt_pip2_file_crc(struct device *dev, u8 file_handle,
+	u32 offset, u32 length, u8 *read_buf)
+{
+	int rc = 1;
+	u16 actual_read_len;
+	u8  data[10];
+
+	data[0] = file_handle;
+	data[1] = PIP2_FILE_IOCTL_CODE_FILE_CRC;
+	data[2] = 0xff & offset;
+	data[3] = 0xff & (offset >> 8);
+	data[4] = 0xff & (offset >> 16);
+	data[5] = 0xff & (offset >> 24);
+	data[6] = 0xff & length;
+	data[7] = 0xff & (length >> 8);
+	data[8] = 0xff & (length >> 16);
+	data[9] = 0xff & (length >> 24);
+
+	rc = _pt_request_pip2_send_cmd(dev,
+		PT_CORE_CMD_UNPROTECTED, PIP2_CMD_ID_FILE_IOCTL,
+		data, 10, read_buf, &actual_read_len);
+
+	if (rc)
+		pt_debug(dev, DL_ERROR,
+			"%s Return FILE_CRC failure, rc = %d\n",
+			__func__, rc);
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pip2_ping_test
+ *
+ * SUMMARY: BIST type test that uses the PIP2 PING command and ramps up the
+ *	optional payload from 0 bytes to max_bytes and ensures the PIP2
+ *	response payload matches what was sent.
+ *	The max payload size is 247:
+ *		(255 - 2 byte reg address - 4 byte header - 2 byte CRC)
+ *
+ * RETURN:
+ *       0 = success
+ *      !0 = failure
+ *
+ * PARAMETERS:
+ *      *dev  - pointer to device structure
+ *      *attr - pointer to device attributes
+ *      *buf  - pointer to output buffer
+ ******************************************************************************/
+int pt_pip2_ping_test(struct device *dev, int max_bytes, int *last_packet_size)
+{
+	u16 actual_read_len;
+	u8 *read_buf = NULL;
+	u8 *data = NULL;
+	int data_offset = PIP2_RESP_STATUS_OFFSET;
+	int i = 1;
+	int j = 0;
+	int rc = 0;
+
+	read_buf = kzalloc(PT_MAX_PIP2_MSG_SIZE, GFP_KERNEL);
+	if (!read_buf)
+		goto ping_test_exit;
+
+	data = kzalloc(PT_MAX_PIP2_MSG_SIZE, GFP_KERNEL);
+	if (!data)
+		goto ping_test_exit;
+
+	/* Load data payload with an array of walking 1's */
+	for (i = 0; i < 255; i++)
+		data[i] = 0x01 << (i % 8);
+
+	/* Send 'max_bytes' PING cmds using 'i' bytes as payload for each */
+	for (i = 0; i <= max_bytes; i++) {
+		rc = _pt_request_pip2_send_cmd(dev, PT_CORE_CMD_UNPROTECTED,
+			PIP2_CMD_ID_PING, data, i, read_buf,
+			&actual_read_len);
+		if (rc) {
+			pt_debug(dev, DL_ERROR,
+				"%s: PING failed with %d byte payload\n",
+				 __func__, i);
+			break;
+		}
+		/* Verify data returned matches data sent */
+		for (j = 0; j < i; j++) {
+			if (read_buf[data_offset + j] != data[j]) {
+				pt_debug(dev, DL_DEBUG,
+					"%s: PING packet size %d: sent[%d]=0x%02X recv[%d]=0x%02X\n",
+					__func__, i, j, data[j], j,
+					read_buf[data_offset + j]);
+				goto ping_test_exit;
+			}
+		}
+	}
+
+ping_test_exit:
+	*last_packet_size = i - 1;
+	kfree(read_buf);
+	kfree(data);
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_ic_parse_input_hex
+ *
+ * SUMMARY: Parse a char data array as space delimited hex values into
+ *	an int array.
+ *
+ * NOTE: _pt_ic_parse_input() function may have similar work while the type of
+ *  buffer to store data is "u32". This function is still needed by the
+ *  "command" sysfs node because the buffer type to store data is "u8".
+ *
+ * RETURN: Length of parsed data
+ *
+ * PARAMETERS:
+ *	*dev         - pointer to device structure
+ *	*buf         - pointer to buffer that holds the input array to parse
+ *	 buf_size    - size of buf
+ *	*ic_buf      - pointer to array to store parsed data
+ *	 ic_buf_size - max size of ic_buf
+ ******************************************************************************/
+static int _pt_ic_parse_input_hex(struct device *dev, const char *buf,
+	size_t buf_size, u8 *ic_buf, size_t ic_buf_size)
+{
+	const char *pbuf = buf;
+	unsigned long value;
+	char scan_buf[PT_MAX_PIP2_MSG_SIZE + 1];
+	u32 i = 0;
+	u32 j;
+	int last = 0;
+	int ret;
+
+	pt_debug(dev, DL_DEBUG,
+		"%s: pbuf=%p buf=%p size=%zu %s=%d buf=%s\n",
+		__func__, pbuf, buf, buf_size, "scan buf size",
+		PT_MAX_PIP2_MSG_SIZE, buf);
+
+	while (pbuf <= (buf + buf_size)) {
+		if (i >= PT_MAX_PIP2_MSG_SIZE) {
+			pt_debug(dev, DL_ERROR, "%s: %s size=%d max=%d\n",
+				__func__, "Max cmd size exceeded", i,
+				PT_MAX_PIP2_MSG_SIZE);
+			return -EINVAL;
+		}
+		if (i >= ic_buf_size) {
+			pt_debug(dev, DL_ERROR, "%s: %s size=%d buf_size=%zu\n",
+				__func__, "Buffer size exceeded", i,
+				ic_buf_size);
+			return -EINVAL;
+		}
+		while (((*pbuf == ' ') || (*pbuf == ','))
+			&& (pbuf < (buf + buf_size))) {
+			last = *pbuf;
+			pbuf++;
+		}
+
+		if (pbuf >= (buf + buf_size))
+			break;
+
+		memset(scan_buf, 0, PT_MAX_PIP2_MSG_SIZE);
+		if ((last == ',') && (*pbuf == ',')) {
+			pt_debug(dev, DL_ERROR, "%s: %s \",,\" not allowed.\n",
+				__func__, "Invalid data format.");
+			return -EINVAL;
+		}
+		for (j = 0; j < (PT_MAX_PIP2_MSG_SIZE - 1)
+		     && (pbuf < (buf + buf_size))
+		     && (*pbuf != ' ')
+		     && (*pbuf != ','); j++) {
+			last = *pbuf;
+			scan_buf[j] = *pbuf++;
+		}
+		ret = kstrtoul(scan_buf, 16, &value);
+		if (ret < 0) {
+			pt_debug(dev, DL_ERROR,
+				"%s: %s '%s' %s%s i=%d r=%d\n", __func__,
+				"Invalid data format. ", scan_buf,
+				"Use \"0xHH,...,0xHH\"", " instead.",
+				i, ret);
+			return ret;
+		}
+
+		ic_buf[i] = value;
+		pt_debug(dev, DL_DEBUG, "%s: item = %d, value = 0x%02lx",
+			__func__, i, value);
+		i++;
+	}
+
+	return i;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_ic_parse_input
+ *
+ * SUMMARY: Parse user sysfs input data as a space or comma delimited list of
+ * hex values or dec values into an int array with the following rules:
+ *  1) Hex values must have a "0x" prefix for each element or the first element
+ *     only
+ *  2) Dec values do not have any prefix
+ *  3) It is not allowed to have a mix of dec and hex values in the user input
+ *     string
+ *
+ * RETURN: Number of values parsed
+ *
+ * PARAMETERS:
+ *	*dev         - pointer to device structure
+ *	*buf         - pointer to buffer that holds the input array to parse
+ *	 buf_size    - size of buf
+ *	*out_buf      - pointer to array to store parsed data
+ *	 out_buf_size - max size of buffer to be stored
+ ******************************************************************************/
+static int _pt_ic_parse_input(struct device *dev,
+	const char *buf, size_t buf_size,
+	u32 *out_buf, size_t out_buf_size)
+{
+	const char *pbuf = buf;
+	unsigned long value;
+	char scan_buf[PT_MAX_PIP2_MSG_SIZE + 1];
+	u32 i = 0;
+	u32 j;
+	int last = 0;
+	int ret = 0;
+	u8 str_base = 0;
+
+	pt_debug(dev, DL_DEBUG,
+		"%s: in_buf_size=%zu out_buf_size=%zu %s=%d buf=%s\n",
+		__func__, buf_size, out_buf_size, "scan buf size",
+		PT_MAX_PIP2_MSG_SIZE, buf);
+
+	while (pbuf <= (buf + buf_size)) {
+		if (i >= PT_MAX_PIP2_MSG_SIZE) {
+			pt_debug(dev, DL_ERROR, "%s: %s size=%d max=%d\n",
+				__func__, "Max cmd size exceeded", i,
+				PT_MAX_PIP2_MSG_SIZE);
+			ret = -EINVAL;
+			goto error;
+		}
+		if (i >= out_buf_size) {
+			pt_debug(dev, DL_ERROR, "%s: %s size=%d buf_size=%zu\n",
+				__func__, "Buffer size exceeded", i,
+				out_buf_size);
+			ret = -EINVAL;
+			goto error;
+		}
+		while (((*pbuf == ' ') || (*pbuf == ','))
+			&& (pbuf < (buf + buf_size))) {
+			last = *pbuf;
+			pbuf++;
+		}
+
+		if (pbuf >= (buf + buf_size))
+			break;
+
+		memset(scan_buf, 0, PT_MAX_PIP2_MSG_SIZE);
+		if ((last == ',') && (*pbuf == ',')) {
+			pt_debug(dev, DL_ERROR, "%s: %s \",,\" not allowed.\n",
+				__func__, "Invalid data format.");
+			ret = -EINVAL;
+			goto error;
+		}
+		for (j = 0; j < (PT_MAX_PIP2_MSG_SIZE - 1)
+		     && (pbuf < (buf + buf_size))
+		     && (*pbuf != ' ')
+		     && (*pbuf != ','); j++) {
+			last = *pbuf;
+			scan_buf[j] = *pbuf++;
+		}
+
+		if (i == 0) {
+			if ((strncmp(scan_buf, "0x", 2) == 0) ||
+					(strncmp(scan_buf, "0X", 2) == 0))
+				str_base = 16;
+			else
+				str_base = 10;
+		} else {
+			if (((strncmp(scan_buf, "0x", 2) == 0) ||
+				(strncmp(scan_buf, "0X", 2) == 0)) &&
+				(str_base == 10)) {
+				pt_debug(dev, DL_ERROR,
+					"%s: Decimal and Heximal data mixed\n",
+					__func__);
+				ret = -EINVAL;
+				goto error;
+			}
+		}
+		ret = kstrtoul(scan_buf, str_base, &value);
+		if (ret < 0) {
+			pt_debug(dev, DL_ERROR,
+				"%s: %s '%s' %s%s i=%d r=%d\n", __func__,
+				"Invalid data format. ", scan_buf,
+				"Use \"0xHH,...,0xHH\" or \"DD DD DD ... DD\"",
+				" instead.", i, ret);
+			goto error;
+		}
+
+		out_buf[i] = value;
+		pt_debug(dev, DL_DEBUG, "%s: item = %d, value = 0x%02lx(%lu)",
+			__func__, i, value, value);
+		i++;
+	}
+	ret = i;
+error:
+	return ret;
+}
+
+#ifdef TTHE_TUNER_SUPPORT
+/*******************************************************************************
+ * FUNCTION: tthe_debugfs_open
+ *
+ * SUMMARY: Open method for tthe_tuner debugfs node. On some hosts the size of
+ *	PT_MAX_PRBUF_SIZE (equal to PAGE_SIZE) is not large enough to handle
+ *	printing a large number of fingers and sensor data without overflowing
+ *	the buffer. tthe_tuner needs ~4K and so the buffer is sized to some
+ *	even multiple of PAGE_SIZE
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *      *inode - file inode number
+ *      *filp  - file pointer to debugfs file
+ ******************************************************************************/
+static int tthe_debugfs_open(struct inode *inode, struct file *filp)
+{
+	struct pt_core_data *cd = inode->i_private;
+	u32 buf_size = PT_MAX_PRBUF_SIZE;
+
+	filp->private_data = inode->i_private;
+	if (cd->tthe_buf)
+		return -EBUSY;
+
+	while (buf_size < 4096)
+		buf_size = buf_size << 1;
+
+	pt_debug(cd->dev, DL_INFO, "%s:PT_MAX_BRBUF_SIZE=%d buf_size=%d\n",
+		__func__, (int)PT_MAX_PRBUF_SIZE, (int)buf_size);
+
+	cd->tthe_buf_size = buf_size;
+	cd->tthe_buf = kzalloc(cd->tthe_buf_size, GFP_KERNEL);
+	if (!cd->tthe_buf)
+		return -ENOMEM;
+
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: tthe_debugfs_close
+ *
+ * SUMMARY: Close method for tthe_tuner debugfs node.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *      *inode - file inode number
+ *      *filp  - file pointer to debugfs file
+ ******************************************************************************/
+static int tthe_debugfs_close(struct inode *inode, struct file *filp)
+{
+	struct pt_core_data *cd = filp->private_data;
+
+	filp->private_data = NULL;
+	kfree(cd->tthe_buf);
+	cd->tthe_buf = NULL;
+
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: tthe_debugfs_read
+ *
+ * SUMMARY: Read method for tthe_tuner debugfs node. This function prints
+ *	tthe_buf to user buffer.
+ *
+ * RETURN: Size of debugfs data print
+ *
+ * PARAMETERS:
+ *      *filp   - file pointer to debugfs file
+ *      *buf    - the user space buffer to read to
+ *       count  - the maximum number of bytes to read
+ *      *ppos   - the current position in the buffer
+ ******************************************************************************/
+static ssize_t tthe_debugfs_read(struct file *filp, char __user *buf,
+		size_t count, loff_t *ppos)
+{
+	struct pt_core_data *cd = filp->private_data;
+	int size;
+	int ret;
+	static int partial_read;
+
+	wait_event_interruptible(cd->wait_q,
+			cd->tthe_buf_len != 0 || cd->tthe_exit);
+	mutex_lock(&cd->tthe_lock);
+	if (cd->tthe_exit) {
+		mutex_unlock(&cd->tthe_lock);
+		return 0;
+	}
+	if (count > cd->tthe_buf_len)
+		size = cd->tthe_buf_len;
+	else
+		size = count;
+	if (!size) {
+		mutex_unlock(&cd->tthe_lock);
+		return 0;
+	}
+
+	if (partial_read) {
+		ret = copy_to_user(buf, cd->tthe_buf + partial_read, size);
+		partial_read = 0;
+	} else {
+		ret = copy_to_user(buf, cd->tthe_buf, size);
+	}
+	if (size == count)
+		partial_read = count;
+
+	if (ret == size)
+		return -EFAULT;
+	size -= ret;
+	cd->tthe_buf_len -= size;
+	mutex_unlock(&cd->tthe_lock);
+	*ppos += size;
+	return size;
+}
+
+static const struct file_operations tthe_debugfs_fops = {
+	.open = tthe_debugfs_open,
+	.release = tthe_debugfs_close,
+	.read = tthe_debugfs_read,
+};
+#endif
+
+static struct pt_core_nonhid_cmd _pt_core_nonhid_cmd = {
+	.start_bl              = _pt_request_pip_start_bl,
+	.suspend_scanning      = _pt_request_pip_suspend_scanning,
+	.resume_scanning       = _pt_request_pip_resume_scanning,
+	.get_param             = _pt_request_pip_get_param,
+	.set_param             = _pt_request_pip_set_param,
+	.verify_cfg_block_crc  = _pt_request_pip_verify_config_block_crc,
+	.get_config_row_size   = _pt_request_pip_get_config_row_size,
+	.get_data_structure    = _pt_request_pip_get_data_structure,
+	.run_selftest          = _pt_request_pip_run_selftest,
+	.get_selftest_result   = _pt_request_pip_get_selftest_result,
+	.load_self_test_param  = _pt_request_pip_load_self_test_param,
+	.calibrate_idacs       = _pt_request_pip_calibrate_idacs,
+	.calibrate_ext         = _pt_request_pip_calibrate_ext,
+	.initialize_baselines  = _pt_request_pip_initialize_baselines,
+	.exec_panel_scan       = _pt_request_pip_exec_panel_scan,
+	.retrieve_panel_scan   = _pt_request_pip_retrieve_panel_scan,
+	.read_data_block       = _pt_request_pip_read_data_block,
+	.write_data_block      = _pt_request_pip_write_data_block,
+	.user_cmd              = _pt_request_pip_user_cmd,
+	.get_bl_info           = _pt_request_pip_bl_get_information,
+	.initiate_bl           = _pt_request_pip_bl_initiate_bl,
+	.launch_app            = _pt_request_pip_launch_app,
+	.prog_and_verify       = _pt_request_pip_bl_program_and_verify,
+	.verify_app_integrity  = _pt_request_pip_bl_verify_app_integrity,
+	.get_panel_id          = _pt_request_pip_bl_get_panel_id,
+	.pip2_send_cmd         = _pt_request_pip2_send_cmd,
+	.pip2_send_cmd_no_int  = _pt_pip2_send_cmd_no_int,
+	.pip2_file_open        = _pt_pip2_file_open,
+	.pip2_file_close       = _pt_pip2_file_close,
+	.pip2_file_erase       = _pt_pip2_file_erase,
+	.read_us_file          = _pt_read_us_file,
+	.manage_cal_data       = _pt_manage_local_cal_data,
+	.calc_crc              = crc_ccitt_calculate,
+#ifdef TTDL_DIAGNOSTICS
+	.pip2_file_read        = _pt_pip2_file_read,
+	.pip2_file_seek_offset = _pt_pip2_file_seek_offset,
+	.pip2_file_get_stats   = _pt_pip2_file_get_stats,
+	.pip2_file_crc         = _pt_pip2_file_crc,
+#endif
+};
+
+static struct pt_core_commands _pt_core_commands = {
+	.subscribe_attention           = _pt_subscribe_attention,
+	.unsubscribe_attention         = _pt_unsubscribe_attention,
+	.request_exclusive             = _pt_request_exclusive,
+	.release_exclusive             = _pt_release_exclusive,
+	.request_reset                 = _pt_request_reset,
+	.request_pip2_launch_app       = _pt_request_pip2_launch_app,
+	.request_enum                  = _pt_request_enum,
+	.request_sysinfo               = _pt_request_sysinfo,
+	.request_loader_pdata          = _pt_request_loader_pdata,
+	.request_stop_wd               = _pt_request_stop_wd,
+	.request_start_wd              = _pt_request_start_wd,
+	.request_get_mode              = _pt_request_get_mode,
+	.request_active_pip_prot       = _pt_request_active_pip_protocol,
+	.request_pip2_get_mode_sysmode = _pt_request_pip2_get_mode_sysmode,
+	.request_pip2_enter_bl         = _pt_request_pip2_enter_bl,
+	.request_pip2_bin_hdr          = _pt_request_pip2_bin_hdr,
+	.request_dut_generation        = _pt_request_dut_generation,
+	.request_hw_version            = _pt_request_hw_version,
+	.parse_sysfs_input             = _pt_ic_parse_input,
+#ifdef TTHE_TUNER_SUPPORT
+	.request_tthe_print            = _pt_request_tthe_print,
+#endif
+#ifdef TTDL_DIAGNOSTICS
+	.request_toggle_err_gpio       = _pt_request_toggle_err_gpio,
+#endif
+	.nonhid_cmd                    = &_pt_core_nonhid_cmd,
+	.request_get_fw_mode           = _pt_request_get_fw_sys_mode,
+};
+
+struct pt_core_commands *pt_get_commands(void)
+{
+	return &_pt_core_commands;
+}
+EXPORT_SYMBOL_GPL(pt_get_commands);
+
+static DEFINE_MUTEX(core_list_lock);
+static LIST_HEAD(core_list);
+static DEFINE_MUTEX(module_list_lock);
+static LIST_HEAD(module_list);
+static int core_number;
+
+/*******************************************************************************
+ * FUNCTION: pt_probe_module
+ *
+ * SUMMARY: Add the module pointer to module_node and call the probe pointer.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *cd      - pointer to core data
+ *  *module  - pointer to module structure
+ ******************************************************************************/
+static int pt_probe_module(struct pt_core_data *cd,
+		struct pt_module *module)
+{
+	struct module_node *module_node;
+	int rc = 0;
+
+	module_node = kzalloc(sizeof(*module_node), GFP_KERNEL);
+	if (!module_node)
+		return -ENOMEM;
+
+	module_node->module = module;
+
+	mutex_lock(&cd->module_list_lock);
+	list_add(&module_node->node, &cd->module_list);
+	mutex_unlock(&cd->module_list_lock);
+
+	rc = module->probe(cd->dev, &module_node->data);
+	if (rc) {
+		/*
+		 * Remove from the list when probe fails
+		 * in order not to call release
+		 */
+		mutex_lock(&cd->module_list_lock);
+		list_del(&module_node->node);
+		mutex_unlock(&cd->module_list_lock);
+		kfree(module_node);
+		goto exit;
+	}
+
+exit:
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_release_module
+ *
+ * SUMMARY: Call the release pointer and remove the module pointer from
+ *  module_list.
+ *
+ * PARAMETERS:
+ *  *cd      - pointer to core data
+ *  *module  - pointer to module structure
+ ******************************************************************************/
+static void pt_release_module(struct pt_core_data *cd,
+		struct pt_module *module)
+{
+	struct module_node *m, *m_n;
+
+	mutex_lock(&cd->module_list_lock);
+	list_for_each_entry_safe(m, m_n, &cd->module_list, node)
+		if (m->module == module) {
+			module->release(cd->dev, m->data);
+			list_del(&m->node);
+			kfree(m);
+			break;
+		}
+	mutex_unlock(&cd->module_list_lock);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_probe_modules
+ *
+ * SUMMARY: Iterate module_list and probe each module.
+ *
+ * PARAMETERS:
+ *  *cd  - pointer to core data
+ ******************************************************************************/
+static void pt_probe_modules(struct pt_core_data *cd)
+{
+	struct pt_module *m;
+	int rc = 0;
+
+	mutex_lock(&module_list_lock);
+	list_for_each_entry(m, &module_list, node) {
+		pt_debug(cd->dev, DL_ERROR, "%s: Probe module %s\n",
+			__func__, m->name);
+		rc = pt_probe_module(cd, m);
+		if (rc)
+			pt_debug(cd->dev, DL_ERROR,
+				"%s: Probe fails for module %s\n",
+				__func__, m->name);
+	}
+	mutex_unlock(&module_list_lock);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_release_modules
+ *
+ * SUMMARY: Iterate module_list and remove each module.
+ *
+ * PARAMETERS:
+ *  *cd  - pointer to core data
+ ******************************************************************************/
+static void pt_release_modules(struct pt_core_data *cd)
+{
+	struct pt_module *m;
+
+	mutex_lock(&module_list_lock);
+	list_for_each_entry(m, &module_list, node)
+		pt_release_module(cd, m);
+	mutex_unlock(&module_list_lock);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_get_core_data
+ *
+ * SUMMARY: Iterate core_list and get core data.
+ *
+ * RETURN:
+ *   pointer to core data or null pointer if fail
+ *
+ * PARAMETERS:
+ *  *id  - pointer to core id
+ ******************************************************************************/
+struct pt_core_data *pt_get_core_data(char *id)
+{
+	struct pt_core_data *d;
+
+	list_for_each_entry(d, &core_list, node)
+		if (!strncmp(d->core_id, id, 20))
+			return d;
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(pt_get_core_data);
+
+/*******************************************************************************
+ * FUNCTION: pt_add_core
+ *
+ * SUMMARY: Add core data to the core_list.
+ *
+ * PARAMETERS:
+ *  *dev  - pointer to device structure
+ ******************************************************************************/
+static void pt_add_core(struct device *dev)
+{
+	struct pt_core_data *d;
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+
+	mutex_lock(&core_list_lock);
+	list_for_each_entry(d, &core_list, node)
+		if (d->dev == dev)
+			goto unlock;
+
+	list_add(&cd->node, &core_list);
+unlock:
+	mutex_unlock(&core_list_lock);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_del_core
+ *
+ * SUMMARY: Remove core data from the core_list.
+ *
+ * PARAMETERS:
+ *  *dev  - pointer to device structure
+ ******************************************************************************/
+static void pt_del_core(struct device *dev)
+{
+	struct pt_core_data *d, *d_n;
+
+	mutex_lock(&core_list_lock);
+	list_for_each_entry_safe(d, d_n, &core_list, node)
+		if (d->dev == dev) {
+			list_del(&d->node);
+			goto unlock;
+		}
+unlock:
+	mutex_unlock(&core_list_lock);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_register_module
+ *
+ * SUMMARY: Register the module to module_list and probe the module for each
+ *  core.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *module  - pointer to module structure
+ ******************************************************************************/
+int pt_register_module(struct pt_module *module)
+{
+	struct pt_module *m;
+	struct pt_core_data *cd;
+
+	int rc = 0;
+
+	if (!module || !module->probe || !module->release)
+		return -EINVAL;
+
+	mutex_lock(&module_list_lock);
+	list_for_each_entry(m, &module_list, node)
+		if (m == module) {
+			rc = -EEXIST;
+			goto unlock;
+		}
+
+	list_add(&module->node, &module_list);
+
+	/* Probe the module for each core */
+	mutex_lock(&core_list_lock);
+	list_for_each_entry(cd, &core_list, node)
+		pt_probe_module(cd, module);
+	mutex_unlock(&core_list_lock);
+
+unlock:
+	mutex_unlock(&module_list_lock);
+	return rc;
+}
+EXPORT_SYMBOL_GPL(pt_register_module);
+
+/*******************************************************************************
+ * FUNCTION: pt_unregister_module
+ *
+ * SUMMARY: Release the module for each core and remove the module from
+ *  module_list.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *module  - pointer to module structure
+ ******************************************************************************/
+void pt_unregister_module(struct pt_module *module)
+{
+	struct pt_module *m, *m_n;
+	struct pt_core_data *cd;
+
+	if (!module)
+		return;
+
+	mutex_lock(&module_list_lock);
+
+	/* Release the module for each core */
+	mutex_lock(&core_list_lock);
+	list_for_each_entry(cd, &core_list, node)
+		pt_release_module(cd, module);
+	mutex_unlock(&core_list_lock);
+
+	list_for_each_entry_safe(m, m_n, &module_list, node)
+		if (m == module) {
+			list_del(&m->node);
+			break;
+		}
+
+	mutex_unlock(&module_list_lock);
+}
+EXPORT_SYMBOL_GPL(pt_unregister_module);
+
+/*******************************************************************************
+ * FUNCTION: pt_get_module_data
+ *
+ * SUMMARY: Get module data from module_node by module_list.
+ *
+ * RETURN:
+ *   pointer to module data
+ *
+ * PARAMETERS:
+ *  *dev     - pointer to device structure
+ *  *module  - pointer to module structure
+ ******************************************************************************/
+void *pt_get_module_data(struct device *dev, struct pt_module *module)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct module_node *m;
+	void *data = NULL;
+
+	mutex_lock(&cd->module_list_lock);
+	list_for_each_entry(m, &cd->module_list, node)
+		if (m->module == module) {
+			data = m->data;
+			break;
+		}
+	mutex_unlock(&cd->module_list_lock);
+
+	return data;
+}
+EXPORT_SYMBOL(pt_get_module_data);
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+/*******************************************************************************
+ * FUNCTION: pt_early_suspend
+ *
+ * SUMMARY: Android PM architecture function that will call "PT_ATTEN_SUSPEND"
+ *  attention list.
+ *
+ * PARAMETERS:
+ *  *h  - pointer to early_suspend structure
+ ******************************************************************************/
+static void pt_early_suspend(struct early_suspend *h)
+{
+	struct pt_core_data *cd =
+		container_of(h, struct pt_core_data, es);
+
+	call_atten_cb(cd, PT_ATTEN_SUSPEND, 0);
+}
+/*******************************************************************************
+ * FUNCTION: pt_late_resume
+ *
+ * SUMMARY: Android PM architecture function that will call "PT_ATTEN_RESUME"
+ * attention list.
+ *
+ * PARAMETERS:
+ *  *h  - pointer to early_suspend structure
+ ******************************************************************************/
+static void pt_late_resume(struct early_suspend *h)
+{
+	struct pt_core_data *cd =
+		container_of(h, struct pt_core_data, es);
+
+	call_atten_cb(cd, PT_ATTEN_RESUME, 0);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_setup_early_suspend
+ *
+ * SUMMARY: Register early/suspend function to the system.
+ *
+ * PARAMETERS:
+ *  *cd  - pointer to core data
+ ******************************************************************************/
+static void pt_setup_early_suspend(struct pt_core_data *cd)
+{
+	cd->es.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
+	cd->es.suspend = pt_early_suspend;
+	cd->es.resume = pt_late_resume;
+
+	register_early_suspend(&cd->es);
+}
+#elif defined(CONFIG_DRM)
+
+static void pt_resume_work(struct work_struct *work)
+{
+	struct pt_core_data *pt_data = container_of(work, struct pt_core_data,
+					resume_work);
+	int rc = 0;
+
+	pt_debug(pt_data->dev, DL_INFO, "%s start ", __func__);
+	if (pt_data->cpdata->flags & PT_CORE_FLAG_SKIP_RUNTIME)
+		return;
+
+	rc = pt_core_easywake_off(pt_data);
+	if (rc < 0) {
+		pt_debug(pt_data->dev, DL_ERROR,
+			"%s: Error on wake\n", __func__);
+	}
+	pt_debug(pt_data->dev, DL_INFO, "%s touch to wake disabled ", __func__);
+	return;
+}
+
+static void pt_suspend_work(struct work_struct *work)
+{
+	struct pt_core_data *pt_data = container_of(work, struct pt_core_data,
+					suspend_work);
+	int rc = 0;
+
+	pt_debug(pt_data->dev, DL_INFO, "%s start\n", __func__);
+
+	if (pt_data->cpdata->flags & PT_CORE_FLAG_SKIP_RUNTIME)
+		return;
+
+	rc = pt_core_easywake_on(pt_data);
+	if (rc < 0) {
+		pt_debug(pt_data->dev, DL_ERROR, "%s: Error on sleep\n", __func__);
+		return;
+	}
+	pt_debug(pt_data->dev, DL_INFO, "%s Exit touch to wake enabled\n", __func__);
+	return;
+}
+
+#if defined(CONFIG_PANEL_NOTIFIER)
+/*******************************************************************************
+ * FUNCTION: panel_event_notifier_callback
+ *
+ * SUMMARY: Call back function for Panel Event notifier to allow to call
+ * resume/suspend attention list.
+ *
+ * PARAMETERS:
+ *   tag           - type of input panel.
+ *  *notification  - pointer to notification details.
+ *  *client_data   - pointer to core data
+ ******************************************************************************/
+static void panel_event_notifier_callback(enum panel_event_notifier_tag tag,
+		struct panel_event_notification *notification, void *client_data)
+{
+	struct pt_core_data *cd = client_data;
+
+	if(!notification)
+	{
+		pt_debug(cd->dev,DL_INFO, "%s: Invalid notification\n", __func__);
+		return;
+	}
+
+	pt_debug(cd->dev, DL_INFO, "%s: DRM notifier called!\n", __func__);
+	if (cd->quick_boot || cd->drv_debug_suspend)
+		goto exit;
+
+	pt_debug(cd->dev, DL_INFO, "%s: DRM event:%d,fb_state %d",
+		__func__, notification->notif_type, cd->fb_state);
+	pt_debug(cd->dev, DL_INFO, "%s: DRM Power - %s - FB state %d ",
+		__func__, (notification->notif_type == DRM_PANEL_EVENT_UNBLANK)?"UP":"DOWN", cd->fb_state);
+
+	if (notification->notif_type == DRM_PANEL_EVENT_UNBLANK) {
+		pt_debug(cd->dev, DL_INFO, "%s: UNBLANK!\n", __func__);
+		if (notification->notif_data.early_trigger) {
+			pr_err("%s: resume: event = %d, not care\n", __func__, notification->notif_type);
+			pt_debug(cd->dev, DL_INFO, "%s: resume: event = %d, not care\n",
+				__func__, notification->notif_type);
+		} else {
+				pt_debug(cd->dev, DL_INFO, "%s: Resume notifier called!\n",
+					__func__);
+				cancel_work_sync(&cd->resume_offload_work);
+				cancel_work_sync(&cd->suspend_offload_work);
+				cancel_work_sync(&cd->resume_work);
+				cancel_work_sync(&cd->suspend_work);
+
+				queue_work(cd->pt_workqueue, &cd->resume_work);
+#if defined(CONFIG_PM_SLEEP)
+				pt_debug(cd->dev, DL_INFO, "%s: Resume notifier called!\n",
+					__func__);
+				if (cd->cpdata->flags & PT_CORE_FLAG_SKIP_RUNTIME)
+					pt_core_resume_(cd->dev);
+#endif
+				cd->fb_state = FB_ON;
+				pt_debug(cd->dev, DL_INFO, "%s: Resume notified!\n", __func__);
+		}
+	} else if (notification->notif_type == DRM_PANEL_EVENT_BLANK) {
+		pt_debug(cd->dev, DL_INFO, "%s: BLANK!\n", __func__);
+		if (notification->notif_data.early_trigger) {
+#if defined(CONFIG_PM_SLEEP)
+			pt_debug(cd->dev, DL_INFO, "%s: Suspend notifier called!\n",
+				__func__);
+			if (cd->cpdata->flags & PT_CORE_FLAG_SKIP_RUNTIME)
+				pt_core_suspend_(cd->dev);
+#endif
+			cancel_work_sync(&cd->resume_offload_work);
+			cancel_work_sync(&cd->suspend_offload_work);
+			cancel_work_sync(&cd->resume_work);
+			cancel_work_sync(&cd->suspend_work);
+
+			queue_work(cd->pt_workqueue, &cd->suspend_work);
+			cd->fb_state = FB_OFF;
+			pt_debug(cd->dev, DL_INFO, "%s: Suspend notified!\n", __func__);
+		} else {
+			pt_debug(cd->dev, DL_INFO, "%s: suspend: event = %d, not care\n",
+				__func__, notification->notif_type);
+		}
+	} else if (notification->notif_type == DRM_PANEL_EVENT_BLANK_LP) {
+		pt_debug(cd->dev, DL_INFO, "%s: LOWPOWER!\n", __func__);
+                if (notification->notif_data.early_trigger) {
+#if defined(CONFIG_PM_SLEEP)
+			pt_debug(cd->dev, DL_INFO, "%s: Suspend notifier called!\n", __func__);
+			if (cd->cpdata->flags & PT_CORE_FLAG_SKIP_RUNTIME)
+				pt_core_suspend_(cd->dev);
+#endif
+			cancel_work_sync(&cd->resume_offload_work);
+			cancel_work_sync(&cd->suspend_offload_work);
+			cancel_work_sync(&cd->resume_work);
+			cancel_work_sync(&cd->suspend_work);
+
+			queue_work(cd->pt_workqueue, &cd->suspend_work);
+			cd->fb_state = FB_OFF;
+			pt_debug(cd->dev, DL_INFO, "%s: Suspend notified!\n", __func__);
+		} else {
+			pt_debug(cd->dev, DL_INFO, "%s: suspend: event = %d, not care\n",
+                                __func__, notification->notif_type);
+		}
+
+	} else {
+		pt_debug(cd->dev, DL_INFO, "%s: DRM BLANK(%d) do not need process\n",
+			__func__, notification->notif_type);
+	}
+exit:
+	return;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_setup_panel_event_notifier
+ *
+ * SUMMARY: Set up call back function into drm notifier.
+ *
+ * PARAMETERS:
+ *  *cd   - pointer to core data
+ ******************************************************************************/
+static void pt_setup_panel_event_notifier(struct pt_core_data *cd)
+{
+	void *cookie = NULL;
+
+	if (!active_panel)
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: Active panel not registered!\n", __func__);
+
+	cd->pt_workqueue = create_singlethread_workqueue("ts_wq");
+	if (!cd->pt_workqueue) {
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: worker thread creation failed !\n", __func__);
+	}
+
+	if (cd->pt_workqueue) {
+		INIT_WORK(&cd->resume_work, pt_resume_work);
+		INIT_WORK(&cd->suspend_work, pt_suspend_work);
+	}
+
+
+	cookie = panel_event_notifier_register(PANEL_EVENT_NOTIFICATION_PRIMARY,
+			PANEL_EVENT_NOTIFIER_CLIENT_PRIMARY_TOUCH,
+			active_panel,&panel_event_notifier_callback, cd);
+
+	if (active_panel && !cookie)
+	{
+		pt_debug(cd->dev, DL_ERROR,
+				"%s: Register notifier failed!\n", __func__);
+	}
+	cd->entry = cookie;
+}
+#else
+
+/*******************************************************************************
+ * FUNCTION: drm_notifier_callback
+ *
+ * SUMMARY: Call back function for DRM notifier to allow to call
+ * resume/suspend attention list.
+ *
+ * RETURN:
+ *   0 = success
+ *
+ * PARAMETERS:
+ *  *self   - pointer to notifier_block structure
+ *   event  - event type of fb notifier
+ *  *data   - pointer to fb_event structure
+ ******************************************************************************/
+static int drm_notifier_callback(struct notifier_block *self,
+		unsigned long event, void *data)
+{
+	struct pt_core_data *cd =
+		container_of(self, struct pt_core_data, fb_notifier);
+	struct drm_panel_notifier *evdata = data;
+	int *blank;
+
+	pt_debug(cd->dev, DL_INFO, "%s: DRM notifier called!\n", __func__);
+
+	if (!evdata)
+		goto exit;
+
+	if (!(event == DRM_PANEL_EARLY_EVENT_BLANK ||
+		event == DRM_PANEL_EVENT_BLANK)) {
+		pt_debug(cd->dev, DL_INFO, "%s: Event(%lu) do not need process\n",
+			__func__, event);
+		goto exit;
+	}
+
+	if (cd->quick_boot || cd->drv_debug_suspend)
+		goto exit;
+
+	blank = evdata->data;
+	pt_debug(cd->dev, DL_INFO, "%s: DRM event:%lu,blank:%d fb_state %d sleep state %d ",
+		__func__, event, *blank, cd->fb_state, cd->sleep_state);
+	pt_debug(cd->dev, DL_INFO, "%s: DRM Power - %s - FB state %d ",
+		__func__, (*blank == DRM_PANEL_BLANK_UNBLANK)?"UP":"DOWN", cd->fb_state);
+
+	if (*blank == DRM_PANEL_BLANK_UNBLANK) {
+		pt_debug(cd->dev, DL_INFO, "%s: UNBLANK!\n", __func__);
+		if (event == DRM_PANEL_EARLY_EVENT_BLANK) {
+			pt_debug(cd->dev, DL_INFO, "%s: resume: event = %lu, not care\n",
+				__func__, event);
+		} else if (event == DRM_PANEL_EVENT_BLANK) {
+			if (cd->fb_state != FB_ON) {
+				pt_debug(cd->dev, DL_INFO, "%s: Resume notifier called!\n",
+					__func__);
+				cancel_work_sync(&cd->resume_offload_work);
+				cancel_work_sync(&cd->suspend_offload_work);
+				cancel_work_sync(&cd->resume_work);
+				cancel_work_sync(&cd->suspend_work);
+
+				queue_work(cd->pt_workqueue, &cd->resume_work);
+#if defined(CONFIG_PM_SLEEP)
+				pt_debug(cd->dev, DL_INFO, "%s: Resume notifier called!\n",
+					__func__);
+				if (cd->cpdata->flags & PT_CORE_FLAG_SKIP_RUNTIME)
+					pt_core_resume_(cd->dev);
+#endif
+				cd->fb_state = FB_ON;
+				pt_debug(cd->dev, DL_INFO, "%s: Resume notified!\n", __func__);
+			}
+		}
+	} else if (*blank == DRM_PANEL_BLANK_LP || *blank == DRM_PANEL_BLANK_POWERDOWN) {
+		pt_debug(cd->dev, DL_INFO, "%s: LOWPOWER!\n", __func__);
+		if (event == DRM_PANEL_EARLY_EVENT_BLANK) {
+			if (cd->fb_state != FB_OFF) {
+#if defined(CONFIG_PM_SLEEP)
+				pt_debug(cd->dev, DL_INFO, "%s: Suspend notifier called!\n",
+					__func__);
+				if (cd->cpdata->flags & PT_CORE_FLAG_SKIP_RUNTIME)
+					pt_core_suspend_(cd->dev);
+#endif
+				cancel_work_sync(&cd->resume_offload_work);
+				cancel_work_sync(&cd->suspend_offload_work);
+				cancel_work_sync(&cd->resume_work);
+				cancel_work_sync(&cd->suspend_work);
+
+				queue_work(cd->pt_workqueue, &cd->suspend_work);
+				cd->fb_state = FB_OFF;
+				pt_debug(cd->dev, DL_INFO, "%s: Suspend notified!\n", __func__);
+			}
+
+		} else if (event == DRM_PANEL_EVENT_BLANK) {
+			pt_debug(cd->dev, DL_INFO, "%s: suspend: event = %lu, not care\n",
+				__func__, event);
+		}
+	} else {
+		pt_debug(cd->dev, DL_INFO, "%s: DRM BLANK(%d) do not need process\n",
+			__func__, *blank);
+	}
+exit:
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_setup_drm_notifier
+ *
+ * SUMMARY: Set up call back function into drm notifier.
+ *
+ * PARAMETERS:
+ *  *cd   - pointer to core data
+ ******************************************************************************/
+static void pt_setup_drm_notifier(struct pt_core_data *cd)
+{
+	cd->fb_state = FB_NONE;
+	cd->fb_notifier.notifier_call = drm_notifier_callback;
+	pt_debug(cd->dev, DL_INFO, "%s: Setting up drm notifier\n", __func__);
+
+	if (!active_panel)
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: Active panel not registered!\n", __func__);
+
+	cd->pt_workqueue = create_singlethread_workqueue("ts_wq");
+	if (!cd->pt_workqueue) {
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: worker thread creation failed !\n", __func__);
+	}
+
+	if (cd->pt_workqueue) {
+		INIT_WORK(&cd->resume_work, pt_resume_work);
+		INIT_WORK(&cd->suspend_work, pt_suspend_work);
+	}
+
+	if (active_panel &&
+		drm_panel_notifier_register(active_panel,
+			&cd->fb_notifier) < 0)
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: Register notifier failed!\n", __func__);
+}
+#endif
+#elif defined(CONFIG_FB)
+/*******************************************************************************
+ * FUNCTION: fb_notifier_callback
+ *
+ * SUMMARY: Call back function for FrameBuffer notifier to allow to call
+ * resume/suspend attention list.
+ *
+ * RETURN:
+ *   0 = success
+ *
+ * PARAMETERS:
+ *  *self   - pointer to notifier_block structure
+ *   event  - event type of fb notifier
+ *  *data   - pointer to fb_event structure
+ ******************************************************************************/
+static int fb_notifier_callback(struct notifier_block *self,
+		unsigned long event, void *data)
+{
+	struct pt_core_data *cd =
+		container_of(self, struct pt_core_data, fb_notifier);
+	struct fb_event *evdata = data;
+	int *blank;
+
+	if (event != FB_EVENT_BLANK || !evdata)
+		goto exit;
+
+	blank = evdata->data;
+	if (*blank == FB_BLANK_UNBLANK) {
+		pt_debug(cd->dev, DL_INFO, "%s: UNBLANK!\n", __func__);
+		if (cd->fb_state != FB_ON) {
+			call_atten_cb(cd, PT_ATTEN_RESUME, 0);
+#if defined(CONFIG_PM_SLEEP)
+			if (cd->cpdata->flags & PT_CORE_FLAG_SKIP_RUNTIME)
+				pt_core_resume_(cd->dev);
+#endif
+			cd->fb_state = FB_ON;
+		}
+	} else if (*blank == FB_BLANK_POWERDOWN) {
+		pt_debug(cd->dev, DL_INFO, "%s: POWERDOWN!\n", __func__);
+		if (cd->fb_state != FB_OFF) {
+#if defined(CONFIG_PM_SLEEP)
+			if (cd->cpdata->flags & PT_CORE_FLAG_SKIP_RUNTIME)
+				pt_core_suspend_(cd->dev);
+#endif
+			call_atten_cb(cd, PT_ATTEN_SUSPEND, 0);
+			cd->fb_state = FB_OFF;
+		}
+	}
+
+exit:
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_setup_fb_notifier
+ *
+ * SUMMARY: Set up call back function into fb notifier.
+ *
+ * PARAMETERS:
+ *  *cd   - pointer to core data
+ ******************************************************************************/
+static void pt_setup_fb_notifier(struct pt_core_data *cd)
+{
+	int rc = 0;
+
+	cd->fb_state = FB_ON;
+	cd->fb_notifier.notifier_call = fb_notifier_callback;
+
+	rc = fb_register_client(&cd->fb_notifier);
+	if (rc)
+		pt_debug(cd->dev, DL_ERROR,
+			"Unable to register fb_notifier: %d\n", rc);
+}
+#endif
+
+/*******************************************************************************
+ * FUNCTION: pt_watchdog_work
+ *
+ * SUMMARY: This is where the watchdog work is done except if the DUT is
+ *	sleeping then this function simply returns. If the DUT is awake the
+ *	first thing is to ensure the IRQ is not stuck asserted meaning that
+ *	somehow a response is waiting on the DUT that has not been read. If
+ *	this occurs the message is simply consumed. If or once the IRQ is
+ *	cleared, a PIP PING message is sent to the DUT and if the response
+ *	is received the watchdog succeeds and exits, if no response is seen
+ *	a startup is queued unless the maximum number of startups have already
+ *	been attempted, in that case a BL is attempted.
+ *
+ *	NOTE: pt_stop_wd_timer() cannot be called within the context of this
+ *	      work thread
+ *
+ * RETURN: void
+ *
+ * PARAMETERS:
+ *      *work - pointer to a work structure for the watchdog work queue
+ ******************************************************************************/
+static void pt_watchdog_work(struct work_struct *work)
+{
+	int rc = 0;
+	struct pt_core_data *cd = container_of(work,
+				struct pt_core_data, watchdog_work);
+	/*
+	 * if found the current sleep_state is SS_SLEEPING
+	 * then no need to request_exclusive, directly return
+	 */
+	if (cd->sleep_state == SS_SLEEPING)
+		return;
+
+#ifdef TTDL_DIAGNOSTICS
+	cd->watchdog_count++;
+#endif /* TTDL_DIAGNOSTICS */
+
+	/*
+	 * The first WD interval was extended to allow DDI to come up.
+	 * If the WD interval is not the default then adjust timer to the
+	 * current setting. The user can override value with drv_debug sysfs.
+	 */
+	if (cd->watchdog_interval != PT_WATCHDOG_TIMEOUT) {
+		mod_timer_pending(&cd->watchdog_timer, jiffies +
+			msecs_to_jiffies(cd->watchdog_interval));
+	}
+
+	if (pt_check_irq_asserted(cd)) {
+#ifdef TTDL_DIAGNOSTICS
+		cd->watchdog_irq_stuck_count++;
+		pt_toggle_err_gpio(cd, PT_ERR_GPIO_IRQ_STUCK);
+#endif /* TTDL_DIAGNOSTICS */
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: TTDL WD found IRQ asserted, attempt to clear\n",
+			__func__);
+		pt_flush_bus(cd, PT_FLUSH_BUS_BASED_ON_LEN, NULL);
+	}
+
+	rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
+	if (rc < 0) {
+		pt_debug(cd->dev, DL_ERROR,
+				"%s: fail get exclusive ex=%p own=%p\n",
+				__func__, cd->exclusive_dev, cd->dev);
+		goto queue_startup;
+	}
+
+	rc = pt_pip_null_(cd);
+
+	if (release_exclusive(cd, cd->dev) < 0)
+		pt_debug(cd->dev, DL_ERROR,
+				"%s: fail to release exclusive\n", __func__);
+
+queue_startup:
+	if (rc) {
+#ifdef TTDL_DIAGNOSTICS
+		cd->watchdog_failed_access_count++;
+		pt_toggle_err_gpio(cd, PT_ERR_GPIO_EXCLUSIVE_ACCESS);
+#endif /* TTDL_DIAGNOSTICS */
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: failed to access device in WD, retry count=%d\n",
+			__func__, cd->startup_retry_count);
+
+		/* Already tried FW upgrade because of watchdog but failed */
+		if (cd->startup_retry_count > PT_WATCHDOG_RETRY_COUNT)
+			return;
+
+		if (cd->startup_retry_count++ < PT_WATCHDOG_RETRY_COUNT) {
+			/*
+			 * Any wrapper function that trys to disable the
+			 * WD killing this worker cannot be called here.
+			 */
+			rc = request_exclusive(cd, cd->dev,
+				PT_REQUEST_EXCLUSIVE_TIMEOUT);
+			if (rc < 0) {
+				pt_debug(cd->dev, DL_ERROR,
+					"%s: fail get exclusive ex=%p own=%p\n",
+					__func__, cd->exclusive_dev, cd->dev);
+				goto exit;
+			}
+
+			cd->hw_detected = false;
+			cd->startup_status = STARTUP_STATUS_START;
+			pt_debug(cd->dev, DL_DEBUG,
+					"%s: Startup Status Reset\n", __func__);
+			rc = pt_dut_reset_and_wait(cd);
+			if (release_exclusive(cd, cd->dev) < 0)
+				pt_debug(cd->dev, DL_ERROR,
+					"%s: fail to release exclusive\n",
+					__func__);
+			if (!rc) {
+				cd->hw_detected = true;
+				if (!cd->flashless_dut)
+					pt_queue_enum(cd);
+			}
+#ifdef TTDL_DIAGNOSTICS
+			cd->wd_xres_count++;
+			pt_debug(cd->dev, DL_ERROR,
+				"%s: Comm Failed - DUT reset [#%d]\n",
+				__func__, cd->wd_xres_count);
+#endif /* TTDL_DIAGNOSTICS */
+		} else {
+			/*
+			 * After trying PT_WATCHDOG_RETRY_COUNT times to
+			 * reset the part to regain communications, try to BL
+			 */
+			pt_debug(cd->dev, DL_ERROR,
+				"%s: WD DUT access failure, Start FW Upgrade\n",
+				__func__);
+#ifdef TTDL_DIAGNOSTICS
+			/*
+			 * When diagnostics is enabled allow TTDL to keep
+			 * trying to find the DUT. This allows the DUT to be
+			 * hot swap-able while the host stays running. In
+			 * production this may not be wanted as a customer
+			 * may have several touch drivers and any driver
+			 * that doesn't match the current DUT should give
+			 * up trying and give up using the bus.
+			 */
+			pt_debug(cd->dev, DL_INFO,
+				"%s: Resetting startup_retry_count\n",
+				__func__);
+			cd->startup_retry_count = 0;
+#endif /* TTDL_DIAGNOSTICS */
+			/*
+			 * Since fw may be broken,reset sysinfo ready flag
+			 * to let upgrade function work.
+			 */
+			mutex_lock(&cd->system_lock);
+			cd->sysinfo.ready = false;
+			mutex_unlock(&cd->system_lock);
+
+			if (cd->active_dut_generation == DUT_UNKNOWN) {
+				pt_debug(cd->dev, DL_ERROR,
+					"%s: Queue Restart\n", __func__);
+				pt_queue_restart(cd);
+			} else
+				kthread_run(start_fw_upgrade, cd, "pt_loader");
+		}
+	} else {
+		cd->hw_detected = true;
+		if (cd->startup_status <= (STARTUP_STATUS_FW_RESET_SENTINEL |
+		    STARTUP_STATUS_BL_RESET_SENTINEL)) {
+			pt_debug(cd->dev, DL_ERROR,
+				"%s: HW detected but not enumerated\n",
+				__func__);
+			pt_queue_enum(cd);
+		}
+	}
+
+exit:
+	pt_start_wd_timer(cd);
+}
+
+#if (KERNEL_VERSION(4, 14, 0) > LINUX_VERSION_CODE)
+/*******************************************************************************
+ * FUNCTION: pt_watchdog_timer
+ *
+ * SUMMARY: The function that is called when the WD timer expires. If the
+ *	watchdog work is not already busy schedule the watchdog work queue.
+ *
+ * RETURN: void
+ *
+ * PARAMETERS:
+ *      handle - Handle to the watchdog timer
+ ******************************************************************************/
+static void pt_watchdog_timer(unsigned long handle)
+{
+	struct pt_core_data *cd = (struct pt_core_data *)handle;
+	if (!cd)
+		return;
+
+	pt_debug(cd->dev, DL_DEBUG, "%s: Watchdog timer triggered\n",
+		__func__);
+
+	if (!work_pending(&cd->watchdog_work))
+		schedule_work(&cd->watchdog_work);
+}
+#else
+/*******************************************************************************
+ * FUNCTION: pt_watchdog_timer
+ *
+ * SUMMARY: The function that is called when the WD timer expires. If the
+ *	watchdog work is not already busy schedule the watchdog work queue.
+ *
+ * RETURN: void
+ *
+ * PARAMETERS:
+ *      *t - Pointer to timer list
+ ******************************************************************************/
+static void pt_watchdog_timer(struct timer_list *t)
+{
+	struct pt_core_data *cd = from_timer(cd, t, watchdog_timer);
+	if (!cd)
+		return;
+
+	pt_debug(cd->dev, DL_DEBUG, "%s: Watchdog timer triggered\n",
+		__func__);
+
+	if (!work_pending(&cd->watchdog_work))
+		schedule_work(&cd->watchdog_work);
+}
+#endif
+
+
+
+
+/*******************************************************************************
+ * Core sysfs show and store functions
+ ******************************************************************************/
+
+/*******************************************************************************
+ * FUNCTION: pt_hw_version_show
+ *
+ * SUMMARY: Gets the HW version for either PIP1.x or PIP2.x DUTS
+ *	Output data format: [SiliconID].[RevID FamilyID].[PanelID]
+ *
+ * RETURN: size of data written to sysfs node
+ *
+ * PARAMETERS:
+ *	*dev  - pointer to device structure
+ *	*attr - pointer to device attributes structure
+ *	*buf  - pointer to print output buffer
+ ******************************************************************************/
+static ssize_t pt_hw_version_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	_pt_request_hw_version(dev, cd->hw_version);
+	return scnprintf(buf, PT_MAX_PRBUF_SIZE, "%s\n", cd->hw_version);
+}
+static DEVICE_ATTR(hw_version, 0444, pt_hw_version_show, NULL);
+
+/*******************************************************************************
+ * FUNCTION: pt_drv_version_show
+ *
+ * SUMMARY: Show method for the drv_version sysfs node that will show the
+ *	TTDL version information
+ *
+ * RETURN: Char buffer with printed TTDL version information
+ *
+ * PARAMETERS:
+ *	*dev  - pointer to device structure
+ *	*attr - pointer to device attributes
+ *	*buf  - pointer to output buffer
+ ******************************************************************************/
+static ssize_t pt_drv_version_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	return scnprintf(buf, PT_MAX_PRBUF_SIZE,
+		"Driver: %s\nVersion: %s\nDate: %s\n",
+		pt_driver_core_name, pt_driver_core_version,
+		pt_driver_core_date);
+}
+static DEVICE_ATTR(drv_version, 0444, pt_drv_version_show, NULL);
+static DEVICE_ATTR(drv_ver, 0444, pt_drv_version_show, NULL);
+
+/*******************************************************************************
+ * FUNCTION: pt_fw_version_show
+ *
+ * SUMMARY: Show method for the fw_version sysfs node that will
+ *	show the firmware, bootloader and PIP version information
+ *
+ * RETURN: Size of printed buffer
+ *
+ * PARAMETERS:
+ *	*dev  - pointer to device structure
+ *	*attr - pointer to device attributes
+ *	*buf  - pointer to output buffer
+ ******************************************************************************/
+static ssize_t pt_fw_version_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_ttdata *ttdata;
+	int rc = 0;
+
+	if (cd->mode == PT_MODE_OPERATIONAL)
+		rc = pt_hid_output_get_sysinfo_(cd);
+
+	pt_debug(cd->dev, DL_INFO, "%s: mode = %d sysinfo.ready = %d\n",
+		__func__, cd->mode, cd->sysinfo.ready);
+
+	if (cd->sysinfo.ready)
+		ttdata = &cd->sysinfo.ttdata;
+	else
+		rc = -ENODATA;
+
+	if (!rc) {
+		return scnprintf(buf, strlen(buf),
+			"Status: %d\n"
+			"FW    : %d.%d.%d\n"
+			"Config: %d\n"
+			"BL    : %d.%d\n"
+			"PIP   : %d.%d\n",
+			rc,
+			ttdata->fw_ver_major, ttdata->fw_ver_minor,
+			ttdata->revctrl,
+			ttdata->fw_ver_conf,
+			ttdata->bl_ver_major, ttdata->bl_ver_minor,
+			ttdata->pip_ver_major, ttdata->pip_ver_minor);
+	} else {
+		return scnprintf(buf, strlen(buf),
+			"Status: %d\n"
+			"FW    : n/a\n"
+			"Config: n/a\n"
+			"BL    : n/a\n"
+			"PIP   : n/a\n",
+			rc);
+	}
+}
+static DEVICE_ATTR(fw_version, 0444, pt_fw_version_show, NULL);
+
+/*******************************************************************************
+ * FUNCTION: pt_sysinfo_show
+ *
+ * SUMMARY: Show method for the sysinfo sysfs node that will
+ *	show all the information from get system information command.
+ *
+ * RETURN: Size of printed buffer
+ *
+ * PARAMETERS:
+ *	*dev  - pointer to device structure
+ *	*attr - pointer to device attributes
+ *	*buf  - pointer to output buffer
+ ******************************************************************************/
+static ssize_t pt_sysinfo_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_sysinfo *si;
+	struct pt_ttdata *ttdata = NULL;
+	struct pt_sensing_conf_data *scd = NULL;
+	int rc = 0;
+
+	if (cd->mode == PT_MODE_OPERATIONAL) {
+		rc = pt_hid_output_get_sysinfo_(cd);
+		if (cd->sysinfo.ready) {
+			si = &cd->sysinfo;
+			ttdata = &si->ttdata;
+			scd = &si->sensing_conf_data;
+		} else
+			rc = -ENODATA;
+	} else
+		rc = -EPERM;
+
+	pt_debug(cd->dev, DL_INFO, "%s: mode = %d sysinfo.ready = %d\n",
+		__func__, cd->mode, cd->sysinfo.ready);
+
+	if (!rc && ttdata && scd) {
+		return scnprintf(buf, strlen(buf),
+			"Status: %d\n"
+			"pip_ver_major: 0x%02X\n"
+			"pip_ver_minor: 0x%02X\n"
+			"fw_pid       : 0x%04X\n"
+			"fw_ver_major : 0x%02X\n"
+			"fw_ver_minor : 0x%02X\n"
+			"revctrl      : 0x%08X\n"
+			"fw_ver_conf  : 0x%04X\n"
+			"bl_ver_major : 0x%02X\n"
+			"bl_ver_minor : 0x%02X\n"
+			"jtag_id_h    : 0x%04X\n"
+			"jtag_id_l    : 0x%04X\n"
+			"mfg_id[0]    : 0x%02X\n"
+			"mfg_id[1]    : 0x%02X\n"
+			"mfg_id[2]    : 0x%02X\n"
+			"mfg_id[3]    : 0x%02X\n"
+			"mfg_id[4]    : 0x%02X\n"
+			"mfg_id[5]    : 0x%02X\n"
+			"mfg_id[6]    : 0x%02X\n"
+			"mfg_id[7]    : 0x%02X\n"
+			"post_code    : 0x%04X\n"
+			"electrodes_x : 0x%02X\n"
+			"electrodes_y : 0x%02X\n"
+			"len_x        : 0x%04X\n"
+			"len_y        : 0x%04X\n"
+			"res_x        : 0x%04X\n"
+			"res_y        : 0x%04X\n"
+			"max_z        : 0x%04X\n"
+			"origin_x     : 0x%02X\n"
+			"origin_y     : 0x%02X\n"
+			"panel_id     : 0x%02X\n"
+			"btn          : 0x%02X\n"
+			"scan_mode    : 0x%02X\n"
+			"max_num_of_tch_per_refresh_cycle: 0x%02X\n",
+			rc,
+			ttdata->pip_ver_major,
+			ttdata->pip_ver_minor,
+			ttdata->fw_pid,
+			ttdata->fw_ver_major,
+			ttdata->fw_ver_minor,
+			ttdata->revctrl,
+			ttdata->fw_ver_conf,
+			ttdata->bl_ver_major,
+			ttdata->bl_ver_minor,
+			ttdata->jtag_id_h,
+			ttdata->jtag_id_l,
+			ttdata->mfg_id[0],
+			ttdata->mfg_id[1],
+			ttdata->mfg_id[2],
+			ttdata->mfg_id[3],
+			ttdata->mfg_id[4],
+			ttdata->mfg_id[5],
+			ttdata->mfg_id[6],
+			ttdata->mfg_id[7],
+			ttdata->post_code,
+			scd->electrodes_x,
+			scd->electrodes_y,
+			scd->len_x,
+			scd->len_y,
+			scd->res_x,
+			scd->res_y,
+			scd->max_z,
+			scd->origin_x,
+			scd->origin_y,
+			scd->panel_id,
+			scd->btn,
+			scd->scan_mode,
+			scd->max_tch);
+	} else {
+		return scnprintf(buf, strlen(buf),
+			"Status: %d\n",
+			rc);
+	}
+}
+static DEVICE_ATTR(sysinfo, 0444, pt_sysinfo_show, NULL);
+
+/*******************************************************************************
+ * FUNCTION: pt_hw_reset_show
+ *
+ * SUMMARY: The show method for the hw_reset sysfs node that does a hw reset
+ *	by toggling the XRES line and then calls the startup function to
+ *	allow TTDL to re-enumerate the DUT.
+ *	The printed value reflects the status of the full reset/enum.
+ *
+ * PARAMETERS:
+ *      *dev  - pointer to Device structure
+ *      *attr - pointer to the device attribute structure
+ *      *buf  - pointer to buffer to print
+ ******************************************************************************/
+static ssize_t pt_hw_reset_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	int rc = 0;
+	int time = 0;
+	u8 reset_status = 0;
+	int t;
+	struct pt_hid_desc hid_desc;
+
+	memset(&hid_desc, 0, sizeof(hid_desc));
+
+	/* Only allow DUT reset if no active BL in progress */
+	mutex_lock(&cd->firmware_class_lock);
+
+	mutex_lock(&cd->system_lock);
+	cd->startup_state = STARTUP_NONE;
+	mutex_unlock(&(cd->system_lock));
+
+	pt_stop_wd_timer(cd);
+
+	/* ensure no left over exclusive access is still locked */
+	release_exclusive(cd, cd->dev);
+
+	rc = pt_dut_reset(cd, PT_CORE_CMD_PROTECTED);
+	if (rc) {
+		mutex_unlock(&cd->firmware_class_lock);
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: HW reset failed rc = %d\n", __func__, rc);
+		goto exit_hw_reset;
+	}
+	reset_status |= 0x01 << 0;
+
+	if (cd->flashless_dut) {
+		mutex_unlock(&cd->firmware_class_lock);
+
+		t = wait_event_timeout(cd->wait_q, (cd->fw_updating == true),
+			msecs_to_jiffies(200));
+		if (IS_TMO(t)) {
+			pt_debug(dev, DL_ERROR,
+				"%s: Timeout waiting for FW update",
+				__func__);
+			rc = -ETIME;
+			goto exit_hw_reset;
+		} else {
+			pt_debug(dev, DL_INFO,
+				 "%s: ----- Wait FW Loading ----",
+				 __func__);
+			rc = _pt_request_wait_for_enum_state(
+			    dev, 4000, STARTUP_STATUS_FW_RESET_SENTINEL);
+			if (rc) {
+				pt_debug(cd->dev, DL_ERROR,
+					"%s: No FW Sentinel detected rc = %d\n",
+					__func__, rc);
+				goto exit_hw_reset;
+			}
+			reset_status |= 0x01 << 1;
+		}
+	} else {
+		/* Wait for any sentinel */
+		rc = _pt_request_wait_for_enum_state(dev, 150,
+			STARTUP_STATUS_BL_RESET_SENTINEL |
+			STARTUP_STATUS_FW_RESET_SENTINEL);
+		if (rc) {
+			mutex_unlock(&cd->firmware_class_lock);
+			pt_debug(cd->dev, DL_ERROR,
+				"%s: No Sentinel detected rc = %d\n",
+				__func__, rc);
+			goto exit_hw_reset;
+		}
+
+		/* sleep needed to ensure no cmd is sent while DUT will NAK */
+		msleep(30);
+
+		if (cd->active_dut_generation == DUT_PIP1_ONLY) {
+			rc = pt_get_hid_descriptor_(cd, &hid_desc);
+			if (rc < 0) {
+				pt_debug(cd->dev, DL_ERROR,
+					"%s: Error on getting HID descriptor r=%d\n",
+					__func__, rc);
+				goto exit_hw_reset;
+			}
+
+			cd->mode = pt_get_mode(cd, &hid_desc);
+			if (cd->mode == PT_MODE_BOOTLOADER)
+				rc = pt_hid_output_bl_launch_app_(cd);
+		} else {
+			if (cd->mode == PT_MODE_BOOTLOADER)
+				rc = pt_pip2_launch_app(dev,
+						PT_CORE_CMD_UNPROTECTED);
+		}
+
+		if (rc) {
+			mutex_unlock(&cd->firmware_class_lock);
+			pt_debug(cd->dev, DL_ERROR,
+				"%s: PIP launch app failed rc = %d\n",
+				__func__, rc);
+			goto exit_hw_reset;
+		}
+		mutex_unlock(&cd->firmware_class_lock);
+
+		reset_status |= 0x01 << 1;
+		msleep(20);
+		if ((cd->active_dut_generation == DUT_UNKNOWN) ||
+			(cd->mode != PT_MODE_OPERATIONAL))
+			pt_queue_restart(cd);
+		else
+			pt_queue_enum(cd);
+	}
+
+	while (!(cd->startup_status & STARTUP_STATUS_COMPLETE) && time < 2000) {
+		msleep(50);
+		pt_debug(cd->dev, DL_INFO,
+			"%s: wait %dms for 0x%04X, current enum=0x%04X\n",
+			__func__, time, STARTUP_STATUS_COMPLETE,
+			cd->startup_status);
+		time += 50;
+	}
+	if (!(cd->startup_status & STARTUP_STATUS_COMPLETE)) {
+		rc = -ETIME;
+		goto exit_hw_reset;
+	}
+
+	pt_debug(cd->dev, DL_INFO, "%s: HW Reset complete. enum=0x%04X\n",
+		__func__, cd->startup_status);
+	reset_status |= 0x01 << 2;
+	pt_start_wd_timer(cd);
+
+exit_hw_reset:
+	return scnprintf(buf, strlen(buf),
+		"Status: %d\n"
+		"Reset Status: 0x%02X\n", rc, reset_status);
+}
+static DEVICE_ATTR(hw_reset, 0444, pt_hw_reset_show, NULL);
+
+/*******************************************************************************
+ * FUNCTION: pt_pip2_cmd_rsp_store
+ *
+ * SUMMARY: This is the store method for the raw PIP2 cmd/rsp sysfs node. Any
+ *	raw PIP2 command echo'd to this node will be sent directly to the DUT.
+ *	Command byte order:
+ *		Byte [0]   - PIP2 command ID
+ *		Byte [1-n] - PIP2 command payload
+ *
+ * RETURN: Size of passed in buffer
+ *
+ * PARAMETERS:
+ *	*dev  - pointer to device structure
+ *	*attr - pointer to device attributes
+ *	*buf  - pointer to buffer that hold the command parameters
+ *	 size - size of buf
+ ******************************************************************************/
+static ssize_t pt_pip2_cmd_rsp_store(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t size)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	u16 actual_read_len;
+	u8  input_data[PT_MAX_PIP2_MSG_SIZE + 1];
+	u8  read_buf[PT_MAX_PIP2_MSG_SIZE + 1];
+	u8  pip2_cmd_id = 0x00;
+	u8 *pip2_cmd_data = NULL;
+	int data_len = 0;
+	int length;
+	int rc = 0;
+
+	/* clear shared data */
+	mutex_lock(&cd->sysfs_lock);
+	cd->raw_cmd_status = 0;
+	cd->cmd_rsp_buf_len = 0;
+	memset(cd->cmd_rsp_buf, 0, sizeof(cd->cmd_rsp_buf));
+	mutex_unlock(&cd->sysfs_lock);
+
+	length = _pt_ic_parse_input_hex(dev, buf, size,
+			input_data, PT_MAX_PIP2_MSG_SIZE);
+	if (length <= 0 || length > (PT_MAX_PIP2_MSG_SIZE - 2)) {
+		pt_debug(dev, DL_ERROR, "%s: Invalid number of arguments\n",
+			__func__);
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	/* Send PIP2 command if enough data was provided */
+	if (length >= 1) {
+		pip2_cmd_id = input_data[0];
+		pip2_cmd_data = &input_data[1];
+		data_len = length - 1;
+		rc = _pt_request_pip2_send_cmd(dev, PT_CORE_CMD_PROTECTED,
+			pip2_cmd_id, pip2_cmd_data, data_len,
+			read_buf, &actual_read_len);
+		cd->raw_cmd_status = rc;
+		if (rc) {
+			pt_debug(dev, DL_ERROR,
+				"%s: PIP2 cmd 0x%02x failed rc = %d\n",
+				__func__, pip2_cmd_id, rc);
+			goto exit;
+		} else {
+			cd->cmd_rsp_buf_len = actual_read_len;
+			memcpy(cd->cmd_rsp_buf, read_buf, actual_read_len);
+			pt_debug(dev, DL_ERROR,
+				"%s: PIP2 actual_read_len = %d\n",
+				__func__, actual_read_len);
+		}
+	} else {
+		rc = -EINVAL;
+		pt_debug(dev, DL_ERROR,
+			"%s: Insufficient data provided for PIP2 cmd\n",
+			__func__);
+	}
+
+exit:
+	if (rc)
+		return rc;
+	return size;
+}
+/*******************************************************************************
+ * FUNCTION: pt_pip2_cmd_rsp_show
+ *
+ * SUMMARY: The show method for the raw pip2_cmd_rsp sysfs node. Any PIP2
+ *	response generated after using the store method of the pip2_cmd_rsp
+ *	sysfs node, are available to be read here.
+ *
+ * PARAMETERS:
+ *      *dev  - pointer to Device structure
+ *      *attr - pointer to the device attribute structure
+ *      *buf  - pointer to buffer to print
+ ******************************************************************************/
+static ssize_t pt_pip2_cmd_rsp_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	int i;
+	ssize_t data_len;
+	int index;
+
+	mutex_lock(&cd->sysfs_lock);
+	index = scnprintf(buf, PT_MAX_PRBUF_SIZE,
+		"Status: %d\n", cd->raw_cmd_status);
+	if (cd->raw_cmd_status)
+		goto error;
+
+	/* Remove the CRC from the length of the response */
+	data_len = cd->cmd_rsp_buf_len - 2;
+
+	/* Start printing from the data payload */
+	for (i = PIP1_RESP_COMMAND_ID_OFFSET; i < data_len; i++)
+		index += scnprintf(buf + index, PT_MAX_PRBUF_SIZE - index,
+			"%02X ", cd->cmd_rsp_buf[i]);
+
+	if (data_len >= PIP1_RESP_COMMAND_ID_OFFSET) {
+		index += scnprintf(buf + index, PT_MAX_PRBUF_SIZE - index,
+			"\n(%zd bytes)\n",
+			data_len - PIP1_RESP_COMMAND_ID_OFFSET);
+	} else {
+		index += scnprintf(buf + index, PT_MAX_PRBUF_SIZE - index,
+			"\n(%zd bytes)\n", 0);
+	}
+
+error:
+	mutex_unlock(&cd->sysfs_lock);
+	return index;
+}
+static DEVICE_ATTR(pip2_cmd_rsp, 0644, pt_pip2_cmd_rsp_show,
+	pt_pip2_cmd_rsp_store);
+
+/*******************************************************************************
+ * FUNCTION: pt_command_store
+ *
+ * SUMMARY: This is the store method for the raw PIP command sysfs node. Any
+ *	raw PIP command echo'd to this node will be sent directly to the DUT.
+ *	TTDL will not parse the command.
+ *
+ * RETURN: Size of passed in buffer
+ *
+ * PARAMETERS:
+ *	*dev  - pointer to device structure
+ *	*attr - pointer to device attributes
+ *	*buf  - pointer to buffer that hold the command parameters
+ *	 size - size of buf
+ ******************************************************************************/
+static ssize_t pt_command_store(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t size)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	unsigned short crc;
+	u16 actual_read_len;
+	u8 input_data[PT_MAX_PIP2_MSG_SIZE + 1];
+	int length;
+	int len_field;
+	int rc = 0;
+
+	mutex_lock(&cd->sysfs_lock);
+	cd->cmd_rsp_buf_len = 0;
+	memset(cd->cmd_rsp_buf, 0, sizeof(cd->cmd_rsp_buf));
+	mutex_unlock(&cd->sysfs_lock);
+
+	length = _pt_ic_parse_input_hex(dev, buf, size,
+			input_data, PT_MAX_PIP2_MSG_SIZE);
+	if (length <= 0 || length > PT_MAX_PIP2_MSG_SIZE) {
+		pt_debug(dev, DL_ERROR, "%s: Invalid number of arguments\n",
+			__func__);
+		rc = -EINVAL;
+		goto pt_command_store_exit;
+	}
+
+	/* PIP2 messages begin with 01 01 */
+	if (length >= 2 && input_data[0] == 0x01 && input_data[1] == 0x01) {
+		cd->pip2_prot_active = 1;
+		/* Override next seq tag with what was sent */
+		cd->pip2_cmd_tag_seq = input_data[4] & 0x0F;
+		/* For PIP2 cmd if length does not include crc, add it */
+		len_field = (input_data[3] << 8) | input_data[2];
+		if (len_field == length && length <= 254) {
+			crc = crc_ccitt_calculate(&input_data[2],
+				length - 2);
+			pt_debug(dev, DL_ERROR, "%s: len=%d crc=0x%02X\n",
+				__func__, length, crc);
+			input_data[length] = (crc & 0xFF00) >> 8;
+			input_data[length + 1] = crc & 0x00FF;
+			length = length + 2;
+		}
+	}
+
+	/* write PIP command to log */
+	pt_pr_buf(dev, DL_INFO, input_data, length, "command_buf");
+
+	pm_runtime_get_sync(dev);
+
+	rc = pt_hid_output_user_cmd(cd, PT_MAX_INPUT, cd->cmd_rsp_buf,
+		length, input_data, &actual_read_len);
+	pm_runtime_put(dev);
+
+	mutex_lock(&cd->sysfs_lock);
+	cd->raw_cmd_status = rc;
+	if (rc) {
+		cd->cmd_rsp_buf_len = 0;
+		pt_debug(dev, DL_ERROR, "%s: Failed to send command: %s\n",
+			__func__, buf);
+	} else {
+		cd->cmd_rsp_buf_len = actual_read_len;
+	}
+	cd->pip2_prot_active = 0;
+	mutex_unlock(&cd->sysfs_lock);
+
+pt_command_store_exit:
+	if (rc)
+		return rc;
+	return size;
+}
+static DEVICE_ATTR(command, 0220, NULL, pt_command_store);
+
+/*******************************************************************************
+ * FUNCTION: pt_response_show
+ *
+ * SUMMARY: The show method for the raw PIP response sysfs node. Any PIP
+ *	response generated after using the pt_command_store sysfs node, are
+ *	available to be read here.
+ *
+ * PARAMETERS:
+ *      *dev  - pointer to Device structure
+ *      *attr - pointer to the device attribute structure
+ *      *buf  - pointer to buffer to print
+ ******************************************************************************/
+static ssize_t pt_response_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	int i;
+	ssize_t num_read;
+	int index;
+
+	mutex_lock(&cd->sysfs_lock);
+	index = scnprintf(buf, PT_MAX_PRBUF_SIZE,
+		"Status: %d\n", cd->raw_cmd_status);
+	if (cd->raw_cmd_status)
+		goto error;
+
+	num_read = cd->cmd_rsp_buf_len;
+	for (i = 0; i < num_read; i++)
+		index += scnprintf(buf + index, PT_MAX_PRBUF_SIZE - index,
+			"0x%02X\n", cd->cmd_rsp_buf[i]);
+
+	index += scnprintf(buf + index, PT_MAX_PRBUF_SIZE - index,
+		"(%zd bytes)\n", num_read);
+
+error:
+	mutex_unlock(&cd->sysfs_lock);
+	return index;
+}
+static DEVICE_ATTR(response, 0444, pt_response_show, NULL);
+
+/*******************************************************************************
+ * FUNCTION: pt_dut_debug_show
+ *
+ * SUMMARY: Show method for the dut_debug sysfs node. Shows what parameters
+ *	are available for the store method.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*dev  - pointer to device structure
+ *	*attr - pointer to device attributes
+ *	*buf  - pointer to output buffer
+ ******************************************************************************/
+static ssize_t pt_dut_debug_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	ssize_t ret;
+
+	ret = scnprintf(buf, strlen(buf),
+		"Status: 0\n"
+		"dut_debug sends the following commands to the DUT:\n"
+		"%d %s \t- %s\n"
+		"%d %s \t- %s\n"
+		"%d %s \t- %s\n"
+		"%d %s \t- %s\n"
+		"%d %s \t- %s\n"
+		"%d %s \t- %s\n"
+		"%d %s \t- %s\n"
+		"%d %s \t- %s\n"
+		"%d %s \t- %s\n"
+		"%d %s \t- %s\n"
+		"%d %s \t- %s\n"
+		"%d %s \t- %s\n"
+		"%d %s \t- %s\n"
+		"%d %s \t- %s\n"
+		"%d %s \t- %s\n"
+		"%d %s \t- %s\n"
+		"%d %s \t- %s\n"
+		,
+		PIP1_BL_CMD_ID_VERIFY_APP_INTEGRITY, "", "BL Verify APP",
+		PT_DUT_DBG_HID_RESET, "", "HID Reset",
+		PT_DUT_DBG_HID_SET_POWER_ON, "", "HID SET_POWER ON",
+		PT_DUT_DBG_HID_SET_POWER_SLEEP, "", "HID SET_POWER SLEEP",
+		PT_DUT_DBG_HID_SET_POWER_STANDBY, "", "HID SET_POWER STANDBY",
+		PIP1_BL_CMD_ID_GET_INFO, "", "BL Get Info",
+		PIP1_BL_CMD_ID_PROGRAM_AND_VERIFY, "", "BL Program & Verify",
+		PIP1_BL_CMD_ID_LAUNCH_APP, "", "BL Launch APP",
+		PIP1_BL_CMD_ID_INITIATE_BL, "", "BL Initiate BL",
+		PT_DUT_DBG_PIP_SOFT_RESET, "", "PIP Soft Reset",
+		PT_DUT_DBG_RESET, "", "Toggle the TP_XRES GPIO",
+		PT_DUT_DBG_PIP_NULL, "", "PIP NULL (PING)",
+		PT_DUT_DBG_PIP_ENTER_BL, "", "PIP enter BL",
+		PT_DUT_DBG_HID_SYSINFO, "", "HID system info",
+		PT_DUT_DBG_PIP_SUSPEND_SCAN, "", "Suspend Scan",
+		PT_DUT_DBG_PIP_RESUME_SCAN, "", "Resume Scan",
+		PT_DUT_DBG_HID_DESC, "", "Get HID Desc"
+	);
+
+	return ret;
+}
+/*******************************************************************************
+ * FUNCTION: pt_drv_debug_show
+ *
+ * SUMMARY: Show method for the drv_debug sysfs node. Shows what parameters
+ *	are available for the store method.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*dev  - pointer to device structure
+ *	*attr - pointer to device attributes
+ *	*buf  - pointer to output buffer
+ ******************************************************************************/
+static ssize_t pt_drv_debug_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	ssize_t ret;
+
+	ret = scnprintf(buf, strlen(buf),
+		"Status: 0\n"
+		"drv_debug supports the following values:\n"
+		"%d %s \t- %s\n"
+		"%d %s \t- %s\n"
+		"%d %s \t- %s\n"
+		"%d %s \t- %s\n"
+		"%d %s \t- %s\n"
+		"%d %s \t- %s\n"
+		"%d %s \t- %s\n"
+		"%d %s \t- %s\n"
+		"%d %s \t- %s\n"
+		"%d %s - %s\n"
+		"%d %s \t- %s\n"
+		"%d %s \t- %s\n"
+#ifdef TTDL_DIAGNOSTICS
+		"%d %s \t- %s\n"
+		"%d %s \t- %s\n"
+		"%d %s \t- %s\n"
+#endif /* TTDL_DIAGNOSTICS */
+		"%d %s \t- %s\n"
+#ifdef TTDL_DIAGNOSTICS
+		"%d %s \t- %s\n"
+		"%d %s \t- %s\n"
+		"%d %s \t- %s\n"
+		"%d %s \t- %s\n"
+		"%d %s \t- %s\n"
+		"%d %s \t- %s\n"
+		"%d %s \t- %s\n"
+		"%d %s \t- %s\n"
+		"%d %s \t- %s\n"
+#endif /* TTDL_DIAGNOSTICS */
+		,
+		PT_DRV_DBG_SUSPEND, "       ", "Suspend TTDL responding to INT",
+		PT_DRV_DBG_RESUME,  "       ", "Resume TTDL responding to INT",
+		PT_DRV_DBG_STOP_WD, "     ", "Stop TTDL WD",
+		PT_DRV_DBG_START_WD, "     ", "Start TTDL WD",
+		PT_DRV_DBG_TTHE_TUNER_EXIT, "     ", "Exit TTHE Tuner Logging",
+		PT_DRV_DBG_TTHE_BUF_CLEAN, "     ", "Clear TTHE Tuner buffer",
+		PT_DRV_DBG_CLEAR_PARM_LIST, "     ", "Clear RAM Param list",
+		PT_DRV_DBG_FORCE_BUS_READ, "     ", "Force bus read",
+		PT_DRV_DBG_CLEAR_CAL_DATA, "     ", "Clear CAL Cache",
+		PT_DRV_DBG_REPORT_LEVEL, "[0|1|2|3|4]", "Set TTDL Debug Level",
+		PT_DRV_DBG_WATCHDOG_INTERVAL, "[n] ", "TTDL WD Interval in ms",
+		PT_DRV_DBG_SHOW_TIMESTAMP, "[0|1]", "Show Timestamps"
+#ifdef TTDL_DIAGNOSTICS
+		, PT_DRV_DBG_SETUP_PWR, "[0|1]", "Power DUT up/down",
+		PT_DRV_DBG_GET_PUT_SYNC, "[0|1]", "Get/Put Linux Sleep",
+		PT_DRV_DBG_SET_TT_DATA, "[0|1]", "Display TT_DATA"
+#endif /* TTDL_DIAGNOSTICS */
+		, PT_DRV_DBG_SET_GENERATION, "[0|1|2]", "Set DUT generation"
+#ifdef TTDL_DIAGNOSTICS
+		, PT_DRV_DBG_SET_BRIDGE_MODE, "[0|1]", "On/Off Bridge Mode",
+		PT_DRV_DBG_SET_I2C_ADDRESS, "[0-127]", "I2C DUT Address",
+		PT_DRV_DBG_SET_FLASHLESS_DUT, "[0|1]", "Flashless DUT yes/no",
+		PT_DRV_DBG_SET_FORCE_SEQ, "[8-15]", "Force PIP2 Sequence #",
+		PT_DRV_DBG_BL_WITH_NO_INT, "[0|1]", "BL with no INT",
+		PT_DRV_DBG_CAL_CACHE_IN_HOST, "[0|1]", "CAL Cache in host",
+		PT_DRV_DBG_MULTI_CHIP, "[0|1]", "Multi Chip Support",
+		PT_DRV_DBG_PIP_TIMEOUT, "[100-7000]", "PIP Resp Timeout (ms)",
+		PT_DRV_DBG_TTHE_HID_USB_FORMAT, "[0|1]",
+			"TTHE_TUNER HID USB Format"
+#endif /* TTDL_DIAGNOSTICS */
+	);
+
+	return ret;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_drv_debug_store
+ *
+ * SUMMARY: Currently the store method for both sysfs nodes: drv_debug and
+ *	dut_debug. Drv_debug will contain all functionality that can be run
+ *	without a DUT preset and is available anytime TTDL is running.
+ *	Dut_debug requires a DUT to be available and will only be created after
+ *	a DUT has been detected.
+ *	This function will eventually be split into two but until the overlap
+ *	has been depricated this function contains all commands that can be
+ *	used for TTDL/DUT debugging status and control.
+ *	All commands require at least one value to be passed in *buf with some
+ *	requiring two.
+ *
+ * RETURN: Size of passed in buffer
+ *
+ * PARAMETERS:
+ *	*dev  - pointer to device structure
+ *	*attr - pointer to device attributes
+ *	*buf  - pointer to buffer that hold the command parameters
+ *	 size - size of buf
+ ******************************************************************************/
+static ssize_t pt_drv_debug_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	unsigned long value;
+	int rc = 0;
+	u8 return_data[8];
+	static u8 wd_disabled;
+	u32 input_data[3];
+	int length;
+#ifdef TTDL_DIAGNOSTICS
+	struct i2c_client *client = to_i2c_client(dev);
+	unsigned short crc = 0;
+	u16 cal_size;
+#endif
+	input_data[0] = 0;
+	input_data[1] = 0;
+
+	/* Maximmum input is two elements */
+	length = _pt_ic_parse_input(dev, buf, size,
+			input_data, ARRAY_SIZE(input_data));
+	if (length < 1 || length > 2) {
+		pt_debug(dev, DL_ERROR, "%s: Invalid number of arguments\n",
+			__func__);
+		rc = -EINVAL;
+		goto pt_drv_debug_store_exit;
+	}
+	value = input_data[0];
+
+	if (length == 1) {
+		pt_debug(dev, DL_DEBUG,
+			"%s: Debug Cmd Received (id=%d)\n",
+			__func__, input_data[0]);
+	} else if (length == 2) {
+		pt_debug(dev, DL_DEBUG,
+			"%s: Debug Cmd Received (id=%d, data=%d)\n",
+			__func__, input_data[0], input_data[1]);
+	} else {
+		pt_debug(dev, DL_DEBUG,
+			"%s: Invalid arguments received\n", __func__);
+		rc = -EINVAL;
+		goto pt_drv_debug_store_exit;
+	}
+
+	/* Start watchdog timer command */
+	if (value == PT_DRV_DBG_START_WD) {
+		pt_debug(dev, DL_INFO, "%s: Cmd: Start Watchdog\n", __func__);
+		wd_disabled = 0;
+		cd->watchdog_force_stop = false;
+		pt_start_wd_timer(cd);
+		goto pt_drv_debug_store_exit;
+	}
+
+	/* Stop watchdog timer temporarily */
+	pt_stop_wd_timer(cd);
+
+	if (value == PT_DRV_DBG_STOP_WD) {
+		pt_debug(dev, DL_INFO, "%s: Cmd: Stop Watchdog\n", __func__);
+		wd_disabled = 1;
+		cd->watchdog_force_stop = true;
+		goto pt_drv_debug_store_exit;
+	}
+
+	switch (value) {
+	case PT_DRV_DBG_SUSPEND:			/* 4 */
+		pt_debug(dev, DL_INFO, "%s: TTDL: Core Sleep\n", __func__);
+		wd_disabled = 1;
+		rc = pt_core_suspend_(cd->dev);
+		if (rc)
+			pt_debug(dev, DL_ERROR, "%s: Suspend failed rc=%d\n",
+				__func__, rc);
+		else {
+			pt_debug(dev, DL_INFO, "%s: Suspend succeeded\n",
+				__func__);
+			cd->drv_debug_suspend = true;
+			pt_debug(dev, DL_INFO, "%s: Debugfs flag set:\n", __func__);
+		}
+		break;
+
+	case PT_DRV_DBG_RESUME:				/* 5 */
+		pt_debug(dev, DL_INFO, "%s: TTDL: Wake\n", __func__);
+		rc = pt_core_resume_(cd->dev);
+		if (rc)
+			pt_debug(dev, DL_ERROR, "%s: Resume failed rc=%d\n",
+				__func__, rc);
+		else {
+			pt_debug(dev, DL_INFO, "%s: Resume succeeded\n",
+				__func__);
+			cd->drv_debug_suspend = false;
+			pt_debug(dev, DL_INFO, "%s: Debugfs flag reset:\n", __func__);
+		}
+		break;
+	case PIP1_BL_CMD_ID_VERIFY_APP_INTEGRITY:	/* BL - 49 */
+		pt_debug(dev, DL_INFO, "%s: Cmd: verify app integ\n", __func__);
+		pt_hid_output_bl_verify_app_integrity(cd, &return_data[0]);
+		break;
+	case PT_DUT_DBG_HID_RESET:			/* 50 */
+		pt_debug(dev, DL_INFO, "%s: Cmd: hid_reset\n", __func__);
+		pt_hid_cmd_reset(cd);
+		break;
+	case PT_DUT_DBG_HID_SET_POWER_ON:		/* 53 */
+		pt_debug(dev, DL_INFO, "%s: Cmd: hid_set_power_on\n", __func__);
+		pt_hid_cmd_set_power(cd, HID_POWER_ON);
+		wd_disabled = 0;
+		break;
+	case PT_DUT_DBG_HID_SET_POWER_SLEEP:		/* 54 */
+		pt_debug(dev, DL_INFO, "%s: Cmd: hid_set_power_off\n",
+			 __func__);
+		wd_disabled = 1;
+		pt_hid_cmd_set_power(cd, HID_POWER_SLEEP);
+		break;
+	case PT_DUT_DBG_HID_SET_POWER_STANDBY:		/* 55 */
+		pt_debug(dev, DL_INFO, "%s: Cmd: hid_set_power_standby\n",
+			 __func__);
+		wd_disabled = 1;
+		pt_hid_cmd_set_power(cd, HID_POWER_STANDBY);
+		break;
+	case PIP1_BL_CMD_ID_GET_INFO:			/* BL - 56 */
+		pt_debug(dev, DL_INFO, "%s: Cmd: bl get info\n", __func__);
+		pt_hid_output_bl_get_information(cd, return_data);
+		break;
+	case PIP1_BL_CMD_ID_PROGRAM_AND_VERIFY:		/* BL - 57 */
+		pt_debug(dev, DL_INFO, "%s: Cmd: program and verify\n",
+			__func__);
+		pt_hid_output_bl_program_and_verify(cd, 0, NULL);
+		break;
+	case PIP1_BL_CMD_ID_LAUNCH_APP:			/* BL - 59 */
+		pt_debug(dev, DL_INFO, "%s: Cmd: launch app\n", __func__);
+		pt_hid_output_bl_launch_app(cd);
+		break;
+	case PIP1_BL_CMD_ID_INITIATE_BL:		/* BL - 72 */
+		pt_debug(dev, DL_INFO, "%s: Cmd: initiate bl\n", __func__);
+		pt_hid_output_bl_initiate_bl(cd, 0, NULL, 0, NULL);
+		break;
+	case PT_DUT_DBG_PIP_SOFT_RESET:			/* 97 */
+		pt_debug(dev, DL_INFO, "%s: Cmd: Soft Reset\n", __func__);
+		rc = pt_hw_soft_reset(cd, PT_CORE_CMD_PROTECTED);
+		break;
+	case PT_DUT_DBG_RESET:				/* 98 */
+		pt_debug(dev, DL_INFO, "%s: Cmd: Hard Reset\n", __func__);
+		rc = pt_hw_hard_reset(cd);
+		break;
+	case PT_DUT_DBG_PIP_NULL:			/* 100 */
+		pt_debug(dev, DL_INFO, "%s: Cmd: Ping (null)\n", __func__);
+		pt_pip_null(cd);
+		break;
+	case PT_DUT_DBG_PIP_ENTER_BL:			/* 101 */
+		pt_debug(dev, DL_INFO, "%s: Cmd: start_bootloader\n", __func__);
+		rc = pt_pip_start_bootloader(cd);
+		if (!rc) {
+			cd->startup_status = STARTUP_STATUS_BL_RESET_SENTINEL;
+			cd->mode = PT_MODE_BOOTLOADER;
+		}
+		break;
+	case PT_DUT_DBG_HID_SYSINFO:			/* 102 */
+		pt_debug(dev, DL_INFO, "%s: Cmd: get_sysinfo\n", __func__);
+		pt_hid_output_get_sysinfo(cd);
+		break;
+	case PT_DUT_DBG_PIP_SUSPEND_SCAN:		/* 103 */
+		pt_debug(dev, DL_INFO, "%s: Cmd: suspend_scanning\n", __func__);
+		pt_pip_suspend_scanning(cd);
+		break;
+	case PT_DUT_DBG_PIP_RESUME_SCAN:		/* 104 */
+		pt_debug(dev, DL_INFO, "%s: Cmd: resume_scanning\n", __func__);
+		pt_pip_resume_scanning(cd);
+		break;
+#ifdef TTHE_TUNER_SUPPORT
+	case PT_DRV_DBG_TTHE_TUNER_EXIT:		/* 107 */
+		cd->tthe_exit = 1;
+		wake_up(&cd->wait_q);
+		kfree(cd->tthe_buf);
+		cd->tthe_buf = NULL;
+		cd->tthe_exit = 0;
+		break;
+	case PT_DRV_DBG_TTHE_BUF_CLEAN:			/* 108 */
+		if (cd->tthe_buf)
+			memset(cd->tthe_buf, 0, PT_MAX_PRBUF_SIZE);
+		else
+			pt_debug(dev, DL_INFO, "%s : tthe_buf not existed\n",
+				__func__);
+		break;
+#endif
+#ifdef TTDL_DIAGNOSTICS
+	case PT_DUT_DBG_HID_DESC:			/* 109 */
+		pt_debug(dev, DL_INFO, "%s: Cmd: get_hid_desc\n", __func__);
+		pt_get_hid_descriptor(cd, &cd->hid_desc);
+		break;
+	case PT_DRV_DBG_CLEAR_PARM_LIST:		/* 110 */
+		pt_debug(dev, DL_INFO, "%s: TTDL: Clear Parameter List\n",
+			__func__);
+		pt_erase_parameter_list(cd);
+		break;
+	case PT_DRV_DBG_FORCE_BUS_READ:			/* 111 */
+		rc = pt_read_input(cd);
+		if (!rc)
+			pt_parse_input(cd);
+		break;
+	case PT_DRV_DBG_CLEAR_CAL_DATA:			/* 112 */
+		rc = _pt_manage_local_cal_data(dev, PT_CAL_DATA_CLEAR,
+			&cal_size, &crc);
+		if (rc)
+			pt_debug(dev, DL_ERROR,
+				"%s: CAL Data clear failed rc=%d\n",
+				__func__, rc);
+		else
+			pt_debug(dev, DL_INFO,
+				"%s: CAL Cleared, Chip ID=0x%04X size=%d\n",
+				__func__, crc, size);
+		break;
+
+	case PT_DRV_DBG_REPORT_LEVEL:			/* 200 */
+		mutex_lock(&cd->system_lock);
+		if (input_data[1] >= 0 && input_data[1] < DL_MAX) {
+			cd->debug_level = input_data[1];
+			pt_debug(dev, DL_INFO, "%s: Set debug_level: %d\n",
+				__func__, cd->debug_level);
+		} else {
+			rc = -EINVAL;
+			pt_debug(dev, DL_ERROR, "%s: Invalid debug_level: %d\n",
+				__func__, input_data[1]);
+		}
+		mutex_unlock(&(cd->system_lock));
+		break;
+#endif
+	case PT_DRV_DBG_WATCHDOG_INTERVAL:		/* 201 */
+		mutex_lock(&cd->system_lock);
+		if (input_data[1] > 100) {
+			cd->watchdog_interval = input_data[1];
+			pt_debug(dev, DL_INFO,
+				"%s: Set watchdog_ interval to: %d\n",
+				__func__, cd->watchdog_interval);
+			pt_start_wd_timer(cd);
+		} else {
+			rc = -EINVAL;
+			pt_debug(dev, DL_ERROR,
+				"%s: Invalid watchdog interval: %d\n",
+				__func__, input_data[1]);
+		}
+		mutex_unlock(&(cd->system_lock));
+		break;
+#ifdef TTDL_DIAGNOSTICS
+	case PT_DRV_DBG_SHOW_TIMESTAMP:			/* 202 */
+		mutex_lock(&cd->system_lock);
+		if (input_data[1] == 0) {
+			cd->show_timestamp = 0;
+			pt_debug(dev, DL_INFO, "%s: Disable show_timestamp\n",
+				__func__);
+		} else if (input_data[1] == 1) {
+			cd->show_timestamp = 1;
+			pt_debug(dev, DL_INFO, "%s: Enable show_timestamp\n",
+				__func__);
+		} else {
+			rc = -EINVAL;
+			pt_debug(dev, DL_ERROR,
+				"%s: Invalid parameter: %d\n",
+				__func__, input_data[1]);
+		}
+		mutex_unlock(&(cd->system_lock));
+		break;
+	case PT_DRV_DBG_SETUP_PWR:			/* 205 */
+		if (input_data[1] == 0) {
+			cd->cpdata->setup_power(cd->cpdata,
+				PT_MT_POWER_OFF, cd->dev);
+			pt_debug(dev, DL_INFO,
+				"%s: Initiate Power Off\n", __func__);
+		} else if (input_data[1] == 1) {
+			cd->cpdata->setup_power(cd->cpdata,
+				PT_MT_POWER_ON, cd->dev);
+			pt_debug(dev, DL_INFO,
+				"%s: Initiate Power On\n", __func__);
+		} else {
+			rc = -EINVAL;
+			pt_debug(dev, DL_ERROR,
+				"%s: Invalid parameter: %d\n",
+				__func__, input_data[1]);
+		}
+		break;
+	case PT_DRV_DBG_GET_PUT_SYNC:			/* 206 */
+		if (input_data[1] == 0) {
+			pm_runtime_put(dev);
+			pt_debug(dev, DL_ERROR,
+				"%s: Force call pm_runtime_put()\n", __func__);
+		} else if (input_data[1] == 1) {
+			pm_runtime_get_sync(dev);
+			pt_debug(dev, DL_ERROR,
+				"%s: Force call pm_runtime_get_sync()\n",
+				__func__);
+		} else {
+			rc = -EINVAL;
+			pt_debug(dev, DL_ERROR,
+				"%s: WARNING: Invalid parameter: %d\n",
+				__func__, input_data[1]);
+		}
+		break;
+	case PT_DRV_DBG_SET_TT_DATA:			/* 208 */
+		mutex_lock(&cd->system_lock);
+		if (input_data[1] == 0) {
+			cd->show_tt_data = false;
+			pt_debug(dev, DL_INFO,
+				"%s: Disable TT_DATA\n", __func__);
+		} else if (input_data[1] == 1) {
+			cd->show_tt_data = true;
+			pt_debug(dev, DL_INFO,
+				"%s: Enable TT_DATA\n", __func__);
+		} else {
+			rc = -EINVAL;
+			pt_debug(dev, DL_ERROR,
+				"%s: Invalid parameter: %d\n",
+				__func__, input_data[1]);
+		}
+		mutex_unlock(&(cd->system_lock));
+		break;
+	case PT_DRV_DBG_SET_GENERATION:			/* 210 */
+		if (input_data[1] == cd->active_dut_generation) {
+			mutex_lock(&cd->system_lock);
+			cd->set_dut_generation = true;
+			mutex_unlock(&(cd->system_lock));
+		} else {
+			mutex_lock(&cd->system_lock);
+			if (input_data[1] == 0) {
+				cd->active_dut_generation = DUT_UNKNOWN;
+				cd->set_dut_generation = false;
+			} else if (input_data[1] == 1) {
+				cd->active_dut_generation = DUT_PIP1_ONLY;
+				cd->set_dut_generation = true;
+			} else if (input_data[1] == 2) {
+				cd->active_dut_generation = DUT_PIP2_CAPABLE;
+				cd->set_dut_generation = true;
+			} else {
+				rc = -EINVAL;
+				pt_debug(dev, DL_ERROR,
+					"%s: Invalid parameter: %d\n",
+					__func__, input_data[1]);
+				break;
+			}
+			cd->startup_status = STARTUP_STATUS_START;
+			pt_debug(cd->dev, DL_DEBUG,
+				"%s: Startup Status Reset\n", __func__);
+			mutex_unlock(&(cd->system_lock));
+
+			pt_debug(dev, DL_INFO,
+				"%s: Active DUT Generation Set to: %d\n",
+				__func__, cd->active_dut_generation);
+
+			/* Changing DUT generations full restart needed */
+			pt_queue_restart(cd);
+		}
+		break;
+	case PT_DRV_DBG_SET_BRIDGE_MODE:		/* 211 */
+		mutex_lock(&cd->system_lock);
+		if (input_data[1] == 0) {
+			cd->bridge_mode = false;
+			pt_debug(dev, DL_INFO,
+				"%s: Disable Bridge Mode\n", __func__);
+		} else if (input_data[1] == 1) {
+			cd->bridge_mode = true;
+			pt_debug(dev, DL_INFO,
+				"%s: Enable Bridge Mode\n", __func__);
+		} else {
+			rc = -EINVAL;
+			pt_debug(dev, DL_ERROR, "%s: Invalid parameter: %d\n",
+				__func__, input_data[1]);
+		}
+		mutex_unlock(&(cd->system_lock));
+		break;
+	case PT_DRV_DBG_SET_I2C_ADDRESS:		/* 212 */
+		mutex_lock(&cd->system_lock);
+		/* Only a 7bit address is valid */
+		if (input_data[1] >= 0 && input_data[1] <= 0x7F) {
+			client->addr = input_data[1];
+			pt_debug(dev, DL_INFO,
+				"%s: Set I2C Address: 0x%2X\n",
+				__func__, client->addr);
+		} else {
+			rc = -EINVAL;
+			pt_debug(dev, DL_ERROR, "%s: Invalid I2C Address %d\n",
+				__func__, input_data[1]);
+			client->addr = 0x24;
+		}
+		mutex_unlock(&(cd->system_lock));
+		break;
+	case PT_DRV_DBG_SET_FLASHLESS_DUT:		/* 213 */
+		mutex_lock(&cd->system_lock);
+		if (input_data[1] == 0) {
+			cd->flashless_dut = 0;
+			cd->flashless_auto_bl = PT_SUPPRESS_AUTO_BL;
+			pt_debug(dev, DL_INFO, "%s: Disable FLAHLESS DUT\n",
+			__func__);
+		} else if (input_data[1] == 1) {
+			cd->flashless_dut = 1;
+			cd->flashless_auto_bl = PT_ALLOW_AUTO_BL;
+			pt_debug(dev, DL_INFO, "%s: Enable FLAHLESS DUT\n",
+			__func__);
+		}
+		mutex_unlock(&(cd->system_lock));
+		break;
+	case PT_DRV_DBG_SET_FORCE_SEQ:			/* 214 */
+		mutex_lock(&cd->system_lock);
+		if (input_data[1] >= 0x8 && input_data[1] <= 0xF) {
+			cd->force_pip2_seq = input_data[1];
+			cd->pip2_cmd_tag_seq = input_data[1];
+			pt_debug(dev, DL_INFO,
+				"%s: Force PIP2 Seq to: 0x%02X\n",
+				__func__, input_data[1]);
+		} else {
+			cd->force_pip2_seq = 0;
+			pt_debug(dev, DL_INFO,
+				"%s: Clear Forced PIP2 Seq\n", __func__);
+		}
+		mutex_unlock(&(cd->system_lock));
+		break;
+	case PT_DRV_DBG_BL_WITH_NO_INT:			/* 215 */
+		mutex_lock(&cd->system_lock);
+		if (input_data[1] == 0) {
+			cd->bl_with_no_int = 0;
+			pt_debug(dev, DL_INFO, "%s: BL using IRQ\n", __func__);
+		} else if (input_data[1] == 1) {
+			cd->bl_with_no_int = 1;
+			pt_debug(dev, DL_INFO, "%s: BL using Polling\n",
+				__func__);
+		} else {
+			rc = -EINVAL;
+			pt_debug(dev, DL_ERROR, "%s: Invalid parameter: %d\n",
+				__func__, input_data[1]);
+		}
+		mutex_unlock(&(cd->system_lock));
+		break;
+	case PT_DRV_DBG_CAL_CACHE_IN_HOST:		/* 216 */
+		mutex_lock(&cd->system_lock);
+		if (input_data[1] == 0) {
+			cd->cal_cache_in_host = PT_FEATURE_DISABLE;
+			pt_debug(dev, DL_INFO,
+				"%s: Disable Calibration cache in host\n",
+				__func__);
+		} else if (input_data[1] == 1) {
+			cd->cal_cache_in_host = PT_FEATURE_ENABLE;
+			pt_debug(dev, DL_INFO,
+				"%s: Enable Calibration cache in host\n",
+				__func__);
+		} else {
+			rc = -EINVAL;
+			pt_debug(dev, DL_ERROR, "%s: Invalid parameter: %d\n",
+				__func__, input_data[1]);
+		}
+		mutex_unlock(&(cd->system_lock));
+		break;
+	case PT_DRV_DBG_MULTI_CHIP:			/* 217 */
+		mutex_lock(&cd->system_lock);
+		if (input_data[1] == 0) {
+			cd->multi_chip = PT_FEATURE_DISABLE;
+			cd->ttdl_bist_select = 0x07;
+			pt_debug(dev, DL_INFO,
+				"%s: Disable Multi-chip support\n", __func__);
+		} else if (input_data[1] == 1) {
+			cd->multi_chip = PT_FEATURE_ENABLE;
+			cd->ttdl_bist_select = 0x3F;
+			pt_debug(dev, DL_INFO,
+				"%s: Enable Multi-chip support\n", __func__);
+		} else {
+			rc = -EINVAL;
+			pt_debug(dev, DL_ERROR, "%s: Invalid parameter: %d\n",
+				__func__, input_data[1]);
+		}
+		mutex_unlock(&(cd->system_lock));
+		break;
+	case PT_DRV_DBG_SET_PANEL_ID_TYPE:		/* 218 */
+		mutex_lock(&cd->system_lock);
+		if (input_data[1] <= 0x07) {
+			cd->panel_id_support = input_data[1];
+			pt_debug(dev, DL_INFO,
+				"%s: Set panel_id_support to %d\n",
+				__func__, cd->panel_id_support);
+		} else {
+			rc = -EINVAL;
+			pt_debug(dev, DL_ERROR, "%s: Invalid parameter: %d\n",
+				__func__, input_data[1]);
+		}
+		mutex_unlock(&(cd->system_lock));
+		break;
+	case PT_DRV_DBG_PIP_TIMEOUT:			/* 219 */
+		mutex_lock(&cd->system_lock);
+		if (input_data[1] >= 100 && input_data[1] <= 7000) {
+			/*
+			 * The timeout is changed for some cases so the
+			 * pip_cmd_timeout_default is used to retore back to
+			 * what the user requested as the new timeout.
+			 */
+			cd->pip_cmd_timeout_default = input_data[1];
+			cd->pip_cmd_timeout = input_data[1];
+			pt_debug(dev, DL_INFO,
+				"%s: PIP Timeout = %d\n", __func__,
+				cd->pip_cmd_timeout_default);
+		} else {
+			rc = -EINVAL;
+			pt_debug(dev, DL_ERROR, "%s: Invalid parameter: %d\n",
+				__func__, input_data[1]);
+		}
+		mutex_unlock(&(cd->system_lock));
+		break;
+	case PT_DRV_DBG_TTHE_HID_USB_FORMAT:		/* 220 */
+		mutex_lock(&cd->system_lock);
+		if (input_data[1] == 0) {
+			cd->tthe_hid_usb_format = PT_FEATURE_DISABLE;
+			pt_debug(dev, DL_INFO,
+				"%s: Disable tthe_tuner HID-USB format\n",
+				__func__);
+		} else if (input_data[1] == 1) {
+			cd->tthe_hid_usb_format = PT_FEATURE_ENABLE;
+			pt_debug(dev, DL_INFO,
+				"%s: Enable tthe_tuner HID-USB format\n",
+				__func__);
+		} else {
+			rc = -EINVAL;
+			pt_debug(dev, DL_ERROR, "%s: Invalid parameter: %d\n",
+				__func__, input_data[1]);
+		}
+		mutex_unlock(&(cd->system_lock));
+		break;
+#endif /* TTDL_DIAGNOSTICS */
+	default:
+		rc = -EINVAL;
+		pt_debug(dev, DL_ERROR, "%s: Invalid value\n", __func__);
+	}
+
+	/* Enable watchdog timer */
+	if (!wd_disabled)
+		pt_start_wd_timer(cd);
+
+pt_drv_debug_store_exit:
+	if (rc)
+		return rc;
+	return size;
+}
+static DEVICE_ATTR(drv_debug, 0644, pt_drv_debug_show,
+	pt_drv_debug_store);
+
+/*******************************************************************************
+ * FUNCTION: pt_sleep_status_show
+ *
+ * SUMMARY: Show method for the sleep_status sysfs node that will show the
+ *	sleep status as either ON or OFF
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*dev  - pointer to device structure
+ *	*attr - pointer to device attributes
+ *	*buf  - pointer to output buffer
+ ******************************************************************************/
+static ssize_t pt_sleep_status_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	ssize_t ret;
+
+	mutex_lock(&cd->system_lock);
+	if (cd->sleep_state == SS_SLEEP_ON)
+		ret = scnprintf(buf, PT_MAX_PRBUF_SIZE, "off\n");
+	else
+		ret = scnprintf(buf, PT_MAX_PRBUF_SIZE, "on\n");
+	mutex_unlock(&cd->system_lock);
+
+	return ret;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_panel_id_show
+ *
+ * SUMMARY: Show method for the panel_id sysfs node that will show the
+ *	detected panel ID from the DUT
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*dev  - pointer to device structure
+ *	*attr - pointer to device attributes
+ *	*buf  - pointer to output buffer
+ ******************************************************************************/
+static ssize_t pt_panel_id_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	ssize_t ret;
+	u8 pid = PANEL_ID_NOT_ENABLED;
+	int rc = 0;
+
+	if (cd->active_dut_generation == DUT_PIP1_ONLY) {
+		/*
+		 * The DUT should report the same panel ID from both the BL and
+		 * the FW unless the panel_id feature is set to only
+		 * PT_PANEL_ID_BY_SYS_INFO, in which case the BL is not able
+		 * to retrieve the panel_id.
+		 */
+		if (cd->mode == PT_MODE_BOOTLOADER) {
+			/*
+			 * Return the stored value if PT_PANEL_ID_BY_BL
+			 * is not supported while other feature exits.
+			 */
+			if (cd->panel_id_support & PT_PANEL_ID_BY_BL) {
+				rc = pt_hid_output_bl_get_panel_id_(cd, &pid);
+				if (rc) {
+					pt_debug(dev, DL_ERROR, "%s: %s %s\n",
+						 "Failed to retrieve Panel ID. ",
+						 "Using cached value\n",
+						 __func__);
+				}
+			}
+		} else if (cd->mode == PT_MODE_OPERATIONAL) {
+			if (cd->panel_id_support &
+			    (PT_PANEL_ID_BY_BL | PT_PANEL_ID_BY_SYS_INFO)) {
+				/* For all systems sysinfo has the panel_id */
+				rc = pt_hid_output_get_sysinfo(cd);
+				if (!rc)
+					pid =
+					 cd->sysinfo.sensing_conf_data.panel_id;
+				pt_debug(dev, DL_ERROR,
+					 "%s: Gen6 FW mode rc=%d PID=0x%02X\n",
+					 __func__, rc, pid);
+			}
+		} else {
+			pt_debug(dev, DL_ERROR, "%s: Active mode unknown\n",
+				__func__);
+			rc = -EPERM;
+		}
+	} else if (cd->active_dut_generation == DUT_PIP2_CAPABLE) {
+		if (cd->mode == PT_MODE_BOOTLOADER) {
+			if (cd->panel_id_support & PT_PANEL_ID_BY_BL) {
+				rc = _pt_pip2_get_panel_id_by_gpio(cd, &pid);
+				if (rc) {
+					pt_debug(dev, DL_ERROR,
+						"%s: BL get panel ID failed rc=%d\n",
+						__func__, rc);
+				}
+			}
+		} else if (cd->mode == PT_MODE_OPERATIONAL) {
+			if (cd->panel_id_support &
+			    (PT_PANEL_ID_BY_BL | PT_PANEL_ID_BY_SYS_INFO)) {
+				rc = pt_hid_output_get_sysinfo(cd);
+				if (!rc)
+					pid =
+					 cd->sysinfo.sensing_conf_data.panel_id;
+				pt_debug(dev, DL_ERROR,
+					 "%s: TT/TC FW mode rc=%d PID=0x%02X\n",
+					 __func__, rc, pid);
+			}
+		} else {
+			pt_debug(dev, DL_ERROR, "%s: Active mode unknown\n",
+				__func__);
+			rc = -EPERM;
+		}
+	} else {
+		pt_debug(dev, DL_ERROR, "%s: Dut generation is unknown\n",
+				__func__);
+		rc = -EPERM;
+	}
+
+	ret = scnprintf(buf, PT_MAX_PRBUF_SIZE, "Status: %d\n0x%02X\n",
+			rc, pid);
+	return ret;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_get_param_store
+ *
+ * SUMMARY: Store method for the get_param sysfs node. Stores what parameter
+ *	ID to retrieve with the show method.
+ *
+ * NOTE: This sysfs node is only available after a DUT has been enumerated
+ *
+ * RETURN: Size of passed in buffer if successful
+ *
+ * PARAMETERS:
+ *	*dev  - pointer to device structure
+ *	*attr - pointer to device attributes
+ *	*buf  - pointer to output buffer
+ *	 size - size of buf
+ ******************************************************************************/
+static ssize_t pt_get_param_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	u32 input_data[2];
+	int length;
+	int rc = 0;
+
+	/* Maximum input of one value */
+	length = _pt_ic_parse_input(dev, buf, size, input_data,
+		ARRAY_SIZE(input_data));
+	if (length != 1) {
+		pt_debug(dev, DL_ERROR, "%s: Invalid number of arguments\n",
+			__func__);
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	mutex_lock(&cd->system_lock);
+	cd->get_param_id = input_data[0];
+	mutex_unlock(&(cd->system_lock));
+
+exit:
+	if (rc)
+		return rc;
+	return size;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_get_param_show
+ *
+ * SUMMARY: Show method for the get_param sysfs node. Retrieves the
+ *	parameter data from the DUT based on the ID stored in the core
+ *	data variable "get_param_id". If the ID is invalid, the DUT cannot
+ *	communicate or some other error occures, an error status is returned
+ *	with no value following.
+ *	Output is in the form:
+ *	   Status: x
+ *	   0xyyyyyyyy
+ *	The 32bit data will only follow the status code if the status == 0
+ *
+ * NOTE: This sysfs node is only available after a DUT has been enumerated
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*dev  - pointer to device structure
+ *	*attr - pointer to device attributes
+ *	*buf  - pointer to output buffer
+ ******************************************************************************/
+static ssize_t pt_get_param_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct  pt_core_data *cd = dev_get_drvdata(dev);
+	ssize_t ret = 0;
+	int     status;
+	u32     value = 0;
+
+	status = pt_pip_get_param(cd, cd->get_param_id, &value);
+
+	if (status) {
+		pt_debug(dev, DL_ERROR, "%s: %s Failed, status = %d\n",
+			__func__, "pt_get_param", status);
+		ret = scnprintf(buf, strlen(buf),
+			"%s %d\n",
+			"Status:", status);
+	} else {
+		pt_debug(dev, DL_DEBUG, "%s: Param [%d] = 0x%04X\n",
+			__func__, cd->get_param_id, value);
+		ret = scnprintf(buf, strlen(buf),
+			"Status: %d\n"
+			"0x%04X\n",
+			status, value);
+	}
+	return ret;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_ttdl_restart_show
+ *
+ * SUMMARY: Show method for ttdl_restart sysfs node. This node releases all
+ *	probed modules, calls startup() and then re-probes modules.
+ *
+ * RETURN: size of data written to sysfs node
+ *
+ * PARAMETERS:
+ *	*dev  - pointer to device structure
+ *	*attr - pointer to device attributes structure
+ *	*buf  - pointer to print output buffer
+ ******************************************************************************/
+static ssize_t pt_ttdl_restart_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	int t;
+	int rc = 0;
+
+	mutex_lock(&cd->system_lock);
+	cd->startup_state = STARTUP_NONE;
+	mutex_unlock(&(cd->system_lock));
+	/* ensure no left over exclusive access is still locked */
+	release_exclusive(cd, cd->dev);
+
+	pt_queue_restart(cd);
+
+	t = wait_event_timeout(cd->wait_q,
+		(cd->startup_status >= STARTUP_STATUS_COMPLETE),
+		msecs_to_jiffies(PT_REQUEST_ENUM_TIMEOUT));
+	if (IS_TMO(t)) {
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: TMO waiting for FW sentinel\n", __func__);
+		rc = -ETIME;
+	}
+
+	return scnprintf(buf, strlen(buf),
+		"Status: %d\n"
+		"Enum Status: 0x%04X\n", rc, cd->startup_status);
+}
+static DEVICE_ATTR(ttdl_restart, 0444, pt_ttdl_restart_show, NULL);
+
+/*******************************************************************************
+ * FUNCTION: pt_pip2_gpio_read_show
+ *
+ * SUMMARY: Sends a PIP2 READ_GPIO command to the DUT and prints the
+ *	contents of the response to the passed in output buffer.
+ *
+ * RETURN: size of data written to sysfs node
+ *
+ * PARAMETERS:
+ *	*dev  - pointer to device structure
+ *	*attr - pointer to device attributes structure
+ *	*buf  - pointer to print output buffer
+ ******************************************************************************/
+static ssize_t pt_pip2_gpio_read_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	u8 status = 0;
+	u32 gpio_value = 0;
+	int rc = 0;
+
+	/* This functionality is only available in the BL */
+	if (cd->mode == PT_MODE_BOOTLOADER)
+		rc = pt_pip2_read_gpio(dev, &status, &gpio_value);
+	else
+		rc = -EPERM;
+
+	if (!rc) {
+		if (status == 0)
+			return scnprintf(buf, PT_MAX_PRBUF_SIZE,
+					"Status: %d\n"
+					"DUT GPIO Reg: 0x%08X\n",
+					rc, gpio_value);
+		else
+			return scnprintf(buf, PT_MAX_PRBUF_SIZE,
+					"Status: %d\n"
+					"DUT GPIO Reg: n/a\n",
+					status);
+	} else
+		return scnprintf(buf, PT_MAX_PRBUF_SIZE,
+				"Status: %d\n"
+				"DUT GPIO Reg: n/a\n",
+				rc);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_device_exit
+ *
+ * SUMMARY: Remove functon for the I2C module
+ *
+ * PARAMETERS:
+ *      *client - pointer to i2c client structure
+ ******************************************************************************/
+#ifdef PT_AMBIENT_MODE
+static int pt_device_exit(struct i2c_client *client)
+{
+
+		struct pt_core_data *cd = i2c_get_clientdata(client);
+		struct device *dev = cd->dev;
+
+		void *glink_pt_send_msg;
+		int glink_touch_enter = TOUCH_ENTER;
+
+		pt_debug(dev, DL_INFO,"%s: Start pt_device_exit\n", __func__);
+
+		glink_pt_send_msg = &glink_touch_enter;
+		pt_debug(dev, DL_INFO, "[touch]glink_pt_send_msg = %0x\n", glink_pt_send_msg);
+		glink_touch_tx_msg(glink_pt_send_msg, TOUCH_MSG_SIZE);
+
+		if (active_panel)
+			panel_event_notifier_unregister(cd->entry);
+		pt_core_state = STATE_SUSPEND;
+
+		pm_runtime_suspend(dev);
+		pm_runtime_disable(dev);
+
+		pt_stop_wd_timer(cd);
+		call_atten_cb(cd, PT_ATTEN_CANCEL_LOADER, 0);
+		cancel_work_sync(&cd->ttdl_restart_work);
+		cancel_work_sync(&cd->enum_work);
+		cancel_work_sync(&cd->resume_offload_work);
+		cancel_work_sync(&cd->suspend_offload_work);
+		cancel_work_sync(&cd->resume_work);
+		cancel_work_sync(&cd->suspend_work);
+
+		pt_stop_wd_timer(cd);
+		device_init_wakeup(dev, 0);
+		disable_irq_nosync(cd->irq);
+
+		if (cd->cpdata->setup_irq)
+			cd->cpdata->setup_irq(cd->cpdata, PT_MT_IRQ_FREE, dev);
+
+		if (cd->cpdata->init)
+			cd->cpdata->init(cd->cpdata, PT_MT_POWER_OFF, dev);
+
+		if (cd->cpdata->setup_power)
+			cd->cpdata->setup_power(cd->cpdata, PT_MT_POWER_OFF, dev);
+
+		pt_debug(dev, DL_INFO,"%s: End pt_device_exit \n", __func__);
+		return 0;
+}
+#endif
+
+/*******************************************************************************
+ * FUNCTION: pt_touch_offload_store
+ *
+ * SUMMARY: The store method for the touch_offload sysfs node that allows the TTDL
+ * 			 to be enabled/disabled.
+ *
+ * RETURN: Size of passed in buffer
+ *
+ * PARAMETERS:
+ * 			*dev  - pointer to device structure
+ *			*attr - pointer to device attributes
+ *			*buf  - pointer to buffer that hold the command parameters
+ *			size - size of buf
+ ******************************************************************************/
+static ssize_t pt_touch_offload_store(struct device *dev,
+			struct device_attribute *attr, const char *buf, size_t size)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct i2c_client *client = to_i2c_client(dev);
+	u32 input_data[2];
+	int length;
+	int rc = 0;
+
+	/* Maximum input of one value */
+	length = _pt_ic_parse_input(dev, buf, size, input_data, ARRAY_SIZE(input_data));
+	if (length != 1) {
+			pt_debug(dev, DL_ERROR, "%s: Invalid number of arguments\n", __func__);
+			rc = -EINVAL;
+			goto exit;
+	}
+
+	switch (input_data[0]) {
+	case 0:
+		pt_debug(dev, DL_ERROR, "%s: TTDL: Core Touch Offload OFF\n", __func__);
+		cd->touch_offload = true;
+		rc = pt_device_exit(client);
+		if (rc)
+			pt_debug(dev, DL_ERROR, "%s: Power off error detected rc=%d\n",
+					__func__, rc);
+		else {
+			cd->touch_offload = true;
+			pt_debug(dev, DL_ERROR, "%s: Debugfs PT DEVICE EXIT flag set:\n",
+				__func__);
+		}
+	break;
+
+	case 1:
+		pt_debug(dev, DL_ERROR, "%s: TTDL: Core Touch Offload ON\n", __func__);
+		rc = pt_device_entry(&client->dev, client->irq, PT_DATA_SIZE);
+		if (rc)
+			pt_debug(dev, DL_ERROR, "%s: Power on error detected rc=%d\n",
+				__func__, rc);
+		else {
+			cd->touch_offload = false;
+			pt_debug(dev, DL_ERROR, "%s: Debugfs PT DEVICE ENTRY flag set:\n",
+				__func__);
+		}
+	break;
+
+	default:
+		rc = -EINVAL;
+		pt_debug(dev, DL_ERROR, "%s: Invalid value\n", __func__);
+	}
+
+exit:
+	if (rc)
+		return rc;
+	return size;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_touch_offload_show
+ *
+ * SUMMARY: The show method for the touch_offload sysfs node that allows the TTDL
+ * 			 to verify touch offload enable or disabled.
+ *
+ * RETURN: size of data written to sysfs node
+ *
+ * PARAMETERS:
+ * 			*dev  - pointer to device structure
+ *			*attr - pointer to device attributes
+ *			*buf  - pointer to print output buffer
+ ******************************************************************************/
+static ssize_t pt_touch_offload_show(struct device *dev,
+			struct device_attribute *attr, char *buf)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+
+	return scnprintf(buf, PT_MAX_PRBUF_SIZE,
+			"Touch offload   : %s\n",
+			(cd->touch_offload)? "Enabled" : "Disabled");
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pip2_version_show
+ *
+ * SUMMARY: Sends a PIP2 VERSION command to the DUT and prints the
+ *	contents of the response to the passed in output buffer.
+ *
+ * RETURN: size of data written to sysfs node
+ *
+ * PARAMETERS:
+ *	*dev  - pointer to device structure
+ *	*attr - pointer to device attributes structure
+ *	*buf  - pointer to print output buffer
+ ******************************************************************************/
+static ssize_t pt_pip2_version_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	int rc = 0;
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_ttdata *ttdata = &cd->sysinfo.ttdata;
+
+	rc = pt_pip2_get_version(cd);
+	if (!rc) {
+		return scnprintf(buf, PT_MAX_PRBUF_SIZE,
+			"PIP VERSION   : %02X.%02X\n"
+			"BL VERSION    : %02X.%02X\n"
+			"FW VERSION    : %02X.%02X\n"
+			"SILICON ID    : %04X.%04X\n"
+			"UID           : 0x%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X\n",
+			ttdata->pip_ver_major, ttdata->pip_ver_minor,
+			ttdata->bl_ver_major,  ttdata->bl_ver_minor,
+			ttdata->fw_ver_major,  ttdata->fw_ver_minor,
+			ttdata->chip_id,       ttdata->chip_rev,
+			ttdata->uid[0], ttdata->uid[1],
+			ttdata->uid[2], ttdata->uid[3],
+			ttdata->uid[4], ttdata->uid[5],
+			ttdata->uid[6], ttdata->uid[7],
+			ttdata->uid[8], ttdata->uid[9],
+			ttdata->uid[10], ttdata->uid[11]);
+	} else {
+		pt_debug(dev, DL_ERROR,
+			"%s: Failed to retriev PIP2 VERSION data\n", __func__);
+
+		return scnprintf(buf, PT_MAX_PRBUF_SIZE,
+			"PIP VERSION   : -\n"
+			"BL VERSION    : -\n"
+			"FW VERSION    : -\n"
+			"SILICON ID    : -\n"
+			"UID           : -\n");
+	}
+}
+
+#ifdef TTDL_DIAGNOSTICS
+/*******************************************************************************
+ * FUNCTION: pt_ttdl_status_show
+ *
+ * SUMMARY: Show method for the ttdl_status sysfs node. Displays TTDL internal
+ *	variable states and GPIO levels. Additional information printed when
+ *	TTDL_DIAGNOSTICS is enabled.
+ *
+ *	NOTE: All counters will be reset to 0 when this function is called.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*dev  - pointer to device structure
+ *	*attr - pointer to device attributes
+ *	*buf  - pointer to output buffer
+ ******************************************************************************/
+static ssize_t pt_ttdl_status_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_platform_data *pdata = dev_get_platdata(dev);
+	struct i2c_client *client = to_i2c_client(dev);
+	ssize_t ret;
+	u16 cal_size = 0;
+	unsigned short crc = 0;
+
+	if (cd->cal_cache_in_host)
+		_pt_manage_local_cal_data(dev,
+			PT_CAL_DATA_INFO, &cal_size, &crc);
+	ret = scnprintf(buf, strlen(buf),
+		"%s: 0x%04X\n"
+		"%s: %d\n"
+		"%s: %s\n"
+		"%s: %s %s\n"
+		"%s: %s\n"
+		"%s: 0x%02X\n"
+		"%s: %s\n"
+		"%s: %s\n"
+		"%s: %s\n"
+		"%s: %s\n"
+		"%s: %d\n"
+		"%s: %d\n"
+		"%s: %s\n"
+		"%s: %s\n"
+		"%s: %d\n"
+#ifdef TTDL_DIAGNOSTICS
+		"%s: %d\n"
+		"%s: %d\n"
+		"%s: %d\n"
+		"%s: %d\n"
+		"%s: %d\n"
+		"%s: %d\n"
+		"%s: %d\n"
+		"%s: %d\n"
+		"%s: %d\n"
+		"%s: %d\n"
+		"%s: %s\n"
+		"%s: %s\n"
+		"%s: %s\n"
+		"%s: %d\n"
+		"%s: 0x%04X\n"
+		"%s: %s\n"
+#endif /* TTDL_DIAGNOSTICS */
+		,
+		"Startup Status            ", cd->startup_status,
+		"TTDL Debug Level          ", cd->debug_level,
+		"Mode                      ",
+			cd->mode ? (cd->mode == PT_MODE_OPERATIONAL ?
+			"Operational" : "BL") : "Unknown",
+		"DUT Generation            ",
+			cd->active_dut_generation ?
+			(cd->active_dut_generation == DUT_PIP2_CAPABLE ?
+			"PT TC/TT" : "Gen5/6") : "Unknown",
+			cd->active_dut_generation ?
+			(cd->set_dut_generation == true ?
+			"(Set)" : "(Detected)") : "",
+		"HW Detected               ",
+			cd->hw_detected ? "True" : "False",
+		"I2C Address               ",
+			cd->bus_ops->bustype == BUS_I2C ? client->addr : 0,
+		"Active Bus Module         ",
+			cd->bus_ops->bustype == BUS_I2C ? "I2C" : "SPI",
+		"Flashless Mode            ",
+			cd->flashless_dut == 1 ? "Yes" : "No",
+		"GPIO state - IRQ          ",
+			cd->cpdata->irq_stat ?
+			(cd->cpdata->irq_stat(cd->cpdata, dev) ?
+			"High" : "Low") : "not defined",
+		"GPIO state - TP_XRES      ",
+			pdata->core_pdata->rst_gpio ?
+			(gpio_get_value(pdata->core_pdata->rst_gpio) ?
+			"High" : "Low") : "not defined",
+		"RAM Parm restore list     ", pt_count_parameter_list(cd),
+		"Startup Retry Count       ", cd->startup_retry_count,
+		"WD - Manual Force Stop    ",
+			cd->watchdog_force_stop ? "True" : "False",
+		"WD - Enabled              ",
+			cd->watchdog_enabled ? "True" : "False",
+		"WD - Interval (ms)        ", cd->watchdog_interval
+#ifdef TTDL_DIAGNOSTICS
+		, "WD - Triggered Count      ", cd->watchdog_count,
+		"WD - IRQ Stuck low count  ", cd->watchdog_irq_stuck_count,
+		"WD - Device Access Errors ", cd->watchdog_failed_access_count,
+		"WD - XRES Count           ", cd->wd_xres_count,
+		"IRQ Triggered Count       ", cd->irq_count,
+		"BL Packet Retry Count     ", cd->bl_retry_packet_count,
+		"PIP2 CRC Error Count      ", cd->pip2_crc_error_count,
+		"Bus Transmit Error Count  ", cd->bus_transmit_error_count,
+		"File Erase Timeout Count  ", cd->file_erase_timeout_count,
+		"Error GPIO trigger type   ", cd->err_gpio_type,
+		"Exclusive Access Lock     ", cd->exclusive_dev ? "Set":"Free",
+		"Suppress No-Flash Auto BL ",
+			cd->flashless_auto_bl == PT_SUPPRESS_AUTO_BL ?
+			"Yes" : "No",
+		"Calibration Cache on host ",
+			cd->cal_cache_in_host == PT_FEATURE_ENABLE ?
+			"Yes" : "No",
+		"Calibration Cache size    ", cal_size,
+		"Calibration Cache chip ID ", crc,
+		"Multi-Chip Support        ",
+			cd->multi_chip == PT_FEATURE_ENABLE ? "Yes" : "No"
+#endif /* TTDL_DIAGNOSTICS */
+	);
+
+#ifdef TTDL_DIAGNOSTICS
+	/* Reset all diagnostic counters */
+	cd->watchdog_count               = 0;
+	cd->watchdog_irq_stuck_count     = 0;
+	cd->watchdog_failed_access_count = 0;
+	cd->wd_xres_count                = 0;
+	cd->irq_count                    = 0;
+	cd->bl_retry_packet_count        = 0;
+	cd->pip2_crc_error_count         = 0;
+	cd->bus_transmit_error_count     = 0;
+#endif
+
+	return ret;
+}
+static DEVICE_ATTR(ttdl_status,  0444, pt_ttdl_status_show, NULL);
+
+/*******************************************************************************
+ * FUNCTION: pt_pip2_enter_bl_show
+ *
+ * SUMMARY: Show method for the pip2_enter_bl sysfs node that will force
+ *	the DUT into the BL and show the success or failure of entering the BL
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*dev  - pointer to device structure
+ *	*attr - pointer to device attributes
+ *	*buf  - pointer to output buffer
+ ******************************************************************************/
+static ssize_t pt_pip2_enter_bl_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	ssize_t ret = 0;
+	int rc = 0;
+	int result = 0;
+	u8 mode = PT_MODE_UNKNOWN;
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+
+	/* Turn off the TTDL WD before enter bootloader */
+	pt_stop_wd_timer(cd);
+
+	/* Ensure NO enumeration work is queued or will be queued */
+	cancel_work_sync(&cd->enum_work);
+	mutex_lock(&cd->system_lock);
+	cd->bridge_mode = true;
+	mutex_unlock(&cd->system_lock);
+
+	/* set mode to operational to avoid any extra PIP traffic */
+	rc = _pt_request_pip2_enter_bl(dev, &mode, &result);
+	switch (result) {
+	case PT_ENTER_BL_PASS:
+		ret = scnprintf(buf, PT_MAX_PRBUF_SIZE,
+			"Status: %d\nEntered BL\n", PT_ENTER_BL_PASS);
+		break;
+	case PT_ENTER_BL_ERROR:
+		ret = scnprintf(buf, PT_MAX_PRBUF_SIZE, "Status: %d\n%s\n",
+			rc,
+			"  Unknown Error");
+		break;
+	case PT_ENTER_BL_RESET_FAIL:
+		ret = scnprintf(buf, PT_MAX_PRBUF_SIZE, "Status: %d\n%s\n",
+			rc,
+			"  Soft Reset Failed");
+		break;
+	case PT_ENTER_BL_HID_START_BL_FAIL:
+		ret = scnprintf(buf, PT_MAX_PRBUF_SIZE, "Status: %d\n%s\n",
+			rc,
+			"  PIP Start BL Cmd Failed");
+		break;
+	case PT_ENTER_BL_CONFIRM_FAIL:
+		ret = scnprintf(buf, PT_MAX_PRBUF_SIZE, "Status: %d\n%s\n",
+			rc,
+			"  Error confirming DUT entered BL");
+		break;
+	default:
+		ret = scnprintf(buf, PT_MAX_PRBUF_SIZE, "Status: %d\n%s\n",
+			rc, "  Unknown Error");
+		break;
+	};
+
+	/* Allow enumeration work to be queued again */
+	cd->bridge_mode = false;
+
+	return ret;
+}
+static DEVICE_ATTR(pip2_enter_bl,  0444, pt_pip2_enter_bl_show, NULL);
+
+/*******************************************************************************
+ * FUNCTION: pt_pip2_exit_bl_show
+ *
+ * SUMMARY: Show method for the pip2_exit_bl sysfs node that will attempt to
+ *	launch the APP and put the DUT Application mode
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*dev  - pointer to device structure
+ *	*attr - pointer to device attributes
+ *	*buf  - pointer to output buffer
+ ******************************************************************************/
+static ssize_t pt_pip2_exit_bl_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	ssize_t ret = 0;
+	int rc = 0;
+	u8 status_str[PT_STATUS_STR_LEN];
+
+	rc = pt_pip2_exit_bl_(cd, status_str, PT_STATUS_STR_LEN);
+	/*
+	 * Perform enum if startup_status doesn't reach to
+	 * STARTUP_STATUS_FW_OUT_OF_BOOT.
+	 */
+	if (!rc && (!(cd->startup_status & STARTUP_STATUS_FW_OUT_OF_BOOT))) {
+		rc = pt_enum_with_dut(cd, false, &cd->startup_status);
+		if (!(cd->startup_status & STARTUP_STATUS_FW_OUT_OF_BOOT)) {
+			strlcpy(status_str,
+			       "Already in APP mode - FW stuck in Boot mode", sizeof(status_str));
+		}
+	}
+	ret = scnprintf(buf, PT_MAX_PRBUF_SIZE, "Status: %d\n%s\n",
+		rc, status_str);
+	return ret;
+}
+static DEVICE_ATTR(pip2_exit_bl,  0444, pt_pip2_exit_bl_show, NULL);
+#endif
+
+#ifdef EASYWAKE_TSG6
+/*******************************************************************************
+ * FUNCTION: pt_easy_wakeup_gesture_show
+ *
+ * SUMMARY: Show method for the easy_wakeup_gesture sysfs node that will show
+ *	current easy wakeup gesture
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*dev  - pointer to device structure
+ *	*attr - pointer to device attributes
+ *	*buf  - pointer to output buffer
+ ******************************************************************************/
+static ssize_t pt_easy_wakeup_gesture_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	ssize_t ret;
+
+	mutex_lock(&cd->system_lock);
+	ret = scnprintf(buf, PT_MAX_PRBUF_SIZE, "0x%02X\n",
+			cd->easy_wakeup_gesture);
+	mutex_unlock(&cd->system_lock);
+	return ret;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_easy_wakeup_gesture_store
+ *
+ * SUMMARY: The store method for the easy_wakeup_gesture sysfs node that
+ *	allows the wake gesture to be set to a custom value.
+ *
+ * RETURN: Size of passed in buffer is success
+ *
+ * PARAMETERS:
+ *	*dev  - pointer to device structure
+ *	*attr - pointer to device attributes
+ *	*buf  - pointer to buffer that hold the command parameters
+ *	 size - size of buf
+ ******************************************************************************/
+static ssize_t pt_easy_wakeup_gesture_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	u32 input_data[2];
+	int length;
+	int rc = 0;
+
+	/* Maximum input of one value */
+	length = _pt_ic_parse_input(dev, buf, size, input_data,
+		ARRAY_SIZE(input_data));
+	if (length != 1) {
+		pt_debug(dev, DL_ERROR, "%s: Invalid number of arguments\n",
+			__func__);
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	pt_debug(dev, DL_INFO, "%s: features.easywake = 0x%02X\n",
+		__func__, cd->features.easywake);
+
+	if (!cd->features.easywake || input_data[0] > 0xFF) {
+		rc = -EINVAL;
+		goto exit;
+	}
+
+
+	pm_runtime_get_sync(dev);
+	mutex_lock(&cd->system_lock);
+
+	if (cd->sysinfo.ready && IS_PIP_VER_GE(&cd->sysinfo, 1, 2)) {
+		cd->easy_wakeup_gesture = (u8)input_data[0];
+		pt_debug(dev, DL_INFO,
+			"%s: Updated easy_wakeup_gesture = 0x%02X\n",
+			__func__, cd->easy_wakeup_gesture);
+	} else
+		rc = -ENODEV;
+
+	mutex_unlock(&cd->system_lock);
+	pm_runtime_put(dev);
+
+exit:
+	if (rc)
+		return rc;
+	return size;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_easy_wakeup_gesture_id_show
+ *
+ * SUMMARY: Show method for the easy_wakeup_gesture_id sysfs node that will
+ *	show the TSG6 easywake gesture ID
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*dev  - pointer to device structure
+ *	*attr - pointer to device attributes
+ *	*buf  - pointer to output buffer
+ ******************************************************************************/
+static ssize_t pt_easy_wakeup_gesture_id_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	ssize_t ret;
+
+	mutex_lock(&cd->system_lock);
+	ret = scnprintf(buf, PT_MAX_PRBUF_SIZE, "Status: 0\n0x%02X\n",
+			cd->gesture_id);
+	mutex_unlock(&cd->system_lock);
+	return ret;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_easy_wakeup_gesture_data_show
+ *
+ * SUMMARY: Show method for the easy_wakeup_gesture_data sysfs node that will
+ *	show the TSG6 easywake gesture data in the following format:
+ *	x1(LSB),x1(MSB), y1(LSB),y1(MSB), x2(LSB),x2(MSB), y2(LSB),y2(MSB),...
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*dev  - pointer to device structure
+ *	*attr - pointer to device attributes
+ *	*buf  - pointer to output buffer
+ ******************************************************************************/
+static ssize_t pt_easy_wakeup_gesture_data_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	ssize_t ret = 0;
+	int i;
+
+	mutex_lock(&cd->system_lock);
+	ret += scnprintf(buf + ret, PT_MAX_PRBUF_SIZE - ret, "Status: %d\n", 0);
+	for (i = 0; i < cd->gesture_data_length; i++)
+		ret += scnprintf(buf + ret, PT_MAX_PRBUF_SIZE - ret,
+				"0x%02X\n", cd->gesture_data[i]);
+
+	ret += scnprintf(buf + ret, PT_MAX_PRBUF_SIZE - ret,
+			"(%d bytes)\n", cd->gesture_data_length);
+
+	mutex_unlock(&cd->system_lock);
+	return ret;
+}
+#endif /* EASYWAKE_TSG6 */
+
+#ifdef TTDL_DIAGNOSTICS
+/*******************************************************************************
+ * FUNCTION: pt_err_gpio_show
+ *
+ * SUMMARY: Show method for the err_gpio sysfs node that will show if
+ *	setting up the gpio was successful
+ *
+ * RETURN: Char buffer with printed GPIO creation state
+ *
+ * PARAMETERS:
+ *	*dev  - pointer to device structure
+ *	*attr - pointer to device attributes
+ *	*buf  - pointer to output buffer
+ ******************************************************************************/
+static ssize_t pt_err_gpio_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+
+	return scnprintf(buf, strlen(buf), "Status: 0\n"
+		"Err GPIO (%d)     : %s\n"
+		"Err GPIO trig type: %d\n",
+		cd->err_gpio,
+		(cd->err_gpio ? (gpio_get_value(cd->err_gpio) ?
+		"HIGH" : "low") : "not defined"),
+		cd->err_gpio_type);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_err_gpio_store
+ *
+ * SUMMARY: The store method for the err_gpio sysfs node that allows any
+ *	available host GPIO to be used to trigger when TTDL detects a PIP
+ *	command/response timeout.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*dev  - pointer to device structure
+ *	*attr - pointer to device attributes
+ *	*buf  - pointer to buffer that hold the command parameters
+ *	 size - size of buf
+ ******************************************************************************/
+static ssize_t pt_err_gpio_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	unsigned long gpio;
+	int rc = 0;
+	u32 input_data[3];
+	int length;
+	u8 err_type;
+
+	input_data[0] = 0;
+	input_data[1] = 0;
+	/* Maximmum input is two elements */
+	length = _pt_ic_parse_input(dev, buf, size,
+			input_data, ARRAY_SIZE(input_data));
+	if (length < 1 || length > 2) {
+		pt_debug(dev, DL_ERROR, "%s: Invalid number of arguments\n",
+			__func__);
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	gpio = input_data[0];
+	err_type = (u8)input_data[1];
+	if (err_type < 0 || err_type > PT_ERR_GPIO_MAX_TYPE) {
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	mutex_lock(&cd->system_lock);
+	gpio_free(gpio);
+	rc = gpio_request(gpio, NULL);
+	if (rc) {
+		pt_debug(dev, DL_ERROR, "error requesting gpio %lu\n", gpio);
+		rc = -ENODEV;
+	} else {
+		cd->err_gpio = gpio;
+		cd->err_gpio_type = err_type;
+		gpio_direction_output(gpio, 0);
+	}
+	mutex_unlock(&cd->system_lock);
+
+exit:
+	if (rc)
+		return rc;
+	return size;
+}
+static DEVICE_ATTR(err_gpio, 0644, pt_err_gpio_show,
+	pt_err_gpio_store);
+
+/*******************************************************************************
+ * FUNCTION: pt_drv_irq_show
+ *
+ * SUMMARY: Show method for the drv_irq sysfs node that will show if the
+ *	TTDL interrupt is enabled/disabled
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*dev  - pointer to device structure
+ *	*attr - pointer to device attributes
+ *	*buf  - pointer to output buffer
+ ******************************************************************************/
+static ssize_t pt_drv_irq_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	ssize_t ret = 0;
+
+	mutex_lock(&cd->system_lock);
+	ret += scnprintf(buf, PT_MAX_PRBUF_SIZE, "Status: %d\n", 0);
+	if (cd->irq_enabled)
+		ret += scnprintf(buf + ret, PT_MAX_PRBUF_SIZE - ret,
+			"Driver interrupt: ENABLED\n");
+	else
+		ret += scnprintf(buf + ret, PT_MAX_PRBUF_SIZE - ret,
+			"Driver interrupt: DISABLED\n");
+	mutex_unlock(&cd->system_lock);
+
+	return ret;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_drv_irq_store
+ *
+ * SUMMARY: The store method for the drv_irq sysfs node that allows the TTDL
+ *	IRQ to be enabled/disabled.
+ *
+ * RETURN: Size of passed in buffer
+ *
+ * PARAMETERS:
+ *	*dev  - pointer to device structure
+ *	*attr - pointer to device attributes
+ *	*buf  - pointer to buffer that hold the command parameters
+ *	 size - size of buf
+ ******************************************************************************/
+static ssize_t pt_drv_irq_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	u32 input_data[2];
+	int length;
+	int rc = 0;
+
+	/* Maximum input of one value */
+	length = _pt_ic_parse_input(dev, buf, size, input_data,
+		ARRAY_SIZE(input_data));
+	if (length != 1) {
+		pt_debug(dev, DL_ERROR, "%s: Invalid number of arguments\n",
+			__func__);
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	mutex_lock(&cd->system_lock);
+	switch (input_data[0]) {
+	case 0:
+		if (cd->irq_enabled) {
+			cd->irq_enabled = false;
+			/* Disable IRQ has no return value to check */
+			disable_irq_nosync(cd->irq);
+			pt_debug(dev, DL_INFO,
+				"%s: Driver IRQ now disabled\n",
+				__func__);
+		} else
+			pt_debug(dev, DL_INFO,
+				"%s: Driver IRQ already disabled\n",
+				__func__);
+		break;
+
+	case 1:
+		if (cd->irq_enabled == false) {
+			cd->irq_enabled = true;
+			enable_irq(cd->irq);
+			pt_debug(dev, DL_INFO,
+				"%s: Driver IRQ now enabled\n",
+				__func__);
+		} else
+			pt_debug(dev, DL_INFO,
+				"%s: Driver IRQ already enabled\n",
+				__func__);
+		break;
+
+	default:
+		rc = -EINVAL;
+		pt_debug(dev, DL_ERROR, "%s: Invalid value\n", __func__);
+	}
+	mutex_unlock(&(cd->system_lock));
+
+exit:
+	if (rc)
+		return rc;
+	return size;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pip2_bin_hdr_show
+ *
+ * SUMMARY: Show method for the pip2_bin_hdr sysfs node that will read
+ *	the bin file header from flash and show each field
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*dev  - pointer to device structure
+ *	*attr - pointer to device attributes
+ *	*buf  - pointer to output buffer
+ ******************************************************************************/
+static ssize_t pt_pip2_bin_hdr_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	ssize_t ret = 0;
+	struct pt_bin_file_hdr hdr = {0};
+	int rc;
+
+	rc = _pt_request_pip2_bin_hdr(dev, &hdr);
+
+	ret = scnprintf(buf, strlen(buf),
+		"%s: %d\n"
+		"%s: %d\n"
+		"%s: 0x%04X\n"
+		"%s: %d\n"
+		"%s: %d\n"
+		"%s: %d\n"
+		"%s: 0x%08X\n"
+		"%s: 0x%04X\n"
+		"%s: 0x%04X\n"
+		"%s: %d\n"
+		"%s: %d\n",
+		"Status", rc,
+		"Header Length  ", hdr.length,
+		"TTPID          ", hdr.ttpid,
+		"FW Major Ver   ", hdr.fw_major,
+		"FW Minor Ver   ", hdr.fw_minor,
+		"FW Rev Control ", hdr.fw_rev_ctrl,
+		"FW CRC         ", hdr.fw_crc,
+		"Silicon Rev    ", hdr.si_rev,
+		"Silicon ID     ", hdr.si_id,
+		"Config Ver     ", hdr.config_ver,
+		"HEX File Size  ", hdr.hex_file_size
+	);
+	return ret;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_platform_data_show
+ *
+ * SUMMARY: Show method for the platform_data sysfs node that will show the
+ *	active platform data including: GPIOs, Vendor and Product IDs,
+ *	Virtual Key coordinates, Core/MT/Loader flags, Level trigger delay,
+ *	HID registers, and Easy wake gesture
+ *
+ * RETURN: Size of printed data
+ *
+ * PARAMETERS:
+ *	*dev  - pointer to device structure
+ *	*attr - pointer to device attributes
+ *	*buf  - pointer to output buffer
+ ******************************************************************************/
+static ssize_t pt_platform_data_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct pt_platform_data *pdata = dev_get_platdata(dev);
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	ssize_t ret;
+
+	ret = scnprintf(buf, strlen(buf),
+		"%s: %d\n"
+		"%s: %d\n"
+		"%s: %d\n"
+		"%s: %d\n"
+		"%s: %d\n"
+		"%s: %d\n"
+		"%s: %d\n"
+		"%s: %d\n"
+		"%s: %d\n"
+		"%s: %d\n"
+		"%s: %d\n"
+		"%s: %d\n"
+		"%s: %d\n"
+		"%s: %d\n"
+		"%s: %d\n"
+		"%s: %d\n"
+		"%s: %d\n"
+		"%s: %d\n"
+		"%s: %d\n"
+		"%s: %d\n"
+		"%s: %d\n"
+		"%s: %d\n"
+		"%s: %d\n"
+		"%s: %d\n"
+		"%s: %d\n"
+		"%s: %d\n"
+		"%s: %d\n"
+		"%s: %s\n"
+		"%s: %s\n"
+		"%s: %d\n",
+		"Status", 0,
+		"Interrupt GPIO           ", pdata->core_pdata->irq_gpio,
+		"Interrupt GPIO Value     ",
+		pdata->core_pdata->irq_gpio ?
+			gpio_get_value(pdata->core_pdata->irq_gpio) : 0,
+		"Reset GPIO               ", pdata->core_pdata->rst_gpio,
+		"Reset GPIO Value         ",
+		pdata->core_pdata->rst_gpio ?
+			gpio_get_value(pdata->core_pdata->rst_gpio) : 0,
+		"DDI Reset GPIO           ", pdata->core_pdata->ddi_rst_gpio,
+		"DDI Reset GPIO Value     ",
+		pdata->core_pdata->ddi_rst_gpio ?
+			gpio_get_value(pdata->core_pdata->ddi_rst_gpio) : 0,
+		"VDDI GPIO                ", pdata->core_pdata->vddi_gpio,
+		"VDDI GPIO Value          ",
+		pdata->core_pdata->vddi_gpio ?
+			gpio_get_value(pdata->core_pdata->vddi_gpio) : 0,
+		"VCC GPIO                 ", pdata->core_pdata->vcc_gpio,
+		"VCC GPIO Value           ",
+		pdata->core_pdata->vcc_gpio ?
+			gpio_get_value(pdata->core_pdata->vcc_gpio) : 0,
+		"AVDD GPIO                ", pdata->core_pdata->avdd_gpio,
+		"AVDD GPIO Value          ",
+		pdata->core_pdata->avdd_gpio ?
+			gpio_get_value(pdata->core_pdata->avdd_gpio) : 0,
+		"AVEE GPIO                ", pdata->core_pdata->avee_gpio,
+		"AVEE GPIO Value          ",
+		pdata->core_pdata->avee_gpio ?
+			gpio_get_value(pdata->core_pdata->avee_gpio) : 0,
+		"Vendor ID                ", pdata->core_pdata->vendor_id,
+		"Product ID               ", pdata->core_pdata->product_id,
+		"Vkeys x                  ", pdata->mt_pdata->vkeys_x,
+		"Vkeys y                  ", pdata->mt_pdata->vkeys_y,
+		"Core data flags          ", pdata->core_pdata->flags,
+		"MT data flags            ", pdata->mt_pdata->flags,
+		"Loader data flags        ", pdata->loader_pdata->flags,
+		"Level trigger delay (us) ",
+			pdata->core_pdata->level_irq_udelay,
+		"HID Descriptor Register  ",
+			pdata->core_pdata->hid_desc_register,
+		"HID Output Register      ",
+			cd->hid_desc.output_register,
+		"HID Command Register     ",
+			cd->hid_desc.command_register,
+		"Easy wakeup gesture      ",
+			pdata->core_pdata->easy_wakeup_gesture,
+		"Config DUT generation    ",
+			pdata->core_pdata->config_dut_generation ?
+			(pdata->core_pdata->config_dut_generation ==
+				CONFIG_DUT_PIP2_CAPABLE ?
+			"PT TC/TT" : "Gen5/6") : "Auto",
+		"Watchdog Force Stop      ",
+			pdata->core_pdata->watchdog_force_stop ?
+			"True" : "False",
+		"Panel ID Support         ",
+			pdata->core_pdata->panel_id_support);
+	return ret;
+}
+
+#define PT_ERR_STR_SIZE 64
+/*******************************************************************************
+ * FUNCTION: pt_bist_bus_test
+ *
+ * SUMMARY: Tests the connectivity of the active bus pins:
+ *	I2C - SDA and SCL
+ *	SPI - MOSI, MISO, CLK
+ *
+ *	Disable TTDL interrupts, send a PIP cmd and then manually read the
+ *	bus. If any data is read we know the I2C/SPI pins are connected
+ *
+ * RETURN:
+ *	 0 = Success
+ *	!0 = Failure
+ *
+ * PARAMETERS:
+ *	*dev         - pointer to device structure
+ *	*net_toggled - pointer to where to store if bus toggled
+ *	*err_str     - pointer to error string buffer
+ ******************************************************************************/
+static int pt_bist_bus_test(struct device *dev, u8 *net_toggled, u8 *err_str)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	u8 ver_cmd[8] = {0x01, 0x01, 0x06, 0x00, 0x0E, 0x07, 0xF0, 0xB1};
+	u8 *read_buf = NULL;
+	int bytes_read = 0;
+	int rc = 0;
+
+	read_buf = kzalloc(PT_MAX_PIP1_MSG_SIZE, GFP_KERNEL);
+	if (read_buf == NULL) {
+		rc = -ENOMEM;
+		goto exit;
+	}
+
+	bytes_read = pt_flush_bus(cd, PT_FLUSH_BUS_BASED_ON_LEN, NULL);
+
+	pt_debug(dev, DL_INFO, "%s: TTDL Core Suspend\n", __func__);
+	disable_irq(cd->irq);
+	mutex_lock(&cd->system_lock);
+	cd->irq_disabled = true;
+	mutex_unlock(&cd->system_lock);
+
+	/*
+	 * Sleep >4ms to allow any pending TTDL IRQ to finish. Without this
+	 * the disable_irq_nosync() could cause the IRQ to get stuck asserted
+	 */
+	usleep_range(5000, 6000);
+
+	pt_debug(cd->dev, DL_INFO, ">>> %s: Write Buffer Size[%d] VERSION\n",
+		__func__, (int)sizeof(ver_cmd));
+
+	pt_pr_buf(cd->dev, DL_DEBUG, ver_cmd, (int)sizeof(ver_cmd),
+		">>> User CMD");
+	rc = pt_adap_write_read_specific(cd, sizeof(ver_cmd), ver_cmd, NULL);
+	if (rc) {
+		pt_debug(dev, DL_ERROR,
+			"%s: BUS Test - Failed to send VER cmd\n", __func__);
+		*net_toggled = 0;
+		strlcpy(err_str,
+			"- Write failed, bus open or shorted or DUT in reset", PT_ERR_STR_SIZE);
+		goto exit_enable_irq;
+	}
+	usleep_range(4000, 5000);
+	bytes_read = pt_flush_bus(cd, PT_FLUSH_BUS_BASED_ON_LEN, read_buf);
+	pt_debug(dev, DL_INFO, "%s: BUS Test - %d bytes forced read\n",
+		__func__, bytes_read);
+	if (bytes_read == 0) {
+		*net_toggled = 0;
+		pt_debug(dev, DL_INFO, "%s: BUS Read Failed, 0 bytes read\n",
+			__func__);
+		strlcpy(err_str,
+			"- Bus open, shorted or DUT in reset", PT_ERR_STR_SIZE);
+		rc = -EIO;
+		goto exit_enable_irq;
+	} else {
+		if (cd->bus_ops->bustype == BUS_I2C)
+			*net_toggled = 1;
+		else {
+			if ((bytes_read > 3) &&
+			    (read_buf[3] & PIP2_CMD_COMMAND_ID_MASK) ==
+			     PIP2_CMD_ID_VERSION)
+				*net_toggled = 1;
+			else {
+				*net_toggled = 0;
+				pt_debug(dev, DL_INFO,
+					"%s: BUS Read Failed, %d bytes read\n",
+					__func__, bytes_read);
+				strlcpy(err_str,
+					"- Bus open, shorted or DUT in reset", PT_ERR_STR_SIZE);
+			}
+		}
+	}
+
+exit_enable_irq:
+	enable_irq(cd->irq);
+	usleep_range(5000, 6000);
+	mutex_lock(&cd->system_lock);
+	cd->irq_disabled = false;
+	mutex_unlock(&cd->system_lock);
+	pt_debug(dev, DL_INFO, "%s: TTDL Core Resumed\n", __func__);
+
+exit:
+	kfree(read_buf);
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_bist_irq_test
+ *
+ * SUMMARY: Tests the connectivity of the IRQ net
+ *
+ *	This test will ensure there is a good connection between the host
+ *	and the DUT on the irq pin. First determine if the IRQ is stuck
+ *	asserted and if so keep reading messages off of the bus until
+ *	it de-asserts. Possible outcomes:
+ *	 - IRQ was already de-asserted: Send a PIP command and if an
+ *	interrupt is generated the test passes.
+ *	 - IRQ was asserted: Reading off the bus de-assertes the IRQ,
+ *	test passes.
+ *	 - IRQ stays asserted: After reading the bus multiple times
+ *	the IRQ stays asserted. Likely open or shorted to GND
+ *
+ * RETURN:
+ *	 0 = Success
+ *	!0 = Failure
+ *
+ * PARAMETERS:
+ *	*dev          - pointer to device structure
+ *	*bus_toggled  - pointer to where to store if bus toggled
+ *	*irq_toggled  - pointer to where to store if IRQ toggled
+ *	*xres_toggled - pointer to where to store if XRES toggled
+ *	*err_str      - pointer to error string buffer
+ ******************************************************************************/
+static int pt_bist_irq_test(struct device *dev,
+	u8 *bus_toggled, u8 *irq_toggled, u8 *xres_toggled, u8 *err_str)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	u8 *read_buf = NULL;
+	u8 mode = PT_MODE_UNKNOWN;
+	u16 actual_read_len;
+	int bytes_read  = 0;
+	int count       = 0;
+	int rc          = 0;
+
+	read_buf = kzalloc(PT_MAX_PIP1_MSG_SIZE, GFP_KERNEL);
+	if (read_buf == NULL) {
+		rc = -ENOMEM;
+		goto exit;
+	}
+
+	/* Clear IRQ triggered count, and re-evaluate at the end of test */
+	cd->irq_count = 0;
+
+	/*
+	 * Check if IRQ is stuck asserted, if so try a non forced flush of
+	 * the bus based on the 2 byte initial length read. Try up to 5x.
+	 */
+	while (pt_check_irq_asserted(cd) && count < 5) {
+		count++;
+		bytes_read += pt_flush_bus(cd, PT_FLUSH_BUS_BASED_ON_LEN, NULL);
+	}
+
+	if (count > 1 && count < 5 && bytes_read > 0) {
+		/*
+		 * IRQ was stuck but data was successfully read from the
+		 * bus releasing the IRQ line.
+		 */
+		pt_debug(dev, DL_INFO, "%s: count=%d bytes_read=%d\n",
+			__func__, count, bytes_read);
+		*bus_toggled = 1;
+		*irq_toggled = 1;
+		goto exit;
+	}
+
+	if (count == 5 && bytes_read == 0) {
+		/*
+		 * Looped 5x and read nothing off the bus yet the IRQ is still
+		 * asserted. Possible conditions:
+		 * - IRQ open circuit
+		 * - IRQ shorted to GND
+		 * - I2C/SPI bus is disconnected
+		 * - FW holding the pin low
+		 * Try entering the BL to see if communication works there.
+		 */
+		mode = PT_MODE_IGNORE;
+		rc = _pt_request_pip2_enter_bl(dev, &mode, NULL);
+		if (rc) {
+			pt_debug(dev, DL_ERROR,
+				"%s Failed to enter BL\n", __func__);
+			strlcpy(err_str,
+				"- likely shorted to GND or FW holding it.", PT_ERR_STR_SIZE);
+			*irq_toggled = 0;
+			goto exit;
+		}
+		/*
+		 * If original mode was operational and we successfully
+		 * entered the BL, then the XRES net must have toggled
+		 */
+		if (mode == PT_MODE_OPERATIONAL)
+			*xres_toggled = 1;
+
+		rc = _pt_request_pip2_send_cmd(dev, PT_CORE_CMD_PROTECTED,
+			PIP2_CMD_ID_VERSION, NULL, 0, read_buf,
+			&actual_read_len);
+		if (rc) {
+			/*
+			 * Could not communicate to DUT in BL mode. Save the
+			 * error string, slim chance but the XRES test below may
+			 * show the IRQ is actually working.
+			 */
+			strlcpy(err_str, "- likely shorted to GND.", PT_ERR_STR_SIZE);
+			pt_debug(dev, DL_ERROR,
+				"%s: %s, count=%d bytes_read=%d\n",
+				__func__, err_str, count, bytes_read);
+			*irq_toggled = 0;
+			rc = pt_pip2_exit_bl_(cd, NULL, 0);
+			goto exit;
+		} else {
+			*bus_toggled = 1;
+			*irq_toggled = 1;
+			goto exit;
+		}
+	}
+	if (pt_check_irq_asserted(cd)) {
+		strlcpy(err_str, "- likely shorted to GND", PT_ERR_STR_SIZE);
+		rc = -EIO;
+		*irq_toggled = 0;
+		goto exit;
+	}
+
+	/* Try sending a PIP command to see if we get a response */
+	rc = _pt_request_pip2_send_cmd(dev, PT_CORE_CMD_PROTECTED,
+		PIP2_CMD_ID_VERSION, NULL, 0, read_buf, &actual_read_len);
+	if (rc) {
+		/*
+		 * Potential IRQ issue, no communication in App mode, attempt
+		 * the same command in the BL
+		 */
+		mode = PT_MODE_IGNORE;
+		rc = _pt_request_pip2_enter_bl(dev, &mode, NULL);
+		if (rc) {
+			pt_debug(dev, DL_ERROR,
+				"%s Failed to enter BL\n", __func__);
+			*irq_toggled = 0;
+			strlcpy(err_str, "- likely open or shorted to VDDI.", PT_ERR_STR_SIZE);
+			goto exit;
+		}
+		/*
+		 * If original mode was operational and we successfully
+		 * entered the BL, this will be useful info for the tp_xres
+		 * test below.
+		 */
+		if (mode == PT_MODE_OPERATIONAL)
+			*xres_toggled = 1;
+
+		rc = _pt_request_pip2_send_cmd(dev, PT_CORE_CMD_PROTECTED,
+			PIP2_CMD_ID_VERSION, NULL, 0, read_buf,
+			&actual_read_len);
+		if (rc) {
+			/*
+			 * Could not communicate in FW mode or BL mode. Save the
+			 * error string, slim chance but the XRES test below may
+			 * show the IRQ is actually working.
+			 */
+			strlcpy(err_str, "- likely open or shorted to VDDI.", PT_ERR_STR_SIZE);
+			pt_debug(dev, DL_ERROR,
+				"%s: request_active_pip_prot failed\n",
+				__func__);
+			*irq_toggled = 0;
+			goto exit;
+		}
+	}
+
+	if (cd->irq_count > 0) {
+		pt_debug(dev, DL_INFO, "%s: irq_count=%d\n", __func__,
+			cd->irq_count);
+		*bus_toggled = 1;
+		*irq_toggled = 1;
+		goto exit;
+	}
+
+exit:
+	kfree(read_buf);
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_bist_xres_test
+ *
+ * SUMMARY: Tests the connectivity of the TP_XRES net
+ *
+ *	This test will ensure there is a good connection between the host
+ *	and the DUT on the tp_xres pin. The pin will be toggled to
+ *	generate a TP reset which will cause the DUT to output a reset
+ *	sentinel. If the reset sentinel is seen the test passes. If it is
+ *	not seen the test will attempt to send a soft reset to simply gain
+ *	some additional information on the failure:
+ *	 - soft reset fails to send: XRES and IRQ likely open
+ *	 - soft reset passes: XRES likely open or stuck de-asserted
+ *	 - soft reset fails: XRES likely stuck asserted
+ *
+ * RETURN:
+ *	 0 = Success
+ *	!0 = Failure
+ *
+ * PARAMETERS:
+ *	*dev          - pointer to device structure
+ *	*bus_toggled  - pointer to where to store if bus toggled
+ *	*irq_toggled  - pointer to where to store if IRQ toggled
+ *	*xres_toggled - pointer to where to store if XRES toggled
+ *	*err_str      - pointer to error string buffer
+ ******************************************************************************/
+static int pt_bist_xres_test(struct device *dev,
+	u8 *bus_toggled, u8 *irq_toggled, u8 *xres_toggled, u8 *err_str)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_platform_data *pdata = dev_get_platdata(dev);
+	u8 *read_buf = NULL;
+	u8 mode = PT_MODE_UNKNOWN;
+	int rc         = 0;
+	int t          = 0;
+	int timeout    = 300;
+
+	read_buf = kzalloc(PT_MAX_PIP1_MSG_SIZE, GFP_KERNEL);
+	if (read_buf == NULL) {
+		rc = -ENOMEM;
+		goto exit;
+	}
+
+	/* Clear the startup bit mask, reset and enum will re-populate it */
+	cd->startup_status = STARTUP_STATUS_START;
+	pt_debug(dev, DL_DEBUG, "%s: Startup Status Reset\n", __func__);
+
+	if ((!pdata->core_pdata->rst_gpio) || (!pdata->core_pdata->xres)) {
+		strlcpy(err_str, "- Net not configured or available", PT_ERR_STR_SIZE);
+		rc = -ENODEV;
+		goto exit;
+	}
+
+
+	/* Ensure we have nothing pending on active bus */
+	pt_flush_bus_if_irq_asserted(cd, PT_FLUSH_BUS_BASED_ON_LEN);
+
+	/* Perform a hard XRES toggle and wait for reset sentinel */
+	mutex_lock(&cd->system_lock);
+	cd->hid_reset_cmd_state = 1;
+	mutex_unlock(&cd->system_lock);
+	pt_debug(dev, DL_INFO, "%s: Perform a hard reset\n", __func__);
+	rc = pt_hw_hard_reset(cd);
+
+	/* Set timeout to 1s for the flashless case where a BL could be done */
+	if (cd->flashless_dut)
+		timeout = 1000;
+
+	/*
+	 * To avoid the case that next PIP command can be confused by BL/FW
+	 * sentinel's "wakeup" event, chekcing hid_reset_cmd_state  which is
+	 * followed by "wakeup event" function can lower the failure rate.
+	 */
+	t = wait_event_timeout(cd->wait_q,
+		((cd->startup_status != STARTUP_STATUS_START)
+		 && (cd->hid_reset_cmd_state == 0)),
+		msecs_to_jiffies(timeout));
+	if (IS_TMO(t)) {
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: TMO waiting for sentinel\n", __func__);
+		*xres_toggled = 0;
+		strlcpy(err_str, "- likely open. (No Reset Sentinel)", PT_ERR_STR_SIZE);
+
+		/*
+		 * Possibly bad FW, Try entering BL and wait for reset sentinel.
+		 * To enter the BL we need to generate an XRES so first try to
+		 * launch the applicaiton
+		 */
+		if (cd->mode == PT_MODE_BOOTLOADER)
+			pt_pip2_launch_app(dev, PT_CORE_CMD_PROTECTED);
+		pt_debug(dev, DL_INFO, "%s: Enter BL with a hard reset\n",
+			__func__);
+		mode = PT_MODE_IGNORE;
+		rc = _pt_request_pip2_enter_bl(dev, &mode, NULL);
+		if (rc) {
+			pt_debug(dev, DL_ERROR, "%s Failed to enter BL\n",
+				__func__);
+			*xres_toggled = 0;
+			strlcpy(err_str, "- likely open or shorted to VDDI.", PT_ERR_STR_SIZE);
+			goto exit;
+		} else {
+			/* Wait for the BL sentinel */
+			t = wait_event_timeout(cd->wait_q,
+				(cd->startup_status != STARTUP_STATUS_START),
+				msecs_to_jiffies(500));
+			if (IS_TMO(t)) {
+				pt_debug(cd->dev, DL_ERROR,
+					"%s: TMO waiting for BL sentinel\n",
+					__func__);
+				*xres_toggled = 0;
+				strlcpy(err_str,
+					"- likely open or shorted to VDDI.", PT_ERR_STR_SIZE);
+				rc = -ETIME;
+				goto exit;
+			}
+		}
+	}
+	mutex_lock(&cd->system_lock);
+	cd->hid_reset_cmd_state = 0;
+	mutex_unlock(&cd->system_lock);
+
+	/* Look for BL or FW reset sentinels */
+	if (!rc && ((cd->startup_status & STARTUP_STATUS_BL_RESET_SENTINEL) ||
+	    (cd->startup_status & STARTUP_STATUS_FW_RESET_SENTINEL))) {
+		pt_debug(dev, DL_INFO, "%s: hard XRES pass\n", __func__);
+		/* If a sentinel was seen, all nets are working */
+		*xres_toggled = 1;
+		*irq_toggled  = 1;
+		/*
+		 * For SPI test, bus read result can be confused as FW sentinel
+		 * if MISO(slave) is connected to MISO(host).
+		 */
+		if (cd->bus_ops->bustype == BUS_I2C)
+			*bus_toggled = 1;
+	} else {
+		/*
+		 * Hard reset failed, however some additional information
+		 * could be determined. Try a soft reset to see if DUT resets
+		 * with the possible outcomes:
+		 * - if it resets the line is not stuck asserted
+		 * - if it does not reset the line could be stuck asserted
+		 */
+		*xres_toggled = 0;
+		rc = pt_hw_soft_reset(cd, PT_CORE_CMD_PROTECTED);
+		msleep(30);
+		pt_debug(dev, DL_INFO, "%s: TP_XRES BIST soft reset rc=%d",
+			__func__, rc);
+		if (rc) {
+			strlcpy(err_str, "- likely open.", PT_ERR_STR_SIZE);
+			pt_debug(dev, DL_ERROR,
+				"%s: Hard reset failed, soft reset failed %s\n",
+				__func__, err_str);
+			goto exit;
+		}
+		if (cd->startup_status & STARTUP_STATUS_BL_RESET_SENTINEL ||
+		    cd->startup_status & STARTUP_STATUS_FW_RESET_SENTINEL) {
+			strlcpy(err_str,
+				"- likely open or stuck high, soft reset OK", PT_ERR_STR_SIZE);
+			pt_debug(dev, DL_ERROR,
+				"%s: Hard reset failed, soft reset passed-%s\n",
+				__func__, err_str);
+		} else if (cd->startup_status == 0) {
+			strlcpy(err_str, "- likely stuck high.", PT_ERR_STR_SIZE);
+			pt_debug(dev, DL_ERROR,
+				"%s: Hard reset failed, soft reset failed-%s\n",
+				__func__, err_str);
+		} else {
+			strlcpy(err_str, "- open or stuck.", PT_ERR_STR_SIZE);
+			pt_debug(dev, DL_ERROR,
+				"%s: Hard and Soft reset failed - %s\n",
+				__func__, err_str);
+		}
+	}
+
+exit:
+	kfree(read_buf);
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_bist_slave_irq_test
+ *
+ * SUMMARY: Tests the connectivity of the Master/Slave IRQ net
+ *
+ *	This test will ensure there is a good IRQ connection between the master
+ *	DUT and the slave DUT(s). After power up the STATUS command is sent
+ *	and the 'Slave Detect' bits are verified to ensure the master DUT
+ *	saw each slave trigger the IRQ with it's reset sentinel.
+ *
+ * RETURN:
+ *	 0 = Success
+ *	!0 = Failure
+ *
+ * PARAMETERS:
+ *	*dev                - pointer to device structure
+ *	*slave_irq_toggled  - pointer to where to store if slave IRQ toggled
+ *	*slave_bus_toggled  - pointer to where to store if slave Bus toggled
+ *	*err_str            - pointer to error string buffer
+ *	*slave_detect       - pointer to slave detect buffer
+ *	*boot_err           - pointer to boot_err  buffer
+ ******************************************************************************/
+static int pt_bist_slave_irq_test(struct device *dev,
+	u8 *slave_irq_toggled, u8 *slave_bus_toggled, u8 *err_str,
+	u8 *slave_detect, u8 *boot_err)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	u8 mode = PT_MODE_UNKNOWN;
+	u8 status;
+	u8 boot;
+	u8 read_buf[12];
+	u8 detected = 0;
+	u8 last_err = -1;
+	u16 actual_read_len;
+	int result = 0;
+	int rc = 0;
+
+	/*
+	 * Ensure DUT is in the BL where the STATUS cmd will report the slave
+	 * detect bits. If the DUT was in FW, entering the BL will cause an
+	 * XRES signal which will inadvertently test the XRES net as well
+	 */
+	rc = _pt_request_pip2_enter_bl(dev, &mode, &result);
+	if (rc) {
+		pt_debug(cd->dev, DL_ERROR, "%s: Error entering BL rc=%d\n",
+			__func__, rc);
+		strlcpy(err_str, "- State could not be determined.", PT_ERR_STR_SIZE);
+		goto exit;
+	}
+
+	/* Use the STATUS command to retrieve the slave detect bit(s) */
+	rc = _pt_request_pip2_send_cmd(cd->dev, PT_CORE_CMD_UNPROTECTED,
+		PIP2_CMD_ID_STATUS, NULL, 0, read_buf,
+		&actual_read_len);
+	if (!rc) {
+		pt_pr_buf(cd->dev, DL_INFO, read_buf, actual_read_len,
+			"PIP2 STATUS");
+		status = read_buf[PIP2_RESP_STATUS_OFFSET];
+		boot = read_buf[PIP2_RESP_BODY_OFFSET] & 0x01;
+
+		/* Slave detect is only valid if status ok and in boot exec */
+		if (status == PIP2_RSP_ERR_NONE &&
+		    boot == PIP2_STATUS_BOOT_EXEC) {
+			detected = read_buf[PIP2_RESP_BODY_OFFSET + 2] &
+					SLAVE_DETECT_MASK;
+		} else {
+			strlcpy(err_str, "- State could not be determined", PT_ERR_STR_SIZE);
+			rc = -EPERM;
+		}
+	} else {
+		pt_debug(cd->dev, DL_ERROR, "%s: STATUS cmd failure\n",
+			__func__);
+		strlcpy(err_str, "- State could not be determined.", PT_ERR_STR_SIZE);
+		goto exit;
+	}
+
+	/*
+	 * Retrieve boot error regardless of the state of the slave detect
+	 * bit because the IRQ could have been stuck high or low.
+	 */
+	rc = _pt_request_pip2_send_cmd(cd->dev, PT_CORE_CMD_UNPROTECTED,
+		PIP2_CMD_ID_GET_LAST_ERRNO, NULL, 0,
+		read_buf, &actual_read_len);
+	if (!rc) {
+		pt_pr_buf(cd->dev, DL_INFO, read_buf, actual_read_len,
+			"PIP2 GET_LAST_ERRNO");
+		status = read_buf[PIP2_RESP_STATUS_OFFSET];
+		last_err = read_buf[PIP2_RESP_BODY_OFFSET];
+		if (last_err) {
+			pt_debug(cd->dev, DL_ERROR,
+				"%s: Master Boot Last Err = 0x%02X\n",
+				__func__, last_err);
+		}
+	} else {
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: GET_LAST_ERRNO cmd failure\n", __func__);
+		strlcpy(err_str, "- stuck, likely shorted to GND.", PT_ERR_STR_SIZE);
+	}
+
+exit:
+	pt_debug(cd->dev, DL_INFO,
+		"%s: rc=%d detected=0x%02X boot_err=0x%02X\n",
+		__func__, rc, detected, last_err);
+	if (err_str && last_err) {
+		if (detected)
+			scnprintf(err_str, PT_ERR_STR_SIZE, "%s 0x%02X",
+				"- Likely stuck low. Boot Error:",
+				last_err);
+		else
+			scnprintf(err_str, PT_ERR_STR_SIZE, "%s 0x%02X",
+				"- Likely stuck high. Boot Error:",
+				last_err);
+	}
+
+	/* Ignore an invalid image error as BIST doesn't need valid FW */
+	if (last_err == PIP2_RSP_ERR_INVALID_IMAGE)
+		last_err = PIP2_RSP_ERR_NONE;
+
+	if (slave_irq_toggled)
+		*slave_irq_toggled = (detected && !last_err) ? true : false;
+	if (slave_bus_toggled) {
+		/* Leave as UNTEST if slave not detected */
+		if (detected)
+			*slave_bus_toggled = !last_err ? true : false;
+	}
+	if (slave_detect)
+		*slave_detect = detected;
+	if (boot_err)
+		*boot_err = last_err;
+
+	if (slave_irq_toggled && slave_bus_toggled)
+		pt_debug(cd->dev, DL_INFO, "%s: %s=0x%02X, %s=0x%02X, %s=0x%02X\n",
+				__func__,
+				"Detected", detected,
+				"slave_irq_toggled", *slave_irq_toggled,
+				"slave_bus_toggled", *slave_bus_toggled);
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_bist_slave_xres_test
+ *
+ * SUMMARY: Tests the connectivity of the Master/Slave TP_XRES net
+ *
+ *	This test will ensure there is a good TP_XRES connection between the
+ *	master DUT and the slave DUT(s). After toggling the XRES pin to the
+ *	master, the STATUS command is sent and the 'Slave Detect' bits are
+ *	verified to ensure the master DUT saw each slave trigger the IRQ with
+ *	it's reset sentinel.
+ *
+ * RETURN:
+ *	 0 = Success
+ *	!0 = Failure
+ *
+ * PARAMETERS:
+ *	*dev                - pointer to device structure
+ *	*slave_irq_toggled  - pointer to where to store if slave IRQ toggled
+ *	*slave_bus_toggled  - pointer to where to store if slave Bus toggled
+ *	*slave_xres_toggled - pointer to where to store if slave XRES toggled
+ *	*err_str            - pointer to error string buffer
+ ******************************************************************************/
+static int pt_bist_slave_xres_test(struct device *dev,
+	u8 *slave_irq_toggled, u8 *slave_bus_toggled, u8 *slave_xres_toggled,
+	u8 *err_str)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	u8 slave_detect = 0;
+	u8 boot_err = 0;
+	int rc = 0;
+
+	/* Force a reset to force the 'slave detect' bits to be re-acquired */
+	mutex_lock(&cd->system_lock);
+	cd->hid_reset_cmd_state = 1;
+	mutex_unlock(&cd->system_lock);
+	pt_debug(dev, DL_INFO, "%s: Perform a hard reset\n", __func__);
+	pt_hw_hard_reset(cd);
+	msleep(100);
+
+	rc = pt_bist_slave_irq_test(dev, slave_irq_toggled,
+		slave_bus_toggled, err_str, &slave_detect, &boot_err);
+	pt_debug(dev, DL_INFO, "%s: IRQ test rc = %d\n", __func__, rc);
+
+	if (!rc && *slave_irq_toggled == false) {
+		/*
+		 * If the slave IRQ did not toggle, either the slave_detect
+		 * bit was not set or we had a boot error. If the slave
+		 * detect was not set the slave did not reset causing a boot
+		 * error.
+		 */
+		if (!slave_detect)
+			strlcpy(err_str, "- likely open.", PT_ERR_STR_SIZE);
+		else
+			scnprintf(err_str, PT_ERR_STR_SIZE, "%s 0x%02X",
+				"- likely open or an IRQ issue. Boot Error:",
+				boot_err);
+	}
+	if (slave_xres_toggled) {
+		if (!rc)
+			*slave_xres_toggled = *slave_irq_toggled ? true : false;
+		else
+			*slave_xres_toggled = false;
+
+	}
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_bist_slave_bus_test
+ *
+ * SUMMARY: Tests the connectivity of the Master/Slave SPI bus net
+ *
+ *	This test will ensure a good SPI bus connection between the
+ *	master DUT and the slave DUT(s). This bus connection is ensured by
+ *	opening file 0 (SRAM loader). If there is a slave and the open fails
+ *	then there is a master/slave communication issue. Opening file 0 on
+ *	the master will open it on the slave as well if the slave was detected.
+ *
+ * RETURN:
+ *	 0 = Success
+ *	!0 = Failure
+ *
+ * PARAMETERS:
+ *	*dev                - pointer to device structure
+ *	*slave_irq_toggled  - pointer to where to store if slave IRQ toggled
+ *	*slave_bus_toggled  - pointer to where to store if slave Bus toggled
+ *	*err_str            - pointer to error string buffer
+ ******************************************************************************/
+static int pt_bist_slave_bus_test(struct device *dev,
+	u8 *slave_irq_toggled, u8 *slave_bus_toggled, u8 *err_str)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	u8 mode = PT_MODE_UNKNOWN;
+	u8 bus_toggled = false;
+	u8 file_handle;
+	int result = 0;
+	int rc = 0;
+
+	rc = _pt_request_pip2_enter_bl(dev, &mode, &result);
+	if (rc) {
+		pt_debug(cd->dev, DL_ERROR, "%s: Error entering BL rc=%d\n",
+			__func__, rc);
+		strlcpy(err_str, "- State could not be determined.", PT_ERR_STR_SIZE);
+		goto exit;
+	}
+
+	pt_debug(dev, DL_INFO, "%s Attempt open file 0\n", __func__);
+	file_handle = _pt_pip2_file_open(dev, PIP2_RAM_FILE);
+	if (file_handle != PIP2_RAM_FILE) {
+		rc = -ENOENT;
+		bus_toggled = false;
+		pt_debug(dev, DL_ERROR,
+			"%s Failed to open bin file\n", __func__);
+		strlcpy(err_str, "- Bus open, shorted or DUT in reset", PT_ERR_STR_SIZE);
+		goto exit;
+	} else {
+		bus_toggled = true;
+		if (file_handle != _pt_pip2_file_close(dev, file_handle)) {
+			pt_debug(dev, DL_ERROR,
+				"%s: File Close failed, file_handle=%d\n",
+				__func__, file_handle);
+		}
+	}
+
+exit:
+	/* If the master was able to send/recv a PIP msg, the IRQ must be ok */
+	if (slave_irq_toggled)
+		*slave_irq_toggled = bus_toggled;
+	if (slave_bus_toggled)
+		*slave_bus_toggled = bus_toggled;
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_ttdl_bist_show
+ *
+ * SUMMARY: Show method for the ttdl_bist sysfs node. This built in self test
+ *	will test that I2C/SPI, IRQ and TP_XRES pins are operational.
+ *
+ *	NOTE: This function will reset the DUT and the startup_status bit
+ *	mask. A pt_enum will be queued after completion.
+ *
+ *	NOTE: The order of the net tests is done to optimize the time it takes
+ *	to run. The first test is capable of verifying all nets, each subsequent
+ *	test is only run if the previous was not able to see all nets toggle.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*dev  - pointer to device structure
+ *	*attr - pointer to device attributes
+ *	*buf  - pointer to output buffer
+ ******************************************************************************/
+static ssize_t pt_ttdl_bist_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	ssize_t ret;
+	char *bus_err_str        = NULL;
+	char *irq_err_str        = NULL;
+	char *xres_err_str       = NULL;
+	char *slave_bus_err_str  = NULL;
+	char *slave_irq_err_str  = NULL;
+	char *slave_xres_err_str = NULL;
+	u8 tests;
+	int rc                = 0;
+	int num_tests         = 0;
+	int status            = 1;    /* 0 = Pass, !0 = fail */
+	u8 bus_toggled        = 0x0F; /* default to untested */
+	u8 i2c_toggled        = 0x0F; /* default to untested */
+	u8 spi_toggled        = 0x0F; /* default to untested */
+	u8 irq_toggled        = 0x0F; /* default to untested */
+	u8 xres_toggled       = 0x0F; /* default to untested */
+	u8 slave_bus_toggled  = 0x0F; /* default to untested */
+	u8 slave_irq_toggled  = 0x0F; /* default to untested */
+	u8 slave_xres_toggled = 0x0F; /* default to untested */
+
+	bus_err_str = kzalloc(PT_ERR_STR_SIZE, GFP_KERNEL);
+	irq_err_str = kzalloc(PT_ERR_STR_SIZE, GFP_KERNEL);
+	xres_err_str = kzalloc(PT_ERR_STR_SIZE, GFP_KERNEL);
+	if (!bus_err_str || !irq_err_str || !xres_err_str)
+		goto print_results;
+
+	memset(xres_err_str, 0, PT_ERR_STR_SIZE);
+	memset(irq_err_str, 0, PT_ERR_STR_SIZE);
+	memset(bus_err_str, 0, PT_ERR_STR_SIZE);
+
+	if (cd->multi_chip) {
+		slave_bus_err_str = kzalloc(PT_ERR_STR_SIZE, GFP_KERNEL);
+		slave_irq_err_str = kzalloc(PT_ERR_STR_SIZE, GFP_KERNEL);
+		slave_xres_err_str = kzalloc(PT_ERR_STR_SIZE, GFP_KERNEL);
+		if (!slave_bus_err_str ||
+		    !slave_irq_err_str ||
+		    !slave_xres_err_str)
+			goto print_results;
+		memset(slave_bus_err_str, 0, PT_ERR_STR_SIZE);
+		memset(slave_irq_err_str, 0, PT_ERR_STR_SIZE);
+		memset(slave_xres_err_str, 0, PT_ERR_STR_SIZE);
+	}
+
+	/* Turn off the TTDL WD during the test */
+	pt_stop_wd_timer(cd);
+
+	/* Shorten default PIP cmd timeout while running BIST */
+	cd->pip_cmd_timeout = 200;
+
+	/* Count the number of tests to run */
+	tests = cd->ttdl_bist_select;
+	while (tests) {
+		num_tests += tests & 1;
+		tests >>= 1;
+	}
+	pt_debug(dev, DL_ERROR, "%s: BIST select = 0x%02X, run %d tests\n",
+		__func__, cd->ttdl_bist_select, num_tests);
+
+	/* Suppress auto BL to avoid loader thread sending PIP during xres */
+	if (cd->flashless_dut) {
+		pt_debug(dev, DL_INFO, "%s Flashless Auto_BL - SUPPRESS\n",
+			__func__);
+		mutex_lock(&cd->system_lock);
+		cd->flashless_auto_bl = PT_SUPPRESS_AUTO_BL;
+		mutex_unlock(&cd->system_lock);
+	}
+
+	/* --------------- TP_XRES BIST TEST --------------- */
+	if ((cd->ttdl_bist_select & PT_TTDL_BIST_TP_XRES_TEST) != 0) {
+		pt_debug(dev, DL_INFO,
+			"%s: ----- Start TP_XRES BIST -----", __func__);
+		rc = pt_bist_xres_test(dev, &bus_toggled, &irq_toggled,
+			&xres_toggled, xres_err_str);
+		/* Done if the rest of all nets toggled */
+		if (bus_toggled == 1 && irq_toggled == 1 && xres_toggled == 1)
+			goto host_nets_complete;
+	}
+
+	/* Flush bus in case a PIP response is waiting from previous test */
+	pt_flush_bus(cd, PT_FLUSH_BUS_BASED_ON_LEN, NULL);
+
+	/* --------------- IRQ BIST TEST --------------- */
+	if ((cd->ttdl_bist_select & PT_TTDL_BIST_IRQ_TEST) != 0) {
+		pt_debug(dev, DL_INFO,
+			"%s: ----- Start IRQ BIST -----", __func__);
+		bus_toggled = 0xFF;
+		irq_toggled = 0xFF;
+		rc = pt_bist_irq_test(dev, &bus_toggled, &irq_toggled,
+			&xres_toggled, irq_err_str);
+		/* If this net failed clear results from previous net */
+		if (irq_toggled != 1) {
+			xres_toggled = 0x0F;
+			memset(xres_err_str, 0, PT_ERR_STR_SIZE);
+		}
+		if (bus_toggled == 1 && irq_toggled == 1)
+			goto host_nets_complete;
+	}
+
+	/* Flush bus in case a PIP response is waiting from previous test */
+	pt_flush_bus(cd, PT_FLUSH_BUS_BASED_ON_LEN, NULL);
+
+	/* --------------- BUS BIST TEST --------------- */
+	if ((cd->ttdl_bist_select & PT_TTDL_BIST_BUS_TEST) != 0) {
+		pt_debug(dev, DL_INFO,
+			"%s: ----- Start BUS BIST -----", __func__);
+		bus_toggled = 0xFF;
+		rc = pt_bist_bus_test(dev, &bus_toggled, bus_err_str);
+		/* If this net failed clear results from previous net */
+		if (bus_toggled == 0) {
+			irq_toggled = 0x0F;
+			memset(irq_err_str, 0, PT_ERR_STR_SIZE);
+		}
+	}
+
+host_nets_complete:
+	/* --------------- SLAVE XRES BIST TEST --------------- */
+	if (cd->multi_chip &&
+	    (cd->ttdl_bist_select & PT_TTDL_BIST_SLAVE_XRES_TEST) != 0) {
+		pt_debug(dev, DL_INFO,
+			"%s: ----- Start Slave XRES BIST -----", __func__);
+		slave_xres_toggled = 0xFF;
+		rc = pt_bist_slave_xres_test(dev, &slave_irq_toggled,
+			&slave_bus_toggled, &slave_xres_toggled,
+			slave_xres_err_str);
+		if ((slave_bus_toggled == 1 && slave_irq_toggled == 1 &&
+		    slave_xres_toggled == 1) || slave_xres_toggled == 0)
+			goto print_results;
+	}
+
+	/* --------------- SLAVE IRQ BIST TEST --------------- */
+	if (cd->multi_chip &&
+	    (cd->ttdl_bist_select & PT_TTDL_BIST_SLAVE_IRQ_TEST) != 0) {
+		pt_debug(dev, DL_INFO,
+			"%s: ----- Start Slave IRQ BIST -----", __func__);
+		slave_irq_toggled = 0xFF;
+		rc = pt_bist_slave_irq_test(dev, &slave_irq_toggled,
+			&slave_bus_toggled, slave_irq_err_str, NULL, NULL);
+		pt_debug(dev, DL_INFO, "%s: slave_irq_toggled = 0x%02X\n",
+			__func__, slave_irq_toggled);
+		if (slave_irq_toggled == 1) {
+			slave_bus_toggled = 1;
+			goto print_results;
+		}
+	}
+
+	/* --------------- SLAVE BUS BIST TEST --------------- */
+	if (cd->multi_chip &&
+	    (cd->ttdl_bist_select & PT_TTDL_BIST_SLAVE_BUS_TEST) != 0) {
+		pt_debug(dev, DL_INFO,
+			"%s: ----- Start Slave BUS BIST -----", __func__);
+		slave_bus_toggled = 0xFF;
+		rc = pt_bist_slave_bus_test(dev, &slave_irq_toggled,
+			&slave_bus_toggled, slave_bus_err_str);
+	}
+
+print_results:
+	/* Restore PIP command timeout */
+	cd->pip_cmd_timeout = cd->pip_cmd_timeout_default;
+
+	/*
+	 * We're done! - Perform a hard XRES toggle, allowing BL
+	 * to load FW if there is any in Flash
+	 */
+	mutex_lock(&cd->system_lock);
+	cd->hid_reset_cmd_state = 0;
+	mutex_unlock(&cd->system_lock);
+
+	pt_debug(dev, DL_INFO,
+		"%s: TTDL BIST Complete - Final reset\n", __func__);
+
+	if (cd->flashless_dut) {
+		/*
+		 * For Flashless solution, FW update is triggered after BL is
+		 * seen that several miliseconds delay is needed.
+		 */
+		pt_debug(dev, DL_INFO, "%s Flashless Auto_BL - ALLOW\n",
+			__func__);
+		mutex_lock(&cd->system_lock);
+		cd->flashless_auto_bl = PT_ALLOW_AUTO_BL;
+		mutex_unlock(&cd->system_lock);
+
+		/* Reset DUT and wait 100ms to see if loader started */
+		pt_hw_hard_reset(cd);
+		msleep(100);
+		if (cd->fw_updating) {
+			pt_debug(dev, DL_INFO,
+				 "%s: ----- BIST Wait FW Loading ----",
+				 __func__);
+			rc = _pt_request_wait_for_enum_state(
+			    dev, 4000, STARTUP_STATUS_COMPLETE);
+		}
+	} else {
+		if (cd->mode == PT_MODE_BOOTLOADER) {
+			rc = pt_pip2_launch_app(dev, PT_CORE_CMD_PROTECTED);
+			if (rc) {
+				pt_debug(dev, DL_ERROR,
+					"%s Failed to launch app\n", __func__);
+				rc = pt_hw_hard_reset(cd);
+			}
+		}
+
+		/*
+		 * If FW exists the BL may have just started or will start soon,
+		 * so the FW sentinel may be on it's way but with no FW it will
+		 * not arrive, wait for it before deciding if we need to queue
+		 * an enum.
+		 */
+		rc = _pt_request_wait_for_enum_state(
+		    dev, 400, STARTUP_STATUS_FW_RESET_SENTINEL);
+		if ((cd->startup_status & STARTUP_STATUS_FW_RESET_SENTINEL) &&
+		    (cd->startup_status & STARTUP_STATUS_COMPLETE) == 0) {
+			pt_debug(dev, DL_INFO, "%s: ----- BIST Enum ----",
+				 __func__);
+			pt_queue_enum(cd);
+			rc = _pt_request_wait_for_enum_state(
+			    dev, 2000, STARTUP_STATUS_COMPLETE);
+		}
+	}
+	msleep(20);
+
+	/* --------------- PRINT OUT BIST RESULTS ---------------*/
+	pt_debug(dev, DL_INFO, "%s: ----- BIST Print Results ----", __func__);
+	pt_start_wd_timer(cd);
+
+	/* Canned print if any memory allocation issues */
+	if (!bus_err_str || !irq_err_str || !xres_err_str) {
+		ret = scnprintf(buf, strlen(buf),
+			"Status: %d\n"
+			"I2C (SDA,SCL):              [UNTEST]\n"
+			"SPI (MISO,MOSI,CS,CLK):     [UNTEST]\n"
+			"IRQ connection:             [UNTEST]\n"
+			"TP_XRES connection:         [UNTEST]\n", -ENOMEM);
+		if (cd->multi_chip) {
+			ret += scnprintf(buf + ret, strlen(buf),
+				"I/P SPI (MISO,MOSI,CS,CLK): [UNTEST]\n"
+				"I/P IRQ connection:         [UNTEST]\n"
+				"I/P TP_XRES connection:     [UNTEST]\n");
+		}
+	} else {
+		status = 0;
+		if (bus_toggled == 1)
+			memset(bus_err_str, 0, PT_ERR_STR_SIZE);
+		if (irq_toggled == 1)
+			memset(irq_err_str, 0, PT_ERR_STR_SIZE);
+		if (xres_toggled == 1)
+			memset(xres_err_str, 0, PT_ERR_STR_SIZE);
+
+		if (cd->ttdl_bist_select & PT_TTDL_BIST_BUS_TEST)
+			status += bus_toggled;
+		if (cd->ttdl_bist_select & PT_TTDL_BIST_IRQ_TEST)
+			status += irq_toggled;
+		if (cd->ttdl_bist_select & PT_TTDL_BIST_TP_XRES_TEST)
+			status += xres_toggled;
+		pt_debug(dev, DL_ERROR, "%s: status = %d (%d,%d,%d)\n",
+			__func__, status, bus_toggled, irq_toggled,
+			xres_toggled);
+
+		if (cd->multi_chip) {
+			if (slave_irq_toggled == 1)
+				memset(slave_irq_err_str, 0, PT_ERR_STR_SIZE);
+			if (slave_xres_toggled == 1)
+				memset(slave_xres_err_str, 0, PT_ERR_STR_SIZE);
+			if (slave_bus_toggled == 1)
+				memset(slave_bus_err_str, 0, PT_ERR_STR_SIZE);
+
+			if (cd->ttdl_bist_select & PT_TTDL_BIST_SLAVE_BUS_TEST)
+				status += slave_bus_toggled;
+			if (cd->ttdl_bist_select & PT_TTDL_BIST_SLAVE_IRQ_TEST)
+				status += slave_irq_toggled;
+			if (cd->ttdl_bist_select & PT_TTDL_BIST_SLAVE_XRES_TEST)
+				status += slave_xres_toggled;
+			pt_debug(dev, DL_ERROR,
+				"%s: status = %d (%d,%d,%d,%d,%d,%d)\n",
+				__func__, status, bus_toggled, irq_toggled,
+				xres_toggled, slave_bus_toggled,
+				slave_irq_toggled, slave_xres_toggled);
+		}
+
+		if (cd->bus_ops->bustype == BUS_I2C)
+			i2c_toggled = bus_toggled;
+		else
+			spi_toggled = bus_toggled;
+
+		ret = scnprintf(buf, strlen(buf),
+			"Status: %d\n"
+			"I2C (SDA,SCL):               %s %s\n"
+			"SPI (MISO,MOSI,CS,CLK):      %s %s\n"
+			"IRQ connection:              %s %s\n"
+			"TP_XRES connection:          %s %s\n",
+			status == num_tests ? 0 : 1,
+			i2c_toggled == 0x0F ? "[UNTEST]" :
+				i2c_toggled == 1 ? "[  OK  ]" : "[FAILED]",
+			i2c_toggled == 0x0F ? "" : bus_err_str,
+			spi_toggled == 0x0F ? "[UNTEST]" :
+				spi_toggled == 1 ? "[  OK  ]" : "[FAILED]",
+			spi_toggled == 0x0F ? "" : bus_err_str,
+			irq_toggled == 0x0F ? "[UNTEST]" :
+				irq_toggled == 1 ? "[  OK  ]" : "[FAILED]",
+			irq_err_str,
+			xres_toggled == 0x0F ? "[UNTEST]" :
+				xres_toggled == 1 ? "[  OK  ]" : "[FAILED]",
+			xres_err_str);
+
+		if (cd->multi_chip) {
+			ret += scnprintf(buf + ret, strlen(buf),
+				"I/P SPI (MISO,MOSI,CS,CLK):  %s %s\n"
+				"I/P IRQ connection:          %s %s\n"
+				"I/P TP_XRES connection:      %s %s\n",
+				slave_bus_toggled == 0x0F ? "[UNTEST]" :
+					slave_bus_toggled == 1 ? "[  OK  ]" :
+						"[FAILED]", slave_bus_err_str,
+				slave_irq_toggled == 0x0F ? "[UNTEST]" :
+					slave_irq_toggled == 1 ? "[  OK  ]" :
+						"[FAILED]", slave_irq_err_str,
+				slave_xres_toggled == 0x0F ? "[UNTEST]" :
+					slave_xres_toggled == 1 ? "[  OK  ]" :
+						"[FAILED]", slave_xres_err_str);
+		}
+	}
+
+	/* Put TTDL back into a known state, issue a ttdl enum if needed */
+	pt_debug(dev, DL_INFO, "%s: Startup_status = 0x%04X\n",
+		__func__, cd->startup_status);
+	kfree(bus_err_str);
+	kfree(irq_err_str);
+	kfree(xres_err_str);
+	return ret;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_ttdl_bist_store
+ *
+ * SUMMARY: Store method for the ttdl_bist sysfs node.
+ *
+ * RETURN: Size of passed in buffer if successful
+ *
+ * PARAMETERS:
+ *	*dev  - pointer to device structure
+ *	*attr - pointer to device attributes
+ *	*buf  - pointer to command buffer
+ *	 size - size of buf
+ ******************************************************************************/
+static ssize_t pt_ttdl_bist_store(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t size)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	u32 input_data[2] = {0};
+	int length;
+	int rc = 0;
+
+	/* Maximum input of one value */
+	length = _pt_ic_parse_input(dev, buf, size, input_data,
+		ARRAY_SIZE(input_data));
+	if (length != 1) {
+		pt_debug(dev, DL_ERROR, "%s: Invalid number of arguments\n",
+			__func__);
+		rc = -EINVAL;
+		goto exit;
+	} else {
+		mutex_lock(&cd->system_lock);
+		cd->ttdl_bist_select = input_data[0];
+		mutex_unlock(&cd->system_lock);
+	}
+
+exit:
+	if (rc)
+		return rc;
+	return size;
+}
+static DEVICE_ATTR(ttdl_bist,  0644, pt_ttdl_bist_show,
+	pt_ttdl_bist_store);
+
+/*******************************************************************************
+ * FUNCTION: pt_flush_bus_store
+ *
+ * SUMMARY: Store method for the flush_bus sysfs node.
+ *
+ * RETURN: Size of passed in buffer if successful
+ *
+ * PARAMETERS:
+ *	*dev  - pointer to device structure
+ *	*attr - pointer to device attributes
+ *	*buf  - pointer to command buffer
+ *	 size - size of buf
+ ******************************************************************************/
+static ssize_t pt_flush_bus_store(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t size)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	u32 input_data[2] = {0};
+	int length;
+	int rc = 0;
+
+	/* Maximum input of one value */
+	length = _pt_ic_parse_input(dev, buf, size, input_data,
+		ARRAY_SIZE(input_data));
+	if (length != 1) {
+		pt_debug(dev, DL_ERROR, "%s: Invalid number of arguments\n",
+			__func__);
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	mutex_lock(&cd->system_lock);
+	if (input_data[0] == 0)
+		cd->flush_bus_type = PT_FLUSH_BUS_BASED_ON_LEN;
+	else if (input_data[0] == 1)
+		cd->flush_bus_type = PT_FLUSH_BUS_FULL_256_READ;
+	else
+		rc = -EINVAL;
+	mutex_unlock(&cd->system_lock);
+
+exit:
+	if (rc)
+		return rc;
+	return size;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_flush_bus_show
+ *
+ * SUMMARY: Show method for the flush_bus sysfs node that flushes the active bus
+ *      based on either the size of the first two bytes or a blind 256 bytes.
+ *
+ * RETURN:
+ *       0 = success
+ *      !0 = failure
+ *
+ * PARAMETERS:
+ *      *dev  - pointer to device structure
+ *      *attr - pointer to device attributes
+ *      *buf  - pointer to output buffer
+ ******************************************************************************/
+static ssize_t pt_flush_bus_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	ssize_t ret = 0;
+	ssize_t bytes = 0;
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+
+	mutex_lock(&cd->system_lock);
+	bytes = pt_flush_bus(cd, cd->flush_bus_type, NULL);
+
+	ret = scnprintf(buf, strlen(buf),
+		"Status: 0\n"
+		"%s: %zd\n",
+		"Bytes flushed", bytes);
+
+	mutex_unlock(&cd->system_lock);
+	return ret;
+}
+static DEVICE_ATTR(flush_bus, 0644, pt_flush_bus_show,
+	pt_flush_bus_store);
+
+/*******************************************************************************
+ * FUNCTION: pt_pip2_ping_test_store
+ *
+ * SUMMARY: Store method for the pip2_ping_test sysfs node.
+ *
+ * NOTE: The max PIP2 packet size is 255 (payload for PING 247) however
+ *	someone may want to test sending invalid packet lengths so any values
+ *	up to 255 are allowed.
+ *
+ * RETURN: Size of passed in buffer if successful
+ *
+ * PARAMETERS:
+ *	*dev  - pointer to device structure
+ *	*attr - pointer to device attributes
+ *	*buf  - pointer to command buffer
+ *	 size - size of buf
+ ******************************************************************************/
+static ssize_t pt_pip2_ping_test_store(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t size)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	u32 input_data[2];
+	int length;
+	int rc = 0;
+
+	/* Maximum input of one value */
+	length = _pt_ic_parse_input(dev, buf, size, input_data,
+		ARRAY_SIZE(input_data));
+	if (length != 1) {
+		pt_debug(dev, DL_ERROR, "%s: Invalid number of arguments\n",
+			__func__);
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	mutex_lock(&cd->system_lock);
+	if (input_data[1] >= 0 && input_data[0] <= PT_MAX_PIP2_MSG_SIZE)
+		cd->ping_test_size = input_data[0];
+	else
+		rc = -EINVAL;
+	mutex_unlock(&cd->system_lock);
+
+exit:
+	if (rc)
+		return rc;
+	return size;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_ping_test_show
+ *
+ * SUMMARY: Show method for the ping_test_show sysfs node that sends the PIP2
+ *      PING command and ramps up the optional payload from 0 to
+ *	ping_test_size.
+ *	The max payload size is 247:
+ *		(255 - 2 byte reg address - 4 byte header - 2 byte CRC)
+ *
+ * RETURN:
+ *       0 = success
+ *      !0 = failure
+ *
+ * PARAMETERS:
+ *      *dev  - pointer to device structure
+ *      *attr - pointer to device attributes
+ *      *buf  - pointer to output buffer
+ ******************************************************************************/
+static ssize_t pt_pip2_ping_test_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	ssize_t ret;
+	int last_packet_size;
+	int rc = 0;
+
+	rc = pt_pip2_ping_test(dev, cd->ping_test_size, &last_packet_size);
+
+	if (rc) {
+		ret = scnprintf(buf, strlen(buf), "Status: %d\n", rc);
+		return ret;
+	}
+	ret = scnprintf(buf, strlen(buf),
+		"Status: %d\n"
+		"PING payload test passed with packet sizes 0 - %d\n",
+		(last_packet_size == cd->ping_test_size ? 0 : 1),
+		last_packet_size);
+
+	return ret;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_t_refresh_store
+ *
+ * SUMMARY: Store method for the t-refresh sysfs node that will takes a passed
+ *	in integer as the number of interrupts to count. A timer is started to
+ *	calculate the total time it takes to see that number of interrupts.
+ *
+ * RETURN: Size of passed in buffer if successful
+ *
+ * PARAMETERS:
+ *	*dev  - pointer to device structure
+ *	*attr - pointer to device attributes
+ *	*buf  - pointer to buffer that hold the command parameters
+ *	 size - size of buf
+ ******************************************************************************/
+static ssize_t pt_t_refresh_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	u32 input_data[2];
+	int length;
+	int rc = 0;
+
+	/* Maximum input of one value */
+	length = _pt_ic_parse_input(dev, buf, size, input_data,
+		ARRAY_SIZE(input_data));
+	if (length != 1) {
+		pt_debug(dev, DL_ERROR, "%s: Invalid number of arguments\n",
+			__func__);
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	mutex_lock(&cd->system_lock);
+	pt_debug(dev, DL_INFO, "%s: Input value: %d\n", __func__,
+		input_data[0]);
+	if (input_data[0] >= 0 && input_data[0] <= 1000) {
+		cd->t_refresh_total = input_data[0];
+		cd->t_refresh_count  = 0;
+		cd->t_refresh_active = 1;
+	} else {
+		pt_debug(dev, DL_ERROR, "%s: Invalid value\n", __func__);
+		rc = -EINVAL;
+	}
+	mutex_unlock(&cd->system_lock);
+
+exit:
+	pt_debug(dev, DL_ERROR, "%s: rc = %d\n", __func__, rc);
+	if (rc)
+		return rc;
+	return size;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_t_refresh_show
+ *
+ * SUMMARY: Show method for the t-refresh sysfs node that will show the results
+ *	of the T-Refresh timer counting the time it takes to see a user defined
+ *	number of interrupts.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*dev  - pointer to device structure
+ *	*attr - pointer to device attributes
+ *	*buf  - pointer to output buffer
+ ******************************************************************************/
+static ssize_t pt_t_refresh_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	ssize_t ret = 0;
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	u32 whole;
+	u16 fraction;
+
+	mutex_lock(&cd->system_lock);
+
+	/* Check if we have counted the number requested */
+	if (cd->t_refresh_count != cd->t_refresh_total) {
+		ret = scnprintf(buf, strlen(buf),
+			"Status: 0\n"
+			"%s: %d\n",
+			"Still counting... current IRQ count",
+			cd->t_refresh_count);
+	} else {
+		/* Ensure T-Refresh is de-activated */
+		cd->t_refresh_active = 0;
+
+		whole = cd->t_refresh_time / cd->t_refresh_count;
+		fraction = cd->t_refresh_time % cd->t_refresh_count;
+		fraction = fraction * 1000 / cd->t_refresh_count;
+
+		ret = scnprintf(buf, strlen(buf),
+			"Status: 0\n"
+			"%s: %d\n"
+			"%s: %d\n"
+			"%s: %d\n"
+			"%s: %d.%02d\n",
+			"Requested IRQ Count     ", cd->t_refresh_total,
+			"IRQ Counted             ", cd->t_refresh_count,
+			"Total Time Elapsed (ms) ", (int)cd->t_refresh_time,
+			"Average T-Refresh  (ms) ", whole, fraction);
+	}
+	mutex_unlock(&cd->system_lock);
+	return ret;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_dut_status_show
+ *
+ * SUMMARY: Show method for DUT status sysfs node. Display DUT's scan state, and
+ * more items such as operation mode,easywake state are added in the future.
+ *
+ * RETURN: Char buffer with printed scan status information
+ *
+ * PARAMETERS:
+ *	*dev  - pointer to device structure
+ *	*attr - pointer to device attributes
+ *	*buf  - pointer to output buffer
+ ******************************************************************************/
+static ssize_t pt_dut_status_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	u8 sys_mode = FW_SYS_MODE_UNDEFINED;
+	u8 mode = PT_MODE_UNKNOWN;
+	char *outputstring[7] = {"BOOT", "SCANNING", "DEEP_SLEEP",
+				 "TEST", "DEEP_STANDBY", "UNDEFINED", "n/a"};
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	ssize_t ret;
+	u16 calculated_crc = 0;
+	u16 stored_crc = 0;
+	u8 status;
+	int rc = 0;
+
+	/* In STANDBY the DUT will not repond to any PIP cmd */
+	if (cd->fw_sys_mode_in_standby_state) {
+		mode = PT_MODE_OPERATIONAL;
+		sys_mode = FW_SYS_MODE_DEEP_STANDBY;
+		goto print_limited_results;
+	}
+
+	/* Retrieve mode and FW system mode which can only be 0-4 */
+	rc = pt_get_fw_sys_mode(cd, &sys_mode, &mode);
+	if (rc || mode == PT_MODE_UNKNOWN) {
+		ret = scnprintf(buf, PT_MAX_PRBUF_SIZE,
+			"%s: %d\n"
+			"%s: n/a\n"
+			"%s: n/a\n"
+			"%s: n/a\n"
+			"%s: n/a\n",
+			"Status", rc,
+			"Active Exec    ",
+			"FW System Mode ",
+			"Stored CRC     ",
+			"Calculated CRC ");
+		return ret;
+	} else {
+		if (mode == PT_MODE_OPERATIONAL) {
+			if (sys_mode > FW_SYS_MODE_MAX)
+				sys_mode = FW_SYS_MODE_UNDEFINED;
+			if (sys_mode != FW_SYS_MODE_TEST)
+				goto print_limited_results;
+
+			rc = pt_pip_verify_config_block_crc_(cd,
+				PT_TCH_PARM_EBID, &status,
+				&calculated_crc, &stored_crc);
+			if (rc)
+				goto print_limited_results;
+
+			ret = scnprintf(buf, PT_MAX_PRBUF_SIZE,
+				"%s: %d\n"
+				"%s: %s\n"
+				"%s: %s\n"
+				"%s: 0x%04X\n"
+				"%s: 0x%04X\n",
+				"Status", rc,
+				"Active Exec    ", "FW",
+				"FW System Mode ", outputstring[sys_mode],
+				"Stored CRC     ", stored_crc,
+				"Calculated CRC ", calculated_crc);
+			return ret;
+		} else {
+			/* When in BL or unknon mode Active Exec is "n/a" */
+			sys_mode = FW_SYS_MODE_UNDEFINED + 1;
+		}
+	}
+
+print_limited_results:
+	ret = scnprintf(buf, PT_MAX_PRBUF_SIZE,
+		"%s: %d\n"
+		"%s: %s\n"
+		"%s: %s\n"
+		"%s: n/a\n"
+		"%s: n/a\n",
+		"Status", rc,
+		"Active Exec    ",
+			mode == PT_MODE_OPERATIONAL ? "FW" : "BL",
+		"FW System Mode ", outputstring[sys_mode],
+		"Stored CRC     ",
+		"Calculated CRC ");
+
+	return ret;
+}
+#endif /* TTDL_DIAGNOSTICS */
+
+
+/*******************************************************************************
+ * Structures of sysfs attributes for all DUT dependent sysfs node
+ ******************************************************************************/
+static struct attribute *early_attrs[] = {
+	&dev_attr_hw_version.attr,
+	&dev_attr_drv_version.attr,
+	&dev_attr_drv_ver.attr,
+	&dev_attr_fw_version.attr,
+	&dev_attr_sysinfo.attr,
+	&dev_attr_pip2_cmd_rsp.attr,
+	&dev_attr_command.attr,
+	&dev_attr_drv_debug.attr,
+	&dev_attr_hw_reset.attr,
+	&dev_attr_response.attr,
+	&dev_attr_ttdl_restart.attr,
+#ifdef TTDL_DIAGNOSTICS
+	&dev_attr_ttdl_status.attr,
+	&dev_attr_pip2_enter_bl.attr,
+	&dev_attr_pip2_exit_bl.attr,
+	&dev_attr_err_gpio.attr,
+	&dev_attr_flush_bus.attr,
+	&dev_attr_ttdl_bist.attr,
+#endif /* TTDL_DIAGNOSTICS */
+	NULL,
+};
+
+static struct attribute_group early_attr_group = {
+	.attrs	= early_attrs,
+};
+
+static struct device_attribute pip2_attributes[] = {
+	__ATTR(pip2_version, 0444, pt_pip2_version_show, NULL),
+	__ATTR(pip2_gpio_read, 0444, pt_pip2_gpio_read_show, NULL),
+#ifdef TTDL_DIAGNOSTICS
+	__ATTR(pip2_bin_hdr,  0444, pt_pip2_bin_hdr_show, NULL),
+	__ATTR(pip2_ping_test, 0644, pt_pip2_ping_test_show,
+		pt_pip2_ping_test_store),
+#endif
+};
+
+static struct device_attribute attributes[] = {
+	__ATTR(dut_debug, 0644,
+		pt_dut_debug_show, pt_drv_debug_store),
+	__ATTR(sleep_status, 0444, pt_sleep_status_show, NULL),
+	__ATTR(panel_id, 0444, pt_panel_id_show, NULL),
+	__ATTR(get_param, 0644,
+		pt_get_param_show, pt_get_param_store),
+	__ATTR(pt_touch_offload, 0644,
+		pt_touch_offload_show, pt_touch_offload_store),
+#ifdef EASYWAKE_TSG6
+	__ATTR(easy_wakeup_gesture, 0644, pt_easy_wakeup_gesture_show,
+		pt_easy_wakeup_gesture_store),
+	__ATTR(easy_wakeup_gesture_id, 0444,
+		pt_easy_wakeup_gesture_id_show, NULL),
+	__ATTR(easy_wakeup_gesture_data, 0444,
+		pt_easy_wakeup_gesture_data_show, NULL),
+#endif
+#ifdef TTDL_DIAGNOSTICS
+	__ATTR(platform_data, 0444, pt_platform_data_show, NULL),
+	__ATTR(drv_irq, 0644, pt_drv_irq_show, pt_drv_irq_store),
+	__ATTR(dut_status, 0444, pt_dut_status_show, NULL),
+	__ATTR(t_refresh, 0644, pt_t_refresh_show, pt_t_refresh_store),
+#endif /* TTDL_DIAGNOSTICS */
+};
+
+/*******************************************************************************
+ * FUNCTION: add_sysfs_interfaces
+ *
+ * SUMMARY: Creates all DUT dependent sysfs nodes owned by the core
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*dev  - pointer to device structure
+ ******************************************************************************/
+static int add_sysfs_interfaces(struct device *dev)
+{
+	int i;
+	int j = 0;
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+
+	for (i = 0; i < ARRAY_SIZE(attributes); i++) {
+		if (device_create_file(dev, attributes + i))
+			goto undo;
+	}
+
+	pt_debug(dev, DL_INFO, "%s: Active DUT Generation: %d",
+		__func__, cd->active_dut_generation);
+	if (cd->active_dut_generation == DUT_PIP2_CAPABLE) {
+		for (j = 0; j < ARRAY_SIZE(pip2_attributes); j++) {
+			if (device_create_file(dev, pip2_attributes + j))
+				goto undo;
+		}
+	}
+	return 0;
+undo:
+	for (i--; i >= 0; i--)
+		device_remove_file(dev, attributes + i);
+	for (j--; j >= 0; j--)
+		device_remove_file(dev, pip2_attributes + j);
+	pt_debug(dev, DL_ERROR, "%s: failed to create sysfs interface\n",
+		__func__);
+	return -ENODEV;
+}
+
+/*******************************************************************************
+ * FUNCTION: remove_sysfs_interfaces
+ *
+ * SUMMARY: Removes all DUT dependent sysfs nodes owned by the core
+ *
+ * RETURN: void
+ *
+ * PARAMETERS:
+ *	*dev  - pointer to device structure
+ ******************************************************************************/
+static void remove_sysfs_interfaces(struct device *dev)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(attributes); i++)
+		device_remove_file(dev, attributes + i);
+	for (i = 0; i < ARRAY_SIZE(pip2_attributes); i++)
+		device_remove_file(dev, pip2_attributes + i);
+}
+
+/*******************************************************************************
+ * FUNCTION: remove_sysfs_and_modules
+ *
+ * SUMMARY: Removes all DUT dependent sysfs nodes and modules
+ *
+ * RETURN: void
+ *
+ * PARAMETERS:
+ *	*dev  - pointer to device structure
+ ******************************************************************************/
+static void remove_sysfs_and_modules(struct device *dev)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+
+	/* Queued work should be removed before to release loader module */
+	call_atten_cb(cd, PT_ATTEN_CANCEL_LOADER, 0);
+	pt_release_modules(cd);
+	pt_btn_release(dev);
+	pt_mt_release(dev);
+	remove_sysfs_interfaces(dev);
+}
+
+static int pt_ts_pinctrl_init(struct pt_core_data *cd)
+{
+	int retval;
+
+	/* Get pinctrl if target uses pinctrl */
+	cd->ts_pinctrl = devm_pinctrl_get(cd->dev);
+	if (IS_ERR_OR_NULL(cd->ts_pinctrl)) {
+		retval = PTR_ERR(cd->ts_pinctrl);
+		dev_dbg(cd->dev,
+			"Target does not use pinctrl %d\n", retval);
+		goto err_pinctrl_get;
+	}
+
+	cd->pinctrl_state_active
+		= pinctrl_lookup_state(cd->ts_pinctrl,
+				PINCTRL_STATE_ACTIVE);
+	if (IS_ERR_OR_NULL(cd->pinctrl_state_active)) {
+		retval = PTR_ERR(cd->pinctrl_state_active);
+		dev_err(cd->dev,
+			"Can not lookup %s pinstate %d\n",
+			PINCTRL_STATE_ACTIVE, retval);
+		goto err_pinctrl_lookup;
+	}
+
+	cd->pinctrl_state_suspend
+		= pinctrl_lookup_state(cd->ts_pinctrl,
+			PINCTRL_STATE_SUSPEND);
+	if (IS_ERR_OR_NULL(cd->pinctrl_state_suspend)) {
+		retval = PTR_ERR(cd->pinctrl_state_suspend);
+		dev_err(cd->dev,
+			"Can not lookup %s pinstate %d\n",
+			PINCTRL_STATE_SUSPEND, retval);
+		goto err_pinctrl_lookup;
+	}
+
+	cd->pinctrl_state_release
+		= pinctrl_lookup_state(cd->ts_pinctrl,
+			PINCTRL_STATE_RELEASE);
+	if (IS_ERR_OR_NULL(cd->pinctrl_state_release)) {
+		retval = PTR_ERR(cd->pinctrl_state_release);
+		dev_dbg(cd->dev,
+			"Can not lookup %s pinstate %d\n",
+			PINCTRL_STATE_RELEASE, retval);
+	}
+
+	return 0;
+
+err_pinctrl_lookup:
+	devm_pinctrl_put(cd->ts_pinctrl);
+err_pinctrl_get:
+	cd->ts_pinctrl = NULL;
+	return retval;
+}
+
+void touch_notify_glink_pt_channel_state(bool state)
+{
+	pr_info("%s:[touch] touch_notify_glink\n", __func__);
+}
+
+void glink_touch_pt_rx_msg(void *data, int len)
+{
+	int rc = 0;
+	pr_info("%s: TOUCH_RX_MSG Start:\n", __func__);
+
+	if (len > TOUCH_GLINK_INTENT_SIZE) {
+		pr_err("Invalid TOUCH glink intent size\n");
+		return;
+	}
+	/* check SLATE response */
+	pt_slate_resp_ack = *(uint32_t *)&data[8];
+	if (pt_slate_resp_ack == 0x01) {
+			pr_err("Bad SLATE response\n");
+			rc = -EINVAL;
+			goto err_ret;
+	}
+	pr_info("%s: TOUCH_RX_MSG End:\n", __func__);
+err_ret:
+return;
+}
+
+/*******************************************************************************
+ *******************************************************************************
+ * FUNCTION: pt_probe
+ *
+ * SUMMARY: Probe of the core module.
+ *
+ *	NOTE: For the Parade Technologies development platform (PtSBC) the
+ *	probe functionality is split into two functions; pt_probe() and
+ *	pt_probe_complete(). the initial setup is done in this function which
+ *	then creates a WORK task which runs after the probe timer expires. This
+ *	ensures the I2C/SPI is up on the PtSBC in time for TTDL.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*ops           - pointer to the bus
+ *	*dev           - pointer to the device structure
+ *	 irq           - IRQ
+ *	 xfer_buf_size - size of the buffer
+ ******************************************************************************/
+int pt_probe(const struct pt_bus_ops *ops, struct device *dev,
+		u16 irq, size_t xfer_buf_size)
+{
+	struct pt_core_data *cd;
+	struct pt_platform_data *pdata = dev_get_platdata(dev);
+	enum pt_atten_type type;
+	struct i2c_client *client = to_i2c_client(dev);
+	int rc = 0;
+	u8 pip_ver_major;
+	u8 pip_ver_minor;
+	u32 status = STARTUP_STATUS_START;
+
+	if (!pdata || !pdata->core_pdata || !pdata->mt_pdata) {
+		pt_debug(dev, DL_ERROR, "%s: Missing platform data\n",
+				__func__);
+		rc = -ENODEV;
+		goto error_no_pdata;
+	}
+
+	if (pdata->core_pdata->flags & PT_CORE_FLAG_POWEROFF_ON_SLEEP) {
+		if (!pdata->core_pdata->power) {
+			pt_debug(dev, DL_ERROR,
+					"%s: Missing platform data function\n",
+					__func__);
+			rc = -ENODEV;
+			goto error_no_pdata;
+		}
+	}
+	/* get context and debug print buffers */
+	cd = kzalloc(sizeof(*cd), GFP_KERNEL);
+	if (!cd) {
+		rc = -ENOMEM;
+		goto error_no_pdata;
+	}
+	/* Initialize device info */
+	cd->dev                        = dev;
+	cd->pdata                      = pdata;
+	cd->cpdata                     = pdata->core_pdata;
+	cd->bus_ops                    = ops;
+	cd->debug_level                = PT_INITIAL_DEBUG_LEVEL;
+	cd->show_timestamp             = PT_INITIAL_SHOW_TIME_STAMP;
+	scnprintf(cd->core_id, 20, "%s%d", PT_CORE_NAME, core_number++);
+	cd->hw_detected                = false;
+	cd->pip2_prot_active           = false;
+	cd->pip2_send_user_cmd         = false;
+	cd->bl_pip_ver_ready           = false;
+	cd->app_pip_ver_ready          = false;
+	cd->pip2_cmd_tag_seq           = 0x08; /* PIP2 TAG=1 and 3 bit SEQ=0 */
+	cd->get_param_id               = 0;
+	cd->watchdog_enabled           = 0;
+	cd->startup_retry_count        = 0;
+	cd->core_probe_complete        = 0;
+	cd->fw_system_mode             = FW_SYS_MODE_BOOT;
+	cd->pip_cmd_timeout            = PT_PIP_CMD_DEFAULT_TIMEOUT;
+	cd->pip_cmd_timeout_default    = PT_PIP_CMD_DEFAULT_TIMEOUT;
+	cd->flashless_dut              = 0;
+	cd->flashless_auto_bl          = PT_SUPPRESS_AUTO_BL;
+	cd->bl_with_no_int             = 0;
+	cd->cal_cache_in_host          = PT_FEATURE_DISABLE;
+	cd->multi_chip                 = PT_FEATURE_DISABLE;
+	cd->tthe_hid_usb_format        = PT_FEATURE_DISABLE;
+	cd->sleep_state			= SS_SLEEP_NONE;
+	cd->quick_boot			= false;
+	cd->drv_debug_suspend          = false;
+	cd->touch_offload = false;
+
+	if (cd->cpdata->config_dut_generation == CONFIG_DUT_PIP2_CAPABLE) {
+		cd->set_dut_generation = true;
+		cd->active_dut_generation = DUT_PIP2_CAPABLE;
+	} else if (cd->cpdata->config_dut_generation == CONFIG_DUT_PIP1_ONLY) {
+		cd->set_dut_generation = true;
+		cd->active_dut_generation = DUT_PIP1_ONLY;
+	} else {
+		cd->set_dut_generation = false;
+		cd->active_dut_generation = DUT_UNKNOWN;
+	}
+	/* Initialize with platform data */
+	cd->watchdog_force_stop        = cd->cpdata->watchdog_force_stop;
+	cd->watchdog_interval          = PT_WATCHDOG_TIMEOUT;
+	cd->hid_cmd_state                 = 1;
+	cd->fw_updating                   = false;
+	cd->multi_chip                    = 0;
+
+#ifdef TTDL_DIAGNOSTICS
+	cd->t_refresh_active              = 0;
+	cd->t_refresh_count               = 0;
+	cd->pip2_crc_error_count          = 0;
+	cd->wd_xres_count                 = 0;
+	cd->bl_retry_packet_count         = 0;
+	cd->file_erase_timeout_count      = 0;
+	cd->show_tt_data                  = false;
+	cd->flush_bus_type                = PT_FLUSH_BUS_BASED_ON_LEN;
+	cd->err_gpio                      = 0;
+	cd->err_gpio_type                 = PT_ERR_GPIO_NONE;
+	cd->ttdl_bist_select              = 0x07;
+	cd->force_pip2_seq                = 0;
+#endif /* TTDL_DIAGNOSTICS */
+
+	memset(cd->pip2_us_file_path, 0, PT_MAX_PATH_SIZE);
+	memcpy(cd->pip2_us_file_path, PT_PIP2_BIN_FILE_PATH,
+		sizeof(PT_PIP2_BIN_FILE_PATH));
+	pt_init_hid_descriptor(&cd->hid_desc);
+	/* Read and store the descriptor lengths */
+	cd->hid_core.hid_report_desc_len =
+	    le16_to_cpu(cd->hid_desc.report_desc_len);
+	cd->hid_core.hid_max_input_len =
+	    le16_to_cpu(cd->hid_desc.max_input_len);
+	cd->hid_core.hid_max_output_len =
+	    le16_to_cpu(cd->hid_desc.max_output_len);
+	/* Initialize mutexes and spinlocks */
+	mutex_init(&cd->module_list_lock);
+	mutex_init(&cd->system_lock);
+	mutex_init(&cd->sysfs_lock);
+	mutex_init(&cd->ttdl_restart_lock);
+	mutex_init(&cd->firmware_class_lock);
+	spin_lock_init(&cd->spinlock);
+
+	/* Initialize module list */
+	INIT_LIST_HEAD(&cd->module_list);
+
+	/* Initialize attention lists */
+	for (type = 0; type < PT_ATTEN_NUM_ATTEN; type++)
+		INIT_LIST_HEAD(&cd->atten_list[type]);
+
+	/* Initialize parameter list */
+	INIT_LIST_HEAD(&cd->param_list);
+
+	/* Initialize wait queue */
+	init_waitqueue_head(&cd->wait_q);
+	rc = pt_ts_pinctrl_init(cd);
+	if (!rc && cd->ts_pinctrl) {
+		/*
+		 * Pinctrl handle is optional. If pinctrl handle is found
+		 * let pins to be configured in active state. If not
+		 * found continue further without error.
+		 */
+		rc = pinctrl_select_state(cd->ts_pinctrl,
+					cd->pinctrl_state_active);
+		if (rc < 0)
+			dev_err(&client->dev, "failed to select pin to active state\n");
+	}
+	rc = pt_get_regulator(cd, true);
+	if (rc) {
+		dev_err(&client->dev, "Failed to get voltage regulators\n");
+		goto error_alloc_data;
+	}
+
+	rc = pt_enable_regulator(cd, true);
+	if (rc) {
+		dev_err(dev, "Failed to enable regulators: rc=%d\n", rc);
+		goto error_get_regulator;
+	}
+
+	/* Initialize works */
+	INIT_WORK(&cd->enum_work, pt_enum_work_function);
+	INIT_WORK(&cd->ttdl_restart_work, pt_restart_work_function);
+	INIT_WORK(&cd->watchdog_work, pt_watchdog_work);
+
+	/* Initialize HID specific data */
+	cd->hid_core.hid_vendor_id = (cd->cpdata->vendor_id) ?
+		cd->cpdata->vendor_id : HID_VENDOR_ID;
+	cd->hid_core.hid_product_id = (cd->cpdata->product_id) ?
+		cd->cpdata->product_id : HID_APP_PRODUCT_ID;
+	cd->hid_core.hid_desc_register =
+		cpu_to_le16(cd->cpdata->hid_desc_register);
+
+	/* Set platform easywake value */
+	cd->easy_wakeup_gesture = cd->cpdata->easy_wakeup_gesture;
+
+#if defined(CONFIG_DRM) || defined(CONFIG_PANEL_NOTIFIER)
+	/* Setup active dsi panel */
+	active_panel = cd->cpdata->active_panel;
+#endif
+
+	/* Set platform panel_id value */
+	cd->panel_id_support = cd->cpdata->panel_id_support;
+
+	if (cd->panel_id_support & PT_PANEL_ID_BY_SYS_INFO)
+		/* Set Panel ID to default to 0 */
+		cd->pid_for_loader = PT_PANEL_ID_DEFAULT;
+	else
+		/* Set Panel ID to Not Enabled */
+		cd->pid_for_loader = PANEL_ID_NOT_ENABLED;
+	/* Initialize hw_version default to FFFF.FFFF.FF */
+	snprintf(cd->hw_version, HW_VERSION_LEN_MAX, "FFFF.FFFF.FF");
+
+	dev_set_drvdata(dev, cd);
+	/* PtSBC builds will call this function in pt_probe_complete() */
+	pt_add_core(dev);
+
+	rc = sysfs_create_group(&dev->kobj, &early_attr_group);
+	if (rc) {
+		pt_debug(cd->dev, DL_ERROR, "%s:create early attrs failed\n",
+			__func__);
+		goto error_enable_regulator;
+	}
+	/*
+	 * Save the pointer to a global value, which will be used
+	 * in ttdl_restart function
+	 */
+	cd->bus_ops = ops;
+
+	glink_touch_channel_init(&touch_notify_glink_pt_channel_state, &glink_touch_pt_rx_msg);
+
+	/*
+	 * When the IRQ GPIO is not direclty accessible and no function is
+	 * defined to get the IRQ status, the IRQ passed in must be assigned
+	 * directly as the gpio_to_irq will not work. e.g. CHROMEOS
+	 */
+	if (!cd->cpdata->irq_stat) {
+		cd->irq = irq;
+		pt_debug(cd->dev, DL_ERROR, "%s:No irq_stat, Set cd->irq = %d\n",
+			__func__, cd->irq);
+	}
+	/* Call platform init function before setting up the GPIO's */
+	if (cd->cpdata->init) {
+		pt_debug(cd->dev, DL_INFO, "%s: Init HW\n", __func__);
+		rc = cd->cpdata->init(cd->cpdata, PT_MT_POWER_ON, cd->dev);
+	} else {
+		pt_debug(cd->dev, DL_ERROR, "%s: No HW INIT function\n",
+			__func__);
+		rc = 0;
+	}
+	if (rc < 0) {
+		pt_debug(cd->dev, DL_ERROR, "%s: HW Init fail r=%d\n",
+			__func__, rc);
+	}
+	/* Power on any needed regulator(s) */
+	if (cd->cpdata->setup_power) {
+		pt_debug(cd->dev, DL_INFO, "%s: Device power on!\n", __func__);
+		rc = cd->cpdata->setup_power(cd->cpdata,
+			PT_MT_POWER_ON, cd->dev);
+	} else {
+		pt_debug(cd->dev, DL_ERROR, "%s: No setup power function\n",
+			__func__);
+		rc = 0;
+	}
+	if (rc < 0)
+		pt_debug(cd->dev, DL_ERROR, "%s: Setup power on fail r=%d\n",
+			__func__, rc);
+
+#ifdef TTDL_DIAGNOSTICS
+	cd->watchdog_irq_stuck_count = 0;
+	cd->bus_transmit_error_count = 0;
+#endif /* TTDL_DIAGNOSTICS */
+		if (cd->cpdata->detect) {
+			pt_debug(cd->dev, DL_INFO, "%s: Detect HW\n", __func__);
+			rc = cd->cpdata->detect(cd->cpdata, cd->dev,
+					pt_platform_detect_read);
+			if (!rc) {
+				cd->hw_detected = true;
+				pt_debug(cd->dev, DL_INFO,
+					"%s: HW detected\n", __func__);
+			} else {
+				cd->hw_detected = false;
+				pt_debug(cd->dev, DL_INFO,
+					"%s: No HW detected\n", __func__);
+				rc = -ENODEV;
+				goto error_detect;
+			}
+		} else {
+			pt_debug(dev, DL_ERROR,
+				"%s: PARADE No HW detect function pointer\n",
+				__func__);
+			/*
+			 * "hw_reset" is not needed in the "if" statement,
+			 * because "hw_reset" is already included in "hw_detect"
+			 * function.
+			 */
+			rc = pt_hw_hard_reset(cd);
+			if (rc)
+				pt_debug(cd->dev, DL_ERROR,
+					"%s: FAILED to execute HARD reset\n",
+					__func__);
+		}
+
+	if (cd->cpdata->setup_irq) {
+		pt_debug(cd->dev, DL_INFO, "%s: setup IRQ\n", __func__);
+		rc = cd->cpdata->setup_irq(cd->cpdata, PT_MT_IRQ_REG, cd->dev);
+		if (rc) {
+			pt_debug(dev, DL_ERROR,
+				"%s: Error, couldn't setup IRQ\n", __func__);
+			goto error_setup_irq;
+		}
+	} else {
+		pt_debug(dev, DL_ERROR,
+				"%s: IRQ function pointer not setup\n",
+				__func__);
+		goto error_setup_irq;
+	}
+
+#if (KERNEL_VERSION(4, 14, 0) > LINUX_VERSION_CODE)
+	setup_timer(&cd->watchdog_timer, pt_watchdog_timer,
+			(unsigned long)cd);
+#else
+	timer_setup(&cd->watchdog_timer, pt_watchdog_timer, 0);
+#endif
+	pt_stop_wd_timer(cd);
+#ifdef TTHE_TUNER_SUPPORT
+	mutex_init(&cd->tthe_lock);
+	cd->tthe_debugfs = debugfs_create_file(PT_TTHE_TUNER_FILE_NAME,
+			0644, NULL, cd, &tthe_debugfs_fops);
+#endif
+	rc = device_init_wakeup(dev, 1);
+	if (rc < 0)
+		pt_debug(dev, DL_ERROR, "%s: Error, device_init_wakeup rc:%d\n",
+			__func__, rc);
+
+	if (!enable_irq_wake(cd->irq)) {
+		cd->irq_wake = 1;
+		pt_debug(cd->dev, DL_WARN, "%s Device MAY wakeup\n", __func__);
+	}
+	pm_runtime_get_noresume(dev);
+	pm_runtime_set_active(dev);
+	pm_runtime_enable(dev);
+	/* If IRQ asserted, read out all from buffer to release INT pin */
+	if (cd->cpdata->irq_stat) {
+		pt_flush_bus_if_irq_asserted(cd, PT_FLUSH_BUS_BASED_ON_LEN);
+	} else {
+		/* Force a read in case the reset sentinel already arrived */
+		rc = pt_read_input(cd);
+		if (!rc)
+			pt_parse_input(cd);
+	}
+	/* Without sleep DUT is not ready and will NAK the first write */
+	msleep(150);
+	/* Attempt to set the DUT generation if not yet set */
+	if (cd->active_dut_generation == DUT_UNKNOWN) {
+		if (cd->bl_pip_ver_ready ||
+			(cd->app_pip_ver_ready &&
+			IS_PIP_VER_GE(&cd->sysinfo, 1, 12))) {
+			cd->active_dut_generation = DUT_PIP2_CAPABLE;
+			pt_debug(dev, DL_INFO, "%s: dut generation is %d\n",
+				__func__, cd->active_dut_generation);
+		} else {
+			rc = _pt_detect_dut_generation(cd->dev,
+				&status, &cd->active_dut_generation,
+				&cd->mode);
+			if ((cd->active_dut_generation == DUT_UNKNOWN)
+				|| rc) {
+				pt_debug(cd->dev, DL_ERROR,
+					" === DUT Gen unknown, Skip Enum ===\n");
+				goto skip_enum;
+			}
+		}
+	}
+	_pt_request_active_pip_protocol(cd->dev, PT_CORE_CMD_PROTECTED,
+		&pip_ver_major, &pip_ver_minor);
+	if (pip_ver_major == 2) {
+		cd->bl_pip_ver_ready = true;
+		pt_debug(dev, DL_ERROR,
+			" === PIP2.%d Detected, Attempt to launch APP ===\n",
+			pip_ver_minor);
+		cd->hw_detected = true;
+	} else if (pip_ver_major == 1) {
+		cd->app_pip_ver_ready = true;
+		pt_debug(dev, DL_ERROR,
+			" === PIP1.%d Detected ===\n", pip_ver_minor);
+		cd->hw_detected = true;
+	} else {
+		cd->sysinfo.ttdata.pip_ver_major = 0;
+		cd->sysinfo.ttdata.pip_ver_minor = 0;
+		cd->app_pip_ver_ready = false;
+		cd->hw_detected = false;
+		pt_debug(dev, DL_ERROR,
+			" === PIP Version Not Detected, Skip Enum ===\n");
+		/* For legacy DUTS proceed, enum will attempt to launch app */
+		if (cd->active_dut_generation != DUT_PIP1_ONLY)
+			goto skip_enum;
+	}
+	rc = pt_enum_with_dut(cd, false, &status);
+	pt_debug(dev, DL_ERROR, "%s: cd->startup_status=0x%04X status=0x%04X\n",
+		__func__, cd->startup_status, status);
+	if (rc == -ENODEV) {
+		pt_debug(cd->dev, DL_ERROR,
+			"%s: Enumeration Failed r=%d\n", __func__, rc);
+		/* For PtSBC don't error out, allow TTDL to stay up */
+		rc = -EPROBE_DEFER;
+		goto error_after_startup;
+	}
+	/* Suspend scanning until probe is complete to avoid asyc touches */
+	pt_pip_suspend_scanning_(cd);
+
+	if (cd->hw_detected) {
+		pt_debug(dev, DL_INFO, "%s: Add sysfs interfaces\n",
+			__func__);
+		rc = add_sysfs_interfaces(dev);
+		if (rc < 0) {
+			pt_debug(dev, DL_ERROR,
+				"%s: Error, fail sysfs init\n", __func__);
+			goto error_after_startup;
+		}
+	} else {
+		pt_debug(dev, DL_ERROR,
+			"%s: No HW detected, sysfs interfaces not added\n",
+			__func__);
+	}
+skip_enum:
+	pm_runtime_put_sync(dev);
+
+	pt_debug(dev, DL_INFO, "%s: Probe: MT, BTN\n", __func__);
+	rc = pt_mt_probe(dev);
+	if (rc < 0) {
+		pt_debug(dev, DL_ERROR, "%s: Error, fail mt probe\n",
+			__func__);
+		goto error_after_sysfs_create;
+	}
+	rc = pt_btn_probe(dev);
+	if (rc < 0) {
+		pt_debug(dev, DL_ERROR, "%s: Error, fail btn probe\n",
+			__func__);
+		goto error_after_startup_mt;
+	}
+	pt_probe_modules(cd);
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	pt_setup_early_suspend(cd);
+#elif defined(CONFIG_PANEL_NOTIFIER)
+	pt_debug(dev, DL_ERROR, "%s: Probe: Setup Panel Event notifier\n", __func__);
+	pt_setup_panel_event_notifier(cd);
+	INIT_WORK(&cd->resume_offload_work, pt_resume_offload_work);
+	INIT_WORK(&cd->suspend_offload_work, pt_suspend_offload_work);
+#elif defined(CONFIG_DRM)
+	pt_debug(dev, DL_ERROR, "%s: Probe: Setup drm notifier\n", __func__);
+	pt_setup_drm_notifier(cd);
+	INIT_WORK(&cd->resume_offload_work, pt_resume_offload_work);
+	INIT_WORK(&cd->suspend_offload_work, pt_suspend_offload_work);
+#elif defined(CONFIG_FB)
+	pt_setup_fb_notifier(cd);
+#endif
+
+#ifdef NEED_SUSPEND_NOTIFIER
+	cd->pm_notifier.notifier_call = pt_pm_notifier;
+	register_pm_notifier(&cd->pm_notifier);
+#endif
+	pt_pip_resume_scanning_(cd);
+	mutex_lock(&cd->system_lock);
+	cd->startup_status |= status;
+	cd->core_probe_complete = 1;
+	mutex_unlock(&cd->system_lock);
+
+	pt_core_state = STATE_RESUME;
+	pt_debug(dev, DL_ERROR, "%s: TTDL Core Probe Completed Successfully\n",
+		__func__);
+	return 0;
+
+
+error_after_startup_mt:
+	pr_err("%s PARADE error_after_startup_mt\n", __func__);
+	pt_mt_release(dev);
+error_after_sysfs_create:
+	pr_err("%s PARADE error_after_sysfs_create\n", __func__);
+	pm_runtime_disable(dev);
+#if (KERNEL_VERSION(3, 16, 0) > LINUX_VERSION_CODE)
+	device_wakeup_disable(dev);
+#endif
+	device_init_wakeup(dev, 0);
+	cancel_work_sync(&cd->enum_work);
+	pt_stop_wd_timer(cd);
+	pt_free_si_ptrs(cd);
+	remove_sysfs_interfaces(dev);
+
+error_after_startup:
+	pr_err("%s PARADE error_after_startup\n", __func__);
+	del_timer(&cd->watchdog_timer);
+	if (cd->cpdata->setup_irq)
+		cd->cpdata->setup_irq(cd->cpdata, PT_MT_IRQ_FREE, dev);
+error_setup_irq:
+error_detect:
+	if (cd->cpdata->init)
+		cd->cpdata->init(cd->cpdata, PT_MT_POWER_OFF, dev);
+	if (cd->cpdata->setup_power)
+		cd->cpdata->setup_power(cd->cpdata, PT_MT_POWER_OFF, dev);
+	sysfs_remove_group(&dev->kobj, &early_attr_group);
+error_enable_regulator:
+	pt_del_core(dev);
+	dev_set_drvdata(dev, NULL);
+	pt_enable_regulator(cd, false);
+error_get_regulator:
+	pt_get_regulator(cd, false);
+error_alloc_data:
+	kfree(cd);
+error_no_pdata:
+	pr_err("%s failed.\n", __func__);
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_device_entry
+ *
+ * SUMMARY: Wrapper function of pt_core_suspend_() to help avoid TP from being
+ *  woke up or put to sleep based on Kernel power state even when the display
+ *  is off based on the check of TTDL core platform flag.
+ *
+ * RETURN:
+ *        0 = success
+ *       !0 = failure
+ *
+ * PARAMETERS:
+ *      *dev  - pointer to core device
+ ******************************************************************************/
+#ifdef PT_AMBIENT_MODE
+int pt_device_entry(struct device *dev,
+				u16 irq, size_t xfer_buf_size)
+{
+		struct pt_core_data *cd = dev_get_drvdata(dev);
+		struct pt_platform_data *pdata = dev_get_platdata(dev);
+		struct i2c_client *client = to_i2c_client(dev);
+		int rc = 0;
+		void *glink_pt_send_msg;
+		int glink_touch_exit = TOUCH_EXIT;
+
+		pt_debug(dev, DL_INFO, "%s: Start pt_device_entry\n", __func__);
+
+		cd->dev  	= dev;
+		cd->pdata   = pdata;
+		cd->cpdata  = pdata->core_pdata;
+
+		glink_pt_send_msg = &glink_touch_exit;
+		pt_debug(dev, DL_INFO, "[touch]glink_pt_send_msg = %d\n", glink_pt_send_msg);
+		glink_touch_tx_msg(glink_pt_send_msg, TOUCH_MSG_SIZE);
+
+		msleep(150);
+
+		if (!rc && cd->ts_pinctrl) {
+				/*
+				 * Pinctrl handle is optional. If pinctrl handle is found
+				 * let pins to be configured in active state. If not
+				 * found continue further without error.
+				 */
+			rc = pinctrl_select_state(cd->ts_pinctrl, cd->pinctrl_state_active);
+			if (rc < 0)
+				dev_err(&client->dev, "failed to select pin to active state\n");
+		}
+
+		/* Set platform easywake value */
+		cd->easy_wakeup_gesture = cd->cpdata->easy_wakeup_gesture;
+
+	   /*
+		* When the IRQ GPIO is not direclty accessible and no function is
+		* defined to get the IRQ status, the IRQ passed in must be assigned
+		* directly as the gpio_to_irq will not work. e.g. CHROMEOS
+		*/
+
+		if (!cd->cpdata->irq_stat) {
+			cd->irq = irq;
+			pt_debug(cd->dev, DL_ERROR, "%s:No irq_stat, Set cd->irq = %d\n", __func__, cd->irq);
+		}
+
+		/* Call platform init function before setting up the GPIO's */
+		if (cd->cpdata->init) {
+			pt_debug(cd->dev, DL_INFO, "%s: Init HW\n", __func__);
+			rc = cd->cpdata->init(cd->cpdata, PT_MT_POWER_ON, cd->dev);
+		} else {
+			pt_debug(cd->dev, DL_ERROR, "%s: No HW INIT function\n", __func__);
+			rc = 0;
+		}
+		if (rc < 0) {
+			pt_debug(cd->dev, DL_ERROR, "%s: HW Init fail r=%d\n", __func__, rc);
+		}
+
+		/* Power on any needed regulator(s) */
+		if (cd->cpdata->setup_power) {
+				pt_debug(cd->dev, DL_INFO, "%s: Device power on!\n", __func__);
+				rc = cd->cpdata->setup_power(cd->cpdata, PT_MT_POWER_ON, cd->dev);
+		} else {
+				pt_debug(cd->dev, DL_ERROR, "%s: No setup power function\n", __func__);
+				rc = 0;
+		}
+		if (rc < 0)
+			pt_debug(cd->dev, DL_ERROR, "%s: Setup power on fail r=%d\n", __func__, rc);
+
+		if (cd->cpdata->detect) {
+			pt_debug(cd->dev, DL_INFO, "%s: Detect HW\n", __func__);
+			rc = cd->cpdata->detect(cd->cpdata, cd->dev, pt_platform_detect_read);
+			if (!rc) {
+					cd->hw_detected = true;
+					pt_debug(cd->dev, DL_INFO, "%s: HW detected\n", __func__);
+			} else {
+					cd->hw_detected = false;
+					pt_debug(cd->dev, DL_INFO, "%s: No HW detected\n", __func__);
+					rc = -ENODEV;
+					goto pt_error_detect;
+			}
+		} else {
+				pt_debug(dev, DL_ERROR,	"%s: PARADE No HW detect function pointer\n", __func__);
+				/*
+				 * "hw_reset" is not needed in the "if" statement,
+				 * because "hw_reset" is already included in "hw_detect"
+				 * function.
+				 */
+				rc = pt_hw_hard_reset(cd);
+				if (rc)
+					pt_debug(cd->dev, DL_ERROR,	"%s: FAILED to execute HARD reset\n", __func__);
+		}
+
+			if (cd->cpdata->setup_irq) {
+				pt_debug(cd->dev, DL_INFO, "%s: setup IRQ\n", __func__);
+				rc = cd->cpdata->setup_irq(cd->cpdata, PT_MT_IRQ_REG, cd->dev);
+				if (rc) {
+					pt_debug(dev, DL_ERROR,	"%s: Error, couldn't setup IRQ\n", __func__);
+					goto pt_error_setup_irq;
+				}
+			} else {
+				pt_debug(dev, DL_ERROR,	"%s: IRQ function pointer not setup\n", __func__);
+				goto pt_error_setup_irq;
+			}
+
+			rc = device_init_wakeup(dev, 1);
+			if (rc < 0)
+				pt_debug(dev, DL_ERROR, "%s: Error, device_init_wakeup rc:%d\n", __func__, rc);
+
+			if (!enable_irq_wake(cd->irq)) {
+				cd->irq_wake = 1;
+				pt_debug(cd->dev, DL_WARN, "%s Device MAY wakeup\n", __func__);
+			}
+
+			pm_runtime_get_noresume(dev);
+			pm_runtime_set_active(dev);
+			pm_runtime_enable(dev);
+
+			/* Without sleep DUT is not ready and will NAK the first write */
+			msleep(150);
+
+			pm_runtime_put_sync(dev);
+
+#if defined(CONFIG_PANEL_NOTIFIER)
+		/* Setup active dsi panel */
+		active_panel = cd->cpdata->active_panel;
+
+
+		pt_debug(dev, DL_ERROR, "%s: Probe: Setup Panel Event notifier\n", __func__);
+		pt_setup_panel_event_notifier(cd);
+#endif
+
+		mutex_lock(&cd->system_lock);
+		cd->core_probe_complete = 1;
+		mutex_unlock(&cd->system_lock);
+
+		pt_debug(dev, DL_INFO, "%s: ####TTDL Core Device Probe Completed Successfully\n", __func__);
+		pt_core_state = STATE_RESUME;
+		return 0;
+
+pt_error_setup_irq:
+		device_init_wakeup(dev, 0);
+pt_error_detect:
+		if (cd->cpdata->init)
+			cd->cpdata->init(cd->cpdata, PT_MT_POWER_OFF, dev);
+		if (cd->cpdata->setup_power)
+			cd->cpdata->setup_power(cd->cpdata, PT_MT_POWER_OFF, dev);
+		return rc;
+}
+#endif
+
+EXPORT_SYMBOL_GPL(pt_probe);
+
+/*******************************************************************************
+ * FUNCTION: pt_release
+ *
+ * SUMMARY: This function does the following cleanup:
+ *	- Releases all probed modules
+ *	- Stops the watchdog
+ *	- Cancels all pending work tasks
+ *	- Removes all created sysfs nodes
+ *	- Removes all created debugfs nodes
+ *	- Frees the IRQ
+ *	- Deletes the core
+ *	- Frees all pointers and HID reports
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*cd - pointer to the core data structure
+ ******************************************************************************/
+int pt_release(struct pt_core_data *cd)
+{
+	struct device *dev = cd->dev;
+
+	/*
+	 * Suspend the device before freeing the startup_work and stopping
+	 * the watchdog since sleep function restarts watchdog on failure
+	 */
+	pm_runtime_suspend(dev);
+	pm_runtime_disable(dev);
+
+	/*
+	 * Any 'work' that can trigger a new thread should be canceled first.
+	 * The watchdog is also stopped again because another thread could have
+	 * restarted it. The irq_work is cancelled last because it is used for
+	 * all I2C/SPI communication.
+	 */
+	pt_stop_wd_timer(cd);
+	call_atten_cb(cd, PT_ATTEN_CANCEL_LOADER, 0);
+	cancel_work_sync(&cd->ttdl_restart_work);
+	cancel_work_sync(&cd->enum_work);
+	cancel_work_sync(&cd->resume_offload_work);
+	cancel_work_sync(&cd->suspend_offload_work);
+	cancel_work_sync(&cd->resume_work);
+	cancel_work_sync(&cd->suspend_work);
+	destroy_workqueue(cd->pt_workqueue);
+
+	pt_stop_wd_timer(cd);
+
+	pt_release_modules(cd);
+	pt_proximity_release(dev);
+	pt_btn_release(dev);
+	pt_mt_release(dev);
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	unregister_early_suspend(&cd->es);
+#elif defined(CONFIG_PANEL_NOTIFIER)
+	if (active_panel)
+		panel_event_notifier_unregister(cd->entry);
+#elif defined(CONFIG_DRM)
+	if (active_panel)
+		drm_panel_notifier_unregister(active_panel, &cd->fb_notifier);
+#elif defined(CONFIG_FB)
+	fb_unregister_client(&cd->fb_notifier);
+#endif
+
+#ifdef NEED_SUSPEND_NOTIFIER
+	unregister_pm_notifier(&cd->pm_notifier);
+#endif
+
+#if (KERNEL_VERSION(3, 16, 0) > LINUX_VERSION_CODE)
+	device_wakeup_disable(dev);
+#endif
+	device_init_wakeup(dev, 0);
+
+#ifdef TTHE_TUNER_SUPPORT
+	mutex_lock(&cd->tthe_lock);
+	cd->tthe_exit = 1;
+	wake_up(&cd->wait_q);
+	mutex_unlock(&cd->tthe_lock);
+	debugfs_remove(cd->tthe_debugfs);
+#endif
+
+	sysfs_remove_group(&dev->kobj, &early_attr_group);
+
+	remove_sysfs_interfaces(dev);
+
+	disable_irq_nosync(cd->irq);
+	if (cd->cpdata->setup_irq)
+		cd->cpdata->setup_irq(cd->cpdata, PT_MT_IRQ_FREE, dev);
+	if (cd->cpdata->init)
+		cd->cpdata->init(cd->cpdata, PT_MT_POWER_OFF, dev);
+	if (cd->cpdata->setup_power)
+		cd->cpdata->setup_power(cd->cpdata, PT_MT_POWER_OFF, dev);
+	dev_set_drvdata(dev, NULL);
+	pt_del_core(dev);
+	if (cd->vcc_i2c)
+		regulator_set_load(cd->vcc_i2c, 0);
+
+	if (cd->vdd)
+		regulator_set_load(cd->vdd, 0);
+	pt_enable_regulator(cd, false);
+	pt_get_regulator(cd, false);
+	pt_free_si_ptrs(cd);
+	kfree(cd);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(pt_release);
+
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Parade TrueTouch(R) Standard Product Core Driver");
+MODULE_AUTHOR("Parade Technologies <[email protected]>");

+ 230 - 0
qcom/opensource/touch-drivers/pt/pt_core.h

@@ -0,0 +1,230 @@
+/*
+ * pt_core.h
+ * Parade TrueTouch(TM) Standard Product Core Module.
+ * For use with Parade touchscreen controllers.
+ * Supported parts include:
+ * TMA5XX
+ * TMA448
+ * TMA445A
+ * TT21XXX
+ * TT31XXX
+ * TT4XXXX
+ * TT7XXX
+ * TC3XXX
+ *
+ * Copyright (C) 2015-2020 Parade Technologies
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * Contact Parade Technologies at www.parade.com <[email protected]>
+ */
+
+#ifndef _LINUX_PT_CORE_H
+#define _LINUX_PT_CORE_H
+
+#include <linux/stringify.h>
+#include <drm/drm_panel.h>
+
+#define PT_I2C_NAME "pt_i2c_adapter"
+#define PT_SPI_NAME "pt_spi_adapter"
+
+#define PT_CORE_NAME "pt_core"
+#define PT_MT_NAME "pt_mt"
+#define PT_BTN_NAME "pt_btn"
+#define PT_PROXIMITY_NAME "pt_proximity"
+
+#define PT_DRIVER_NAME TTDL
+#define PT_DRIVER_MAJOR 04
+#define PT_DRIVER_MINOR 11
+
+#define PT_DRIVER_REVCTRL 977092
+
+#define PT_DRIVER_VERSION		    \
+__stringify(PT_DRIVER_NAME)		    \
+"." __stringify(PT_DRIVER_MAJOR)	    \
+"." __stringify(PT_DRIVER_MINOR)	    \
+"." __stringify(PT_DRIVER_REVCTRL)
+
+#define PT_DRIVER_DATE "20201210"
+
+/* abs settings */
+#define PT_IGNORE_VALUE             -1
+
+enum pt_core_platform_flags {
+	PT_CORE_FLAG_NONE,
+	PT_CORE_FLAG_POWEROFF_ON_SLEEP = 0x02,
+	PT_CORE_FLAG_RESTORE_PARAMETERS = 0x04,
+	PT_CORE_FLAG_DEEP_STANDBY = 0x08,
+	PT_CORE_FLAG_SKIP_SYS_SLEEP = 0x10,
+	PT_CORE_FLAG_SKIP_RUNTIME = 0x20,
+	PT_CORE_FLAG_SKIP_RESUME = 0x40,
+};
+
+enum pt_core_platform_easy_wakeup_gesture {
+	PT_CORE_EWG_NONE,
+	PT_CORE_EWG_TAP_TAP,
+	PT_CORE_EWG_TWO_FINGER_SLIDE,
+	PT_CORE_EWG_RESERVED,
+	PT_CORE_EWG_WAKE_ON_INT_FROM_HOST = 0xFF,
+};
+
+enum pt_loader_platform_flags {
+	PT_LOADER_FLAG_NONE,
+	PT_LOADER_FLAG_CALIBRATE_AFTER_FW_UPGRADE,
+	/* Use CONFIG_VER field in TT_CFG to decide TT_CFG update */
+	PT_LOADER_FLAG_CHECK_TTCONFIG_VERSION,
+	PT_LOADER_FLAG_CALIBRATE_AFTER_TTCONFIG_UPGRADE,
+};
+
+enum CONFIG_DUT_GENERATION {
+	CONFIG_DUT_AUTO_DETECT          = 0x00,
+	CONFIG_DUT_PIP1_ONLY            = 0x01,
+	CONFIG_DUT_PIP2_CAPABLE         = 0x02,
+};
+
+enum pt_core_platform_panel_id_flags {
+	PT_PANEL_ID_DISABLE         = 0x00,
+	PT_PANEL_ID_BY_BL           = 0x01,
+	PT_PANEL_ID_BY_SYS_INFO     = 0x02,
+	PT_PANEL_ID_BY_MFG_DATA     = 0x04,
+};
+
+struct touch_settings {
+	const uint8_t   *data;
+	uint32_t         size;
+	uint8_t         tag;
+};
+
+struct pt_touch_firmware {
+	const uint8_t *img;
+	uint32_t size;
+	const uint8_t *ver;
+	uint8_t vsize;
+	uint8_t panel_id;
+};
+
+struct pt_touch_config {
+	struct touch_settings *param_regs;
+	struct touch_settings *param_size;
+	const uint8_t *fw_ver;
+	uint8_t fw_vsize;
+	uint8_t panel_id;
+};
+
+struct pt_loader_platform_data {
+	struct pt_touch_firmware *fw;
+	struct pt_touch_config *ttconfig;
+	struct pt_touch_firmware **fws;
+	struct pt_touch_config **ttconfigs;
+	u32 flags;
+};
+
+typedef int (*pt_platform_read) (struct device *dev, void *buf, int size);
+
+#define PT_TOUCH_SETTINGS_MAX 32
+
+struct pt_core_platform_data {
+	int irq_gpio;
+	u32 irq_gpio_flags;
+	int rst_gpio;
+	u32 rst_gpio_flags;
+	int ddi_rst_gpio;
+	int vddi_gpio;
+	int vcc_gpio;
+	int avdd_gpio;
+	int avee_gpio;
+	int level_irq_udelay;
+	u16 hid_desc_register;
+	u16 vendor_id;
+	u16 product_id;
+
+	int (*xres)(struct pt_core_platform_data *pdata,
+		struct device *dev);
+	int (*init)(struct pt_core_platform_data *pdata,
+		int on, struct device *dev);
+	int (*power)(struct pt_core_platform_data *pdata,
+		int on, struct device *dev, atomic_t *ignore_irq);
+	int (*detect)(struct pt_core_platform_data *pdata,
+		struct device *dev, pt_platform_read read);
+	int (*irq_stat)(struct pt_core_platform_data *pdata,
+		struct device *dev);
+	int (*setup_power)(struct pt_core_platform_data *pdata,
+		int on, struct device *dev);
+	int (*setup_irq)(struct pt_core_platform_data *pdata,
+		int on, struct device *dev);
+	struct touch_settings *sett[PT_TOUCH_SETTINGS_MAX];
+	u32 flags;
+	u8 easy_wakeup_gesture;
+	u8 config_dut_generation;
+	u8 watchdog_force_stop;
+	u8 panel_id_support;
+
+	struct device_node *node;
+	struct pinctrl *pinctrl;
+	struct pinctrl_state *pins_active;
+	struct pinctrl_state *pins_suspend;
+	struct pinctrl_state *pins_release;
+
+	struct  drm_panel *active_panel;
+};
+
+struct touch_framework {
+	const int16_t  *abs;
+	uint8_t         size;
+	uint8_t         enable_vkeys;
+} __packed;
+
+enum pt_mt_platform_power_state {
+	PT_MT_POWER_OFF = 0x00,
+	PT_MT_POWER_ON  = 0x01
+};
+
+enum pt_mt_platform_irq_state {
+	PT_MT_IRQ_FREE = 0x00,
+	PT_MT_IRQ_REG  = 0x01
+};
+
+enum pt_mt_platform_flags {
+	PT_MT_FLAG_NONE,
+	PT_MT_FLAG_HOVER = 0x04,
+	PT_MT_FLAG_FLIP = 0x08,
+	PT_MT_FLAG_INV_X = 0x10,
+	PT_MT_FLAG_INV_Y = 0x20,
+	PT_MT_FLAG_VKEYS = 0x40,
+	PT_MT_FLAG_NO_TOUCH_ON_LO = 0x80,
+};
+
+struct pt_mt_platform_data {
+	struct touch_framework *frmwrk;
+	unsigned short flags;
+	char const *inp_dev_name;
+	int vkeys_x;
+	int vkeys_y;
+};
+
+struct pt_btn_platform_data {
+	char const *inp_dev_name;
+};
+
+struct pt_proximity_platform_data {
+	struct touch_framework *frmwrk;
+	char const *inp_dev_name;
+};
+
+struct pt_platform_data {
+	struct pt_core_platform_data *core_pdata;
+	struct pt_mt_platform_data *mt_pdata;
+	struct pt_btn_platform_data *btn_pdata;
+	struct pt_proximity_platform_data *prox_pdata;
+	struct pt_loader_platform_data *loader_pdata;
+};
+
+#endif /* _LINUX_PT_CORE_H */

+ 556 - 0
qcom/opensource/touch-drivers/pt/pt_debug.c

@@ -0,0 +1,556 @@
+/*
+ * pt_debug.c
+ * Parade TrueTouch(TM) Standard Product Debug Module.
+ * For use with Parade touchscreen controllers.
+ * Supported parts include:
+ * TMA5XX
+ * TMA448
+ * TMA445A
+ * TT21XXX
+ * TT31XXX
+ * TT4XXXX
+ * TT7XXX
+ * TC3XXX
+ *
+ * Copyright (C) 2015-2020 Parade Technologies
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * Contact Parade Technologies at www.paradetech.com <[email protected]>
+ */
+
+#include "pt_regs.h"
+
+#define PT_DEBUG_NAME "pt_debug"
+
+struct pt_debug_data {
+	struct device *dev;
+	struct pt_sysinfo *si;
+	uint32_t interrupt_count;
+	uint32_t formated_output;
+	struct mutex sysfs_lock;
+	u8 pr_buf[PT_MAX_PRBUF_SIZE];
+};
+
+static struct pt_core_commands *cmd;
+
+static struct pt_module debug_module;
+
+/*******************************************************************************
+ * FUNCTION: pt_get_debug_data
+ *
+ * SUMMARY: Inline function to get pt_debug_data pointer from debug module.
+ *
+ * RETURN:
+ *	 pointer to pt_debug_data structure
+ *
+ * PARAMETERS:
+ *	*dev          - pointer to device structure
+ ******************************************************************************/
+static inline struct pt_debug_data *pt_get_debug_data(
+		struct device *dev)
+{
+	return pt_get_module_data(dev, &debug_module);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pr_buf_op_mode
+ *
+ * SUMMARY: Formats touch/button report to pr_buf that combined xy_mode and
+ *  xy_data. The feature is required by TTHE.
+ *
+ * PARAMETERS:
+ *  *dd         - pointer to pt_debug_data structure
+ *  *pr_buf     - pointer to print buffer
+ *  *si         - pointer to sysinfo structure
+ *   cur_touch  - number of current touch
+ ******************************************************************************/
+static void pt_pr_buf_op_mode(struct pt_debug_data *dd, u8 *pr_buf,
+		struct pt_sysinfo *si, u8 cur_touch)
+{
+	const char fmt[] = "%02X ";
+	int max = (PT_MAX_PRBUF_SIZE - 1) - sizeof(PT_PR_TRUNCATED);
+	u8 report_id = si->xy_mode[2];
+	int header_size = 0;
+	int report_size = 0;
+	int total_size = 0;
+	int i, k;
+
+	if (report_id == si->desc.tch_report_id) {
+		header_size = si->desc.tch_header_size;
+		report_size = cur_touch * si->desc.tch_record_size;
+	} else if (report_id == si->desc.btn_report_id) {
+		header_size = BTN_INPUT_HEADER_SIZE;
+		report_size = BTN_REPORT_SIZE;
+	}
+	total_size = header_size + report_size;
+
+	pr_buf[0] = 0;
+	for (i = k = 0; i < header_size && i < max; i++, k += 3)
+		scnprintf(pr_buf + k, PT_MAX_PRBUF_SIZE, fmt, si->xy_mode[i]);
+
+	for (i = 0; i < report_size && i < max; i++, k += 3)
+		scnprintf(pr_buf + k, PT_MAX_PRBUF_SIZE, fmt, si->xy_data[i]);
+
+	pr_info("%s=%s%s\n", "pt_OpModeData", pr_buf,
+			total_size <= max ? "" : PT_PR_TRUNCATED);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_debug_print
+ *
+ * SUMMARY: This function prints header to show data size and data_name and
+ *  content of "pr_buf" with hex base.
+ *
+ * PARAMETERS:
+ *  *dev        - pointer to device structure
+ *  *pr_buf     - pointer to input buffer which stores the formated data
+ *  *sptr       - pointer to the buffer to print
+ *   size       - size of data elements
+ *  *data_name  - data name to print
+ ******************************************************************************/
+static void pt_debug_print(struct device *dev, u8 *pr_buf, u8 *sptr,
+		int size, const char *data_name)
+{
+	int i, j;
+	int elem_size = sizeof("XX ") - 1;
+	int max = (PT_MAX_PRBUF_SIZE - 1) / elem_size;
+	int limit = size < max ? size : max;
+
+	if (limit < 0)
+		limit = 0;
+
+	pr_buf[0] = 0;
+	for (i = j = 0; i < limit; i++, j += elem_size)
+		scnprintf(pr_buf + j, PT_MAX_PRBUF_SIZE - j, "%02X ", sptr[i]);
+
+	if (size)
+		pr_info("%s[0..%d]=%s%s\n", data_name, size - 1, pr_buf,
+			size <= max ? "" : PT_PR_TRUNCATED);
+	else
+		pr_info("%s[]\n", data_name);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_debug_formated
+ *
+ * SUMMARY: Formats and prints touch & button report.
+ *
+ * PARAMETERS:
+ *  *dev        - pointer to device structure
+ *  *pr_buf     - pointer to print buffer
+ *  *si         - pointer to sysinfo structure
+ *   cur_touch  - number of current touch
+ ******************************************************************************/
+static void pt_debug_formated(struct device *dev, u8 *pr_buf,
+		struct pt_sysinfo *si, u8 num_cur_tch)
+{
+	u8 report_id = si->xy_mode[2];
+	int header_size = 0;
+	int report_size = 0;
+	u8 data_name[] = "touch[99]";
+	int max_print_length = 20;
+	int i;
+
+	if (report_id == si->desc.tch_report_id) {
+		header_size = si->desc.tch_header_size;
+		report_size = num_cur_tch * si->desc.tch_record_size;
+	} else if (report_id == si->desc.btn_report_id) {
+		header_size = BTN_INPUT_HEADER_SIZE;
+		report_size = BTN_REPORT_SIZE;
+	}
+
+	/* xy_mode */
+	pt_debug_print(dev, pr_buf, si->xy_mode, header_size, "xy_mode");
+
+	/* xy_data */
+	if (report_size > max_print_length) {
+		pr_info("xy_data[0..%d]:\n", report_size);
+		for (i = 0; i < report_size - max_print_length;
+				i += max_print_length) {
+			pt_debug_print(dev, pr_buf, si->xy_data + i,
+					max_print_length, " ");
+		}
+		if (report_size - i)
+			pt_debug_print(dev, pr_buf, si->xy_data + i,
+					report_size - i, " ");
+	} else {
+		pt_debug_print(dev, pr_buf, si->xy_data, report_size,
+				"xy_data");
+	}
+
+	/* touches */
+	if (report_id == si->desc.tch_report_id) {
+		for (i = 0; i < num_cur_tch; i++) {
+			scnprintf(data_name, sizeof(data_name) - 1,
+					"touch[%u]", i);
+			pt_debug_print(dev, pr_buf,
+				si->xy_data + (i * si->desc.tch_record_size),
+				si->desc.tch_record_size, data_name);
+		}
+	}
+
+	/* buttons */
+	if (report_id == si->desc.btn_report_id)
+		pt_debug_print(dev, pr_buf, si->xy_data, report_size,
+				"button");
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_xy_worker
+ *
+ * SUMMARY: Read xy_data for all touches for debug.
+ *
+ * RETURN:
+ *   0 = success
+ *
+ * PARAMETERS:
+ *  *dd - pointer to pt_debug_data structure
+ ******************************************************************************/
+static int pt_xy_worker(struct pt_debug_data *dd)
+{
+	struct device *dev = dd->dev;
+	struct pt_sysinfo *si = dd->si;
+	u8 report_reg = si->xy_mode[TOUCH_COUNT_BYTE_OFFSET];
+	u8 num_cur_tch = GET_NUM_TOUCHES(report_reg);
+	uint32_t formated_output;
+
+	mutex_lock(&dd->sysfs_lock);
+	dd->interrupt_count++;
+	formated_output = dd->formated_output;
+	mutex_unlock(&dd->sysfs_lock);
+
+	/* Interrupt */
+	pr_info("Interrupt(%u)\n", dd->interrupt_count);
+
+	if (formated_output)
+		pt_debug_formated(dev, dd->pr_buf, si, num_cur_tch);
+	else
+		/* print data for TTHE */
+		pt_pr_buf_op_mode(dd, dd->pr_buf, si, num_cur_tch);
+
+	pr_info("\n");
+
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_debug_attention
+ *
+ * SUMMARY: Wrapper function for pt_xy_worker() to subcribe into TTDL attention
+ *  list.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*dev  - pointer to device structure
+ ******************************************************************************/
+static int pt_debug_attention(struct device *dev)
+{
+	struct pt_debug_data *dd = pt_get_debug_data(dev);
+	struct pt_sysinfo *si = dd->si;
+	u8 report_id = si->xy_mode[2];
+	int rc = 0;
+
+	if (report_id != si->desc.tch_report_id
+			&& report_id != si->desc.btn_report_id)
+		return 0;
+
+	/* core handles handshake */
+	rc = pt_xy_worker(dd);
+	if (rc < 0)
+		pt_debug(dev, DL_ERROR, "%s: xy_worker error r=%d\n",
+			__func__, rc);
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_status_show
+ *
+ * SUMMARY: The show method for the int_count sysfs node. This node displays
+ *	the count of interrupt.
+ *
+ * PARAMETERS:
+ *      *dev  - pointer to Device structure
+ *      *attr - pointer to the device attribute structure
+ *      *buf  - pointer to buffer to print
+ ******************************************************************************/
+static ssize_t pt_interrupt_count_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct pt_debug_data *dd = pt_get_debug_data(dev);
+	int val;
+
+	mutex_lock(&dd->sysfs_lock);
+	val = dd->interrupt_count;
+	mutex_unlock(&dd->sysfs_lock);
+
+	return scnprintf(buf, PT_MAX_PRBUF_SIZE, "Interrupt Count: %d\n", val);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_interrupt_count_store
+ *
+ * SUMMARY: The store method for the int_count sysfs node that allows the count
+ *	of interrput to be cleared.
+ *
+ * RETURN: Size of passed in buffer
+ *
+ * PARAMETERS:
+ *	*dev  - pointer to device structure
+ *	*attr - pointer to device attributes
+ *	*buf  - pointer to buffer that hold the command parameters
+ *	 size - size of buf
+ ******************************************************************************/
+static ssize_t pt_interrupt_count_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	struct pt_debug_data *dd = pt_get_debug_data(dev);
+
+	mutex_lock(&dd->sysfs_lock);
+	dd->interrupt_count = 0;
+	mutex_unlock(&dd->sysfs_lock);
+	return size;
+}
+
+static DEVICE_ATTR(int_count, 0600,
+	pt_interrupt_count_show, pt_interrupt_count_store);
+
+/*******************************************************************************
+ * FUNCTION: pt_formated_output_show
+ *
+ * SUMMARY: Show method for the formated_output sysfs node that will show
+ *  whether to format data to buffer or print directly.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*dev  - pointer to device structure
+ *	*attr - pointer to device attributes
+ *	*buf  - pointer to output buffer
+ ******************************************************************************/
+static ssize_t pt_formated_output_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct pt_debug_data *dd = pt_get_debug_data(dev);
+	int val;
+
+	mutex_lock(&dd->sysfs_lock);
+	val = dd->formated_output;
+	mutex_unlock(&dd->sysfs_lock);
+
+	return scnprintf(buf, PT_MAX_PRBUF_SIZE,
+			"Formated debug output: %x\n", val);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_formated_output_store
+ *
+ * SUMMARY: The store method for the formated_output sysfs node. Allows the
+ *  setting to format data to buffer or print directly.
+ *
+ * RETURN: Size of passed in buffer
+ *
+ * PARAMETERS:
+ *	*dev  - pointer to device structure
+ *	*attr - pointer to device attributes
+ *	*buf  - pointer to buffer that hold the command parameters
+ *	 size - size of buf
+ ******************************************************************************/
+static ssize_t pt_formated_output_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	struct pt_debug_data *dd = pt_get_debug_data(dev);
+	unsigned long value;
+	int rc;
+
+	rc = kstrtoul(buf, 10, &value);
+	if (rc < 0) {
+		pt_debug(dev, DL_ERROR, "%s: Invalid value\n", __func__);
+		return size;
+	}
+
+	/* Expecting only 0 or 1 */
+	if (value != 0 && value != 1) {
+		pt_debug(dev, DL_ERROR, "%s: Invalid value %lu\n",
+					__func__, value);
+		return size;
+	}
+
+	mutex_lock(&dd->sysfs_lock);
+	dd->formated_output = value;
+	mutex_unlock(&dd->sysfs_lock);
+	return size;
+}
+
+static DEVICE_ATTR(formated_output, 0600,
+	pt_formated_output_show, pt_formated_output_store);
+
+/*******************************************************************************
+ * FUNCTION: pt_mt_probe
+ *
+ * SUMMARY: The probe function for debug module to create sysfs nodes and
+ *  subscribe attention list.
+ *
+ * RETURN:
+ *	 0 = success
+ *
+ * PARAMETERS:
+ *   *dev   - pointer to device structure
+ *  **data  - double pointer tothe pt_debug_data structure to be created here
+ ******************************************************************************/
+static int pt_debug_probe(struct device *dev, void **data)
+{
+	struct pt_debug_data *dd;
+	int rc;
+
+	/* get context and debug print buffers */
+	dd = kzalloc(sizeof(*dd), GFP_KERNEL);
+	if (!dd) {
+		rc = -ENOMEM;
+		goto pt_debug_probe_alloc_failed;
+	}
+
+	rc = device_create_file(dev, &dev_attr_int_count);
+	if (rc) {
+		pt_debug(dev, DL_ERROR, "%s: Error, could not create int_count\n",
+					__func__);
+		goto pt_debug_probe_create_int_count_failed;
+	}
+
+	rc = device_create_file(dev, &dev_attr_formated_output);
+	if (rc) {
+		pt_debug(dev, DL_ERROR, "%s: Error, could not create formated_output\n",
+					__func__);
+		goto pt_debug_probe_create_formated_failed;
+	}
+
+	mutex_init(&dd->sysfs_lock);
+	dd->dev = dev;
+	*data = dd;
+
+	dd->si = cmd->request_sysinfo(dev);
+	if (!dd->si) {
+		pt_debug(dev, DL_ERROR, "%s: Fail get sysinfo pointer from core\n",
+					__func__);
+		rc = -ENODEV;
+		goto pt_debug_probe_sysinfo_failed;
+	}
+
+	rc = cmd->subscribe_attention(dev, PT_ATTEN_IRQ, PT_DEBUG_NAME,
+		pt_debug_attention, PT_MODE_OPERATIONAL);
+	if (rc < 0) {
+		pt_debug(dev, DL_ERROR, "%s: Error, could not subscribe attention cb\n",
+					__func__);
+		goto pt_debug_probe_subscribe_failed;
+	}
+
+	return 0;
+
+pt_debug_probe_subscribe_failed:
+pt_debug_probe_sysinfo_failed:
+	device_remove_file(dev, &dev_attr_formated_output);
+pt_debug_probe_create_formated_failed:
+	device_remove_file(dev, &dev_attr_int_count);
+pt_debug_probe_create_int_count_failed:
+	kfree(dd);
+pt_debug_probe_alloc_failed:
+	pt_debug(dev, DL_ERROR, "%s failed.\n", __func__);
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_debug_release
+ *
+ * SUMMARY: Remove function for debug module that does following cleanup:
+ *	- Unsubscibe all registered attention tasks
+ *	- Removes all created sysfs nodes
+ *
+ * PARAMETERS:
+ *	*dev   - pointer to device structure
+ *	*data  - pointer to the pt_debug_data structure
+ ******************************************************************************/
+static void pt_debug_release(struct device *dev, void *data)
+{
+	struct pt_debug_data *dd = data;
+	int rc;
+
+	rc = cmd->unsubscribe_attention(dev, PT_ATTEN_IRQ, PT_DEBUG_NAME,
+		pt_debug_attention, PT_MODE_OPERATIONAL);
+	if (rc < 0) {
+		pt_debug(dev, DL_ERROR, "%s: Error, could not un-subscribe attention\n",
+					__func__);
+		goto pt_debug_release_exit;
+	}
+
+pt_debug_release_exit:
+	device_remove_file(dev, &dev_attr_formated_output);
+	device_remove_file(dev, &dev_attr_int_count);
+	kfree(dd);
+}
+
+static struct pt_module debug_module = {
+	.name = PT_DEBUG_NAME,
+	.probe = pt_debug_probe,
+	.release = pt_debug_release,
+};
+
+/*******************************************************************************
+ * FUNCTION: pt_debug_init
+ *
+ * SUMMARY: Initialize function for debug module which to register
+ *  debug_module into TTDL module list.
+ *
+ * RETURN:
+ *	 0 = success
+ ******************************************************************************/
+static int __init pt_debug_init(void)
+{
+	int rc;
+
+	cmd = pt_get_commands();
+	if (!cmd)
+		return -EINVAL;
+
+	rc = pt_register_module(&debug_module);
+	if (rc < 0) {
+		pr_err("%s: Error, failed registering module\n",
+			__func__);
+			return rc;
+	}
+
+	pr_info("%s: Parade TTSP Debug Driver (Built %s) rc=%d\n",
+		 __func__, PT_DRIVER_VERSION, rc);
+	return 0;
+}
+module_init(pt_debug_init);
+
+/*******************************************************************************
+ * FUNCTION: pt_debug_exit
+ *
+ * SUMMARY: Exit function for debug module which to unregister debug_module
+ * from TTDL module list.
+ *
+ ******************************************************************************/
+static void __exit pt_debug_exit(void)
+{
+	pt_unregister_module(&debug_module);
+}
+module_exit(pt_debug_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Parade TrueTouch(R) Standard Product Debug Driver");
+MODULE_AUTHOR("Parade Technologies <[email protected]>");

+ 6746 - 0
qcom/opensource/touch-drivers/pt/pt_device_access.c

@@ -0,0 +1,6746 @@
+/*
+ * pt_device_access.c
+ * Parade TrueTouch(TM) Standard Product Device Access Module.
+ * Configuration and Test command/status user interface.
+ * For use with Parade touchscreen controllers.
+ * Supported parts include:
+ * TMA5XX
+ * TMA448
+ * TMA445A
+ * TT21XXX
+ * TT31XXX
+ * TT4XXXX
+ * TT7XXX
+ * TC3XXX
+ *
+ * Copyright (C) 2015-2020 Parade Technologies
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * Contact Parade Technologies at www.paradetech.com <[email protected]>
+ */
+
+#include "pt_regs.h"
+#include <linux/firmware.h>
+
+#include <linux/timer.h>
+#include <linux/timex.h>
+#include <linux/rtc.h>
+
+#define PT_CMCP_THRESHOLD_FILE_NAME "pt_thresholdfile.csv"
+#define CMCP_THRESHOLD_FILE_NAME "ttdl_cmcp_thresholdfile.csv"
+
+/* Max test case number */
+#define MAX_CASE_NUM            (22)
+
+/* ASCII */
+#define ASCII_LF                (0x0A)
+#define ASCII_CR                (0x0D)
+#define ASCII_COMMA             (0x2C)
+#define ASCII_ZERO              (0x30)
+#define ASCII_NINE              (0x39)
+
+/* Max characters of test case name */
+#define NAME_SIZE_MAX           (50)
+
+/* Max sensor and button number */
+#define MAX_BUTTONS             (PIP1_SYSINFO_MAX_BTN)
+#define MAX_SENSORS             (5120)
+#define MAX_TX_SENSORS          (128)
+#define MAX_RX_SENSORS          (128)
+
+/* Multiply by 2 for double (min, max) values */
+#define TABLE_BUTTON_MAX_SIZE   (MAX_BUTTONS * 2)
+#define TABLE_SENSOR_MAX_SIZE   (MAX_SENSORS * 2)
+#define TABLE_TX_MAX_SIZE       (MAX_TX_SENSORS*2)
+#define TABLE_RX_MAX_SIZE       (MAX_RX_SENSORS*2)
+
+#define CM_PANEL_DATA_OFFSET    (6)
+#define CM_BTN_DATA_OFFSET      (6)
+#define CP_PANEL_DATA_OFFSET    (6)
+#define CP_BTN_DATA_OFFSET      (6)
+#define MAX_BUF_LEN             (100000)
+#define RETRIEVE_PANEL_SCAN_HDR (10)
+
+enum print_buffer_format {
+	PT_PR_FORMAT_DEFAULT        = 0,
+	PT_PR_FORMAT_U8_SPACE       = 1,
+	PT_PR_FORMAT_U16_SPACE      = 2,
+	PT_PR_FORMAT_U8_NO_SPACE    = 3,
+	PT_PR_FORMAT_U32_SPACE      = 4,
+	PT_PR_FORMAT_UNDEFINE
+};
+
+/* cmcp csv file information */
+struct configuration {
+	u32 cm_range_limit_row;
+	u32 cm_range_limit_col;
+	u32 cm_min_limit_cal;
+	u32 cm_max_limit_cal;
+	u32 cm_max_delta_sensor_percent;
+	u32 cm_max_delta_button_percent;
+	u32 min_button;
+	u32 max_button;
+	u32 cp_max_delta_sensor_rx_percent;
+	u32 cp_max_delta_sensor_tx_percent;
+	u32 cm_min_max_table_btn[TABLE_BUTTON_MAX_SIZE];
+	u32 cp_min_max_table_btn[TABLE_BUTTON_MAX_SIZE];
+	u32 cm_min_max_table_sensor[TABLE_SENSOR_MAX_SIZE];
+	u32 cp_min_max_table_rx[TABLE_RX_MAX_SIZE];
+	u32 cp_min_max_table_tx[TABLE_TX_MAX_SIZE];
+	u32 cm_min_max_table_btn_size;
+	u32 cp_min_max_table_btn_size;
+	u32 cm_min_max_table_sensor_size;
+	u32 cp_min_max_table_rx_size;
+	u32 cp_min_max_table_tx_size;
+	u32 cp_max_delta_button_percent;
+	u32 cm_max_table_gradient_cols_percent[TABLE_TX_MAX_SIZE];
+	u32 cm_max_table_gradient_cols_percent_size;
+	u32 cm_max_table_gradient_rows_percent[TABLE_RX_MAX_SIZE];
+	u32 cm_max_table_gradient_rows_percent_size;
+	u32 cm_excluding_row_edge;
+	u32 cm_excluding_col_edge;
+	u32 rx_num;
+	u32 tx_num;
+	u32 btn_num;
+	u32 cm_enabled;
+	u32 cp_enabled;
+	u32 is_valid_or_not;
+};
+
+/* Test case search definition */
+struct test_case_search {
+	char name[NAME_SIZE_MAX]; /* Test case name */
+	u32 name_size;            /* Test case name size */
+	u32 offset;               /* Test case offset */
+};
+
+/* Test case field definition */
+struct test_case_field {
+	char *name;     /* Test case name */
+	u32 name_size;  /* Test case name size */
+	u32 type;       /* Test case type */
+	u32 *bufptr;    /* Buffer to store value information */
+	u32 exist_or_not;/* Test case exist or not */
+	u32 data_num;   /* Buffer data number */
+	u32 line_num;   /* Buffer line number */
+};
+
+/* Test case type */
+enum test_case_type {
+	TEST_CASE_TYPE_NO,
+	TEST_CASE_TYPE_ONE,
+	TEST_CASE_TYPE_MUL,
+	TEST_CASE_TYPE_MUL_LINES,
+};
+
+/* Test case order in test_case_field_array */
+enum case_order {
+	CM_TEST_INPUTS,
+	CM_EXCLUDING_COL_EDGE,
+	CM_EXCLUDING_ROW_EDGE,
+	CM_GRADIENT_CHECK_COL,
+	CM_GRADIENT_CHECK_ROW,
+	CM_RANGE_LIMIT_ROW,
+	CM_RANGE_LIMIT_COL,
+	CM_MIN_LIMIT_CAL,
+	CM_MAX_LIMIT_CAL,
+	CM_MAX_DELTA_SENSOR_PERCENT,
+	CM_MAX_DELTA_BUTTON_PERCENT,
+	PER_ELEMENT_MIN_MAX_TABLE_BUTTON,
+	PER_ELEMENT_MIN_MAX_TABLE_SENSOR,
+	CP_TEST_INPUTS,
+	CP_MAX_DELTA_SENSOR_RX_PERCENT,
+	CP_MAX_DELTA_SENSOR_TX_PERCENT,
+	CP_MAX_DELTA_BUTTON_PERCENT,
+	CP_PER_ELEMENT_MIN_MAX_BUTTON,
+	MIN_BUTTON,
+	MAX_BUTTON,
+	PER_ELEMENT_MIN_MAX_RX,
+	PER_ELEMENT_MIN_MAX_TX,
+	CASE_ORDER_MAX,
+};
+
+enum cmcp_test_item {
+	CMCP_FULL = 0,
+	CMCP_CM_PANEL,
+	CMCP_CP_PANEL,
+	CMCP_CM_BTN,
+	CMCP_CP_BTN,
+};
+
+#define CM_ENABLED 0x10
+#define CP_ENABLED 0x20
+#define CM_PANEL (0x01 | CM_ENABLED)
+#define CP_PANEL (0x02 | CP_ENABLED)
+#define CM_BTN (0x04 | CM_ENABLED)
+#define CP_BTN (0x08 | CP_ENABLED)
+#define CMCP_FULL_CASE\
+	(CM_PANEL | CP_PANEL | CM_BTN | CP_BTN | CM_ENABLED | CP_ENABLED)
+
+#define PT_DEVICE_ACCESS_NAME "pt_device_access"
+#define PT_INPUT_ELEM_SZ (sizeof("0xHH") + 1)
+
+#define PIP_CMD_MAX_LENGTH ((1 << 16) - 1)
+
+#ifdef TTHE_TUNER_SUPPORT
+struct heatmap_param {
+	bool scan_start;
+	enum scan_data_type_list data_type; /* raw, base, diff */
+	int num_element;
+};
+#endif
+#define ABS(x)			(((x) < 0) ? -(x) : (x))
+
+#ifndef MIN
+#define MIN(a, b)  (((a) < (b)) ? (a) : (b))
+#endif
+
+#define PT_MAX_CONFIG_BYTES    256
+#define PT_TTHE_TUNER_GET_PANEL_DATA_FILE_NAME "get_panel_data"
+#define TTHE_TUNER_MAX_BUF	(PT_MAX_PRBUF_SIZE * 8)
+
+struct pt_device_access_data {
+	struct device *dev;
+	struct pt_sysinfo *si;
+	struct mutex sysfs_lock;
+	bool sysfs_nodes_created;
+	struct kobject mfg_test;
+	u8 panel_scan_retrieve_id;
+	u8 panel_scan_type_id;
+	u8 get_idac_data_id;
+	u8 calibrate_sensing_mode;
+	u8 calibrate_initialize_baselines;
+	u8 baseline_sensing_mode;
+	u8 fw_self_test_id;
+	u8 fw_self_test_format;
+	u16 fw_self_test_param_len;
+	u8 fw_self_test_param[PT_FW_SELF_TEST_MAX_PARM];
+	struct pt_cal_ext_data cal_ext_data;
+	struct dentry *panel_scan_debugfs;
+	int panel_scan_size;
+	u8 panel_scan_data_buf[TTHE_TUNER_MAX_BUF];
+	struct mutex debugfs_lock;
+#ifdef TTHE_TUNER_SUPPORT
+	struct heatmap_param heatmap;
+	struct dentry *tthe_get_panel_data_debugfs;
+	u8 tthe_get_panel_data_is_open;
+#endif
+	struct dentry *cmcp_results_debugfs;
+	struct dentry *base_dentry;
+	struct dentry *mfg_test_dentry;
+	u8 ic_buf[PT_MAX_PRBUF_SIZE];
+	u8 response_buf[PT_MAX_PRBUF_SIZE];
+	struct mutex cmcp_threshold_lock;
+	u8 *cmcp_threshold_data;
+	int cmcp_threshold_size;
+	bool cmcp_threshold_loading;
+	struct work_struct cmcp_threshold_update;
+	int builtin_cmcp_threshold_status;
+	bool is_manual_upgrade_enabled;
+	struct configuration *configs;
+	struct cmcp_data *cmcp_info;
+	struct result *result;
+	struct test_case_search *test_search_array;
+	struct test_case_field *test_field_array;
+	int cmcp_test_items;
+	int test_executed;
+	int cmcp_range_check;
+	int cmcp_force_calibrate;
+	int cmcp_test_in_progress;
+};
+
+struct cmcp_data {
+	struct gd_sensor *gd_sensor_col;
+	struct gd_sensor *gd_sensor_row;
+	int32_t *cm_data_panel;
+	int32_t *cp_tx_data_panel;
+	int32_t *cp_rx_data_panel;
+	int32_t *cp_tx_cal_data_panel;
+	int32_t *cp_rx_cal_data_panel;
+	int32_t cp_sensor_rx_delta;
+	int32_t cp_sensor_tx_delta;
+	int32_t cp_button_delta;
+	int32_t *cm_btn_data;
+	int32_t *cp_btn_data;
+	int32_t *cm_sensor_column_delta;
+	int32_t *cm_sensor_row_delta;
+	int32_t cp_btn_cal;
+	int32_t cm_btn_cal;
+	int32_t cp_button_ave;
+	int32_t cm_ave_data_panel;
+	int32_t cp_tx_ave_data_panel;
+	int32_t cp_rx_ave_data_panel;
+	int32_t cm_cal_data_panel;
+	int32_t cm_ave_data_btn;
+	int32_t cm_cal_data_btn;
+	int32_t cm_delta_data_btn;
+	int32_t cm_sensor_delta;
+
+	int32_t tx_num;
+	int32_t rx_num;
+	int32_t btn_num;
+};
+
+struct result {
+	int32_t config_ver;
+	int32_t revision_ctrl;
+	int32_t device_id_high;
+	int32_t device_id_low;
+	/* Sensor Cm validation */
+	bool cm_test_pass;
+	bool cm_sensor_validation_pass;
+	bool cm_sensor_row_delta_pass;
+	bool cm_sensor_col_delta_pass;
+	bool cm_sensor_gd_row_pass;
+	bool cm_sensor_gd_col_pass;
+	bool cm_sensor_calibration_pass;
+	bool cm_sensor_delta_pass;
+	bool cm_button_validation_pass;
+	bool cm_button_delta_pass;
+
+	int32_t *cm_sensor_raw_data;
+	int32_t cm_sensor_calibration;
+	int32_t cm_sensor_delta;
+	int32_t *cm_button_raw_data;
+	int32_t cm_button_delta;
+
+	/* Sensor Cp validation */
+	bool cp_test_pass;
+	bool cp_sensor_delta_pass;
+	bool cp_sensor_rx_delta_pass;
+	bool cp_sensor_tx_delta_pass;
+	bool cp_sensor_average_pass;
+	bool cp_button_delta_pass;
+	bool cp_button_average_pass;
+	bool cp_rx_validation_pass;
+	bool cp_tx_validation_pass;
+	bool cp_button_validation_pass;
+
+	int32_t *cp_sensor_rx_raw_data;
+	int32_t *cp_sensor_tx_raw_data;
+	int32_t cp_sensor_rx_delta;
+	int32_t cp_sensor_tx_delta;
+	int32_t cp_sensor_rx_calibration;
+	int32_t cp_sensor_tx_calibration;
+	int32_t *cp_button_raw_data;
+	int32_t cp_button_delta;
+
+	/*other validation*/
+	bool short_test_pass;
+	bool test_summary;
+};
+
+static struct pt_core_commands *cmd;
+
+static struct pt_module device_access_module;
+
+static ssize_t pt_run_and_get_selftest_result(struct device *dev,
+		int protect, char *buf, size_t buf_len, u8 test_id,
+		u16 read_length, bool get_result_on_pass,
+		bool print_results, u8 print_format);
+
+static int _pt_calibrate_idacs_cmd(struct device *dev,
+		u8 sensing_mode, u8 *status);
+
+static int pt_perform_calibration(struct device *dev);
+
+/*******************************************************************************
+ * FUNCTION: pt_get_device_access_data
+ *
+ * SUMMARY: Inline function to get pt_device_access_data.
+ *
+ * RETURN:
+ *	 pointer to pt_device_access_data structure
+ *
+ * PARAMETERS:
+ *	*dev          - pointer to device structure
+ ******************************************************************************/
+static inline struct pt_device_access_data *pt_get_device_access_data(
+		struct device *dev)
+{
+	return pt_get_module_data(dev, &device_access_module);
+}
+
+/*******************************************************************************
+ * FUNCTION: cmcp_check_config_fw_match
+ *
+ * SUMMARY: Checks if tx,rx and btn num of firmware match with configuration.
+ *
+ * RETURN:
+ *	 0 = match
+ *	!0 = doesn't match
+ *
+ * PARAMETERS:
+ *	*dev           - pointer to device structure
+ *	*configuration - pointer to configuration structure
+ ******************************************************************************/
+static int cmcp_check_config_fw_match(struct device *dev,
+	struct configuration *configuration)
+{
+	struct pt_device_access_data *dad
+		= pt_get_device_access_data(dev);
+	int32_t tx_num = dad->configs->tx_num;
+	int32_t rx_num = dad->configs->rx_num;
+	int32_t button_num = dad->configs->btn_num;
+	int ret = 0;
+
+	if (tx_num != dad->si->sensing_conf_data.tx_num) {
+		pt_debug(dev, DL_ERROR,
+			"%s: TX number mismatch! CSV=%d DUT=%d\n",
+			__func__, tx_num, dad->si->sensing_conf_data.tx_num);
+		ret = -EINVAL;
+	}
+
+	if (rx_num != dad->si->sensing_conf_data.rx_num) {
+		pt_debug(dev, DL_ERROR,
+			"%s: RX number mismatch! CSV=%d DUT=%d\n",
+			__func__, rx_num, dad->si->sensing_conf_data.rx_num);
+		ret = -EINVAL;
+	}
+
+	if (button_num != dad->si->num_btns) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Button number mismatch! CSV=%d DUT=%d\n",
+			__func__, button_num, dad->si->num_btns);
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+/*******************************************************************************
+ * FUNCTION: validate_cm_test_results
+ *
+ * SUMMARY: Checks cm test results and outputs each test and a summary result
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *dev           - pointer to device structure
+ *  *configuration - pointer to configuration structure
+ *  *cmcp_info     - pointer to cmcp_data structure to store cmcp data from fw
+ *  *result        - pointer to result structure
+ *  *pass          - pointer to bool value
+ *   test_item     - flag to store all test item are requested
+ ******************************************************************************/
+static int validate_cm_test_results(struct device *dev,
+	struct configuration *configuration, struct cmcp_data *cmcp_info,
+	struct result *result, bool *pass, int test_item)
+{
+	int32_t tx_num = cmcp_info->tx_num;
+	int32_t rx_num = cmcp_info->rx_num;
+	int32_t button_num =  cmcp_info->btn_num;
+	uint32_t sensor_num = tx_num * rx_num;
+	int32_t *cm_sensor_data = cmcp_info->cm_data_panel;
+	int32_t cm_button_delta;
+	int32_t cm_sensor_calibration;
+	int32_t *cm_button_data = cmcp_info->cm_btn_data;
+	struct gd_sensor *gd_sensor_col = cmcp_info->gd_sensor_col;
+	struct gd_sensor *gd_sensor_row = cmcp_info->gd_sensor_row;
+	int32_t *cm_sensor_column_delta = cmcp_info->cm_sensor_column_delta;
+	int32_t *cm_sensor_row_delta = cmcp_info->cm_sensor_row_delta;
+	int ret = 0;
+	int i, j;
+
+	pt_debug(dev, DL_INFO, "%s: start\n", __func__);
+
+	if ((test_item & CM_PANEL) == CM_PANEL) {
+		pt_debug(dev, DL_INFO,
+			"Check each sensor Cm data for min max value\n ");
+
+		/* Check each sensor Cm data for min/max values */
+		result->cm_sensor_validation_pass = true;
+
+	for (i = 0; i < sensor_num; i++) {
+		int row = i % rx_num;
+		int col = i / rx_num;
+		int32_t cm_sensor_min =
+		  configuration->cm_min_max_table_sensor[(row*tx_num+col)*2];
+		int32_t cm_sensor_max =
+		  configuration->cm_min_max_table_sensor[(row*tx_num+col)*2+1];
+		if ((cm_sensor_data[i] < cm_sensor_min) ||
+		    (cm_sensor_data[i] > cm_sensor_max)) {
+			pt_debug(dev, DL_WARN,
+					"%s: Sensor[%d,%d]:%d (%d,%d)\n",
+					"Cm sensor min/max test",
+					row, col,
+					cm_sensor_data[i],
+					cm_sensor_min, cm_sensor_max);
+			result->cm_sensor_validation_pass = false;
+		}
+	}
+
+	/*check cm gradient column data*/
+	result->cm_sensor_gd_col_pass = true;
+	for (i = 0; i < configuration->cm_max_table_gradient_cols_percent_size;
+	     i++) {
+		if ((gd_sensor_col + i)->gradient_val >
+		    10 * configuration->cm_max_table_gradient_cols_percent[i]) {
+			pt_debug(dev, DL_WARN,
+			"%s: cm_max_table_gradient_cols_percent[%d]:%d, gradient_val:%d\n",
+			__func__, i,
+			configuration->cm_max_table_gradient_cols_percent[i],
+			(gd_sensor_col + i)->gradient_val);
+			result->cm_sensor_gd_col_pass = false;
+		}
+	}
+
+	/*check cm gradient row data*/
+	result->cm_sensor_gd_row_pass = true;
+	for (j = 0; j < configuration->cm_max_table_gradient_rows_percent_size;
+	     j++) {
+		if ((gd_sensor_row + j)->gradient_val >
+		    10 * configuration->cm_max_table_gradient_rows_percent[j]) {
+			pt_debug(dev, DL_WARN,
+			"%s: cm_max_table_gradient_rows_percent[%d]:%d, gradient_val:%d\n",
+			__func__, j,
+			configuration->cm_max_table_gradient_rows_percent[j],
+			(gd_sensor_row + j)->gradient_val);
+			result->cm_sensor_gd_row_pass = false;
+		}
+	}
+
+	result->cm_sensor_row_delta_pass = true;
+	result->cm_sensor_col_delta_pass = true;
+	result->cm_sensor_calibration_pass = true;
+	result->cm_sensor_delta_pass = true;
+
+	/* Check each row Cm data with neighbor for difference */
+	for (i = 0; i < tx_num; i++) {
+		for (j = 1; j < rx_num; j++) {
+			int32_t cm_sensor_row_diff =
+				ABS(cm_sensor_data[i * rx_num + j] -
+				cm_sensor_data[i * rx_num + j - 1]);
+			cm_sensor_row_delta[i * rx_num + j - 1] =
+				cm_sensor_row_diff;
+			if (cm_sensor_row_diff >
+			    configuration->cm_range_limit_row) {
+				pt_debug(dev, DL_DEBUG,
+					"%s: Sensor[%d,%d]:%d (%d)\n",
+					"Cm sensor row range limit test",
+					j, i, cm_sensor_row_diff,
+					configuration->cm_range_limit_row);
+				result->cm_sensor_row_delta_pass = false;
+			}
+		}
+	}
+
+	/* Check each column Cm data with neighbor for difference */
+	for (i = 1; i < tx_num; i++) {
+		for (j = 0; j < rx_num; j++) {
+			int32_t cm_sensor_col_diff =
+				ABS((int)cm_sensor_data[i * rx_num + j] -
+				(int)cm_sensor_data[(i - 1) * rx_num + j]);
+			cm_sensor_column_delta[(i - 1) * rx_num + j] =
+				cm_sensor_col_diff;
+			if (cm_sensor_col_diff >
+			    configuration->cm_range_limit_col) {
+				pt_debug(dev, DL_DEBUG,
+					"%s: Sensor[%d,%d]:%d (%d)\n",
+					"Cm sensor column range limit test",
+					j, i, cm_sensor_col_diff,
+					configuration->cm_range_limit_col);
+				result->cm_sensor_col_delta_pass = false;
+			}
+		}
+	}
+
+	/* Check sensor calculated Cm for min/max values */
+	cm_sensor_calibration = cmcp_info->cm_cal_data_panel;
+	if (cm_sensor_calibration <
+	    configuration->cm_min_limit_cal ||
+	    cm_sensor_calibration > configuration->cm_max_limit_cal) {
+		pt_debug(dev, DL_DEBUG, "%s: Cm_cal:%d (%d,%d)\n",
+			"Cm sensor Cm_cal min/max test",
+			cm_sensor_calibration,
+			configuration->cm_min_limit_cal,
+			configuration->cm_max_limit_cal);
+		result->cm_sensor_calibration_pass = false;
+	}
+
+	/* Check sensor Cm delta for range limit */
+	if (cmcp_info->cm_sensor_delta >
+	    (10 * configuration->cm_max_delta_sensor_percent)) {
+		pt_debug(dev, DL_DEBUG,
+			"%s: Cm_sensor_delta:%d (%d)\n",
+			"Cm sensor delta range limit test",
+			cmcp_info->cm_sensor_delta,
+			configuration->cm_max_delta_sensor_percent);
+		result->cm_sensor_delta_pass = false;
+	}
+
+	result->cm_test_pass = result->cm_sensor_gd_col_pass
+			&& result->cm_sensor_gd_row_pass
+			&& result->cm_sensor_validation_pass
+			&& result->cm_sensor_row_delta_pass
+			&& result->cm_sensor_col_delta_pass
+			&& result->cm_sensor_calibration_pass
+			&& result->cm_sensor_delta_pass;
+	}
+
+	if (((test_item & CM_BTN) == CM_BTN) && (cmcp_info->btn_num)) {
+		/* Check each button Cm data for min/max values */
+		result->cm_button_validation_pass = true;
+		for (i = 0; i < button_num; i++) {
+			int32_t  cm_button_min =
+				configuration->cm_min_max_table_btn[i * 2];
+			int32_t  cm_button_max =
+				configuration->cm_min_max_table_btn[i * 2 + 1];
+			if ((cm_button_data[i] <= cm_button_min) ||
+			    (cm_button_data[i] >= cm_button_max)) {
+				pt_debug(dev, DL_DEBUG,
+					"%s: Button[%d]:%d (%d,%d)\n",
+					"Cm button min/max test",
+					i, cm_button_data[i],
+					cm_button_min, cm_button_max);
+				result->cm_button_validation_pass = false;
+			}
+		}
+
+		/* Check button Cm delta for range limit */
+		result->cm_button_delta_pass = true;
+
+		cm_button_delta = ABS((cmcp_info->cm_ave_data_btn -
+			cmcp_info->cm_cal_data_btn) * 100 /
+			cmcp_info->cm_ave_data_btn);
+		if (cm_button_delta >
+		    configuration->cm_max_delta_button_percent) {
+			pt_debug(dev, DL_INFO,
+				"%s: Cm_button_delta:%d (%d)\n",
+				"Cm button delta range limit test",
+				cm_button_delta,
+				configuration->cm_max_delta_button_percent);
+			result->cm_button_delta_pass = false;
+		}
+
+		result->cm_test_pass = result->cm_test_pass &&
+				       result->cm_button_validation_pass &&
+				       result->cm_button_delta_pass;
+	}
+
+	if (pass)
+		*pass = result->cm_test_pass;
+
+	return ret;
+}
+
+/*******************************************************************************
+ * FUNCTION: validate_cp_test_results
+ *
+ * SUMMARY: Checks cp test results and outputs each test and a summary result.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *dev           - pointer to device structure
+ *  *configuration - pointer to configuration structure
+ *  *cmcp_info     - pointer to cmcp_data structure to store cmcp data from fw
+ *  *result        - pointer to result structure
+ *  *pass          - pointer to bool value
+ *   test_item     - flag to store all test item are requested
+ ******************************************************************************/
+static int validate_cp_test_results(struct device *dev,
+	struct configuration *configuration, struct cmcp_data *cmcp_info,
+	struct result *result, bool *pass, int test_item)
+{
+	int i = 0;
+	uint32_t configuration_rx_num;
+	uint32_t configuration_tx_num;
+	int32_t *cp_sensor_tx_data = cmcp_info->cp_tx_data_panel;
+	int32_t *cp_sensor_rx_data = cmcp_info->cp_rx_data_panel;
+	int32_t cp_button_delta;
+	int32_t cp_button_average;
+
+	result->cp_test_pass = true;
+	configuration_rx_num = configuration->cp_min_max_table_rx_size/2;
+	configuration_tx_num = configuration->cp_min_max_table_tx_size/2;
+
+	pt_debug(dev, DL_INFO, "%s start\n", __func__);
+
+	if ((test_item & CP_PANEL) == CP_PANEL) {
+		int32_t cp_sensor_tx_delta;
+		int32_t cp_sensor_rx_delta;
+
+		/* Check Sensor Cp delta for range limit */
+		result->cp_sensor_delta_pass = true;
+		/*check cp_sensor_tx_delta */
+		for (i = 0; i < configuration_tx_num; i++) {
+			cp_sensor_tx_delta =
+				ABS((cmcp_info->cp_tx_cal_data_panel[i]-
+				cmcp_info->cp_tx_data_panel[i]) * 100 /
+				cmcp_info->cp_tx_data_panel[i]);
+
+			if (cp_sensor_tx_delta >
+			    configuration->cp_max_delta_sensor_tx_percent) {
+				pt_debug(dev, DL_DEBUG,
+				"%s: Cp_sensor_tx_delta:%d (%d)\n",
+				"Cp sensor delta range limit test",
+				cp_sensor_tx_delta,
+				configuration->cp_max_delta_sensor_tx_percent);
+				result->cp_sensor_delta_pass = false;
+			}
+		}
+
+		/*check cp_sensor_rx_delta */
+		for (i = 0; i < configuration_rx_num; i++) {
+			cp_sensor_rx_delta =
+				ABS((cmcp_info->cp_rx_cal_data_panel[i] -
+				cmcp_info->cp_rx_data_panel[i]) * 100 /
+				cmcp_info->cp_rx_data_panel[i]);
+			if (cp_sensor_rx_delta >
+			    configuration->cp_max_delta_sensor_rx_percent) {
+				pt_debug(dev, DL_DEBUG,
+				"%s: Cp_sensor_rx_delta:%d(%d)\n",
+				"Cp sensor delta range limit test",
+				cp_sensor_rx_delta,
+				configuration->cp_max_delta_sensor_rx_percent);
+				result->cp_sensor_delta_pass = false;
+			}
+		}
+
+		/* Check sensor Cp rx for min/max values */
+		result->cp_rx_validation_pass = true;
+		for (i = 0; i < configuration_rx_num; i++) {
+			int32_t cp_rx_min =
+				configuration->cp_min_max_table_rx[i * 2];
+			int32_t cp_rx_max =
+				configuration->cp_min_max_table_rx[i * 2 + 1];
+			if ((cp_sensor_rx_data[i] <= cp_rx_min) ||
+			    (cp_sensor_rx_data[i] >= cp_rx_max)) {
+				pt_debug(dev, DL_DEBUG,
+					"%s: Cp Rx[%d]:%d (%d,%d)\n",
+					"Cp Rx min/max test",
+					i, (int)cp_sensor_rx_data[i],
+					cp_rx_min, cp_rx_max);
+				result->cp_rx_validation_pass = false;
+			}
+		}
+
+		/* Check sensor Cp tx for min/max values */
+		result->cp_tx_validation_pass = true;
+		for (i = 0; i < configuration_tx_num; i++) {
+			int32_t cp_tx_min =
+				configuration->cp_min_max_table_tx[i * 2];
+			int32_t cp_tx_max =
+				configuration->cp_min_max_table_tx[i * 2 + 1];
+			if ((cp_sensor_tx_data[i] <= cp_tx_min) ||
+			    (cp_sensor_tx_data[i] >= cp_tx_max)) {
+				pt_debug(dev, DL_DEBUG,
+					"%s: Cp Tx[%d]:%d(%d,%d)\n",
+					"Cp Tx min/max test",
+					i, cp_sensor_tx_data[i],
+					cp_tx_min, cp_tx_max);
+				result->cp_tx_validation_pass = false;
+			}
+		}
+
+		result->cp_test_pass = result->cp_test_pass
+				&& result->cp_sensor_delta_pass
+				&& result->cp_rx_validation_pass
+				&& result->cp_tx_validation_pass;
+	}
+
+	if (((test_item & CP_BTN) == CP_BTN) && (cmcp_info->btn_num)) {
+		result->cp_button_delta_pass = true;
+
+		/* Check button Cp delta for range limit */
+		cp_button_delta = ABS((cmcp_info->cp_btn_cal
+		- cmcp_info->cp_button_ave) * 100 /
+		cmcp_info->cp_button_ave);
+		if (cp_button_delta >
+		    configuration->cp_max_delta_button_percent) {
+			pt_debug(dev, DL_INFO,
+				"%s: Cp_button_delta:%d (%d)\n",
+				"Cp button delta range limit test",
+				cp_button_delta,
+				configuration->cp_max_delta_button_percent);
+			result->cp_button_delta_pass = false;
+		}
+
+		/* Check button Cp average for min/max values */
+		result->cp_button_average_pass = true;
+		cp_button_average = cmcp_info->cp_button_ave;
+		if (cp_button_average < configuration->min_button ||
+		    cp_button_average > configuration->max_button) {
+			pt_debug(dev, DL_INFO,
+				"%s: Button Cp average fails min/max test\n",
+				__func__);
+			pt_debug(dev, DL_INFO,
+				"%s: Cp_button_average:%d (%d,%d)\n",
+				"Cp button average min/max test",
+				cp_button_average,
+				configuration->min_button,
+				configuration->max_button);
+			result->cp_button_average_pass = false;
+		}
+
+		/* Check each button Cp data for min/max values */
+		result->cp_button_validation_pass = true;
+		for (i = 0; i < cmcp_info->btn_num; i++) {
+			int32_t  cp_button_min =
+				configuration->cp_min_max_table_btn[i * 2];
+			int32_t  cp_button_max =
+				configuration->cp_min_max_table_btn[i * 2 + 1];
+			if ((cmcp_info->cp_btn_data[i] <= cp_button_min) ||
+			    (cmcp_info->cp_btn_data[i] >= cp_button_max)) {
+				pt_debug(dev, DL_DEBUG,
+					"%s: Button[%d]:%d (%d,%d)\n",
+					"Cp button min/max test",
+					i, cmcp_info->cp_btn_data[i],
+					cp_button_min, cp_button_max);
+				result->cp_button_validation_pass = false;
+			}
+		}
+
+		result->cp_test_pass = result->cp_test_pass
+				&& result->cp_button_delta_pass
+				&& result->cp_button_average_pass
+				&& result->cp_button_validation_pass;
+	}
+
+	if (pass)
+		*pass = result->cp_test_pass;
+
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: calculate_gradient_row
+ *
+ * SUMMARY: Calculates gradient value for rows.
+ *
+ * PARAMETERS:
+ *  *gd_sensor_row_head - pointer to gd_sensor structure
+ *   row_num            - number of row
+ *   exclude_row_edge   - flag to exclude row edge(1:exclude; 0:include)
+ *   exclude_col_edge   - flag to exclude column edge(1:exclude; 0:include)
+ ******************************************************************************/
+static void calculate_gradient_row(struct gd_sensor *gd_sensor_row_head,
+		 uint16_t row_num, int exclude_row_edge, int exclude_col_edge)
+{
+	int i = 0;
+	uint16_t cm_min_cur = 0;
+	uint16_t cm_max_cur = 0;
+	uint16_t cm_ave_cur = 0;
+	uint16_t cm_ave_next = 0;
+	uint16_t cm_ave_prev = 0;
+	struct gd_sensor *p = gd_sensor_row_head;
+
+	if (exclude_row_edge) {
+		for (i = 0; i < row_num; i++) {
+			if (!exclude_col_edge) {
+				cm_ave_cur = (p + i)->cm_ave;
+				cm_min_cur = (p + i)->cm_min;
+				cm_max_cur = (p + i)->cm_max;
+				if (i < (row_num-1))
+					cm_ave_next = (p + i+1)->cm_ave;
+				if (i > 0)
+					cm_ave_prev = (p + i-1)->cm_ave;
+			} else {
+				cm_ave_cur = (p + i)->cm_ave_exclude_edge;
+				cm_min_cur = (p + i)->cm_min_exclude_edge;
+				cm_max_cur = (p + i)->cm_max_exclude_edge;
+				if (i < (row_num-1))
+					cm_ave_next =
+					(p + i+1)->cm_ave_exclude_edge;
+				if (i > 0)
+					cm_ave_prev =
+					(p + i-1)->cm_ave_exclude_edge;
+			}
+
+			if (cm_ave_cur == 0)
+				cm_ave_cur = 1;
+
+			/*multiple 1000 to increate accuracy*/
+			if ((i == 0) || (i == (row_num-1))) {
+				(p + i)->gradient_val =
+				(cm_max_cur - cm_min_cur) * 1000 /
+				cm_ave_cur;
+			} else if (i == 1) {
+				(p + i)->gradient_val = (cm_max_cur - cm_min_cur
+				+ ABS(cm_ave_cur - cm_ave_next)) * 1000 /
+				cm_ave_cur;
+			} else {
+				(p + i)->gradient_val = (cm_max_cur - cm_min_cur
+				+ ABS(cm_ave_cur - cm_ave_prev)) * 1000 /
+				cm_ave_cur;
+			}
+		}
+	} else if (!exclude_row_edge) {
+		for (i = 0; i < row_num; i++) {
+			if (!exclude_col_edge) {
+				cm_ave_cur = (p + i)->cm_ave;
+				cm_min_cur = (p + i)->cm_min;
+				cm_max_cur = (p + i)->cm_max;
+				if (i < (row_num-1))
+					cm_ave_next = (p + i+1)->cm_ave;
+				if (i > 0)
+					cm_ave_prev = (p + i-1)->cm_ave;
+			} else {
+				cm_ave_cur = (p + i)->cm_ave_exclude_edge;
+				cm_min_cur = (p + i)->cm_min_exclude_edge;
+				cm_max_cur = (p + i)->cm_max_exclude_edge;
+				if (i < (row_num-1))
+					cm_ave_next =
+					(p + i+1)->cm_ave_exclude_edge;
+				if (i > 0)
+					cm_ave_prev =
+					(p + i-1)->cm_ave_exclude_edge;
+			}
+
+			if (cm_ave_cur == 0)
+				cm_ave_cur = 1;
+			/*multiple 1000 to increate accuracy*/
+			if (i <= 1)
+				(p + i)->gradient_val = (cm_max_cur - cm_min_cur
+				+ ABS(cm_ave_cur - cm_ave_next)) * 1000 /
+				cm_ave_cur;
+			else
+				(p + i)->gradient_val = (cm_max_cur - cm_min_cur
+				+ ABS(cm_ave_cur - cm_ave_prev)) * 1000 /
+				cm_ave_cur;
+		}
+	}
+}
+
+/*******************************************************************************
+ * FUNCTION: calculate_gradient_col
+ *
+ * SUMMARY: Calculates gradient value for columns.
+ *
+ * PARAMETERS:
+ *  *gd_sensor_row_head - pointer to gd_sensor structure
+ *   col_num            - number of column
+ *   exclude_row_edge   - flag to exclude row edge(1:exclude; 0:include)
+ *   exclude_col_edge   - flag to exclude column edge(1:exclude; 0:include)
+ ******************************************************************************/
+static void calculate_gradient_col(struct gd_sensor *gd_sensor_row_head,
+	uint16_t col_num, int exclude_row_edge, int exclude_col_edge)
+{
+	int i = 0;
+	int32_t cm_min_cur = 0;
+	int32_t cm_max_cur = 0;
+	int32_t cm_ave_cur = 0;
+	int32_t cm_ave_next = 0;
+	int32_t cm_ave_prev = 0;
+	struct gd_sensor *p = gd_sensor_row_head;
+
+	if (!exclude_col_edge) {
+		for (i = 0; i < col_num; i++) {
+			if (!exclude_row_edge) {
+				cm_ave_cur = (p + i)->cm_ave;
+				cm_min_cur = (p + i)->cm_min;
+				cm_max_cur = (p + i)->cm_max;
+				if (i < (col_num-1))
+					cm_ave_next = (p + i+1)->cm_ave;
+				if (i > 0)
+					cm_ave_prev = (p + i-1)->cm_ave;
+			} else {
+				cm_ave_cur = (p + i)->cm_ave_exclude_edge;
+				cm_min_cur = (p + i)->cm_min_exclude_edge;
+				cm_max_cur = (p + i)->cm_max_exclude_edge;
+				if (i < (col_num-1))
+					cm_ave_next =
+					(p + i+1)->cm_ave_exclude_edge;
+				if (i > 0)
+					cm_ave_prev =
+					(p + i-1)->cm_ave_exclude_edge;
+			}
+			if (cm_ave_cur == 0)
+				cm_ave_cur = 1;
+			/*multiple 1000 to increate accuracy*/
+			if (i <= 1)
+				(p + i)->gradient_val = (cm_max_cur - cm_min_cur
+				+ ABS(cm_ave_cur - cm_ave_next)) * 1000 /
+				cm_ave_cur;
+			else
+				(p + i)->gradient_val = (cm_max_cur - cm_min_cur
+				+ ABS(cm_ave_cur - cm_ave_prev)) * 1000 /
+				cm_ave_cur;
+		}
+	} else if (exclude_col_edge) {
+		for (i = 0; i < col_num; i++) {
+			if (!exclude_row_edge) {
+				cm_ave_cur = (p + i)->cm_ave;
+				cm_min_cur = (p + i)->cm_min;
+				cm_max_cur = (p + i)->cm_max;
+				if (i < (col_num-1))
+					cm_ave_next = (p + i+1)->cm_ave;
+				if (i > 0)
+					cm_ave_prev = (p + i-1)->cm_ave;
+			} else {
+				cm_ave_cur = (p + i)->cm_ave_exclude_edge;
+				cm_min_cur = (p + i)->cm_min_exclude_edge;
+				cm_max_cur = (p + i)->cm_max_exclude_edge;
+				if (i < (col_num-1))
+					cm_ave_next =
+					(p + i+1)->cm_ave_exclude_edge;
+				if (i > 0)
+					cm_ave_prev =
+					(p + i-1)->cm_ave_exclude_edge;
+			}
+
+			if (cm_ave_cur == 0)
+				cm_ave_cur = 1;
+			/*multiple 1000 to increate accuracy*/
+			if ((i == 0) || (i == (col_num - 1)))
+				(p + i)->gradient_val =
+					 (cm_max_cur - cm_min_cur) * 1000 /
+					 cm_ave_cur;
+			else if (i == 1)
+				(p + i)->gradient_val =
+					(cm_max_cur - cm_min_cur +
+					ABS(cm_ave_cur - cm_ave_next))
+					 * 1000 / cm_ave_cur;
+			else
+				(p + i)->gradient_val =
+					(cm_max_cur - cm_min_cur +
+					ABS(cm_ave_cur - cm_ave_prev))
+					* 1000 / cm_ave_cur;
+			}
+	}
+}
+
+/*******************************************************************************
+ * FUNCTION: fill_gd_sensor_table
+ *
+ * SUMMARY: Fills cm calculation result and exclude parameter to gd_sensor
+ *  structure.
+ *
+ * PARAMETERS:
+ *  *head                - pointer to gd_sensor structure
+ *   index               - index of row or column
+ *   cm_max              - maximum of cm
+ *   cm_min              - minmum of cm
+ *   cm_ave              - average of cm
+ *   cm_max_exclude_edge - maximum of cm without edge data
+ *   cm_min_exclude_edge - minmum of cm without edge data
+ *   cm_ave_exclude_edge - average of cm without edge data
+ ******************************************************************************/
+static void fill_gd_sensor_table(struct gd_sensor *head, int32_t index,
+	int32_t cm_max, int32_t cm_min,	int32_t cm_ave,
+	int32_t cm_max_exclude_edge, int32_t cm_min_exclude_edge,
+	int32_t cm_ave_exclude_edge)
+{
+	(head + index)->cm_max = cm_max;
+	(head + index)->cm_min = cm_min;
+	(head + index)->cm_ave = cm_ave;
+	(head + index)->cm_ave_exclude_edge = cm_ave_exclude_edge;
+	(head + index)->cm_max_exclude_edge = cm_max_exclude_edge;
+	(head + index)->cm_min_exclude_edge = cm_min_exclude_edge;
+}
+
+/*******************************************************************************
+ * FUNCTION: calculate_gd_info
+ *
+ * SUMMARY: Calculates gradient panel sensor column and row by calling
+ *  function calculate_gradient_col() & calculate_gradient_row().
+ *
+ * PARAMETERS:
+ *  *head                - pointer to gd_sensor structure
+ *   index               - index of row or column
+ *   cm_max              - maximum of cm
+ *   cm_min              - minmum of cm
+ *   cm_ave              - average of cm
+ *   cm_max_exclude_edge - maximum of cm without edge data
+ *   cm_min_exclude_edge - minmum of cm without edge data
+ *   cm_ave_exclude_edge - average of cm without edge data
+ ******************************************************************************/
+static void calculate_gd_info(struct gd_sensor *gd_sensor_col,
+	struct gd_sensor *gd_sensor_row, int tx_num, int rx_num,
+	int32_t *cm_sensor_data, int cm_excluding_row_edge,
+	int cm_excluding_col_edge)
+{
+	int32_t cm_max;
+	int32_t cm_min;
+	int32_t cm_ave;
+	int32_t cm_max_exclude_edge;
+	int32_t cm_min_exclude_edge;
+	int32_t cm_ave_exclude_edge;
+	int32_t cm_data;
+	int i;
+	int j;
+
+	/*calculate all the gradient related info for column*/
+	for (i = 0; i < tx_num; i++) {
+		/*re-initialize for a new col*/
+		cm_max = cm_sensor_data[i * rx_num];
+		cm_min = cm_max;
+		cm_ave = 0;
+		cm_max_exclude_edge = cm_sensor_data[i * rx_num + 1];
+		cm_min_exclude_edge = cm_max_exclude_edge;
+		cm_ave_exclude_edge = 0;
+
+		for (j = 0; j < rx_num; j++) {
+			cm_data = cm_sensor_data[i * rx_num + j];
+			if (cm_data > cm_max)
+				cm_max = cm_data;
+			if (cm_data < cm_min)
+				cm_min = cm_data;
+			cm_ave += cm_data;
+			/*calculate exclude edge data*/
+			if ((j > 0) && (j < (rx_num-1))) {
+				if (cm_data > cm_max_exclude_edge)
+					cm_max_exclude_edge = cm_data;
+				if (cm_data < cm_min_exclude_edge)
+					cm_min_exclude_edge = cm_data;
+				cm_ave_exclude_edge += cm_data;
+			}
+		}
+		cm_ave /= rx_num;
+		cm_ave_exclude_edge /= (rx_num-2);
+		fill_gd_sensor_table(gd_sensor_col, i, cm_max, cm_min, cm_ave,
+		cm_max_exclude_edge, cm_min_exclude_edge, cm_ave_exclude_edge);
+	}
+
+	calculate_gradient_col(gd_sensor_col, tx_num, cm_excluding_row_edge,
+		 cm_excluding_col_edge);
+
+	/*calculate all the gradient related info for row*/
+	for (j = 0; j < rx_num; j++) {
+		/*re-initialize for a new row*/
+		cm_max = cm_sensor_data[j];
+		cm_min = cm_max;
+		cm_ave = 0;
+		cm_max_exclude_edge = cm_sensor_data[rx_num + j];
+		cm_min_exclude_edge = cm_max_exclude_edge;
+		cm_ave_exclude_edge = 0;
+		for (i = 0; i < tx_num; i++) {
+			cm_data = cm_sensor_data[i * rx_num + j];
+			if (cm_data > cm_max)
+				cm_max = cm_data;
+			if (cm_data < cm_min)
+				cm_min = cm_data;
+			cm_ave += cm_data;
+			/*calculate exclude edge data*/
+			if ((i >  0) && (i < (tx_num-1))) {
+				if (cm_data > cm_max_exclude_edge)
+					cm_max_exclude_edge = cm_data;
+				if (cm_data < cm_min_exclude_edge)
+					cm_min_exclude_edge = cm_data;
+				cm_ave_exclude_edge += cm_data;
+			}
+		}
+		cm_ave /= tx_num;
+		cm_ave_exclude_edge /= (tx_num-2);
+		fill_gd_sensor_table(gd_sensor_row, j, cm_max, cm_min, cm_ave,
+		cm_max_exclude_edge, cm_min_exclude_edge, cm_ave_exclude_edge);
+	}
+	calculate_gradient_row(gd_sensor_row, rx_num, cm_excluding_row_edge,
+		 cm_excluding_col_edge);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_get_cmcp_info
+ *
+ * SUMMARY: Function to include following work:
+ *  1) run short test and get result
+ *  2) run selftest to get cm_panel data, cm_cal_data_panel data, calculate
+ *     cm_ave_data_panel, cm_sensor_delta and gradient by column and row.
+ *  3) run selftest to get cp_panel data, cp_cal_data_panel data, cacluate
+ *     cp_ave_data_panel, cp_sensor_delta for tx and rx.
+ *  4) run selftest to get cm_btn data, cm_cal_data_btn data, cacluate
+ *     cm_delta_data_btn data, cm_ave_data_btn data.
+ *  5) run selftest to get cp_btn data, cp_btn_cal data, cacluate
+ *     cp_button_delta data, cp_button_ave data.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *   *dad       - pointer to pt_device_access_data structure
+ *   *cmcp_info - pointer to cmcp_data structure
+ ******************************************************************************/
+static int  pt_get_cmcp_info(struct pt_device_access_data *dad,
+	struct cmcp_data *cmcp_info)
+{
+	struct device *dev;
+	int32_t *cm_data_panel = cmcp_info->cm_data_panel;
+	int32_t *cp_tx_data_panel = cmcp_info->cp_tx_data_panel;
+	int32_t *cp_rx_data_panel = cmcp_info->cp_rx_data_panel;
+	int32_t *cp_tx_cal_data_panel = cmcp_info->cp_tx_cal_data_panel;
+	int32_t *cp_rx_cal_data_panel = cmcp_info->cp_rx_cal_data_panel;
+	int32_t *cm_btn_data = cmcp_info->cm_btn_data;
+	int32_t *cp_btn_data = cmcp_info->cp_btn_data;
+	struct gd_sensor *gd_sensor_col = cmcp_info->gd_sensor_col;
+	struct gd_sensor *gd_sensor_row = cmcp_info->gd_sensor_row;
+	struct result *result = dad->result;
+	int32_t cp_btn_cal = 0;
+	int32_t cp_btn_ave = 0;
+	int32_t cm_ave_data_panel = 0;
+	int32_t cm_ave_data_btn = 0;
+	int32_t cp_tx_ave_data_panel = 0;
+	int32_t cp_rx_ave_data_panel = 0;
+	u8 tmp_buf[3];
+	int tx_num;
+	int rx_num;
+	int btn_num;
+	int rc = 0;
+	int i;
+
+	dev = dad->dev;
+	cmcp_info->tx_num = dad->si->sensing_conf_data.tx_num;
+	cmcp_info->rx_num = dad->si->sensing_conf_data.rx_num;
+	cmcp_info->btn_num = dad->si->num_btns;
+
+	tx_num = cmcp_info->tx_num;
+	rx_num = cmcp_info->rx_num;
+	btn_num = cmcp_info->btn_num;
+	pt_debug(dev, DL_INFO, "%s tx_num=%d", __func__, tx_num);
+	pt_debug(dev, DL_INFO, "%s rx_num=%d", __func__, rx_num);
+	pt_debug(dev, DL_INFO, "%s btn_num=%d", __func__, btn_num);
+
+	/*short test*/
+	result->short_test_pass = true;
+	rc = pt_run_and_get_selftest_result(dev, PT_CORE_CMD_UNPROTECTED,
+		tmp_buf, sizeof(tmp_buf),
+		PT_ST_ID_AUTOSHORTS, PIP_CMD_MAX_LENGTH,
+		PT_ST_DONT_GET_RESULTS, PT_ST_NOPRINT, PT_PR_FORMAT_DEFAULT);
+	if (rc) {
+		pt_debug(dev, DL_ERROR, "short test not supported");
+		goto exit;
+	}
+	if (dad->ic_buf[1] != 0)
+		result->short_test_pass = false;
+
+	/*Get cm_panel data*/
+	rc = pt_run_and_get_selftest_result(dev, PT_CORE_CMD_UNPROTECTED,
+		tmp_buf, sizeof(tmp_buf),
+		PT_ST_ID_CM_PANEL, PIP_CMD_MAX_LENGTH,
+		PT_ST_GET_RESULTS, PT_ST_NOPRINT, PT_PR_FORMAT_DEFAULT);
+	if (rc) {
+		pt_debug(dev, DL_ERROR, "Get CM Panel not supported");
+		goto exit;
+	}
+	if (cm_data_panel != NULL) {
+		for (i = 0; i < tx_num * rx_num;  i++) {
+			cm_data_panel[i] =
+			10*(dad->ic_buf[CM_PANEL_DATA_OFFSET+i*2] + 256
+			* dad->ic_buf[CM_PANEL_DATA_OFFSET+i*2+1]);
+			pt_debug(dev, DL_DEBUG,
+				"cm_data_panel[%d]=%d\n",
+				i, cm_data_panel[i]);
+			cm_ave_data_panel += cm_data_panel[i];
+		}
+		cm_ave_data_panel /= (tx_num * rx_num);
+		cmcp_info->cm_ave_data_panel = cm_ave_data_panel;
+
+		/* Calculate gradient panel sensor column/row here */
+		calculate_gd_info(gd_sensor_col, gd_sensor_row, tx_num, rx_num,
+			cm_data_panel, 1, 1);
+		for (i = 0; i < tx_num; i++) {
+			pt_debug(dev, DL_DEBUG,
+				"i=%d max=%d,min=%d,ave=%d, gradient=%d", i,
+				gd_sensor_col[i].cm_max,
+				gd_sensor_col[i].cm_min,
+				gd_sensor_col[i].cm_ave,
+				gd_sensor_col[i].gradient_val);
+		}
+
+		for (i = 0; i < rx_num; i++) {
+			pt_debug(dev, DL_DEBUG,
+				"i=%d max=%d,min=%d,ave=%d, gradient=%d", i,
+				gd_sensor_row[i].cm_max,
+				gd_sensor_row[i].cm_min,
+				gd_sensor_row[i].cm_ave,
+				gd_sensor_row[i].gradient_val);
+		}
+	}
+
+	/*Get cp data*/
+	rc = pt_run_and_get_selftest_result(dev, PT_CORE_CMD_UNPROTECTED,
+		tmp_buf, sizeof(tmp_buf),
+		PT_ST_ID_CP_PANEL, PIP_CMD_MAX_LENGTH,
+		PT_ST_GET_RESULTS, PT_ST_NOPRINT, PT_PR_FORMAT_DEFAULT);
+	if (rc) {
+		pt_debug(dev, DL_ERROR, "Get CP Panel not supported");
+		goto exit;
+	}
+	/*Get cp_tx_data_panel*/
+	if (cp_tx_data_panel != NULL) {
+		for (i = 0; i < tx_num; i++) {
+			cp_tx_data_panel[i] =
+			10*(dad->ic_buf[CP_PANEL_DATA_OFFSET+i*2]
+			+ 256 * dad->ic_buf[CP_PANEL_DATA_OFFSET+i*2+1]);
+			pt_debug(dev, DL_DEBUG,
+				"cp_tx_data_panel[%d]=%d\n",
+				i, cp_tx_data_panel[i]);
+			cp_tx_ave_data_panel += cp_tx_data_panel[i];
+		}
+		cp_tx_ave_data_panel /= tx_num;
+		cmcp_info->cp_tx_ave_data_panel = cp_tx_ave_data_panel;
+	}
+
+	/*Get cp_tx_cal_data_panel*/
+	if (cp_tx_cal_data_panel != NULL) {
+		for (i = 0; i < tx_num; i++) {
+			cp_tx_cal_data_panel[i] =
+			10*(dad->ic_buf[CP_PANEL_DATA_OFFSET+tx_num*2+i*2]
+		+ 256 * dad->ic_buf[CP_PANEL_DATA_OFFSET+tx_num*2+i*2+1]);
+			pt_debug(dev, DL_DEBUG, "cp_tx_cal_data_panel[%d]=%d\n",
+				i, cp_tx_cal_data_panel[i]);
+		}
+
+		/*get cp_sensor_tx_delta,using the first sensor cal value for temp */
+		/*multiple 1000 to increase accuracy*/
+		cmcp_info->cp_sensor_tx_delta = ABS((cp_tx_cal_data_panel[0]
+			- cp_tx_ave_data_panel) * 1000 / cp_tx_ave_data_panel);
+	}
+
+	/*Get cp_rx_data_panel*/
+	if (cp_rx_data_panel != NULL) {
+		for (i = 0; i < rx_num;  i++) {
+			cp_rx_data_panel[i] =
+			10*(dad->ic_buf[CP_PANEL_DATA_OFFSET+tx_num*4+i*2] +
+			256 * dad->ic_buf[CP_PANEL_DATA_OFFSET+tx_num*4+i*2+1]);
+			pt_debug(dev, DL_DEBUG,
+				"cp_rx_data_panel[%d]=%d\n", i,
+				cp_rx_data_panel[i]);
+			cp_rx_ave_data_panel += cp_rx_data_panel[i];
+		}
+		cp_rx_ave_data_panel /= rx_num;
+		cmcp_info->cp_rx_ave_data_panel = cp_rx_ave_data_panel;
+	}
+
+	/*Get cp_rx_cal_data_panel*/
+	if (cp_rx_cal_data_panel != NULL) {
+		for (i = 0; i < rx_num; i++) {
+			cp_rx_cal_data_panel[i] =
+		10 * (dad->ic_buf[CP_PANEL_DATA_OFFSET+tx_num*4+rx_num*2+i*2] +
+		256 *
+		dad->ic_buf[CP_PANEL_DATA_OFFSET+tx_num*4+rx_num*2+i*2+1]);
+			pt_debug(dev, DL_DEBUG,
+				"cp_rx_cal_data_panel[%d]=%d\n", i,
+				cp_rx_cal_data_panel[i]);
+		}
+
+		/*get cp_sensor_rx_delta,using the first sensor cal value for temp */
+		/*multiple 1000 to increase accuracy*/
+		cmcp_info->cp_sensor_rx_delta = ABS((cp_rx_cal_data_panel[0]
+			- cp_rx_ave_data_panel) * 1000 / cp_rx_ave_data_panel);
+	}
+
+	if (btn_num == 0) {
+		pt_debug(dev, DL_INFO, "%s: Skip Button Test\n", __func__);
+		goto skip_button_test;
+	}
+
+	/*get cm btn data*/
+	rc = pt_run_and_get_selftest_result(dev, PT_CORE_CMD_UNPROTECTED,
+		tmp_buf, sizeof(tmp_buf),
+		PT_ST_ID_CM_BUTTON, PIP_CMD_MAX_LENGTH,
+		PT_ST_GET_RESULTS, PT_ST_NOPRINT, PT_PR_FORMAT_DEFAULT);
+	if (rc) {
+		pt_debug(dev, DL_ERROR, "Get CM BTN not supported");
+		goto exit;
+	}
+	if (cm_btn_data != NULL) {
+		for (i = 0; i < btn_num; i++) {
+			cm_btn_data[i] =
+			10 * (dad->ic_buf[CM_BTN_DATA_OFFSET+i*2] +
+			256 * dad->ic_buf[CM_BTN_DATA_OFFSET+i*2+1]);
+			pt_debug(dev, DL_DEBUG,
+				" cm_btn_data[%d]=%d\n",
+				i, cm_btn_data[i]);
+			cm_ave_data_btn += cm_btn_data[i];
+		}
+		cm_ave_data_btn /= btn_num;
+		cmcp_info->cm_ave_data_btn = cm_ave_data_btn;
+	}
+
+	/*get cp btn data*/
+	rc = pt_run_and_get_selftest_result(dev, PT_CORE_CMD_UNPROTECTED,
+		tmp_buf, sizeof(tmp_buf),
+		PT_ST_ID_CP_BUTTON, PIP_CMD_MAX_LENGTH,
+		PT_ST_GET_RESULTS, PT_ST_NOPRINT, PT_PR_FORMAT_DEFAULT);
+	if (rc) {
+		pt_debug(dev, DL_ERROR, "Get CP BTN not supported");
+		goto exit;
+	}
+	if (cp_btn_data != NULL) {
+		for (i = 0; i < btn_num; i++) {
+			cp_btn_data[i] =
+			10 * (dad->ic_buf[CP_BTN_DATA_OFFSET+i*2] +
+			256 * dad->ic_buf[CP_BTN_DATA_OFFSET+i*2+1]);
+			cp_btn_ave += cp_btn_data[i];
+			pt_debug(dev, DL_DEBUG,
+				"cp_btn_data[%d]=%d\n",
+				i, cp_btn_data[i]);
+		}
+		cp_btn_ave /= btn_num;
+		cp_btn_cal = 10*(dad->ic_buf[CP_BTN_DATA_OFFSET+i*2]
+			 + 256 * dad->ic_buf[CP_BTN_DATA_OFFSET+i*2+1]);
+		cmcp_info->cp_button_ave = cp_btn_ave;
+		cmcp_info->cp_btn_cal = cp_btn_cal;
+		/*multiple 1000 to increase accuracy*/
+		cmcp_info->cp_button_delta = ABS((cp_btn_cal
+		- cp_btn_ave) * 1000 / cp_btn_ave);
+		pt_debug(dev, DL_INFO, " cp_btn_cal=%d\n",
+			cp_btn_cal);
+		pt_debug(dev, DL_INFO, " cp_btn_ave=%d\n",
+			cp_btn_ave);
+	}
+
+skip_button_test:
+exit:
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_get_cm_cal
+ *
+ * SUMMARY: Function to include following work:
+ *  1) run selftest to get cm_cal_data_panel, cm_sensor_delta
+ *  2) run selftest to get cm_cal_data_btn, cm_delta_data_btn
+ *
+ * NOTE:
+ *  This function depends on the calculation result of pt_get_cmcp_info()
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *   *dad       - pointer to pt_device_access_data structure
+ *   *cmcp_info - pointer to cmcp_data structure
+ ******************************************************************************/
+static int pt_get_cm_cal(struct pt_device_access_data *dad,
+	struct cmcp_data *cmcp_info)
+{
+	struct device *dev;
+	int32_t *cm_data_panel = cmcp_info->cm_data_panel;
+	int32_t *cm_btn_data = cmcp_info->cm_btn_data;
+	u8 tmp_buf[3];
+	int rc = 0;
+	int i;
+
+	dev = dad->dev;
+
+	/*Get cm_cal data*/
+	rc = pt_run_and_get_selftest_result(dev, PT_CORE_CMD_UNPROTECTED,
+			tmp_buf, sizeof(tmp_buf),
+			PT_ST_ID_CM_PANEL, PIP_CMD_MAX_LENGTH,
+			PT_ST_GET_RESULTS, PT_ST_NOPRINT, PT_PR_FORMAT_DEFAULT);
+	if (rc) {
+		pt_debug(dev, DL_ERROR, "Get CM Panel not supported");
+		goto exit;
+	}
+	if (cm_data_panel != NULL) {
+		i = cmcp_info->tx_num * cmcp_info->rx_num;
+		cmcp_info->cm_cal_data_panel =
+		    10 * (dad->ic_buf[CM_PANEL_DATA_OFFSET + i * 2] +
+			  256 * dad->ic_buf[CM_PANEL_DATA_OFFSET + i * 2 + 1]);
+		/*multiple 1000 to increase accuracy*/
+		cmcp_info->cm_sensor_delta =
+		    ABS((cmcp_info->cm_ave_data_panel -
+			 cmcp_info->cm_cal_data_panel) *
+			1000 / cmcp_info->cm_ave_data_panel);
+	}
+
+	if (cmcp_info->btn_num == 0) {
+		pt_debug(dev, DL_INFO, "%s: Skip Button Test\n", __func__);
+		goto skip_button_test;
+	}
+
+	/*get cm_btn_cal data*/
+	rc = pt_run_and_get_selftest_result(dev, PT_CORE_CMD_UNPROTECTED,
+			tmp_buf, sizeof(tmp_buf),
+			PT_ST_ID_CM_BUTTON, PIP_CMD_MAX_LENGTH,
+			PT_ST_GET_RESULTS, PT_ST_NOPRINT, PT_PR_FORMAT_DEFAULT);
+	if (rc) {
+		pt_debug(dev, DL_ERROR, "Get CM BTN not supported");
+		goto exit;
+	}
+	if (cm_btn_data != NULL) {
+		i = cmcp_info->btn_num;
+		cmcp_info->cm_cal_data_btn =
+		    10 * (dad->ic_buf[CM_BTN_DATA_OFFSET + i * 2] +
+			  256 * dad->ic_buf[CM_BTN_DATA_OFFSET + i * 2 + 1]);
+		/*multiple 1000 to increase accuracy*/
+		cmcp_info->cm_delta_data_btn = ABS(
+		    (cmcp_info->cm_ave_data_btn - cmcp_info->cm_cal_data_btn) *
+		    1000 / cmcp_info->cm_ave_data_btn);
+		pt_debug(dev, DL_INFO, " cm_btn_cal=%d\n",
+			 cmcp_info->cm_cal_data_btn);
+	}
+
+skip_button_test:
+exit:
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_free_cmcp_buf
+ *
+ * SUMMARY: Free pointers in cmcp_data structure
+ *
+ * PARAMETERS:
+ *  *cmcp_info  - pointer to cmcp_data structure
+ ******************************************************************************/
+static void pt_free_cmcp_buf(struct cmcp_data *cmcp_info)
+{
+	if (cmcp_info->gd_sensor_col != NULL)
+		kfree(cmcp_info->gd_sensor_col);
+	if (cmcp_info->gd_sensor_row != NULL)
+		kfree(cmcp_info->gd_sensor_row);
+	if (cmcp_info->cm_data_panel != NULL)
+		kfree(cmcp_info->cm_data_panel);
+	if (cmcp_info->cp_tx_data_panel != NULL)
+		kfree(cmcp_info->cp_tx_data_panel);
+	if (cmcp_info->cp_rx_data_panel != NULL)
+		kfree(cmcp_info->cp_rx_data_panel);
+	if (cmcp_info->cp_tx_cal_data_panel != NULL)
+		kfree(cmcp_info->cp_tx_cal_data_panel);
+	if (cmcp_info->cp_rx_cal_data_panel != NULL)
+		kfree(cmcp_info->cp_rx_cal_data_panel);
+	if (cmcp_info->cm_btn_data != NULL)
+		kfree(cmcp_info->cm_btn_data);
+	if (cmcp_info->cp_btn_data != NULL)
+		kfree(cmcp_info->cp_btn_data);
+	if (cmcp_info->cm_sensor_column_delta != NULL)
+		kfree(cmcp_info->cm_sensor_column_delta);
+	if (cmcp_info->cm_sensor_row_delta != NULL)
+		kfree(cmcp_info->cm_sensor_row_delta);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_cmcp_get_test_item
+ *
+ * SUMMARY: Parses enum cmcp_test_item to integer value test_item as bitwise
+ *  type.
+ *
+ * RETURN: integer value to indidate available test item with bitwise type
+ *
+ * PARAMETERS:
+ *	 item_input  - enum cmcp_test_item
+ ******************************************************************************/
+static int pt_cmcp_get_test_item(int item_input)
+{
+	int test_item = 0;
+
+	switch (item_input) {
+	case CMCP_FULL:
+		test_item = CMCP_FULL_CASE;
+		break;
+	case CMCP_CM_PANEL:
+		test_item = CM_PANEL;
+		break;
+	case CMCP_CP_PANEL:
+		test_item = CP_PANEL;
+		break;
+	case CMCP_CM_BTN:
+		test_item = CM_BTN;
+		break;
+	case CMCP_CP_BTN:
+		test_item = CP_BTN;
+		break;
+	}
+	return test_item;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_cmcp_test_show
+ *
+ * SUMMARY: Show method for cmcp_test sysfs node. Allows to perform cmcp test
+ *  with following steps:
+ *   1) Get cmcp test items which from threhold file
+ *   2) Check whether cmcp test items match with firmware
+ *   3) Set parameter to force single TX
+ *   4) Do calibration if requested
+ *   5) Get all cmcp data from FW and do calculation
+ *   6) Set parameter to restore to multi tx
+ *   7) Do calibration if requested
+ *   8) Check scan state,try to fix if it is not right
+ *   9) Start watchdog
+ *  10) Validate cm and cp test results if requested
+ *  11) Fill the test result
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*dev  - pointer to device structure
+ *	*attr - pointer to device attributes
+ *	*buf  - pointer to output buffer
+ ******************************************************************************/
+static ssize_t pt_cmcp_test_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct pt_device_access_data *dad
+		= pt_get_device_access_data(dev);
+	struct cmcp_data *cmcp_info = dad->cmcp_info;
+	struct result *result = dad->result;
+	struct configuration *configuration = dad->configs;
+	bool final_pass = true;
+	static const char * const cmcp_test_case_array[] = {"Full Cm/Cp test",
+		"Cm panel test", "Cp panel test",
+		"Cm button test", "Cp button test"};
+	int index = 0;
+	int test_item = 0;
+	int no_builtin_file = 0;
+	int rc = 0;
+	int self_test_result_1 = 0;
+	int self_test_result_2 = 0;
+	u8 sys_mode = FW_SYS_MODE_UNDEFINED;
+	u8 retry = 3;
+
+	dev = dad->dev;
+	if ((configuration == NULL) || (cmcp_info == NULL))
+		goto exit;
+
+	mutex_lock(&dad->sysfs_lock);
+
+	if (dad->cmcp_test_in_progress) {
+		mutex_unlock(&dad->sysfs_lock);
+		goto cmcp_not_ready;
+	}
+	dad->cmcp_test_in_progress = 1;
+
+	dad->test_executed = 0;
+	test_item = pt_cmcp_get_test_item(dad->cmcp_test_items);
+
+	if (dad->builtin_cmcp_threshold_status < 0) {
+		pt_debug(dev, DL_WARN, "%s: No cmcp threshold file.\n",
+			__func__);
+		no_builtin_file = 1;
+		mutex_unlock(&dad->sysfs_lock);
+		goto start_testing;
+	}
+
+	if (dad->cmcp_test_items < 0) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Invalid test item! Should be 0~4!\n", __func__);
+		mutex_unlock(&dad->sysfs_lock);
+		goto invalid_item;
+	}
+
+	pt_debug(dev, DL_INFO, "%s: Test item is %s, %d\n",
+		__func__, cmcp_test_case_array[dad->cmcp_test_items],
+		test_item);
+
+	if ((dad->si->num_btns == 0)
+		&& ((dad->cmcp_test_items == CMCP_CM_BTN)
+			|| (dad->cmcp_test_items == CMCP_CP_BTN))) {
+		pt_debug(dev, DL_WARN,
+			"%s: FW doesn't support button!\n", __func__);
+		mutex_unlock(&dad->sysfs_lock);
+		goto invalid_item_btn;
+	}
+
+	mutex_unlock(&dad->sysfs_lock);
+
+	if (cmcp_check_config_fw_match(dev, configuration))
+		goto mismatch;
+
+start_testing:
+	pt_debug(dev, DL_INFO, "%s: Start Cm/Cp test!\n", __func__);
+	result->cm_test_pass = true;
+	result->cp_test_pass = true;
+
+	/*stop watchdog*/
+	rc = cmd->request_stop_wd(dev);
+	if (rc)
+		pt_debug(dev, DL_ERROR, "stop watchdog failed");
+
+	/* Make sure the device is awake */
+	pm_runtime_get_sync(dev);
+	/* Resource protect */
+	rc = cmd->request_exclusive(dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
+	if (rc) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Error on request exclusive rc = %d\n",
+			__func__, rc);
+	}
+
+	/*force single tx*/
+	rc = cmd->nonhid_cmd->set_param(dev,
+			PT_CORE_CMD_UNPROTECTED, 0x1F, 1, 1);
+	if (rc)
+		pt_debug(dev, DL_ERROR, "force single tx failed");
+
+	/*suspend_scanning */
+	rc = cmd->nonhid_cmd->suspend_scanning(dev, PT_CORE_CMD_UNPROTECTED);
+	if (rc)
+		pt_debug(dev, DL_ERROR, "suspend_scanning failed");
+
+	/* Do calibration if requested */
+	if (!dad->cmcp_force_calibrate) {
+		pt_debug(dev, DL_INFO, "do calibration in single tx mode");
+		rc = pt_perform_calibration(dev);
+		if (rc)
+			pt_debug(dev, DL_ERROR, "calibration failed");
+	}
+	/*resume_scanning */
+	rc = cmd->nonhid_cmd->resume_scanning(dev, PT_CORE_CMD_UNPROTECTED);
+	if (rc)
+		pt_debug(dev, DL_ERROR, "resume_scanning failed");
+
+	/*get all cmcp data from FW*/
+	self_test_result_1 = pt_get_cmcp_info(dad, cmcp_info);
+	if (self_test_result_1)
+		pt_debug(dev, DL_ERROR, "pt_get_cmcp_info failed");
+
+	/*restore to multi tx*/
+	rc = cmd->nonhid_cmd->set_param(dev,
+			PT_CORE_CMD_UNPROTECTED, 0x1F, 0, 1);
+	if (rc)
+		pt_debug(dev, DL_ERROR, "restore multi tx failed");
+
+	/*suspend_scanning */
+	rc = cmd->nonhid_cmd->suspend_scanning(dev, 0);
+	if (rc)
+		pt_debug(dev, DL_ERROR, "suspend_scanning failed");
+
+	/* Do calibration if requested */
+	if (!dad->cmcp_force_calibrate) {
+		pt_debug(dev, DL_INFO, "do calibration in multi tx mode");
+		rc = pt_perform_calibration(dev);
+		if (rc)
+			pt_debug(dev, DL_ERROR, "calibration failed");
+	}
+	/*resume_scanning */
+	rc = cmd->nonhid_cmd->resume_scanning(dev, PT_CORE_CMD_UNPROTECTED);
+	if (rc)
+		pt_debug(dev, DL_ERROR, "resume_scanning failed");
+
+	/*get cm cal data from FW*/
+	self_test_result_2 = pt_get_cm_cal(dad, cmcp_info);
+	if (self_test_result_2)
+		pt_debug(dev, DL_ERROR, "pt_get_cm_cal failed");
+
+	/* check scan state,try to fix if it is not right*/
+	while (retry--) {
+		rc = cmd->request_get_fw_mode(dev, PT_CORE_CMD_UNPROTECTED,
+			&sys_mode, NULL);
+
+		if (sys_mode != FW_SYS_MODE_SCANNING) {
+			pt_debug(dev, DL_ERROR,
+				"%s: fw mode: %d, retry: %d, rc = %d\n",
+				__func__, sys_mode, retry, rc);
+			rc = cmd->nonhid_cmd->resume_scanning(dev,
+						PT_CORE_CMD_UNPROTECTED);
+		}
+	}
+
+	rc = cmd->release_exclusive(dev);
+	if (rc) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Error on release exclusive rc = %d\n",
+			__func__, rc);
+	}
+	pm_runtime_put(dev);
+
+	/*start  watchdog*/
+	rc = cmd->request_start_wd(dev);
+	if (rc)
+		pt_debug(dev, DL_ERROR, "start watchdog failed");
+
+	if (self_test_result_1 || self_test_result_2)
+		goto self_test_failed;
+
+	/* The tests are finished without failure */
+	mutex_lock(&dad->sysfs_lock);
+	dad->test_executed = 1;
+	mutex_unlock(&dad->sysfs_lock);
+
+	if (no_builtin_file)
+		goto no_builtin;
+
+	if ((test_item) & (CM_ENABLED))
+		validate_cm_test_results(dev, configuration, cmcp_info,
+		result, &final_pass, test_item);
+
+	if ((test_item) & (CP_ENABLED))
+		validate_cp_test_results(dev, configuration, cmcp_info,
+		result, &final_pass, test_item);
+
+	if ((dad->cmcp_test_items == CMCP_FULL)
+	&& (dad->cmcp_range_check == 0)) {
+		/*full test and full check*/
+		result->test_summary = result->cm_test_pass
+			&& result->cp_test_pass
+			&& result->short_test_pass;
+	} else if ((dad->cmcp_test_items == CMCP_FULL)
+		&& (dad->cmcp_range_check == 1)) {
+		/*full test and basic check*/
+		result->test_summary = result->cm_sensor_gd_col_pass
+			&& result->cm_sensor_gd_row_pass
+			&& result->cm_sensor_validation_pass
+			&& result->cp_rx_validation_pass
+			&& result->cp_tx_validation_pass
+			&& result->short_test_pass;
+	} else if (dad->cmcp_test_items == CMCP_CM_PANEL) {
+		/*cm panel test result only*/
+		result->test_summary = result->cm_sensor_gd_col_pass
+			&& result->cm_sensor_gd_row_pass
+			&& result->cm_sensor_validation_pass
+			&& result->cm_sensor_row_delta_pass
+			&& result->cm_sensor_col_delta_pass
+			&& result->cm_sensor_calibration_pass
+			&& result->cm_sensor_delta_pass;
+	} else if (dad->cmcp_test_items == CMCP_CP_PANEL) {
+		/*cp panel test result only*/
+		result->test_summary = result->cp_sensor_delta_pass
+			&& result->cp_rx_validation_pass
+			&& result->cp_tx_validation_pass;
+	} else if (dad->cmcp_test_items == CMCP_CM_BTN) {
+		/*cm button test result only*/
+		result->test_summary = result->cm_button_validation_pass
+			&& result->cm_button_delta_pass;
+	} else if (dad->cmcp_test_items == CMCP_CP_BTN) {
+		/*cp button test result only*/
+		result->test_summary = result->cp_button_delta_pass
+			&& result->cp_button_average_pass
+			&& result->cp_button_validation_pass;
+	}
+
+	if (result->test_summary) {
+		pt_debug(dev, DL_INFO,
+			"%s: Finish Cm/Cp test! All Test Passed\n", __func__);
+		index = scnprintf(buf, PT_MAX_PRBUF_SIZE, "Status: 1\n");
+	} else {
+		pt_debug(dev, DL_INFO,
+			"%s: Finish Cm/Cp test! Range Check Failure\n",
+			__func__);
+		index = scnprintf(buf, PT_MAX_PRBUF_SIZE, "Status: 6\n");
+	}
+	goto cmcp_ready;
+
+mismatch:
+	index = scnprintf(buf, PT_MAX_PRBUF_SIZE,
+		 "Status: 2\nInput cmcp threshold file mismatches with FW\n");
+	goto cmcp_ready;
+invalid_item_btn:
+	index = scnprintf(buf, PT_MAX_PRBUF_SIZE,
+		"Status: 3\nFW doesn't support button!\n");
+	goto cmcp_ready;
+invalid_item:
+	index = scnprintf(buf, PT_MAX_PRBUF_SIZE,
+		"Status: 4\nWrong test item or range check input!\nOnly support below items:\n0 - Cm/Cp Panel & Button with Gradient (Typical)\n1 - Cm Panel with Gradient\n2 - Cp Panel\n3 - Cm Button\n4 - Cp Button\nOnly support below range check:\n0 - Full Range Checking (default)\n1 - Basic Range Checking(TSG5 style)\n");
+	goto cmcp_ready;
+self_test_failed:
+	index = scnprintf(buf, PT_MAX_PRBUF_SIZE,
+		"Status: 5\nget self test ID not supported!\n");
+	goto cmcp_ready;
+cmcp_not_ready:
+	index = scnprintf(buf, PT_MAX_PRBUF_SIZE, "Status: 0\n");
+	goto cmcp_ready;
+no_builtin:
+	index = scnprintf(buf, PT_MAX_PRBUF_SIZE,
+		"Status: 7\nNo cmcp threshold file!\n");
+cmcp_ready:
+	mutex_lock(&dad->sysfs_lock);
+	dad->cmcp_test_in_progress = 0;
+	mutex_unlock(&dad->sysfs_lock);
+exit:
+	return index;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_cmcp_test_store
+ *
+ * SUMMARY: The store method for cmcp_test sysfs node.Allows the user to
+ *  configure which cm/cp tests will be executed on the "cat" of this node.
+ *
+ * RETURN: Size of passed in buffer
+ *
+ * PARAMETERS:
+ *  *dev  - pointer to device structure
+ *  *attr - pointer to device attributes
+ *  *buf  - pointer to buffer that hold the command parameters
+ *   size - size of buf
+ ******************************************************************************/
+static ssize_t pt_cmcp_test_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	struct pt_device_access_data *dad
+		= pt_get_device_access_data(dev);
+	u8 test_item = 0;
+	u8 range_check = 0;
+	u8 force_calibrate = 0;
+	u32 input_data[4];
+	int ret = 0;
+	static const char * const cmcp_test_case_array[] = {"Full Cm/Cp test",
+		"Cm panel test", "Cp panel test",
+		"Cm button test", "Cp button test"};
+	static const char * const cmcp_test_range_check_array[] = {
+		"Full (default)", "Basic"};
+	static const char * const cmcp_test_force_cal_array[] =	{
+		"Calibrate When Testing (default)", "No Calibration"};
+	ssize_t length = 0;
+
+	pm_runtime_get_sync(dev);
+	mutex_lock(&dad->sysfs_lock);
+
+	length = cmd->parse_sysfs_input(dev, buf, size, input_data,
+			ARRAY_SIZE(input_data));
+	if (length <= 0 || length > 3) {
+		pt_debug(dev, DL_ERROR, "%s: Input format error!\n",
+					__func__);
+		dad->cmcp_test_items = -EINVAL;
+		ret = -EINVAL;
+		goto error;
+	}
+
+	/* Get test item  */
+	test_item = input_data[0];
+	/* Get range check */
+	if (length >= 2)
+		range_check = input_data[1];
+	/* Get force calibration */
+	if (length == 3)
+		force_calibrate = input_data[2];
+
+	/*
+	 * Test item limitation:
+	 *	 0: Perform all Tests
+	 *	 1: CM Panel with Gradient
+	 *	 2: CP Panel
+	 *	 3: CM Button
+	 *	 4: CP Button
+	 * Ranage check limitation:
+	 *	 0: full check
+	 *	 1: basic check
+	 * Force calibrate limitation:
+	 *	 0: do calibration
+	 *	 1: don't do calibration
+	 */
+	if ((test_item < 0) || (test_item > 4) || (range_check > 1)
+		|| (force_calibrate > 1)) {
+		pt_debug(dev, DL_ERROR,
+		"%s: Test item should be 0~4; Range check should be 0~1; Force calibrate should be 0~1\n",
+		__func__);
+		dad->cmcp_test_items = -EINVAL;
+		ret = -EINVAL;
+		goto error;
+	}
+	/*
+	 * If it is not all Test, then range_check should be 0
+	 * because other test does not has concept of basic check
+	 */
+	if (test_item > 0 && test_item < 5)
+		range_check = 0;
+
+	dad->cmcp_test_items = test_item;
+	dad->cmcp_range_check = range_check;
+	dad->cmcp_force_calibrate = force_calibrate;
+	pt_debug(dev, DL_INFO,
+		"%s: Test item=%s; Range check=%s; Force cal=%s.\n",
+		__func__,
+		cmcp_test_case_array[test_item],
+		cmcp_test_range_check_array[range_check],
+		cmcp_test_force_cal_array[force_calibrate]);
+
+error:
+	mutex_unlock(&dad->sysfs_lock);
+	pm_runtime_put(dev);
+
+	if (ret)
+		return ret;
+
+	return size;
+}
+
+static DEVICE_ATTR(cmcp_test, 0600,
+	pt_cmcp_test_show, pt_cmcp_test_store);
+
+/*******************************************************************************
+ * FUNCTION: prepare_print_string
+ *
+ * SUMMARY: Formats input buffer to out buffer with string type,and increases
+ *  the index by size of formated data.
+ *
+ * RETURN:
+ *	 index plus with size of formated data
+ *
+ * PARAMETERS:
+ *  *out_buf  - output buffer to store formated data
+ *  *in_buf   - input buffer to be formated
+ *   index    - index in output buffer for appending content
+ ******************************************************************************/
+int prepare_print_string(char *out_buf, char *in_buf, int index)
+{
+	if ((out_buf == NULL) || (in_buf == NULL))
+		return index;
+	index += scnprintf(&out_buf[index], MAX_BUF_LEN - index,
+			"%s", in_buf);
+	return index;
+}
+
+/*******************************************************************************
+ * FUNCTION: prepare_print_string
+ *
+ * SUMMARY: Formats input buffer to out buffer with decimal base,and increases
+ *  the index by size of formated data.
+ *
+ * RETURN:
+ *	 index plus with size of formated data
+ *
+ * PARAMETERS:
+ *  *out_buf  - output buffer to store formated data
+ *  *in_buf   - input buffer to be formated
+ *   index    - index in output buffer for appending content
+ *   data_num - data number in input buffer
+ ******************************************************************************/
+int prepare_print_data(char *out_buf, int32_t *in_buf, int index, int data_num)
+{
+	int i;
+
+	if ((out_buf == NULL) || (in_buf == NULL))
+		return index;
+	for (i = 0; i < data_num; i++)
+		index += scnprintf(&out_buf[index], MAX_BUF_LEN - index,
+				"%d,", in_buf[i]);
+	return index;
+}
+
+/*******************************************************************************
+ * FUNCTION: save_header
+ *
+ * SUMMARY: Appends "header" for cmcp test result to output buffer.
+ *
+ * RETURN:
+ *	 index plus with size of formated data
+ *
+ * PARAMETERS:
+ *  *out_buf  - output buffer to store formated data
+ *   index    - index in output buffer for appending content
+ *  *result   - pointer to result structure
+ ******************************************************************************/
+static int save_header(char *out_buf, int index, struct result *result)
+{
+	struct rtc_time tm;
+	char time_buf[100] = {0};
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0))
+	struct timespec64 ts;
+
+	ktime_get_real_ts64(&ts);
+	rtc_time64_to_tm(ts.tv_sec, &tm);
+#else
+	struct timex txc;
+
+	do_gettimeofday(&(txc.time));
+	rtc_time_to_tm(txc.time.tv_sec, &tm);
+#endif
+
+	scnprintf(time_buf, 100, "%d/%d/%d,TIME,%d:%d:%d,", tm.tm_year+1900,
+		 tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
+
+	index = prepare_print_string(out_buf, ",.header,\n", index);
+	index = prepare_print_string(out_buf, ",DATE,", index);
+	index = prepare_print_string(out_buf, &time_buf[0], index);
+	index = prepare_print_string(out_buf, ",\n", index);
+	index = prepare_print_string(out_buf, ",SW_VERSION,", index);
+	index = prepare_print_string(out_buf, PT_DRIVER_VERSION, index);
+	index = prepare_print_string(out_buf, ",\n", index);
+	index = prepare_print_string(out_buf, ",.end,\n", index);
+	index = prepare_print_string(out_buf, ",.engineering data,\n", index);
+
+	return index;
+}
+
+/*******************************************************************************
+ * FUNCTION: print_silicon_id
+ *
+ * SUMMARY: Formats input buffer(silicon id) to out buffer with
+ *  string type,and increases the index by size of formated data.
+ *
+ * RETURN:
+ *	 index plus with size of formated data
+ *
+ * PARAMETERS:
+ *  *out_buf  - output buffer to store formated data
+ *  *in_buf   - input buffer to be formated
+ *   index    - index in output buffer for appending content
+ ******************************************************************************/
+static int print_silicon_id(char *out_buf, char *in_buf, int index)
+{
+	index = prepare_print_string(out_buf, ",1,", index);
+	index = prepare_print_string(out_buf, &in_buf[0], index);
+	return index;
+}
+
+/*******************************************************************************
+ * FUNCTION: save_engineering_data
+ *
+ * SUMMARY: Generates cmcp test result with *.csv format to output buffer, but
+ *  it doesn't include the header.
+ *
+ * RETURN:
+ *	 index plus with size of formated data
+ *
+ * PARAMETERS:
+ *  *dev             - pointer to device structure
+ *  *out_buf         - output buffer to store formated data
+ *   index           - index in output buffer for appending content
+ *  *cmcp_info       - pointer to cmcp_data structure
+ *  *configuration   - pointer to configuration structure
+ *  *result          - pointer to result structure
+ *   test_item       - test control in bitwise
+ *   no_builtin_file - flag to determin if builtin-file exist
+ ******************************************************************************/
+int save_engineering_data(struct device *dev, char *out_buf, int index,
+	struct cmcp_data *cmcp_info, struct configuration *configuration,
+	struct result *result, int test_item, int no_builtin_file)
+{
+	int i;
+	int j;
+	int tx_num = 0;
+	int rx_num = 0;
+	int btn_num = 0;
+	int tmp = 0;
+	uint32_t fw_revision_control;
+	uint32_t fw_config_ver;
+	char device_id[20] = {0};
+	struct pt_device_access_data *dad
+		= pt_get_device_access_data(dev);
+
+	if ((result == NULL) || (cmcp_info == NULL))
+		return -EINVAL;
+
+	tx_num = cmcp_info->tx_num;
+	rx_num = cmcp_info->rx_num;
+	btn_num = cmcp_info->btn_num;
+	fw_revision_control = dad->si->ttdata.revctrl;
+	fw_config_ver = dad->si->ttdata.fw_ver_conf;
+	/*calculate silicon id*/
+	result->device_id_low = 0;
+	result->device_id_high = 0;
+
+	for (i = 0; i < 4; i++)
+		result->device_id_low =
+		(result->device_id_low << 8) + dad->si->ttdata.mfg_id[i];
+
+	for (i = 4; i < 8; i++)
+		result->device_id_high =
+		(result->device_id_high << 8) + dad->si->ttdata.mfg_id[i];
+
+	scnprintf(device_id, 20, "%x%x",
+		result->device_id_high, result->device_id_low);
+
+	/*print test summary*/
+	index = print_silicon_id(out_buf, &device_id[0], index);
+	if (result->test_summary)
+		index = prepare_print_string(out_buf, ",PASS,\n", index);
+	else
+		index = prepare_print_string(out_buf, ",FAIL,\n", index);
+
+	/*revision ctrl number*/
+	index = print_silicon_id(out_buf, &device_id[0], index);
+	index = prepare_print_string(out_buf, ",FW revision Control,", index);
+	index = prepare_print_data(out_buf, &fw_revision_control, index, 1);
+	index = prepare_print_string(out_buf, "\n", index);
+
+	/*config version*/
+	index = print_silicon_id(out_buf, &device_id[0], index);
+	index = prepare_print_string(out_buf, ",CONFIG_VER,", index);
+	index = prepare_print_data(out_buf, &fw_config_ver, index, 1);
+	index = prepare_print_string(out_buf, "\n", index);
+
+	/* Shorts test */
+	index = print_silicon_id(out_buf, &device_id[0], index);
+	if (result->short_test_pass)
+		index = prepare_print_string(out_buf, ",Shorts,PASS,\n", index);
+	else
+		index = prepare_print_string(out_buf, ",Shorts,FAIL,\n", index);
+
+	if ((test_item & CM_ENABLED) == CM_ENABLED) {
+		/*print BUTNS_CM_DATA_ROW00*/
+		if (((test_item & CM_BTN) == CM_BTN) && (btn_num > 0)) {
+			index = print_silicon_id(out_buf, &device_id[0], index);
+			index = prepare_print_string(out_buf,
+				",Sensor Cm Validation,BUTNS_CM_DATA_ROW00,",
+				index);
+			index = prepare_print_data(out_buf,
+				&cmcp_info->cm_btn_data[0],
+				index,
+				btn_num);
+			index = prepare_print_string(out_buf, "\n", index);
+		}
+
+		if ((test_item & CM_PANEL) == CM_PANEL) {
+			/*print CM_DATA_ROW*/
+			for (i = 0; i < rx_num; i++) {
+				index = print_silicon_id(out_buf, &device_id[0],
+							index);
+				index = prepare_print_string(out_buf,
+							",Sensor Cm Validation,CM_DATA_ROW",
+							index);
+				index = prepare_print_data(out_buf, &i,
+							index, 1);
+				for (j = 0; j < tx_num; j++)
+					index = prepare_print_data(out_buf,
+					&cmcp_info->cm_data_panel[j*rx_num+i],
+					index, 1);
+				index = prepare_print_string(out_buf,
+					"\n", index);
+			}
+
+			if (!no_builtin_file) {
+				/*print CM_MAX_GRADIENT_COLS_PERCENT*/
+				index = print_silicon_id(out_buf,
+							&device_id[0], index);
+				index = prepare_print_string(out_buf,
+					 ",Sensor Cm Validation,CM_MAX_GRADIENT_COLS_PERCENT,",
+					index);
+				for (i = 0; i < tx_num; i++) {
+					char tmp_buf[10] = {0};
+
+					scnprintf(tmp_buf, 10, "%d.%d,",
+				cmcp_info->gd_sensor_col[i].gradient_val / 10,
+				cmcp_info->gd_sensor_col[i].gradient_val % 10);
+					index = prepare_print_string(out_buf,
+						&tmp_buf[0], index);
+				}
+				index = prepare_print_string(out_buf,
+								"\n", index);
+
+				/*print CM_MAX_GRADIENT_ROWS_PERCENT*/
+				index = print_silicon_id(out_buf,
+							&device_id[0], index);
+				index = prepare_print_string(out_buf,
+			",Sensor Cm Validation,CM_MAX_GRADIENT_ROWS_PERCENT,",
+					index);
+				for (i = 0; i < rx_num; i++) {
+					char tmp_buf[10] = {0};
+
+					scnprintf(tmp_buf, 10, "%d.%d,",
+				cmcp_info->gd_sensor_row[i].gradient_val / 10,
+				cmcp_info->gd_sensor_row[i].gradient_val % 10);
+					index = prepare_print_string(out_buf,
+						&tmp_buf[0], index);
+				}
+				index = prepare_print_string(out_buf,
+								"\n", index);
+
+				if (!dad->cmcp_range_check) {
+					/*print CM_DELTA_COLUMN*/
+					for (i = 0; i < rx_num; i++) {
+						index = print_silicon_id(
+							out_buf,
+							&device_id[0], index);
+						index = prepare_print_string(
+							out_buf,
+							",Sensor Cm Validation,DELTA_COLUMNS_ROW",
+							index);
+						index = prepare_print_data(
+							out_buf,
+							&i, index, 1);
+						index = prepare_print_data(
+							out_buf,
+							&tmp, index, 1);
+						for (j = 1; j < tx_num; j++)
+							index = prepare_print_data(
+							out_buf,
+				&cmcp_info->cm_sensor_column_delta[(j-1)*rx_num+i],
+							index, 1);
+						index = prepare_print_string(
+								out_buf,
+								"\n", index);
+					}
+
+					/*print CM_DELTA_ROW*/
+					index = print_silicon_id(out_buf,
+								&device_id[0],
+								index);
+					index = prepare_print_string(out_buf,
+						 ",Sensor Cm Validation,DELTA_ROWS_ROW",
+								index);
+					index = prepare_print_data(out_buf,
+								&tmp, index, 1);
+					for (j = 0; j < tx_num; j++)
+						index = prepare_print_data(
+								out_buf,
+								&tmp, index, 1);
+					index = prepare_print_string(out_buf,
+								"\n", index);
+
+					for (i = 1; i < rx_num; i++) {
+						index = print_silicon_id(
+								out_buf,
+								&device_id[0],
+								index);
+						index = prepare_print_string(
+								out_buf,
+						",Sensor Cm Validation,DELTA_ROWS_ROW",
+								index);
+						index = prepare_print_data(
+								out_buf, &i,
+								index, 1);
+						for (j = 0; j < tx_num; j++)
+							index = prepare_print_data(
+								out_buf,
+					&cmcp_info->cm_sensor_row_delta[j*rx_num+i-1],
+								index, 1);
+						index = prepare_print_string(
+							out_buf,
+							"\n", index);
+					}
+
+				/*print pass/fail Sensor Cm Validation*/
+				index = print_silicon_id(out_buf, &device_id[0],
+							index);
+				if (result->cm_test_pass)
+					index = prepare_print_string(out_buf,
+						",Sensor Cm Validation,PASS,\n",
+						index);
+				else
+					index = prepare_print_string(out_buf,
+						",Sensor Cm Validation,FAIL,\n",
+						index);
+			}
+			}
+		}
+
+		if (!no_builtin_file) {
+			if (((test_item & CM_BTN) == CM_BTN) && (btn_num > 0)
+				&& (!dad->cmcp_range_check)) {
+				char tmp_buf[10] = {0};
+				/*print Button Element by Element */
+				index = print_silicon_id(out_buf, &device_id[0],
+					index);
+				if (result->cm_button_validation_pass)
+					index = prepare_print_string(out_buf,
+					",Sensor Cm Validation - Button Element by Element,PASS\n",
+					index);
+				else
+					index = prepare_print_string(out_buf,
+					",Sensor Cm Validation - Button Element by Element,FAIL\n",
+					index);
+
+				/*
+				 *print  Sensor Cm Validation
+				 *- Buttons Range Buttons Range
+				 */
+				index = print_silicon_id(out_buf,
+					&device_id[0], index);
+				index = prepare_print_string(out_buf,
+					 ",Sensor Cm Validation - Buttons Range,Buttons Range,",
+					 index);
+				scnprintf(tmp_buf, 10, "%d.%d,",
+					 cmcp_info->cm_delta_data_btn / 10,
+					 cmcp_info->cm_delta_data_btn % 10);
+				index = prepare_print_string(out_buf,
+					&tmp_buf[0], index);
+				index = prepare_print_string(out_buf,
+					"\n", index);
+
+				/*print  Sensor Cm Validation
+				 *-Buttons Range Cm_button_avg
+				 */
+				index = print_silicon_id(out_buf,
+					&device_id[0], index);
+				index = prepare_print_string(out_buf,
+					 ",Sensor Cm Validation - Buttons Range,Cm_button_avg,",
+					 index);
+				index = prepare_print_data(out_buf,
+					&cmcp_info->cm_ave_data_btn,
+					 index, 1);
+				index = prepare_print_string(out_buf,
+					"\n", index);
+
+				/*print  Sensor Cm Validation
+				 * -Buttons Range Cm_button_avg
+				 */
+				index = print_silicon_id(out_buf,
+					&device_id[0], index);
+				index = prepare_print_string(out_buf,
+					",Sensor Cm Validation - Buttons Range,Cm_button_cal,",
+					index);
+				index = prepare_print_data(out_buf,
+					&cmcp_info->cm_cal_data_btn,
+					index, 1);
+				index = prepare_print_string(out_buf,
+					"\n", index);
+
+				/*print  Sensor Cm Validation
+				 *-Buttons Range pass/fail
+				 */
+				index = print_silicon_id(out_buf,
+					&device_id[0], index);
+				if (result->cm_button_delta_pass)
+					index = prepare_print_string(out_buf,
+						",Sensor Cm Validation - Buttons Range,PASS,LIMITS,",
+						index);
+				else
+					index = prepare_print_string(out_buf,
+						",Sensor Cm Validation - Buttons Range,FAIL,LIMITS,",
+						index);
+				index = prepare_print_data(out_buf,
+				&configuration->cm_max_delta_button_percent,
+				index, 1);
+				index = prepare_print_string(out_buf,
+				"\n", index);
+			}
+
+			if ((test_item & CM_PANEL) == CM_PANEL &&
+				!dad->cmcp_range_check) {
+				char tmp_buf[10] = {0};
+				/*print Cm_sensor_cal */
+				index = print_silicon_id(out_buf,
+					&device_id[0], index);
+				index = prepare_print_string(out_buf,
+					",Sensor Cm Validation - Calibration,Cm_sensor_cal,",
+					index);
+				index = prepare_print_data(out_buf,
+					&cmcp_info->cm_cal_data_panel,
+					index, 1);
+				index = prepare_print_string(out_buf,
+					"\n", index);
+
+				/*print Cm_sensor_cal limit*/
+				index = print_silicon_id(out_buf,
+					&device_id[0], index);
+				if (result->cm_sensor_calibration_pass)
+					index = prepare_print_string(out_buf,
+						",Sensor Cm Validation - Calibration,PASS,LIMITS,",
+						index);
+				else
+					index = prepare_print_string(out_buf,
+						",Sensor Cm Validation - Calibration,FAIL,LIMITS,",
+						index);
+				index = prepare_print_data(out_buf,
+					&configuration->cm_min_limit_cal,
+					index, 1);
+				index = prepare_print_data(out_buf,
+					&configuration->cm_max_limit_cal,
+					index, 1);
+				index = prepare_print_string(out_buf,
+					"\n", index);
+
+				/*print Columns Delta Matrix*/
+				index = print_silicon_id(out_buf,
+					&device_id[0], index);
+				if (result->cm_sensor_col_delta_pass)
+					index = prepare_print_string(out_buf,
+					",Sensor Cm Validation - Columns Delta Matrix,PASS,LIMITS,",
+					index);
+				else
+					index = prepare_print_string(out_buf,
+					",Sensor Cm Validation - Columns Delta Matrix,FAIL,LIMITS,",
+					index);
+				index = prepare_print_data(out_buf,
+					&configuration->cm_range_limit_col,
+					index, 1);
+				index = prepare_print_string(out_buf,
+					"\n", index);
+
+				/*print Cm Validation - Element by Element*/
+				index = print_silicon_id(out_buf,
+					&device_id[0], index);
+				if (result->cm_sensor_validation_pass)
+					index = prepare_print_string(out_buf,
+						",Sensor Cm Validation - Element by Element,PASS,",
+						index);
+				else
+					index = prepare_print_string(out_buf,
+						",Sensor Cm Validation - Element by Element,FAIL,",
+						index);
+				index = prepare_print_string(out_buf,
+					"\n", index);
+
+				/*print Cm Validation -Gradient Cols*/
+				index = print_silicon_id(out_buf,
+					&device_id[0], index);
+				if (result->cm_sensor_gd_col_pass)
+					index = prepare_print_string(out_buf,
+						",Sensor Cm Validation - Gradient Cols,PASS,",
+						index);
+				else
+					index = prepare_print_string(out_buf,
+						",Sensor Cm Validation - Gradient Cols,FAIL,",
+						index);
+				index = prepare_print_string(out_buf,
+					"\n", index);
+
+				/*print Cm Validation -Gradient Rows*/
+				index = print_silicon_id(out_buf,
+					&device_id[0], index);
+				if (result->cm_sensor_gd_row_pass)
+					index = prepare_print_string(out_buf,
+						",Sensor Cm Validation - Gradient Rows,PASS,",
+						index);
+				else
+					index = prepare_print_string(out_buf,
+						",Sensor Cm Validation - Gradient Rows,FAIL,",
+						index);
+				index = prepare_print_string(out_buf,
+					"\n", index);
+
+
+				/*
+				 * Print Sensor Cm Validation
+				 * -Rows Delta Matrix
+				 */
+				index = print_silicon_id(out_buf,
+					&device_id[0], index);
+				if (result->cm_sensor_row_delta_pass)
+					index = prepare_print_string(out_buf,
+					",Sensor Cm Validation - Rows Delta Matrix,PASS,LIMITS,",
+					index);
+				else
+					index = prepare_print_string(out_buf,
+					",Sensor Cm Validation - Rows Delta Matrix,FAIL,LIMITS,",
+					index);
+				index = prepare_print_data(out_buf,
+					&configuration->cm_range_limit_row,
+					index, 1);
+				index = prepare_print_string(out_buf,
+					"\n", index);
+
+				/*print Cm_sensor_avg */
+				index = print_silicon_id(out_buf,
+					&device_id[0], index);
+				index = prepare_print_string(out_buf,
+					",Sensor Cm Validation - Sensor Range,Cm_sensor_avg,",
+					index);
+				index = prepare_print_data(out_buf,
+					&cmcp_info->cm_ave_data_panel,
+					index, 1);
+				index = prepare_print_string(out_buf,
+					"\n", index);
+
+				/*printSensor Cm Validation -
+				 * Sensor Range,   Sensor Range
+				 */
+				index = print_silicon_id(out_buf,
+					&device_id[0], index);
+				index = prepare_print_string(out_buf,
+					",Sensor Cm Validation - Sensor Range,Sensor Range,",
+					index);
+				scnprintf(tmp_buf, 10, "%d.%d,",
+					cmcp_info->cm_sensor_delta / 10,
+					cmcp_info->cm_sensor_delta % 10);
+				index = prepare_print_string(out_buf,
+					&tmp_buf[0], index);
+				index = prepare_print_string(out_buf,
+					"\n", index);
+
+				/*print Sensor Cm Validation - Sensor Range*/
+				index = print_silicon_id(out_buf,
+					&device_id[0], index);
+				if (result->cm_sensor_delta_pass)
+					index = prepare_print_string(out_buf,
+						",Sensor Cm Validation - Sensor Range,PASS,LIMITS,",
+						index);
+				else
+					index = prepare_print_string(out_buf,
+						",Sensor Cm Validation - Sensor Range,FAIL,LIMITS,",
+						index);
+				index = prepare_print_data(out_buf,
+				&configuration->cm_max_delta_sensor_percent,
+						index, 1);
+				index = prepare_print_string(out_buf,
+					"\n", index);
+			}
+		}
+	}
+
+	if ((test_item & CP_ENABLED) == CP_ENABLED) {
+		if (((test_item & CP_BTN) == CP_BTN) && (btn_num > 0)) {
+			/*print   BUTNS_CP_DATA_ROW00  */
+			index = print_silicon_id(out_buf, &device_id[0], index);
+			index = prepare_print_string(out_buf,
+			",Self-cap Calibration Check,BUTNS_CP_DATA_ROW00,",
+				index);
+			index = prepare_print_data(out_buf,
+				&cmcp_info->cp_btn_data[0],
+				index, btn_num);
+			index = prepare_print_string(out_buf,
+				"\n", index);
+
+			if (!no_builtin_file && !dad->cmcp_range_check) {
+				/*print Cp Button Element by Element */
+				index = print_silicon_id(out_buf, &device_id[0],
+					index);
+				if (result->cp_button_validation_pass)
+					index = prepare_print_string(out_buf,
+					",Self-cap Calibration Check - Button Element by Element,PASS\n",
+					index);
+				else
+					index = prepare_print_string(out_buf,
+					",Self-cap Calibration Check - Button Element by Element,FAIL\n",
+					index);
+
+				/*print   cp_button_ave  */
+				index = print_silicon_id(out_buf,
+					&device_id[0], index);
+				index = prepare_print_string(out_buf,
+				",Self-cap Calibration Check,Cp_button_avg,",
+					index);
+				index = prepare_print_data(out_buf,
+					&cmcp_info->cp_button_ave,
+					index, 1);
+				index = prepare_print_string(out_buf,
+					"\n", index);
+
+				/*print   Cp_button_cal  */
+				index = print_silicon_id(out_buf,
+					&device_id[0], index);
+				index = prepare_print_string(out_buf,
+				",Self-cap Calibration Check,Cp_button_cal,",
+					index);
+				index = prepare_print_data(out_buf,
+					&cmcp_info->cp_btn_cal,
+					index, 1);
+				index = prepare_print_string(out_buf,
+					"\n", index);
+			}
+		}
+
+		if ((test_item & CP_PANEL) == CP_PANEL) {
+			/*print CP_DATA_RX */
+			index = print_silicon_id(out_buf, &device_id[0], index);
+			index = prepare_print_string(out_buf,
+			",Self-cap Calibration Check,CP_DATA_RX,", index);
+			index = prepare_print_data(out_buf,
+				&cmcp_info->cp_rx_data_panel[0], index, rx_num);
+			index = prepare_print_string(out_buf, "\n", index);
+
+			/*print CP_DATA_TX */
+			index = print_silicon_id(out_buf, &device_id[0], index);
+			index = prepare_print_string(out_buf,
+			",Self-cap Calibration Check,CP_DATA_TX,", index);
+			index = prepare_print_data(out_buf,
+				&cmcp_info->cp_tx_data_panel[0], index, tx_num);
+			index = prepare_print_string(out_buf, "\n", index);
+		}
+		if (((test_item & CP_BTN) == CP_BTN) && (btn_num > 0)
+			&& !dad->cmcp_range_check) {
+			if (!no_builtin_file) {
+				char tmp_buf[10] = {0};
+				/*print  Cp_delta_button */
+				index = print_silicon_id(out_buf, &device_id[0],
+					index);
+				index = prepare_print_string(out_buf,
+				",Self-cap Calibration Check,Cp_delta_button,",
+				index);
+				scnprintf(tmp_buf, 10, "%d.%d,",
+				cmcp_info->cp_button_delta / 10,
+				cmcp_info->cp_button_delta % 10);
+				index = prepare_print_string(out_buf,
+						&tmp_buf[0], index);
+				index = prepare_print_string(out_buf, "\n",
+					index);
+			}
+		}
+		if ((test_item & CP_PANEL) == CP_PANEL &&
+			!dad->cmcp_range_check) {
+			if (!no_builtin_file) {
+				char tmp_buf[10] = {0};
+				/*print Cp_delta_rx */
+				index = print_silicon_id(out_buf, &device_id[0],
+					index);
+				index = prepare_print_string(out_buf,
+				",Self-cap Calibration Check,Cp_delta_rx,",
+				index);
+				scnprintf(tmp_buf, 10, "%d.%d,",
+				cmcp_info->cp_sensor_rx_delta / 10,
+				cmcp_info->cp_sensor_rx_delta % 10);
+				index = prepare_print_string(out_buf,
+						&tmp_buf[0], index);
+				index = prepare_print_string(out_buf, "\n",
+					index);
+
+				/*print Cp_delta_tx */
+				index = print_silicon_id(out_buf, &device_id[0],
+					index);
+				index = prepare_print_string(out_buf,
+				",Self-cap Calibration Check,Cp_delta_tx,",
+					index);
+				scnprintf(tmp_buf, 10, "%d.%d,",
+				cmcp_info->cp_sensor_tx_delta / 10,
+				cmcp_info->cp_sensor_tx_delta % 10);
+				index = prepare_print_string(out_buf,
+						&tmp_buf[0], index);
+				index = prepare_print_string(out_buf, "\n",
+					index);
+
+				/*print Cp_sensor_avg_rx */
+				index = print_silicon_id(out_buf, &device_id[0],
+					index);
+				index = prepare_print_string(out_buf,
+				",Self-cap Calibration Check,Cp_sensor_avg_rx,",
+					index);
+				index = prepare_print_data(out_buf,
+					&cmcp_info->cp_rx_ave_data_panel,
+					index, 1);
+				index = prepare_print_string(out_buf,
+					"\n", index);
+
+				/*print Cp_sensor_avg_tx */
+				index = print_silicon_id(out_buf,
+					&device_id[0], index);
+				index = prepare_print_string(out_buf,
+				",Self-cap Calibration Check,Cp_sensor_avg_tx,",
+					index);
+				index = prepare_print_data(out_buf,
+					&cmcp_info->cp_tx_ave_data_panel,
+					index, 1);
+				index = prepare_print_string(out_buf,
+					"\n", index);
+
+				/*print Cp_sensor_cal_rx */
+				index = print_silicon_id(out_buf,
+					&device_id[0], index);
+				index = prepare_print_string(out_buf,
+				",Self-cap Calibration Check,Cp_sensor_cal_rx,",
+					index);
+				index = prepare_print_data(out_buf,
+					&cmcp_info->cp_rx_cal_data_panel[0],
+					index, rx_num);
+				index = prepare_print_string(out_buf,
+					"\n", index);
+
+				/*print Cp_sensor_cal_tx */
+				index = print_silicon_id(out_buf,
+					&device_id[0], index);
+				index = prepare_print_string(out_buf,
+				",Self-cap Calibration Check,Cp_sensor_cal_tx,",
+					index);
+				index = prepare_print_data(out_buf,
+					&cmcp_info->cp_tx_cal_data_panel[0],
+					index, tx_num);
+				index = prepare_print_string(out_buf,
+					"\n", index);
+			}
+		}
+
+		if (!no_builtin_file && !dad->cmcp_range_check) {
+			/*print  cp test limits  */
+			index = print_silicon_id(out_buf, &device_id[0], index);
+			if (result->cp_test_pass)
+				index = prepare_print_string(out_buf,
+				",Self-cap Calibration Check,PASS, LIMITS,",
+				index);
+			else
+				index = prepare_print_string(out_buf,
+				",Self-cap Calibration Check,FAIL, LIMITS,",
+				index);
+
+			index = prepare_print_string(out_buf,
+				"CP_MAX_DELTA_SENSOR_RX_PERCENT,", index);
+			index = prepare_print_data(out_buf,
+				&configuration->cp_max_delta_sensor_rx_percent,
+				index, 1);
+			index = prepare_print_string(out_buf,
+				"CP_MAX_DELTA_SENSOR_TX_PERCENT,", index);
+			index = prepare_print_data(out_buf,
+				&configuration->cp_max_delta_sensor_tx_percent,
+				index, 1);
+			index = prepare_print_string(out_buf,
+				"CP_MAX_DELTA_BUTTON_PERCENT,", index);
+			index = prepare_print_data(out_buf,
+				&configuration->cp_max_delta_button_percent,
+				index, 1);
+			index = prepare_print_string(out_buf, "\n", index);
+		}
+	}
+
+	if (!no_builtin_file) {
+		if ((test_item & CM_ENABLED) == CM_ENABLED) {
+			if ((test_item & CM_PANEL) == CM_PANEL) {
+				/*print columns gradient limit*/
+				index = prepare_print_string(out_buf,
+				",Sensor Cm Validation,MAX_LIMITS,CM_MAX_GRADIENT_COLS_PERCENT,",
+				index);
+				index = prepare_print_data(out_buf,
+			&configuration->cm_max_table_gradient_cols_percent[0],
+					index,
+			configuration->cm_max_table_gradient_cols_percent_size);
+				index = prepare_print_string(out_buf,
+					"\n", index);
+				/*print rows gradient limit*/
+				index = prepare_print_string(out_buf,
+				",Sensor Cm Validation,MAX_LIMITS,CM_MAX_GRADIENT_ROWS_PERCENT,",
+				index);
+				index = prepare_print_data(out_buf,
+			&configuration->cm_max_table_gradient_rows_percent[0],
+					index,
+			configuration->cm_max_table_gradient_rows_percent_size);
+				index = prepare_print_string(out_buf,
+					"\n", index);
+
+				/*print cm max limit*/
+				for (i = 0; i < rx_num; i++) {
+					index = prepare_print_string(out_buf,
+						",Sensor Cm Validation,MAX_LIMITS,CM_DATA_ROW",
+						index);
+					index = prepare_print_data(out_buf,
+						&i, index, 1);
+					for (j = 0; j < tx_num; j++)
+						index = prepare_print_data(
+							out_buf,
+		&configuration->cm_min_max_table_sensor[i*tx_num*2+j*2+1],
+							index, 1);
+					index = prepare_print_string(out_buf,
+						"\n", index);
+				}
+			}
+
+			if (((test_item & CM_BTN) == CM_BTN) && (btn_num > 0)) {
+				index = prepare_print_string(out_buf,
+					",Sensor Cm Validation,MAX LIMITS,M_BUTNS,",
+					index);
+				for (j = 0; j < btn_num; j++) {
+					index = prepare_print_data(out_buf,
+				&configuration->cm_min_max_table_btn[2*j+1],
+						index, 1);
+				}
+				index = prepare_print_string(out_buf,
+					"\n", index);
+			}
+
+			index = prepare_print_string(out_buf,
+				 ",Sensor Cm Validation MAX LIMITS\n", index);
+
+			if ((test_item & CM_PANEL) == CM_PANEL) {
+				/*print cm min limit*/
+				for (i = 0; i < rx_num; i++) {
+					index = prepare_print_string(out_buf,
+						",Sensor Cm Validation,MIN_LIMITS,CM_DATA_ROW",
+						index);
+					index = prepare_print_data(out_buf, &i,
+						index, 1);
+					for (j = 0; j < tx_num; j++)
+						index = prepare_print_data(
+							out_buf,
+		&configuration->cm_min_max_table_sensor[i*tx_num*2 + j*2],
+							index, 1);
+					index = prepare_print_string(out_buf,
+						"\n", index);
+				}
+			}
+
+			if (((test_item & CM_BTN) == CM_BTN) && (btn_num > 0)) {
+				index = prepare_print_string(out_buf,
+					",Sensor Cm Validation,MIN LIMITS,M_BUTNS,",
+					index);
+				for (j = 0; j < btn_num; j++) {
+					index = prepare_print_data(out_buf,
+				&configuration->cm_min_max_table_btn[2*j],
+						 index, 1);
+				}
+				index = prepare_print_string(out_buf,
+					"\n", index);
+			}
+			index = prepare_print_string(out_buf,
+				",Sensor Cm Validation MIN LIMITS\n", index);
+		}
+
+		if ((test_item & CP_ENABLED) == CP_ENABLED) {
+			if ((test_item & CP_PANEL) == CP_PANEL) {
+				/*print cp tx max limit*/
+				index = prepare_print_string(out_buf,
+				",Self-cap Calibration Check,MAX_LIMITS,TX,",
+					index);
+				for (i = 0; i < tx_num; i++)
+					index = prepare_print_data(out_buf,
+				&configuration->cp_min_max_table_tx[i*2+1],
+						index, 1);
+				index = prepare_print_string(out_buf,
+					"\n", index);
+
+				/*print cp rx max limit*/
+				index = prepare_print_string(out_buf,
+				",Self-cap Calibration Check,MAX_LIMITS,RX,",
+					index);
+				for (i = 0; i < rx_num; i++)
+					index = prepare_print_data(out_buf,
+				&configuration->cp_min_max_table_rx[i*2+1],
+						index, 1);
+				index = prepare_print_string(out_buf,
+					"\n", index);
+			}
+
+			/*print cp btn max limit*/
+			if (((test_item & CP_BTN) == CP_BTN) && (btn_num > 0)) {
+				index = prepare_print_string(out_buf,
+			",Self-cap Calibration Check,MAX_LIMITS,S_BUTNS,",
+					index);
+				for (i = 0; i < btn_num; i++)
+					index = prepare_print_data(out_buf,
+				&configuration->cp_min_max_table_btn[i*2+1],
+						index, 1);
+				index = prepare_print_string(out_buf,
+						"\n", index);
+			}
+
+			if ((test_item & CP_PANEL) == CP_PANEL) {
+				/*print cp tx min limit*/
+				index = prepare_print_string(out_buf,
+				",Self-cap Calibration Check,MIN_LIMITS,TX,",
+					index);
+				for (i = 0; i < tx_num; i++)
+					index = prepare_print_data(out_buf,
+				&configuration->cp_min_max_table_tx[i*2],
+						index, 1);
+				index = prepare_print_string(out_buf,
+					"\n", index);
+
+				/*print cp rx min limit*/
+				index = prepare_print_string(out_buf,
+				",Self-cap Calibration Check,MIN_LIMITS,RX,",
+					index);
+				for (i = 0; i < rx_num; i++)
+					index = prepare_print_data(out_buf,
+				&configuration->cp_min_max_table_rx[i*2],
+						index, 1);
+				index = prepare_print_string(out_buf,
+					"\n", index);
+			}
+
+			/*print cp btn min limit*/
+			if (((test_item & CP_BTN) == CP_BTN) && (btn_num > 0)) {
+				index = prepare_print_string(out_buf,
+			",Self-cap Calibration Check,MIN_LIMITS,S_BUTNS,",
+					index);
+				for (i = 0; i < btn_num; i++)
+					index = prepare_print_data(out_buf,
+				&configuration->cp_min_max_table_btn[i*2],
+						index, 1);
+				index = prepare_print_string(out_buf,
+						"\n", index);
+			}
+		}
+	}
+	return index;
+}
+
+/*******************************************************************************
+ * FUNCTION: result_save
+ *
+ * SUMMARY: Malloc memory for output buffer and populate with the cmcp test
+ *  header and results in the csv file format.
+ *
+ * NOTE: It supports simple_read_from_buffer() to read data multiple times to
+ *  the buffer.
+ *
+ * RETURN:
+ *	 Size of data printed to "buf"
+ *
+ * PARAMETERS:
+ *  *dev             - pointer to device structure
+ *  *buf             - the user space buffer to read to
+ *  *configuration   - pointer to configuration structure
+ *  *result          - pointer to result structure
+ *  *cmcp_info       - pointer to cmcp_data structure
+ *  *ppos            - the current position in the buffer
+ *   count           - the maximum number of bytes to read
+ *   test_item       - test control in bitwise
+ *   no_builtin_file - flag to determine if builtin-file exist
+ ******************************************************************************/
+int result_save(struct device *dev, char *buf,
+	struct configuration *configuration, struct result *result,
+	struct cmcp_data *cmcp_info, loff_t *ppos, size_t count, int test_item,
+	int no_builtin_file)
+{
+	u8 *out_buf = NULL;
+	int index = 0;
+	int byte_left;
+
+	out_buf = kzalloc(MAX_BUF_LEN, GFP_KERNEL);
+	if (configuration == NULL) {
+		pt_debug(dev, DL_WARN, "config is NULL");
+		return -ENOMEM;
+	}
+	if (result == NULL) {
+		pt_debug(dev, DL_WARN, "result is NULL");
+		return -ENOMEM;
+	}
+	if (cmcp_info == NULL) {
+		pt_debug(dev, DL_WARN, "cmcp_info is NULL");
+		return -ENOMEM;
+	}
+
+	index = save_header(out_buf, index, result);
+	index = save_engineering_data(dev, out_buf, index,
+		cmcp_info, configuration, result,
+		test_item, no_builtin_file);
+	byte_left = simple_read_from_buffer(buf, count, ppos, out_buf, index);
+
+	kfree(out_buf);
+	return byte_left;
+}
+
+/*******************************************************************************
+ * FUNCTION: cmcp_results_debugfs_open
+ *
+ * SUMMARY: Open method for cmcp_results debugfs node.
+ *
+ * RETURN: 0 = success
+ *
+ * PARAMETERS:
+ *      *inode - file inode number
+ *      *filp  - file pointer to debugfs file
+ ******************************************************************************/
+static int cmcp_results_debugfs_open(struct inode *inode,
+		struct file *filp)
+{
+	filp->private_data = inode->i_private;
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: cmcp_results_debugfs_close
+ *
+ * SUMMARY: Close method for cmcp_results debugfs node.
+ *
+ * RETURN: 0 = success
+ *
+ * PARAMETERS:
+ *      *inode - file inode number
+ *      *filp  - file pointer to debugfs file
+ ******************************************************************************/
+static int cmcp_results_debugfs_close(struct inode *inode,
+		struct file *filp)
+{
+	filp->private_data = NULL;
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: cmcp_results_debugfs_read
+ *
+ * SUMMARY: Read method for cmcp_results debugfs node. This function prints
+ *	cmcp test results to user buffer.
+ *
+ * RETURN: Size of debugfs data print
+ *
+ * PARAMETERS:
+ *      *filp   - file pointer to debugfs file
+ *      *buf    - the user space buffer to read to
+ *       count  - the maximum number of bytes to read
+ *      *ppos   - the current position in the buffer
+ ******************************************************************************/
+static ssize_t cmcp_results_debugfs_read(struct file *filp,
+		char __user *buf, size_t count, loff_t *ppos)
+{
+	struct pt_device_access_data *dad = filp->private_data;
+	struct device *dev;
+	struct cmcp_data *cmcp_info = dad->cmcp_info;
+	struct result *result = dad->result;
+	struct configuration *configuration = dad->configs;
+	int ret = 0;
+	int test_item;
+	int no_builtin_file = 0;
+	int test_executed = 0;
+
+	dev = dad->dev;
+
+	mutex_lock(&dad->sysfs_lock);
+	test_executed = dad->test_executed;
+	test_item = pt_cmcp_get_test_item(dad->cmcp_test_items);
+	if (dad->builtin_cmcp_threshold_status < 0) {
+		pt_debug(dev, DL_WARN,
+				"%s: No cmcp threshold file.\n", __func__);
+		no_builtin_file = 1;
+	}
+	mutex_unlock(&dad->sysfs_lock);
+
+	if (test_executed)
+		/*save result to buf*/
+		ret = result_save(dev, buf, configuration, result, cmcp_info,
+			ppos, count, test_item, no_builtin_file);
+	else {
+		char warning_info[] =
+		"No test result available!\n";
+		pt_debug(dev, DL_ERROR,
+				"%s: No test result available!\n", __func__);
+
+		return simple_read_from_buffer(buf, count, ppos, warning_info,
+			strlen(warning_info));
+	}
+
+	return ret;
+}
+
+static const struct file_operations cmcp_results_debugfs_fops = {
+	.open = cmcp_results_debugfs_open,
+	.release = cmcp_results_debugfs_close,
+	.read = cmcp_results_debugfs_read,
+	.write = NULL,
+};
+
+/*******************************************************************************
+ * FUNCTION: cmcp_return_offset_of_new_case
+ *
+ * SUMMARY: Returns the buffer offset of new test case
+ *
+ * NOTE: There are two static variable inside this function.
+ *
+ * RETURN: offset index for new case
+ *
+ * PARAMETERS:
+ *   *bufPtr      - pointer to input buffer
+ *    first_time  - flag to initialize some static variable
+ *                  (0:init; 1:don't init)
+ *   *pFileEnd    - pointer to the end of file for safe check
+ ******************************************************************************/
+u32 cmcp_return_offset_of_new_case(const char *bufPtr, u32 first_time,
+		const char *pFileEnd)
+{
+	static u32 offset, first_search;
+
+	if (first_time == 0) {
+		first_search = 0;
+		offset = 0;
+	}
+
+	if (first_search != 0) {
+		/* Search one case */
+		for (;;) {
+			/* Search ASCII_LF */
+			while (bufPtr < pFileEnd) {
+				if (*bufPtr++ != ASCII_LF)
+					offset++;
+				else
+					break;
+			}
+			if (bufPtr >= pFileEnd)
+				break;
+			offset++;
+			/*
+			 * Single line: end loop
+			 * Multiple lines: continue loop
+			 */
+			if (*bufPtr != ASCII_COMMA)
+				break;
+		}
+	} else
+		first_search = 1;
+
+	return offset;
+}
+
+/*******************************************************************************
+ * FUNCTION: cmcp_get_case_info_from_threshold_file
+ *
+ * SUMMARY: Gets test case information from cmcp threshold file
+ *
+ * RETURN:
+ *   Number of test cases
+ *
+ * PARAMETERS:
+ *  *dev           - pointer to Device structure
+ *  *buf           - pointer to input file
+ *  *search_array  - pointer to test_case_search structure
+ *   file_size     - size of input file for safe check
+ ******************************************************************************/
+u32 cmcp_get_case_info_from_threshold_file(struct device *dev, const char *buf,
+		struct test_case_search *search_array, u32 file_size)
+{
+	u32 case_num = 0, buffer_offset = 0, name_count = 0, first_search = 0;
+	const char *pFileEnd = buf + file_size;
+
+	pt_debug(dev, DL_INFO, "%s: Search cmcp threshold file\n",
+		__func__);
+
+	/* Get all the test cases */
+	for (case_num = 0; case_num < MAX_CASE_NUM; case_num++) {
+		buffer_offset =
+			cmcp_return_offset_of_new_case(&buf[buffer_offset],
+			first_search, pFileEnd);
+		first_search = 1;
+
+		if (buf[buffer_offset] == 0)
+			break;
+
+		for (name_count = 0; name_count < NAME_SIZE_MAX; name_count++) {
+			/* File end */
+			if (buf[buffer_offset + name_count] == ASCII_COMMA)
+				break;
+
+			search_array[case_num].name[name_count] =
+					buf[buffer_offset + name_count];
+		}
+
+		/* Exit when buffer offset is larger than file size */
+		if (buffer_offset >= file_size)
+			break;
+
+		search_array[case_num].name_size = name_count;
+		search_array[case_num].offset = buffer_offset;
+		/*
+		 *  pt_debug(dev, DL_INFO, "Find case %d: Name is %s;
+		 *  Name size is %d; Case offset is %d\n",
+		 *	case_num,
+		 *	search_array[case_num].name,
+		 *	search_array[case_num].name_size,
+		 *	search_array[case_num].offset);
+		 */
+	}
+
+	return case_num;
+}
+
+/*******************************************************************************
+ * FUNCTION: cmcp_compose_data
+ *
+ * SUMMARY: Composes one value based on data of each bit
+ *
+ * RETURN:
+ *   Value that composed from buffer
+ *
+ * PARAMETERS:
+ *  *buf     - pointer to input file
+ *   count   - number of data elements in *buf in decimal
+ ******************************************************************************/
+int cmcp_compose_data(char *buf, u32 count)
+{
+	u32 base_array[] = {1, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9};
+	int value = 0;
+	u32 index = 0;
+
+	for (index = 0; index < count; index++)
+		value += buf[index] * base_array[count - 1 - index];
+
+	return value;
+}
+
+/*******************************************************************************
+ * FUNCTION: cmcp_return_one_value
+ *
+ * SUMMARY: Parses csv file at a given row and offset and combines multiple
+ *  "bits' as a single value. Handles data over multiple lines and various
+ *  end-of-line characters.
+ *
+ * NOTE: There is a static value to calculate line count inside this function.
+ *
+ * RETURN:
+ *   Value that parsed from buffer
+ *
+ * PARAMETERS:
+ *  *dev       - pointer to devices structure
+ *  *buf       - pointer to input buffer
+ *  *offset    - offset index of input buffer
+ *  *line_num  - store line count
+ *   pFileEnd -  pointer to the end of threshold file
+ ******************************************************************************/
+int cmcp_return_one_value(struct device *dev, const char *buf, u32 *offset,
+			  u32 *line_num, const char *pFileEnd)
+{
+	int value = -1;
+	char tmp_buffer[10];
+	u32 count = 0;
+	u32 tmp_offset = *offset;
+	static u32 line_count = 1;
+
+	/* Bypass extra commas */
+	while (((buf + tmp_offset + 1) < pFileEnd)
+			&& buf[tmp_offset] == ASCII_COMMA
+			&& buf[tmp_offset + 1] == ASCII_COMMA)
+		tmp_offset++;
+
+	if ((buf + tmp_offset + 1) >= pFileEnd)
+		goto exit;
+
+	/* Windows and Linux difference at the end of one line */
+	if (buf[tmp_offset] == ASCII_COMMA && buf[tmp_offset + 1] == ASCII_CR) {
+		if ((buf + tmp_offset + 2) < pFileEnd) {
+			if (buf[tmp_offset + 2] == ASCII_LF)
+				tmp_offset += 2;
+		} else
+			goto exit;
+	} else if (buf[tmp_offset] == ASCII_COMMA &&
+		 buf[tmp_offset + 1] == ASCII_LF)
+		tmp_offset += 1;
+	else if (buf[tmp_offset] == ASCII_COMMA
+			&& buf[tmp_offset + 1] == ASCII_CR)
+		tmp_offset += 1;
+
+	if ((buf + tmp_offset + 1) >= pFileEnd)
+		goto exit;
+
+	/* New line for multiple lines */
+	if ((buf[tmp_offset] == ASCII_LF || buf[tmp_offset] == ASCII_CR) &&
+	     buf[tmp_offset + 1] == ASCII_COMMA) {
+		tmp_offset++;
+		line_count++;
+		pt_debug(dev, DL_DEBUG, "%s: Line Count = %d\n",
+			__func__, line_count);
+	}
+
+	/* Beginning */
+	if (buf[tmp_offset] == ASCII_COMMA) {
+		tmp_offset++;
+		for (;;) {
+			if ((buf + tmp_offset) >= pFileEnd)
+				break;
+
+			if ((buf[tmp_offset] >= ASCII_ZERO)
+			&& (buf[tmp_offset] <= ASCII_NINE)) {
+				tmp_buffer[count++] =
+				buf[tmp_offset] - ASCII_ZERO;
+				tmp_offset++;
+			} else {
+				if (count != 0) {
+					value = cmcp_compose_data(tmp_buffer,
+					count);
+					/*pt_debug(dev, DL_DEBUG, */
+					/* ",%d", value);*/
+				} else {
+					/* 0 indicates no data available */
+					value = -1;
+				}
+				break;
+			}
+		}
+	} else {
+		/* Multiple line: line count */
+		if (line_num)
+			*line_num = line_count;
+		/* Reset for next case */
+		line_count = 1;
+	}
+
+exit:
+	*offset = tmp_offset;
+
+	return value;
+}
+
+/*******************************************************************************
+ * FUNCTION: cmcp_get_configuration_info
+ *
+ * SUMMARY: Gets cmcp configuration information.
+ *
+ * PARAMETERS:
+ *  *dev          - pointer to devices structure
+ *  *buf          - pointer to input buffer
+ *  *search_array - pointer to test_case_search structure
+ *   case_count   - number of test cases
+ *  *field_array  - pointer to test_case_field structure
+ *  *config       - pointer to configuration structure
+ *   file_size    - file size of threshold file
+ ******************************************************************************/
+void cmcp_get_configuration_info(struct device *dev,
+		const char *buf, struct test_case_search *search_array,
+		u32 case_count, struct test_case_field *field_array,
+		struct configuration *config, u32 file_size)
+{
+	u32 count = 0, sub_count = 0;
+	u32 exist_or_not = 0;
+	u32 value_offset = 0;
+	int retval = 0;
+	u32 data_num = 0;
+	u32 line_num = 1;
+	const char *pFileEnd = buf + file_size;
+
+	pt_debug(dev, DL_INFO,
+		"%s: Fill configuration struct per cmcp threshold file\n",
+		__func__);
+
+	/* Search cases */
+	for (count = 0; count < MAX_CASE_NUM; count++) {
+		exist_or_not = 0;
+		for (sub_count = 0; sub_count < case_count; sub_count++) {
+			if (!strncmp(field_array[count].name,
+					search_array[sub_count].name,
+					field_array[count].name_size)) {
+				exist_or_not = 1;
+				break;
+			}
+		}
+
+		field_array[count].exist_or_not = exist_or_not;
+
+		pt_debug(dev, DL_DEBUG,
+			"%s: Field Array[%d] exists: %d, type: %d\n",
+			__func__, count, exist_or_not, field_array[count].type);
+
+		/* Clear data number */
+		data_num = 0;
+
+		if (exist_or_not == 1) {
+			switch (field_array[count].type) {
+			case TEST_CASE_TYPE_NO:
+				field_array[count].data_num = 0;
+				field_array[count].line_num = 1;
+				break;
+			case TEST_CASE_TYPE_ONE:
+				value_offset = search_array[sub_count].offset
+					+ search_array[sub_count].name_size;
+				*field_array[count].bufptr =
+						cmcp_return_one_value(dev, buf,
+						&value_offset, 0, pFileEnd);
+				field_array[count].data_num = 1;
+				field_array[count].line_num = 1;
+				break;
+			case TEST_CASE_TYPE_MUL:
+			case TEST_CASE_TYPE_MUL_LINES:
+				line_num = 1;
+				value_offset = search_array[sub_count].offset
+					+ search_array[sub_count].name_size;
+				for (;;) {
+					retval = cmcp_return_one_value(
+					    dev, buf, &value_offset, &line_num,
+					    pFileEnd);
+					if (retval >= 0) {
+						*field_array[count].bufptr++ =
+						retval;
+						data_num++;
+					} else
+						break;
+				}
+
+				field_array[count].data_num = data_num;
+				field_array[count].line_num = line_num;
+				break;
+			default:
+				break;
+			}
+			pt_debug(dev, DL_DEBUG,
+				"%s: %s: Data count is %d, line number is %d\n",
+				__func__,
+				field_array[count].name,
+				field_array[count].data_num,
+				field_array[count].line_num);
+		} else
+			pt_debug(dev, DL_ERROR, "%s: !!! %s doesn't exist\n",
+				__func__, field_array[count].name);
+	}
+}
+
+/*******************************************************************************
+ * FUNCTION: cmcp_get_basic_info
+ *
+ * SUMMARY: Gets basic information for cmcp test, such as available test item,
+ *  number of tx, rx, button.
+ *
+ * PARAMETERS:
+ *  *dev          - pointer to devices structure
+ *  *field_array  - pointer to test_case_field structure
+ *  *config       - pointer to configuration structure
+ ******************************************************************************/
+void cmcp_get_basic_info(struct device *dev,
+	struct test_case_field *field_array, struct configuration *config)
+{
+	u32 tx_num = 0;
+	u32 index = 0;
+
+	config->is_valid_or_not = 1; /* Set to valid by default */
+	config->cm_enabled = 0;
+	config->cp_enabled = 0;
+
+	if (field_array[CM_TEST_INPUTS].exist_or_not)
+		config->cm_enabled = 1;
+	if (field_array[CP_TEST_INPUTS].exist_or_not)
+		config->cp_enabled = 1;
+
+	/* Get basic information only when CM and CP are enabled */
+	if (config->cm_enabled && config->cp_enabled) {
+		pt_debug(dev, DL_INFO,
+			"%s: Find CM and CP thresholds\n", __func__);
+
+		config->rx_num =
+			field_array[PER_ELEMENT_MIN_MAX_TABLE_SENSOR].line_num;
+		tx_num =
+		(field_array[PER_ELEMENT_MIN_MAX_TABLE_SENSOR].data_num >> 1)
+			/field_array[PER_ELEMENT_MIN_MAX_TABLE_SENSOR].line_num;
+		config->tx_num = tx_num;
+
+		config->btn_num =
+		field_array[PER_ELEMENT_MIN_MAX_TABLE_BUTTON].data_num >> 1;
+
+		config->cm_min_max_table_btn_size =
+			field_array[PER_ELEMENT_MIN_MAX_TABLE_BUTTON].data_num;
+		config->cm_min_max_table_sensor_size =
+			field_array[PER_ELEMENT_MIN_MAX_TABLE_SENSOR].data_num;
+		config->cp_min_max_table_rx_size =
+			field_array[PER_ELEMENT_MIN_MAX_RX].data_num;
+		config->cp_min_max_table_tx_size =
+			field_array[PER_ELEMENT_MIN_MAX_TX].data_num;
+		config->cm_max_table_gradient_cols_percent_size =
+			field_array[CM_GRADIENT_CHECK_COL].data_num;
+		config->cm_max_table_gradient_rows_percent_size =
+			field_array[CM_GRADIENT_CHECK_ROW].data_num;
+		config->cp_min_max_table_btn_size =
+			field_array[CP_PER_ELEMENT_MIN_MAX_BUTTON].data_num;
+
+		/* *** Detailed Debug Information *** */
+		pt_debug(dev, DL_DEBUG, "%d\n",
+					config->cm_excluding_col_edge);
+		pt_debug(dev, DL_DEBUG, "%d\n",
+					config->cm_excluding_row_edge);
+		for (index = 0;
+		index < config->cm_max_table_gradient_cols_percent_size;
+		index++)
+			pt_debug(dev, DL_DEBUG, "%d\n",
+			config->cm_max_table_gradient_cols_percent[index]);
+		for (index = 0;
+		index < config->cm_max_table_gradient_rows_percent_size;
+		index++)
+			pt_debug(dev, DL_DEBUG, "%d\n",
+			config->cm_max_table_gradient_rows_percent[index]);
+			pt_debug(dev, DL_DEBUG, "%d\n",
+				config->cm_range_limit_row);
+			pt_debug(dev, DL_DEBUG, "%d\n",
+				config->cm_range_limit_col);
+			pt_debug(dev, DL_DEBUG, "%d\n",
+				config->cm_min_limit_cal);
+			pt_debug(dev, DL_DEBUG, "%d\n",
+				config->cm_max_limit_cal);
+			pt_debug(dev, DL_DEBUG, "%d\n",
+				config->cm_max_delta_sensor_percent);
+			pt_debug(dev, DL_DEBUG, "%d\n",
+				config->cm_max_delta_button_percent);
+		for (index = 0;
+		index < config->cm_min_max_table_btn_size; index++)
+			pt_debug(dev, DL_DEBUG, "%d\n",
+				config->cm_min_max_table_btn[index]);
+		for (index = 0;
+			index < config->cm_min_max_table_sensor_size; index++)
+			pt_debug(dev, DL_DEBUG, "%d\n",
+				config->cm_min_max_table_sensor[index]);
+			pt_debug(dev, DL_DEBUG, "%d\n",
+				config->cp_max_delta_sensor_rx_percent);
+			pt_debug(dev, DL_DEBUG, "%d\n",
+				config->cp_max_delta_sensor_tx_percent);
+			pt_debug(dev, DL_DEBUG, "%d\n",
+				config->cp_max_delta_button_percent);
+			pt_debug(dev, DL_DEBUG, "%d\n",
+				config->min_button);
+			pt_debug(dev, DL_DEBUG, "%d\n",
+				config->max_button);
+
+		for (index = 0;
+		index < config->cp_min_max_table_btn_size; index++)
+			pt_debug(dev, DL_DEBUG, "%d\n",
+				config->cp_min_max_table_btn[index]);
+		for (index = 0;
+		index < config->cp_min_max_table_rx_size; index++)
+			pt_debug(dev, DL_DEBUG, "%d\n",
+				config->cp_min_max_table_rx[index]);
+		for (index = 0;
+		index < config->cp_min_max_table_tx_size; index++)
+			pt_debug(dev, DL_DEBUG, "%d\n",
+				config->cp_min_max_table_tx[index]);
+		/* *** End of Detailed Debug Information *** */
+
+		/* Invalid mutual data length */
+		if ((field_array[PER_ELEMENT_MIN_MAX_TABLE_SENSOR].data_num >>
+		1) % field_array[PER_ELEMENT_MIN_MAX_TABLE_SENSOR].line_num) {
+			config->is_valid_or_not = 0;
+			pt_debug(dev, DL_ERROR, "Invalid mutual data length\n");
+		}
+	} else {
+		if (!config->cm_enabled)
+			pt_debug(dev, DL_ERROR,
+				"%s: Miss CM thresholds or CM data format is wrong!\n",
+				__func__);
+
+		if (!config->cp_enabled)
+			pt_debug(dev, DL_ERROR,
+				"%s: Miss CP thresholds or CP data format is wrong!\n",
+				__func__);
+
+		config->rx_num = 0;
+		config->tx_num = 0;
+		config->btn_num = 0;
+		config->is_valid_or_not = 0;
+	}
+
+	pt_debug(dev, DL_DEBUG,
+		"%s:\n"
+		"Input file is %s!\n"
+		"CM test: %s\n"
+		"CP test: %s\n"
+		"rx_num is %d\n"
+		"tx_num is %d\n"
+		"btn_num is %d\n",
+		__func__,
+		config->is_valid_or_not == 1 ? "VALID" : "!!! INVALID !!!",
+		config->cm_enabled == 1 ? "Found" : "Not found",
+		config->cp_enabled == 1 ? "Found" : "Not found",
+		config->rx_num,
+		config->tx_num,
+		config->btn_num);
+}
+
+/*******************************************************************************
+ * FUNCTION: cmcp_test_case_field_init
+ *
+ * SUMMARY: Initialize the structure test_field_array.
+ *
+ * PARAMETERS:
+ *  *test_field_array  - pointer to test_case_field structure
+ *  *configuration     - pointer to configuration structure
+ ******************************************************************************/
+void cmcp_test_case_field_init(struct test_case_field *test_field_array,
+	struct configuration *configs)
+{
+	struct test_case_field test_case_field_array[MAX_CASE_NUM] = {
+		{"CM TEST INPUTS", 14, TEST_CASE_TYPE_NO,
+				NULL, 0, 0, 0},
+		{"CM_EXCLUDING_COL_EDGE", 21, TEST_CASE_TYPE_ONE,
+				&configs->cm_excluding_col_edge, 0, 0, 0},
+		{"CM_EXCLUDING_ROW_EDGE", 21, TEST_CASE_TYPE_ONE,
+				&configs->cm_excluding_row_edge, 0, 0, 0},
+		{"CM_GRADIENT_CHECK_COL", 21, TEST_CASE_TYPE_MUL,
+				&configs->cm_max_table_gradient_cols_percent[0],
+				0, 0, 0},
+		{"CM_GRADIENT_CHECK_ROW", 21, TEST_CASE_TYPE_MUL,
+				&configs->cm_max_table_gradient_rows_percent[0],
+				0, 0, 0},
+		{"CM_RANGE_LIMIT_ROW", 18, TEST_CASE_TYPE_ONE,
+				&configs->cm_range_limit_row, 0, 0, 0},
+		{"CM_RANGE_LIMIT_COL", 18, TEST_CASE_TYPE_ONE,
+				&configs->cm_range_limit_col, 0, 0, 0},
+		{"CM_MIN_LIMIT_CAL", 16, TEST_CASE_TYPE_ONE,
+				&configs->cm_min_limit_cal, 0, 0, 0},
+		{"CM_MAX_LIMIT_CAL", 16, TEST_CASE_TYPE_ONE,
+				&configs->cm_max_limit_cal, 0, 0, 0},
+		{"CM_MAX_DELTA_SENSOR_PERCENT", 27, TEST_CASE_TYPE_ONE,
+				&configs->cm_max_delta_sensor_percent, 0, 0, 0},
+		{"CM_MAX_DELTA_BUTTON_PERCENT", 27, TEST_CASE_TYPE_ONE,
+				&configs->cm_max_delta_button_percent, 0, 0, 0},
+		{"PER_ELEMENT_MIN_MAX_TABLE_BUTTON", 32, TEST_CASE_TYPE_MUL,
+				&configs->cm_min_max_table_btn[0], 0, 0, 0},
+		{"PER_ELEMENT_MIN_MAX_TABLE_SENSOR", 32,
+				TEST_CASE_TYPE_MUL_LINES,
+				&configs->cm_min_max_table_sensor[0], 0, 0, 0},
+		{"CP TEST INPUTS", 14, TEST_CASE_TYPE_NO,
+				NULL, 0, 0, 0},
+		{"CP_PER_ELEMENT_MIN_MAX_BUTTON", 29, TEST_CASE_TYPE_MUL,
+				&configs->cp_min_max_table_btn[0], 0, 0, 0},
+		{"CP_MAX_DELTA_SENSOR_RX_PERCENT", 30, TEST_CASE_TYPE_ONE,
+				&configs->cp_max_delta_sensor_rx_percent,
+				0, 0, 0},
+		{"CP_MAX_DELTA_SENSOR_TX_PERCENT", 30, TEST_CASE_TYPE_ONE,
+				&configs->cp_max_delta_sensor_tx_percent,
+				0, 0, 0},
+		{"CP_MAX_DELTA_BUTTON_PERCENT", 27, TEST_CASE_TYPE_ONE,
+				&configs->cp_max_delta_button_percent, 0, 0, 0},
+		{"MIN_BUTTON", 10, TEST_CASE_TYPE_ONE,
+				&configs->min_button, 0, 0, 0},
+		{"MAX_BUTTON", 10, TEST_CASE_TYPE_ONE,
+				&configs->max_button, 0, 0, 0},
+		{"PER_ELEMENT_MIN_MAX_RX", 22, TEST_CASE_TYPE_MUL,
+				&configs->cp_min_max_table_rx[0], 0, 0, 0},
+		{"PER_ELEMENT_MIN_MAX_TX", 22, TEST_CASE_TYPE_MUL,
+				&configs->cp_min_max_table_tx[0], 0, 0, 0},
+	};
+
+	memcpy(test_field_array, test_case_field_array,
+		sizeof(struct test_case_field) * MAX_CASE_NUM);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_parse_cmcp_threshold_file_common
+ *
+ * SUMMARY: Parses cmcp threshold file and stores to the data structure.
+ *
+ * PARAMETERS:
+ *  *dev       - pointer to devices structure
+ *  *buf       - pointer to input buffer
+ *   file_size - file size
+ ******************************************************************************/
+static ssize_t pt_parse_cmcp_threshold_file_common(
+	struct device *dev, const char *buf, u32 file_size)
+{
+	struct pt_device_access_data *dad
+		= pt_get_device_access_data(dev);
+	ssize_t rc = 0;
+	u32 case_count = 0;
+
+	pt_debug(dev, DL_INFO,
+		"%s: Start parsing cmcp threshold file. File size is %d\n",
+		__func__, file_size);
+
+	cmcp_test_case_field_init(dad->test_field_array, dad->configs);
+
+	/* Get all the cases from .csv file */
+	case_count = cmcp_get_case_info_from_threshold_file(dev,
+		buf, dad->test_search_array, file_size);
+
+	pt_debug(dev, DL_INFO,
+		"%s: Number of cases found in CSV file: %d\n",
+		__func__, case_count);
+
+	/* Search cases */
+	cmcp_get_configuration_info(dev,
+		buf,
+		dad->test_search_array, case_count, dad->test_field_array,
+		dad->configs, file_size);
+
+	/* Get basic information */
+	cmcp_get_basic_info(dev, dad->test_field_array, dad->configs);
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_cmcp_threshold_loading_store
+ *
+ * SUMMARY: The store method for the cmcp_threshold_loading sysfs node. The
+ *  passed in value controls if threshold loading is performed.
+ *
+ * RETURN: Size of passed in buffer is success
+ *
+ * PARAMETERS:
+ *	*dev  - pointer to device structure
+ *	*attr - pointer to device attributes
+ *	*buf  - pointer to buffer that hold the command parameters
+ *	 size - size of buf
+ ******************************************************************************/
+static ssize_t pt_cmcp_threshold_loading_store(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t size)
+{
+	struct pt_device_access_data *dad = pt_get_device_access_data(dev);
+	ssize_t length;
+	u32 input_data[3];
+	int rc = 0;
+
+	length = cmd->parse_sysfs_input(dev, buf, size, input_data,
+			ARRAY_SIZE(input_data));
+
+	if (length != 1) {
+		pt_debug(dev, DL_WARN, "%s: Invalid number of arguments\n",
+			__func__);
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	mutex_lock(&dad->cmcp_threshold_lock);
+
+	if (input_data[0] == 1)
+		dad->cmcp_threshold_loading = true;
+	else if (input_data[0] == -1)
+		dad->cmcp_threshold_loading = false;
+	else if (input_data[0] == 0 && dad->cmcp_threshold_loading) {
+		dad->cmcp_threshold_loading = false;
+
+		if (dad->cmcp_threshold_size == 0) {
+			pt_debug(dev, DL_ERROR, "%s: No cmcp threshold data\n",
+					__func__);
+			goto exit_free;
+		}
+
+		/* Clear test executed flag */
+		dad->test_executed = 0;
+
+		pt_parse_cmcp_threshold_file_common(dev,
+			&dad->cmcp_threshold_data[0], dad->cmcp_threshold_size);
+
+		/* Mark valid */
+		dad->builtin_cmcp_threshold_status = 0;
+		/* Restore test item to default value when new file input */
+		dad->cmcp_test_items = 0;
+	} else {
+		pt_debug(dev, DL_WARN, "%s: Invalid value\n", __func__);
+		rc = -EINVAL;
+		mutex_unlock(&dad->cmcp_threshold_lock);
+		goto exit;
+	}
+
+exit_free:
+	kfree(dad->cmcp_threshold_data);
+	dad->cmcp_threshold_data = NULL;
+	dad->cmcp_threshold_size = 0;
+	mutex_unlock(&dad->cmcp_threshold_lock);
+
+exit:
+	if (rc)
+		return rc;
+	return size;
+}
+
+static DEVICE_ATTR(cmcp_threshold_loading, 0200,
+	NULL, pt_cmcp_threshold_loading_store);
+
+/*******************************************************************************
+ * FUNCTION: pt_cmcp_threshold_data_write
+ *
+ * SUMMARY: The write method for the cmcp_threshold_data_sysfs node. The passed
+ *  in data (threshold file) is written to the threshold buffer.
+ *
+ * RETURN: Size of passed in buffer is success
+ *
+ * PARAMETERS:
+ *  *filp     - pointer to file structure
+ *  *kobj     - pointer to kobject structure
+ *  *bin_attr - pointer to bin_attribute structure
+ *   buf      - pointer to cmd input buffer
+ *   offset   - offset index to store input buffer
+ *   count    - size of data in buffer
+ ******************************************************************************/
+static ssize_t pt_cmcp_threshold_data_write(struct file *filp,
+		struct kobject *kobj, struct bin_attribute *bin_attr,
+		char *buf, loff_t offset, size_t count)
+{
+	struct device *dev = container_of(kobj, struct device, kobj);
+	struct pt_device_access_data *dad
+		= pt_get_device_access_data(dev);
+	u8 *p;
+
+	pt_debug(dev, DL_INFO, "%s: offset:%lld count:%zu\n",
+		__func__, offset, count);
+
+	mutex_lock(&dad->cmcp_threshold_lock);
+
+	if (!dad->cmcp_threshold_loading) {
+		mutex_unlock(&dad->cmcp_threshold_lock);
+		return -ENODEV;
+	}
+
+	p = krealloc(dad->cmcp_threshold_data, offset + count, GFP_KERNEL);
+	if (!p) {
+		kfree(dad->cmcp_threshold_data);
+		dad->cmcp_threshold_data = NULL;
+		mutex_unlock(&dad->cmcp_threshold_lock);
+		return -ENOMEM;
+	}
+	dad->cmcp_threshold_data = p;
+
+	memcpy(&dad->cmcp_threshold_data[offset], buf, count);
+	dad->cmcp_threshold_size += count;
+
+	mutex_unlock(&dad->cmcp_threshold_lock);
+
+	return count;
+}
+
+static struct bin_attribute bin_attr_cmcp_threshold_data = {
+	.attr = {
+		.name = "cmcp_threshold_data",
+		.mode = 0200,
+	},
+	.size = 0,
+	.write = pt_cmcp_threshold_data_write,
+};
+
+
+/*******************************************************************************
+ * FUNCTION: pt_suspend_scan_cmd_
+ *
+ * SUMMARY: Non-protected wrapper function for suspend scan command
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*dev - pointer to devices structure
+ ******************************************************************************/
+static int pt_suspend_scan_cmd_(struct device *dev)
+{
+	int rc;
+
+	rc = cmd->nonhid_cmd->suspend_scanning(dev, 0);
+	if (rc)
+		pt_debug(dev, DL_ERROR, "%s: Suspend scan failed rc = %d\n",
+			__func__, rc);
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_resume_scan_cmd_
+ *
+ * SUMMARY: Non-protected wrapper function for resume scan command
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*dev - pointer to devices structure
+ ******************************************************************************/
+static int pt_resume_scan_cmd_(struct device *dev)
+{
+	int rc;
+
+	rc = cmd->nonhid_cmd->resume_scanning(dev, 0);
+	if (rc)
+		pt_debug(dev, DL_ERROR, "%s: Resume scan failed rc = %d\n",
+			__func__, rc);
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_exec_scan_cmd_
+ *
+ * SUMMARY: Non-protected wrapper function for execute scan command
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*dev       - pointer to devices structure
+ *	 scan_type - type of panel scan to perform (PIP2 only)
+ ******************************************************************************/
+static int pt_exec_scan_cmd_(struct device *dev, u8 scan_type)
+{
+	int rc;
+
+	rc = cmd->nonhid_cmd->exec_panel_scan(dev, PT_CORE_CMD_UNPROTECTED,
+		scan_type);
+	if (rc)
+		pt_debug(dev, DL_ERROR,
+			"%s: Heatmap start scan failed rc = %d\n",
+			__func__, rc);
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_ret_scan_data_cmd_
+ *
+ * SUMMARY: Non-protected wrapper function for retrieve panel data command
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*dev             - pointer to device structure
+ *	 read_offset     - read pointer offset
+ *	 read_count      - length of data to read
+ *	 data_id         - enumerated test ID to read selftest results from
+ *	*response        - pointer to store the read response status
+ *	*config          - pointer to store config data
+ *	*actual_read_len - pointer to store data length actually read
+ *	*return_buf      - pointer to the read buffer
+ ******************************************************************************/
+static int pt_ret_scan_data_cmd_(struct device *dev, u16 read_offset,
+		u16 read_count, u8 data_id, u8 *response, u8 *config,
+		u16 *actual_read_len, u8 *return_buf)
+{
+	int rc;
+
+	rc = cmd->nonhid_cmd->retrieve_panel_scan(dev, 0, read_offset,
+			read_count, data_id, response, config, actual_read_len,
+			return_buf);
+	if (rc)
+		pt_debug(dev, DL_ERROR,
+				"%s: Retrieve scan data failed rc = %d\n",
+				__func__, rc);
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_get_data_structure_cmd_
+ *
+ * SUMMARY: Non-protected wrapper function for get data structure command
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*dev             - pointer to device structure
+ *	 read_offset     - read pointer offset
+ *	 read_length     - length of data to read
+ *	 data_id         - data ID to read
+ *	*status          - pointer to store the read response status
+ *	*data_format     - pointer to store format of data read
+ *	*actual_read_len - pointer to store data length actually read
+ *	*data            - pointer to store data read
+ ******************************************************************************/
+static int pt_get_data_structure_cmd_(struct device *dev, u16 read_offset,
+		u16 read_length, u8 data_id, u8 *status, u8 *data_format,
+		u16 *actual_read_len, u8 *data)
+{
+	int rc;
+
+	rc = cmd->nonhid_cmd->get_data_structure(dev, 0, read_offset,
+			read_length, data_id, status, data_format,
+			actual_read_len, data);
+	if (rc)
+		pt_debug(dev, DL_ERROR,
+				"%s: Get data structure failed rc = %d\n",
+				__func__, rc);
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_run_selftest_cmd_
+ *
+ * SUMMARY: Non-protected wrapper function for run self test command
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*dev                  - pointer to device structure
+ *	 test_id              - enumerated test ID to run
+ *	 write_idacs_to_flash - flag whether to write new IDACS to flash
+ *	*status               - pointer to store the read response status
+ *	*summary_results      - pointer to store the results summary
+ *	*results_available    - pointer to store if results are available
+ ******************************************************************************/
+static int pt_run_selftest_cmd_(struct device *dev, u8 test_id,
+		u8 write_idacs_to_flash, u8 *status, u8 *summary_result,
+		u8 *results_available)
+{
+	int rc;
+
+	rc = cmd->nonhid_cmd->run_selftest(dev, 0, test_id,
+			write_idacs_to_flash, status, summary_result,
+			results_available);
+	if (rc)
+		pt_debug(dev, DL_ERROR, "%s: Run self test failed rc = %d\n",
+				__func__, rc);
+	return rc;
+}
+
+
+/*******************************************************************************
+ * FUNCTION: pt_get_selftest_result_cmd_
+ *
+ * SUMMARY: Non-protected wrapper function for get self test result command
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*dev             - pointer to device structure
+ *	 read_offset     - read pointer offset
+ *	 read_length     - length of data to read
+ *	 test_id         - enumerated test ID to read selftest results from
+ *	*status          - pointer to store the read response status
+ *	*actual_read_len - pointer to store data length actually read
+ *	*data            - pointer to where the data read is stored
+ ******************************************************************************/
+static int pt_get_selftest_result_cmd_(struct device *dev,
+		u16 read_offset, u16 read_length, u8 test_id, u8 *status,
+		u16 *actual_read_len, u8 *data)
+{
+	int rc;
+
+	rc = cmd->nonhid_cmd->get_selftest_result(dev, 0, read_offset,
+			read_length, test_id, status, actual_read_len, data);
+	if (rc)
+		pt_debug(dev, DL_ERROR,
+			"%s: Get self test result failed rc = %d\n",
+			__func__, rc);
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_calibrate_ext_cmd
+ *
+ * SUMMARY: Wrapper function to function calibrate_ext() in pt_core_commands
+ *  structure
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *  *dev          - pointer to device structure
+ *  *cal_data     - pointer to extended calibration data structure
+ *  *status       - pointer to where the command response status is stored
+ ******************************************************************************/
+static int _pt_calibrate_ext_cmd(struct device *dev,
+		struct pt_cal_ext_data *cal_data, u8 *status)
+{
+	int rc;
+
+	rc = cmd->nonhid_cmd->calibrate_ext(dev,
+			PT_CORE_CMD_UNPROTECTED, cal_data, status);
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_calibrate_idacs_cmd
+ *
+ * SUMMARY: Wrapper function to function calibrate_idacs() in pt_core_commands
+ *  structure
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *  *dev          - pointer to device structure
+ *   sensing_mode - sense mode to calibrate (0-5)
+ *  *status       - pointer to where the command response status is stored
+ ******************************************************************************/
+static int _pt_calibrate_idacs_cmd(struct device *dev,
+		u8 sensing_mode, u8 *status)
+{
+	int rc;
+
+	rc = cmd->nonhid_cmd->calibrate_idacs(dev, 0, sensing_mode, status);
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_initialize_baselines_cmd
+ *
+ * SUMMARY: Wrapper function to call initialize_baselines() in pt_core_commands
+ *  structure
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *  *dev          - pointer to device structure
+ *   sensing_mode - enumerated ID against which to initialize the baseline
+ *  *status       - pointer to where the command response statas is stored
+ ******************************************************************************/
+static int _pt_initialize_baselines_cmd(struct device *dev,
+		u8 sensing_mode, u8 *status)
+{
+	int rc;
+
+	rc = cmd->nonhid_cmd->initialize_baselines(dev, 0, sensing_mode,
+			status);
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_perform_calibration
+ *
+ * SUMMARY: For Gen5/6, Send the PIP1 Calibrate IDACs command (0x28). For TT/TC,
+ *  send PIP1 Extended Calibrate command (0x30).
+ *
+ * NOTE: Panel scan must be suspended prior to calling this function.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*dev - pointer to device structure
+ ******************************************************************************/
+static int pt_perform_calibration(struct device *dev)
+{
+	struct pt_cal_ext_data cal_data = {0};
+	u8 dut_gen = cmd->request_dut_generation(dev);
+	u8 mode;
+	u8 status;
+	int rc;
+
+	if (dut_gen == DUT_PIP1_ONLY) {
+		for (mode = 0; mode < 3; mode++) {
+			rc = _pt_calibrate_idacs_cmd(dev, mode, &status);
+			if (rc < 0) {
+				pt_debug(dev, DL_ERROR,
+					"%s: calibrate idac error, mode= %d, rc = %d\n",
+					__func__, mode, rc);
+				break;
+			}
+		}
+	} else {
+		memset(&cal_data, 0, sizeof(struct pt_cal_ext_data));
+		rc = _pt_calibrate_ext_cmd(dev, &cal_data, &status);
+		if (rc < 0)
+			pt_debug(dev, DL_ERROR,
+				"%s: extended calibrate error, rc = %d\n",
+				__func__, rc);
+	}
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: prepare_print_buffer
+ *
+ * SUMMARY: Format input buffer to out buffer with Hex base,and format "status"
+ *  to decimal base.
+ *
+ * RETURN:
+ *	 size of formated data in output buffer
+ *
+ * PARAMETERS:
+ *   status       - Indicate test result:0(STATUS_SUCCESS),-1(STATUS_FAIL)
+ *  *in_buf       - input buffer to be formated
+ *   length       - length of input buffer
+ *  *out_buf      - output buffer to store formated data
+ *   out_buf_size - length of output buffer
+ *   out_format   - format of output data (5 supported formats):
+ *    PT_PR_FORMAT_DEFAULT      : format all data as a column
+ *    PT_PR_FORMAT_U8_SPACE     : sort status bytes and self test results,
+ *      and format the results as a row, each element include 1 byte
+ *    PT_PR_FORMAT_U16_SPACE    : sort status bytes and self test results,
+ *      and format the results as a row, each element include 2 byte
+ *    PT_PR_FORMAT_U8_NO_SPACE  : sort status bytes and self test results,
+ *      and format the results as a row, no space between the elements
+ *    PT_PR_FORMAT_U32_SPACE    : sort status bytes and self test results,
+ *      and format the results as a row, each element include 4 byte
+ ******************************************************************************/
+static int prepare_print_buffer(int status, u8 *in_buf, int length,
+		u8 *out_buf, size_t out_buf_size, u8 out_format)
+{
+	int index = 0;
+	int data_length;
+	int i;
+
+	index += scnprintf(out_buf, out_buf_size, "Status: %d\n", status);
+
+	if (out_format == PT_PR_FORMAT_DEFAULT) {
+		for (i = 0; i < length; i++)
+			index += scnprintf(&out_buf[index],
+				out_buf_size - index,
+				"%02X\n", in_buf[i]);
+	} else {
+		index += scnprintf(&out_buf[index],
+			out_buf_size - index,
+			"Response Status[1-%d]: ", MIN(length, 3));
+		for (i = 0; i < MIN(length, 3); i++)
+			index += scnprintf(&out_buf[index],
+				out_buf_size - index,
+				"%02X ", in_buf[i]);
+		index += scnprintf(&out_buf[index], out_buf_size - index, "\n");
+		if (length <= 6) {
+			goto exit;
+		} else {
+			data_length = get_unaligned_le16(&in_buf[4]);
+			index += scnprintf(&out_buf[index],
+				out_buf_size - index, "RAW_DATA: ");
+		}
+
+		if (out_format == PT_PR_FORMAT_U8_SPACE) {
+			for (i = 6; i < length; i++)
+				index += scnprintf(&out_buf[index],
+					out_buf_size - index,
+					"%02X ", in_buf[i]);
+			index += scnprintf(&out_buf[index],
+				out_buf_size - index,
+				":(%d bytes)\n", data_length);
+		} else if (out_format == PT_PR_FORMAT_U16_SPACE) {
+			for (i = 6; (i + 1) < length; i += 2)
+				index += scnprintf(&out_buf[index],
+					out_buf_size - index, "%04X ",
+					get_unaligned_le16(&in_buf[i]));
+			index += scnprintf(&out_buf[index],
+				out_buf_size - index,
+				":(%d words)\n", (length-6)/2);
+		} else if (out_format == PT_PR_FORMAT_U8_NO_SPACE) {
+			for (i = 6; i < length; i++)
+				index += scnprintf(&out_buf[index],
+					out_buf_size - index,
+					"%02X", in_buf[i]);
+			index += scnprintf(&out_buf[index],
+				out_buf_size - index,
+				":(%d bytes)\n", data_length);
+		} else if (out_format == PT_PR_FORMAT_U32_SPACE) {
+			for (i = 6; (i + 1) < length; i += 4)
+				index += scnprintf(&out_buf[index],
+					out_buf_size - index, "%08X ",
+					get_unaligned_le32(&in_buf[i]));
+			index += scnprintf(&out_buf[index],
+				out_buf_size - index,
+				":(%d 32bit values)\n", (length-6)/4);
+		}
+	}
+
+exit:
+	return index;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_run_and_get_selftest_result
+ *
+ * SUMMARY: Run the selftest and store the test result in the
+ *  pt_device_access_data struct.
+ *
+ * RETURN:
+ *	 >0 : Size of debugfs data to print
+ *	 <0 : failure
+ *	  0 : success
+ *
+ * NOTE: "Status: x" - x will contain the first error code if any
+ *
+ * PARAMETERS:
+ *  *dev                - pointer to device structure
+ *   protect            - flag to call protected or non-protected
+ *  *buf                - pointer to print buf of return data
+ *   buf_len            - length of print buf of return data
+ *   test_id            - selftest id
+ *   read_length        - max length to stor return data
+ *   get_result_on_pass - indicate whether to get result when finish test
+ *   print_results      - print results to log
+ * (true:get result;false:don't get result )
+ *   print_format       - format of print results
+ ******************************************************************************/
+static ssize_t pt_run_and_get_selftest_result(struct device *dev,
+		int protect, char *buf, size_t buf_len, u8 test_id,
+		u16 read_length, bool get_result_on_pass, bool print_results,
+		u8 print_format)
+{
+	struct pt_device_access_data *dad = pt_get_device_access_data(dev);
+	int status = STATUS_SUCCESS;
+	u8 cmd_status = STATUS_SUCCESS;
+	u8 summary_result = 0;
+	u8 sys_mode = FW_SYS_MODE_UNDEFINED;
+	u16 act_length = 0;
+	int length = 0;
+	int size = 0;
+	int rc;
+
+	mutex_lock(&dad->sysfs_lock);
+
+	pm_runtime_get_sync(dev);
+
+	if (protect == PT_CORE_CMD_PROTECTED) {
+		rc = cmd->request_exclusive(dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
+		if (rc) {
+			pt_debug(dev, DL_ERROR,
+				"%s: Error on request exclusive rc = %d\n",
+				__func__, rc);
+			status = -EPERM;
+			goto put_pm_runtime;
+		}
+	}
+
+	/* Get the current scan state so we restore to the same at the end */
+	rc = cmd->request_get_fw_mode(dev, PT_CORE_CMD_UNPROTECTED, &sys_mode,
+		NULL);
+	if (rc) {
+		status = rc;
+		goto release_exclusive;
+	}
+
+	if (sys_mode != FW_SYS_MODE_TEST) {
+		rc = pt_suspend_scan_cmd_(dev);
+		if (rc) {
+			pt_debug(dev, DL_ERROR,
+					"%s: Error on suspend scan rc = %d\n",
+					__func__, rc);
+			status = -EPERM;
+			goto release_exclusive;
+		}
+	}
+
+	/* Sleep for 20ms to allow the last scan to be available in FW */
+	msleep(20);
+
+	rc = pt_run_selftest_cmd_(dev, test_id, 0,
+		&cmd_status, &summary_result, NULL);
+	if (rc) {
+		/* Error sending self test */
+		pt_debug(dev, DL_ERROR,
+			"%s: Error on run self test for test_id:%d rc = %d\n",
+			__func__, test_id, rc);
+		status = rc;
+		goto resume_scan;
+	}
+	if (cmd_status) {
+		/* Self test response status failure */
+		pt_debug(dev, DL_WARN,
+			"%s: Test ID: 0x%02X resulted in status: 0x%02X\n",
+			__func__, test_id, cmd_status);
+		status = cmd_status;
+	}
+
+	dad->si = cmd->request_sysinfo(dad->dev);
+	if (!dad->si) {
+		pt_debug(dad->dev, DL_ERROR,
+			"%s: Fail get sysinfo pointer from core\n", __func__);
+		if (status == STATUS_SUCCESS)
+			status = -EINVAL;
+		goto resume_scan;
+	}
+	if (IS_PIP_VER_GE(dad->si, 1, 11)) {
+		/* PIP1.11+ does not report the summary_result in byte 6 */
+		summary_result = cmd_status;
+	}
+
+	/* Form response buffer */
+	dad->ic_buf[0] = cmd_status;
+	dad->ic_buf[1] = summary_result;
+
+	pt_debug(dev, DL_INFO, "%s: Run Self Test cmd status = %d\n",
+		__func__, cmd_status);
+	pt_debug(dev, DL_INFO, "%s: Run Self Test result summary = %d\n",
+		__func__, summary_result);
+
+	length = 2;
+
+	/*
+	 * Get data if requested and the cmd status indicates that the test
+	 * completed with either a pass or a fail. All other status codes
+	 * indicate the test itself was not run so there is no data to retrieve
+	 */
+	if ((cmd_status == PT_ST_RESULT_PASS ||
+	     cmd_status == PT_ST_RESULT_FAIL)  && get_result_on_pass) {
+		rc = pt_get_selftest_result_cmd_(dev, 0, read_length,
+			test_id, &cmd_status, &act_length, &dad->ic_buf[6]);
+		if (rc) {
+			pt_debug(dev, DL_ERROR,
+				"%s: Error on get self test result rc = %d\n",
+				__func__, rc);
+			if (status == STATUS_SUCCESS)
+				status = rc;
+			goto resume_scan;
+		}
+
+		pt_debug(dev, DL_INFO, "%s: Get Self Test result status = %d\n",
+			__func__, cmd_status);
+
+		/* Only store new status if no error on running self test */
+		if (status == STATUS_SUCCESS)
+			status = cmd_status;
+
+		dad->ic_buf[2] = cmd_status;
+		dad->ic_buf[3] = test_id;
+		dad->ic_buf[4] = LOW_BYTE(act_length);
+		dad->ic_buf[5] = HI_BYTE(act_length);
+
+		length = 6 + act_length;
+	}
+
+resume_scan:
+	/* Only resume scanning if we suspended it */
+	if (sys_mode == FW_SYS_MODE_SCANNING)
+		pt_resume_scan_cmd_(dev);
+
+release_exclusive:
+	if (protect == PT_CORE_CMD_PROTECTED)
+		cmd->release_exclusive(dev);
+
+put_pm_runtime:
+	pm_runtime_put(dev);
+
+	/* Communication error */
+	if (status < 0)
+		length = 0;
+
+	if (print_results) {
+		size = prepare_print_buffer(status, dad->ic_buf, length,
+			buf, buf_len, print_format);
+		rc = size;
+	}
+
+	mutex_unlock(&dad->sysfs_lock);
+
+	return rc;
+}
+
+struct pt_device_access_debugfs_data {
+	struct pt_device_access_data *dad;
+	ssize_t pr_buf_len;
+	u8 pr_buf[10 * PT_MAX_PRBUF_SIZE];
+};
+
+/*******************************************************************************
+ * FUNCTION: pt_device_access_debugfs_open
+ *
+ * SUMMARY: Open the device_access debugfs node to initialize.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *  *inode - pointer to inode structure
+ *  *filp  - pointer to file structure
+ ******************************************************************************/
+static int pt_device_access_debugfs_open(struct inode *inode,
+		struct file *filp)
+{
+	struct pt_device_access_data *dad = inode->i_private;
+	struct pt_device_access_debugfs_data *data;
+
+	data = kzalloc(sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	data->dad = dad;
+
+	filp->private_data = data;
+
+	return nonseekable_open(inode, filp);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_device_access_debugfs_release
+ *
+ * SUMMARY: Close the device_access debugfs node to free pointer.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *  *inode - pointer to inode structure
+ *  *filp  - pointer to file structure
+ ******************************************************************************/
+static int pt_device_access_debugfs_release(struct inode *inode,
+		struct file *filp)
+{
+	kfree(filp->private_data);
+
+	return 0;
+}
+
+#define PT_DEBUGFS_FOPS(_name, _read, _write) \
+static const struct file_operations _name##_debugfs_fops = { \
+	.open = pt_device_access_debugfs_open, \
+	.release = pt_device_access_debugfs_release, \
+	.read = _read, \
+	.write = _write, \
+}
+
+/*******************************************************************************
+ * FUNCTION: panel_scan_debugfs_read
+ *
+ * SUMMARY: This function retrieves a full panel scan by sending the following
+ *	PIP commands:
+ *		1) Suspend Scanning
+ *		2) Execute Panel Scan
+ *		3) Retrieve Panel Scan (n times to retrieve full scan)
+ *		4) Resume Scanning
+ *
+ * RETURN: Size of debugfs data print
+ *
+ * PARAMETERS:
+ *      *filp   - file pointer to debugfs file
+ *      *buf    - the user space buffer to read to
+ *       count  - the maximum number of bytes to read
+ *      *ppos   - the current position in the buffer
+ ******************************************************************************/
+static ssize_t panel_scan_debugfs_read(struct file *filp, char __user *buf,
+		size_t count, loff_t *ppos)
+{
+	struct pt_device_access_debugfs_data *data = filp->private_data;
+	struct pt_device_access_data *dad = data->dad;
+	struct device *dev = dad->dev;
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	int status = STATUS_FAIL;
+	u8 config;
+	u16 num_elem_read;
+	int length = 0;
+	u8 element_size = 0;
+	u8 *buf_out;
+	u8 *buf_offset;
+	u8 sys_mode = FW_SYS_MODE_UNDEFINED;
+	int elem_offset = 0;
+	int rc;
+	int print_idx = 0;
+	int i;
+
+	mutex_lock(&dad->debugfs_lock);
+	buf_out = dad->panel_scan_data_buf;
+	if (!buf_out)
+		goto release_mutex;
+
+	pm_runtime_get_sync(dev);
+
+	/*
+	 * This function will re-enter if the panel_scan_size is greater than
+	 * count (count is the kernel page size which is typically 4096), on
+	 * re-entry, *ppos will retain how far the last copy to user space
+	 * completed
+	 */
+	if (*ppos) {
+		if (*ppos >= dad->panel_scan_size)
+			goto release_mutex;
+
+		print_idx = simple_read_from_buffer(buf, count, ppos,
+			buf_out, dad->panel_scan_size);
+
+		pt_debug(dev, DL_DEBUG, "%s: Sent %d bytes to user space\n",
+			__func__, print_idx);
+
+		goto release_mutex;
+	}
+
+	rc = cmd->request_exclusive(dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
+	if (rc) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Error on request exclusive rc = %d\n",
+			__func__, rc);
+		goto put_pm_runtime;
+	}
+
+	/* Get the current scan state so we restore to the same at the end */
+	rc = cmd->request_get_fw_mode(dev, PT_CORE_CMD_UNPROTECTED, &sys_mode,
+		NULL);
+	if (rc) {
+		status = rc;
+		goto release_exclusive;
+	}
+
+	if (sys_mode != FW_SYS_MODE_TEST) {
+		rc = pt_suspend_scan_cmd_(dev);
+		if (rc) {
+			pt_debug(dev, DL_ERROR,
+					"%s: Error on suspend scan rc = %d\n",
+					__func__, rc);
+			goto release_exclusive;
+		}
+	}
+
+	rc = pt_exec_scan_cmd_(dev, dad->panel_scan_type_id);
+	if (rc) {
+		pt_debug(dev, DL_ERROR,
+				"%s: Error on execute panel scan rc = %d\n",
+				__func__, rc);
+		goto resume_scan;
+	}
+
+	/* Set length to max to read all */
+	rc = pt_ret_scan_data_cmd_(dev, 0, 0xFFFF,
+			dad->panel_scan_retrieve_id, dad->ic_buf, &config,
+			&num_elem_read, NULL);
+	if (rc) {
+		pt_debug(dev, DL_ERROR,
+				"%s: Error on retrieve panel scan rc = %d\n",
+				__func__, rc);
+		goto resume_scan;
+	}
+
+	length = get_unaligned_le16(&dad->ic_buf[0]);
+	buf_offset = dad->ic_buf + length;
+	element_size = config & 0x07;
+	elem_offset = num_elem_read;
+	while (num_elem_read > 0) {
+		rc = pt_ret_scan_data_cmd_(dev, elem_offset, 0xFFFF,
+				dad->panel_scan_retrieve_id, NULL, &config,
+				&num_elem_read, buf_offset);
+		if (rc)
+			goto resume_scan;
+
+		length += num_elem_read * element_size;
+		buf_offset = dad->ic_buf + length;
+		elem_offset += num_elem_read;
+		if (num_elem_read < 0x7A)
+			break;
+	}
+	/* Reconstruct cmd header */
+	put_unaligned_le16(length, &dad->ic_buf[0]);
+	put_unaligned_le16(elem_offset, &dad->ic_buf[7]);
+
+	status = STATUS_SUCCESS;
+
+resume_scan:
+	/* Only resume scanning if we suspended it */
+	if (sys_mode == FW_SYS_MODE_SCANNING)
+		pt_resume_scan_cmd_(dev);
+
+release_exclusive:
+	cmd->release_exclusive(dev);
+
+put_pm_runtime:
+	pm_runtime_put(dev);
+
+	if (status == STATUS_FAIL)
+		length = 0;
+	if (cd->show_timestamp)
+		print_idx += scnprintf(buf_out + print_idx, TTHE_TUNER_MAX_BUF,
+			"[%u] SCAN_DATA:", pt_get_time_stamp());
+	else
+		print_idx += scnprintf(buf_out + print_idx, TTHE_TUNER_MAX_BUF,
+			"SCAN_DATA:");
+
+	for (i = 0; i < length; i++)
+		print_idx += scnprintf(buf_out + print_idx,
+			TTHE_TUNER_MAX_BUF - print_idx,
+			"%02X ", dad->ic_buf[i]);
+	print_idx += scnprintf(buf_out + print_idx,
+			TTHE_TUNER_MAX_BUF - print_idx,
+			":(%d bytes)\n", length);
+
+	/*
+	 * Save the size of the full scan which this function uses on re-entry
+	 * to send the data back to user space in 'count' size chuncks
+	 */
+	dad->panel_scan_size = print_idx;
+	print_idx = simple_read_from_buffer(buf, count, ppos, buf_out,
+		print_idx);
+	pt_debug(dev, DL_DEBUG, "%s: Sent %d bytes to user space\n",
+		__func__, print_idx);
+
+release_mutex:
+	mutex_unlock(&dad->debugfs_lock);
+	return print_idx;
+}
+
+/*******************************************************************************
+ * FUNCTION: panel_scan_debugfs_write
+ *
+ * SUMMARY: Store the type of panel scan the read method will perform.
+ *
+ * RETURN: Size of debugfs data write
+ *
+ * PARAMETERS:
+ *      *filp   - file pointer to debugfs file
+ *      *buf    - the user space buffer to write to
+ *       count  - the maximum number of bytes to write
+ *      *ppos   - the current position in the buffer
+ ******************************************************************************/
+static ssize_t panel_scan_debugfs_write(struct file *filp,
+		const char __user *buf, size_t count, loff_t *ppos)
+{
+	struct pt_device_access_debugfs_data *data = filp->private_data;
+	struct pt_device_access_data *dad = data->dad;
+	ssize_t length;
+	u32 input_data[3];
+	int rc = 0;
+
+	rc = simple_write_to_buffer(data->pr_buf, sizeof(data->pr_buf), ppos,
+			buf, count);
+	if (rc < 0)
+		return rc;
+
+	count = rc;
+
+	mutex_lock(&dad->debugfs_lock);
+	length = cmd->parse_sysfs_input(dad->dev, data->pr_buf, count,
+			input_data, ARRAY_SIZE(input_data));
+	switch (length) {
+	case 1:
+		dad->panel_scan_retrieve_id = input_data[0];
+		dad->panel_scan_type_id = 0;
+		break;
+	case 2:
+		dad->panel_scan_retrieve_id = input_data[0];
+		dad->panel_scan_type_id = input_data[1];
+		break;
+	default:
+		pt_debug(dad->dev, DL_ERROR,
+				"%s: Malformed input\n", __func__);
+		rc = -EINVAL;
+	}
+	mutex_unlock(&dad->debugfs_lock);
+
+	if (rc)
+		return rc;
+	return count;
+}
+
+/*******************************************************************************
+ * FUNCTION: panel_scan_debugfs_open
+ *
+ * SUMMARY: Open the panel_scan debugfs node to initialize.
+ *
+ * RETURN: 0 = success
+ *        !0 = failure
+ *
+ * PARAMETERS:
+ *      *inode - file inode number
+ *      *filp  - file pointer to debugfs file
+ ******************************************************************************/
+static int panel_scan_debugfs_open(struct inode *inode,
+		struct file *filp)
+{
+	struct pt_device_access_data *dad = inode->i_private;
+	struct pt_device_access_debugfs_data *data;
+
+	data = kzalloc(sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	data->dad = dad;
+	data->pr_buf_len = 4 * PT_MAX_PRBUF_SIZE;
+
+	filp->private_data = data;
+
+	return nonseekable_open(inode, filp);
+}
+
+/*******************************************************************************
+ * FUNCTION: panel_scan_debugfs_close
+ *
+ * SUMMARY: Close the panel_scan debugfs node to free pointer.
+ *
+ * RETURN: 0 = success
+ *
+ * PARAMETERS:
+ *      *inode - file inode number
+ *      *filp  - file pointer to debugfs file
+ ******************************************************************************/
+static int panel_scan_debugfs_close(struct inode *inode,
+		struct file *filp)
+{
+	kfree(filp->private_data);
+	filp->private_data = NULL;
+	return 0;
+}
+
+static const struct file_operations panel_scan_fops = {
+	.open = panel_scan_debugfs_open,
+	.release = panel_scan_debugfs_close,
+	.read = panel_scan_debugfs_read,
+	.write = panel_scan_debugfs_write,
+};
+
+/*******************************************************************************
+ * FUNCTION: get_idac_debugfs_read
+ *
+ * SUMMARY: Retrieve data structure with idac data id by sending the following
+ *	PIP commands:
+ *		1) Suspend Scanning
+ *		2) Retrieve data structure
+ *		3) Resume Scanning
+ *	The "Status: n" this node prints, 'n' will be:
+ *		- zero for a full pass
+ *		- negative for TTDL communication errors
+ *		- positive for any FW status errors
+ *
+ * RETURN: Size of debugfs data print
+ *
+ * PARAMETERS:
+ *      *filp   - file pointer to debugfs file
+ *      *buf    - the user space buffer to read to
+ *       count  - the maximum number of bytes to read
+ *      *ppos   - the current position in the buffer
+ ******************************************************************************/
+static ssize_t get_idac_debugfs_read(struct file *filp, char __user *buf,
+		size_t count, loff_t *ppos)
+{
+	struct pt_device_access_debugfs_data *data = filp->private_data;
+	struct pt_device_access_data *dad = data->dad;
+	struct device *dev = dad->dev;
+	int status = STATUS_FAIL;
+	u8 cmd_status = 0;
+	u8 data_format = 0;
+	u16 act_length = 0;
+	int length = 0;
+	int rc;
+
+	if (*ppos)
+		goto exit;
+
+	mutex_lock(&dad->sysfs_lock);
+
+	pm_runtime_get_sync(dev);
+
+	rc = cmd->request_exclusive(dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
+	if (rc) {
+		status = rc;
+		pt_debug(dev, DL_ERROR,
+			"%s: Error on request exclusive rc = %d\n",
+			__func__, rc);
+		goto put_pm_runtime;
+	}
+
+	rc = pt_suspend_scan_cmd_(dev);
+	if (rc) {
+		status = rc;
+		pt_debug(dev, DL_ERROR, "%s: Error on suspend scan rc = %d\n",
+			__func__, rc);
+		goto release_exclusive;
+	}
+
+	rc = pt_get_data_structure_cmd_(dev, 0, PIP_CMD_MAX_LENGTH,
+		dad->get_idac_data_id, &cmd_status, &data_format,
+		&act_length, &dad->ic_buf[5]);
+	if (rc) {
+		status = rc;
+		pt_debug(dev, DL_ERROR,
+			"%s: Error on get data structure rc = %d\n",
+			__func__, rc);
+		goto resume_scan;
+	}
+
+	dad->ic_buf[0] = cmd_status;
+	dad->ic_buf[1] = dad->get_idac_data_id;
+	dad->ic_buf[2] = LOW_BYTE(act_length);
+	dad->ic_buf[3] = HI_BYTE(act_length);
+	dad->ic_buf[4] = data_format;
+
+	length = 5 + act_length;
+
+	status = cmd_status;
+
+resume_scan:
+	pt_resume_scan_cmd_(dev);
+
+release_exclusive:
+	cmd->release_exclusive(dev);
+
+put_pm_runtime:
+	pm_runtime_put(dev);
+
+	if (status == STATUS_FAIL)
+		length = 0;
+
+	data->pr_buf_len = prepare_print_buffer(status, dad->ic_buf, length,
+		data->pr_buf, sizeof(data->pr_buf), PT_PR_FORMAT_DEFAULT);
+
+	mutex_unlock(&dad->sysfs_lock);
+
+exit:
+	return simple_read_from_buffer(buf, count, ppos, data->pr_buf,
+			data->pr_buf_len);
+}
+
+/*******************************************************************************
+ * FUNCTION: get_idac_debugfs_write
+ *
+ * SUMMARY: Store the data id of idac,the read method will perform.
+ *
+ * RETURN: Size of debugfs data write
+ *
+ * PARAMETERS:
+ *      *filp   - file pointer to debugfs file
+ *      *buf    - the user space buffer to write to
+ *       count  - the maximum number of bytes to write
+ *      *ppos   - the current position in the buffer
+ ******************************************************************************/
+static ssize_t get_idac_debugfs_write(struct file *filp,
+		const char __user *buf, size_t count, loff_t *ppos)
+{
+	struct pt_device_access_debugfs_data *data = filp->private_data;
+	struct pt_device_access_data *dad = data->dad;
+	ssize_t length;
+	u32 input_data[2];
+	int rc = 0;
+
+	rc = simple_write_to_buffer(data->pr_buf, sizeof(data->pr_buf), ppos,
+			buf, count);
+	if (rc < 0)
+		return rc;
+
+	count = rc;
+
+	mutex_lock(&dad->sysfs_lock);
+
+	length = cmd->parse_sysfs_input(dad->dev, data->pr_buf, count,
+			input_data, ARRAY_SIZE(input_data));
+	if (length != 1) {
+		pt_debug(dad->dev, DL_ERROR,
+				"%s: Malformed input\n", __func__);
+		rc = -EINVAL;
+		goto exit_unlock;
+	}
+
+	dad->get_idac_data_id = input_data[0];
+
+exit_unlock:
+	mutex_unlock(&dad->sysfs_lock);
+
+	if (rc)
+		return rc;
+
+	return count;
+}
+
+PT_DEBUGFS_FOPS(get_idac, get_idac_debugfs_read, get_idac_debugfs_write);
+
+/*******************************************************************************
+ * FUNCTION: calibrate_ext_debugfs_read
+ *
+ * SUMMARY: Perform extended calibration command(0x30) which is flexible to
+ *  calibrate each individual feature by adding extra parameter for calibration
+ *  mode.
+ *
+ * NOTE:
+ *	- This calibrate command requires the DUT to support PIP version >= 1.10
+ *	- The "Status:" included in the printout will be one of the following:
+ *		<0 - Linux error code (PIP transmission error)
+ *		 0 - Full pass
+ *		>0 - PIP error status
+ *
+ * RETURN: Size of debugfs data print
+ *
+ * PARAMETERS:
+ *	*filp   - file pointer to debugfs file
+ *	*buf    - the user space buffer to read to
+ *	 count  - the maximum number of bytes to read
+ *	*ppos   - the current position in the buffer
+ ******************************************************************************/
+static ssize_t calibrate_ext_debugfs_read(struct file *filp, char __user *buf,
+		size_t count, loff_t *ppos)
+{
+	struct pt_device_access_debugfs_data *data = filp->private_data;
+	struct pt_device_access_data *dad = data->dad;
+	struct device *dev = dad->dev;
+	int status = STATUS_FAIL;
+	int length = 0;
+	int rc;
+
+	if (*ppos)
+		goto exit;
+
+	dad->si = cmd->request_sysinfo(dad->dev);
+	if (!dad->si) {
+		pt_debug(dad->dev, DL_ERROR,
+			"%s: Fail get sysinfo pointer from core\n",
+			__func__);
+		status = -EIO;
+		data->pr_buf_len = prepare_print_buffer(status, dad->ic_buf, 0,
+			data->pr_buf, sizeof(data->pr_buf),
+			PT_PR_FORMAT_DEFAULT);
+		goto exit;
+	}
+
+	if (!IS_PIP_VER_GE(dad->si, 1, 10)) {
+		pt_debug(dad->dev, DL_ERROR,
+			"%s: extended calibration command is not supported\n",
+			__func__);
+		status = -EPROTONOSUPPORT;
+		data->pr_buf_len = prepare_print_buffer(status, dad->ic_buf, 0,
+			data->pr_buf, sizeof(data->pr_buf),
+			PT_PR_FORMAT_DEFAULT);
+		goto exit;
+	}
+
+	if (dad->cal_ext_data.mode == PT_CAL_EXT_MODE_UNDEFINED) {
+		pt_debug(dad->dev, DL_ERROR,
+			"%s: No parameters provided for calibration command\n",
+			__func__);
+		status = -EINVAL;
+		data->pr_buf_len = prepare_print_buffer(status, dad->ic_buf, 0,
+			data->pr_buf, sizeof(data->pr_buf),
+			PT_PR_FORMAT_DEFAULT);
+		goto exit;
+	}
+
+	mutex_lock(&dad->sysfs_lock);
+
+	pm_runtime_get_sync(dev);
+
+	rc = cmd->request_exclusive(dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
+	if (rc) {
+		status = rc;
+		pt_debug(dev, DL_ERROR,
+				"%s: Error on request exclusive rc = %d\n",
+				__func__, rc);
+		goto put_pm_runtime;
+	}
+
+	rc = pt_suspend_scan_cmd_(dev);
+	if (rc) {
+		status = rc;
+		pt_debug(dev, DL_ERROR, "%s: Error on suspend scan rc = %d\n",
+				__func__, rc);
+		goto release_exclusive;
+	}
+
+	rc = _pt_calibrate_ext_cmd(dev, &dad->cal_ext_data, &dad->ic_buf[0]);
+	if (rc) {
+		status = rc;
+		pt_debug(dev, DL_ERROR,
+				"%s: Error on calibrate_ext rc = %d\n",
+				__func__, rc);
+		goto resume_scan;
+	}
+
+	/*
+	 * Include PIP errors as positive status codes and report the data.
+	 * No PIP error "0x00" in the response indicates full success
+	 */
+	length = 1;
+	status = dad->ic_buf[0];
+
+resume_scan:
+	pt_resume_scan_cmd_(dev);
+
+release_exclusive:
+	cmd->release_exclusive(dev);
+
+put_pm_runtime:
+	pm_runtime_put(dev);
+
+	/* Negative status codes are bus transmission errors and have no data */
+	if (status < 0)
+		length = 0;
+
+	data->pr_buf_len = prepare_print_buffer(status, dad->ic_buf, length,
+		data->pr_buf, sizeof(data->pr_buf), PT_PR_FORMAT_DEFAULT);
+
+	mutex_unlock(&dad->sysfs_lock);
+
+exit:
+	return simple_read_from_buffer(buf, count, ppos, data->pr_buf,
+			data->pr_buf_len);
+}
+
+/*******************************************************************************
+ * FUNCTION: calibrate_ext_debugfs_write
+ *
+ * SUMMARY: Stores the calibration mode and up to three parameters to perform
+ * individual features.
+ *
+ * RETURN: Size of debugfs data write
+ *
+ * PARAMETERS:
+ *	   *filp   - file pointer to debugfs file
+ *	   *buf    - the user space buffer to write to
+ *		count  - the maximum number of bytes to write
+ *	   *ppos   - the current position in the buffer
+ ******************************************************************************/
+static ssize_t calibrate_ext_debugfs_write(struct file *filp,
+		const char __user *buf, size_t count, loff_t *ppos)
+{
+	struct pt_device_access_debugfs_data *data = filp->private_data;
+	struct pt_device_access_data *dad = data->dad;
+	ssize_t length;
+	u32 input_data[5];
+	int rc = 0;
+	int i = 0;
+
+	rc = simple_write_to_buffer(data->pr_buf, sizeof(data->pr_buf), ppos,
+			buf, count);
+	if (rc < 0)
+		return rc;
+
+	count = rc;
+
+	mutex_lock(&dad->sysfs_lock);
+
+	length = cmd->parse_sysfs_input(dad->dev, data->pr_buf, count,
+			input_data, ARRAY_SIZE(input_data));
+	if ((length <= 4) && (length > 0)) {
+		for (i = length; i < 4; i++)
+			input_data[i] = 0;
+
+		dad->cal_ext_data.mode = (u8)input_data[0];
+		dad->cal_ext_data.data0 = (u8)input_data[1];
+		dad->cal_ext_data.data1 = (u8)input_data[2];
+		dad->cal_ext_data.data2 = (u8)input_data[3];
+#ifdef TTDL_DIAGNOSTICS
+		pt_debug(dad->dev, DL_INFO,
+			"%s: calibration mode=%d, data[0..2]=0x%02X %02X %02X\n",
+			__func__,
+			dad->cal_ext_data.mode, dad->cal_ext_data.data0,
+			dad->cal_ext_data.data1, dad->cal_ext_data.data2);
+#endif
+	} else {
+		pt_debug(dad->dev, DL_ERROR,
+				"%s: Malformed input\n", __func__);
+		rc = -EINVAL;
+		goto exit_unlock;
+	}
+
+exit_unlock:
+	mutex_unlock(&dad->sysfs_lock);
+
+	if (rc)
+		return rc;
+
+	return count;
+}
+
+PT_DEBUGFS_FOPS(calibrate_ext,
+	calibrate_ext_debugfs_read, calibrate_ext_debugfs_write);
+
+/*******************************************************************************
+ * FUNCTION: calibrate_debugfs_read
+ *
+ * SUMMARY: Perform calibration by sending the following PIP commands:
+ *     1) Suspend Scanning
+ *     2) Execute calibrate
+ *     3) Initialize baseline conditionally
+ *     4) Resume Scanning
+ *
+ * RETURN: Size of debugfs data print
+ *
+ * PARAMETERS:
+ *	   *filp   - file pointer to debugfs file
+ *	   *buf    - the user space buffer to read to
+ *		count  - the maximum number of bytes to read
+ *	   *ppos   - the current position in the buffer
+ ******************************************************************************/
+static ssize_t calibrate_debugfs_read(struct file *filp, char __user *buf,
+		size_t count, loff_t *ppos)
+{
+	struct pt_device_access_debugfs_data *data = filp->private_data;
+	struct pt_device_access_data *dad = data->dad;
+	struct device *dev = dad->dev;
+	int status = STATUS_FAIL;
+	int length = 0;
+	int rc;
+
+	if (*ppos)
+		goto exit;
+
+	mutex_lock(&dad->sysfs_lock);
+
+	pm_runtime_get_sync(dev);
+
+	rc = cmd->request_exclusive(dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
+	if (rc) {
+		status = rc;
+		pt_debug(dev, DL_ERROR,
+				"%s: Error on request exclusive rc = %d\n",
+				__func__, rc);
+		goto put_pm_runtime;
+	}
+
+	rc = pt_suspend_scan_cmd_(dev);
+	if (rc) {
+		status = rc;
+		pt_debug(dev, DL_ERROR, "%s: Error on suspend scan rc = %d\n",
+				__func__, rc);
+		goto release_exclusive;
+	}
+
+	rc = _pt_calibrate_idacs_cmd(dev, dad->calibrate_sensing_mode,
+			&dad->ic_buf[0]);
+	if (rc) {
+		status = rc;
+		pt_debug(dev, DL_ERROR,
+				"%s: Error on calibrate idacs rc = %d\n",
+				__func__, rc);
+		goto resume_scan;
+	}
+
+	length = 1;
+
+	/* Check if baseline initialization is requested */
+	if (dad->calibrate_initialize_baselines) {
+		/* Perform baseline initialization for all modes */
+		rc = _pt_initialize_baselines_cmd(dev, PT_IB_SM_MUTCAP |
+				PT_IB_SM_SELFCAP | PT_IB_SM_BUTTON,
+				&dad->ic_buf[length]);
+		if (rc) {
+			status = rc;
+			pt_debug(dev, DL_ERROR,
+				"%s: Error on initialize baselines rc = %d\n",
+				__func__, rc);
+			goto resume_scan;
+		}
+
+		length++;
+	}
+
+	status = STATUS_SUCCESS;
+
+resume_scan:
+	pt_resume_scan_cmd_(dev);
+
+release_exclusive:
+	cmd->release_exclusive(dev);
+
+put_pm_runtime:
+	pm_runtime_put(dev);
+
+	if (status == STATUS_FAIL)
+		length = 0;
+
+	data->pr_buf_len = prepare_print_buffer(status, dad->ic_buf, length,
+		data->pr_buf, sizeof(data->pr_buf), PT_PR_FORMAT_DEFAULT);
+
+	mutex_unlock(&dad->sysfs_lock);
+
+exit:
+	return simple_read_from_buffer(buf, count, ppos, data->pr_buf,
+			data->pr_buf_len);
+}
+
+/*******************************************************************************
+ * FUNCTION: calibrate_debugfs_write
+ *
+ * SUMMARY: Stores the calibration sense mode and a flag to control if the
+ *  baseline will be initialized for the read method of this node.
+ *
+ * RETURN: Size of debugfs data write
+ *
+ * PARAMETERS:
+ *	   *filp   - file pointer to debugfs file
+ *	   *buf    - the user space buffer to write to
+ *		count  - the maximum number of bytes to write
+ *	   *ppos   - the current position in the buffer
+ ******************************************************************************/
+static ssize_t calibrate_debugfs_write(struct file *filp,
+		const char __user *buf, size_t count, loff_t *ppos)
+{
+	struct pt_device_access_debugfs_data *data = filp->private_data;
+	struct pt_device_access_data *dad = data->dad;
+	ssize_t length;
+	u32 input_data[3];
+	int rc = 0;
+
+	rc = simple_write_to_buffer(data->pr_buf, sizeof(data->pr_buf), ppos,
+			buf, count);
+	if (rc < 0)
+		return rc;
+
+	count = rc;
+
+	mutex_lock(&dad->sysfs_lock);
+
+	length = cmd->parse_sysfs_input(dad->dev, data->pr_buf, count,
+			input_data, ARRAY_SIZE(input_data));
+	if (length != 2) {
+		pt_debug(dad->dev, DL_ERROR,
+				"%s: Malformed input\n", __func__);
+		rc = -EINVAL;
+		goto exit_unlock;
+	}
+
+	dad->calibrate_sensing_mode = input_data[0];
+	dad->calibrate_initialize_baselines = input_data[1];
+
+exit_unlock:
+	mutex_unlock(&dad->sysfs_lock);
+
+	if (rc)
+		return rc;
+
+	return count;
+}
+
+PT_DEBUGFS_FOPS(calibrate, calibrate_debugfs_read, calibrate_debugfs_write);
+
+/*******************************************************************************
+ * FUNCTION: baseline_debugfs_read
+ *
+ * SUMMARY: Perform baseline initialization by sending the following PIP
+ *  commands:
+ *	   1) Suspend Scanning
+ *	   2) Execute initialize baseline
+ *	   3) Resume Scanning
+ *
+ * RETURN: Size of debugfs data print
+ *
+ * PARAMETERS:
+ *	   *filp   - file pointer to debugfs file
+ *	   *buf    - the user space buffer to read to
+ *		count  - the maximum number of bytes to read
+ *	   *ppos   - the current position in the buffer
+ ******************************************************************************/
+static ssize_t baseline_debugfs_read(struct file *filp, char __user *buf,
+		size_t count, loff_t *ppos)
+{
+	struct pt_device_access_debugfs_data *data = filp->private_data;
+	struct pt_device_access_data *dad = data->dad;
+	struct device *dev = dad->dev;
+	int status = STATUS_FAIL;
+	int length = 0;
+	int rc;
+
+	if (*ppos)
+		goto exit;
+
+	mutex_lock(&dad->sysfs_lock);
+
+	pm_runtime_get_sync(dev);
+
+	rc = cmd->request_exclusive(dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
+	if (rc) {
+		status = rc;
+		pt_debug(dev, DL_ERROR,
+				"%s: Error on request exclusive rc = %d\n",
+				__func__, rc);
+		goto put_pm_runtime;
+	}
+
+	rc = pt_suspend_scan_cmd_(dev);
+	if (rc) {
+		status = rc;
+		pt_debug(dev, DL_ERROR, "%s: Error on suspend scan rc = %d\n",
+				__func__, rc);
+		goto release_exclusive;
+	}
+
+	rc = _pt_initialize_baselines_cmd(dev, dad->baseline_sensing_mode,
+			&dad->ic_buf[0]);
+	if (rc) {
+		status = rc;
+		pt_debug(dev, DL_ERROR,
+				"%s: Error on initialize baselines rc = %d\n",
+				__func__, rc);
+		goto resume_scan;
+	}
+
+	length = 1;
+
+	status = STATUS_SUCCESS;
+
+resume_scan:
+	pt_resume_scan_cmd_(dev);
+
+release_exclusive:
+	cmd->release_exclusive(dev);
+
+put_pm_runtime:
+	pm_runtime_put(dev);
+
+	if (status == STATUS_FAIL)
+		length = 0;
+
+	data->pr_buf_len = prepare_print_buffer(status, dad->ic_buf, length,
+		data->pr_buf, sizeof(data->pr_buf), PT_PR_FORMAT_DEFAULT);
+
+	mutex_unlock(&dad->sysfs_lock);
+
+exit:
+	return simple_read_from_buffer(buf, count, ppos, data->pr_buf,
+			data->pr_buf_len);
+}
+
+/*******************************************************************************
+ * FUNCTION: baseline_debugfs_write
+ *
+ * SUMMARY: Store the sense mode of base initialization, the read method will
+ *  perform.
+ *
+ * RETURN: Size of debugfs data write
+ *
+ * PARAMETERS:
+ *	   *filp   - file pointer to debugfs file
+ *	   *buf    - the user space buffer to write to
+ *		count  - the maximum number of bytes to write
+ *	   *ppos   - the current position in the buffer
+ ******************************************************************************/
+static ssize_t baseline_debugfs_write(struct file *filp,
+		const char __user *buf, size_t count, loff_t *ppos)
+{
+	struct pt_device_access_debugfs_data *data = filp->private_data;
+	struct pt_device_access_data *dad = data->dad;
+	ssize_t length;
+	u32 input_data[2];
+	int rc = 0;
+
+	rc = simple_write_to_buffer(data->pr_buf, sizeof(data->pr_buf), ppos,
+			buf, count);
+	if (rc < 0)
+		return rc;
+
+	count = rc;
+
+	mutex_lock(&dad->sysfs_lock);
+
+	length = cmd->parse_sysfs_input(dad->dev, data->pr_buf, count,
+		input_data, ARRAY_SIZE(input_data));
+	if (length != 1) {
+		pt_debug(dad->dev, DL_ERROR,
+				"%s: Malformed input\n", __func__);
+		rc = -EINVAL;
+		goto exit_unlock;
+	}
+
+	dad->baseline_sensing_mode = input_data[0];
+
+exit_unlock:
+	mutex_unlock(&dad->sysfs_lock);
+
+	if (rc)
+		return rc;
+
+	return count;
+}
+
+PT_DEBUGFS_FOPS(baseline, baseline_debugfs_read, baseline_debugfs_write);
+
+/*******************************************************************************
+ * FUNCTION: auto_shorts_debugfs_read
+ *
+ * SUMMARY: Performs the "auto shorts" test and prints the result to the output
+ *  buffer.
+ *
+ * RETURN: Size of debugfs data print
+ *
+ * PARAMETERS:
+ *	*filp   - file pointer to debugfs file
+ *	*buf    - the user space buffer to read to
+ *	 count  - the maximum number of bytes to read
+ *	*ppos   - the current position in the buffer
+ ******************************************************************************/
+static ssize_t auto_shorts_debugfs_read(struct file *filp, char __user *buf,
+		size_t count, loff_t *ppos)
+{
+	struct pt_device_access_debugfs_data *data = filp->private_data;
+
+	if (!*ppos)
+		/* Set length to PIP_CMD_MAX_LENGTH to read all */
+		data->pr_buf_len = pt_run_and_get_selftest_result(
+			data->dad->dev, PT_CORE_CMD_PROTECTED,
+			data->pr_buf, sizeof(data->pr_buf),
+			PT_ST_ID_AUTOSHORTS, PIP_CMD_MAX_LENGTH,
+			PT_ST_DONT_GET_RESULTS, PT_ST_PRINT_RESULTS,
+			PT_PR_FORMAT_DEFAULT);
+
+	return simple_read_from_buffer(buf, count, ppos, data->pr_buf,
+			data->pr_buf_len);
+}
+
+PT_DEBUGFS_FOPS(auto_shorts, auto_shorts_debugfs_read, NULL);
+
+/*******************************************************************************
+ * FUNCTION: opens_debugfs_read
+ *
+ * SUMMARY: Performs the "opens" test and prints the results to the output
+ *  buffer.
+ *
+ * RETURN: Size of debugfs data print
+ *
+ * PARAMETERS:
+ *	   *filp   - file pointer to debugfs file
+ *	   *buf    - the user space buffer to read to
+ *		count  - the maximum number of bytes to read
+ *	   *ppos   - the current position in the buffer
+ ******************************************************************************/
+static ssize_t opens_debugfs_read(struct file *filp, char __user *buf,
+		size_t count, loff_t *ppos)
+{
+	struct pt_device_access_debugfs_data *data = filp->private_data;
+
+	if (!*ppos)
+		/* Set length to PIP_CMD_MAX_LENGTH to read all */
+		data->pr_buf_len = pt_run_and_get_selftest_result(
+			data->dad->dev, PT_CORE_CMD_PROTECTED,
+			data->pr_buf, sizeof(data->pr_buf),
+			PT_ST_ID_OPENS, PIP_CMD_MAX_LENGTH,
+			PT_ST_DONT_GET_RESULTS, PT_ST_PRINT_RESULTS,
+			PT_PR_FORMAT_DEFAULT);
+
+	return simple_read_from_buffer(buf, count, ppos, data->pr_buf,
+			data->pr_buf_len);
+}
+
+PT_DEBUGFS_FOPS(opens, opens_debugfs_read, NULL);
+
+/*******************************************************************************
+ * FUNCTION: cm_panel_debugfs_read
+ *
+ * SUMMARY: Performs the "CM panel" test and prints the result to the output
+ *  buffer.
+ *
+ * RETURN: Size of debugfs data print
+ *
+ * PARAMETERS:
+ *	   *filp   - file pointer to debugfs file
+ *	   *buf    - the user space buffer to read to
+ *		count  - the maximum number of bytes to read
+ *	   *ppos   - the current position in the buffer
+ ******************************************************************************/
+static ssize_t cm_panel_debugfs_read(struct file *filp, char __user *buf,
+		size_t count, loff_t *ppos)
+{
+	struct pt_device_access_debugfs_data *data = filp->private_data;
+
+	if (!*ppos)
+		/* Set length to PIP_CMD_MAX_LENGTH to read all */
+		data->pr_buf_len = pt_run_and_get_selftest_result(
+			data->dad->dev, PT_CORE_CMD_PROTECTED,
+			data->pr_buf, sizeof(data->pr_buf),
+			PT_ST_ID_CM_PANEL, PIP_CMD_MAX_LENGTH,
+			PT_ST_GET_RESULTS, PT_ST_PRINT_RESULTS,
+			PT_PR_FORMAT_DEFAULT);
+
+	return simple_read_from_buffer(buf, count, ppos, data->pr_buf,
+			data->pr_buf_len);
+}
+
+PT_DEBUGFS_FOPS(cm_panel, cm_panel_debugfs_read, NULL);
+
+/*******************************************************************************
+ * FUNCTION: cp_panel_debugfs_read
+ *
+ * SUMMARY: Performs the "CP panel" test and prints the result to the output
+ *  buffer.
+ *
+ * RETURN: Size of debugfs data print
+ *
+ * PARAMETERS:
+ *	   *filp   - file pointer to debugfs file
+ *	   *buf    - the user space buffer to read to
+ *		count  - the maximum number of bytes to read
+ *	   *ppos   - the current position in the buffer
+ ******************************************************************************/
+static ssize_t cp_panel_debugfs_read(struct file *filp, char __user *buf,
+		size_t count, loff_t *ppos)
+{
+	struct pt_device_access_debugfs_data *data = filp->private_data;
+
+	if (!*ppos)
+		/* Set length to PIP_CMD_MAX_LENGTH to read all */
+		data->pr_buf_len = pt_run_and_get_selftest_result(
+			data->dad->dev, PT_CORE_CMD_PROTECTED,
+			data->pr_buf, sizeof(data->pr_buf),
+			PT_ST_ID_CP_PANEL, PIP_CMD_MAX_LENGTH,
+			PT_ST_GET_RESULTS, PT_ST_PRINT_RESULTS,
+			PT_PR_FORMAT_DEFAULT);
+
+	return simple_read_from_buffer(buf, count, ppos, data->pr_buf,
+			data->pr_buf_len);
+}
+
+PT_DEBUGFS_FOPS(cp_panel, cp_panel_debugfs_read, NULL);
+
+/*******************************************************************************
+ * FUNCTION: cm_button_debugfs_read
+ *
+ * SUMMARY: Performs the "CM buttons" test and prints the result to the output
+ *  buffer.
+ *
+ * RETURN: Size of debugfs data print
+ *
+ * PARAMETERS:
+ *	   *filp   - file pointer to debugfs file
+ *	   *buf    - the user space buffer to read to
+ *		count  - the maximum number of bytes to read
+ *	   *ppos   - the current position in the buffer
+ ******************************************************************************/
+static ssize_t cm_button_debugfs_read(struct file *filp, char __user *buf,
+		size_t count, loff_t *ppos)
+{
+	struct pt_device_access_debugfs_data *data = filp->private_data;
+
+	if (!*ppos)
+		/* Set length to PIP_CMD_MAX_LENGTH to read all */
+		data->pr_buf_len = pt_run_and_get_selftest_result(
+			data->dad->dev, PT_CORE_CMD_PROTECTED,
+			data->pr_buf, sizeof(data->pr_buf),
+			PT_ST_ID_CM_BUTTON, PIP_CMD_MAX_LENGTH,
+			PT_ST_GET_RESULTS, PT_ST_PRINT_RESULTS,
+			PT_PR_FORMAT_DEFAULT);
+
+	return simple_read_from_buffer(buf, count, ppos, data->pr_buf,
+			data->pr_buf_len);
+}
+
+PT_DEBUGFS_FOPS(cm_button, cm_button_debugfs_read, NULL);
+
+/*******************************************************************************
+ * FUNCTION: cp_button_debugfs_read
+ *
+ * SUMMARY: Performs the "CP buttons" test and prints the result to the output
+ *  buffer.
+ *
+ * RETURN: Size of debugfs data print
+ *
+ * PARAMETERS:
+ *	   *filp   - file pointer to debugfs file
+ *	   *buf    - the user space buffer to read to
+ *		count  - the maximum number of bytes to read
+ *	   *ppos   - the current position in the buffer
+ ******************************************************************************/
+static ssize_t cp_button_debugfs_read(struct file *filp, char __user *buf,
+		size_t count, loff_t *ppos)
+{
+	struct pt_device_access_debugfs_data *data = filp->private_data;
+
+	if (!*ppos)
+		/* Set length to PIP_CMD_MAX_LENGTH to read all */
+		data->pr_buf_len = pt_run_and_get_selftest_result(
+			data->dad->dev, PT_CORE_CMD_PROTECTED,
+			data->pr_buf, sizeof(data->pr_buf),
+			PT_ST_ID_CP_BUTTON, PIP_CMD_MAX_LENGTH,
+			PT_ST_GET_RESULTS, PT_ST_PRINT_RESULTS,
+			PT_PR_FORMAT_DEFAULT);
+
+	return simple_read_from_buffer(buf, count, ppos, data->pr_buf,
+			data->pr_buf_len);
+}
+
+PT_DEBUGFS_FOPS(cp_button, cp_button_debugfs_read, NULL);
+
+/*******************************************************************************
+ * FUNCTION: fw_self_test_debugfs_read
+ *
+ * SUMMARY: Performs the self test by firmware and prints the results to the
+ *  output buffer.
+ *
+ * RETURN: Size of debugfs data print
+ *
+ * PARAMETERS:
+ *	*filp   - file pointer to debugfs file
+ *	*buf    - the user space buffer to read to
+ *	 count  - the maximum number of bytes to read
+ *	*ppos   - the current position in the buffer
+ ******************************************************************************/
+static ssize_t fw_self_test_debugfs_read(struct file *filp, char __user *buf,
+		size_t count, loff_t *ppos)
+{
+	u8 ret_status;
+	u8 ret_self_test_id;
+	u8 sys_mode = FW_SYS_MODE_UNDEFINED;
+	u16 ret_act_load_len;
+	int rc;
+	struct pt_device_access_debugfs_data *data = filp->private_data;
+	struct pt_device_access_data *dad = data->dad;
+
+	if (!*ppos) {
+		if (dad->fw_self_test_id == PT_ST_ID_INVALID ||
+			dad->fw_self_test_format >= PT_PR_FORMAT_UNDEFINE) {
+			data->pr_buf_len = scnprintf(data->pr_buf,
+				sizeof(data->pr_buf), "Status: %d\n", -EINVAL);
+			pt_debug(dad->dev, DL_ERROR,
+				"%s: Malformed input\n", __func__);
+			goto exit;
+		}
+
+		/* only send the load parameters cmd if param data exists */
+		if (dad->fw_self_test_param_len > 0) {
+			rc = cmd->request_get_fw_mode(dad->dev,
+				PT_CORE_CMD_UNPROTECTED, &sys_mode, NULL);
+			if (rc) {
+				pt_debug(dad->dev, DL_ERROR,
+					"%s: ERR on request mode rc=%d\n",
+					__func__, rc);
+				data->pr_buf_len = scnprintf(
+					data->pr_buf,
+					sizeof(data->pr_buf),
+					"Status: %d\n", rc);
+				goto exit;
+			}
+
+			if (sys_mode != FW_SYS_MODE_TEST) {
+				rc = pt_suspend_scan_cmd_(dad->dev);
+				if (rc) {
+					pt_debug(dad->dev, DL_ERROR,
+						"%s: ERR on sus scan rc=%d\n",
+						__func__, rc);
+					data->pr_buf_len = scnprintf(
+						data->pr_buf,
+						sizeof(data->pr_buf),
+						"Status: %d\n", rc);
+					goto exit;
+				}
+			}
+			cmd->nonhid_cmd->load_self_test_param(dad->dev,
+				PT_CORE_CMD_PROTECTED,
+				dad->fw_self_test_id, 0,
+				dad->fw_self_test_param_len,
+				dad->fw_self_test_param, &ret_status,
+				&ret_self_test_id, &ret_act_load_len);
+			if (ret_status) {
+				data->pr_buf_len = scnprintf(data->pr_buf,
+					sizeof(data->pr_buf),
+					"Status: %d\n", -EINVAL);
+				pt_debug(dad->dev, DL_ERROR,
+					"%s: Load Param Malformed input\n",
+					__func__);
+				goto resume_scan;
+			}
+		}
+
+		/* Set length to PIP_CMD_MAX_LENGTH to read all */
+		data->pr_buf_len = pt_run_and_get_selftest_result(
+			dad->dev, PT_CORE_CMD_PROTECTED,
+			data->pr_buf, sizeof(data->pr_buf),
+			dad->fw_self_test_id, PIP_CMD_MAX_LENGTH,
+			PT_ST_GET_RESULTS, PT_ST_PRINT_RESULTS,
+			dad->fw_self_test_format);
+
+		/* Clear the parameters so next test won't use them */
+		if (dad->fw_self_test_param_len > 0) {
+			cmd->nonhid_cmd->load_self_test_param(dad->dev,
+				PT_CORE_CMD_PROTECTED,
+				dad->fw_self_test_id, 0, 0, NULL,
+				&ret_status, &ret_self_test_id,
+				&ret_act_load_len);
+		}
+
+		dad->fw_self_test_id = PT_ST_ID_INVALID;
+		dad->fw_self_test_format = PT_PR_FORMAT_UNDEFINE;
+		dad->fw_self_test_param_len = 0;
+
+resume_scan:
+		/* Only resume scanning if we suspended it */
+		if (sys_mode == FW_SYS_MODE_SCANNING)
+			pt_resume_scan_cmd_(dad->dev);
+	}
+
+exit:
+	return simple_read_from_buffer(buf, count, ppos, data->pr_buf,
+			data->pr_buf_len);
+}
+
+/*******************************************************************************
+ * FUNCTION: fw_self_test_debugfs_write
+ *
+ * SUMMARY: Store the self test ID and ouptut format, the read method will
+ *  perform.
+ *
+ * RETURN: Size of debugfs data write
+ *
+ * PARAMETERS:
+ *	*filp   - file pointer to debugfs file
+ *	*buf    - the user space buffer to write to
+ *	 count  - the maximum number of bytes to write
+ *	*ppos   - the current position in the buffer
+ ******************************************************************************/
+static ssize_t fw_self_test_debugfs_write(struct file *filp,
+		const char __user *buf, size_t count, loff_t *ppos)
+{
+	struct pt_device_access_debugfs_data *data = filp->private_data;
+	struct pt_device_access_data *dad = data->dad;
+	ssize_t length;
+	u32 input_data[PT_FW_SELF_TEST_MAX_PARM + 1];
+	int rc = 0;
+	int i;
+
+	rc = simple_write_to_buffer(data->pr_buf, sizeof(data->pr_buf), ppos,
+			buf, count);
+	if (rc < 0)
+		return rc;
+
+	count = rc;
+
+	mutex_lock(&dad->sysfs_lock);
+
+	length = cmd->parse_sysfs_input(dad->dev, data->pr_buf, count,
+		input_data, PT_FW_SELF_TEST_MAX_PARM + 1);
+	if (length == 1) {
+		dad->fw_self_test_id = input_data[0];
+		dad->fw_self_test_format = PT_PR_FORMAT_DEFAULT;
+		dad->fw_self_test_param_len = 0;
+	} else if (length == 2) {
+		dad->fw_self_test_id = input_data[0];
+		dad->fw_self_test_format = input_data[1];
+		dad->fw_self_test_param_len = 0;
+	} else if (length > 2 && (length <= PT_FW_SELF_TEST_MAX_PARM)) {
+		dad->fw_self_test_id = input_data[0];
+		dad->fw_self_test_format = input_data[1];
+		dad->fw_self_test_param_len = length - 2;
+		pt_debug(dad->dev, DL_INFO,
+			"%s: test_id=%d, format=%d, param_len=%d",
+			__func__, dad->fw_self_test_id,
+			dad->fw_self_test_format, dad->fw_self_test_param_len);
+		for (i = 0; i < dad->fw_self_test_param_len; i++)
+			dad->fw_self_test_param[i] = input_data[i + 2];
+	} else {
+		pt_debug(dad->dev, DL_ERROR,
+				"%s: Malformed input\n", __func__);
+		rc = -EINVAL;
+		goto exit_unlock;
+	}
+
+exit_unlock:
+	mutex_unlock(&dad->sysfs_lock);
+
+	if (rc)
+		return rc;
+
+	return count;
+}
+
+PT_DEBUGFS_FOPS(fw_self_test,
+	fw_self_test_debugfs_read, fw_self_test_debugfs_write);
+
+#ifdef TTHE_TUNER_SUPPORT
+/*******************************************************************************
+ * FUNCTION: tthe_get_panel_data_debugfs_read
+ *
+ * SUMMARY: Performs a panel scan and prints the panel data into the output
+ *  buffer.
+ *
+ * RETURN: Size of debugfs data print
+ *
+ * PARAMETERS:
+ *	   *filp   - file pointer to debugfs file
+ *	   *buf    - the user space buffer to read to
+ *		count  - the maximum number of bytes to read
+ *	   *ppos   - the current position in the buffer
+ ******************************************************************************/
+static ssize_t tthe_get_panel_data_debugfs_read(struct file *filp,
+		char __user *buf, size_t count, loff_t *ppos)
+{
+	struct pt_device_access_data *dad = filp->private_data;
+	struct device *dev;
+	struct pt_core_data *cd;
+	u8 config;
+	u16 actual_read_len;
+	u16 length = 0;
+	u8 element_size = 0;
+	u8 *buf_offset;
+	u8 *buf_out;
+	int elem;
+	int elem_offset = 0;
+	int print_idx = 0;
+	int rc;
+	int rc1;
+	int i;
+
+	mutex_lock(&dad->debugfs_lock);
+	dev = dad->dev;
+	cd = dev_get_drvdata(dev);
+	buf_out = dad->panel_scan_data_buf;
+	if (!buf_out)
+		goto release_mutex;
+
+	pm_runtime_get_sync(dev);
+
+	rc = cmd->request_exclusive(dev, PT_REQUEST_EXCLUSIVE_TIMEOUT);
+	if (rc)
+		goto put_runtime;
+
+	if (dad->heatmap.scan_start) {
+		/*
+		 * To fix CDT206291: avoid multiple scans when
+		 * return data is larger than 4096 bytes in one cycle
+		 */
+		dad->heatmap.scan_start = 0;
+
+		/* Start scan */
+		rc = pt_exec_scan_cmd_(dev, 0);
+		if (rc)
+			goto release_exclusive;
+	}
+
+	elem = dad->heatmap.num_element;
+
+#if defined(PT_ENABLE_MAX_ELEN)
+	if (elem > PT_MAX_ELEN) {
+		rc = pt_ret_scan_data_cmd_(dev, elem_offset,
+		PT_MAX_ELEN, dad->heatmap.data_type, dad->ic_buf,
+		&config, &actual_read_len, NULL);
+	} else {
+		rc = pt_ret_scan_data_cmd_(dev, elem_offset, elem,
+			dad->heatmap.data_type, dad->ic_buf, &config,
+			&actual_read_len, NULL);
+	}
+#else
+	rc = pt_ret_scan_data_cmd_(dev, elem_offset, elem,
+			dad->heatmap.data_type, dad->ic_buf, &config,
+			&actual_read_len, NULL);
+#endif
+	if (rc)
+		goto release_exclusive;
+
+	length = get_unaligned_le16(&dad->ic_buf[0]);
+	buf_offset = dad->ic_buf + length;
+
+	element_size = config & PT_CMD_RET_PANEL_ELMNT_SZ_MASK;
+
+	elem -= actual_read_len;
+	elem_offset = actual_read_len;
+	while (elem > 0) {
+#ifdef PT_ENABLE_MAX_ELEN
+		if (elem > PT_MAX_ELEN) {
+			rc = pt_ret_scan_data_cmd_(dev, elem_offset,
+			PT_MAX_ELEN, dad->heatmap.data_type, NULL, &config,
+			&actual_read_len, buf_offset);
+		} else {
+			rc = pt_ret_scan_data_cmd_(dev, elem_offset, elem,
+				dad->heatmap.data_type, NULL, &config,
+				&actual_read_len, buf_offset);
+		}
+#else
+
+		rc = pt_ret_scan_data_cmd_(dev, elem_offset, elem,
+				dad->heatmap.data_type, NULL, &config,
+				&actual_read_len, buf_offset);
+#endif
+		if (rc)
+			goto release_exclusive;
+
+		if (!actual_read_len)
+			break;
+
+		length += actual_read_len * element_size;
+		buf_offset = dad->ic_buf + length;
+		elem -= actual_read_len;
+		elem_offset += actual_read_len;
+	}
+
+	/* Reconstruct cmd header */
+	put_unaligned_le16(length, &dad->ic_buf[0]);
+	put_unaligned_le16(elem_offset, &dad->ic_buf[7]);
+
+release_exclusive:
+	rc1 = cmd->release_exclusive(dev);
+put_runtime:
+	pm_runtime_put(dev);
+
+	if (rc)
+		goto release_mutex;
+	if (cd->show_timestamp)
+		print_idx += scnprintf(buf_out, TTHE_TUNER_MAX_BUF,
+			"[%u] PT_DATA:", pt_get_time_stamp());
+	else
+		print_idx += scnprintf(buf_out, TTHE_TUNER_MAX_BUF,
+			"PT_DATA:");
+	for (i = 0; i < length; i++)
+		print_idx += scnprintf(buf_out + print_idx,
+				TTHE_TUNER_MAX_BUF - print_idx,
+				"%02X ", dad->ic_buf[i]);
+	print_idx += scnprintf(buf_out + print_idx,
+			TTHE_TUNER_MAX_BUF - print_idx,
+			":(%d bytes)\n", length);
+	rc = simple_read_from_buffer(buf, count, ppos, buf_out, print_idx);
+	print_idx = rc;
+
+release_mutex:
+	mutex_unlock(&dad->debugfs_lock);
+	return print_idx;
+}
+
+/*******************************************************************************
+ * FUNCTION: tthe_get_panel_data_debugfs_write
+ *
+ * SUMMARY: Store the panel data type to retrieve and size of panel data, the
+ *  read method will perform.
+ *
+ * RETURN: Size of debugfs data write
+ *
+ * PARAMETERS:
+ *	*filp   - file pointer to debugfs file
+ *	*buf    - the user space buffer to write to
+ *	 count  - the maximum number of bytes to write
+ *	*ppos   - the current position in the buffer
+ ******************************************************************************/
+static ssize_t tthe_get_panel_data_debugfs_write(struct file *filp,
+		const char __user *buf, size_t count, loff_t *ppos)
+{
+	struct pt_device_access_data *dad = filp->private_data;
+	struct device *dev = dad->dev;
+	ssize_t length;
+	int max_read;
+	u32 input_data[8];
+	u8 *buf_in = dad->panel_scan_data_buf;
+	int ret;
+
+	mutex_lock(&dad->debugfs_lock);
+	ret = copy_from_user(buf_in + (*ppos), buf, count);
+	if (ret)
+		goto exit;
+	buf_in[count] = 0;
+
+	length = cmd->parse_sysfs_input(dev, buf_in, count, input_data,
+			ARRAY_SIZE(input_data));
+	if (length <= 0) {
+		pt_debug(dev, DL_ERROR,
+				"%s: %s Group Data store\n",
+				__func__, "Malformed input for");
+		goto exit;
+	}
+
+	/* update parameter value */
+	dad->heatmap.num_element = input_data[3] + (input_data[4] << 8);
+	dad->heatmap.data_type = input_data[5];
+
+	if (input_data[6] > 0)
+		dad->heatmap.scan_start = true;
+	else
+		dad->heatmap.scan_start = false;
+
+	/* elem can not be bigger then buffer size */
+	max_read = PT_CMD_RET_PANEL_HDR;
+	max_read += dad->heatmap.num_element * PT_CMD_RET_PANEL_ELMNT_SZ_MAX;
+
+	if (max_read >= PT_MAX_PRBUF_SIZE) {
+		dad->heatmap.num_element =
+			(PT_MAX_PRBUF_SIZE - PT_CMD_RET_PANEL_HDR)
+			/ PT_CMD_RET_PANEL_ELMNT_SZ_MAX;
+		pt_debug(dev, DL_INFO, "%s: Will get %d element\n",
+			__func__, dad->heatmap.num_element);
+	}
+
+exit:
+	mutex_unlock(&dad->debugfs_lock);
+	pt_debug(dev, DL_INFO, "%s: return count=%zu\n",
+		__func__, count);
+	return count;
+}
+
+/*******************************************************************************
+ * FUNCTION: tthe_get_panel_data_debugfs_open
+ *
+ * SUMMARY: Open the get_panel_data debugfs node to initialize.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *  *inode - pointer to inode structure
+ *  *filp  - pointer to file structure
+ ******************************************************************************/
+static int tthe_get_panel_data_debugfs_open(struct inode *inode,
+		struct file *filp)
+{
+	struct pt_device_access_data *dad = inode->i_private;
+
+	mutex_lock(&dad->debugfs_lock);
+
+	if (dad->tthe_get_panel_data_is_open) {
+		mutex_unlock(&dad->debugfs_lock);
+		return -EBUSY;
+	}
+
+	filp->private_data = inode->i_private;
+
+	dad->tthe_get_panel_data_is_open = 1;
+	mutex_unlock(&dad->debugfs_lock);
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: tthe_get_panel_data_debugfs_close
+ *
+ * SUMMARY: Close the get_panel_data debugfs node to free pointer.
+ *
+ * RETURN:
+ *  0 = success
+ * !0 = failure
+ *
+ * PARAMETERS:
+ *	*inode - pointer to inode structure
+ *	*filp  - pointer to file structure
+ ******************************************************************************/
+static int tthe_get_panel_data_debugfs_close(struct inode *inode,
+		struct file *filp)
+{
+	struct pt_device_access_data *dad = filp->private_data;
+
+	mutex_lock(&dad->debugfs_lock);
+	filp->private_data = NULL;
+	dad->tthe_get_panel_data_is_open = 0;
+	mutex_unlock(&dad->debugfs_lock);
+
+	return 0;
+}
+
+static const struct file_operations tthe_get_panel_data_fops = {
+	.open = tthe_get_panel_data_debugfs_open,
+	.release = tthe_get_panel_data_debugfs_close,
+	.read = tthe_get_panel_data_debugfs_read,
+	.write = tthe_get_panel_data_debugfs_write,
+};
+#endif
+
+/*******************************************************************************
+ * FUNCTION: pt_setup_sysfs
+ *
+ * SUMMARY: Creates all device test dependent sysfs nodes owned by the
+ *  device access file.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*dev  - pointer to device structure
+ ******************************************************************************/
+static int pt_setup_sysfs(struct device *dev)
+{
+	struct pt_device_access_data *dad
+		= pt_get_device_access_data(dev);
+	int rc = 0;
+
+	pt_debug(dev, DL_INFO, "Entering %s\n", __func__);
+
+	dad->base_dentry = debugfs_create_dir(dev_name(dev), NULL);
+	if (IS_ERR_OR_NULL(dad->base_dentry)) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Error, could not create base directory\n",
+			__func__);
+		goto exit;
+	}
+
+	dad->mfg_test_dentry = debugfs_create_dir("mfg_test",
+			dad->base_dentry);
+	if (IS_ERR_OR_NULL(dad->mfg_test_dentry)) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Error, could not create mfg_test directory\n",
+			__func__);
+		goto unregister_base_dir;
+	}
+
+	dad->panel_scan_debugfs = debugfs_create_file(
+		"panel_scan", 0644, dad->mfg_test_dentry, dad,
+		&panel_scan_fops);
+	if (IS_ERR_OR_NULL(dad->panel_scan_debugfs)) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Error, could not create panel_scan\n",
+			__func__);
+		goto unregister_base_dir;
+	}
+
+	if (IS_ERR_OR_NULL(debugfs_create_file("get_idac", 0600,
+			dad->mfg_test_dentry, dad, &get_idac_debugfs_fops))) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Error, could not create get_idac\n",
+			__func__);
+		goto unregister_base_dir;
+	}
+
+	if (IS_ERR_OR_NULL(debugfs_create_file("auto_shorts", 0400,
+			dad->mfg_test_dentry, dad,
+			&auto_shorts_debugfs_fops))) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Error, could not create auto_shorts\n",
+			__func__);
+		goto unregister_base_dir;
+	}
+
+	if (IS_ERR_OR_NULL(debugfs_create_file("opens", 0400,
+			dad->mfg_test_dentry, dad, &opens_debugfs_fops))) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Error, could not create opens\n",
+			__func__);
+		goto unregister_base_dir;
+	}
+
+	if (IS_ERR_OR_NULL(debugfs_create_file("calibrate_ext",
+			0600, dad->mfg_test_dentry,
+			dad, &calibrate_ext_debugfs_fops))) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Error, could not create calibrate_ext\n",
+			__func__);
+		goto unregister_base_dir;
+	}
+
+	if (IS_ERR_OR_NULL(debugfs_create_file("calibrate", 0600,
+			dad->mfg_test_dentry, dad, &calibrate_debugfs_fops))) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Error, could not create calibrate\n",
+			__func__);
+		goto unregister_base_dir;
+	}
+
+	if (IS_ERR_OR_NULL(debugfs_create_file("baseline", 0600,
+			dad->mfg_test_dentry, dad, &baseline_debugfs_fops))) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Error, could not create baseline\n",
+			__func__);
+		goto unregister_base_dir;
+	}
+
+	if (IS_ERR_OR_NULL(debugfs_create_file("cm_panel", 0400,
+			dad->mfg_test_dentry, dad, &cm_panel_debugfs_fops))) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Error, could not create cm_panel\n",
+			__func__);
+		goto unregister_base_dir;
+	}
+
+	if (IS_ERR_OR_NULL(debugfs_create_file("cp_panel", 0400,
+			dad->mfg_test_dentry, dad, &cp_panel_debugfs_fops))) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Error, could not create cp_panel\n",
+			__func__);
+		goto unregister_base_dir;
+	}
+
+	if (IS_ERR_OR_NULL(debugfs_create_file("cm_button", 0400,
+			dad->mfg_test_dentry, dad, &cm_button_debugfs_fops))) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Error, could not create cm_button\n",
+			__func__);
+		goto unregister_base_dir;
+	}
+
+	if (IS_ERR_OR_NULL(debugfs_create_file("cp_button", 0400,
+			dad->mfg_test_dentry, dad, &cp_button_debugfs_fops))) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Error, could not create cp_button\n",
+			__func__);
+		goto unregister_base_dir;
+	}
+
+	if (IS_ERR_OR_NULL(debugfs_create_file("fw_self_test", 0600,
+		dad->mfg_test_dentry, dad, &fw_self_test_debugfs_fops))) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Error, could not create fw_self_test\n",
+			__func__);
+		goto unregister_base_dir;
+	}
+
+	dad->cmcp_results_debugfs = debugfs_create_file("cmcp_results", 0644,
+		dad->mfg_test_dentry, dad, &cmcp_results_debugfs_fops);
+	if (IS_ERR_OR_NULL(dad->cmcp_results_debugfs)) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Error, could not create cmcp_results\n",
+			__func__);
+		dad->cmcp_results_debugfs = NULL;
+		goto unregister_base_dir;
+	}
+
+#ifdef TTHE_TUNER_SUPPORT
+	dad->tthe_get_panel_data_debugfs = debugfs_create_file(
+			PT_TTHE_TUNER_GET_PANEL_DATA_FILE_NAME,
+			0644, NULL, dad, &tthe_get_panel_data_fops);
+	if (IS_ERR_OR_NULL(dad->tthe_get_panel_data_debugfs)) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Error, could not create get_panel_data\n",
+			__func__);
+		dad->tthe_get_panel_data_debugfs = NULL;
+		goto unregister_base_dir;
+	}
+#endif
+
+	rc = device_create_file(dev, &dev_attr_cmcp_test);
+	if (rc) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Error, could not create cmcp_test\n",
+			__func__);
+		goto unregister_base_dir;
+	}
+
+	rc = device_create_file(dev, &dev_attr_cmcp_threshold_loading);
+	if (rc) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Error, could not create cmcp_threshold_loading\n",
+			__func__);
+		goto unregister_cmcp_test;
+	}
+
+	rc = device_create_bin_file(dev, &bin_attr_cmcp_threshold_data);
+	if (rc) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Error, could not create cmcp_threshold_data\n",
+			__func__);
+		goto unregister_cmcp_thresold_loading;
+	}
+
+	dad->sysfs_nodes_created = true;
+	return rc;
+
+unregister_cmcp_thresold_loading:
+	device_remove_file(dev, &dev_attr_cmcp_threshold_loading);
+unregister_cmcp_test:
+	device_remove_file(dev, &dev_attr_cmcp_test);
+unregister_base_dir:
+	debugfs_remove_recursive(dad->base_dentry);
+exit:
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_setup_cmcp_attention
+ *
+ * SUMMARY: Function to be registered to TTDL attention list to setup sysfs and
+ *  parse threshold file for device test.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*dev  - pointer to device structure
+ ******************************************************************************/
+static int pt_setup_cmcp_attention(struct device *dev)
+{
+	struct pt_device_access_data *dad
+		= pt_get_device_access_data(dev);
+	int rc = 0;
+
+	dad->si = cmd->request_sysinfo(dev);
+	if (!dad->si)
+		return -EINVAL;
+
+	rc = pt_setup_sysfs(dev);
+	schedule_work(&dad->cmcp_threshold_update);
+
+	cmd->unsubscribe_attention(dev, PT_ATTEN_STARTUP,
+		PT_DEVICE_ACCESS_NAME, pt_setup_cmcp_attention,
+		0);
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_cmcp_parse_threshold_file
+ *
+ * SUMMARY: Parse cmcp threshold file and store it to the structure.
+ *
+ * PARAMETERS:
+ *	*fw       - pointer to firmware structure
+ *	*context  - expected read length of the response
+ ******************************************************************************/
+static void pt_cmcp_parse_threshold_file(const struct firmware *fw,
+		void *context)
+{
+	struct device *dev = context;
+	struct pt_device_access_data *dad =
+		pt_get_device_access_data(dev);
+
+	if (!fw) {
+		pt_debug(dev, DL_WARN,
+				"%s: No builtin cmcp threshold file\n",
+				__func__);
+		goto exit;
+	}
+
+	if (!fw->data || !fw->size) {
+		pt_debug(dev, DL_ERROR,
+				"%s: Invalid builtin cmcp threshold file\n",
+		__func__);
+		goto exit;
+	}
+
+	pt_debug(dev, DL_WARN, "%s: Found cmcp threshold file.\n",
+		__func__);
+
+	pt_parse_cmcp_threshold_file_common(dev, &fw->data[0], fw->size);
+
+	dad->builtin_cmcp_threshold_status = 0;
+	release_firmware(fw);
+	return;
+
+exit:
+	if (fw)
+		release_firmware(fw);
+
+	dad->builtin_cmcp_threshold_status = -EINVAL;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_device_access_user_command
+ *
+ * SUMMARY: Wrapper function to call pt_cmcp_parse_threshold_file() by firmware
+ *  class function.
+ *
+ * PARAMETERS:
+ *	*cmcp_threshold_update - pointer to work_struct structure
+ ******************************************************************************/
+static void pt_parse_cmcp_threshold_builtin(
+	struct work_struct *cmcp_threshold_update)
+{
+	struct pt_device_access_data *dad =
+		container_of(cmcp_threshold_update,
+		struct pt_device_access_data,
+		cmcp_threshold_update);
+	struct device *dev = dad->dev;
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	const struct firmware *fw_entry = NULL;
+	int retval;
+
+	dad->si = cmd->request_sysinfo(dev);
+	if (!dad->si) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Fail get sysinfo pointer from core\n",
+			__func__);
+		return;
+	}
+
+	pt_debug(dev, DL_INFO,
+		"%s: Enabling cmcp threshold class loader built-in\n",
+		__func__);
+
+	/* Open threshold file */
+	mutex_lock(&cd->firmware_class_lock);
+#if (KERNEL_VERSION(3, 13, 0) > LINUX_VERSION_CODE)
+	retval = request_firmware(&fw_entry, CMCP_THRESHOLD_FILE_NAME, dev);
+#else
+	retval = request_firmware_direct(&fw_entry,
+				CMCP_THRESHOLD_FILE_NAME, dev);
+#endif
+	if (retval < 0) {
+		pt_debug(dev, DL_WARN,
+			"%s: Failed loading cmcp threshold file, attempting legacy file\n",
+			__func__);
+		/* Try legacy file name */
+#if (KERNEL_VERSION(3, 13, 0) > LINUX_VERSION_CODE)
+	retval = request_firmware(&fw_entry,
+				PT_CMCP_THRESHOLD_FILE_NAME, dev);
+#else
+	retval = request_firmware_direct(&fw_entry,
+				PT_CMCP_THRESHOLD_FILE_NAME, dev);
+#endif
+		if (retval < 0) {
+			mutex_unlock(&cd->firmware_class_lock);
+			dad->builtin_cmcp_threshold_status = -EINVAL;
+			pt_debug(dev, DL_WARN,
+				"%s: Fail request cmcp threshold class file load\n",
+				__func__);
+			goto exit;
+		}
+	}
+	pt_cmcp_parse_threshold_file(fw_entry, dev);
+
+	mutex_unlock(&cd->firmware_class_lock);
+
+exit:
+	return;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_device_access_probe
+ *
+ * SUMMARY: The probe function for the device access
+ *
+ * PARAMETERS:
+ *	*dev   - pointer to device structure
+ *	**data - double pointer to pt_device_access_data data to be created here
+ ******************************************************************************/
+static int pt_device_access_probe(struct device *dev, void **data)
+{
+	struct pt_device_access_data *dad;
+	struct configuration *configurations;
+	struct cmcp_data *cmcp_info;
+	struct result *result;
+
+	int tx_num = MAX_TX_SENSORS;
+	int rx_num = MAX_RX_SENSORS;
+	int btn_num = MAX_BUTTONS;
+
+	struct test_case_field *test_case_field_array;
+	struct test_case_search *test_case_search_array;
+	int rc = 0;
+
+	dad = kzalloc(sizeof(*dad), GFP_KERNEL);
+	if (!dad) {
+		rc = -ENOMEM;
+		goto pt_device_access_probe_data_failed;
+	}
+
+	configurations =
+		kzalloc(sizeof(*configurations), GFP_KERNEL);
+	if (!configurations) {
+		rc = -ENOMEM;
+		goto pt_device_access_probe_configs_failed;
+	}
+	dad->configs = configurations;
+
+	cmcp_info = kzalloc(sizeof(*cmcp_info), GFP_KERNEL);
+	if (!cmcp_info) {
+		rc = -ENOMEM;
+		goto pt_device_access_probe_cmcp_info_failed;
+	}
+	dad->cmcp_info = cmcp_info;
+
+	cmcp_info->tx_num = tx_num;
+	cmcp_info->rx_num = rx_num;
+	cmcp_info->btn_num = btn_num;
+
+	result = kzalloc(sizeof(*result), GFP_KERNEL);
+	if (!result) {
+		rc = -ENOMEM;
+		goto pt_device_access_probe_result_failed;
+	}
+	dad->result = result;
+
+	test_case_field_array =
+		kzalloc(sizeof(*test_case_field_array) * MAX_CASE_NUM,
+		GFP_KERNEL);
+	if (!test_case_field_array) {
+		rc = -ENOMEM;
+		goto pt_device_access_probe_field_array_failed;
+	}
+
+	test_case_search_array =
+		kzalloc(sizeof(*test_case_search_array) * MAX_CASE_NUM,
+		GFP_KERNEL);
+	if (!test_case_search_array) {
+		rc = -ENOMEM;
+		goto pt_device_access_probe_search_array_failed;
+	}
+
+	cmcp_info->gd_sensor_col = (struct gd_sensor *)
+		kzalloc(tx_num * sizeof(struct gd_sensor), GFP_KERNEL);
+	if (cmcp_info->gd_sensor_col == NULL)
+		goto pt_device_access_probe_gd_sensor_col_failed;
+
+	cmcp_info->gd_sensor_row = (struct gd_sensor *)
+		kzalloc(rx_num * sizeof(struct gd_sensor), GFP_KERNEL);
+	if (cmcp_info->gd_sensor_row == NULL)
+		goto pt_device_access_probe_gd_sensor_row_failed;
+
+	cmcp_info->cm_data_panel =
+		kzalloc((tx_num * rx_num + 1) * sizeof(int32_t), GFP_KERNEL);
+	if (cmcp_info->cm_data_panel == NULL)
+		goto pt_device_access_probe_cm_data_panel_failed;
+
+	cmcp_info->cp_tx_data_panel =
+		kzalloc(tx_num * sizeof(int32_t), GFP_KERNEL);
+	if (cmcp_info->cp_tx_data_panel == NULL)
+		goto pt_device_access_probe_cp_tx_data_panel_failed;
+
+	cmcp_info->cp_tx_cal_data_panel =
+		kzalloc(tx_num * sizeof(int32_t), GFP_KERNEL);
+	if (cmcp_info->cp_tx_cal_data_panel == NULL)
+		goto pt_device_access_probe_cp_tx_cal_data_panel_failed;
+
+	cmcp_info->cp_rx_data_panel =
+		kzalloc(rx_num * sizeof(int32_t), GFP_KERNEL);
+	if (cmcp_info->cp_rx_data_panel == NULL)
+		goto pt_device_access_probe_cp_rx_data_panel_failed;
+
+	cmcp_info->cp_rx_cal_data_panel =
+		kzalloc(rx_num * sizeof(int32_t), GFP_KERNEL);
+	if (cmcp_info->cp_rx_cal_data_panel == NULL)
+		goto pt_device_access_probe_cp_rx_cal_data_panel_failed;
+
+	cmcp_info->cm_btn_data = kcalloc(btn_num, sizeof(int32_t), GFP_KERNEL);
+	if (cmcp_info->cm_btn_data == NULL)
+		goto pt_device_access_probe_cm_btn_data_failed;
+
+	cmcp_info->cp_btn_data = kcalloc(btn_num, sizeof(int32_t), GFP_KERNEL);
+	if (cmcp_info->cp_btn_data == NULL)
+		goto pt_device_access_probe_cp_btn_data_failed;
+
+	cmcp_info->cm_sensor_column_delta =
+		 kzalloc(rx_num * tx_num * sizeof(int32_t), GFP_KERNEL);
+	if (cmcp_info->cm_sensor_column_delta == NULL)
+		goto pt_device_access_probe_cm_sensor_column_delta_failed;
+
+	cmcp_info->cm_sensor_row_delta =
+		 kzalloc(tx_num * rx_num  * sizeof(int32_t), GFP_KERNEL);
+	if (cmcp_info->cm_sensor_row_delta == NULL)
+		goto pt_device_access_probe_cm_sensor_row_delta_failed;
+
+	mutex_init(&dad->sysfs_lock);
+	mutex_init(&dad->cmcp_threshold_lock);
+	dad->dev = dev;
+#ifdef TTHE_TUNER_SUPPORT
+	mutex_init(&dad->debugfs_lock);
+	dad->heatmap.num_element = 200;
+#endif
+	*data = dad;
+
+	dad->test_field_array = test_case_field_array;
+	dad->test_search_array = test_case_search_array;
+	dad->test_executed = 0;
+
+	dad->cal_ext_data.mode = PT_CAL_EXT_MODE_UNDEFINED;
+	dad->panel_scan_retrieve_id = 0;
+	dad->panel_scan_type_id = 0;
+
+	INIT_WORK(&dad->cmcp_threshold_update, pt_parse_cmcp_threshold_builtin);
+
+	/* get sysinfo */
+	dad->si = cmd->request_sysinfo(dev);
+	if (dad->si) {
+		rc = pt_setup_sysfs(dev);
+		if (rc)
+			goto pt_device_access_setup_sysfs_failed;
+	} else {
+		pt_debug(dev, DL_ERROR,
+				"%s: Fail get sysinfo pointer from core p=%p\n",
+				__func__, dad->si);
+		cmd->subscribe_attention(dev, PT_ATTEN_STARTUP,
+			PT_DEVICE_ACCESS_NAME,
+			pt_setup_cmcp_attention, 0);
+	}
+
+	schedule_work(&dad->cmcp_threshold_update);
+
+	return 0;
+
+pt_device_access_setup_sysfs_failed:
+	kfree(cmcp_info->cm_sensor_row_delta);
+pt_device_access_probe_cm_sensor_row_delta_failed:
+	kfree(cmcp_info->cm_sensor_column_delta);
+pt_device_access_probe_cm_sensor_column_delta_failed:
+	kfree(cmcp_info->cp_btn_data);
+pt_device_access_probe_cp_btn_data_failed:
+	kfree(cmcp_info->cm_btn_data);
+pt_device_access_probe_cm_btn_data_failed:
+	kfree(cmcp_info->cp_rx_cal_data_panel);
+pt_device_access_probe_cp_rx_cal_data_panel_failed:
+	kfree(cmcp_info->cp_rx_data_panel);
+pt_device_access_probe_cp_rx_data_panel_failed:
+	kfree(cmcp_info->cp_tx_cal_data_panel);
+pt_device_access_probe_cp_tx_cal_data_panel_failed:
+	kfree(cmcp_info->cp_tx_data_panel);
+pt_device_access_probe_cp_tx_data_panel_failed:
+	kfree(cmcp_info->cm_data_panel);
+pt_device_access_probe_cm_data_panel_failed:
+	kfree(cmcp_info->gd_sensor_row);
+pt_device_access_probe_gd_sensor_row_failed:
+	kfree(cmcp_info->gd_sensor_col);
+pt_device_access_probe_gd_sensor_col_failed:
+	kfree(test_case_search_array);
+pt_device_access_probe_search_array_failed:
+	kfree(test_case_field_array);
+pt_device_access_probe_field_array_failed:
+	kfree(result);
+pt_device_access_probe_result_failed:
+	kfree(cmcp_info);
+pt_device_access_probe_cmcp_info_failed:
+	kfree(configurations);
+pt_device_access_probe_configs_failed:
+	kfree(dad);
+pt_device_access_probe_data_failed:
+	pt_debug(dev, DL_ERROR, "%s failed.\n", __func__);
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_device_access_release
+ *
+ * SUMMARY: Remove function for device_access module that does following
+ *  cleanup:
+ *	- Unsubscibe all registered attention tasks
+ *	- Removes all created sysfs nodes
+ *	- Frees all pointers
+ *
+ * PARAMETERS:
+ *	*dev   - pointer to device structure
+ *	*data  - pointer to the device_access data
+ ******************************************************************************/
+static void pt_device_access_release(struct device *dev, void *data)
+{
+	struct pt_device_access_data *dad = data;
+
+	if (dad->sysfs_nodes_created) {
+		debugfs_remove(dad->cmcp_results_debugfs);
+		debugfs_remove_recursive(dad->base_dentry);
+#ifdef TTHE_TUNER_SUPPORT
+		debugfs_remove(dad->tthe_get_panel_data_debugfs);
+#endif
+		device_remove_file(dev, &dev_attr_cmcp_test);
+		device_remove_file(dev, &dev_attr_cmcp_threshold_loading);
+		device_remove_bin_file(dev, &bin_attr_cmcp_threshold_data);
+		kfree(dad->cmcp_threshold_data);
+	} else {
+		cmd->unsubscribe_attention(dev, PT_ATTEN_STARTUP,
+			PT_DEVICE_ACCESS_NAME,
+			pt_setup_cmcp_attention, 0);
+	}
+
+	kfree(dad->test_search_array);
+	kfree(dad->test_field_array);
+	kfree(dad->configs);
+	pt_free_cmcp_buf(dad->cmcp_info);
+	kfree(dad->cmcp_info);
+	kfree(dad->result);
+	kfree(dad);
+}
+
+static struct pt_module device_access_module = {
+	.name = PT_DEVICE_ACCESS_NAME,
+	.probe = pt_device_access_probe,
+	.release = pt_device_access_release,
+};
+
+/*******************************************************************************
+ * FUNCTION: pt_device_access_init
+ *
+ * SUMMARY: Initialize function for device access module which to register
+ * device_access_module into TTDL module list.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ ******************************************************************************/
+static int __init pt_device_access_init(void)
+{
+	int rc;
+
+	cmd = pt_get_commands();
+	if (!cmd)
+		return -EINVAL;
+
+	rc = pt_register_module(&device_access_module);
+	if (rc) {
+		pr_err("%s: Error, failed registering module\n",
+			__func__);
+			return rc;
+	}
+
+	pr_info("%s: Parade TTSP Device Access Driver (Built %s) rc = %d\n",
+		 __func__, PT_DRIVER_VERSION, rc);
+	return 0;
+}
+module_init(pt_device_access_init);
+
+/*******************************************************************************
+ * FUNCTION: pt_device_access_exit
+ *
+ * SUMMARY: Exit function for device access module which to unregister
+ * device_access_module from TTDL module list.
+ *
+ ******************************************************************************/
+static void __exit pt_device_access_exit(void)
+{
+	pt_unregister_module(&device_access_module);
+}
+module_exit(pt_device_access_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Parade TrueTouch(R) Standard Product Device Access Driver");
+MODULE_AUTHOR("Parade Technologies <[email protected]>");

+ 1128 - 0
qcom/opensource/touch-drivers/pt/pt_devtree.c

@@ -0,0 +1,1128 @@
+/*
+ * pt_devtree.c
+ * Parade TrueTouch(TM) Standard Product Device Tree Support Module.
+ * For use with Parade touchscreen controllers.
+ * Supported parts include:
+ * TMA5XX
+ * TMA448
+ * TMA445A
+ * TT21XXX
+ * TT31XXX
+ * TT4XXXX
+ * TT7XXX
+ * TC3XXX
+ *
+ * Copyright (C) 2015-2020 Parade Technologies
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * Contact Parade Technologies at www.paradetech.com <[email protected]>
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/of_device.h>
+#include <linux/slab.h>
+#include "pt_platform.h"
+#include "pt_regs.h"
+
+#define MAX_NAME_LENGTH		64
+
+static bool is_create_and_get_pdata;
+
+enum pt_device_type {
+	DEVICE_MT,
+	DEVICE_BTN,
+	DEVICE_PROXIMITY,
+	DEVICE_TYPE_MAX,
+};
+
+struct pt_device_pdata_func {
+	void * (*create_and_get_pdata)(struct device_node *dn);
+	void (*free_pdata)(void *ptr);
+};
+
+struct pt_pdata_ptr {
+	void **pdata;
+};
+
+#ifdef ENABLE_VIRTUAL_KEYS
+static struct kobject *board_properties_kobj;
+
+struct pt_virtual_keys {
+	struct kobj_attribute kobj_attr;
+	u16 *data;
+	int size;
+};
+#endif
+
+struct pt_extended_mt_platform_data {
+	struct pt_mt_platform_data pdata;
+#ifdef ENABLE_VIRTUAL_KEYS
+	struct pt_virtual_keys vkeys;
+#endif
+};
+
+/*******************************************************************************
+ * FUNCTION: get_inp_dev_name
+ *
+ * SUMMARY: Get the name of input device from dts.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *   *dev_node      - pointer to device_node structure
+ *  **inp_dev_name  - double pointer to the name of input device
+ ******************************************************************************/
+static inline int get_inp_dev_name(struct device_node *dev_node,
+		const char **inp_dev_name)
+{
+	return of_property_read_string(dev_node, "parade,inp_dev_name",
+			inp_dev_name);
+}
+
+/*******************************************************************************
+ * FUNCTION: create_and_get_u16_array
+ *
+ * SUMMARY: Create and get u16 array from dts.
+ *
+ * RETURN:
+ *   success: the pointer of the created array
+ *   fail   : error code with type of error pointer
+ *
+ * PARAMETERS:
+ *   *dev_node - pointer to device_node structure
+ *   *name     - name of device node
+ *    size     - number of u16 array elements
+ ******************************************************************************/
+static s16 *create_and_get_u16_array(struct device_node *dev_node,
+		const char *name, int *size)
+{
+	const __be32 *values;
+	s16 *val_array;
+	int len;
+	int sz;
+	int rc;
+	int i;
+
+	values = of_get_property(dev_node, name, &len);
+	if (values == NULL)
+		return NULL;
+
+	sz = len / sizeof(u32);
+	pr_debug("%s: %s size:%d\n", __func__, name, sz);
+
+	val_array = kcalloc(sz, sizeof(s16), GFP_KERNEL);
+	if (!val_array) {
+		rc = -ENOMEM;
+		goto fail;
+	}
+
+	for (i = 0; i < sz; i++)
+		val_array[i] = (s16)be32_to_cpup(values++);
+
+	*size = sz;
+
+	return val_array;
+
+fail:
+	return ERR_PTR(rc);
+}
+
+/*******************************************************************************
+ * FUNCTION: create_and_get_touch_framework
+ *
+ * SUMMARY: Create and get touch framework structure from dts.
+ *
+ * RETURN:
+ *   success: the pointer of the touch framework data
+ *   fail   : error code with type of error pointer
+ *
+ * PARAMETERS:
+ *   *dev_node - pointer to device_node structure
+ ******************************************************************************/
+static struct touch_framework *create_and_get_touch_framework(
+		struct device_node *dev_node)
+{
+	struct touch_framework *frmwrk;
+	s16 *abs;
+	int size;
+	int rc;
+
+	abs = create_and_get_u16_array(dev_node, "parade,abs", &size);
+	if (IS_ERR_OR_NULL(abs))
+		return (void *)abs;
+
+	/* Check for valid abs size */
+	if (size % PT_NUM_ABS_SET) {
+		rc = -EINVAL;
+		goto fail_free_abs;
+	}
+
+	frmwrk = kzalloc(sizeof(*frmwrk), GFP_KERNEL);
+	if (!frmwrk) {
+		rc = -ENOMEM;
+		goto fail_free_abs;
+	}
+
+	frmwrk->abs = abs;
+	frmwrk->size = size;
+
+	return frmwrk;
+
+fail_free_abs:
+	kfree(abs);
+
+	return ERR_PTR(rc);
+}
+
+/*******************************************************************************
+ * FUNCTION: free_touch_framework
+ *
+ * SUMMARY: Free all the pointer of touch framework structure.
+ *
+ * PARAMETERS:
+ *   *frmwrk - pointer to touch framework structure
+ ******************************************************************************/
+static void free_touch_framework(struct touch_framework *frmwrk)
+{
+	kfree(frmwrk->abs);
+	kfree(frmwrk);
+}
+
+#ifdef ENABLE_VIRTUAL_KEYS
+#define VIRTUAL_KEY_ELEMENT_SIZE	5
+/*******************************************************************************
+ * FUNCTION: virtual_keys_show
+ *
+ * SUMMARY: Show method for the board_properties sysfs node that will show the
+ *  information for all virtual keys
+ *
+ * RETURN: size of data written to sysfs node
+ *
+ * PARAMETERS:
+ *	*kobj  - pointer to kobject structure
+ *	*attr  - pointer to kobject attributes
+ *	*buf   - pointer to print output buffer
+ ******************************************************************************/
+static ssize_t virtual_keys_show(struct kobject *kobj,
+		struct kobj_attribute *attr, char *buf)
+{
+	struct pt_virtual_keys *vkeys = container_of(attr,
+		struct pt_virtual_keys, kobj_attr);
+	u16 *data = vkeys->data;
+	int size = vkeys->size;
+	int index;
+	int i;
+
+	index = 0;
+	for (i = 0; i < size; i += VIRTUAL_KEY_ELEMENT_SIZE)
+		index += scnprintf(buf + index, PT_MAX_PRBUF_SIZE - index,
+			"0x01:%d:%d:%d:%d:%d\n",
+			data[i], data[i+1], data[i+2], data[i+3], data[i+4]);
+
+	return index;
+}
+
+/*******************************************************************************
+ * FUNCTION: setup_virtual_keys
+ *
+ * SUMMARY: Create virtual key data array from dts and set up dynamic sysfs for
+ *  board_properties node.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *   *dev_node      - pointer to device_node structure
+ *  **inp_dev_name  - double pointer to the name of input device
+ *   *vkeys         - pointer to virtual key structure
+ ******************************************************************************/
+static int setup_virtual_keys(struct device_node *dev_node,
+		const char *inp_dev_name, struct pt_virtual_keys *vkeys)
+{
+	char *name;
+	u16 *data;
+	int size;
+	int rc;
+
+	data = create_and_get_u16_array(dev_node, "parade,virtual_keys", &size);
+	if (data == NULL)
+		return 0;
+	else if (IS_ERR(data)) {
+		rc = PTR_ERR(data);
+		goto fail;
+	}
+
+	/* Check for valid virtual keys size */
+	if (size % VIRTUAL_KEY_ELEMENT_SIZE) {
+		rc = -EINVAL;
+		goto fail_free_data;
+	}
+
+	name = kzalloc(MAX_NAME_LENGTH, GFP_KERNEL);
+	if (!name) {
+		rc = -ENOMEM;
+		goto fail_free_data;
+	}
+
+	snprintf(name, MAX_NAME_LENGTH, "virtualkeys.%s", inp_dev_name);
+
+	vkeys->data = data;
+	vkeys->size = size;
+
+	/* TODO: Instantiate in board file and export it */
+	if (board_properties_kobj == NULL)
+		board_properties_kobj =
+			kobject_create_and_add("board_properties", NULL);
+	if (board_properties_kobj == NULL) {
+		pr_err("%s: Cannot get board_properties kobject!\n", __func__);
+		rc = -EINVAL;
+		goto fail_free_name;
+	}
+
+	/* Initialize dynamic SysFs attribute */
+	sysfs_attr_init(&vkeys->kobj_attr.attr);
+	vkeys->kobj_attr.attr.name = name;
+	vkeys->kobj_attr.attr.mode = 0444;
+	vkeys->kobj_attr.show = virtual_keys_show;
+
+	rc = sysfs_create_file(board_properties_kobj, &vkeys->kobj_attr.attr);
+	if (rc)
+		goto fail_del_kobj;
+
+	return 0;
+
+fail_del_kobj:
+	kobject_del(board_properties_kobj);
+fail_free_name:
+	kfree(name);
+	vkeys->kobj_attr.attr.name = NULL;
+fail_free_data:
+	kfree(data);
+	vkeys->data = NULL;
+fail:
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: free_virtual_keys
+ *
+ * SUMMARY: Remove board_properties node and free all pointers.
+ *
+ * PARAMETERS:
+ *   *vkeys  - pointer to virtual key structure
+ ******************************************************************************/
+static void free_virtual_keys(struct pt_virtual_keys *vkeys)
+{
+	if (board_properties_kobj)
+		sysfs_remove_file(board_properties_kobj,
+			&vkeys->kobj_attr.attr);
+
+
+	kobject_del(board_properties_kobj);
+	board_properties_kobj = NULL;
+
+	kfree(vkeys->data);
+	kfree(vkeys->kobj_attr.attr.name);
+}
+#endif
+
+/*******************************************************************************
+ * FUNCTION: create_and_get_mt_pdata
+ *
+ * SUMMARY: Create and get touch platform data from dts.Touch framework and
+ *  virtual keys are set up in this function.
+ *
+ * RETURN:
+ *   success: the pointer of the platform data
+ *   fail   : error code with type of error pointer
+ *
+ * PARAMETERS:
+ *   *dev_node - pointer to device_node structure
+ ******************************************************************************/
+static void *create_and_get_mt_pdata(struct device_node *dev_node)
+{
+	struct pt_extended_mt_platform_data *ext_pdata;
+	struct pt_mt_platform_data *pdata;
+	u32 value;
+	int rc;
+
+	ext_pdata = kzalloc(sizeof(*ext_pdata), GFP_KERNEL);
+	if (!ext_pdata) {
+		rc = -ENOMEM;
+		goto fail;
+	}
+
+	pdata = &ext_pdata->pdata;
+
+	rc = get_inp_dev_name(dev_node, &pdata->inp_dev_name);
+	if (rc)
+		goto fail_free_pdata;
+
+	/* Optional fields */
+	rc = of_property_read_u32(dev_node, "parade,flags", &value);
+	if (!rc)
+		pdata->flags = value;
+
+	rc = of_property_read_u32(dev_node, "parade,vkeys_x", &value);
+	if (!rc)
+		pdata->vkeys_x = value;
+
+	rc = of_property_read_u32(dev_node, "parade,vkeys_y", &value);
+	if (!rc)
+		pdata->vkeys_y = value;
+
+	/* Required fields */
+	pdata->frmwrk = create_and_get_touch_framework(dev_node);
+	if (pdata->frmwrk == NULL) {
+		rc = -EINVAL;
+		goto fail_free_pdata;
+	} else if (IS_ERR(pdata->frmwrk)) {
+		rc = PTR_ERR(pdata->frmwrk);
+		goto fail_free_pdata;
+	}
+#ifdef ENABLE_VIRTUAL_KEYS
+	rc = setup_virtual_keys(dev_node, pdata->inp_dev_name,
+			&ext_pdata->vkeys);
+	if (rc) {
+		pr_err("%s: Cannot setup virtual keys!\n", __func__);
+		goto fail_free_pdata;
+	}
+#endif
+	return pdata;
+
+fail_free_pdata:
+	kfree(ext_pdata);
+fail:
+	return ERR_PTR(rc);
+}
+
+/*******************************************************************************
+ * FUNCTION: free_mt_pdata
+ *
+ * SUMMARY: Free all pointers that include touch framework, virtual keys and
+ *  touch data.
+ *
+ * PARAMETERS:
+ *   *vkeys  - pointer to virtual key structure
+ ******************************************************************************/
+static void free_mt_pdata(void *pdata)
+{
+	struct pt_mt_platform_data *mt_pdata =
+		(struct pt_mt_platform_data *)pdata;
+	struct pt_extended_mt_platform_data *ext_mt_pdata =
+		container_of(mt_pdata,
+			struct pt_extended_mt_platform_data, pdata);
+
+	free_touch_framework(mt_pdata->frmwrk);
+#ifdef ENABLE_VIRTUAL_KEYS
+	free_virtual_keys(&ext_mt_pdata->vkeys);
+#endif
+	kfree(ext_mt_pdata);
+}
+
+/*******************************************************************************
+ * FUNCTION: create_and_get_btn_pdata
+ *
+ * SUMMARY: Create and get button platform data from dts.
+ *
+ * RETURN:
+ *   success: the pointer of the platform data
+ *   fail   : error code with type of error pointer
+ *
+ * PARAMETERS:
+ *   *dev_node - pointer to device_node structure
+ ******************************************************************************/
+static void *create_and_get_btn_pdata(struct device_node *dev_node)
+{
+	struct pt_btn_platform_data *pdata;
+	int rc;
+
+	pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
+	if (!pdata) {
+		rc = -ENOMEM;
+		goto fail;
+	}
+
+	rc = get_inp_dev_name(dev_node, &pdata->inp_dev_name);
+	if (rc)
+		goto fail_free_pdata;
+
+	return pdata;
+
+fail_free_pdata:
+	kfree(pdata);
+fail:
+	return ERR_PTR(rc);
+}
+
+/*******************************************************************************
+ * FUNCTION: free_btn_pdata
+ *
+ * SUMMARY: Free all pointers for button platform data.
+ *
+ * PARAMETERS:
+ *   *vkeys  - pointer to virtual key structure
+ ******************************************************************************/
+static void free_btn_pdata(void *pdata)
+{
+	struct pt_btn_platform_data *btn_pdata =
+		(struct pt_btn_platform_data *)pdata;
+
+	kfree(btn_pdata);
+}
+
+/*******************************************************************************
+ * FUNCTION: create_and_get_proximity_pdata
+ *
+ * SUMMARY: Create and get proximity platform data from dts.
+ *
+ * RETURN:
+ *   success: the pointer of the proximity platform data
+ *   fail   : error code with type of error pointer
+ *
+ * PARAMETERS:
+ *   *dev_node - pointer to device_node structure
+ ******************************************************************************/
+static void *create_and_get_proximity_pdata(struct device_node *dev_node)
+{
+	struct pt_proximity_platform_data *pdata;
+	int rc;
+
+	pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
+	if (!pdata) {
+		rc = -ENOMEM;
+		goto fail;
+	}
+
+	rc = get_inp_dev_name(dev_node, &pdata->inp_dev_name);
+	if (rc)
+		goto fail_free_pdata;
+
+	pdata->frmwrk = create_and_get_touch_framework(dev_node);
+	if (pdata->frmwrk == NULL) {
+		rc = -EINVAL;
+		goto fail_free_pdata;
+	} else if (IS_ERR(pdata->frmwrk)) {
+		rc = PTR_ERR(pdata->frmwrk);
+		goto fail_free_pdata;
+	}
+
+	return pdata;
+
+fail_free_pdata:
+	kfree(pdata);
+fail:
+	return ERR_PTR(rc);
+}
+
+/*******************************************************************************
+ * FUNCTION: free_proximity_pdata
+ *
+ * SUMMARY: Free all pointers for proximity platform data.
+ *
+ * PARAMETERS:
+ *   *vkeys  - pointer to virtual key structure
+ ******************************************************************************/
+static void free_proximity_pdata(void *pdata)
+{
+	struct pt_proximity_platform_data *proximity_pdata =
+		(struct pt_proximity_platform_data *)pdata;
+
+	free_touch_framework(proximity_pdata->frmwrk);
+
+	kfree(proximity_pdata);
+}
+
+static struct pt_device_pdata_func device_pdata_funcs[DEVICE_TYPE_MAX] = {
+	[DEVICE_MT] = {
+		.create_and_get_pdata = create_and_get_mt_pdata,
+		.free_pdata = free_mt_pdata,
+	},
+	[DEVICE_BTN] = {
+		.create_and_get_pdata = create_and_get_btn_pdata,
+		.free_pdata = free_btn_pdata,
+	},
+	[DEVICE_PROXIMITY] = {
+		.create_and_get_pdata = create_and_get_proximity_pdata,
+		.free_pdata = free_proximity_pdata,
+	},
+};
+
+static struct pt_pdata_ptr pdata_ptr[DEVICE_TYPE_MAX];
+
+static const char *device_names[DEVICE_TYPE_MAX] = {
+	[DEVICE_MT] = "parade,mt",
+	[DEVICE_BTN] = "parade,btn",
+	[DEVICE_PROXIMITY] = "parade,proximity",
+};
+
+/*******************************************************************************
+ * FUNCTION: set_pdata_ptr
+ *
+ * SUMMARY: Set platform data pointer for touch, button, proximity module.
+ *
+ * PARAMETERS:
+ *   *pdata  - pointer to platform data structure
+ ******************************************************************************/
+static void set_pdata_ptr(struct pt_platform_data *pdata)
+{
+	pdata_ptr[DEVICE_MT].pdata = (void **)&pdata->mt_pdata;
+	pdata_ptr[DEVICE_BTN].pdata = (void **)&pdata->btn_pdata;
+	pdata_ptr[DEVICE_PROXIMITY].pdata = (void **)&pdata->prox_pdata;
+}
+
+/*******************************************************************************
+ * FUNCTION: get_device_type
+ *
+ * SUMMARY: Determine the device type[mt,btn,proximity] from dts.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *   *dev_node  - pointer to device_node structure
+ *   *type      - pointer to store the device type
+ ******************************************************************************/
+static int get_device_type(struct device_node *dev_node,
+		enum pt_device_type *type)
+{
+	const char *name;
+	enum pt_device_type t;
+	int rc;
+
+	rc = of_property_read_string(dev_node, "name", &name);
+	if (rc)
+		return rc;
+
+	for (t = 0; t < DEVICE_TYPE_MAX; t++)
+		if (!strncmp(name, device_names[t], MAX_NAME_LENGTH)) {
+			*type = t;
+			return 0;
+		}
+
+	return -EINVAL;
+}
+
+/*******************************************************************************
+ * FUNCTION: create_and_get_device_pdata
+ *
+ * SUMMARY: Create platform data for mt, btn, proximity module.
+ *
+ * RETURN:
+ *   success: the pointer of the platform data
+ *   fail   : error code with type of error pointer
+ *
+ * PARAMETERS:
+ *   *dev_node - pointer to device_node structure
+ *    type     - determine the device type
+ ******************************************************************************/
+static inline void *create_and_get_device_pdata(struct device_node *dev_node,
+		enum pt_device_type type)
+{
+	return device_pdata_funcs[type].create_and_get_pdata(dev_node);
+}
+
+/*******************************************************************************
+ * FUNCTION: free_device_pdata
+ *
+ * SUMMARY: Free platform data for mt, btn, proximity module.
+ *
+ * PARAMETERS:
+ *    type - determine the device type
+ ******************************************************************************/
+static inline void free_device_pdata(enum pt_device_type type)
+{
+	device_pdata_funcs[type].free_pdata(*pdata_ptr[type].pdata);
+}
+
+/*******************************************************************************
+ * FUNCTION: create_and_get_touch_setting
+ *
+ * SUMMARY: Create and get touch settings from dts.
+ *
+ * RETURN:
+ *   success: the pointer of touch settings
+ *   fail   : error code with type of error pointer
+ *
+ * PARAMETERS:
+ *   *core_node - pointer to device_node structure
+ *    name      - name of touch setting
+ ******************************************************************************/
+static struct touch_settings *create_and_get_touch_setting(
+		struct device_node *core_node, const char *name)
+{
+	struct touch_settings *setting;
+	char *tag_name;
+	u32 tag_value;
+	u16 *data;
+	int size;
+	int rc;
+
+	data = create_and_get_u16_array(core_node, name, &size);
+	if (IS_ERR_OR_NULL(data))
+		return (void *)data;
+
+	pr_debug("%s: Touch setting:'%s' size:%d\n", __func__, name, size);
+
+	setting = kzalloc(sizeof(*setting), GFP_KERNEL);
+	if (!setting) {
+		rc = -ENOMEM;
+		goto fail_free_data;
+	}
+
+	setting->data = (u8 *)data;
+	setting->size = size;
+
+	tag_name = kzalloc(MAX_NAME_LENGTH, GFP_KERNEL);
+	if (!tag_name) {
+		rc = -ENOMEM;
+		goto fail_free_setting;
+	}
+
+	snprintf(tag_name, MAX_NAME_LENGTH, "%s-tag", name);
+
+	rc = of_property_read_u32(core_node, tag_name, &tag_value);
+	if (!rc)
+		setting->tag = tag_value;
+
+	kfree(tag_name);
+
+	return setting;
+
+fail_free_setting:
+	kfree(setting);
+fail_free_data:
+	kfree(data);
+
+	return ERR_PTR(rc);
+}
+
+/*******************************************************************************
+ * FUNCTION: free_touch_setting
+ *
+ * SUMMARY: Free touch setting data.
+ *
+ * PARAMETERS:
+ *    setting - pointer to touch setting
+ ******************************************************************************/
+static void free_touch_setting(struct touch_settings *setting)
+{
+	if (setting) {
+		kfree(setting->data);
+		kfree(setting);
+	}
+}
+
+static char *touch_setting_names[PT_IC_GRPNUM_NUM] = {
+	NULL,			/* PT_IC_GRPNUM_RESERVED */
+	"parade,cmd_regs",		/* PT_IC_GRPNUM_CMD_REGS */
+	"parade,tch_rep",		/* PT_IC_GRPNUM_TCH_REP */
+	"parade,data_rec",		/* PT_IC_GRPNUM_DATA_REC */
+	"parade,test_rec",		/* PT_IC_GRPNUM_TEST_REC */
+	"parade,pcfg_rec",		/* PT_IC_GRPNUM_PCFG_REC */
+	"parade,tch_parm_val",	/* PT_IC_GRPNUM_TCH_PARM_VAL */
+	"parade,tch_parm_size",	/* PT_IC_GRPNUM_TCH_PARM_SIZE */
+	NULL,			/* PT_IC_GRPNUM_RESERVED1 */
+	NULL,			/* PT_IC_GRPNUM_RESERVED2 */
+	"parade,opcfg_rec",		/* PT_IC_GRPNUM_OPCFG_REC */
+	"parade,ddata_rec",		/* PT_IC_GRPNUM_DDATA_REC */
+	"parade,mdata_rec",		/* PT_IC_GRPNUM_MDATA_REC */
+	"parade,test_regs",		/* PT_IC_GRPNUM_TEST_REGS */
+	"parade,btn_keys",		/* PT_IC_GRPNUM_BTN_KEYS */
+	NULL,			/* PT_IC_GRPNUM_TTHE_REGS */
+};
+
+/*******************************************************************************
+ * FUNCTION: pt_check_dsi_panel_dt
+ *
+ * SUMMARY: Get the DSI active panel information from dtsi
+ *
+ * RETURN:
+ *   0 = success
+ *   !0 = fail
+ *
+ * PARAMETERS:
+ * np            -  pointer to device_node structure
+ * active_panel  -  name of active DSI panel
+ ******************************************************************************/
+
+
+static int pt_check_dsi_panel_dt(struct device_node *np, struct drm_panel **active_panel)
+{
+	int i;
+	int count;
+	struct device_node *node;
+	struct drm_panel *panel;
+
+	count = of_count_phandle_with_args(np, "panel", NULL);
+	pr_info("%s: Active panel count: %d\n", __func__, count);
+	if (count <= 0)
+		return 0;
+
+	for (i = 0; i < count; i++) {
+		node = of_parse_phandle(np, "panel", i);
+		if (node != NULL)
+			pr_info("%s: Node handle successfully parsed !\n", __func__);
+		panel = of_drm_find_panel(node);
+		of_node_put(node);
+		if (!IS_ERR(panel)) {
+			pr_info("%s: Active panel selected !\n", __func__);
+			*active_panel = panel;
+			return 0;
+		}
+	}
+	pr_err("%s: Active panel NOT selected !\n", __func__);
+	return PTR_ERR(panel);
+}
+
+/*******************************************************************************
+ * FUNCTION: create_and_get_core_pdata
+ *
+ * SUMMARY: Create and get core module platform data from dts.
+ *
+ * RETURN:
+ *   success: the pointer of core platform data
+ *   fail   : error code with type of error pointer
+ *
+ * PARAMETERS:
+ *   *core_node - pointer to device_node structure
+ ******************************************************************************/
+static struct pt_core_platform_data *create_and_get_core_pdata(
+		struct device_node *core_node)
+{
+	struct pt_core_platform_data *pdata;
+	u32 value;
+	int rc;
+	int i;
+
+	pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
+	if (!pdata) {
+		rc = -ENOMEM;
+		goto fail;
+	}
+
+	/* Required fields */
+	value = of_get_named_gpio_flags(core_node, "parade,irq_gpio", 0, &pdata->irq_gpio_flags);
+	pdata->irq_gpio = value;
+
+	rc = of_property_read_u32(core_node, "parade,hid_desc_register",
+		&value);
+	if (rc)
+		goto fail_free;
+	pdata->hid_desc_register = value;
+
+	/* Optional fields */
+	/* rst_gpio is optional since a platform may use
+	 * power cycling instead of using the XRES pin
+	 */
+	value = of_get_named_gpio_flags(core_node, "parade,rst_gpio", 0, &pdata->rst_gpio_flags);
+	pdata->rst_gpio = value;
+
+	rc = of_property_read_u32(core_node, "parade,ddi_rst_gpio", &value);
+	if (!rc)
+		pdata->ddi_rst_gpio = value;
+
+	rc = of_property_read_u32(core_node, "parade,vddi_gpio", &value);
+	if (!rc)
+		pdata->vddi_gpio = value;
+
+	rc = of_property_read_u32(core_node, "parade,vcc_gpio", &value);
+	if (!rc)
+		pdata->vcc_gpio = value;
+
+	rc = of_property_read_u32(core_node, "parade,avdd_gpio", &value);
+	if (!rc)
+		pdata->avdd_gpio = value;
+
+	rc = of_property_read_u32(core_node, "parade,avee_gpio", &value);
+	if (!rc)
+		pdata->avee_gpio = value;
+
+	rc = of_property_read_u32(core_node, "parade,level_irq_udelay", &value);
+	if (!rc)
+		pdata->level_irq_udelay = value;
+
+	rc = of_property_read_u32(core_node, "parade,vendor_id", &value);
+	if (!rc)
+		pdata->vendor_id = value;
+
+	rc = of_property_read_u32(core_node, "parade,product_id", &value);
+	if (!rc)
+		pdata->product_id = value;
+
+	rc = of_property_read_u32(core_node, "parade,flags", &value);
+	if (!rc)
+		pdata->flags = value;
+
+	rc = of_property_read_u32(core_node, "parade,easy_wakeup_gesture",
+		&value);
+	if (!rc)
+		pdata->easy_wakeup_gesture = (u8)value;
+
+	rc = of_property_read_u32(core_node, "parade,config_dut_generation",
+		&value);
+	if (!rc)
+		pdata->config_dut_generation = (u8)value;
+	else {
+		pr_err("%s: dut_generation is not configured, set default: DUT_PIP2_CAPABLE!\n",
+			__func__);
+		pdata->config_dut_generation = CONFIG_DUT_PIP2_CAPABLE;
+	}
+
+	rc = of_property_read_u32(core_node, "parade,watchdog_force_stop",
+		&value);
+	if (!rc) {
+		if (value)
+			pdata->watchdog_force_stop = true;
+		else
+			pdata->watchdog_force_stop = false;
+	} else {
+		pr_err("%s: watchdog_force_stop is not configured, set default: false!\n",
+			__func__);
+		pdata->watchdog_force_stop = false;
+	}
+
+	rc = of_property_read_u32(core_node, "parade,panel_id_support",
+		&value);
+	if (!rc) {
+		pdata->panel_id_support = (u8)value;
+	} else {
+		pr_err("%s: panel_id_support is not configured, set default: PT_PANEL_ID_DISABLE\n",
+			__func__);
+		pdata->panel_id_support = PT_PANEL_ID_DISABLE;
+	}
+
+	for (i = 0; (unsigned int)i < ARRAY_SIZE(touch_setting_names); i++) {
+		if (touch_setting_names[i] == NULL)
+			continue;
+
+		pdata->sett[i] = create_and_get_touch_setting(core_node,
+				touch_setting_names[i]);
+		if (IS_ERR(pdata->sett[i])) {
+			rc = PTR_ERR(pdata->sett[i]);
+			goto fail_free_sett;
+		} else if (pdata->sett[i] == NULL)
+			pr_debug("%s: No data for setting '%s'\n", __func__,
+				touch_setting_names[i]);
+	}
+
+	pr_debug("%s: irq_gpio:%d rst_gpio:%d\n"
+		"hid_desc_reg:%d level_irq_udelay:%d vendor_id:%d prod_id:%d\n"
+		"flags:%d easy_wakeup_gesture:%d\n", __func__,
+		pdata->irq_gpio, pdata->rst_gpio,
+		pdata->hid_desc_register,
+		pdata->level_irq_udelay, pdata->vendor_id, pdata->product_id,
+		pdata->flags, pdata->easy_wakeup_gesture);
+
+	pdata->xres = pt_xres;
+	pdata->init = pt_init;
+	pdata->power = pt_power;
+	pdata->detect = pt_detect;
+	pdata->irq_stat = pt_irq_stat;
+	pdata->setup_power = pt_setup_power;
+	pdata->setup_irq = pt_setup_irq;
+
+	return pdata;
+
+fail_free_sett:
+	for (i--; i >= 0; i--)
+		free_touch_setting(pdata->sett[i]);
+fail_free:
+	kfree(pdata);
+fail:
+	return ERR_PTR(rc);
+}
+
+/*******************************************************************************
+ * FUNCTION: free_core_pdata
+ *
+ * SUMMARY: Free the core module platform data and touch settings data.
+ *
+ * RETURN:
+ *   success: the pointer of core platform data
+ *   fail   : error code with type of error pointer
+ *
+ * PARAMETERS:
+ *   *core_node - pointer to device_node structure
+ ******************************************************************************/
+static void free_core_pdata(void *pdata)
+{
+	struct pt_core_platform_data *core_pdata = pdata;
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(touch_setting_names); i++)
+		free_touch_setting(core_pdata->sett[i]);
+	kfree(core_pdata);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_devtree_create_and_get_pdata
+ *
+ * SUMMARY: Parse dts and set up platform data for core module, multi-touch(mt)
+ *  module, button(btn) module, proximity module.And Assign platform data
+ *  pointer for loader module.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *adap_dev  - pointer to device structure
+ ******************************************************************************/
+int pt_devtree_create_and_get_pdata(struct device *adap_dev)
+{
+	struct pt_platform_data *pdata;
+	struct device_node *core_node, *dev_node, *dev_node_fail;
+	struct drm_panel *active_panel = NULL;
+	enum pt_device_type type;
+	int count = 0;
+	int rc = 0;
+
+	pr_info("%s: Start of fetch dtsi..\n", __func__);
+
+	if (is_create_and_get_pdata == true)
+		return 0;
+
+	if (!adap_dev->of_node)
+		return 0;
+
+	pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
+	if (!pdata)
+		return -ENOMEM;
+
+	adap_dev->platform_data = pdata;
+	set_pdata_ptr(pdata);
+
+	/* There should be only one core node */
+	for_each_child_of_node(adap_dev->of_node, core_node) {
+		const char *name;
+
+		rc = of_property_read_string(core_node, "name", &name);
+		if (!rc)
+			pr_debug("%s: name:%s\n", __func__, name);
+
+		rc = pt_check_dsi_panel_dt(core_node, &active_panel);
+		if (rc) {
+			pr_err("%s: Panel not selected, rc=%d\n", __func__, rc);
+			if (rc == -EPROBE_DEFER) {
+				pr_err("%s: Probe defer selected, rc=%d\n", __func__, rc);
+				kfree(pdata);
+				return rc;
+			}
+		}
+
+		pdata->core_pdata = create_and_get_core_pdata(core_node);
+		if (IS_ERR(pdata->core_pdata)) {
+			pr_err("%s: Error in fetch dtsi..\n", __func__);
+			rc = PTR_ERR(pdata->core_pdata);
+			break;
+		}
+
+		pr_info("%s: End of fetch dtsi..\n", __func__);
+		pdata->core_pdata->active_panel = active_panel;
+		pr_info("%s: Successful insert of active panel in core data\n",
+				__func__);
+
+		/* Increment reference count */
+		of_node_get(core_node);
+
+		for_each_child_of_node(core_node, dev_node) {
+			count++;
+			rc = get_device_type(dev_node, &type);
+			if (rc)
+				break;
+			*pdata_ptr[type].pdata
+				= create_and_get_device_pdata(dev_node, type);
+			if (IS_ERR(*pdata_ptr[type].pdata))
+				rc = PTR_ERR(*pdata_ptr[type].pdata);
+			if (rc)
+				break;
+			/* Increment reference count */
+			of_node_get(dev_node);
+		}
+
+		if (rc) {
+			free_core_pdata(pdata->core_pdata);
+			of_node_put(core_node);
+			for_each_child_of_node(core_node, dev_node_fail) {
+				if (dev_node == dev_node_fail)
+					break;
+				rc = get_device_type(dev_node, &type);
+				if (rc)
+					break;
+				free_device_pdata(type);
+				of_node_put(dev_node);
+			}
+			break;
+		}
+		pdata->loader_pdata = &_pt_loader_platform_data;
+	}
+	is_create_and_get_pdata = true;
+	pr_debug("%s: %d child node(s) found\n", __func__, count);
+
+	return rc;
+}
+EXPORT_SYMBOL_GPL(pt_devtree_create_and_get_pdata);
+
+/*******************************************************************************
+ * FUNCTION: pt_devtree_clean_pdata
+ *
+ * SUMMARY: Free all platform data for core module, multi-touch(mt) module,
+ *  button(btn) module, proximity module.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *adap_dev  - pointer to device structure
+ ******************************************************************************/
+int pt_devtree_clean_pdata(struct device *adap_dev)
+{
+	struct pt_platform_data *pdata;
+	struct device_node *core_node, *dev_node;
+	enum pt_device_type type;
+	int rc = 0;
+
+	if (is_create_and_get_pdata == false)
+		return 0;
+
+	if (!adap_dev->of_node)
+		return 0;
+
+	pdata = dev_get_platdata(adap_dev);
+	set_pdata_ptr(pdata);
+	free_core_pdata(pdata->core_pdata);
+	for_each_child_of_node(adap_dev->of_node, core_node) {
+		of_node_put(core_node);
+		for_each_child_of_node(core_node, dev_node) {
+			rc = get_device_type(dev_node, &type);
+			if (rc)
+				break;
+			free_device_pdata(type);
+			of_node_put(dev_node);
+		}
+	}
+	is_create_and_get_pdata = false;
+
+	return rc;
+}
+EXPORT_SYMBOL_GPL(pt_devtree_clean_pdata);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Parade TrueTouch(R) Standard Product DeviceTree Driver");
+MODULE_AUTHOR("Parade Technologies <[email protected]>");

+ 328 - 0
qcom/opensource/touch-drivers/pt/pt_i2c.c

@@ -0,0 +1,328 @@
+/*
+ * pt_i2c.c
+ * Parade TrueTouch(TM) Standard Product I2C Module.
+ * For use with Parade touchscreen controllers.
+ * Supported parts include:
+ * TMA5XX
+ * TMA448
+ * TMA445A
+ * TT21XXX
+ * TT31XXX
+ * TT4XXXX
+ * TT7XXX
+ * TC3XXX
+ *
+ * Copyright (C) 2015-2020 Parade Technologies
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * Contact Parade Technologies at www.paradetech.com <[email protected]>
+ */
+
+#include "pt_regs.h"
+
+#include <linux/i2c.h>
+#include <linux/version.h>
+
+#define PT_I2C_DATA_SIZE  (2 * 256)
+#define PT_I2C_NAME "pt_i2c_adapter"
+
+/*******************************************************************************
+ * FUNCTION: pt_i2c_read_default
+ *
+ * SUMMARY: Read a certain number of bytes from the I2C bus
+ *
+ * PARAMETERS:
+ *      *dev  - pointer to Device structure
+ *      *buf  - pointer to buffer where the data read will be stored
+ *       size - size to be read
+ ******************************************************************************/
+static int pt_i2c_read_default(struct device *dev, void *buf, int size)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	int rc;
+	int read_size = size;
+
+	if (!buf || !size || size > PT_I2C_DATA_SIZE)
+		return -EINVAL;
+
+	rc = i2c_master_recv(client, buf, read_size);
+
+	return (rc < 0) ? rc : rc != read_size ? -EIO : 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_i2c_read_default_nosize
+ *
+ * SUMMARY: Read from the I2C bus in two transactions first reading the HID
+ *	packet size (2 bytes) followed by reading the rest of the packet based
+ *	on the size initially read.
+ *	NOTE: The empty buffer 'size' was redefined in PIP version 1.7.
+ *
+ * PARAMETERS:
+ *      *dev  - pointer to Device structure
+ *      *buf  - pointer to buffer where the data read will be stored
+ *       max  - max size that can be read
+ ******************************************************************************/
+static int pt_i2c_read_default_nosize(struct device *dev, u8 *buf, u32 max)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct i2c_msg msgs[2];
+	u8 msg_count = 1;
+	int rc;
+	u32 size;
+
+	if (!buf)
+		return -EINVAL;
+
+	msgs[0].addr = client->addr;
+	msgs[0].flags = (client->flags & I2C_M_TEN) | I2C_M_RD;
+	msgs[0].len = 2;
+	msgs[0].buf = buf;
+	rc = i2c_transfer(client->adapter, msgs, msg_count);
+	if (rc < 0 || rc != msg_count)
+		return (rc < 0) ? rc : -EIO;
+
+	size = get_unaligned_le16(&buf[0]);
+	if (!size || size == 2 || size >= PT_PIP_1P7_EMPTY_BUF)
+		/*
+		 * Before PIP 1.7, empty buffer is 0x0002;
+		 * From PIP 1.7, empty buffer is 0xFFXX
+		 */
+		return 0;
+
+	if (size > max)
+		return -EINVAL;
+
+	rc = i2c_master_recv(client, buf, size);
+	return (rc < 0) ? rc : rc != (int)size ? -EIO : 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_i2c_write_read_specific
+ *
+ * SUMMARY: Write the contents of write_buf to the I2C device and then read
+ *	the response using pt_i2c_read_default_nosize()
+ *
+ * PARAMETERS:
+ *      *dev       - pointer to Device structure
+ *       write_len - length of data buffer write_buf
+ *      *write_buf - pointer to buffer to write
+ *      *read_buf  - pointer to buffer to read response into
+ ******************************************************************************/
+static int pt_i2c_write_read_specific(struct device *dev, u16 write_len,
+		u8 *write_buf, u8 *read_buf)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct i2c_msg msgs[2];
+	u8 msg_count = 1;
+	int rc;
+
+	/* Ensure no packet larger than what the PIP spec allows */
+	if (write_len > PT_MAX_PIP2_MSG_SIZE)
+		return -EINVAL;
+
+	if (!write_buf || !write_len) {
+		if (!write_buf)
+			pt_debug(dev, DL_ERROR,
+				"%s write_buf is NULL", __func__);
+		if (!write_len)
+			pt_debug(dev, DL_ERROR,
+				"%s write_len is NULL", __func__);
+		return -EINVAL;
+	}
+
+	msgs[0].addr = client->addr;
+	msgs[0].flags = client->flags & I2C_M_TEN;
+	msgs[0].len = write_len;
+	msgs[0].buf = write_buf;
+	rc = i2c_transfer(client->adapter, msgs, msg_count);
+
+	if (rc < 0 || rc != msg_count)
+		return (rc < 0) ? rc : -EIO;
+
+	rc = 0;
+
+	if (read_buf) {
+		rc = pt_i2c_read_default_nosize(dev, read_buf,
+				PT_I2C_DATA_SIZE);
+	}
+
+	return rc;
+}
+
+static struct pt_bus_ops pt_i2c_bus_ops = {
+	.bustype = BUS_I2C,
+	.read_default = pt_i2c_read_default,
+	.read_default_nosize = pt_i2c_read_default_nosize,
+	.write_read_specific = pt_i2c_write_read_specific,
+};
+
+#ifdef CONFIG_TOUCHSCREEN_PARADE_DEVICETREE_SUPPORT
+static const struct of_device_id pt_i2c_of_match[] = {
+	{ .compatible = "parade,pt_i2c_adapter", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, pt_i2c_of_match);
+#endif
+
+
+/*******************************************************************************
+ * FUNCTION: pt_i2c_probe
+ *
+ * SUMMARY: Probe functon for the I2C module
+ *
+ * PARAMETERS:
+ *      *client - pointer to i2c client structure
+ *      *i2c_id - pointer to i2c device structure
+ ******************************************************************************/
+static int pt_i2c_probe(struct i2c_client *client,
+	const struct i2c_device_id *i2c_id)
+{
+	struct device *dev = &client->dev;
+#ifdef CONFIG_TOUCHSCREEN_PARADE_DEVICETREE_SUPPORT
+	const struct of_device_id *match;
+#endif
+	int rc;
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+		pt_debug(dev, DL_ERROR, "I2C functionality not Supported\n");
+		return -EIO;
+	}
+#ifdef CONFIG_TOUCHSCREEN_PARADE_DEVICETREE_SUPPORT
+	match = of_match_device(of_match_ptr(pt_i2c_of_match), dev);
+	if (match) {
+		rc = pt_devtree_create_and_get_pdata(dev);
+		if (rc < 0)
+			return rc;
+	}
+#endif
+
+	rc = pt_probe(&pt_i2c_bus_ops, &client->dev, client->irq,
+			  PT_I2C_DATA_SIZE);
+
+#ifdef CONFIG_TOUCHSCREEN_PARADE_DEVICETREE_SUPPORT
+	if (rc && match)
+		pt_devtree_clean_pdata(dev);
+#endif
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_i2c_remove
+ *
+ * SUMMARY: Remove functon for the I2C module
+ *
+ * PARAMETERS:
+ *      *client - pointer to i2c client structure
+ ******************************************************************************/
+static int pt_i2c_remove(struct i2c_client *client)
+{
+#ifdef CONFIG_TOUCHSCREEN_PARADE_DEVICETREE_SUPPORT
+	const struct of_device_id *match;
+#endif
+	struct device *dev = &client->dev;
+	struct pt_core_data *cd = i2c_get_clientdata(client);
+
+	pt_release(cd);
+
+#ifdef CONFIG_TOUCHSCREEN_PARADE_DEVICETREE_SUPPORT
+	match = of_match_device(of_match_ptr(pt_i2c_of_match), dev);
+	if (match)
+		pt_devtree_clean_pdata(dev);
+#endif
+
+	return 0;
+}
+/*******************************************************************************
+ * FUNCTION: pt_i2c_shutdown
+ *
+ * SUMMARY: Shutdown functon for the I2C module
+ *
+ * PARAMETERS:
+ *      *client - pointer to i2c client structure
+ ******************************************************************************/
+void pt_i2c_shutdown(struct i2c_client *client)
+{
+#ifdef CONFIG_TOUCHSCREEN_PARADE_DEVICETREE_SUPPORT
+	const struct of_device_id *match;
+#endif
+	struct device *dev = &client->dev;
+	struct pt_core_data *cd = i2c_get_clientdata(client);
+
+	pt_release(cd);
+
+#ifdef CONFIG_TOUCHSCREEN_PARADE_DEVICETREE_SUPPORT
+	match = of_match_device(of_match_ptr(pt_i2c_of_match), dev);
+	if (match)
+		pt_devtree_clean_pdata(dev);
+#endif
+
+}
+
+static const struct i2c_device_id pt_i2c_id[] = {
+	{ PT_I2C_NAME, 0, },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, pt_i2c_id);
+
+static struct i2c_driver pt_i2c_driver = {
+	.driver = {
+		.name = PT_I2C_NAME,
+		.owner = THIS_MODULE,
+		.pm = &pt_pm_ops,
+#ifdef CONFIG_TOUCHSCREEN_PARADE_DEVICETREE_SUPPORT
+		.of_match_table = pt_i2c_of_match,
+#endif
+	},
+	.probe = pt_i2c_probe,
+	.remove = pt_i2c_remove,
+	.shutdown = pt_i2c_shutdown,
+	.id_table = pt_i2c_id,
+};
+
+#if (KERNEL_VERSION(3, 3, 0) <= LINUX_VERSION_CODE)
+module_i2c_driver(pt_i2c_driver);
+#else
+/*******************************************************************************
+ * FUNCTION: pt_i2c_init
+ *
+ * SUMMARY: Initialize function to register i2c module to kernel.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ ******************************************************************************/
+static int __init pt_i2c_init(void)
+{
+	int rc = i2c_add_driver(&pt_i2c_driver);
+
+	pr_info("%s: Parade TTDL I2C Driver (Build %s) rc=%d\n",
+			__func__, PT_DRIVER_VERSION, rc);
+	return rc;
+}
+module_init(pt_i2c_init);
+
+/*******************************************************************************
+ * FUNCTION: pt_i2c_exit
+ *
+ * SUMMARY: Exit function to unregister i2c module from kernel.
+ *
+ ******************************************************************************/
+static void __exit pt_i2c_exit(void)
+{
+	i2c_del_driver(&pt_i2c_driver);
+}
+module_exit(pt_i2c_exit);
+#endif
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Parade TrueTouch(R) Standard Product I2C driver");
+MODULE_AUTHOR("Parade Technologies <[email protected]>");

+ 5437 - 0
qcom/opensource/touch-drivers/pt/pt_loader.c

@@ -0,0 +1,5437 @@
+/*
+ * pt_loader.c
+ * Parade TrueTouch(TM) Standard Product FW Loader Module.
+ * For use with Parade touchscreen controllers.
+ * Supported parts include:
+ * TMA5XX
+ * TMA448
+ * TMA445A
+ * TT21XXX
+ * TT31XXX
+ * TT4XXXX
+ * TT7XXX
+ * TC3XXX
+ *
+ * Copyright (C) 2015-2020 Parade Technologies
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * Contact Parade Technologies at www.paradetech.com <[email protected]>
+ */
+
+#include "pt_regs.h"
+#include <linux/firmware.h>
+
+#define PT_LOADER_NAME "pt_loader"
+
+
+#define PT_AUTO_LOAD_FOR_CORRUPTED_FW 1
+#define PT_LOADER_FW_UPGRADE_RETRY_COUNT 3
+
+#define PT_FW_UPGRADE \
+	(defined(CONFIG_TOUCHSCREEN_PARADE_PLATFORM_FW_UPGRADE) \
+	|| defined(CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE))
+
+#define PT_TTCONFIG_UPGRADE \
+	(defined(CONFIG_TOUCHSCREEN_PARADE_PLATFORM_TTCONFIG_UPGRADE) \
+	|| defined(CONFIG_TOUCHSCREEN_PARADE_MANUAL_TTCONFIG_UPGRADE))
+
+/* Timeout values in ms. */
+#define PT_LDR_REQUEST_EXCLUSIVE_TIMEOUT		3000
+#define PT_LDR_SWITCH_TO_APP_MODE_TIMEOUT		300
+
+#define PT_MAX_STATUS_SIZE				32
+
+#define PT_DATA_MAX_ROW_SIZE				256
+#define PT_DATA_ROW_SIZE				128
+
+#define PT_ARRAY_ID_OFFSET				0
+#define PT_ROW_NUM_OFFSET				1
+#define PT_ROW_SIZE_OFFSET				3
+#define PT_ROW_DATA_OFFSET				5
+
+#define PT_POST_TT_CFG_CRC_MASK				0x2
+static inline struct pt_loader_data *pt_get_loader_data(
+		struct device *dev);
+
+static struct pt_core_commands *cmd;
+
+#define PIP2_LAUNCH_APP_DELAY	400
+
+struct pip2_file_read {
+	s8 para_num;
+	u8 file_handle;
+	int file_offset;
+	int file_max_size;
+	int file_read_size;
+	int file_print_size;
+	u8 *file_print_buf;
+	int file_print_left;
+};
+
+#ifdef TTDL_DIAGNOSTICS
+struct pip2_file_crc {
+	s8 para_num;
+	u8 file_handle;
+	int file_offset;
+	int file_max_size;
+	int file_read_size;
+};
+#endif
+
+struct pip2_loader_data {
+	struct device *dev;
+	struct completion pip2_fw_upgrade_complete; /* mutex for loader */
+	u8 pip2_file_handle;
+};
+
+struct pt_loader_data {
+	struct device *dev;
+	struct pt_sysinfo *si;
+	u8 status_buf[PT_MAX_STATUS_SIZE];
+	struct completion int_running;
+	struct completion calibration_complete;
+#ifdef CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE
+	int builtin_bin_fw_status;
+	bool is_manual_upgrade_enabled;
+#endif
+	struct work_struct fw_and_config_upgrade;
+	struct work_struct calibration_work;
+	struct pt_loader_platform_data *loader_pdata;
+#ifdef CONFIG_TOUCHSCREEN_PARADE_MANUAL_TTCONFIG_UPGRADE
+	struct mutex config_lock;
+	u8 *config_data;
+	int config_size;
+	bool config_loading;
+#endif
+	struct pip2_loader_data *pip2_data;
+	u8 pip2_load_file_no;
+	bool pip2_load_builtin;
+	u8 pip2_file_erase_file_no;
+	struct pip2_file_read pip2_file_data;
+#ifdef TTDL_DIAGNOSTICS
+	struct pip2_file_crc pip2_fcrc;
+#endif
+#ifdef CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE
+	struct work_struct bl_from_file;
+	struct work_struct pip2_bl_from_file;
+#endif
+};
+
+static u8 update_fw_status;
+static int pip2_erase_status;
+static int pip2_erase_rc;
+
+/* PIP2 update fw status codes. 1-99% are "active" */
+enum UPDATE_FW_STATUS {
+	UPDATE_FW_IDLE                   = 0,
+	UPDATE_FW_ACTIVE_10              = 10,
+	UPDATE_FW_ACTIVE_90              = 90,
+	UPDATE_FW_ACTIVE_99              = 99,
+	UPDATE_FW_COMPLETE               = 100,
+	UPDATE_FW_GENERAL_ERROR          = 200,
+	UPDATE_FW_PIP_VERSION_ERROR      = 201,
+	UPDATE_FW_VERSION_ERROR          = 202,
+	UPDATE_FW_ERASE_ERROR            = 203,
+	UPDATE_FW_FILE_CLOSE_ERROR       = 204,
+	UPDATE_FW_WRITE_ERROR            = 205,
+	UPDATE_FW_EXECUTE_ERROR          = 206,
+	UPDATE_FW_RESET_ERROR            = 207,
+	UPDATE_FW_MODE_ERROR             = 208,
+	UPDATE_FW_ENTER_BL_ERROR         = 209,
+	UPDATE_FW_FILE_OPEN_ERROR        = 210,
+	UPDATE_FW_SENTINEL_NOT_SEEN      = 211,
+	UPDATE_FW_EXCLUSIVE_ACCESS_ERROR = 212,
+	UPDATE_FW_NO_FW_PROVIDED         = 213,
+	UPDATE_FW_INVALID_FW_IMAGE       = 214,
+	UPDATE_FW_MISALIGN_FW_IMAGE      = 230,
+	UPDATE_FW_SYSTEM_NOMEM           = 231,
+	UPDATE_FW_INIT_BL_ERROR          = 232,
+	UPDATE_FW_PARSE_ROW_ERROR        = 233,
+	UPDATE_FW_PROGRAM_ROW_ERROR      = 234,
+	UPDATE_FW_EXIT_BL_ERROR          = 235,
+	UPDATE_FW_CHECK_SUM_ERROR        = 236,
+	UPDATE_FW_NO_PLATFORM_DATA       = 237,
+	UPDATE_FW_UNDEFINED_ERROR        = 255,
+};
+
+enum PIP2_FILE_ERASE_STATUS {
+	PIP2_FILE_ERASE_STATUS_DUT_BUSY		= 101,
+	PIP2_FILE_ERASE_STATUS_ENTER_BL_ERROR	= 102,
+};
+
+struct pt_dev_id {
+	u32 silicon_id;
+	u8 rev_id;
+	u32 bl_ver;
+};
+
+struct pt_hex_image {
+	u8 array_id;
+	u16 row_num;
+	u16 row_size;
+	u8 row_data[PT_DATA_ROW_SIZE];
+} __packed;
+
+#ifdef CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE
+static int pt_pip2_upgrade_firmware_from_builtin(struct device *dev);
+#endif
+
+typedef int (*PIP2_SEND_CMD)(struct device *, int,
+		u8, u8 *, u16, u8 *, u16 *);
+static struct pt_module loader_module;
+
+/*******************************************************************************
+ * FUNCTION: pt_get_loader_data
+ *
+ * SUMMARY: Inline function to get pt_loader_data pointer from loader module.
+ *
+ * RETURN:
+ *  pointer to pt_loader_data structure
+ *
+ * PARAMETERS:
+ *  *dev  - pointer to device structure
+ ******************************************************************************/
+static inline struct pt_loader_data *pt_get_loader_data(
+		struct device *dev)
+{
+	return pt_get_module_data(dev, &loader_module);
+}
+
+#if PT_FW_UPGRADE || PT_TTCONFIG_UPGRADE
+/*******************************************************************************
+ * FUNCTION: pt_calibrate_idacs
+ *
+ * SUMMARY: Calibrate idac for mutual-cap,buttons,self-cap by called functions.
+ * It needs stop panel scan during calibration.
+ *
+ * PARAMETERS:
+ *	*calibration_work - pointer to work_struct structure
+ ******************************************************************************/
+static void pt_calibrate_idacs(struct work_struct *calibration_work)
+{
+	struct pt_loader_data *ld = container_of(calibration_work,
+			struct pt_loader_data, calibration_work);
+	struct device *dev = ld->dev;
+	struct pt_cal_ext_data cal_data = {0};
+	u8 dut_gen = cmd->request_dut_generation(dev);
+	u8 mode;
+	u8 status;
+	int rc;
+
+	pt_debug(dev, DL_INFO, "Entering %s\n", __func__);
+	rc = cmd->request_exclusive(dev, PT_LDR_REQUEST_EXCLUSIVE_TIMEOUT);
+	if (rc < 0)
+		goto exit;
+
+	rc = cmd->nonhid_cmd->suspend_scanning(dev, 0);
+	if (rc < 0)
+		goto release;
+
+	if (dut_gen == DUT_PIP1_ONLY) {
+		for (mode = 0; mode < 3; mode++) {
+			rc = cmd->nonhid_cmd->calibrate_idacs(dev, 0, mode,
+							      &status);
+			if (rc < 0) {
+				pt_debug(dev, DL_ERROR,
+					 "%s: calibrate idac error, rc = %d\n",
+					 __func__, rc);
+				break;
+			}
+		}
+	} else {
+		/*
+		 * Manual calibration is not need after fw 3.4.932110.
+		 * Therefore, PT_LOADER_FLAG_NONE should be used to avoid
+		 * manual calibration if the FW is newer than 3.4.932110.
+		 */
+		memset(&cal_data, 0, sizeof(struct pt_cal_ext_data));
+		rc = cmd->nonhid_cmd->calibrate_ext(dev, 0, &cal_data,
+						    &status);
+		if (rc < 0) {
+			pt_debug(dev, DL_ERROR,
+				 "%s: extended calibrate error, rc = %d\n",
+				 __func__, rc);
+		}
+	}
+
+	rc = cmd->nonhid_cmd->resume_scanning(dev, 0);
+	if (rc < 0)
+		goto release;
+
+	pt_debug(dev, DL_WARN, "%s: Calibration Done\n", __func__);
+
+release:
+	cmd->release_exclusive(dev);
+exit:
+	complete(&ld->calibration_complete);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_calibration_attention
+ *
+ * SUMMARY: Wrapper function to schedule calibration work used to subscribe into
+ * TTDL attention list.Once called will unsubscribe from attention list.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *dev - pointer to device structure
+ ******************************************************************************/
+static int pt_calibration_attention(struct device *dev)
+{
+	struct pt_loader_data *ld = pt_get_loader_data(dev);
+	int rc = 0;
+
+	schedule_work(&ld->calibration_work);
+
+	cmd->unsubscribe_attention(dev, PT_ATTEN_STARTUP, PT_LOADER_NAME,
+		pt_calibration_attention, 0);
+
+	return rc;
+}
+#endif /* PT_FW_UPGRADE || PT_TTCONFIG_UPGRADE */
+
+#if PT_FW_UPGRADE \
+	|| defined(CONFIG_TOUCHSCREEN_PARADE_PLATFORM_TTCONFIG_UPGRADE)
+
+/*******************************************************************************
+ * FUNCTION: pt_get_panel_id
+ *
+ * SUMMARY: Get panel id from core data.
+ *
+ * RETURN:
+ *	 panel id
+ *
+ * PARAMETERS:
+ *	*dev  - pointer to device structure
+ ******************************************************************************/
+static u8 pt_get_panel_id(struct device *dev)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+
+	return cd->pid_for_loader;
+}
+#endif
+
+
+#if (PT_FW_UPGRADE || PT_TTCONFIG_UPGRADE)
+/*******************************************************************************
+ * FUNCTION: pt_check_firmware_version
+ *
+ * SUMMARY: Compare fw's version and revision control number with fw image's.
+ *
+ * RETURN:
+ *  -1: Do not upgrade firmware
+ *   0: Version info same, let caller decide
+ *   1: Do a firmware upgrade
+ *
+ * PARAMETERS:
+ *  *dev             - pointer to device structure
+ *   fw_ver_new      - firmware version
+ *   fw_revctrl_new  - firmware revision control number
+ ******************************************************************************/
+static int pt_check_firmware_version(struct device *dev,
+		u32 fw_ver_new, u32 fw_revctrl_new)
+{
+	struct pt_loader_data *ld = pt_get_loader_data(dev);
+	u32 fw_ver_img;
+	u32 fw_revctrl_img;
+
+	fw_ver_img = ld->si->ttdata.fw_ver_major << 8;
+	fw_ver_img += ld->si->ttdata.fw_ver_minor;
+
+	pt_debug(dev, DL_WARN,
+		"%s: Current FW ver:0x%04X New FW ver:0x%04X\n", __func__,
+			fw_ver_img, fw_ver_new);
+
+	if (fw_ver_new > fw_ver_img) {
+		pt_debug(dev, DL_WARN,
+			"%s: Image is newer, will upgrade\n", __func__);
+		return 1;
+	}
+
+	if (fw_ver_new < fw_ver_img) {
+		pt_debug(dev, DL_WARN,
+			"%s: Image is older, will NOT upgrade\n", __func__);
+		return -EPERM;
+	}
+
+	fw_revctrl_img = ld->si->ttdata.revctrl;
+
+	pt_debug(dev, DL_WARN,
+		"%s: Current FW rev:%d New FW rev:%d\n",
+		__func__, fw_revctrl_img, fw_revctrl_new);
+
+	if (fw_revctrl_new > fw_revctrl_img) {
+		pt_debug(dev, DL_WARN,
+			"%s: Image is newer, will upgrade\n", __func__);
+		return 1;
+	}
+
+	if (fw_revctrl_new < fw_revctrl_img) {
+		pt_debug(dev, DL_WARN,
+			"%s: Image is older, will NOT upgrade\n", __func__);
+		return -EPERM;
+	}
+
+	return 0;
+}
+
+#endif /* PT_FW_UPGRADE || PT_TTCONFIG_UPGRADE */
+
+#if PT_FW_UPGRADE
+/*******************************************************************************
+ * FUNCTION: pt_get_row_
+ *
+ * SUMMARY: Copy the image data to the "row_buf".
+ *
+ * RETURN:
+ *	 pointer to image buffer plus copy size
+ *
+ * PARAMETERS:
+ *  *dev        - pointer to device structure
+ *  *row_buf    - pointer to written buffer to program chip
+ *  *image_buf  - pointer to image buffer
+ *   size       - size of written data
+ ******************************************************************************/
+static u8 *pt_get_row_(struct device *dev, u8 *row_buf,
+		u8 *image_buf, int size)
+{
+	memcpy(row_buf, image_buf, size);
+	return image_buf + size;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_ldr_enter_
+ *
+ * SUMMARY: Enter bootloader state and update device id(silicon id, rev id,
+ *  bootloader version).
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *dev     - pointer to device structure
+ *  *dev_id  - pointer to device id
+ ******************************************************************************/
+static int pt_ldr_enter_(struct device *dev, struct pt_dev_id *dev_id)
+{
+	int rc;
+	u8 return_data[8];
+	u8 mode = PT_MODE_OPERATIONAL;
+
+	dev_id->silicon_id = 0;
+	dev_id->rev_id = 0;
+	dev_id->bl_ver = 0;
+
+	/*
+	 * Reset to get to a known state, sleep to allow sentinel processing.
+	 *
+	 * NOTE: This msleep MUST be greater than the auto launch timeout
+	 * that is built into the FW.
+	 */
+	cmd->request_reset(dev, PT_CORE_CMD_UNPROTECTED);
+	msleep(20);
+
+	rc = cmd->request_get_mode(dev, 0, &mode);
+	pt_debug(dev, DL_INFO, "%s:request_get_mode rc=%d mode=%d\n",
+		__func__, rc, mode);
+	if (rc)
+		return rc;
+
+	if (mode == PT_MODE_UNKNOWN)
+		return -EINVAL;
+
+	if (mode == PT_MODE_OPERATIONAL) {
+		rc = cmd->nonhid_cmd->start_bl(dev, PT_CORE_CMD_UNPROTECTED);
+		pt_debug(dev, DL_INFO, "%s:start_bl rc=%d\n", __func__, rc);
+		if (rc)
+			return rc;
+		rc = cmd->request_get_mode(dev, 0, &mode);
+		pt_debug(dev, DL_INFO, "%s:request_get_mode rc=%d mode=%d\n",
+			__func__, rc, mode);
+		if (rc)
+			return rc;
+		if (mode != PT_MODE_BOOTLOADER)
+			return -EINVAL;
+	}
+
+	rc = cmd->nonhid_cmd->get_bl_info(dev,
+		PT_CORE_CMD_UNPROTECTED, return_data);
+	pt_debug(dev, DL_INFO, "%s:get_bl_info rc=%d\n", __func__, rc);
+	if (rc)
+		return rc;
+
+	dev_id->silicon_id = get_unaligned_le32(&return_data[0]);
+	dev_id->rev_id = return_data[4];
+	dev_id->bl_ver = return_data[5] + (return_data[6] << 8)
+		+ (return_data[7] << 16);
+
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_ldr_init_
+ *
+ * SUMMARY: Erase the entire TrueTouch application, Configuration Data block,
+ *  and Design Data block in flash and enables the host to execute the Program
+ *  and Verify Row command to bootload the application image and data.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *dev           - pointer to device structure
+ *  *pt_hex_image  - pointer to hex image structure
+ ******************************************************************************/
+static int pt_ldr_init_(struct device *dev,
+		struct pt_hex_image *row_image)
+{
+	return cmd->nonhid_cmd->initiate_bl(dev, 0, 8,
+			(u8 *)pt_data_block_security_key, row_image->row_size,
+			row_image->row_data);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_ldr_parse_row_
+ *
+ * SUMMARY: Parse and copy the row buffer data to hex image structure.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *dev        - pointer to device structure
+ *  *row_buf    - pointer to row buffer
+ *  *row_image  - pointer to hex image structure
+ ******************************************************************************/
+static int pt_ldr_parse_row_(struct device *dev, u8 *row_buf,
+	struct pt_hex_image *row_image)
+{
+	int rc = 0;
+
+	row_image->array_id = row_buf[PT_ARRAY_ID_OFFSET];
+	row_image->row_num = get_unaligned_be16(&row_buf[PT_ROW_NUM_OFFSET]);
+	row_image->row_size = get_unaligned_be16(&row_buf[PT_ROW_SIZE_OFFSET]);
+
+	if (row_image->row_size > ARRAY_SIZE(row_image->row_data)) {
+		pt_debug(dev, DL_ERROR,
+			"%s: row data buffer overflow\n", __func__);
+		rc = -EOVERFLOW;
+		goto pt_ldr_parse_row_exit;
+	}
+
+	memcpy(row_image->row_data, &row_buf[PT_ROW_DATA_OFFSET],
+	       row_image->row_size);
+pt_ldr_parse_row_exit:
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_ldr_prog_row_
+ *
+ * SUMMARY: Program one row that the hex image structure data to the chip.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *dev        - pointer to device structure
+ *  *row_image  - pointer to hex image structure
+ ******************************************************************************/
+static int pt_ldr_prog_row_(struct device *dev,
+				 struct pt_hex_image *row_image)
+{
+	int rc = 0;
+	u16 length = row_image->row_size + 3;
+	u8 *data = NULL;
+	u8 offset = 0;
+
+	if (length > PT_DATA_MAX_ROW_SIZE)
+		return -EINVAL;
+
+	data = kzalloc(length, GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	data[offset++] = row_image->array_id;
+	data[offset++] = LOW_BYTE(row_image->row_num);
+	data[offset++] = HI_BYTE(row_image->row_num);
+	memcpy(data + 3, row_image->row_data, row_image->row_size);
+	rc = cmd->nonhid_cmd->prog_and_verify(dev, 0, length, data);
+	kfree(data);
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_ldr_verify_chksum_
+ *
+ * SUMMARY: Perform a full verification of the application integrity by
+ *  calculating the CRC of the TrueTouch application image in flash and
+ *  comparing it to the expected CRC stored in the TrueTouch application CRC
+ *  value stored in the Metadata row.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *dev  - pointer to device structure
+ ******************************************************************************/
+static int pt_ldr_verify_chksum_(struct device *dev)
+{
+	u8 result;
+	int rc;
+
+	rc = cmd->nonhid_cmd->verify_app_integrity(dev, 0, &result);
+	if (rc)
+		return rc;
+
+	/* fail */
+	if (result == 0)
+		return -EINVAL;
+
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_ldr_exit_
+ *
+ * SUMMARY: Launch the application from bootloader.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *dev  - pointer to device structure
+ ******************************************************************************/
+static int pt_ldr_exit_(struct device *dev)
+{
+	return cmd->nonhid_cmd->launch_app(dev, 0);
+}
+
+#define PT_NO_INC  0
+/*******************************************************************************
+ * FUNCTION: _pt_pip2_update_bl_status
+ *
+ * SUMMARY: Update the value in the bl_status global by either setting to a
+ *	specific value or incrementing it ensuring we don't go out of bounds.
+ *
+ * PARAMETERS:
+ *	*dev  - pointer to device structure
+ *	value - Value to force, if 0 then ignore.
+ *	inc   - Value to increment, if 0 then ignore.
+ ******************************************************************************/
+static u8 _pt_pip2_update_bl_status(struct device *dev, u8 value, u8 inc)
+{
+	pt_debug(dev, DL_DEBUG,
+		"%s: fw_status = %d, request val=%d, request inc=%d\n",
+		__func__, update_fw_status, value, inc);
+
+	/* Reset status if both value and inc are 0 */
+	if (value == UPDATE_FW_IDLE && inc == 0) {
+		update_fw_status = value;
+		pt_debug(dev, DL_WARN, "%s: ATM - Reset BL Status to %d\n",
+			__func__, value);
+		return update_fw_status;
+	}
+
+	/* Set to value if valid */
+	if (value > UPDATE_FW_IDLE && value < UPDATE_FW_UNDEFINED_ERROR) {
+		if (value <= UPDATE_FW_COMPLETE && value > update_fw_status) {
+			update_fw_status = value;
+		} else if (value >= UPDATE_FW_GENERAL_ERROR) {
+			update_fw_status = value;
+			pt_debug(dev, DL_WARN,
+				"%s: BL Status set to error code %d\n",
+				__func__, value);
+		}
+		return update_fw_status;
+	}
+
+	if (inc > 0 && update_fw_status + inc <= UPDATE_FW_COMPLETE)
+		update_fw_status += inc;
+	else
+		pt_debug(dev, DL_ERROR,
+			"%s: Inc Request out of bounds: status=%d inc=%d\n",
+			__func__, update_fw_status, inc);
+	return update_fw_status;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_load_app_
+ *
+ * SUMMARY: Program the firmware image to the chip.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *dev           - pointer to device structure
+ *  *fw            - pointer to the firmware
+ *   fw_size       - size of firmware
+ *   update_status - store the update status of firmware
+ ******************************************************************************/
+static int pt_load_app_(struct device *dev, const u8 *fw, int fw_size,
+	u8 *update_status)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_dev_id *dev_id;
+	struct pt_hex_image *row_image;
+	u8 *row_buf;
+	size_t image_rec_size;
+	size_t row_buf_size = PT_DATA_MAX_ROW_SIZE;
+	int row_count = 0;
+	u8 *p;
+	u8 *last_row;
+	int rc;
+	int rc_tmp;
+	int percent_cmplt;
+	int total_row_count;
+
+	if (update_status == NULL) {
+		rc = -EINVAL;
+		pt_debug(dev, DL_ERROR,
+			"%s: update_status is NULL pointer %d\n",
+			__func__, rc);
+		return rc;
+	} else if (*update_status > UPDATE_FW_ACTIVE_10) {
+		pt_debug(dev, DL_WARN,
+			"%s: update_status is illegal = %d, fixed to %d\n",
+			__func__, *update_status, UPDATE_FW_ACTIVE_10);
+		*update_status = UPDATE_FW_ACTIVE_10;
+	}
+
+	image_rec_size = sizeof(struct pt_hex_image);
+	if (fw_size % image_rec_size != 0) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Firmware image is misaligned\n", __func__);
+		*update_status = UPDATE_FW_MISALIGN_FW_IMAGE;
+		rc = -EINVAL;
+		goto _pt_load_app_error;
+	}
+	*update_status += 1;
+	total_row_count = fw_size / image_rec_size - 1;
+
+	pt_debug(dev, DL_INFO, "%s: start load app\n", __func__);
+#ifdef TTHE_TUNER_SUPPORT
+	cmd->request_tthe_print(dev, NULL, 0, "start load app");
+#endif
+
+	row_buf = kzalloc(row_buf_size, GFP_KERNEL);
+	row_image = kzalloc(sizeof(struct pt_hex_image), GFP_KERNEL);
+	dev_id = kzalloc(sizeof(struct pt_dev_id), GFP_KERNEL);
+	if (!row_buf || !row_image || !dev_id) {
+		*update_status = UPDATE_FW_SYSTEM_NOMEM;
+		rc = -ENOMEM;
+		goto _pt_load_app_exit;
+	}
+	*update_status += 1;
+
+	cmd->request_stop_wd(dev);
+
+	pt_debug(dev, DL_INFO, "%s: Send BL Loader Enter\n", __func__);
+#ifdef TTHE_TUNER_SUPPORT
+	cmd->request_tthe_print(dev, NULL, 0, "Send BL Loader Enter");
+#endif
+	rc = pt_ldr_enter_(dev, dev_id);
+	if (rc) {
+		*update_status = UPDATE_FW_ENTER_BL_ERROR;
+		pt_debug(dev, DL_ERROR, "%s: Error cannot start Loader (ret=%d)\n",
+			__func__, rc);
+		goto _pt_load_app_exit;
+	}
+	pt_debug(dev, DL_INFO,
+		"%s: dev: silicon id=%08X rev=%02X bl=%08X\n", __func__,
+		dev_id->silicon_id, dev_id->rev_id, dev_id->bl_ver);
+	*update_status += 1;
+
+	/*
+	 * since start loader is successful, firmware mode can be assumed
+	 * at bl mode directly. Updating this variable is helpful to detect
+	 * firmware entered application mode.
+	 */
+	cd->mode = PT_MODE_BOOTLOADER;
+
+	/* get last row */
+	last_row = (u8 *)fw + fw_size - image_rec_size;
+	pt_get_row_(dev, row_buf, last_row, image_rec_size);
+	pt_ldr_parse_row_(dev, row_buf, row_image);
+
+	/* initialise bootloader */
+	rc = pt_ldr_init_(dev, row_image);
+	if (rc) {
+		*update_status = UPDATE_FW_ERASE_ERROR;
+		pt_debug(dev, DL_ERROR, "%s: Error cannot init Loader (ret=%d)\n",
+			__func__, rc);
+		goto _pt_load_app_exit;
+	}
+	*update_status += 5;
+
+	pt_debug(dev, DL_INFO,
+		"%s: Send BL Loader Blocks\n", __func__);
+#ifdef TTHE_TUNER_SUPPORT
+	cmd->request_tthe_print(dev, NULL, 0, "Send BL Loader Blocks");
+#endif
+	p = (u8 *)fw;
+	while (p < last_row) {
+		/* Get row */
+		row_count += 1;
+		pt_debug(dev, DL_INFO, "%s: read row=%d\n",
+			__func__, row_count);
+		memset(row_buf, 0, row_buf_size);
+		p = pt_get_row_(dev, row_buf, p, image_rec_size);
+
+		/* Don't update BL status on every pass */
+		if (row_count / 8 * 8 == row_count) {
+			/* Calculate % complete for update_status sysfs */
+			percent_cmplt = row_count * 100 / total_row_count;
+			if (percent_cmplt > UPDATE_FW_ACTIVE_99)
+				percent_cmplt = UPDATE_FW_ACTIVE_99;
+			*update_status = (percent_cmplt > *update_status) ?
+				percent_cmplt : *update_status;
+#ifdef TTDL_DIAGNOSTICS
+			pt_debug(dev, DL_INFO,
+				"Wrote row num %d of total %d\n",
+				row_count, total_row_count);
+#endif
+		}
+
+		/* Parse row */
+		pt_debug(dev, DL_INFO, "%s: p=%p buf=%p buf[0]=%02X\n",
+			__func__, p, row_buf, row_buf[0]);
+		rc = pt_ldr_parse_row_(dev, row_buf, row_image);
+		pt_debug(dev, DL_INFO,
+			"%s: array_id=%02X row_num=%04X(%d) row_size=%04X(%d)\n",
+			__func__, row_image->array_id,
+			row_image->row_num, row_image->row_num,
+			row_image->row_size, row_image->row_size);
+		if (rc) {
+			*update_status = UPDATE_FW_PARSE_ROW_ERROR;
+			pt_debug(dev, DL_ERROR,
+				"%s: Parse Row Error (a=%d r=%d ret=%d\n",
+				__func__, row_image->array_id,
+				row_image->row_num, rc);
+			goto _pt_load_app_exit;
+		} else {
+			pt_debug(dev, DL_INFO,
+				"%s: Parse Row (a=%d r=%d ret=%d\n",
+				__func__, row_image->array_id,
+				row_image->row_num, rc);
+		}
+
+		/* program row */
+		rc = pt_ldr_prog_row_(dev, row_image);
+		if (rc) {
+			*update_status = UPDATE_FW_PROGRAM_ROW_ERROR;
+			pt_debug(dev, DL_ERROR,
+				"%s: Prog Row Error (array=%d row=%d ret=%d)\n",
+				__func__, row_image->array_id,
+				row_image->row_num, rc);
+			goto _pt_load_app_exit;
+		}
+
+		pt_debug(dev, DL_INFO,
+			"%s: array=%d row_cnt=%d row_num=%04X\n",
+			__func__, row_image->array_id, row_count,
+			row_image->row_num);
+	}
+
+	/* exit loader */
+	pt_debug(dev, DL_INFO, "%s: Send BL Loader Terminate\n", __func__);
+#ifdef TTHE_TUNER_SUPPORT
+	cmd->request_tthe_print(dev, NULL, 0, "Send BL Loader Terminate");
+#endif
+	rc = pt_ldr_exit_(dev);
+	if (rc) {
+		*update_status = UPDATE_FW_EXIT_BL_ERROR;
+		pt_debug(dev, DL_ERROR, "%s: Error on exit Loader (ret=%d)\n",
+			__func__, rc);
+
+		/* verify app checksum */
+		rc_tmp = pt_ldr_verify_chksum_(dev);
+		if (rc_tmp) {
+			*update_status = UPDATE_FW_CHECK_SUM_ERROR;
+			pt_debug(dev, DL_ERROR,
+				"%s: ldr_verify_chksum fail r=%d\n",
+				__func__, rc_tmp);
+		} else
+			pt_debug(dev, DL_INFO,
+				"%s: APP Checksum Verified\n", __func__);
+	} else
+		*update_status = UPDATE_FW_ACTIVE_99;
+
+_pt_load_app_exit:
+	kfree(row_buf);
+	kfree(row_image);
+	kfree(dev_id);
+_pt_load_app_error:
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_upgrade_firmware
+ *
+ * SUMMARY: Program the firmware image and set call back for start up.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *dev           - pointer to device structure
+ *  *fw_img        - pointer to the firmware
+ *   fw_size       - size of firmware
+ *  *update_status - store the update status of firmware
+ ******************************************************************************/
+static int pt_upgrade_firmware(struct device *dev, const u8 *fw_img,
+		int fw_size, u8 *update_status)
+{
+	struct pt_loader_data *ld = pt_get_loader_data(dev);
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	int retry = PT_LOADER_FW_UPGRADE_RETRY_COUNT;
+	bool wait_for_calibration_complete = false;
+	int rc;
+	int t;
+
+	_pt_pip2_update_bl_status(dev, UPDATE_FW_IDLE, PT_NO_INC);
+
+	if (update_status == NULL) {
+		rc = -EINVAL;
+		pt_debug(dev, DL_ERROR,
+			"%s: update_status is NULL pointer %d\n",
+			__func__, rc);
+		return rc;
+	} else if (*update_status > UPDATE_FW_ACTIVE_10) {
+		pt_debug(dev, DL_WARN,
+			"%s: update_status is illegal = %d, fixed to %d\n",
+			__func__, *update_status, UPDATE_FW_ACTIVE_10);
+		*update_status = UPDATE_FW_ACTIVE_10;
+	}
+
+	/* Ensure no enum task is pending */
+	pt_debug(dev, DL_WARN, "%s: Cancel enum work thread\n", __func__);
+	cancel_work_sync(&cd->enum_work);
+
+	pm_runtime_get_sync(dev);
+	*update_status += 1;
+
+	rc = cmd->request_exclusive(dev, PT_LDR_REQUEST_EXCLUSIVE_TIMEOUT);
+	if (rc < 0) {
+		*update_status = UPDATE_FW_EXCLUSIVE_ACCESS_ERROR;
+		goto exit;
+	}
+	*update_status += 1;
+
+	t = *update_status;
+	while (retry--) {
+		/* Restore the update_status */
+		*update_status = t;
+		rc = pt_load_app_(dev, fw_img, fw_size, update_status);
+		if (rc < 0)
+			pt_debug(dev, DL_ERROR,
+				"%s: Firmware update failed rc=%d, retry:%d\n",
+				__func__, rc, retry);
+		else
+			break;
+		msleep(20);
+	}
+	if (rc < 0) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Firmware update failed with error code %d\n",
+			__func__, rc);
+	} else if (ld->loader_pdata &&
+			(ld->loader_pdata->flags
+			 & PT_LOADER_FLAG_CALIBRATE_AFTER_FW_UPGRADE)) {
+#if (KERNEL_VERSION(3, 13, 0) <= LINUX_VERSION_CODE)
+		reinit_completion(&ld->calibration_complete);
+#else
+		INIT_COMPLETION(ld->calibration_complete);
+#endif
+		/* set up call back for startup */
+		pt_debug(dev, DL_INFO,
+			"%s: Adding callback for calibration\n", __func__);
+		rc = cmd->subscribe_attention(dev, PT_ATTEN_STARTUP,
+			PT_LOADER_NAME, pt_calibration_attention, 0);
+		if (rc) {
+			pt_debug(dev, DL_ERROR,
+				"%s: Failed adding callback for calibration\n",
+				__func__);
+			pt_debug(dev, DL_ERROR,
+				"%s: No calibration will be performed\n",
+				__func__);
+			rc = 0;
+		} else
+			wait_for_calibration_complete = true;
+	}
+
+	cmd->release_exclusive(dev);
+
+exit:
+	if (!rc) {
+		cmd->request_enum(dev, true);
+
+		/*
+		 * Wait for FW reset sentinel from reset or execute for up
+		 * to 500ms
+		 */
+		t = wait_event_timeout(cd->wait_q,
+			(cd->startup_status >=
+				STARTUP_STATUS_FW_RESET_SENTINEL) &&
+			(cd->mode == PT_MODE_OPERATIONAL),
+			msecs_to_jiffies(PT_BL_WAIT_FOR_SENTINEL));
+		if (IS_TMO(t)) {
+			pt_debug(dev, DL_WARN,
+				"%s: 0x%04X Timeout waiting for FW sentinel",
+				__func__, cd->startup_status);
+		}
+
+		/* Double verify DUT is alive and well in Application mode */
+		if (!(cd->startup_status & STARTUP_STATUS_FW_RESET_SENTINEL)) {
+			pt_debug(dev, DL_ERROR,
+				"%s FW sentinel not seen\n", __func__);
+			*update_status = UPDATE_FW_SENTINEL_NOT_SEEN;
+		} else if (cd->mode != PT_MODE_OPERATIONAL) {
+			*update_status = UPDATE_FW_MODE_ERROR;
+			pt_debug(dev, DL_ERROR,
+				"%s ERROR: Not in App mode as expected\n",
+				__func__);
+		} else {
+			*update_status = UPDATE_FW_COMPLETE;
+			pt_debug(dev, DL_INFO,
+				"%s == PIP1 FW upgrade finished ==\n",
+				__func__);
+		}
+	} else if (*update_status < UPDATE_FW_COMPLETE) {
+		*update_status = UPDATE_FW_UNDEFINED_ERROR;
+		pt_debug(dev, DL_ERROR, "%s undefined error!\n", __func__);
+	}
+
+	pm_runtime_put_sync(dev);
+
+	if (wait_for_calibration_complete)
+		wait_for_completion(&ld->calibration_complete);
+
+	return rc;
+}
+
+#endif /* PT_FW_UPGRADE */
+
+#ifdef CONFIG_TOUCHSCREEN_PARADE_PLATFORM_FW_UPGRADE
+/*******************************************************************************
+ * FUNCTION: pt_check_firmware_version_platform
+ *
+ * SUMMARY: The caller of function pt_check_firmware_version() to determine
+ *  whether to load firmware from touch_firmware structure.
+ *
+ * RETURN:
+ *   0: Don't upgrade
+ *   1: Upgrade
+ *
+ * PARAMETERS:
+ *  *dev  - pointer to device structure
+ *  *fw   - pointer to the touch_firmware structure
+ ******************************************************************************/
+static int pt_check_firmware_version_platform(struct device *dev,
+		struct pt_touch_firmware *fw)
+{
+	struct pt_loader_data *ld = pt_get_loader_data(dev);
+	u32 fw_ver_new;
+	u32 fw_revctrl_new;
+	int upgrade;
+
+	if (!ld->si) {
+		pt_debug(dev, DL_WARN,
+			"%s: No firmware info found, DUT FW may be corrupted\n",
+			__func__);
+		return PT_AUTO_LOAD_FOR_CORRUPTED_FW;
+	}
+
+	fw_ver_new = get_unaligned_be16(fw->ver + 2);
+	/* 4 middle bytes are not used */
+	fw_revctrl_new = get_unaligned_be32(fw->ver + 8);
+	pt_debug(dev, DL_WARN, "%s: Built-in FW version 0x%04x rev %d\n",
+		__func__, fw_ver_new, fw_revctrl_new);
+
+	upgrade = pt_check_firmware_version(dev, fw_ver_new,
+		fw_revctrl_new);
+
+	if (upgrade > 0)
+		return 1;
+
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_get_platform_firmware
+ *
+ * SUMMARY: To get the pointer of right touch_firmware structure by panel id.
+ *
+ * RETURN:
+ *   pointer to touch_firmware structure or null pointer if fail
+ *
+ * PARAMETERS:
+ *  *dev  - pointer to device structure
+ ******************************************************************************/
+static struct pt_touch_firmware *pt_get_platform_firmware(
+		struct device *dev)
+{
+	struct pt_loader_data *ld = pt_get_loader_data(dev);
+	struct pt_touch_firmware **fws;
+	struct pt_touch_firmware *fw;
+	u8 panel_id;
+
+	panel_id = pt_get_panel_id(dev);
+	if (panel_id == PANEL_ID_NOT_ENABLED) {
+		pt_debug(dev, DL_WARN,
+			"%s: Panel ID not enabled, using legacy firmware\n",
+			__func__);
+		return ld->loader_pdata->fw;
+	}
+
+	fws = ld->loader_pdata->fws;
+	if (!fws) {
+		pt_debug(dev, DL_ERROR,
+			"%s: No firmwares provided\n", __func__);
+		return NULL;
+	}
+
+	/* Find FW according to the Panel ID */
+	while ((fw = *fws++)) {
+		if (fw->panel_id == panel_id) {
+			pt_debug(dev, DL_WARN,
+				"%s: Found matching fw:%p with Panel ID: 0x%02X\n",
+				__func__, fw, fw->panel_id);
+			return fw;
+		}
+		pt_debug(dev, DL_WARN,
+			"%s: Found mismatching fw:%p with Panel ID: 0x%02X\n",
+			__func__, fw, fw->panel_id);
+	}
+
+	return NULL;
+}
+
+/*******************************************************************************
+ * FUNCTION: upgrade_firmware_from_platform
+ *
+ * SUMMARY: Get touch_firmware structure and perform upgrade if pass the
+ *  firmware version check.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *dev     - pointer to device structure
+ *   forced  - flag to force upgrade(1:force to upgrade)
+ ******************************************************************************/
+static int upgrade_firmware_from_platform(struct device *dev,
+		bool forced)
+{
+	struct pt_loader_data *ld = pt_get_loader_data(dev);
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_touch_firmware *fw;
+	int rc = -ENODEV;
+	int upgrade;
+	int retry = 3;
+
+retry_bl:
+	if (!ld->loader_pdata) {
+		_pt_pip2_update_bl_status(dev, UPDATE_FW_NO_PLATFORM_DATA,
+			PT_NO_INC);
+		pt_debug(dev, DL_ERROR,
+			"%s: No loader platform data\n", __func__);
+		return rc;
+	}
+
+	fw = pt_get_platform_firmware(dev);
+	if (!fw || !fw->img || !fw->size) {
+		_pt_pip2_update_bl_status(dev, UPDATE_FW_NO_FW_PROVIDED,
+			PT_NO_INC);
+		pt_debug(dev, DL_ERROR,
+			"%s: No platform firmware\n", __func__);
+		return rc;
+	}
+
+	if (!fw->ver || !fw->vsize) {
+		_pt_pip2_update_bl_status(dev, UPDATE_FW_INVALID_FW_IMAGE,
+			PT_NO_INC);
+		pt_debug(dev, DL_ERROR, "%s: No platform firmware version\n",
+			__func__);
+		return rc;
+	}
+
+	if (forced)
+		upgrade = forced;
+	else
+		upgrade = pt_check_firmware_version_platform(dev, fw);
+
+	if (upgrade) {
+		rc = pt_upgrade_firmware(dev,
+			fw->img, fw->size, &update_fw_status);
+
+		/* An extra BL may be needed if default PID was wrong choice */
+		if ((cd->panel_id_support & PT_PANEL_ID_BY_SYS_INFO) &&
+				!rc && !ld->si && retry--) {
+			pt_debug(dev, DL_WARN, "%s: ATM - An extra BL may be needed\n",
+				__func__);
+			/* Panel_ID coming from sysinfo, ensure we have it */
+			ld->si = cmd->request_sysinfo(dev);
+			goto retry_bl;
+		}
+	}
+
+	return rc;
+}
+#endif /* CONFIG_TOUCHSCREEN_PARADE_PLATFORM_FW_UPGRADE */
+
+#ifdef CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE
+/*******************************************************************************
+ * FUNCTION: _pt_pip1_bl_from_file
+ *
+ * SUMMARY: Wrapper function for _pt_firmware_cont() to perform bl with an
+ *  image file from userspace.
+ *
+ * PARAMETERS:
+ *  *fw      - pointer to firmware structure
+ *   forced  - flag to force upgrade(1:force to upgrade)
+ ******************************************************************************/
+static int _pt_pip1_bl_from_file(struct device *dev)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	int fw_size = 0;
+	int rc = 0;
+	u8 *fw_img = NULL;
+	u8 header_size = 0;
+
+	_pt_pip2_update_bl_status(dev, UPDATE_FW_IDLE, PT_NO_INC);
+
+	fw_img = kzalloc(PT_PIP2_MAX_FILE_SIZE, GFP_KERNEL);
+	if (!fw_img) {
+		_pt_pip2_update_bl_status(dev, UPDATE_FW_SYSTEM_NOMEM,
+					  PT_NO_INC);
+		rc = -ENOMEM;
+		goto exit;
+	}
+
+	rc = cmd->nonhid_cmd->read_us_file(dev, cd->pip2_us_file_path,
+					    fw_img, &fw_size);
+	if (rc) {
+		pt_debug(dev, DL_ERROR, "%s: No firmware provided to load\n",
+			 __func__);
+		_pt_pip2_update_bl_status(dev, UPDATE_FW_NO_FW_PROVIDED,
+					  PT_NO_INC);
+		goto exit;
+	}
+
+	if (!fw_img || !fw_size) {
+		pt_debug(dev, DL_ERROR,
+			"%s: No firmware received\n", __func__);
+		_pt_pip2_update_bl_status(dev, UPDATE_FW_INVALID_FW_IMAGE,
+			PT_NO_INC);
+		goto exit;
+	}
+
+	header_size = fw_img[0];
+	if (header_size >= (fw_size + 1)) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Firmware format is invalid\n", __func__);
+		_pt_pip2_update_bl_status(dev, UPDATE_FW_INVALID_FW_IMAGE,
+			PT_NO_INC);
+		goto exit;
+	}
+
+	pt_upgrade_firmware(dev, &(fw_img[header_size + 1]),
+		fw_size - (header_size + 1), &update_fw_status);
+exit:
+	kfree(fw_img);
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_firmware_cont
+ *
+ * SUMMARY: Firmware upgrade continue function that verifies the firmware size
+ *  in the firmware class and then upgrades the firmware.
+ *
+ * PARAMETERS:
+ *  *fw      - pointer to firmware structure
+ *   forced  - flag to force upgrade(1:force to upgrade)
+ ******************************************************************************/
+static void _pt_firmware_cont(const struct firmware *fw, void *context)
+{
+	struct device *dev = context;
+	struct pt_loader_data *ld = pt_get_loader_data(dev);
+	u8 header_size = 0;
+
+	_pt_pip2_update_bl_status(dev, UPDATE_FW_IDLE, PT_NO_INC);
+	if (!fw) {
+		pt_debug(dev, DL_ERROR, "%s: No firmware\n", __func__);
+		_pt_pip2_update_bl_status(dev, UPDATE_FW_NO_FW_PROVIDED,
+			PT_NO_INC);
+		goto pt_firmware_cont_exit;
+	}
+
+	if (!fw->data || !fw->size) {
+		pt_debug(dev, DL_ERROR,
+			"%s: No firmware received\n", __func__);
+		_pt_pip2_update_bl_status(dev, UPDATE_FW_INVALID_FW_IMAGE,
+			PT_NO_INC);
+		goto pt_firmware_cont_release_exit;
+	}
+
+	header_size = fw->data[0];
+	if (header_size >= (fw->size + 1)) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Firmware format is invalid\n", __func__);
+		_pt_pip2_update_bl_status(dev, UPDATE_FW_INVALID_FW_IMAGE,
+			PT_NO_INC);
+		goto pt_firmware_cont_release_exit;
+	}
+
+	pt_upgrade_firmware(dev, &(fw->data[header_size + 1]),
+		fw->size - (header_size + 1), &update_fw_status);
+
+pt_firmware_cont_release_exit:
+	if (fw)
+		release_firmware(fw);
+
+pt_firmware_cont_exit:
+	ld->is_manual_upgrade_enabled = 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_check_firmware_config_version
+ *
+ * SUMMARY: Compare fw's config version with fw image's. If they are differnt
+ *	report a FW upgrade is needed.
+ *
+ * RETURN:
+ *  -1: Do not upgrade firmware
+ *   0: Version info same, let caller decide
+ *   1: Do a firmware upgrade
+ *
+ * PARAMETERS:
+ *  *dev               - pointer to device structure
+ *   image_config_ver  - Image's firmware config version
+ ******************************************************************************/
+static int pt_check_firmware_config_version(struct device *dev,
+		u16 image_config_ver)
+{
+	struct pt_loader_data *ld = pt_get_loader_data(dev);
+	u16 fw_config_ver;
+
+	fw_config_ver = ld->si->ttdata.fw_ver_conf;
+
+	pt_debug(dev, DL_WARN,
+		 "%s: Current config ver:0x%04X New config ver:0x%04X\n",
+		 __func__, fw_config_ver, image_config_ver);
+
+	if (image_config_ver != fw_config_ver) {
+		pt_debug(dev, DL_WARN,
+			 "%s: Image config ver is different, will upgrade\n",
+			 __func__);
+		return 1;
+	}
+
+	if (image_config_ver == fw_config_ver) {
+		pt_debug(dev, DL_WARN,
+			 "%s: Image config ver is the same, will NOT upgrade\n",
+			 __func__);
+		return 0;
+	}
+
+	return -EPERM;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_check_firmware_version_builtin
+ *
+ * SUMMARY: The caller of function pt_check_firmware_version() to determine
+ *  whether to load built-in firmware.
+ *
+ * RETURN:
+ *   0: Don't upgrade
+ *   1: Upgrade
+ *
+ * PARAMETERS:
+ *  *dev  - pointer to device structure
+ *  *fw   - pointer to the firmware structure
+ ******************************************************************************/
+static int pt_check_firmware_version_builtin(struct device *dev,
+		const struct firmware *fw)
+{
+	struct pt_loader_data *ld = pt_get_loader_data(dev);
+	u16 fw_config_ver_new;
+	u32 fw_ver_new;
+	u32 fw_revctrl_new;
+	int upgrade;
+
+	if (!ld->si) {
+		pt_debug(dev, DL_WARN,
+			"%s: No firmware info found, DUT FW may be corrupted\n",
+			__func__);
+		return PT_AUTO_LOAD_FOR_CORRUPTED_FW;
+	}
+
+	fw_ver_new = get_unaligned_be16(fw->data + 3);
+	/* 4 middle bytes are not used */
+	fw_revctrl_new = get_unaligned_be32(fw->data + 9);
+	/* Offset 17,18 is the TT Config version*/
+	fw_config_ver_new = get_unaligned_be16(fw->data + 17);
+
+	pt_debug(dev, DL_WARN,
+		"%s: Built-in FW version=0x%04x rev=%d config=0x%04X\n",
+		__func__, fw_ver_new, fw_revctrl_new, fw_config_ver_new);
+
+	upgrade = pt_check_firmware_version(dev, fw_ver_new,
+			fw_revctrl_new);
+
+	/* Only check config version if FW version was an exact match */
+	if (upgrade == 0)
+		upgrade = pt_check_firmware_config_version(dev,
+					fw_config_ver_new);
+
+	if (upgrade > 0)
+		return 1;
+
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_firmware_cont_builtin
+ *
+ * SUMMARY: Perform upgrade if pass the firmware version check.
+ *
+ * PARAMETERS:
+ *  *dev     - pointer to device structure
+ *   forced  - flag to force upgrade(1:force to upgrade)
+ ******************************************************************************/
+static void _pt_firmware_cont_builtin(const struct firmware *fw,
+		void *context)
+{
+	struct device *dev = context;
+	struct pt_loader_data *ld = pt_get_loader_data(dev);
+	int upgrade;
+
+	if (!fw) {
+		pt_debug(dev, DL_INFO,
+			"%s: No builtin firmware\n", __func__);
+		goto _pt_firmware_cont_builtin_exit;
+	}
+
+	if (!fw->data || !fw->size) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Invalid builtin firmware\n", __func__);
+		goto _pt_firmware_cont_builtin_exit;
+	}
+
+	pt_debug(dev, DL_WARN, "%s: Found firmware\n", __func__);
+
+	upgrade = pt_check_firmware_version_builtin(dev, fw);
+	if (upgrade) {
+		_pt_firmware_cont(fw, dev);
+		ld->builtin_bin_fw_status = 0;
+		return;
+	}
+
+_pt_firmware_cont_builtin_exit:
+	if (fw)
+		release_firmware(fw);
+
+	ld->builtin_bin_fw_status = -EINVAL;
+}
+
+/*******************************************************************************
+ * FUNCTION: upgrade_firmware_from_class
+ *
+ * SUMMARY: Create the firmware class but don't actually load any FW to the
+ *	DUT. This creates all the sysfs nodes needed for a user to bootload
+ *	the DUT with their own bin file.
+ *
+ * RETURN:
+ *   0 = success
+ *
+ * PARAMETERS:
+ *  *dev     - pointer to device structure
+ ******************************************************************************/
+static int upgrade_firmware_from_class(struct device *dev)
+{
+	int retval;
+
+	pt_debug(dev, DL_INFO,
+		"%s: Enabling firmware class loader\n", __func__);
+
+	retval = request_firmware_nowait(THIS_MODULE, FW_ACTION_NOHOTPLUG,
+			dev_name(dev), dev, GFP_KERNEL, dev,
+			_pt_firmware_cont);
+	if (retval < 0) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Fail request firmware class file load\n",
+			__func__);
+		return retval;
+	}
+
+	return 0;
+}
+
+#define FILENAME_LEN_MAX 64
+/*******************************************************************************
+ * FUNCTION: generate_firmware_filename
+ *
+ * SUMMARY: Generate firmware file name by panel id. Generates binary FW
+ *  filename as following:
+ *  - Panel ID not enabled: tt_fw.bin
+ *  - Panel ID enabled: tt_fw_pidXX.bin
+ *
+ * RETURN:
+ *   pointer to file name or null pointer if fail
+ *
+ * PARAMETERS:
+ *  *dev  - pointer to device structure
+ ******************************************************************************/
+static char *generate_firmware_filename(struct device *dev)
+{
+	char *filename;
+	u8 panel_id;
+
+	filename = kzalloc(FILENAME_LEN_MAX, GFP_KERNEL);
+	if (!filename)
+		return NULL;
+
+	panel_id = pt_get_panel_id(dev);
+	if (panel_id == PANEL_ID_NOT_ENABLED)
+		snprintf(filename, FILENAME_LEN_MAX, "%s", PT_FW_FILE_NAME);
+	else
+		snprintf(filename, FILENAME_LEN_MAX, "%s_pid%02X%s",
+			PT_FW_FILE_PREFIX, panel_id, PT_FW_FILE_SUFFIX);
+
+	pt_debug(dev, DL_INFO, "%s: Filename: %s\n",
+		__func__, filename);
+
+	return filename;
+}
+
+/*******************************************************************************
+ * FUNCTION: generate_silicon_id_firmware_filename
+ *
+ * SUMMARY: Generate firmware file name with the HW version prefix followed by
+ * panel id. Generates binary FW filename as following (where XXXX is the
+ * silicon ID):
+ *  - Panel ID not enabled: XXXX_tt_fw.bin
+ *  - Panel ID enabled: XXXX_tt_fw_pidXX.bin
+ *
+ * RETURN:
+ *   pointer to file name or null pointer if fail
+ *
+ * PARAMETERS:
+ *  *dev  - pointer to device structure
+ ******************************************************************************/
+static char *generate_silicon_id_firmware_filename(struct device *dev)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	char *filename;
+	char si_id[5] = "DEAD";
+	u8 panel_id;
+
+	filename = kzalloc(FILENAME_LEN_MAX, GFP_KERNEL);
+	if (!filename)
+		return NULL;
+
+	panel_id = pt_get_panel_id(dev);
+	memcpy(si_id, cd->hw_version, 4);
+
+	if (panel_id == PANEL_ID_NOT_ENABLED)
+		snprintf(filename, FILENAME_LEN_MAX, "%s_%s", si_id,
+			PT_FW_FILE_NAME);
+	else
+		snprintf(filename, FILENAME_LEN_MAX, "%s_%s_pid%02X%s",
+			si_id, PT_FW_FILE_PREFIX, panel_id, PT_FW_FILE_SUFFIX);
+
+	pt_debug(dev, DL_INFO, "%s: Filename: %s\n", __func__, filename);
+
+	return filename;
+}
+
+#define MAX_FILE_NAMES 2
+/*******************************************************************************
+ * FUNCTION: upgrade_firmware_from_builtin
+ *
+ * SUMMARY: Create the firmware class load FW by searching the name of built-in
+ *	file. Then perform upgrade after getting the file.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *dev     - pointer to device structure
+ ******************************************************************************/
+static int upgrade_firmware_from_builtin(struct device *dev)
+{
+	struct pt_loader_data *ld = pt_get_loader_data(dev);
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	char *filename[MAX_FILE_NAMES];
+	int index = 0;
+	int retval;
+	const struct firmware *fw_entry = NULL;
+	int retry = 3;
+
+retry_bl:
+	pt_debug(dev, DL_WARN,
+		"%s: Enabling firmware class loader built-in\n",
+		__func__);
+
+	/* Load the supported file names in the search order */
+
+	/* 0 = Flash file name with optional PID "pt_fw<_PIDX>.bin" */
+	filename[0] = generate_firmware_filename(dev);
+	if (!filename[0]) {
+		pt_debug(dev, DL_ERROR,
+			"%s: ERROR - Could not generate filename\n", __func__);
+		return -ENOMEM;
+	}
+
+	/*
+	 * 1 = Flash file name with Silicon ID prefix and optional PID
+	 * "XXXX_pt_fw<_PIDX>.bin"
+	 */
+	filename[1] = generate_silicon_id_firmware_filename(dev);
+	if (!filename[1]) {
+		pt_debug(dev, DL_ERROR,
+			"%s: ERROR - Could not generate filename\n", __func__);
+		return -ENOMEM;
+	}
+
+	mutex_lock(&cd->firmware_class_lock);
+	while (index < MAX_FILE_NAMES) {
+		pt_debug(dev, DL_WARN, "%s: Request FW file %s\n", __func__,
+			filename[index]);
+#if (KERNEL_VERSION(3, 13, 0) > LINUX_VERSION_CODE)
+		retval = request_firmware(&fw_entry, filename[index], dev);
+#else
+		retval = request_firmware_direct(&fw_entry,
+			filename[index], dev);
+#endif
+		if (retval < 0) {
+			pt_debug(dev, DL_WARN, "%s: Fail request FW %s load\n",
+				__func__, filename[index]);
+		} else {
+			pt_debug(dev, DL_WARN, "%s: FW %s class file loading\n",
+				__func__, filename[index]);
+			break;
+		}
+		index++;
+	}
+
+	/* Proceed with the BL if a matching file was found */
+	if (index != MAX_FILE_NAMES) {
+		_pt_firmware_cont_builtin(fw_entry, dev);
+		/* An extra BL may be needed if default PID was wrong choice */
+		if ((cd->panel_id_support & PT_PANEL_ID_BY_SYS_INFO) &&
+				!ld->si && retry--) {
+			pt_debug(dev, DL_WARN, "%s: ATM - An extra BL may be needed\n",
+				__func__);
+			/* Free allocated memory */
+			index = 0;
+			while (index < MAX_FILE_NAMES)
+				kfree(filename[index++]);
+			/* Reset index to 0 */
+			index = 0;
+			mutex_unlock(&cd->firmware_class_lock);
+
+			/* Panel_ID coming from sysinfo, ensure we have it */
+			ld->si = cmd->request_sysinfo(dev);
+			goto retry_bl;
+		}
+		retval = ld->builtin_bin_fw_status;
+	}
+
+	index = 0;
+	while (index < MAX_FILE_NAMES)
+		kfree(filename[index++]);
+
+	mutex_unlock(&cd->firmware_class_lock);
+	return retval;
+}
+#endif /* CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE */
+
+#if PT_TTCONFIG_UPGRADE
+/*******************************************************************************
+ * FUNCTION: pt_write_config_row_
+ *
+ * SUMMARY: Allow to program the data block area that includes configuration
+ *  data, manufacturing data, design data.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *dev         - pointer to device structure
+ *   ebid        - block id to determine the block name(configuration,etc)
+ *   row_number  - row number if written block
+ *   row_size    - row size of written data
+ *  *data        - pointer to the data to write
+ ******************************************************************************/
+static int pt_write_config_row_(struct device *dev, u8 ebid,
+		u16 row_number, u16 row_size, u8 *data)
+{
+	int rc;
+	u16 actual_write_len;
+
+	rc = cmd->nonhid_cmd->write_data_block(dev, row_number,
+			row_size, ebid, data, (u8 *)pt_data_block_security_key,
+			&actual_write_len);
+	if (rc) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Fail Put EBID=%d row=%d cmd fail r=%d\n",
+			__func__, ebid, row_number, rc);
+		return rc;
+	}
+
+	if (actual_write_len != row_size) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Fail Put EBID=%d row=%d wrong write size=%d\n",
+			__func__, ebid, row_number, actual_write_len);
+		rc = -EINVAL;
+	}
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_upgrade_ttconfig
+ *
+ * SUMMARY: Program ttconfig_data with following steps:
+ *  1) Suspend scanning
+ *  2) Write data to the data block
+ *  3) Verify the crc for data block
+ *  4) Resume scanning
+ *  5) Set up call back for calibration if required
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *dev            - pointer to device structure
+ *  *ttconfig_data  - pointer to the config data to write to data block
+ *   ttconfig_size  - size of config data to write
+ ******************************************************************************/
+static int pt_upgrade_ttconfig(struct device *dev,
+		const u8 *ttconfig_data, int ttconfig_size)
+{
+	struct pt_loader_data *ld = pt_get_loader_data(dev);
+	bool wait_for_calibration_complete = false;
+	u8 ebid = PT_TCH_PARM_EBID;
+	u16 row_size = PT_DATA_ROW_SIZE;
+	u16 table_size;
+	u16 row_count;
+	u16 residue;
+	u8 *row_buf;
+	u8 verify_crc_status;
+	u16 calculated_crc;
+	u16 stored_crc;
+	int rc = 0;
+	int i;
+
+	table_size = ttconfig_size;
+	row_count = table_size / row_size;
+	row_buf = (u8 *)ttconfig_data;
+	pt_debug(dev, DL_INFO, "%s: size:%d row_size=%d row_count=%d\n",
+		__func__, table_size, row_size, row_count);
+
+	pm_runtime_get_sync(dev);
+
+	rc = cmd->request_exclusive(dev, PT_LDR_REQUEST_EXCLUSIVE_TIMEOUT);
+	if (rc < 0)
+		goto exit;
+
+	rc = cmd->nonhid_cmd->suspend_scanning(dev, 0);
+	if (rc < 0)
+		goto release;
+
+	for (i = 0; i < row_count; i++) {
+		pt_debug(dev, DL_INFO, "%s: row=%d size=%d\n",
+			__func__, i, row_size);
+		rc = pt_write_config_row_(dev, ebid, i, row_size,
+				row_buf);
+		if (rc) {
+			pt_debug(dev, DL_ERROR, "%s: Fail put row=%d r=%d\n",
+				__func__, i, rc);
+			break;
+		}
+		row_buf += row_size;
+	}
+	if (!rc) {
+		residue = table_size % row_size;
+		pt_debug(dev, DL_WARN, "%s: row=%d size=%d\n",
+			__func__, i, residue);
+		rc = pt_write_config_row_(dev, ebid, i, residue,
+				row_buf);
+		row_count++;
+		if (rc)
+			pt_debug(dev, DL_ERROR, "%s: Fail put row=%d r=%d\n",
+				__func__, i, rc);
+	}
+
+	if (!rc)
+		pt_debug(dev, DL_WARN,
+			"%s: TT_CFG updated: rows:%d bytes:%d\n",
+			__func__, row_count, table_size);
+
+	rc = cmd->nonhid_cmd->verify_cfg_block_crc(dev, 0, ebid,
+			&verify_crc_status, &calculated_crc, &stored_crc);
+	if (rc || verify_crc_status)
+		pt_debug(dev, DL_ERROR,
+			"%s: CRC Failed, ebid=%d, status=%d, scrc=%X ccrc=%X\n",
+			__func__, ebid, verify_crc_status,
+			calculated_crc, stored_crc);
+	else
+		pt_debug(dev, DL_INFO,
+			"%s: CRC PASS, ebid=%d, status=%d, scrc=%X ccrc=%X\n",
+			__func__, ebid, verify_crc_status,
+			calculated_crc, stored_crc);
+
+	rc = cmd->nonhid_cmd->resume_scanning(dev, 0);
+	if (rc < 0)
+		goto release;
+
+	if (ld->loader_pdata &&
+			(ld->loader_pdata->flags
+			 & PT_LOADER_FLAG_CALIBRATE_AFTER_TTCONFIG_UPGRADE)) {
+#if (KERNEL_VERSION(3, 13, 0) <= LINUX_VERSION_CODE)
+		reinit_completion(&ld->calibration_complete);
+#else
+		INIT_COMPLETION(ld->calibration_complete);
+#endif
+		/* set up call back for startup */
+		pt_debug(dev, DL_INFO, "%s: Adding callback for calibration\n",
+			__func__);
+		rc = cmd->subscribe_attention(dev, PT_ATTEN_STARTUP,
+			PT_LOADER_NAME, pt_calibration_attention, 0);
+		if (rc) {
+			pt_debug(dev, DL_ERROR,
+				"%s: Failed adding callback for calibration\n",
+				__func__);
+			pt_debug(dev, DL_ERROR,
+				"%s: No calibration will be performed\n",
+				__func__);
+			rc = 0;
+		} else
+			wait_for_calibration_complete = true;
+	}
+
+release:
+	cmd->release_exclusive(dev);
+
+exit:
+	if (!rc)
+		cmd->request_enum(dev, true);
+
+	pm_runtime_put_sync(dev);
+
+	if (wait_for_calibration_complete)
+		wait_for_completion(&ld->calibration_complete);
+
+	return rc;
+}
+#endif /* PT_TTCONFIG_UPGRADE */
+
+#ifdef CONFIG_TOUCHSCREEN_PARADE_PLATFORM_TTCONFIG_UPGRADE
+/*******************************************************************************
+ * FUNCTION: pt_get_ttconfig_crc
+ *
+ * SUMMARY: Get crc from ttconfig data.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *dev            - pointer to device structure
+ *  *ttconfig_data  - pointer to the config data
+ *   ttconfig_size  - size of config data
+ *  *crc            - pointer to the crc of configure to be stored
+ ******************************************************************************/
+static int pt_get_ttconfig_crc(struct device *dev,
+		const u8 *ttconfig_data, int ttconfig_size, u16 *crc)
+{
+	u16 crc_loc;
+
+	crc_loc = get_unaligned_le16(&ttconfig_data[2]);
+	if (ttconfig_size < crc_loc + 2)
+		return -EINVAL;
+
+	*crc = get_unaligned_le16(&ttconfig_data[crc_loc]);
+
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_get_ttconfig_version
+ *
+ * SUMMARY: Get version number from ttconfig data.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *dev            - pointer to device structure
+ *  *ttconfig_data  - pointer to the config data
+ *   ttconfig_size  - size of config data
+ *  *version        - pointer to the version of configure to be stored
+ ******************************************************************************/
+static int pt_get_ttconfig_version(struct device *dev,
+		const u8 *ttconfig_data, int ttconfig_size, u16 *version)
+{
+	if (ttconfig_size < PT_TTCONFIG_VERSION_OFFSET
+			+ PT_TTCONFIG_VERSION_SIZE)
+		return -EINVAL;
+
+	*version = get_unaligned_le16(
+		&ttconfig_data[PT_TTCONFIG_VERSION_OFFSET]);
+
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_check_ttconfig_version
+ *
+ * SUMMARY: Check the configure version and crc value to determine whether to
+ *  upgrade,the upgrade conditions as following:
+ *      1) To upgrade if the config version is newer than current config, but
+ *         this check is based on the flag in loader plarform data.
+ *      2) To upgrade if config CRC is different.
+ *      3) Don't upgrade when can't match any of above conditions.
+ *
+ * RETURN:
+ *   0: Don't upgrade
+ *   1: Upgrade
+ *
+ * PARAMETERS:
+ *  *dev            - pointer to device structure
+ *  *ttconfig_data  - pointer to the config data
+ *   ttconfig_size  - size of config data
+ ******************************************************************************/
+static int pt_check_ttconfig_version(struct device *dev,
+		const u8 *ttconfig_data, int ttconfig_size)
+{
+	struct pt_loader_data *ld = pt_get_loader_data(dev);
+	u16 cfg_crc_new;
+	int rc;
+
+	if (!ld->si)
+		return 0;
+
+	/* Check for config version */
+	if (ld->loader_pdata->flags &
+			PT_LOADER_FLAG_CHECK_TTCONFIG_VERSION) {
+		u16 cfg_ver_new;
+
+		rc = pt_get_ttconfig_version(dev, ttconfig_data,
+				ttconfig_size, &cfg_ver_new);
+		if (rc)
+			return 0;
+
+		pt_debug(dev, DL_INFO, "%s: img_ver:0x%04X new_ver:0x%04X\n",
+			__func__, ld->si->ttdata.fw_ver_conf, cfg_ver_new);
+
+		/* Check if config version is newer */
+		if (cfg_ver_new > ld->si->ttdata.fw_ver_conf) {
+			pt_debug(dev, DL_WARN,
+			"%s: Config version newer, will upgrade\n", __func__);
+			return 1;
+		}
+
+		pt_debug(dev, DL_WARN,
+			"%s: Config version is identical or older, will NOT upgrade\n",
+			__func__);
+	/* Check for config CRC */
+	} else {
+		rc = pt_get_ttconfig_crc(dev, ttconfig_data,
+				ttconfig_size, &cfg_crc_new);
+		if (rc)
+			return 0;
+
+		pt_debug(dev, DL_INFO, "%s: img_crc:0x%04X new_crc:0x%04X\n",
+			__func__, ld->si->ttconfig.crc, cfg_crc_new);
+
+		if (cfg_crc_new != ld->si->ttconfig.crc) {
+			pt_debug(dev, DL_WARN,
+				"%s: Config CRC different, will upgrade\n",
+				__func__);
+			return 1;
+		}
+
+		pt_debug(dev, DL_WARN,
+			"%s: Config CRC equal, will NOT upgrade\n", __func__);
+	}
+
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_check_ttconfig_version_platform
+ *
+ * SUMMARY: To call the function pt_check_ttconfig_version() to determine
+ *  whether to load config if the firmware version match with current firmware.
+ *
+ * RETURN:
+ *   0: Don't upgrade
+ *   1: Upgrade
+ *
+ * PARAMETERS:
+ *  *dev       - pointer to device structure
+ *  *ttconfig  - pointer to touch_config structure
+ ******************************************************************************/
+static int pt_check_ttconfig_version_platform(struct device *dev,
+		struct pt_touch_config *ttconfig)
+{
+	struct pt_loader_data *ld = pt_get_loader_data(dev);
+	u32 fw_ver_config;
+	u32 fw_revctrl_config;
+
+	if (!ld->si) {
+		pt_debug(dev, DL_INFO,
+			"%s: No firmware info found, DUT FW may be corrupted\n",
+			__func__);
+		return 0;
+	}
+
+	fw_ver_config = get_unaligned_be16(ttconfig->fw_ver + 2);
+	/* 4 middle bytes are not used */
+	fw_revctrl_config = get_unaligned_be32(ttconfig->fw_ver + 8);
+
+	/* FW versions should match */
+	if (pt_check_firmware_version(dev, fw_ver_config,
+			fw_revctrl_config)) {
+		pt_debug(dev, DL_ERROR,
+			"%s: FW versions mismatch\n", __func__);
+		return 0;
+	}
+
+	/* Check PowerOn Self Test, TT_CFG CRC bit */
+	if ((ld->si->ttdata.post_code & PT_POST_TT_CFG_CRC_MASK) == 0) {
+		pt_debug(dev, DL_ERROR,
+			"%s: POST, TT_CFG failed (%X), will upgrade\n",
+			__func__, ld->si->ttdata.post_code);
+		return 1;
+	}
+
+	return pt_check_ttconfig_version(dev, ttconfig->param_regs->data,
+			ttconfig->param_regs->size);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_get_platform_ttconfig
+ *
+ * SUMMARY: To get the pointer of right touch_config structure by panel id.
+ *
+ * RETURN:
+ *   pointer to touch_config structure or null pointer if fail
+ *
+ * PARAMETERS:
+ *  *dev  - pointer to device structure
+ ******************************************************************************/
+static struct pt_touch_config *pt_get_platform_ttconfig(
+		struct device *dev)
+{
+	struct pt_loader_data *ld = pt_get_loader_data(dev);
+	struct pt_touch_config **ttconfigs;
+	struct pt_touch_config *ttconfig;
+	u8 panel_id;
+
+	panel_id = pt_get_panel_id(dev);
+	if (panel_id == PANEL_ID_NOT_ENABLED) {
+		/* TODO: Make debug message */
+		pt_debug(dev, DL_INFO,
+			"%s: Panel ID not enabled, using legacy ttconfig\n",
+			__func__);
+		return ld->loader_pdata->ttconfig;
+	}
+
+	ttconfigs = ld->loader_pdata->ttconfigs;
+	if (!ttconfigs)
+		return NULL;
+
+	/* Find TT config according to the Panel ID */
+	while ((ttconfig = *ttconfigs++)) {
+		if (ttconfig->panel_id == panel_id) {
+			/* TODO: Make debug message */
+			pt_debug(dev, DL_INFO,
+				"%s: Found matching ttconfig:%p with Panel ID: 0x%02X\n",
+				__func__, ttconfig, ttconfig->panel_id);
+			return ttconfig;
+		}
+		pt_debug(dev, DL_ERROR,
+			"%s: Found mismatching ttconfig:%p with Panel ID: 0x%02X\n",
+			__func__, ttconfig, ttconfig->panel_id);
+	}
+
+	return NULL;
+}
+
+/*******************************************************************************
+ * FUNCTION: upgrade_ttconfig_from_platform
+ *
+ * SUMMARY: Get touch_firmware structure and perform upgrade if pass the
+ *  firmware version check.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *dev     - pointer to device structure
+ *   forced  - flag to force upgrade(1:force to upgrade)
+ ******************************************************************************/
+static int upgrade_ttconfig_from_platform(struct device *dev)
+{
+	struct pt_loader_data *ld = pt_get_loader_data(dev);
+	struct pt_touch_config *ttconfig;
+	struct touch_settings *param_regs;
+
+	int rc = -ENODEV;
+	int upgrade;
+
+	if (!ld->loader_pdata) {
+		pt_debug(dev, DL_ERROR,
+			"%s: No loader platform data\n", __func__);
+		return rc;
+	}
+
+	ttconfig = pt_get_platform_ttconfig(dev);
+	if (!ttconfig) {
+		pt_debug(dev, DL_ERROR, "%s: No ttconfig data\n", __func__);
+		return rc;
+	}
+
+	param_regs = ttconfig->param_regs;
+	if (!param_regs) {
+		pt_debug(dev, DL_ERROR, "%s: No touch parameters\n",
+			__func__);
+		return rc;
+	}
+
+	if (!param_regs->data || !param_regs->size) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Invalid touch parameters\n", __func__);
+		return rc;
+	}
+
+	if (!ttconfig->fw_ver || !ttconfig->fw_vsize) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Invalid FW version for touch parameters\n",
+			__func__);
+		return rc;
+	}
+
+	upgrade = pt_check_ttconfig_version_platform(dev, ttconfig);
+	if (upgrade)
+		return pt_upgrade_ttconfig(dev, param_regs->data,
+				param_regs->size);
+
+	return rc;
+}
+#endif /* CONFIG_TOUCHSCREEN_PARADE_PLATFORM_TTCONFIG_UPGRADE */
+
+#ifdef CONFIG_TOUCHSCREEN_PARADE_MANUAL_TTCONFIG_UPGRADE
+/*******************************************************************************
+ * FUNCTION: pt_config_data_write
+ *
+ * SUMMARY: The write method for the config_data sysfs node. The passed
+ *  in data (config file) is written to the config_data buffer.
+ *
+ * RETURN: Size of passed in buffer is success
+ *
+ * PARAMETERS:
+ *  *filp     - pointer to file structure
+ *  *kobj     - pointer to kobject structure
+ *  *bin_attr - pointer to bin_attribute structure
+ *   buf      - pointer to cmd input buffer
+ *   offset   - offset index to store input buffer
+ *   count    - size of data in buffer
+ ******************************************************************************/
+static ssize_t pt_config_data_write(struct file *filp,
+		struct kobject *kobj, struct bin_attribute *bin_attr,
+		char *buf, loff_t offset, size_t count)
+{
+	struct device *dev = container_of(kobj, struct device, kobj);
+	struct pt_loader_data *data = pt_get_loader_data(dev);
+	u8 *p;
+
+	pt_debug(dev, DL_INFO, "%s: offset:%lld count:%zu\n",
+		__func__, offset, count);
+
+	mutex_lock(&data->config_lock);
+
+	if (!data->config_loading) {
+		mutex_unlock(&data->config_lock);
+		return -ENODEV;
+	}
+
+	p = krealloc(data->config_data, offset + count, GFP_KERNEL);
+	if (!p) {
+		kfree(data->config_data);
+		data->config_data = NULL;
+		mutex_unlock(&data->config_lock);
+		return -ENOMEM;
+	}
+	data->config_data = p;
+
+	memcpy(&data->config_data[offset], buf, count);
+	data->config_size += count;
+
+	mutex_unlock(&data->config_lock);
+
+	return count;
+}
+
+static struct bin_attribute bin_attr_config_data = {
+	.attr = {
+		.name = "config_data",
+		.mode = 0200,
+	},
+	.size = 0,
+	.write = pt_config_data_write,
+};
+
+/*******************************************************************************
+ * FUNCTION: pt_verify_ttconfig_binary
+ *
+ * SUMMARY: Perform a simple size check if the firmware version match.And
+ *  calculate the start pointer of config data to write and the size to write.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *   *dev              - pointer to device structure
+ *   *bin_config_data  - pointer to binary config data
+ *    bin_config_size  - size of binary config data
+ *  **start            - double pointer to config data where to be written
+ *   *len              - pointer to the size of config data to store
+ ******************************************************************************/
+static int pt_verify_ttconfig_binary(struct device *dev,
+		u8 *bin_config_data, int bin_config_size, u8 **start, int *len)
+{
+	struct pt_loader_data *ld = pt_get_loader_data(dev);
+	int header_size;
+	u16 config_size;
+	u32 fw_ver_config;
+	u32 fw_revctrl_config;
+
+	if (!ld->si) {
+		pt_debug(dev, DL_ERROR,
+			"%s: No firmware info found, DUT FW may be corrupted\n",
+			__func__);
+		return -ENODEV;
+	}
+
+	/*
+	 * We need 11 bytes for FW version control info and at
+	 * least 6 bytes in config (Length + Max Length + CRC)
+	 */
+	header_size = bin_config_data[0] + 1;
+	if (header_size < 11 || header_size >= bin_config_size - 6) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Invalid header size %d\n", __func__,
+			header_size);
+		return -EINVAL;
+	}
+
+	fw_ver_config = get_unaligned_be16(&bin_config_data[1]);
+	/* 4 middle bytes are not used */
+	fw_revctrl_config = get_unaligned_be32(&bin_config_data[7]);
+
+	/* FW versions should match */
+	if (pt_check_firmware_version(dev, fw_ver_config,
+			fw_revctrl_config)) {
+		pt_debug(dev, DL_ERROR,
+			"%s: FW versions mismatch\n", __func__);
+		return -EINVAL;
+	}
+
+	config_size = get_unaligned_le16(&bin_config_data[header_size]);
+	/* Perform a simple size check (2 bytes for CRC) */
+	if (config_size != bin_config_size - header_size - 2) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Config size invalid\n", __func__);
+		return -EINVAL;
+	}
+
+	*start = &bin_config_data[header_size];
+	*len = bin_config_size - header_size;
+
+	return 0;
+}
+
+/*
+ * 1: Start loading TT Config
+ * 0: End loading TT Config and perform upgrade
+ *-1: Exit loading
+ */
+
+/*******************************************************************************
+ * FUNCTION: pt_config_loading_store
+ *
+ * SUMMARY: The store method for the config_loading sysfs node. The
+ *  passed in value controls if config loading is performed.
+ *
+ * RETURN: Size of passed in buffer is success
+ *
+ * PARAMETERS:
+ *  *dev  - pointer to device structure
+ *  *attr - pointer to device attributes
+ *  *buf  - pointer to buffer that hold the command parameters
+ *   size - size of buf
+ ******************************************************************************/
+static ssize_t pt_config_loading_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	struct pt_loader_data *ld = pt_get_loader_data(dev);
+	long value;
+	u8 *start;
+	int length;
+	int rc;
+
+	rc = kstrtol(buf, 10, &value);
+	if (rc < 0 || value < -1 || value > 1) {
+		pt_debug(dev, DL_ERROR, "%s: Invalid value\n", __func__);
+		return size;
+	}
+
+	mutex_lock(&ld->config_lock);
+
+	if (value == 1)
+		ld->config_loading = true;
+	else if (value == -1)
+		ld->config_loading = false;
+	else if (value == 0 && ld->config_loading) {
+		ld->config_loading = false;
+		if (ld->config_size == 0) {
+			pt_debug(dev, DL_ERROR,
+				"%s: No config data\n", __func__);
+			goto exit_free;
+		}
+
+		rc = pt_verify_ttconfig_binary(dev,
+				ld->config_data, ld->config_size,
+				&start, &length);
+		if (rc)
+			goto exit_free;
+
+		rc = pt_upgrade_ttconfig(dev, start, length);
+	}
+
+exit_free:
+	kfree(ld->config_data);
+	ld->config_data = NULL;
+	ld->config_size = 0;
+
+	mutex_unlock(&ld->config_lock);
+
+	if (rc)
+		return rc;
+
+	return size;
+}
+
+static DEVICE_ATTR(config_loading, 0200,
+	NULL, pt_config_loading_store);
+#endif /* CONFIG_TOUCHSCREEN_PARADE_MANUAL_TTCONFIG_UPGRADE */
+
+#ifdef CONFIG_TOUCHSCREEN_PARADE_PLATFORM_FW_UPGRADE
+/*******************************************************************************
+ * FUNCTION: pt_forced_upgrade_store
+ *
+ * SUMMARY: The store method for the forced_upgrade sysfs node. The firmware
+ *  loading is forced to performed with platform upgrade strategy.
+ *
+ * RETURN: Size of passed in buffer is success
+ *
+ * PARAMETERS:
+ *  *dev  - pointer to device structure
+ *  *attr - pointer to device attributes
+ *  *buf  - pointer to buffer that hold the command parameters
+ *   size - size of buf
+ ******************************************************************************/
+static ssize_t pt_forced_upgrade_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	int rc = upgrade_firmware_from_platform(dev, true);
+
+	if (rc)
+		return rc;
+	return size;
+}
+
+static DEVICE_ATTR(forced_upgrade, 0200,
+	NULL, pt_forced_upgrade_store);
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE
+/*******************************************************************************
+ * FUNCTION: pt_manual_upgrade_store
+ *
+ * SUMMARY: The store method for the forced_upgrade sysfs node that it is
+ *  caller for function upgrade_firmware_from_class() to allow upgrade firmware
+ *  manually.
+ *
+ * RETURN: Size of passed in buffer is success
+ *
+ * PARAMETERS:
+ *  *dev  - pointer to device structure
+ *  *attr - pointer to device attributes
+ *  *buf  - pointer to buffer that hold the command parameters
+ *   size - size of buf
+ ******************************************************************************/
+static ssize_t pt_manual_upgrade_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	struct pt_loader_data *ld = pt_get_loader_data(dev);
+	u32 input_data[2] = {0};
+	int length;
+	int rc = 0;
+
+	length = cmd->parse_sysfs_input(dev, buf, size, input_data,
+			ARRAY_SIZE(input_data));
+
+	if (length != 1) {
+		pt_debug(dev, DL_WARN, "%s: Invalid number of arguments\n",
+			__func__);
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	if (input_data[0] < 0 || input_data[0] > 1) {
+		pt_debug(dev, DL_WARN, "%s: Invalid arguments\n", __func__);
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	_pt_pip2_update_bl_status(dev, UPDATE_FW_IDLE, PT_NO_INC);
+	if (ld->is_manual_upgrade_enabled) {
+		rc = -EBUSY;
+		goto exit;
+	}
+
+	ld->is_manual_upgrade_enabled = 1;
+
+	rc = upgrade_firmware_from_class(ld->dev);
+	if (rc < 0)
+		ld->is_manual_upgrade_enabled = 0;
+
+exit:
+	if (rc)
+		return rc;
+	return size;
+}
+
+static DEVICE_ATTR(manual_upgrade, 0200, NULL, pt_manual_upgrade_store);
+#endif /* CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE */
+
+/*******************************************************************************
+ * FUNCTION: pt_fw_and_config_upgrade
+ *
+ * SUMMARY: Perform all methods for firmware upgrade and config upgrade
+ *  according to the definition of macro.
+ *
+ * PARAMETERS:
+ *  *work_struct  - pointer to work_struct structure
+ ******************************************************************************/
+static void pt_fw_and_config_upgrade(
+		struct work_struct *fw_and_config_upgrade)
+{
+	struct pt_loader_data *ld = container_of(fw_and_config_upgrade,
+			struct pt_loader_data, fw_and_config_upgrade);
+	struct device *dev = ld->dev;
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	int retry = 200;
+#if PT_FW_UPGRADE \
+	|| defined(CONFIG_TOUCHSCREEN_PARADE_PLATFORM_TTCONFIG_UPGRADE)
+	u8 dut_gen = cmd->request_dut_generation(dev);
+#endif
+
+	/*
+	 * Since the resume_scan command in pt_probe_complete() has
+	 * no protection,it may cause problem for the commands in fw
+	 * upgrade process during probe. Waiting for the probe to
+	 * complete before performing fw upgrade can avoid this failure.
+	 */
+	while (!cd->core_probe_complete && retry--)
+		msleep(20);
+
+	ld->si = cmd->request_sysinfo(dev);
+	if (!ld->si)
+		pt_debug(dev, DL_ERROR,
+			"%s: Fail get sysinfo pointer from core\n",
+			__func__);
+#if !PT_FW_UPGRADE
+	pt_debug(dev, DL_INFO,
+		"%s: No FW upgrade method selected!\n", __func__);
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_PARADE_PLATFORM_FW_UPGRADE
+	if (dut_gen == DUT_PIP1_ONLY) {
+		if (!upgrade_firmware_from_platform(dev, false))
+			return;
+	}
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE
+	if (dut_gen == DUT_PIP2_CAPABLE) {
+		if (!pt_pip2_upgrade_firmware_from_builtin(dev))
+			return;
+		pt_debug(dev, DL_WARN, "%s: Builtin FW upgrade failed\n",
+			__func__);
+	} else {
+		if (!upgrade_firmware_from_builtin(dev))
+			return;
+	}
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_PARADE_PLATFORM_TTCONFIG_UPGRADE
+	if (dut_gen == DUT_PIP1_ONLY) {
+		if (!upgrade_ttconfig_from_platform(dev))
+			return;
+	}
+#endif
+}
+
+#ifdef CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE
+#ifdef TTDL_DIAGNOSTICS
+/*******************************************************************************
+ * FUNCTION: _pt_pip2_get_flash_info
+ *
+ * SUMMARY: Sends a FLASH_INFO command to the DUT logging the results to kmsg
+ *
+ * PARAMETERS:
+ *	*dev - pointer to device structure
+ *	*read_buf - pointer to the read buffer array to store the response
+ ******************************************************************************/
+static void _pt_pip2_get_flash_info(struct device *dev,
+	u8 *read_buf)
+{
+	u16 actual_read_len;
+	int ret;
+
+	/* Get flash info for debugging information */
+	ret = cmd->nonhid_cmd->pip2_send_cmd(dev,
+		PT_CORE_CMD_UNPROTECTED, PIP2_CMD_ID_FLASH_INFO,
+		NULL, 0, read_buf, &actual_read_len);
+	if (!ret) {
+		pt_debug(dev, DL_DEBUG,
+			"%s --- FLASH Information ---\n", __func__);
+		pt_debug(dev, DL_DEBUG,
+			"%s Manufacturer ID: 0x%02x\n",
+			__func__, read_buf[PIP2_RESP_BODY_OFFSET]);
+		pt_debug(dev, DL_DEBUG,
+			"%s Memory Type    : 0x%02x\n",
+			__func__, read_buf[PIP2_RESP_BODY_OFFSET + 1]);
+		pt_debug(dev, DL_DEBUG,
+			"%s Num Sectors    : 0x%02x%02x%02x%02x\n",
+			__func__, read_buf[PIP2_RESP_BODY_OFFSET + 2],
+			read_buf[PIP2_RESP_BODY_OFFSET + 3],
+			read_buf[PIP2_RESP_BODY_OFFSET + 4],
+			read_buf[PIP2_RESP_BODY_OFFSET + 5]);
+		pt_debug(dev, DL_DEBUG,
+			"%s Sectors Size   : 0x%02x%02x%02x%02x\n",
+			__func__, read_buf[PIP2_RESP_BODY_OFFSET + 6],
+			read_buf[PIP2_RESP_BODY_OFFSET + 7],
+			read_buf[PIP2_RESP_BODY_OFFSET + 8],
+			read_buf[PIP2_RESP_BODY_OFFSET + 9]);
+		pt_debug(dev, DL_DEBUG,
+			"%s Page Size      : 0x%02x%02x%02x%02x\n",
+			__func__, read_buf[PIP2_RESP_BODY_OFFSET + 10],
+			read_buf[PIP2_RESP_BODY_OFFSET + 11],
+			read_buf[PIP2_RESP_BODY_OFFSET + 12],
+			read_buf[PIP2_RESP_BODY_OFFSET + 13]);
+		if (actual_read_len > 21) {
+			pt_debug(dev, DL_DEBUG,
+				"%s Status Reg1    : 0x%02x\n",
+				__func__,
+				read_buf[PIP2_RESP_BODY_OFFSET + 14]);
+			pt_debug(dev, DL_DEBUG,
+				"%s Status Reg2    : 0x%02x\n",
+				__func__,
+				read_buf[PIP2_RESP_BODY_OFFSET + 15]);
+		}
+	}
+}
+#endif /* TTDL_DIAGNOSTICS */
+
+/*******************************************************************************
+ * FUNCTION: _pt_pip2_log_last_error
+ *
+ * SUMMARY: Sends a STATUS command to the DUT logging the results until all
+ *	errors are cleared. Also sends a GET_LAST_ERRNO to get any Boot errors.
+ *	This must be sent after the STATUS flush in order not to have this
+ *	command cause another error.
+ *
+ * NOTE: This function support No Interrupt Solution and switch "pip2_send_cmd"
+ *  function according to the global variable "bl_with_no_int".
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*dev - pointer to device structure
+ *	*read_buf - pointer to the read buffer array to store the response
+ ******************************************************************************/
+static int _pt_pip2_log_last_error(struct device *dev,
+	u8 *read_buf)
+{
+	u16 actual_read_len;
+	u8 loop = 5;
+	u8 info = 0xFF;
+	u8 error = 0xFF;
+	int ret;
+	PIP2_SEND_CMD pip2_send_cmd;
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+
+	if (cd->bl_with_no_int)
+		pip2_send_cmd = cmd->nonhid_cmd->pip2_send_cmd_no_int;
+	else
+		pip2_send_cmd = cmd->nonhid_cmd->pip2_send_cmd;
+	/*
+	 * Send the STATUS command until no errors are found.
+	 * The BL will store an error code for each layer of the stack,
+	 * and each read will return one error.
+	 */
+	while (loop > 0 && error) {
+
+		ret = pip2_send_cmd(dev,
+			PT_CORE_CMD_UNPROTECTED, PIP2_CMD_ID_STATUS,
+			NULL, 0, read_buf, &actual_read_len);
+
+		if (!ret) {
+			info  = (u8)read_buf[PIP2_RESP_BODY_OFFSET];
+			error = (u8)read_buf[PIP2_RESP_BODY_OFFSET + 1];
+
+			pt_debug(dev, DL_ERROR,
+				"%s: STATUS: Status=0x%02X BOOT=%d BUSY=%d INT=%d ERR_PHY=%d ERR_REG=%d ERROR=0x%02X",
+				__func__,
+				(u8)read_buf[PIP2_RESP_STATUS_OFFSET],
+				info & 0x01,
+				(info & 0x02) >> 1,
+				(info & 0x04) >> 2,
+				(info & 0x18) >> 3,
+				(info & 0xE0) >> 5,
+				error);
+		}
+		loop--;
+	}
+
+	/* Send the GET_LAST_ERROR command to get the last BL startup error */
+	ret = pip2_send_cmd(dev,
+		PT_CORE_CMD_UNPROTECTED, PIP2_CMD_ID_GET_LAST_ERRNO,
+		NULL, 0, read_buf, &actual_read_len);
+	if (!ret) {
+		pt_debug(dev, DL_ERROR,
+			"%s: GET_LAST_ERR: Status=0x%02X ERRNO=0x%02X BOOTMODE=%d\n",
+			__func__,
+			(u8)read_buf[PIP2_RESP_STATUS_OFFSET],
+			(u8)read_buf[PIP2_RESP_BODY_OFFSET],
+			(u8)read_buf[PIP2_RESP_BODY_OFFSET + 1]);
+	}
+
+	return ret;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_pip2_check_fw_ver
+ *
+ * SUMMARY: Compare the FW version in the bin file to the current FW. If the
+ *	FW version in the bin file is greater an upgrade should be done.
+ *
+ * RETURN:
+ *  -1: Do not upgrade firmware
+ *   0: Version info same, let caller decide
+ *   1: Do a firmware upgrade
+ *
+ * PARAMETERS:
+ *	*dev - pointer to device structure
+ *	*fw  - pointer to the new FW image to load
+ *	*hdr - pointer to the PIP2 bin file hdr structure
+ ******************************************************************************/
+static int _pt_pip2_check_fw_ver(struct device *dev,
+	const struct firmware *fw, struct pt_bin_file_hdr *hdr)
+{
+	u8 img_app_major_ver = fw->data[3];
+	u8 img_app_minor_ver = fw->data[4];
+	u32 img_app_rev_ctrl  = fw->data[9]<<24 | fw->data[10]<<16 |
+			    fw->data[11]<<8 | fw->data[12];
+
+	pt_debug(dev, DL_WARN,
+		"%s ATM - BL Image Version:   %02x.%02x.%d\n",
+		__func__, img_app_major_ver, img_app_minor_ver,
+		img_app_rev_ctrl);
+
+	pt_debug(dev, DL_WARN,
+		"%s ATM - Current FW Version: %02X.%02X.%d\n",
+		__func__, hdr->fw_major, hdr->fw_minor, hdr->fw_rev_ctrl);
+
+	if ((256 * img_app_major_ver + img_app_minor_ver) >
+	    (256 * hdr->fw_major + hdr->fw_minor)) {
+		pt_debug(dev, DL_WARN,
+			"ATM - bin file version > FW, will upgrade FW");
+		return 1;
+	}
+
+	if ((256 * img_app_major_ver + img_app_minor_ver) <
+	    (256 * hdr->fw_major + hdr->fw_minor)) {
+		pt_debug(dev, DL_WARN,
+			"ATM - bin file version < FW, will NOT upgrade FW");
+		return -EPERM;
+	}
+
+	if (img_app_rev_ctrl > hdr->fw_rev_ctrl) {
+		pt_debug(dev, DL_WARN,
+			"bin file rev ctrl > FW, will upgrade FW");
+		return 1;
+	}
+
+	if (img_app_rev_ctrl < hdr->fw_rev_ctrl) {
+		pt_debug(dev, DL_WARN,
+			"bin file rev ctrl > FW, will NOT upgrade FW");
+		return -EPERM;
+	}
+
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_pip2_check_config_ver
+ *
+ * SUMMARY: Compare the Config version in the bin file to the current FW. If the
+ *	config version in the bin file is greater an upgrade should be done.
+ *
+ * RETURN:
+ *  -1: Do not upgrade firmware
+ *   0: Version info same, let caller decide
+ *   1: Do a firmware upgrade
+ *
+ * PARAMETERS:
+ *	*dev - pointer to device structure
+ *	*fw  - pointer to the new FW image to load
+ *	*hdr - pointer to the PIP2 bin file hdr structure
+ ******************************************************************************/
+static int _pt_pip2_check_config_ver(struct device *dev,
+	const struct firmware *fw, struct pt_bin_file_hdr *hdr)
+{
+	u16 fw_config_ver_new;
+
+	/* Offset 17,18 is the TT Config version*/
+	fw_config_ver_new = get_unaligned_be16(fw->data + 17);
+	pt_debug(dev, DL_WARN,
+		"%s ATM - BL Image Config Version:   %d\n",
+		__func__, fw_config_ver_new);
+
+	pt_debug(dev, DL_WARN,
+		"%s ATM - Current Config Version:    %d\n",
+		__func__, hdr->config_ver);
+
+	if (fw_config_ver_new > hdr->config_ver) {
+		pt_debug(dev, DL_WARN,
+			"ATM - bin file Config version > FW, will upgrade FW");
+		return 1;
+	} else
+		pt_debug(dev, DL_WARN,
+			"ATM - bin file Config version <= FW, will NOT upgrade FW");
+
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_pip2_need_upgrade_due_to_fw_ver
+ *
+ * SUMMARY: Compare the FW version in the bin file to the current FW. If the
+ *	FW version in the bin file is greater an upgrade should be done.
+ *
+ * PARAMETERS:
+ *	*dev - pointer to device structure
+ *	*fw  - pointer to the new FW image to load
+ ******************************************************************************/
+static u8 _pt_pip2_need_upgrade_due_to_fw_ver(struct device *dev,
+	const struct firmware *fw)
+{
+	int ret;
+	u8 should_upgrade = 0;
+	struct pt_bin_file_hdr hdr = {0};
+
+	ret = cmd->request_pip2_bin_hdr(dev, &hdr);
+	if (ret != 0 || hdr.fw_major == 0xFF) {
+		pt_debug(dev, DL_WARN,
+			"App ver info not available, will upgrade FW");
+		should_upgrade = 1;
+		goto exit;
+	}
+
+	ret = _pt_pip2_check_fw_ver(dev, fw, &hdr);
+	if (ret == 0)
+		ret = _pt_pip2_check_config_ver(dev, fw, &hdr);
+
+	if (ret > 0)
+		should_upgrade = 1;
+
+exit:
+	return should_upgrade;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_calibrate_flashless_dut
+ *
+ * SUMMARY: On a flashless DUT the FW would need to re-calibrate on every power
+ *	cycle, so to speed up the BL process the FW does the calibraiton on the
+ *	first power up and TTDL will read and store it in RAM to be able to
+ *	restore it on subsequent resets of the DUT.
+ *
+ * RETURN:
+ *       0 = success
+ *      !0 = failure
+ *
+ * PARAMETERS:
+ *	*dev              - pointer to device structure
+ ******************************************************************************/
+static int _pt_calibrate_flashless_dut(struct device *dev)
+{
+	u8 rc = 0;
+	u8 cal_status = 1;
+	u16 cal_size = 0;
+	struct pt_cal_ext_data cal_data = {0};
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_ttdata *ttdata = &cd->sysinfo.ttdata;
+	unsigned short cache_chip_id = 0;
+	unsigned short active_chip_id = 0;
+
+	cmd->nonhid_cmd->manage_cal_data(dev, PT_CAL_DATA_INFO, &cal_size,
+		&cache_chip_id);
+	pt_debug(dev, DL_WARN, "%s: Stored CAL   ID=0x%04X size=%d\n",
+		__func__, cache_chip_id, cal_size);
+
+	active_chip_id = cmd->nonhid_cmd->calc_crc((u8 *)&ttdata->chip_rev,
+			4 + PT_UID_SIZE);
+	pt_debug(dev, DL_WARN, "%s: Current Chip ID=0x%04X\n",
+		__func__, active_chip_id);
+
+	if (cal_size == 0 || active_chip_id != cache_chip_id) {
+		memset(&cal_data, 0, sizeof(struct pt_cal_ext_data));
+
+		/* Calibrate_ext will also save CAL Data in TTDL cache */
+		rc = cmd->nonhid_cmd->calibrate_ext(dev, 0, &cal_data,
+			&cal_status);
+		pt_debug(dev, DL_INFO,
+			"%s: Calibration Finished rc=%d\n", __func__, rc);
+
+		/* Afer successful calibration read the stored size */
+		if (!rc && cal_status == 0) {
+			rc = cmd->nonhid_cmd->manage_cal_data(dev,
+				PT_CAL_DATA_INFO, &cal_size, &cache_chip_id);
+			if (!rc) {
+				pt_debug(dev, DL_WARN,
+					"%s: First BL, Read %d Bytes of CAL\n",
+					__func__, cal_size);
+			} else {
+				pt_debug(dev, DL_ERROR,
+					"%s: First BL, Failed to read CAL\n",
+					__func__);
+			}
+		} else {
+			pt_debug(dev, DL_ERROR,
+				"%s: First BL, Calibration failed rc=%d\n",
+				__func__, rc);
+		}
+		rc = cmd->nonhid_cmd->resume_scanning(dev, 0);
+		if (rc)
+			pt_debug(dev, DL_ERROR,
+				"%s: First BL, Resume Scan failed rc=%d\n",
+				__func__, rc);
+	} else {
+		rc = cmd->nonhid_cmd->manage_cal_data(dev, PT_CAL_DATA_RESTORE,
+			&cal_size, &cache_chip_id);
+		if (!rc)
+			pt_debug(dev, DL_WARN, "%s: Restored CAL %d Bytes\n",
+				__func__, cal_size);
+		else
+			pt_debug(dev, DL_ERROR, "%s: Failed to restore CAL\n",
+				__func__);
+
+		rc = cmd->nonhid_cmd->resume_scanning(dev, 0);
+		if (rc)
+			pt_debug(dev, DL_ERROR,
+				"%s: Resume Scan failed rc=%d\n",
+				__func__, rc);
+	}
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_pip2_firmware_cont
+ *
+ * SUMMARY: Bootload the DUT with a FW image using the PIP2 protocol. This
+ *	includes getting the DUT into BL mode, writing the file to either SRAM
+ *	or FLASH, and launching the application directly in SRAM or by resetting
+ *	the DUT without the hostmode pin asserted.
+ *
+ *	NOTE: Special care must be taken to support a DUT communicating in
+ *		PIP2.0 where the length field is defined differently.
+ *	NOTE: The write packet len is set so that the overall packet size is
+ *		less than 255. The overhead is 9 bytes: 2 byte address (0101),
+ *		4 byte header, 1 byte file no. 2 byte CRC
+ *
+ * PARAMETERS:
+ *	*fw      - pointer to the new FW image to load
+ *	*context - pointer to the device
+ ******************************************************************************/
+static void _pt_pip2_firmware_cont(const struct firmware *fw,
+		void *context)
+{
+	u8 read_buf[PT_MAX_PIP2_MSG_SIZE];
+	u8 buf[PT_MAX_PIP2_MSG_SIZE];
+	u8 *fw_img = NULL;
+	u16 write_len;
+	u8 mode = PT_MODE_UNKNOWN;
+	u8 retry_packet = 0;
+	u8 us_fw_used = 0;
+	u16 actual_read_len;
+	u16 status = 0;
+	u16 packet_size;
+	int fw_size = 0;
+	int remain_bytes;
+	int ret = 0;
+	int percent_cmplt;
+	int t;
+	int erase_status;
+	u32 max_file_size;
+	bool wait_for_calibration_complete = false;
+	PIP2_SEND_CMD pip2_send_cmd;
+	struct device *dev = context;
+	struct pt_loader_data *ld = pt_get_loader_data(dev);
+	struct pip2_loader_data *pip2_data = ld->pip2_data;
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+
+	pt_debug(dev, DL_WARN, "%s: ATM - Begin BL\n", __func__);
+	_pt_pip2_update_bl_status(dev, UPDATE_FW_IDLE, PT_NO_INC);
+
+	if (cd->bus_ops->bustype == BUS_I2C)
+		packet_size = PIP2_BL_I2C_FILE_WRITE_LEN_PER_PACKET;
+	else
+		packet_size = PIP2_BL_SPI_FILE_WRITE_LEN_PER_PACKET;
+
+	ret = cmd->request_exclusive(dev, PT_LDR_REQUEST_EXCLUSIVE_TIMEOUT);
+	if (ret < 0) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Failed to aquire exclusive access\n", __func__);
+		update_fw_status = UPDATE_FW_EXCLUSIVE_ACCESS_ERROR;
+		goto exit;
+	}
+	_pt_pip2_update_bl_status(dev, 0, 1);
+
+	if (!fw) {
+		if (ld->pip2_load_builtin) {
+			pt_debug(dev, DL_ERROR,
+				"%s: No builtin firmware\n", __func__);
+			ld->builtin_bin_fw_status = -EINVAL;
+			pt_debug(dev, DL_ERROR, "%s: Exit BL\n", __func__);
+			_pt_pip2_update_bl_status(dev,
+				UPDATE_FW_NO_FW_PROVIDED, PT_NO_INC);
+			goto exit;
+		} else {
+			fw_img = kzalloc(PT_PIP2_MAX_FILE_SIZE, GFP_KERNEL);
+			us_fw_used = 1;
+			ret = cmd->nonhid_cmd->read_us_file(dev,
+				cd->pip2_us_file_path, fw_img, &fw_size);
+			if (ret) {
+				pt_debug(dev, DL_ERROR,
+					"%s: No firmware provided to load\n",
+					__func__);
+				pt_debug(dev, DL_ERROR, "%s: Exit BL\n",
+					__func__);
+				_pt_pip2_update_bl_status(dev,
+					UPDATE_FW_NO_FW_PROVIDED, PT_NO_INC);
+				goto exit;
+			}
+		}
+	} else {
+		fw_img = (u8 *)&(fw->data[0]);
+		fw_size = fw->size;
+	}
+
+	if (!fw_img || !fw_size) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Invalid fw or file size=%d\n", __func__,
+			(int)fw_size);
+		_pt_pip2_update_bl_status(dev, UPDATE_FW_INVALID_FW_IMAGE,
+			PT_NO_INC);
+		goto exit;
+	}
+	if (ld->pip2_load_file_no == PIP2_FW_FILE) {
+		if (fw_img[0] >= (fw_size + 1)) {
+			pt_debug(dev, DL_ERROR,
+				"%s: Firmware format is invalid\n", __func__);
+			_pt_pip2_update_bl_status(dev,
+				UPDATE_FW_INVALID_FW_IMAGE, PT_NO_INC);
+			goto exit;
+		}
+	}
+
+	cd->fw_updating = true;
+	wake_up(&cd->wait_q);
+
+	_pt_pip2_update_bl_status(dev, 0, 1);
+	pt_debug(dev, DL_INFO,
+		"%s: Found file of size: %d bytes\n", __func__, (int)fw_size);
+	pm_runtime_get_sync(dev);
+	_pt_pip2_update_bl_status(dev, 0, 1);
+
+	/* Wait for completion of FW upgrade thread before continuing */
+	if (!ld->pip2_load_builtin)
+		init_completion(&pip2_data->pip2_fw_upgrade_complete);
+
+	_pt_pip2_update_bl_status(dev, 0, 1);
+
+	cmd->request_stop_wd(dev);
+
+	if (cd->flashless_dut) {
+		cd->bl_pip_ver_ready = false;
+		cd->app_pip_ver_ready = false;
+	}
+
+	/*
+	 * 'mode' is used below, if DUT was already in BL before attempting to
+	 * enter the BL, there was either no FW to run or the FW was corrupt
+	 * so either way force a BL
+	 */
+	ret = cmd->request_pip2_enter_bl(dev, &mode, NULL);
+	if (ret) {
+		pt_debug(dev, DL_ERROR, "%s: Failed to enter BL\n",
+			__func__);
+		_pt_pip2_update_bl_status(dev, UPDATE_FW_ENTER_BL_ERROR,
+			PT_NO_INC);
+		goto exit;
+	}
+	_pt_pip2_update_bl_status(dev, 0, 1);
+
+	/* Only compare FW ver or previous mode when doing a built-in upgrade */
+	if (ld->pip2_load_builtin) {
+		if (_pt_pip2_need_upgrade_due_to_fw_ver(dev, fw) ||
+		    mode == PT_MODE_BOOTLOADER) {
+			_pt_pip2_update_bl_status(dev, 0, 1);
+		} else {
+			_pt_pip2_update_bl_status(dev,
+				UPDATE_FW_VERSION_ERROR, PT_NO_INC);
+			goto exit;
+		}
+	}
+
+	pt_debug(dev, DL_INFO, "%s OPEN File %d for write\n",
+		__func__, ld->pip2_load_file_no);
+	ret = cmd->nonhid_cmd->pip2_file_open(dev, ld->pip2_load_file_no);
+	if (ret < 0) {
+		pt_debug(dev, DL_ERROR, "%s Open file %d failed\n",
+			__func__, ld->pip2_load_file_no);
+		_pt_pip2_update_bl_status(dev, UPDATE_FW_FILE_OPEN_ERROR,
+			PT_NO_INC);
+		goto exit;
+	}
+	pip2_data->pip2_file_handle = ret;
+	_pt_pip2_update_bl_status(dev, 0, 1);
+
+	/* Regarding to TC3315, the size of RAM_FILE is less than fw image */
+	if (ld->pip2_load_file_no != PIP2_RAM_FILE) {
+		ret = cmd->nonhid_cmd->pip2_file_get_stats(dev,
+			ld->pip2_load_file_no, NULL, &max_file_size);
+		if (ret) {
+			pt_debug(dev, DL_ERROR,
+				"%s: Failed to get_file_state ret=%d\n",
+				__func__, ret);
+			goto exit_close_file;
+		}
+		if (fw_size > max_file_size) {
+			pt_debug(dev, DL_ERROR,
+				"%s: Firmware image(%d) is over size(%d)\n",
+					__func__, fw_size, max_file_size);
+			_pt_pip2_update_bl_status(dev,
+				UPDATE_FW_INVALID_FW_IMAGE, PT_NO_INC);
+			goto exit_close_file;
+		}
+#ifdef TTDL_DIAGNOSTICS
+		/* Log the Flash part info */
+		if (cd->debug_level >= DL_DEBUG)
+			_pt_pip2_get_flash_info(dev, read_buf);
+#endif
+		/* Erase file before loading */
+		ret = cmd->nonhid_cmd->pip2_file_erase(dev,
+			ld->pip2_load_file_no, &erase_status);
+		if (ret < 0) {
+			pt_debug(dev, DL_ERROR,
+				"%s: File erase failed rc=%d status=%d\n",
+				__func__, ret, erase_status);
+			_pt_pip2_update_bl_status(dev,
+				UPDATE_FW_ERASE_ERROR, PT_NO_INC);
+			goto exit_close_file;
+		}
+		_pt_pip2_update_bl_status(dev, 0, 5);
+	}
+
+	remain_bytes = fw_size;
+	buf[0] = pip2_data->pip2_file_handle;
+	pt_debug(dev, DL_WARN,
+		"%s: ATM - Writing %d bytes of firmware data now\n",
+		__func__, fw_size);
+
+	/*
+	 * No IRQ function is used to BL to reduce BL time due to any IRQ
+	 * latency.
+	 */
+	if (cd->bl_with_no_int) {
+		pip2_send_cmd = cmd->nonhid_cmd->pip2_send_cmd_no_int;
+		disable_irq_nosync(cd->irq);
+	} else
+		pip2_send_cmd = cmd->nonhid_cmd->pip2_send_cmd;
+
+	/* Continue writing while data remains */
+	while (remain_bytes > packet_size) {
+		write_len = packet_size;
+
+		/* Don't update BL status on every pass */
+		if (remain_bytes % 2000 < packet_size) {
+			/* Calculate % complete for update_fw_status sysfs */
+			percent_cmplt = (fw_size - remain_bytes) *
+					100 / fw_size;
+			if (percent_cmplt > 0 &&
+			    percent_cmplt > UPDATE_FW_ACTIVE_90)
+				percent_cmplt = UPDATE_FW_ACTIVE_90;
+			_pt_pip2_update_bl_status(dev, percent_cmplt,
+				PT_NO_INC);
+
+#ifdef TTDL_DIAGNOSTICS
+			pt_debug(dev, DL_INFO,
+				"Wrote %d bytes with %d bytes remaining\n",
+				fw_size - remain_bytes - write_len,
+				remain_bytes);
+#endif
+		}
+		if (retry_packet > 0) {
+#ifdef TTDL_DIAGNOSTICS
+			cd->bl_retry_packet_count++;
+			cmd->request_toggle_err_gpio(dev,
+				PT_ERR_GPIO_BL_RETRY_PACKET);
+			pt_debug(dev, DL_WARN,
+				"%s: === Retry Packet #%d ===\n",
+				__func__, retry_packet);
+#endif
+			/* Get and log the last error(s) */
+			_pt_pip2_log_last_error(dev, read_buf);
+		}
+
+		memcpy(&buf[1], fw_img, write_len);
+		ret = pip2_send_cmd(dev,
+			PT_CORE_CMD_UNPROTECTED, PIP2_CMD_ID_FILE_WRITE,
+			buf, write_len + 1, read_buf, &actual_read_len);
+		status = read_buf[PIP2_RESP_STATUS_OFFSET];
+
+		/* Write cmd successful with a fail status */
+		if (!ret && status) {
+			/*
+			 * The last time through the loop when remain_bytes =
+			 * write_len, no partial payload will remain, the last
+			 * successful write (when writing to RAM) will respond
+			 * with EOF status, writing to FLASH will respond with a
+			 * standard success status of 0x00
+			 */
+			if ((ld->pip2_load_file_no == PIP2_RAM_FILE) &&
+			    (status == PIP2_RSP_ERR_END_OF_FILE) &&
+			    (remain_bytes == write_len)) {
+				pt_debug(dev, DL_WARN,
+					"%s Last write, ret = 0x%02x\n",
+					__func__, status);
+				/* Drop out of the while loop */
+				break;
+			}
+			_pt_pip2_update_bl_status(dev,
+				UPDATE_FW_WRITE_ERROR, PT_NO_INC);
+			pt_debug(dev, DL_ERROR,
+				"%s file write failure, status = 0x%02x\n",
+				__func__, status);
+			if (retry_packet >= 3) {
+				/* Tripple retry error - break */
+				remain_bytes = 0;
+				pt_debug(dev, DL_ERROR,
+					"%s %d - Packet status error - Break\n",
+					__func__, status);
+			} else {
+				retry_packet++;
+			}
+		} else if (ret) {
+			/* Packet write failed - retry 3x */
+			if (retry_packet >= 3) {
+				remain_bytes = 0;
+				pt_debug(dev, DL_ERROR,
+					"%s %d - Packet cmd error - Break\n",
+					__func__, ret);
+			}
+			retry_packet++;
+		} else {
+			/* Cmd success and status success */
+			retry_packet = 0;
+		}
+
+		if (retry_packet == 0) {
+			fw_img += write_len;
+			remain_bytes -= write_len;
+		}
+	}
+	/* Write the remaining bytes if any remain */
+	if (remain_bytes > 0 && retry_packet == 0) {
+		pt_debug(dev, DL_INFO,
+			"Write last %d bytes to File = 0x%02x\n",
+			remain_bytes, pip2_data->pip2_file_handle);
+		memcpy(&buf[1], fw_img, remain_bytes);
+		while (remain_bytes > 0 && retry_packet <= 3) {
+			ret = pip2_send_cmd(dev, PT_CORE_CMD_UNPROTECTED,
+				PIP2_CMD_ID_FILE_WRITE, buf, remain_bytes + 1,
+				read_buf, &actual_read_len);
+			status = read_buf[PIP2_RESP_STATUS_OFFSET];
+			if (ret || status) {
+				/*
+				 * Any non zero status when writing to FLASH is
+				 * an error. Only when writing to RAM, the last
+				 * packet must respond with an EOF status
+				 */
+				if ((ld->pip2_load_file_no >= PIP2_FW_FILE) ||
+				    (ld->pip2_load_file_no == PIP2_RAM_FILE &&
+				     status != PIP2_RSP_ERR_END_OF_FILE)) {
+					_pt_pip2_update_bl_status(dev,
+						UPDATE_FW_WRITE_ERROR,
+						PT_NO_INC);
+					pt_debug(dev, DL_ERROR,
+						"%s Write Fail-status=0x%02x\n",
+						__func__, status);
+					retry_packet++;
+				} else if (ld->pip2_load_file_no ==
+					   PIP2_RAM_FILE &&
+					   status == PIP2_RSP_ERR_END_OF_FILE) {
+					/* Special case EOF writing to SRAM */
+					remain_bytes = 0;
+					status = 0;
+				}
+			} else {
+				remain_bytes = 0;
+			}
+		}
+	}
+
+	if (cd->bl_with_no_int)
+		enable_irq(cd->irq);
+
+	if (remain_bytes == 0 && retry_packet == 0)
+		_pt_pip2_update_bl_status(dev, UPDATE_FW_ACTIVE_99, PT_NO_INC);
+
+	if (retry_packet >= 3) {
+		/* A packet write failure occurred 3x */
+		pt_debug(dev, DL_ERROR,
+			"%s: BL terminated due to consecutive write errors\n",
+			__func__);
+		_pt_pip2_update_bl_status(dev, UPDATE_FW_WRITE_ERROR,
+			PT_NO_INC);
+	} else if (status) {
+		ret = status;
+		pt_debug(dev, DL_ERROR,
+			"%s: File write failed with status=%d\n",
+			__func__, status);
+		_pt_pip2_update_bl_status(dev, UPDATE_FW_WRITE_ERROR,
+			PT_NO_INC);
+	} else
+		pt_debug(dev, DL_INFO,
+			"%s: BIN file write finished successfully\n", __func__);
+
+	ret = cmd->nonhid_cmd->pip2_file_close(dev, ld->pip2_load_file_no);
+	if (ret != ld->pip2_load_file_no) {
+		pt_debug(dev, DL_ERROR,
+			"%s file close failure\n", __func__);
+		_pt_pip2_update_bl_status(dev, UPDATE_FW_FILE_CLOSE_ERROR,
+			PT_NO_INC);
+		goto exit;
+	}
+
+	/* When updating non FW files, stay in BL */
+	if (ld->pip2_load_file_no >= PIP2_CONFIG_FILE)
+		goto exit;
+
+	if ((ld->pip2_load_file_no == PIP2_RAM_FILE) &&
+	    (update_fw_status < UPDATE_FW_COMPLETE)) {
+		/* When writing to RAM don't reset, just launch application */
+		pt_debug(dev, DL_INFO,
+			"%s Sending execute command now...\n", __func__);
+		cd->startup_status = STARTUP_STATUS_START;
+		ret = cmd->nonhid_cmd->pip2_send_cmd(dev,
+			PT_CORE_CMD_UNPROTECTED, PIP2_CMD_ID_EXECUTE,
+			NULL, 0, read_buf, &actual_read_len);
+		status = read_buf[PIP2_RESP_STATUS_OFFSET];
+		if (ret || status) {
+			pt_debug(dev, DL_ERROR,
+				"%s Execute command failure\n", __func__);
+			_pt_pip2_update_bl_status(dev,
+				UPDATE_FW_EXECUTE_ERROR, PT_NO_INC);
+			goto exit;
+		}
+	} else if (ld->pip2_load_file_no == PIP2_FW_FILE &&
+		   update_fw_status < UPDATE_FW_COMPLETE) {
+		pt_debug(dev, DL_INFO,
+			"%s Toggle TP_XRES now...\n", __func__);
+		cmd->request_reset(dev, PT_CORE_CMD_UNPROTECTED);
+	}
+	pt_debug(dev, DL_INFO, "%s: APP launched\n", __func__);
+
+	/* If any error occurred simply close the file and exit */
+	if (update_fw_status > UPDATE_FW_COMPLETE)
+		goto exit_close_file;
+
+	/* Wait for FW reset sentinel from reset or execute for up to 500ms */
+	t = wait_event_timeout(cd->wait_q,
+		(cd->startup_status >= STARTUP_STATUS_FW_RESET_SENTINEL),
+		msecs_to_jiffies(PT_BL_WAIT_FOR_SENTINEL));
+	if (IS_TMO(t)) {
+		pt_debug(dev, DL_WARN,
+			"%s: 0x%04X Timeout waiting for FW sentinel",
+			__func__, cd->startup_status);
+	}
+
+	/* Double verify DUT is alive and well in Application mode */
+	if (cd->startup_status & STARTUP_STATUS_FW_RESET_SENTINEL) {
+		ret = cmd->request_pip2_get_mode_sysmode(dev,
+			PT_CORE_CMD_UNPROTECTED, &mode, NULL);
+		pt_debug(dev, DL_WARN, "%s: mode = %d (Expected 2)",
+			__func__, mode);
+		if (mode != PT_MODE_OPERATIONAL) {
+			pt_debug(dev, DL_ERROR,
+				"%s ERROR: Not in App mode as expected\n",
+				__func__);
+			_pt_pip2_update_bl_status(dev, UPDATE_FW_MODE_ERROR,
+				PT_NO_INC);
+			goto exit;
+		}
+	} else {
+		pt_debug(dev, DL_ERROR, "%s: FW sentinel not seen 0x%04X\n",
+			__func__,  cd->startup_status);
+		_pt_pip2_log_last_error(dev, read_buf);
+		_pt_pip2_update_bl_status(dev, UPDATE_FW_SENTINEL_NOT_SEEN,
+			PT_NO_INC);
+		goto exit;
+	}
+
+	/* On a Flashless DUT save or restore the CAL data */
+	if (cd->cal_cache_in_host == PT_FEATURE_ENABLE)
+		_pt_calibrate_flashless_dut(dev);
+
+	/* Subscribe calibration task if calibration flag is set */
+	if (ld->loader_pdata
+		&& (ld->loader_pdata->flags
+			 & PT_LOADER_FLAG_CALIBRATE_AFTER_FW_UPGRADE)
+		&& (cd->cal_cache_in_host == PT_FEATURE_DISABLE)) {
+#if (KERNEL_VERSION(3, 13, 0) <= LINUX_VERSION_CODE)
+		reinit_completion(&ld->calibration_complete);
+#else
+		INIT_COMPLETION(ld->calibration_complete);
+#endif
+		/* set up call back for startup */
+		pt_debug(dev, DL_INFO, "%s: Adding callback for calibration\n",
+			__func__);
+		ret = cmd->subscribe_attention(dev, PT_ATTEN_STARTUP,
+			PT_LOADER_NAME, pt_calibration_attention, 0);
+		if (ret) {
+			pt_debug(dev, DL_ERROR,
+				"%s: Failed adding callback for calibration\n",
+				__func__);
+			ret = 0;
+		} else
+			wait_for_calibration_complete = true;
+	}
+
+	pt_debug(dev, DL_INFO, "%s: == PIP2 FW upgrade finished ==\n",
+		__func__);
+	goto exit;
+
+exit_close_file:
+	ret = cmd->nonhid_cmd->pip2_file_close(dev, ld->pip2_load_file_no);
+	if (ret < 0) {
+		pt_debug(dev, DL_ERROR,
+			"%s file close failure\n", __func__);
+		_pt_pip2_update_bl_status(dev, UPDATE_FW_FILE_CLOSE_ERROR,
+			PT_NO_INC);
+	}
+
+exit:
+	cd->fw_updating = false;
+	if (us_fw_used)
+		kfree(fw_img);
+	if (ld->pip2_load_file_no > PIP2_FW_FILE)
+		goto exit_staying_in_bl;
+
+	cmd->release_exclusive(dev);
+
+	pm_runtime_put_sync(dev);
+	if (fw)
+		release_firmware(fw);
+
+	/*
+	 * For built-in FW update, it should not warn builtin_bin_fw_status
+	 * for each bootup since update_fw_status would be
+	 * UPDATE_FW_VERSION_ERROR in most situations because firmware
+	 * should have been up to date.
+	 */
+	if (ld->pip2_load_builtin) {
+		if ((update_fw_status == UPDATE_FW_ACTIVE_99) ||
+		    (update_fw_status == UPDATE_FW_VERSION_ERROR))
+			ld->builtin_bin_fw_status = 0;
+		else
+			ld->builtin_bin_fw_status = -EINVAL;
+	}
+
+	if ((update_fw_status == UPDATE_FW_ACTIVE_99) ||
+	    (update_fw_status == UPDATE_FW_VERSION_ERROR)) {
+		pt_debug(dev, DL_WARN, "%s: Queue ENUM\n", __func__);
+		cmd->request_enum(dev, true);
+	}
+	if (update_fw_status < UPDATE_FW_COMPLETE)
+		_pt_pip2_update_bl_status(dev, UPDATE_FW_COMPLETE, PT_NO_INC);
+
+	if (wait_for_calibration_complete)
+		wait_for_completion(&ld->calibration_complete);
+
+	pt_debug(dev, DL_INFO, "%s: Starting watchdog\n", __func__);
+	ret = cmd->request_start_wd(dev);
+
+	/*
+	 * When in No-Flash mode allow auto BL after any BL.
+	 * There is an issue where setting flashless mode via drv_debug
+	 * can happen in the middle of pt_pip2_enter_bl() which will revert
+	 * the flashless_auto_bl value back to what it was when the function
+	 * started.
+	 */
+	if (cd->flashless_dut)
+		cd->flashless_auto_bl = PT_ALLOW_AUTO_BL;
+
+	return;
+
+exit_staying_in_bl:
+	/* When updating a non FW file, a restart is not wanted. Stay in BL */
+	_pt_pip2_update_bl_status(dev, UPDATE_FW_COMPLETE, PT_NO_INC);
+	cmd->release_exclusive(dev);
+	pm_runtime_put_sync(dev);
+	if (fw)
+		release_firmware(fw);
+}
+
+#define PIP2_MAX_FILE_NAMES 3
+/*******************************************************************************
+ * FUNCTION: pt_pip2_upgrade_firmware_from_builtin
+ *
+ * SUMMARY: Bootload the DUT with a built in firmware binary image.
+ *	Load either a SRAM image "ttdl_fw_RAM.bin" or a FLASH image
+ *	"ttdl_fw.bin" with the priority being the SRAM image.
+ *
+ * PARAMETERS:
+ *	*dev - pointer to the device structure
+ ******************************************************************************/
+static int pt_pip2_upgrade_firmware_from_builtin(struct device *dev)
+{
+	struct pt_loader_data *ld = pt_get_loader_data(dev);
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	const struct firmware *fw_entry = NULL;
+	int retval;
+	int rc = 0;
+	int read_size = 16;
+	u8 image[16];
+	int index = 0;
+	int file_count = 0;
+	char *filename[PIP2_MAX_FILE_NAMES];
+
+	/*
+	 * Load the supported filenames in the correct search order
+	 * 0 - "tt_fw<_PIDX>.bin"
+	 * 1 - "XXXX_tt_fw<_PIDX>.bin" where XXXX = Silicon ID
+	 * 2 - "tt_fw.bin", default FW name
+	 */
+
+	filename[file_count++] = generate_firmware_filename(dev);
+	filename[file_count++] = generate_silicon_id_firmware_filename(dev);
+	if (pt_get_panel_id(dev) != PANEL_ID_NOT_ENABLED) {
+		filename[file_count] =
+		    kzalloc(sizeof(PT_FW_FILE_NAME), GFP_KERNEL);
+		memcpy(filename[file_count++], PT_FW_FILE_NAME,
+		       sizeof(PT_FW_FILE_NAME));
+	}
+
+	for (index = 0; index < file_count; index++) {
+		if (!filename[index])
+			return -ENOMEM;
+	}
+
+	if (cd->flashless_dut) {
+		pt_debug(dev, DL_INFO,
+			"%s: Proceed to BL flashless DUT\n", __func__);
+		ld->pip2_load_file_no = PIP2_RAM_FILE;
+		if (cd->pip2_us_file_path[0] == '\0') {
+			ld->pip2_load_builtin = true;
+			pt_debug(dev, DL_WARN,
+				"%s: US Path not defined, BL from built-in\n",
+				__func__);
+		} else {
+			/* Read a few bytes to see if file exists */
+			rc = cmd->nonhid_cmd->read_us_file(dev,
+				cd->pip2_us_file_path, image, &read_size);
+			if (!rc) {
+				ld->pip2_load_builtin = false;
+				pt_debug(dev, DL_WARN,
+					"%s: %s Found, BL from US\n",
+					__func__, cd->pip2_us_file_path);
+				goto ready;
+			} else {
+				ld->pip2_load_builtin = true;
+				pt_debug(dev, DL_WARN,
+					"%s: ATM - %s NOT Found, BL from built-in\n",
+					__func__, cd->pip2_us_file_path);
+			}
+		}
+	} else {
+		ld->pip2_load_file_no = PIP2_FW_FILE;
+		ld->pip2_load_builtin = true;
+	}
+
+	/* Look for any FW file name match and request the FW */
+	mutex_lock(&cd->firmware_class_lock);
+	index = 0;
+	while (index < file_count) {
+		pt_debug(dev, DL_INFO, "%s: Request FW class file: %s\n",
+			__func__, filename[index]);
+		retval = request_firmware_direct(&fw_entry,
+			filename[index], dev);
+		if (retval < 0) {
+			pt_debug(dev, DL_WARN, "%s: ATM - Fail request FW %s load\n",
+				__func__, filename[index]);
+		} else {
+			pt_debug(dev, DL_INFO, "%s: FW %s class file loading\n",
+				__func__, filename[index]);
+			break;
+		}
+		index++;
+	}
+
+	/* No matching file names found */
+	if (index == file_count) {
+		pt_debug(dev, DL_WARN, "%s: No FW is found\n", __func__);
+		goto exit;
+	}
+
+ready:
+	_pt_pip2_firmware_cont(fw_entry, dev);
+	retval = ld->builtin_bin_fw_status;
+exit:
+	mutex_unlock(&cd->firmware_class_lock);
+	index = 0;
+	while (index < file_count)
+		kfree(filename[index++]);
+
+	return retval;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pip2_create_fw_class
+ *
+ * SUMMARY: Create the firmware class but don't actually laod any FW to the
+ *	DUT. This creates all the sysfs nodes needed for a user to bootload
+ *	the DUT with their own bin file.
+ *
+ * PARAMETERS:
+ *	*pip2_data     - pointer to the PIP2 loader data structure
+ ******************************************************************************/
+static int pt_pip2_create_fw_class(struct pip2_loader_data *pip2_data)
+{
+
+	int ret = 0;
+	struct device *dev = pip2_data->dev;
+	struct pt_loader_data *ld = pt_get_loader_data(dev);
+
+	/*
+	 * The file name dev_name(dev) is tied with bus name and usually
+	 * it is "x-0024". This name is wanted to keep consistency
+	 * (e.g. /sys/class/firmware/x-0024/) for the path of fw class
+	 * nodes with different kernel release. Also it is an invalid bin
+	 * file name used intentionally because request_firmware_nowait
+	 * will not find the file which is what we want and then simply
+	 * create the fw class nodes.
+	 */
+	ld->pip2_load_builtin = false;
+	pt_debug(dev, DL_INFO, "%s: Request FW Class", __func__);
+	ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_NOHOTPLUG,
+			dev_name(dev), dev, GFP_KERNEL, dev,
+			_pt_pip2_firmware_cont);
+	if (ret) {
+		pt_debug(dev, DL_ERROR,
+			"%s: ERROR requesting firmware class\n", __func__);
+	}
+
+	return ret;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pip2_bl_from_file_work
+ *
+ * SUMMARY: The work function to schedule the BL work for PIP2 only.
+ *
+ * PARAMETERS:
+ *	*bl_from_file - pointer to work_struct structure
+ ******************************************************************************/
+static void pt_pip2_bl_from_file_work(struct work_struct *pip2_bl_from_file)
+{
+	struct pt_loader_data *ld = container_of(pip2_bl_from_file,
+			struct pt_loader_data, pip2_bl_from_file);
+	struct device *dev = ld->dev;
+	const struct firmware *fw_entry = NULL;
+
+	_pt_pip2_firmware_cont(fw_entry, dev);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_bl_from_file_work
+ *
+ * SUMMARY: The work function to schedule the BL work for PIP2 or PIP1
+ *  according to the active_dut_generation in core data.
+ *
+ * PARAMETERS:
+ *	*bl_from_file - pointer to work_struct structure
+ ******************************************************************************/
+static void pt_bl_from_file_work(struct work_struct *bl_from_file)
+{
+	struct pt_loader_data *ld = container_of(bl_from_file,
+			struct pt_loader_data, bl_from_file);
+	struct device *dev = ld->dev;
+	const struct firmware *fw_entry = NULL;
+	u8 dut_gen = cmd->request_dut_generation(dev);
+
+	if (dut_gen == DUT_PIP2_CAPABLE)
+		_pt_pip2_firmware_cont(fw_entry, dev);
+	else if (dut_gen == DUT_PIP1_ONLY)
+		_pt_pip1_bl_from_file(dev);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pip2_bl_from_file_show
+ *
+ * SUMMARY: The show method for the "pip2_bl_from_file" sysfs node. The
+ *  scheduled work will perform PIP2 BL.
+ *
+ * NOTE: Since this function doesn't set pip2_load_file_no, it will use the
+ * value what has been stored there.
+ *
+ * PARAMETERS:
+ *	*dev  - pointer to device structure
+ *	*attr - pointer to device attributes structure
+ *	*buf  - pointer to print output buffer
+ ******************************************************************************/
+static ssize_t pt_pip2_bl_from_file_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct pt_loader_data *ld = pt_get_loader_data(dev);
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	int rc = 0;
+	int read_size = 2;
+	u8 image[2];
+
+	mutex_lock(&cd->firmware_class_lock);
+	ld->pip2_load_builtin = false;
+	mutex_unlock(&cd->firmware_class_lock);
+
+	/* Read a few bytes to see if file exists */
+	rc = cmd->nonhid_cmd->read_us_file(dev,
+		cd->pip2_us_file_path, image, &read_size);
+
+	if (!rc) {
+		schedule_work(&ld->pip2_bl_from_file);
+
+		return scnprintf(buf, PT_MAX_PRBUF_SIZE,
+			"Status: %d\n"
+			"BL File: %s\n",
+			rc, cd->pip2_us_file_path);
+	} else {
+		return scnprintf(buf, PT_MAX_PRBUF_SIZE,
+			"Status: %d\n"
+			"BL File: '%s' - Does not exist\n",
+			rc, cd->pip2_us_file_path);
+	}
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_bl_from_file_show
+ *
+ * SUMMARY: The show method for the "pt_bl_from_file" sysfs node. The scheduled
+ *  work can perform either PIP1 BL and PIP2 BL according to the
+ *  active_dut_generation of core data.
+ *
+ * NOTE: Since this function doesn't set pip2_load_file_no, it will use the
+ * value what has been stored there.
+ *
+ * PARAMETERS:
+ *	*dev  - pointer to device structure
+ *	*attr - pointer to device attributes structure
+ *	*buf  - pointer to print output buffer
+ ******************************************************************************/
+static ssize_t pt_bl_from_file_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct pt_loader_data *ld = pt_get_loader_data(dev);
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	int rc = 0;
+	int read_size = 2;
+	u8 dut_gen = cmd->request_dut_generation(dev);
+	u8 image[2];
+
+	mutex_lock(&cd->firmware_class_lock);
+	ld->pip2_load_builtin = false;
+	mutex_unlock(&cd->firmware_class_lock);
+
+	/* Read a few bytes to see if file exists */
+	rc = cmd->nonhid_cmd->read_us_file(dev,
+		cd->pip2_us_file_path, image, &read_size);
+
+	if (dut_gen == DUT_UNKNOWN) {
+		rc = -EINVAL;
+		return scnprintf(buf, PT_MAX_PRBUF_SIZE,
+			"Status: %d\n"
+			"BL File: '%s' - Failed, DUT Generation could not be determined\n",
+			rc, cd->pip2_us_file_path);
+	} else if (!rc) {
+		schedule_work(&ld->bl_from_file);
+
+		return scnprintf(buf, PT_MAX_PRBUF_SIZE,
+			"Status: %d\n"
+			"BL File: %s\n",
+			rc, cd->pip2_us_file_path);
+	} else {
+		return scnprintf(buf, PT_MAX_PRBUF_SIZE,
+			"Status: %d\n"
+			"BL File: '%s' - Does not exist\n",
+			rc, cd->pip2_us_file_path);
+	}
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pip2_bl_from_file_store
+ *
+ * SUMMARY: The store method for the "pip2_bl_from_file" and "pt_bl_from_file"
+ *  sysfs node. Used to allow any file path[necessary] and file_no[optional] to
+ *  be used to BL, for example: "echo /data/pt_fw 1 > pip2_bl_from_file", and
+ *  do the "cat" will perform FW loader process.
+ *
+ * NOTE: the last char of file path in buf which is a '\n' is not copied.
+ * NOTE: the default file_no is PIP2_RAM_FILE.
+ *
+ * PARAMETERS:
+ *      *dev   - pointer to device structure
+ *      *attr  - pointer to device attributes
+ *      *buf   - pointer to output buffer
+ *       size   - size of data in buffer
+ ******************************************************************************/
+static ssize_t pt_pip2_bl_from_file_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	struct pt_loader_data *ld = pt_get_loader_data(dev);
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	const char *deli_space = " ",  *deli_comma = ",";
+	char name_space[PT_MAX_PATH_SIZE];
+	char *ptr_left = NULL, *ptr_right = name_space;
+	u8 file_no = PIP2_RAM_FILE;
+	u32 input_data[2];
+	int length;
+	bool file_no_set = false;
+
+	memset(name_space, 0, PT_MAX_PATH_SIZE);
+	memset(cd->pip2_us_file_path, 0, PT_MAX_PATH_SIZE);
+	if (size <= PT_MAX_PATH_SIZE) {
+		memcpy(name_space, buf, size);
+		ptr_left = strsep(&ptr_right, deli_space);
+		if (ptr_right == NULL) {
+			ptr_right = name_space;
+			ptr_left = strsep(&ptr_right, deli_comma);
+		}
+
+		if (ptr_right != NULL) {
+			length = cmd->parse_sysfs_input(
+			    dev, ptr_right, strlen(ptr_right), input_data,
+			    ARRAY_SIZE(input_data));
+			if (length <= 0) {
+				pt_debug(dev, DL_ERROR,
+					 "%s: Input format error!\n", __func__);
+				return -EINVAL;
+			}
+			file_no_set = true;
+			file_no = input_data[0];
+		}
+
+		pt_debug(dev, DL_WARN, "%s:Path=%s, File_no=%s(%d)\n", __func__,
+			 ptr_left, ptr_right, file_no);
+
+		if ((file_no_set) &&
+		    ((file_no < PIP2_RAM_FILE) || (file_no > PIP2_FILE_MAX))) {
+			pt_debug(dev, DL_WARN, "%s:Invalid File_no = %d\n",
+				__func__, file_no);
+			return -EINVAL;
+		}
+
+		mutex_lock(&cd->firmware_class_lock);
+		ld->pip2_load_file_no = file_no;
+		mutex_unlock(&cd->firmware_class_lock);
+
+		if (ptr_left[strlen(ptr_left) - 1] == '\n')
+			memcpy(cd->pip2_us_file_path, ptr_left,
+			       strlen(ptr_left) - 1);
+		else
+			memcpy(cd->pip2_us_file_path, ptr_left,
+			       strlen(ptr_left));
+	}
+
+	return size;
+}
+static DEVICE_ATTR(pip2_bl_from_file, 0644,
+		pt_pip2_bl_from_file_show, pt_pip2_bl_from_file_store);
+
+static DEVICE_ATTR(pt_bl_from_file, 0644,
+		pt_bl_from_file_show, pt_pip2_bl_from_file_store);
+
+/*******************************************************************************
+ * FUNCTION: pt_pip2_manual_upgrade_store
+ *
+ * SUMMARY: Store method for the pip2_manual_upgrade sysfs node. Allows
+ *	sysfs control of bootloading a new FW image to FLASH.
+ *
+ * PARAMETERS:
+ *      *dev   - pointer to device structure
+ *      *attr  - pointer to device attributes
+ *      *buf   - pointer to output buffer
+ *       size   - size of data in buffer
+ ******************************************************************************/
+static ssize_t pt_pip2_manual_upgrade_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	struct pt_loader_data *ld = pt_get_loader_data(dev);
+	u32 input_data[2] = {0};
+	int length;
+	int rc = 0;
+
+	length = cmd->parse_sysfs_input(dev, buf, size, input_data,
+			ARRAY_SIZE(input_data));
+
+	if (length != 1) {
+		pt_debug(dev, DL_WARN, "%s: Invalid number of arguments\n",
+			__func__);
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	if (input_data[0] < 0 || input_data[0] > 1) {
+		pt_debug(dev, DL_WARN, "%s: Invalid arguments\n", __func__);
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	_pt_pip2_update_bl_status(dev, UPDATE_FW_IDLE, PT_NO_INC);
+	if (ld->is_manual_upgrade_enabled) {
+		pt_debug(dev, DL_ERROR,
+			"%s: ERROR - Manual upgrade busy\n", __func__);
+		rc = -EBUSY;
+		goto exit;
+	}
+	ld->pip2_load_file_no = PIP2_FW_FILE;
+	pt_debug(dev, DL_DEBUG, "%s: ATM - File number is %d\n",
+		__func__, ld->pip2_load_file_no);
+
+	ld->is_manual_upgrade_enabled = 1;
+	rc = pt_pip2_create_fw_class(ld->pip2_data);
+	ld->is_manual_upgrade_enabled = 0;
+	if (rc < 0)
+		pt_debug(dev, DL_ERROR,
+			"%s: ERROR - FLASH Upgrade failed\n", __func__);
+
+exit:
+	if (rc)
+		return rc;
+	return size;
+}
+static DEVICE_ATTR(pip2_manual_upgrade, 0200,
+	NULL, pt_pip2_manual_upgrade_store);
+
+/*******************************************************************************
+ * FUNCTION: pt_pip2_manual_ram_upgrade_store
+ *
+ * SUMMARY: Store method for the pip2_manual_ram_upgrade sysfs node. Allows
+ *	sysfs control of bootloading a new FW image to SRAM.
+ *
+ * PARAMETERS:
+ *      *dev   - pointer to device structure
+ *      *attr  - pointer to device attributes
+ *      *buf   - pointer to output buffer
+ *       size   - size of data in buffer
+ ******************************************************************************/
+static ssize_t pt_pip2_manual_ram_upgrade_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	struct pt_loader_data *ld = pt_get_loader_data(dev);
+	u32 input_data[2] = {0};
+	int length;
+	int rc = 0;
+
+	length = cmd->parse_sysfs_input(dev, buf, size, input_data,
+			ARRAY_SIZE(input_data));
+
+	if (length != 1) {
+		pt_debug(dev, DL_WARN, "%s: Invalid number of arguments\n",
+			__func__);
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	if (input_data[0] < 0 || input_data[0] > 1) {
+		pt_debug(dev, DL_WARN, "%s: Invalid arguments\n", __func__);
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	_pt_pip2_update_bl_status(dev, UPDATE_FW_IDLE, PT_NO_INC);
+	if (ld->is_manual_upgrade_enabled) {
+		pt_debug(dev, DL_ERROR,
+			"%s: ERROR - Manual upgrade busy\n", __func__);
+		rc = -EBUSY;
+		goto exit;
+	}
+
+	ld->pip2_load_file_no = PIP2_RAM_FILE;
+	pt_debug(dev, DL_DEBUG, "%s: ATM - File number is %d\n",
+		__func__, ld->pip2_load_file_no);
+
+	ld->is_manual_upgrade_enabled = 1;
+	rc = pt_pip2_create_fw_class(ld->pip2_data);
+	ld->is_manual_upgrade_enabled = 0;
+	if (rc < 0)
+		pt_debug(dev, DL_ERROR,
+			"%s: ERROR - RAM Upgrade failed\n", __func__);
+
+exit:
+	if (rc)
+		return rc;
+	return size;
+}
+static DEVICE_ATTR(pip2_manual_ram_upgrade, 0200,
+	NULL, pt_pip2_manual_ram_upgrade_store);
+
+/*******************************************************************************
+ * FUNCTION: pt_pip2_file_write_store
+ *
+ * SUMMARY: Store method for the pip2_file_write sysfs node. Allows
+ *	sysfs control to "load" data into any file.
+ *
+ * PARAMETERS:
+ *      *dev   - pointer to device structure
+ *      *attr  - pointer to device attributes
+ *      *buf   - pointer to output buffer
+ *       size   - size of data in buffer
+ ******************************************************************************/
+static ssize_t pt_pip2_file_write_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	struct pt_loader_data *ld = pt_get_loader_data(dev);
+	int rc;
+	u32 input_data[3];
+	int length;
+
+	if (ld->is_manual_upgrade_enabled) {
+		pt_debug(dev, DL_ERROR,
+			"%s: ERROR - Manual upgrade busy\n", __func__);
+		rc = -EBUSY;
+		goto exit;
+	}
+
+	length = cmd->parse_sysfs_input(dev, buf, size, input_data,
+			ARRAY_SIZE(input_data));
+	if (length <= 0 || length > 2) {
+		pt_debug(dev, DL_ERROR, "%s: Invalid number of arguments\n",
+			__func__);
+		ld->pip2_file_data.para_num = 0;
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	if (input_data[0] < PIP2_FW_FILE || input_data[0] > PIP2_FILE_MAX) {
+		pt_debug(dev, DL_ERROR, "%s: Invalid file handle\n", __func__);
+		ld->pip2_file_data.para_num = 0;
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	ld->pip2_load_file_no = input_data[0];
+	if (length == 2)
+		ld->pip2_file_data.file_offset = input_data[1];
+
+	ld->is_manual_upgrade_enabled = 1;
+	rc = pt_pip2_create_fw_class(ld->pip2_data);
+	ld->is_manual_upgrade_enabled = 0;
+	if (rc < 0)
+		pt_debug(dev, DL_ERROR,
+			"%s: ERROR - RAM Upgrade failed\n", __func__);
+exit:
+	if (rc)
+		return rc;
+	return size;
+}
+static DEVICE_ATTR(pip2_file_write, 0200, NULL, pt_pip2_file_write_store);
+
+/*******************************************************************************
+ * FUNCTION: pt_update_fw_store
+ *
+ * SUMMARY: Store method for the update_fw sysfs node. This node is required
+ *	by ChromeOS to first determine if loading is available and then perform
+ *	the loading if required. This function is simply a wrapper to call:
+ *		pt_pip2_manual_upgrade_store - for the TC3XXX or TT7XXX parts
+ *		pt_manual_upgrade_store - for the legacy Gen5/6 devices.
+ *
+ * PARAMETERS:
+ *      *dev   - pointer to device structure
+ *      *attr  - pointer to device attributes
+ *      *buf   - pointer to output buffer
+ *       size   - size of data in buffer
+ ******************************************************************************/
+static ssize_t pt_update_fw_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	u8 dut_gen = cmd->request_dut_generation(dev);
+
+	if (dut_gen == DUT_PIP2_CAPABLE)
+		size = pt_pip2_manual_upgrade_store(dev, attr, buf, size);
+	else if (dut_gen == DUT_PIP1_ONLY)
+		size = pt_manual_upgrade_store(dev, attr, buf, size);
+
+	return size;
+}
+static DEVICE_ATTR(update_fw, 0200, NULL, pt_update_fw_store);
+#endif /* CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE */
+
+#ifdef TTDL_DIAGNOSTICS
+/*******************************************************************************
+ * FUNCTION: pt_pip2_file_read_show
+ *
+ * SUMMARY: The read method for the pip2_file_read sysfs node. Allows to
+ *  perform flash read action according to stored value. This function will
+ *  re-enter always until it returns:
+ *        0: No data to be written to sysfs node
+ *       <0: Error happens
+ * (For kernel version large than 3.11(not tested), if the function returns
+ * non-zero value in previous chunk, to return 0 once can not stop re-enter.
+ * It needs one more time to stop read action by returned value <= 0. But for
+ * older version, read action will stop when returned value <= 0 once).
+ *
+ * NOTE: Up to PIP2_FILE_WRITE_LEN_PER_PACKET(245) bytes of data are read for
+ * each enter. When last package is read, pip2_file_data.para_num is assigned
+ * as negatie value(-1), then next enter can return 0 to indicate the read
+ * method can be finished.
+ *
+ * RETURN: Size of data written to sysfs node
+ *
+ * PARAMETERS:
+ *  *filp     - pointer to file structure
+ *  *kobj     - pointer to kobject structure
+ *  *bin_attr - pointer to bin_attribute structure
+ *   buf      - pointer to cmd input buffer
+ *   offset   - offset index to store input buffer
+ *   count    - size of data in buffer
+ ******************************************************************************/
+static ssize_t pt_pip2_file_read_show(struct file *filp,
+		struct kobject *kobj, struct bin_attribute *bin_attr,
+		char *buf, loff_t offset, size_t count)
+{
+	struct device *dev = container_of(kobj, struct device, kobj);
+	struct pt_loader_data *ld = pt_get_loader_data(dev);
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	int rc = 0;
+	u8 file_handle = ld->pip2_file_data.file_handle;
+	u8 *pr_buf = ld->pip2_file_data.file_print_buf;
+	int print_idx = 0, read_size = 0, i;
+	u8  read_buf[PT_MAX_PIP2_MSG_SIZE];
+	u8  read_len;
+	u32 address, file_size;
+
+	if (ld->pip2_file_data.file_print_left) {
+		pt_debug(dev, DL_INFO, "%s: print left=%d, count=%zu\n",
+			__func__, ld->pip2_file_data.file_print_left, count);
+
+		print_idx = ld->pip2_file_data.file_print_left;
+		if (count < print_idx) {
+			memcpy(buf, pr_buf, count);
+			ld->pip2_file_data.file_print_left = print_idx - count;
+			for (i = 0; i < ld->pip2_file_data.file_print_left; i++)
+				pr_buf[i] = pr_buf[i+count];
+			print_idx = count;
+		} else {
+			memcpy(buf, pr_buf, print_idx);
+			ld->pip2_file_data.file_print_left = 0;
+		}
+		return print_idx;
+	}
+
+	if (ld->pip2_file_data.para_num == 0) {
+		/*
+		 * When offset != 0, it means the extra call for splice out,
+		 * don't need a warning.
+		 */
+		if (offset != 0)
+			return 0;
+
+		print_idx += scnprintf(buf, count, "Status: %d\n"
+			"No input!\n", -EINVAL);
+		pt_debug(dev, DL_ERROR, "%s: Invalid para_num = %d!\n",
+			__func__, ld->pip2_file_data.para_num);
+		return print_idx;
+	} else if (ld->pip2_file_data.para_num == -1) {
+		ld->pip2_file_data.para_num = 0;
+		pt_debug(dev, DL_INFO, "%s: flash read finish!\n",
+				__func__);
+		rc = 0;
+		goto exit_release;
+	} else if (ld->pip2_file_data.para_num < -1) {
+		ld->pip2_file_data.para_num = 0;
+		pt_debug(dev, DL_ERROR, "%s: Exit directly due to errors!\n",
+				__func__);
+		return 0;
+	} else if (ld->pip2_file_data.para_num > 3) {
+		ld->pip2_file_data.para_num = 0;
+		pt_debug(dev, DL_ERROR,
+			"%s: Exit directly due to invalid parameter!\n",
+				__func__);
+		return 0;
+	}
+
+	if (offset == 0) {
+		ld->pip2_file_data.file_print_buf = kzalloc(PIPE_BUF,
+			GFP_KERNEL);
+		if (!ld->pip2_file_data.file_print_buf) {
+			rc = -ENOMEM;
+			goto exit;
+		}
+		/* This functionality is only available in the BL */
+		if (cd->mode != PT_MODE_BOOTLOADER) {
+			rc = -EPERM;
+			goto exit_free;
+		}
+
+		pr_buf = ld->pip2_file_data.file_print_buf;
+
+		rc = cmd->request_exclusive(dev,
+				PT_LDR_REQUEST_EXCLUSIVE_TIMEOUT);
+		if (rc) {
+			pt_debug(dev, DL_ERROR,
+				"%s: Failed to request exclusive rc=%d\n",
+				__func__, rc);
+			goto exit_free;
+		}
+
+		rc = cmd->nonhid_cmd->pip2_file_open(dev, file_handle);
+		if (rc < 0) {
+			pt_debug(dev, DL_ERROR,
+				"%s: Failed to file_open rc=%d\n",
+				__func__, rc);
+			goto exit_release;
+		}
+
+		rc = cmd->nonhid_cmd->pip2_file_get_stats(dev, file_handle,
+				&address, &file_size);
+		if (rc) {
+			pt_debug(dev, DL_ERROR,
+				"%s: Failed to get_file_state rc=%d\n",
+				__func__, rc);
+			goto exit_file_close;
+		}
+
+		ld->pip2_file_data.file_print_size = 0;
+		ld->pip2_file_data.file_max_size = file_size;
+		ld->pip2_file_data.file_print_left = 0;
+		print_idx += scnprintf(pr_buf, PIPE_BUF, "ROM_DATA:");
+
+		if (ld->pip2_file_data.para_num == 1)
+			ld->pip2_file_data.file_read_size = file_size;
+		else if (ld->pip2_file_data.para_num == 2) {
+			if (ld->pip2_file_data.file_offset < file_size)
+				ld->pip2_file_data.file_read_size = file_size -
+					ld->pip2_file_data.file_offset;
+			else {
+				rc = -EINVAL;
+				pt_debug(dev, DL_ERROR,
+					"%s: File read out of bounds rc=%d\n",
+					__func__, rc);
+				goto exit_file_close;
+			}
+		} else if (ld->pip2_file_data.para_num == 3) {
+			if ((ld->pip2_file_data.file_read_size +
+				ld->pip2_file_data.file_offset) > file_size) {
+				rc = -EINVAL;
+				pt_debug(dev, DL_ERROR,
+					"%s: File read out of bounds rc=%d\n",
+					__func__, rc);
+				goto exit_file_close;
+			}
+		} else {
+			pt_debug(dev, DL_ERROR,
+				"%s: Invalid number of parameters!\n",
+				__func__);
+			goto exit_file_close;
+		}
+	}
+
+	offset = ld->pip2_file_data.file_print_size +
+			ld->pip2_file_data.file_offset;
+	if ((offset >= ld->pip2_file_data.file_max_size) ||
+		(ld->pip2_file_data.file_print_size >=
+		ld->pip2_file_data.file_read_size))
+		goto exit_file_close;
+	else if ((ld->pip2_file_data.file_print_size +
+		PIP2_FILE_WRITE_LEN_PER_PACKET) >=
+		ld->pip2_file_data.file_read_size)
+		read_len = ld->pip2_file_data.file_read_size -
+		ld->pip2_file_data.file_print_size;
+	else
+		read_len = PIP2_FILE_WRITE_LEN_PER_PACKET;
+
+	rc = cmd->nonhid_cmd->pip2_file_seek_offset(dev,
+			file_handle, offset, 0);
+	if (rc) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Failed to seek file offset rc=%d\n",
+			__func__, rc);
+		goto exit_file_close;
+	}
+
+	read_size = cmd->nonhid_cmd->pip2_file_read(dev,
+			file_handle, read_len, read_buf);
+	if (read_size < 0) {
+		pt_debug(dev, DL_ERROR, "%s: Failed to read file rc=%d\n",
+				__func__, read_size);
+		goto exit_file_close;
+	}
+
+	for (i = 0; i < read_size; i++)
+		print_idx += scnprintf(pr_buf + print_idx,
+			PIPE_BUF - print_idx,
+			"%02X ", read_buf[i + PIP2_RESP_BODY_OFFSET]);
+	ld->pip2_file_data.file_print_size += read_size;
+	if (count < print_idx) {
+		memcpy(buf, pr_buf, count);
+		ld->pip2_file_data.file_print_left = print_idx - count;
+		for (i = 0; i < ld->pip2_file_data.file_print_left; i++)
+			pr_buf[i] = pr_buf[i+count];
+		print_idx = count;
+	} else {
+		memcpy(buf, pr_buf, print_idx);
+	}
+	goto exit_for_next_read;
+
+exit_file_close:
+	if (rc)
+		print_idx += scnprintf(pr_buf + print_idx,
+			PIPE_BUF - print_idx, "(READ ERROR)\n");
+	else if (ld->pip2_file_data.file_print_size)
+		print_idx += scnprintf(pr_buf + print_idx,
+				PIPE_BUF - print_idx,
+				":(%d bytes)\n",
+				ld->pip2_file_data.file_print_size);
+	else
+		print_idx += scnprintf(pr_buf + print_idx,
+				PIPE_BUF - print_idx, "No Data\n");
+	if (count < print_idx) {
+		memcpy(buf, pr_buf, count);
+		ld->pip2_file_data.file_print_left = print_idx - count;
+		for (i = 0; i < ld->pip2_file_data.file_print_left; i++)
+			pr_buf[i] = pr_buf[i+count];
+		print_idx = count;
+	} else {
+		memcpy(buf, pr_buf, print_idx);
+	}
+	rc = cmd->nonhid_cmd->pip2_file_close(dev, file_handle);
+	if (file_handle != rc)
+		pt_debug(dev, DL_ERROR,
+			"%s Failed to close file %d, rc = %d\n", __func__,
+			file_handle, rc);
+	/*
+	 * Mark para_num as negative value to finish read method during
+	 * next enter.
+	 */
+	ld->pip2_file_data.para_num = -1;
+
+exit_for_next_read:
+	pt_debug(dev, DL_INFO,
+		"%s: %s=%d, %s=%d, %s=%d, %s=%d, %s=%d\n",
+		__func__,
+		"para_num", ld->pip2_file_data.para_num,
+		"handle", ld->pip2_file_data.file_handle,
+		"offset", ld->pip2_file_data.file_offset,
+		"read", ld->pip2_file_data.file_read_size,
+		"print", ld->pip2_file_data.file_print_size);
+	return print_idx;
+exit_release:
+	cmd->release_exclusive(dev);
+exit_free:
+	kfree(ld->pip2_file_data.file_print_buf);
+exit:
+	if (rc) {
+		ld->pip2_file_data.para_num = -2;
+		print_idx = scnprintf(buf, count, "Status: %d\n", rc);
+		return print_idx;
+	} else
+		return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pip2_file_read_store
+ *
+ * SUMMARY: The write method for the pip2_file_read node. The passed
+ *  in data is needed by read method.
+ *
+ * RETURN: Size of passed in buffer is success
+ *
+ * PARAMETERS:
+ *  *filp     - pointer to file structure
+ *  *kobj     - pointer to kobject structure
+ *  *bin_attr - pointer to bin_attribute structure
+ *   buf      - pointer to cmd input buffer
+ *   offset   - offset index to store input buffer
+ *   count    - size of data in buffer
+ ******************************************************************************/
+static ssize_t pt_pip2_file_read_store(struct file *filp,
+		struct kobject *kobj, struct bin_attribute *bin_attr,
+		char *buf, loff_t offset, size_t count)
+{
+	int rc = 0;
+	int length;
+	struct device *dev = container_of(kobj, struct device, kobj);
+	struct pt_loader_data *ld = pt_get_loader_data(dev);
+	int ic_buffer[4] = {0};
+
+	length = cmd->parse_sysfs_input(dev, buf, count, ic_buffer,
+			ARRAY_SIZE(ic_buffer));
+	if (length <= 0 || length > 3) {
+		pt_debug(dev, DL_ERROR, "%s: Input format error!\n",
+				__func__);
+		ld->pip2_file_data.para_num = 0;
+		rc = -EINVAL;
+		goto error;
+	}
+
+	if (ic_buffer[0] < PIP2_FW_FILE || ic_buffer[0] > PIP2_FILE_7) {
+		pt_debug(dev, DL_ERROR, "%s: Invalid file handle!\n",
+				__func__);
+		ld->pip2_file_data.para_num = 0;
+		rc = -EINVAL;
+		goto error;
+	}
+
+	switch (length) {
+	case 1:
+		ld->pip2_file_data.file_handle = ic_buffer[0];
+		ld->pip2_file_data.file_offset = 0;
+		ld->pip2_file_data.file_read_size = 0;
+		break;
+	case 2:
+		ld->pip2_file_data.file_handle = ic_buffer[0];
+		ld->pip2_file_data.file_offset = ic_buffer[1];
+		ld->pip2_file_data.file_read_size = 0;
+		break;
+	case 3:
+		ld->pip2_file_data.file_handle = ic_buffer[0];
+		ld->pip2_file_data.file_offset = ic_buffer[1];
+		ld->pip2_file_data.file_read_size = ic_buffer[2];
+		break;
+	default:
+		break;
+	}
+
+	ld->pip2_file_data.para_num = length;
+error:
+	pt_debug(dev, DL_INFO,
+		"%s: %s=%d, %s=%d, %s=%d, %s=%d, %s=%d\n",
+		__func__,
+		"para_num", ld->pip2_file_data.para_num,
+		"handle", ld->pip2_file_data.file_handle,
+		"offset", ld->pip2_file_data.file_offset,
+		"read", ld->pip2_file_data.file_read_size,
+		"print", ld->pip2_file_data.file_print_size);
+
+	if (rc)
+		return rc;
+	return count;
+}
+
+static struct bin_attribute bin_attr_pip2_file_read = {
+	.attr = {
+		.name = "pip2_file_read",
+		.mode = (0644),
+	},
+	.read = pt_pip2_file_read_show,
+	.write = pt_pip2_file_read_store,
+};
+
+/******************************************************************************
+ * FUNCTION: pt_pip2_file_crc_show
+ *
+ * SUMMARY: The show method for the "pip2_file_crc" sysfs node.
+ *	Shows the CRC of a file or portion of the file.
+ *
+ *	NOTE: If function is called when DUT is already in BL mode, the DUT
+ *	will remain in BL mode when function exits, however if DUT is in
+ *	normal mode when function is called, the DUT will be forced into BL
+ *	mode and then returned to normal mode when function exits.
+ *
+ *	NOTE: This sysfs node only can be used for BL version 1.8 or greater.
+ *
+ * PARAMETERS:
+ *	*dev  - pointer to device structure
+ *	*attr - pointer to device attributes structure
+ *	*buf  - pointer to print output buffer
+ ******************************************************************************/
+static ssize_t pt_pip2_file_crc_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct pt_loader_data *ld = pt_get_loader_data(dev);
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	int rc = 0;
+	u8 file_handle;
+	int print_idx = 0;
+	u8  read_buf[PT_MAX_PIP2_MSG_SIZE];
+	u32 address, file_size;
+	u32 length;
+	u32 offset;
+	u16 file_crc;
+	u16 status;
+
+	if (ld->pip2_fcrc.para_num == 0 ||
+	    ld->pip2_fcrc.file_handle == 0 ||
+	    ld->pip2_fcrc.file_read_size < 0 ||
+	    ld->pip2_fcrc.file_offset < 0) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Invalid parameters!\n",
+			__func__);
+		print_idx = scnprintf(buf, PT_MAX_PRBUF_SIZE,
+			"Status: %d\n"
+			"Invalid parameters!\n", -EINVAL);
+		return print_idx;
+	}
+
+	file_handle = ld->pip2_fcrc.file_handle;
+	offset = ld->pip2_fcrc.file_offset;
+	length = ld->pip2_fcrc.file_read_size;
+
+	/* This functionality is only available in the BL */
+	if (cd->mode != PT_MODE_BOOTLOADER) {
+		rc = -EPERM;
+		print_idx = scnprintf(buf, PT_MAX_PRBUF_SIZE,
+			"Status: %d\n", rc);
+		goto exit;
+	}
+
+	rc = cmd->request_exclusive(dev, PT_LDR_REQUEST_EXCLUSIVE_TIMEOUT);
+	if (rc) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Failed to request exclusive rc=%d\n",
+			__func__, rc);
+		print_idx = scnprintf(buf, PT_MAX_PRBUF_SIZE,
+			"Status: %d\n", rc);
+		goto exit;
+	}
+
+	rc = cmd->nonhid_cmd->pip2_file_open(dev, file_handle);
+	if (rc < 0) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Failed to file_open rc=%d\n",
+			__func__, rc);
+		print_idx = scnprintf(buf, PT_MAX_PRBUF_SIZE,
+			"Status: %d\n", rc);
+		goto exit_release;
+	}
+
+	rc = cmd->nonhid_cmd->pip2_file_get_stats(dev, file_handle,
+			&address, &file_size);
+	if (rc) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Failed to get_file_state rc=%d\n",
+			__func__, rc);
+		print_idx = scnprintf(buf, PT_MAX_PRBUF_SIZE,
+			"Status: %d\n", rc);
+		goto exit_file_close;
+	}
+
+	ld->pip2_fcrc.file_max_size = file_size;
+
+	if (ld->pip2_fcrc.file_read_size > file_size
+		|| ld->pip2_fcrc.file_offset > file_size
+		|| ((ld->pip2_fcrc.file_offset +
+		    ld->pip2_fcrc.file_read_size) > file_size)) {
+		pt_debug(dev, DL_ERROR,
+				"%s: Invalid parameters!\n",
+				__func__);
+		print_idx = scnprintf(buf, PT_MAX_PRBUF_SIZE,
+			"Status: %d\n", rc);
+		goto exit_file_close;
+	}
+
+	rc = cmd->nonhid_cmd->pip2_file_crc(dev,
+			file_handle, offset, length, read_buf);
+	if (rc) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Failed to get file crc, rc=%d\n",
+			__func__, rc);
+		print_idx = scnprintf(buf, PT_MAX_PRBUF_SIZE,
+			"Status: %d\n", rc);
+	} else {
+		status = read_buf[PIP2_RESP_STATUS_OFFSET];
+		if (status == PIP2_RSP_ERR_NONE) {
+			file_crc = get_unaligned_le16(&read_buf[5]);
+			print_idx = scnprintf(buf, PT_MAX_PRBUF_SIZE,
+				"Status: %d\n"
+				"FILE CRC: %04X\n",
+				status, file_crc);
+		} else
+			print_idx = scnprintf(buf, PT_MAX_PRBUF_SIZE,
+				"Status: %d\n"
+				"FILE CRC: n/a\n",
+				status);
+	}
+
+exit_file_close:
+	rc = cmd->nonhid_cmd->pip2_file_close(dev, file_handle);
+	if (file_handle != rc)
+		pt_debug(dev, DL_ERROR,
+			"%s Failed to close file %d, rc = %d\n", __func__,
+			file_handle, rc);
+
+exit_release:
+	cmd->release_exclusive(dev);
+exit:
+	ld->pip2_fcrc.para_num = 0;
+	return print_idx;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pip2_file_crc_store
+ *
+ * SUMMARY: The store method for the "pip2_file_crc" sysfs node. The passed in
+ *  data including file_handle, offset and read size are needed by show method.
+ *
+ *	NOTE: This sysfs node only can be used for BL version 1.8 or greater..
+ *
+ * PARAMETERS:
+ *	*dev  - pointer to device structure
+ *	*attr - pointer to device attributes structure
+ *	*buf  - pointer to print output buffer
+ *	 size - size of buffer
+ ******************************************************************************/
+static ssize_t pt_pip2_file_crc_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	struct pt_loader_data *ld = pt_get_loader_data(dev);
+	u32 input_data[4] = {0};
+	int length;
+	int rc = 0;
+
+	ld->pip2_fcrc.file_handle = PIP2_RAM_FILE;
+	ld->pip2_fcrc.file_offset = -1;
+	ld->pip2_fcrc.file_read_size = -1;
+
+	length = cmd->parse_sysfs_input(dev, buf, size, input_data,
+			ARRAY_SIZE(input_data));
+
+	if (length != 3) {
+		pt_debug(dev, DL_WARN, "%s: Invalid number of arguments\n",
+			__func__);
+		ld->pip2_fcrc.para_num = 0;
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	if ((input_data[0] < PIP2_FW_FILE) || (input_data[0] > PIP2_FILE_MAX)) {
+		pt_debug(dev, DL_WARN, "%s: Invalid file_no %d\n",
+			__func__, input_data[0]);
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	ld->pip2_fcrc.file_handle = input_data[0];
+	ld->pip2_fcrc.file_offset = input_data[1];
+	ld->pip2_fcrc.file_read_size = input_data[2];
+	ld->pip2_fcrc.para_num = 3;
+
+	pt_debug(dev, DL_INFO,
+		"%s: %s=%d, %s=%d, %s=%d, %s=%d\n",
+		__func__,
+		"para_num", ld->pip2_fcrc.para_num,
+		"handle", ld->pip2_fcrc.file_handle,
+		"offset", ld->pip2_fcrc.file_offset,
+		"length", ld->pip2_fcrc.file_read_size);
+exit:
+	if (rc)
+		return rc;
+	return size;
+}
+
+static DEVICE_ATTR(pip2_file_crc, 0644,
+	pt_pip2_file_crc_show, pt_pip2_file_crc_store);
+#endif
+/*******************************************************************************
+ * FUNCTION: pt_pip2_file_erase_show
+ *
+ * SUMMARY: The show method for the "pip2_file_erase" sysfs node.
+ *	Prints current erase status to output buffer.
+ *
+ * PARAMETERS:
+ *	*dev  - pointer to device structure
+ *	*attr - pointer to device attributes structure
+ *	*buf  - pointer to print output buffer
+ ******************************************************************************/
+static ssize_t pt_pip2_file_erase_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct pt_loader_data *ld = pt_get_loader_data(dev);
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	u8 file_handle;
+	u8 file = ld->pip2_file_erase_file_no;
+	int rc;
+
+	pip2_erase_status = -1;
+	pip2_erase_rc = 0;
+
+	if (file == PIP2_RAM_FILE) {
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	/* This functionality is only available in the BL */
+	if (cd->mode != PT_MODE_BOOTLOADER) {
+		rc = -EPERM;
+		goto exit;
+	}
+
+	rc = cmd->request_exclusive(dev, PT_LDR_REQUEST_EXCLUSIVE_TIMEOUT);
+	if (rc < 0) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Failed to get exclusive access rc=%d\n",
+			__func__, rc);
+		goto exit;
+	}
+
+	file_handle = cmd->nonhid_cmd->pip2_file_open(dev, file);
+	if (file_handle != file) {
+		rc = -EBADF;
+		goto exit_release;
+	}
+
+	file_handle = cmd->nonhid_cmd->pip2_file_erase(dev, file,
+		&pip2_erase_status);
+	if (file_handle < 0) {
+		rc = file_handle;
+		pt_debug(dev, DL_INFO, "%s: File erase error rc = %d\n",
+			__func__, rc);
+	} else if (file_handle == file) {
+		pt_debug(dev, DL_INFO, "%s: File %d erased\n",
+			__func__, file_handle);
+	} else {
+		rc = -EBADF;
+	}
+
+	file_handle = cmd->nonhid_cmd->pip2_file_close(dev, file);
+	if (file_handle != file && !rc)
+		rc = -EBADF;
+
+exit_release:
+	cmd->release_exclusive(dev);
+exit:
+	pip2_erase_rc = rc;
+	if (pip2_erase_status == -1) {
+		return scnprintf(buf, PT_MAX_PRBUF_SIZE,
+			"Status: %d\n"
+			"Erase Status: n/a\n",
+			pip2_erase_rc);
+	}
+	return scnprintf(buf, PT_MAX_PRBUF_SIZE,
+		"Status: %d\n"
+		"Erase Status: 0x%02X\n",
+		pip2_erase_rc, pip2_erase_status);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pip2_file_erase_store
+ *
+ * SUMMARY: The store method for the "pip2_file_erase" sysfs node. Allows the
+ *	caller to provide the file number to erase in FLASH.
+ *
+ *	NOTE: The DUT must be in BL mode before calling this function.
+ *
+ * PARAMETERS:
+ *	*dev  - pointer to device structure
+ *	*attr - pointer to device attributes structure
+ *	*buf  - pointer to print output buffer
+ *	 size - size of buffer
+ ******************************************************************************/
+static ssize_t pt_pip2_file_erase_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	struct pt_loader_data *ld = pt_get_loader_data(dev);
+	u32 input_data[2] = {0};
+	int length;
+	int rc = 0;
+
+	length = cmd->parse_sysfs_input(dev, buf, size, input_data,
+			ARRAY_SIZE(input_data));
+
+	if (length != 1) {
+		pt_debug(dev, DL_WARN, "%s: Invalid number of arguments\n",
+			__func__);
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	/* Only allow valid files to be erased */
+	if (input_data[0] < PIP2_FW_FILE || input_data[0] > PIP2_FILE_MAX) {
+		pip2_erase_status = PIP2_RSP_ERR_BAD_FILE;
+		pt_debug(dev, DL_ERROR, "%s: ERROR - Invalid File\n",
+			__func__);
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	ld->pip2_file_erase_file_no = input_data[0];
+
+exit:
+	if (rc)
+		return rc;
+	return size;
+}
+
+static DEVICE_ATTR(pip2_file_erase, 0644,
+	pt_pip2_file_erase_show, pt_pip2_file_erase_store);
+
+/*******************************************************************************
+ * FUNCTION: pt_pip2_bl_status_show
+ *
+ * SUMMARY: The show method for the pip2_bl_status sysfs node.
+ *	Shows the percent completion of the current BL or an error message.
+ *
+ * PARAMETERS:
+ *	*dev  - pointer to device structure
+ *	*attr - pointer to device attributes structure
+ *	*buf  - pointer to print output buffer
+ ******************************************************************************/
+static ssize_t pt_pip2_bl_status_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	ssize_t ret;
+	u8 status = update_fw_status;
+
+	if (update_fw_status <= UPDATE_FW_COMPLETE) {
+		pt_debug(dev, DL_DEBUG,
+			"%s BL_STATUS = %d\n", __func__, update_fw_status);
+		return scnprintf(buf, strlen(buf), "%d\n", update_fw_status);
+	}
+
+	switch (status) {
+	case UPDATE_FW_GENERAL_ERROR:
+		ret = scnprintf(buf, strlen(buf),
+			"ERROR: %d - General programming failure\n", status);
+		break;
+	case UPDATE_FW_PIP_VERSION_ERROR:
+		ret = scnprintf(buf, strlen(buf),
+			"ERROR: %d - Wrong PIP version detected\n", status);
+		break;
+	case UPDATE_FW_VERSION_ERROR:
+		ret = scnprintf(buf, strlen(buf),
+			"ERROR: %d - FW vervion newer than bin file\n", status);
+		break;
+	case UPDATE_FW_ERASE_ERROR:
+		ret = scnprintf(buf, strlen(buf),
+			"ERROR: %d - ROM BL failed to erase FW file in FLASH\n",
+			status);
+		break;
+	case UPDATE_FW_FILE_CLOSE_ERROR:
+		ret = scnprintf(buf, strlen(buf),
+			"ERROR: %d - ROM BL failed to close FW file in FLASH\n",
+			status);
+		break;
+	case UPDATE_FW_WRITE_ERROR:
+		ret = scnprintf(buf, strlen(buf),
+			"ERROR: %d - ROM BL file write failure\n", status);
+		break;
+	case UPDATE_FW_EXECUTE_ERROR:
+		ret = scnprintf(buf, strlen(buf),
+			"ERROR: %d - ROM BL failed to execute RAM image\n",
+			status);
+		break;
+	case UPDATE_FW_RESET_ERROR:
+		ret = scnprintf(buf, strlen(buf),
+			"ERROR: %d - Reset DUT failure\n",
+			status);
+		break;
+	case UPDATE_FW_MODE_ERROR:
+		ret = scnprintf(buf, strlen(buf),
+			"ERROR: %d - Program complete, Incorrect BL/APP mode detected after reset sentinel\n",
+			status);
+		break;
+	case UPDATE_FW_ENTER_BL_ERROR:
+		ret = scnprintf(buf, strlen(buf),
+			"ERROR: %d - Could not enter the BL\n", status);
+		break;
+	case UPDATE_FW_FILE_OPEN_ERROR:
+		ret = scnprintf(buf, strlen(buf),
+			"ERROR: %d - ROM BL failed to open FW file in FLASH\n",
+			status);
+		break;
+	case UPDATE_FW_SENTINEL_NOT_SEEN:
+		ret = scnprintf(buf, strlen(buf),
+			"ERROR: %d - FW Reset Sentinel not seen after XRES\n",
+			status);
+		break;
+	case UPDATE_FW_EXCLUSIVE_ACCESS_ERROR:
+		ret = scnprintf(buf, strlen(buf),
+			"ERROR: %d - Failed to get DUT exclusive access\n",
+			status);
+		break;
+	case UPDATE_FW_NO_FW_PROVIDED:
+		ret = scnprintf(buf, strlen(buf),
+			"ERROR: %d - No FW provided to load\n", status);
+		break;
+	case UPDATE_FW_INVALID_FW_IMAGE:
+		ret = scnprintf(buf, strlen(buf), "ERROR: %d - Invalid FW image\n", status);
+		break;
+	case UPDATE_FW_MISALIGN_FW_IMAGE:
+		ret = scnprintf(buf, strlen(buf),
+			"ERROR: %d - FW image is misaligned\n", status);
+		break;
+	case UPDATE_FW_SYSTEM_NOMEM:
+		ret = scnprintf(buf, strlen(buf),
+			"ERROR: %d - Failed to alloc memory\n", status);
+		break;
+	case UPDATE_FW_INIT_BL_ERROR:
+		ret = scnprintf(buf, strlen(buf),
+			"ERROR: %d - Failed to init bootloader\n", status);
+		break;
+	case UPDATE_FW_PARSE_ROW_ERROR:
+		ret = scnprintf(buf, strlen(buf),
+			"ERROR: %d - Failed to parse row of FW image\n",
+			status);
+		break;
+	case UPDATE_FW_PROGRAM_ROW_ERROR:
+		ret = scnprintf(buf, strlen(buf),
+			"ERROR: %d - Failed to progrem FW image\n", status);
+		break;
+	case UPDATE_FW_EXIT_BL_ERROR:
+		ret = scnprintf(buf, strlen(buf),
+			"ERROR: %d - Failed to exit bootloader\n", status);
+		break;
+	case UPDATE_FW_CHECK_SUM_ERROR:
+		ret = scnprintf(buf, strlen(buf),
+			"ERROR: %d - Failed to verify app checksum\n", status);
+		break;
+	case UPDATE_FW_NO_PLATFORM_DATA:
+		ret = scnprintf(buf, strlen(buf),
+			"ERROR: %d - No platform data\n", status);
+		break;
+	case UPDATE_FW_UNDEFINED_ERROR:
+	default:
+		ret = scnprintf(buf, strlen(buf), "ERROR: %d - Unknown error\n", status);
+		break;
+	}
+	return ret;
+}
+static DEVICE_ATTR(pip2_bl_status, 0444, pt_pip2_bl_status_show, NULL);
+#if PT_FW_UPGRADE
+static DEVICE_ATTR(update_fw_status, 0444, pt_pip2_bl_status_show, NULL);
+#endif
+/*******************************************************************************
+ * FUNCTION: pt_pip2_get_last_error_show
+ *
+ * SUMMARY: The show method for the pip2_get_last_error_show sysfs node.
+ *	Shows the last BL error code.
+ *
+ *	NOTE: If function is called when DUT is already in BL mode, the DUT
+ *	will remain in BL mode when function exits, however if DUT is in
+ *	normal mode when function is called, the DUT will be forced into BL
+ *	mode and then returned to normal mode when function exits.
+ *
+ * PARAMETERS:
+ *	*dev  - pointer to device structure
+ *	*attr - pointer to device attributes structure
+ *	*buf  - pointer to print output buffer
+ ******************************************************************************/
+static ssize_t pt_pip2_get_last_error_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	int rc;
+	u8 read_buf[256];
+
+	rc = cmd->request_exclusive(dev, PT_LDR_REQUEST_EXCLUSIVE_TIMEOUT);
+	if (rc)
+		goto exit;
+
+	cmd->request_stop_wd(dev);
+
+	/* This functionality is only available in the BL */
+	if (cd->mode != PT_MODE_BOOTLOADER) {
+		rc = -EPERM;
+		goto exit_release;
+	}
+
+	/* Get and log the last error(s) */
+	rc = _pt_pip2_log_last_error(dev, read_buf);
+
+exit_release:
+	cmd->release_exclusive(dev);
+exit:
+	if (rc)
+		return scnprintf(buf, PT_MAX_PRBUF_SIZE, "Status: %d\n", rc);
+
+	if (read_buf[PIP2_RESP_STATUS_OFFSET] == PIP2_RSP_ERR_NONE) {
+		return scnprintf(buf, PT_MAX_PRBUF_SIZE,
+			"Status: %d\n"
+			"Last Error No: 0x%02X\n",
+			PIP2_RSP_ERR_NONE,
+			read_buf[PIP2_RESP_BODY_OFFSET]);
+	} else {
+		return scnprintf(buf, PT_MAX_PRBUF_SIZE,
+			"Status: %d\n"
+			"Last Error No: n/a\n",
+			read_buf[PIP2_RESP_STATUS_OFFSET]);
+	}
+}
+static DEVICE_ATTR(pip2_get_last_error, 0444,
+	pt_pip2_get_last_error_show, NULL);
+
+#if PT_FW_UPGRADE
+/*******************************************************************************
+ * FUNCTION: pt_loader_attention
+ *
+ * SUMMARY: Function to be registered to TTDL attention list to set up
+ *  int_running semaphore.
+ *
+ * RETURN:
+ *   0 = success
+ *
+ * PARAMETERS:
+ *  *dev  - pointer to device structure
+ ******************************************************************************/
+static int pt_loader_attention(struct device *dev)
+{
+	struct pt_loader_data *ld = pt_get_loader_data(dev);
+
+	complete(&ld->int_running);
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_fw_upgrade_cb
+ *
+ * SUMMARY: Function to be registered to TTDL attention list to allow upgrade
+ *  if host cannot get response from firmware with ping command.
+ * RETURN:
+ *   0 = success
+ *
+ * PARAMETERS:
+ *  *dev  - pointer to device structure
+ ******************************************************************************/
+static int pt_fw_upgrade_cb(struct device *dev)
+{
+	u8 dut_gen = cmd->request_dut_generation(dev);
+
+#ifdef CONFIG_TOUCHSCREEN_PARADE_PLATFORM_FW_UPGRADE
+	if (dut_gen == DUT_PIP1_ONLY) {
+		pt_debug(dev, DL_WARN, "%s: Upgrade Platform FW", __func__);
+		if (!upgrade_firmware_from_platform(dev, false))
+			return 0;
+	}
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE
+	pt_debug(dev, DL_WARN, "%s: Upgrade Builtin FW", __func__);
+	if (dut_gen == DUT_PIP2_CAPABLE) {
+		if (!pt_pip2_upgrade_firmware_from_builtin(dev))
+			return 0;
+		pt_debug(dev, DL_WARN, "%s: Builtin FW upgrade failed",
+			__func__);
+	} else {
+		if (!upgrade_firmware_from_builtin(dev))
+			return 0;
+	}
+#endif
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_cancel_fw_upgrade_cb
+ *
+ * SUMMARY: Function to be registered to TTDL attention list to allow an upgrade
+ *	to be canceled.
+ * RETURN:
+ *   0 = success
+ *
+ * PARAMETERS:
+ *  *dev  - pointer to device structure
+ ******************************************************************************/
+static int pt_cancel_fw_upgrade_cb(struct device *dev)
+{
+	struct pt_loader_data *ld = pt_get_loader_data(dev);
+
+	pt_debug(dev, DL_WARN,
+		"%s: CANCELLING All Loader work\n", __func__);
+	cancel_work_sync(&ld->bl_from_file);
+	cancel_work_sync(&ld->pip2_bl_from_file);
+	cancel_work_sync(&ld->calibration_work);
+	cancel_work_sync(&ld->fw_and_config_upgrade);
+
+	return 0;
+}
+#endif /* PT_FW_UPGRADE */
+
+/*******************************************************************************
+ * FUNCTION: pt_loader_probe
+ *
+ * SUMMARY: The probe function for the FW loader.
+ *
+ * PARAMETERS:
+ *   *dev   - pointer to device structure
+ *  **data  - double pointer to the loader data to be created here
+ ******************************************************************************/
+static int pt_loader_probe(struct device *dev, void **data)
+{
+	struct pt_loader_data *ld;
+	struct pip2_loader_data *pip2_data;
+	struct pt_platform_data *pdata = dev_get_platdata(dev);
+	int rc;
+	u8 dut_gen = cmd->request_dut_generation(dev);
+
+#ifdef TTDL_DIAGNOSTICS
+	pt_debug(dev, DL_INFO,
+		"%s: entering %s\n", __func__, __func__);
+#endif /* TTDL_DIAGNOSTICS */
+
+	ld = kzalloc(sizeof(*ld), GFP_KERNEL);
+	if (!ld) {
+		rc = -ENOMEM;
+		goto error_alloc_data_failed;
+	}
+
+#if PT_FW_UPGRADE
+	/* Initialize boot loader status */
+	if (update_fw_status != UPDATE_FW_COMPLETE)
+		_pt_pip2_update_bl_status(dev, UPDATE_FW_IDLE, PT_NO_INC);
+#endif
+
+	if (dut_gen == DUT_PIP2_CAPABLE) {
+		pip2_data = kzalloc(sizeof(*pip2_data), GFP_KERNEL);
+		if (!pip2_data) {
+			rc = -ENOMEM;
+			goto error_alloc_data_failed;
+		}
+		pip2_data->dev = dev;
+		ld->pip2_data = pip2_data;
+
+#if PT_FW_UPGRADE
+		rc = device_create_file(dev, &dev_attr_update_fw_status);
+		if (rc) {
+			pt_debug(dev, DL_ERROR,
+				"%s: Error creating update_fw_status\n",
+				__func__);
+			goto remove_files;
+		}
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE
+		rc = device_create_file(dev, &dev_attr_pt_bl_from_file);
+		if (rc) {
+			pt_debug(dev, DL_ERROR,
+				"%s: Error creating pt_bl_from_file\n",
+				__func__);
+			goto remove_files;
+		}
+		rc = device_create_file(dev, &dev_attr_update_fw);
+		if (rc) {
+			pt_debug(dev, DL_ERROR,
+				"%s: Error creating update_fw\n",
+				__func__);
+			goto remove_files;
+		}
+#endif /* CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE */
+
+#ifdef CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE
+		rc = device_create_file(dev, &dev_attr_pip2_manual_upgrade);
+		if (rc) {
+			pt_debug(dev, DL_ERROR,
+				"%s: Error creating pip2_manual_upgrade\n",
+				__func__);
+			goto remove_files;
+		}
+		rc = device_create_file(dev, &dev_attr_pip2_manual_ram_upgrade);
+		if (rc) {
+			pt_debug(dev, DL_ERROR,
+				"%s: Error creating pip2_manual_ram_upgrade\n",
+				__func__);
+			goto remove_files;
+		}
+		rc = device_create_file(dev, &dev_attr_pip2_file_write);
+		if (rc) {
+			pt_debug(dev, DL_ERROR,
+				"%s: Error creating pip2_file_write\n",
+				__func__);
+			goto remove_files;
+		}
+		rc = device_create_file(dev, &dev_attr_pip2_bl_from_file);
+		if (rc) {
+			pt_debug(dev, DL_ERROR,
+				"%s: Error creating pip2_bl_from_file\n",
+				__func__);
+			goto remove_files;
+		}
+
+#endif /* CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE */
+		rc = device_create_file(dev, &dev_attr_pip2_file_erase);
+		if (rc) {
+			pt_debug(dev, DL_ERROR,
+				"%s: Error creating pip2_file_erase\n",
+				__func__);
+			goto remove_files;
+		}
+		rc = device_create_file(dev, &dev_attr_pip2_bl_status);
+		if (rc) {
+			pt_debug(dev, DL_ERROR,
+				"%s: Error creating pip2_bl_status\n",
+				__func__);
+			goto remove_files;
+		}
+
+#ifdef TTDL_DIAGNOSTICS
+		rc = device_create_file(dev, &dev_attr_pip2_get_last_error);
+		if (rc) {
+			pt_debug(dev, DL_ERROR,
+				"%s: Error creating pip2_get_last_error\n",
+				__func__);
+			goto remove_files;
+		}
+		rc = device_create_bin_file(dev, &bin_attr_pip2_file_read);
+		if (rc) {
+			pt_debug(dev, DL_ERROR,
+				"%s: Error creating bin_attr_pip2_file_read\n",
+				__func__);
+			goto remove_files;
+		}
+		rc = device_create_file(dev, &dev_attr_pip2_file_crc);
+		if (rc) {
+			pt_debug(dev, DL_ERROR,
+				"%s: Error creating pip2_file_crc\n",
+				__func__);
+			goto remove_files;
+		}
+#endif
+	} else {
+#if PT_FW_UPGRADE
+		rc = device_create_file(dev, &dev_attr_update_fw_status);
+		if (rc) {
+			pt_debug(dev, DL_ERROR,
+				"%s: Error creating update_fw_status\n",
+				__func__);
+			goto remove_files;
+		}
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE
+		rc = device_create_file(dev, &dev_attr_pt_bl_from_file);
+		if (rc) {
+			pt_debug(dev, DL_ERROR,
+				"%s: Error creating pt_bl_from_file\n",
+				__func__);
+			goto remove_files;
+		}
+		rc = device_create_file(dev, &dev_attr_update_fw);
+		if (rc) {
+			pt_debug(dev, DL_ERROR,
+				"%s: Error creating update_fw\n",
+				__func__);
+			goto remove_files;
+		}
+#endif /* CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE */
+#ifdef CONFIG_TOUCHSCREEN_PARADE_PLATFORM_FW_UPGRADE
+		rc = device_create_file(dev, &dev_attr_forced_upgrade);
+		if (rc) {
+			pt_debug(dev, DL_ERROR,
+				"%s: Error creating forced_upgrade\n",
+				__func__);
+			goto remove_files;
+		}
+#endif /* CONFIG_TOUCHSCREEN_PARADE_PLATFORM_FW_UPGRADE */
+#ifdef CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE
+		rc = device_create_file(dev, &dev_attr_manual_upgrade);
+		if (rc) {
+			pt_debug(dev, DL_ERROR,
+				"%s: Error creating manual_upgrade\n",
+				__func__);
+			goto remove_files;
+		}
+#endif /* CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE */
+#ifdef CONFIG_TOUCHSCREEN_PARADE_MANUAL_TTCONFIG_UPGRADE
+		rc = device_create_file(dev, &dev_attr_config_loading);
+		if (rc) {
+			pt_debug(dev, DL_ERROR,
+				"%s: Error creating config_loading\n",
+				__func__);
+			goto remove_files;
+		}
+
+		rc = device_create_bin_file(dev, &bin_attr_config_data);
+		if (rc) {
+			pt_debug(dev, DL_ERROR,
+				"%s: Error creating config_data\n",
+				__func__);
+			goto remove_files;
+		}
+#endif /* CONFIG_TOUCHSCREEN_PARADE_MANUAL_TTCONFIG_UPGRADE */
+	}
+
+	if (!pdata || !pdata->loader_pdata) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Missing platform data\n", __func__);
+		rc = -ENODEV;
+		goto error_no_pdata;
+	}
+
+	/* Default erase file to an invalid file */
+	ld->pip2_file_erase_file_no = PIP2_RAM_FILE;
+
+	ld->loader_pdata = pdata->loader_pdata;
+	ld->dev = dev;
+	*data = ld;
+
+#ifdef CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE
+	INIT_WORK(&ld->bl_from_file, pt_bl_from_file_work);
+	INIT_WORK(&ld->pip2_bl_from_file, pt_pip2_bl_from_file_work);
+#endif /* CONFIG_TOUCHSCREEN_PARADE_MANUAL_TTCONFIG_UPGRADE */
+
+#if PT_FW_UPGRADE
+	init_completion(&ld->int_running);
+
+	cmd->subscribe_attention(dev, PT_ATTEN_IRQ, PT_LOADER_NAME,
+		pt_loader_attention, PT_MODE_BOOTLOADER);
+
+	cmd->subscribe_attention(dev, PT_ATTEN_LOADER, PT_LOADER_NAME,
+		pt_fw_upgrade_cb, PT_MODE_UNKNOWN);
+
+	cmd->subscribe_attention(dev, PT_ATTEN_CANCEL_LOADER, PT_LOADER_NAME,
+		pt_cancel_fw_upgrade_cb, PT_MODE_UNKNOWN);
+#endif
+#if PT_FW_UPGRADE || PT_TTCONFIG_UPGRADE
+	pt_debug(dev, DL_INFO, "%s: INIT_WORK pt_calibrate_idacs\n",
+		__func__);
+	init_completion(&ld->calibration_complete);
+	INIT_WORK(&ld->calibration_work, pt_calibrate_idacs);
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_PARADE_MANUAL_TTCONFIG_UPGRADE
+	if (dut_gen == DUT_PIP1_ONLY)
+		mutex_init(&ld->config_lock);
+#endif
+
+	pt_debug(dev, DL_INFO, "%s: Schedule FW upgrade work\n", __func__);
+	INIT_WORK(&ld->fw_and_config_upgrade, pt_fw_and_config_upgrade);
+	schedule_work(&ld->fw_and_config_upgrade);
+
+	pt_debug(dev, DL_INFO, "%s: Successful probe %s\n",
+		__func__, dev_name(dev));
+	return 0;
+
+
+remove_files:
+#ifdef CONFIG_TOUCHSCREEN_PARADE_MANUAL_TTCONFIG_UPGRADE
+	device_remove_file(dev, &dev_attr_config_loading);
+#endif
+#ifdef CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE
+	device_remove_file(dev, &dev_attr_manual_upgrade);
+#endif
+#ifdef CONFIG_TOUCHSCREEN_PARADE_PLATFORM_FW_UPGRADE
+	device_remove_file(dev, &dev_attr_forced_upgrade);
+#endif
+#ifdef TTDL_DIAGNOSTICS
+	device_remove_file(dev, &dev_attr_pip2_get_last_error);
+	device_remove_bin_file(dev, &bin_attr_pip2_file_read);
+	device_remove_file(dev, &dev_attr_pip2_file_crc);
+#endif
+	device_remove_file(dev, &dev_attr_pip2_bl_status);
+	device_remove_file(dev, &dev_attr_pip2_file_erase);
+#ifdef CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE
+	device_remove_file(dev, &dev_attr_pip2_bl_from_file);
+	device_remove_file(dev, &dev_attr_pip2_file_write);
+	device_remove_file(dev, &dev_attr_pt_bl_from_file);
+	device_remove_file(dev, &dev_attr_pip2_manual_ram_upgrade);
+	device_remove_file(dev, &dev_attr_pip2_manual_upgrade);
+	device_remove_file(dev, &dev_attr_update_fw);
+	device_remove_file(dev, &dev_attr_update_fw_status);
+#endif /* CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE */
+
+	kfree(ld->pip2_data);
+	kfree(ld);
+error_alloc_data_failed:
+error_no_pdata:
+	pt_debug(dev, DL_ERROR, "%s failed.\n", __func__);
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_loader_release
+ *
+ * SUMMARY: Remove function for loader module that does following cleanup:
+ *  - Unsubscibe all registered attention tasks
+ *  - Removes all created sysfs nodes
+ *  - Frees all pointers
+ *
+ * PARAMETERS:
+ *  *dev   - pointer to device structure
+ *  *data  - pointer to the loader data
+ ******************************************************************************/
+static void pt_loader_release(struct device *dev, void *data)
+{
+	struct pt_loader_data *ld = (struct pt_loader_data *)data;
+	u8 dut_gen =  cmd->request_dut_generation(dev);
+
+#if PT_FW_UPGRADE
+	cmd->unsubscribe_attention(dev, PT_ATTEN_IRQ, PT_LOADER_NAME,
+		pt_loader_attention, PT_MODE_BOOTLOADER);
+
+	cmd->unsubscribe_attention(dev, PT_ATTEN_LOADER, PT_LOADER_NAME,
+		pt_fw_upgrade_cb, PT_MODE_UNKNOWN);
+
+	cmd->unsubscribe_attention(dev, PT_ATTEN_CANCEL_LOADER, PT_LOADER_NAME,
+		pt_cancel_fw_upgrade_cb, PT_MODE_UNKNOWN);
+#endif
+#if PT_FW_UPGRADE
+	device_remove_file(dev, &dev_attr_update_fw_status);
+#endif
+	if (dut_gen == DUT_PIP2_CAPABLE) {
+#ifdef CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE
+		device_remove_file(dev, &dev_attr_update_fw);
+#endif /* CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE */
+#ifdef TTDL_DIAGNOSTICS
+		device_remove_bin_file(dev, &bin_attr_pip2_file_read);
+		device_remove_file(dev, &dev_attr_pip2_file_crc);
+#endif
+		device_remove_file(dev, &dev_attr_pip2_get_last_error);
+		device_remove_file(dev, &dev_attr_pip2_bl_status);
+		device_remove_file(dev, &dev_attr_pip2_file_erase);
+#ifdef CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE
+		device_remove_file(dev, &dev_attr_pip2_file_write);
+		device_remove_file(dev, &dev_attr_pip2_bl_from_file);
+		device_remove_file(dev, &dev_attr_pt_bl_from_file);
+		device_remove_file(dev, &dev_attr_pip2_manual_ram_upgrade);
+		device_remove_file(dev, &dev_attr_pip2_manual_upgrade);
+#endif /* CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE */
+		kfree(ld->pip2_data);
+	} else {
+#ifdef CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE
+		device_remove_file(dev, &dev_attr_update_fw);
+#endif /* CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE */
+#ifdef CONFIG_TOUCHSCREEN_PARADE_MANUAL_TTCONFIG_UPGRADE
+		device_remove_bin_file(dev, &bin_attr_config_data);
+		device_remove_file(dev, &dev_attr_config_loading);
+		if (!ld->config_data)
+			kfree(ld->config_data);
+#endif /* CONFIG_TOUCHSCREEN_PARADE_MANUAL_TTCONFIG_UPGRADE */
+#ifdef CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE
+		device_remove_file(dev, &dev_attr_pt_bl_from_file);
+		device_remove_file(dev, &dev_attr_manual_upgrade);
+#endif /* CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE */
+#ifdef CONFIG_TOUCHSCREEN_PARADE_PLATFORM_FW_UPGRADE
+		device_remove_file(dev, &dev_attr_forced_upgrade);
+#endif /* CONFIG_TOUCHSCREEN_PARADE_PLATFORM_FW_UPGRADE */
+	}
+	kfree(ld);
+}
+
+static struct pt_module loader_module = {
+	.name = PT_LOADER_NAME,
+	.probe = pt_loader_probe,
+	.release = pt_loader_release,
+};
+
+/*******************************************************************************
+ * FUNCTION: pt_loader_init
+ *
+ * SUMMARY: Initialize function for loader module which to register
+ * loader_module into TTDL module list.
+ *
+ * RETURN:
+ *   0 = success
+ *  !0 = failure
+ *
+ * PARAMETERS:
+ *  *dev   - pointer to device structure
+ *  *data  - pointer to the loader data
+ ******************************************************************************/
+static int __init pt_loader_init(void)
+{
+	int rc;
+
+	cmd = pt_get_commands();
+	if (!cmd)
+		return -EINVAL;
+
+	rc = pt_register_module(&loader_module);
+	if (rc < 0) {
+		pr_err("%s: Error, failed registering module\n",
+			__func__);
+			return rc;
+	}
+
+	pr_info("%s: Parade FW Loader Driver (Version %s) rc=%d\n",
+		 __func__, PT_DRIVER_VERSION, rc);
+	return 0;
+}
+module_init(pt_loader_init);
+
+/*******************************************************************************
+ * FUNCTION: pt_loader_exit
+ *
+ * SUMMARY: Exit function for loader module which to unregister loader_module
+ * from TTDL module list.
+ *
+ ******************************************************************************/
+static void __exit pt_loader_exit(void)
+{
+	pt_unregister_module(&loader_module);
+}
+module_exit(pt_loader_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Parade TrueTouch(R) Standard Product FW Loader Driver");
+MODULE_AUTHOR("Parade Technologies <[email protected]>");

+ 1006 - 0
qcom/opensource/touch-drivers/pt/pt_mt_common.c

@@ -0,0 +1,1006 @@
+/*
+ * pt_mt_common.c
+ * Parade TrueTouch(TM) Standard Product Multi-Touch Reports Module.
+ * For use with Parade touchscreen controllers.
+ * Supported parts include:
+ * TMA5XX
+ * TMA448
+ * TMA445A
+ * TT21XXX
+ * TT31XXX
+ * TT4XXXX
+ * TT7XXX
+ * TC3XXX
+ *
+ * Copyright (C) 2015-2020 Parade Technologies
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * Contact Parade Technologies at www.paradetech.com <[email protected]>
+ */
+
+#include "pt_regs.h"
+
+#define MT_PARAM_SIGNAL(md, sig_ost) PARAM_SIGNAL(md->pdata->frmwrk, sig_ost)
+#define MT_PARAM_MIN(md, sig_ost) PARAM_MIN(md->pdata->frmwrk, sig_ost)
+#define MT_PARAM_MAX(md, sig_ost) PARAM_MAX(md->pdata->frmwrk, sig_ost)
+#define MT_PARAM_FUZZ(md, sig_ost) PARAM_FUZZ(md->pdata->frmwrk, sig_ost)
+#define MT_PARAM_FLAT(md, sig_ost) PARAM_FLAT(md->pdata->frmwrk, sig_ost)
+
+/*******************************************************************************
+ * FUNCTION: pt_mt_lift_all
+ *
+ * SUMMARY: Reports touch liftoff action
+ *
+ * PARAMETERS:
+ *     *md - pointer to touch data structure
+ ******************************************************************************/
+static void pt_mt_lift_all(struct pt_mt_data *md)
+{
+	int max = md->si->tch_abs[PT_TCH_T].max;
+
+	if (md->num_prv_rec != 0) {
+		if (md->mt_function.report_slot_liftoff)
+			md->mt_function.report_slot_liftoff(md, max);
+		input_sync(md->input);
+		md->num_prv_rec = 0;
+	}
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_get_touch_axis
+ *
+ * SUMMARY: Calculates touch axis
+ *
+ * PARAMETERS:
+ *     *md      - pointer to touch data structure
+ *     *axis    - pointer to axis calculation result
+ *      size    - size in byte
+ *      max     - max value of result
+ *     *xy_data - pointer to input data to be parsed
+ *      bofs    - bit offset
+ ******************************************************************************/
+static void pt_get_touch_axis(struct pt_mt_data *md,
+	int *axis, int size, int max, u8 *xy_data, int bofs)
+{
+	int nbyte;
+	int next;
+
+	for (nbyte = 0, *axis = 0, next = 0; nbyte < size; nbyte++) {
+		pt_debug(md->dev, DL_DEBUG,
+			"%s: *axis=%02X(%d) size=%d max=%08X xy_data=%p xy_data[%d]=%02X(%d) bofs=%d\n",
+			__func__, *axis, *axis, size, max, xy_data, next,
+			xy_data[next], xy_data[next], bofs);
+		*axis = *axis + ((xy_data[next] >> bofs) << (nbyte * 8));
+		next++;
+	}
+
+	*axis &= max - 1;
+
+	pt_debug(md->dev, DL_DEBUG,
+		"%s: *axis=%02X(%d) size=%d max=%08X xy_data=%p xy_data[%d]=%02X(%d)\n",
+		__func__, *axis, *axis, size, max, xy_data, next,
+		xy_data[next], xy_data[next]);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_get_touch_hdr
+ *
+ * SUMMARY: Get the header of touch report
+ *
+ * PARAMETERS:
+ *     *md      - pointer to touch data structure
+ *     *touch   - pointer to pt_touch structure
+ *     *xy_mode - pointer to touch mode data
+ ******************************************************************************/
+static void pt_get_touch_hdr(struct pt_mt_data *md,
+	struct pt_touch *touch, u8 *xy_mode)
+{
+	struct device *dev = md->dev;
+	struct pt_sysinfo *si = md->si;
+	enum pt_tch_hdr hdr;
+
+	for (hdr = PT_TCH_TIME; hdr < PT_TCH_NUM_HDR; hdr++) {
+		if (!si->tch_hdr[hdr].report)
+			continue;
+		pt_get_touch_axis(md, &touch->hdr[hdr],
+			si->tch_hdr[hdr].size,
+			si->tch_hdr[hdr].max,
+			xy_mode + si->tch_hdr[hdr].ofs,
+			si->tch_hdr[hdr].bofs);
+		pt_debug(dev, DL_DEBUG, "%s: get %s=%04X(%d)\n",
+			__func__, pt_tch_hdr_string[hdr],
+			touch->hdr[hdr], touch->hdr[hdr]);
+	}
+
+	pt_debug(dev, DL_INFO,
+		"%s: time=%X tch_num=%d lo=%d noise=%d counter=%d\n",
+		__func__,
+		touch->hdr[PT_TCH_TIME],
+		touch->hdr[PT_TCH_NUM],
+		touch->hdr[PT_TCH_LO],
+		touch->hdr[PT_TCH_NOISE],
+		touch->hdr[PT_TCH_COUNTER]);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_get_touch_record
+ *
+ * SUMMARY: Gets axis of touch report
+ *
+ * PARAMETERS:
+ *     *md      - pointer to touch data structure
+ *     *touch   - pointer to pt_touch structure
+ *     *xy_data - pointer to touch data
+ ******************************************************************************/
+static void pt_get_touch_record(struct pt_mt_data *md,
+	struct pt_touch *touch, u8 *xy_data)
+{
+	struct device *dev = md->dev;
+	struct pt_sysinfo *si = md->si;
+	enum pt_tch_abs abs;
+
+	for (abs = PT_TCH_X; abs < PT_TCH_NUM_ABS; abs++) {
+		if (!si->tch_abs[abs].report)
+			continue;
+		pt_get_touch_axis(md, &touch->abs[abs],
+			si->tch_abs[abs].size,
+			si->tch_abs[abs].max,
+			xy_data + si->tch_abs[abs].ofs,
+			si->tch_abs[abs].bofs);
+		pt_debug(dev, DL_DEBUG, "%s: get %s=%04X(%d)\n",
+			__func__, pt_tch_abs_string[abs],
+			touch->abs[abs], touch->abs[abs]);
+	}
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_mt_process_touch
+ *
+ * SUMMARY: Process touch includes oritation,axis invert and
+ *  convert MAJOR/MINOR from mm to resolution
+ *
+ * PARAMETERS:
+ *     *md      - pointer to touch data structure
+ *     *touch   - pointer to pt_touch structure
+ ******************************************************************************/
+static void pt_mt_process_touch(struct pt_mt_data *md,
+	struct pt_touch *touch)
+{
+	struct device *dev = md->dev;
+	struct pt_sysinfo *si = md->si;
+	int tmp;
+	bool flipped;
+
+
+	/* Orientation is signed */
+	touch->abs[PT_TCH_OR] = (int8_t)touch->abs[PT_TCH_OR];
+
+	if (md->pdata->flags & PT_MT_FLAG_FLIP) {
+		tmp = touch->abs[PT_TCH_X];
+		touch->abs[PT_TCH_X] = touch->abs[PT_TCH_Y];
+		touch->abs[PT_TCH_Y] = tmp;
+		if (touch->abs[PT_TCH_OR] > 0)
+			touch->abs[PT_TCH_OR] =
+				md->or_max - touch->abs[PT_TCH_OR];
+		else
+			touch->abs[PT_TCH_OR] =
+				md->or_min - touch->abs[PT_TCH_OR];
+		flipped = true;
+	} else
+		flipped = false;
+
+	/*
+	 * 1 is subtracted from each touch location to make the location
+	 * 0 based. e.g. If the resolution of touch panel is 1200x1600,
+	 * the FW touch report must be (0~1199,0~1599). The driver
+	 * should register the (min,max) value to Linux input system as
+	 * (0~1199,0~1599). When the host needs to invert the
+	 * coordinates, the driver would incorrectly use the resolution
+	 * to subtract the reported point directly, such as
+	 * 1200-(0~1199). The input system will lose the 0 point report
+	 * and the 1200 point will be ignored.
+	 */
+	if (md->pdata->flags & PT_MT_FLAG_INV_X) {
+		if (flipped)
+			touch->abs[PT_TCH_X] = si->sensing_conf_data.res_y -
+				touch->abs[PT_TCH_X] - 1;
+		else
+			touch->abs[PT_TCH_X] = si->sensing_conf_data.res_x -
+				touch->abs[PT_TCH_X] - 1;
+		touch->abs[PT_TCH_OR] *= -1;
+	}
+	if (md->pdata->flags & PT_MT_FLAG_INV_Y) {
+		if (flipped)
+			touch->abs[PT_TCH_Y] = si->sensing_conf_data.res_x -
+				touch->abs[PT_TCH_Y] - 1;
+		else
+			touch->abs[PT_TCH_Y] = si->sensing_conf_data.res_y -
+				touch->abs[PT_TCH_Y] - 1;
+		touch->abs[PT_TCH_OR] *= -1;
+	}
+
+	/* Convert MAJOR/MINOR from mm to resolution */
+	tmp = touch->abs[PT_TCH_MAJ] * 100 * si->sensing_conf_data.res_x;
+	touch->abs[PT_TCH_MAJ] = tmp / si->sensing_conf_data.len_x;
+	tmp = touch->abs[PT_TCH_MIN] * 100 * si->sensing_conf_data.res_x;
+	touch->abs[PT_TCH_MIN] = tmp / si->sensing_conf_data.len_x;
+
+	pt_debug(dev, DL_INFO,
+		"%s: flip=%s inv-x=%s inv-y=%s x=%04X(%d) y=%04X(%d)\n",
+		__func__, flipped ? "true" : "false",
+		md->pdata->flags & PT_MT_FLAG_INV_X ? "true" : "false",
+		md->pdata->flags & PT_MT_FLAG_INV_Y ? "true" : "false",
+		touch->abs[PT_TCH_X], touch->abs[PT_TCH_X],
+		touch->abs[PT_TCH_Y], touch->abs[PT_TCH_Y]);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_report_event
+ *
+ * SUMMARY: Reports touch event
+ *
+ * PARAMETERS:
+ *     *md      - pointer to touch data structure
+ *      event   - type of touch event
+ *      value   - value of report event
+ ******************************************************************************/
+static void pt_report_event(struct pt_mt_data *md, int event,
+		int value)
+{
+	int sig = MT_PARAM_SIGNAL(md, event);
+
+	if (sig != PT_IGNORE_VALUE)
+		input_report_abs(md->input, sig, value);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_get_mt_touches
+ *
+ * SUMMARY: Parse and report touch event
+ *
+ * PARAMETERS:
+ *     *md          - pointer to touch data structure
+ *     *tch         - pointer to touch structure
+ *      num_cur_tch - number of current touch
+ ******************************************************************************/
+static void pt_get_mt_touches(struct pt_mt_data *md,
+		struct pt_touch *tch, int num_cur_tch)
+{
+	struct device *dev = md->dev;
+	struct pt_sysinfo *si = md->si;
+	int sig;
+	int i, j, t = 0;
+	DECLARE_BITMAP(ids, PT_TOUCH_ID_MAX);
+	int mt_sync_count = 0;
+	u8 *tch_addr;
+
+	if (PT_TOUCH_ID_MAX < si->tch_abs[PT_TCH_T].max) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Touch ID num %d is allocated less than needed %d\n",
+			__func__, PT_TOUCH_ID_MAX, si->tch_abs[PT_TCH_T].max);
+		return;
+	}
+
+	bitmap_zero(ids, PT_TOUCH_ID_MAX);
+	memset(tch->abs, 0, sizeof(tch->abs));
+
+	for (i = 0; i < num_cur_tch; i++) {
+		tch_addr = si->xy_data + (i * si->desc.tch_record_size);
+		pt_get_touch_record(md, tch, tch_addr);
+
+		/*  Discard proximity event */
+		if (tch->abs[PT_TCH_O] == PT_OBJ_PROXIMITY) {
+			pt_debug(dev, DL_INFO,
+				"%s: Discarding proximity event\n",
+				__func__);
+			continue;
+		}
+
+		/* Validate track_id */
+		t = tch->abs[PT_TCH_T];
+		if (t < md->t_min || t > md->t_max) {
+			pt_debug(dev, DL_INFO,
+				"%s: tch=%d -> bad trk_id=%d max_id=%d\n",
+				__func__, i, t, md->t_max);
+			if (md->mt_function.input_sync)
+				md->mt_function.input_sync(md->input);
+			mt_sync_count++;
+			continue;
+		}
+
+		/* Lift-off */
+		if (tch->abs[PT_TCH_E] == PT_EV_LIFTOFF) {
+			pt_debug(dev, DL_INFO, "%s: t=%d e=%d lift-off\n",
+				__func__, t, tch->abs[PT_TCH_E]);
+			goto pt_get_mt_touches_pr_tch;
+		}
+
+		/* Process touch */
+		pt_mt_process_touch(md, tch);
+
+		/* use 0 based track id's */
+		t -= md->t_min;
+
+		sig = MT_PARAM_SIGNAL(md, PT_ABS_ID_OST);
+		if (sig != PT_IGNORE_VALUE) {
+			if (md->mt_function.input_report)
+				md->mt_function.input_report(md->input, sig,
+						t, tch->abs[PT_TCH_O]);
+			__set_bit(t, ids);
+		}
+
+		pt_report_event(md, PT_ABS_D_OST, 0);
+
+		/* all devices: position and pressure fields */
+		for (j = 0; j <= PT_ABS_W_OST; j++) {
+			if (!si->tch_abs[j].report)
+				continue;
+			pt_report_event(md, PT_ABS_X_OST + j,
+					tch->abs[PT_TCH_X + j]);
+		}
+
+		/* Get the extended touch fields */
+		for (j = 0; j < PT_NUM_EXT_TCH_FIELDS; j++) {
+			if (!si->tch_abs[PT_ABS_MAJ_OST + j].report)
+				continue;
+			pt_report_event(md, PT_ABS_MAJ_OST + j,
+					tch->abs[PT_TCH_MAJ + j]);
+		}
+		if (md->mt_function.input_sync)
+			md->mt_function.input_sync(md->input);
+		mt_sync_count++;
+
+pt_get_mt_touches_pr_tch:
+		pt_debug(dev, DL_INFO,
+			"%s: t=%d x=%d y=%d z=%d M=%d m=%d o=%d e=%d obj=%d tip=%d\n",
+			__func__, t,
+			tch->abs[PT_TCH_X],
+			tch->abs[PT_TCH_Y],
+			tch->abs[PT_TCH_P],
+			tch->abs[PT_TCH_MAJ],
+			tch->abs[PT_TCH_MIN],
+			tch->abs[PT_TCH_OR],
+			tch->abs[PT_TCH_E],
+			tch->abs[PT_TCH_O],
+			tch->abs[PT_TCH_TIP]);
+	}
+
+	if (md->mt_function.final_sync)
+		md->mt_function.final_sync(md->input,
+				si->tch_abs[PT_TCH_T].max, mt_sync_count, ids);
+
+	md->num_prv_rec = num_cur_tch;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_xy_worker
+ *
+ * SUMMARY: Read xy_data for all current touches
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *     *md - pointer to touch data structure
+ ******************************************************************************/
+static int pt_xy_worker(struct pt_mt_data *md)
+{
+	struct device *dev = md->dev;
+	struct pt_sysinfo *si = md->si;
+	int max_tch = si->sensing_conf_data.max_tch;
+	struct pt_touch tch;
+	u8 num_cur_tch;
+	int rc = 0;
+
+	pt_get_touch_hdr(md, &tch, si->xy_mode + 3);
+
+	num_cur_tch = tch.hdr[PT_TCH_NUM];
+	if (num_cur_tch > max_tch) {
+		pt_debug(dev, DL_ERROR, "%s: Num touch err detected (n=%d)\n",
+			__func__, num_cur_tch);
+		num_cur_tch = max_tch;
+	}
+
+	if (tch.hdr[PT_TCH_LO]) {
+		pt_debug(dev, DL_INFO, "%s: Large area detected\n",
+			__func__);
+		if (md->pdata->flags & PT_MT_FLAG_NO_TOUCH_ON_LO)
+			num_cur_tch = 0;
+	}
+
+	if (num_cur_tch == 0 && md->num_prv_rec == 0)
+		goto pt_xy_worker_exit;
+
+	/* extract xy_data for all currently reported touches */
+	pt_debug(dev, DL_DEBUG, "%s: extract data num_cur_tch=%d\n",
+		__func__, num_cur_tch);
+	if (num_cur_tch)
+		pt_get_mt_touches(md, &tch, num_cur_tch);
+	else
+		pt_mt_lift_all(md);
+
+	rc = 0;
+
+pt_xy_worker_exit:
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_mt_send_dummy_event
+ *
+ * SUMMARY: Send dummy/key event to wakeup upper layer of system
+ *
+ * PARAMETERS:
+ *     *cd  - pointer to core data structure
+ *     *md  - pointer to touch data structure
+ ******************************************************************************/
+static void pt_mt_send_dummy_event(struct pt_core_data *cd,
+		struct pt_mt_data *md)
+{
+#ifndef EASYWAKE_TSG6
+	/* TSG5 EasyWake */
+	unsigned long ids = 0;
+
+	/* for easy wakeup */
+	if (md->mt_function.input_report)
+		md->mt_function.input_report(md->input, ABS_MT_TRACKING_ID,
+			0, PT_OBJ_STANDARD_FINGER);
+	if (md->mt_function.input_sync)
+		md->mt_function.input_sync(md->input);
+	if (md->mt_function.final_sync)
+		md->mt_function.final_sync(md->input, 0, 1, &ids);
+	if (md->mt_function.report_slot_liftoff)
+		md->mt_function.report_slot_liftoff(md, 1);
+	if (md->mt_function.final_sync)
+		md->mt_function.final_sync(md->input, 1, 1, &ids);
+#else
+	/* TSG6 FW1.3 and above only. TSG6 FW1.0 - 1.2 does not */
+	/*  support EasyWake, and this function will not be called */
+	u8 key_value = 0;
+
+	switch (cd->gesture_id) {
+	case GESTURE_DOUBLE_TAP:
+		key_value = KEY_WAKEUP;
+		break;
+	case GESTURE_TWO_FINGERS_SLIDE:
+		key_value = KEY_WAKEUP;
+		break;
+	case GESTURE_TOUCH_DETECTED:
+		key_value = KEY_WAKEUP;
+		break;
+	case GESTURE_PUSH_BUTTON:
+		key_value = KEY_F4;
+		break;
+	case GESTURE_SINGLE_SLIDE_DE_TX:
+		key_value = KEY_F5;
+		break;
+	case GESTURE_SINGLE_SLIDE_IN_TX:
+		key_value = KEY_F6;
+		break;
+	case GESTURE_SINGLE_SLIDE_DE_RX:
+		key_value = KEY_F7;
+		break;
+	case GESTURE_SINGLE_SLIDE_IN_RX:
+		key_value = KEY_F8;
+		break;
+	default:
+		break;
+	}
+
+	if (key_value > 0) {
+		input_report_key(md->input, key_value, 1);
+		input_sync(md->input);
+		input_report_key(md->input, key_value, 0);
+		input_sync(md->input);
+	}
+
+	/*
+	 * Caution - this debug print is needed by the TTDL automated
+	 *           regression test suite
+	 */
+	pt_debug(md->dev, DL_INFO, "%s: report key: %d\n",
+		__func__, key_value);
+#endif
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_mt_attention
+ *
+ * SUMMARY: Wrapper function for pt_xy_worker() that subscribe into the TTDL
+ *  attention list.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *     *dev - pointer to device structure
+ ******************************************************************************/
+static int pt_mt_attention(struct device *dev)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_mt_data *md = &cd->md;
+	int rc;
+
+	if (md->si->xy_mode[2] !=  md->si->desc.tch_report_id)
+		return 0;
+
+	/* core handles handshake */
+	mutex_lock(&md->mt_lock);
+	rc = pt_xy_worker(md);
+	mutex_unlock(&md->mt_lock);
+	if (rc < 0)
+		pt_debug(dev, DL_ERROR,
+			"%s: xy_worker error r=%d\n", __func__, rc);
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_mt_wake_attention
+ *
+ * SUMMARY: Wrapper function for pt_mt_send_dummy_event() that register to
+ *  attention list.
+ *
+ * RETURN:
+ *	 0 = success
+ *
+ * PARAMETERS:
+ *     *dev - pointer to device structure
+ ******************************************************************************/
+static int pt_mt_wake_attention(struct device *dev)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_mt_data *md = &cd->md;
+
+	mutex_lock(&md->mt_lock);
+	pt_mt_send_dummy_event(cd, md);
+	mutex_unlock(&md->mt_lock);
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_startup_attention
+ *
+ * SUMMARY: Wrapper function for pt_mt_lift_all() that subcribe into the TTDL
+ *  attention list.
+ *
+ * RETURN:
+ *	 0 = success
+ *
+ * PARAMETERS:
+ *     *dev - pointer to device structure
+ ******************************************************************************/
+static int pt_startup_attention(struct device *dev)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_mt_data *md = &cd->md;
+
+	mutex_lock(&md->mt_lock);
+	pt_mt_lift_all(md);
+	mutex_unlock(&md->mt_lock);
+
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_mt_suspend_attention
+ *
+ * SUMMARY: Function for touch to enter suspend state that as following steps:
+ *          1) Lift all touch
+ *          2) Set flag with suspend state
+ *          3) Decrese pm system count
+ *
+ * RETURN:
+ *	 0 = success
+ *
+ * PARAMETERS:
+ *     *dev - pointer to device structure
+ ******************************************************************************/
+static int pt_mt_suspend_attention(struct device *dev)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_mt_data *md = &cd->md;
+
+	mutex_lock(&md->mt_lock);
+	pt_mt_lift_all(md);
+	md->is_suspended = true;
+	mutex_unlock(&md->mt_lock);
+
+	pm_runtime_put(dev);
+
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_mt_resume_attention
+ *
+ * SUMMARY: Function for touch to leave suspend state that as following steps:
+ *          1) Increse pm system count
+ *          2) Clear suspend state flag
+ *
+ * RETURN:
+ *	 0 = success
+ *
+ * PARAMETERS:
+ *     *dev - pointer to device structure
+ ******************************************************************************/
+static int pt_mt_resume_attention(struct device *dev)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_mt_data *md = &cd->md;
+
+	pm_runtime_get(dev);
+
+	mutex_lock(&md->mt_lock);
+	md->is_suspended = false;
+	mutex_unlock(&md->mt_lock);
+
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_mt_open
+ *
+ * SUMMARY: Open method for input device(touch) that sets up call back
+ *  functions to TTDL attention list
+ *
+ * RETURN:
+ *	 0 = success
+ *
+ * PARAMETERS:
+ *     *input - pointer to input_dev structure
+ ******************************************************************************/
+static int pt_mt_open(struct input_dev *input)
+{
+	struct device *dev = input->dev.parent;
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_mt_data *md = &cd->md;
+
+	pm_runtime_get_sync(dev);
+
+	mutex_lock(&md->mt_lock);
+	md->is_suspended = false;
+	mutex_unlock(&md->mt_lock);
+
+	pt_debug(dev, DL_INFO, "%s: setup subscriptions\n", __func__);
+
+	/* set up touch call back */
+	_pt_subscribe_attention(dev, PT_ATTEN_IRQ, PT_MT_NAME,
+		pt_mt_attention, PT_MODE_OPERATIONAL);
+
+	/* set up startup call back */
+	_pt_subscribe_attention(dev, PT_ATTEN_STARTUP, PT_MT_NAME,
+		pt_startup_attention, 0);
+
+	/* set up wakeup call back */
+	_pt_subscribe_attention(dev, PT_ATTEN_WAKE, PT_MT_NAME,
+		pt_mt_wake_attention, 0);
+
+	/* set up suspend call back */
+	_pt_subscribe_attention(dev, PT_ATTEN_SUSPEND, PT_MT_NAME,
+		pt_mt_suspend_attention, 0);
+
+	/* set up resume call back */
+	_pt_subscribe_attention(dev, PT_ATTEN_RESUME, PT_MT_NAME,
+		pt_mt_resume_attention, 0);
+
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_mt_close
+ *
+ * SUMMARY: Close method for input device(touch) that clears call back
+ *  functions from TTDL attention list.
+ *
+ * PARAMETERS:
+ *     *input - pointer to input_dev structure
+ ******************************************************************************/
+static void pt_mt_close(struct input_dev *input)
+{
+	struct device *dev = input->dev.parent;
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_mt_data *md = &cd->md;
+
+	_pt_unsubscribe_attention(dev, PT_ATTEN_IRQ, PT_MT_NAME,
+		pt_mt_attention, PT_MODE_OPERATIONAL);
+
+	_pt_unsubscribe_attention(dev, PT_ATTEN_STARTUP, PT_MT_NAME,
+		pt_startup_attention, 0);
+
+	_pt_unsubscribe_attention(dev, PT_ATTEN_WAKE, PT_MT_NAME,
+		pt_mt_wake_attention, 0);
+
+	_pt_unsubscribe_attention(dev, PT_ATTEN_SUSPEND, PT_MT_NAME,
+		pt_mt_suspend_attention, 0);
+
+	_pt_unsubscribe_attention(dev, PT_ATTEN_RESUME, PT_MT_NAME,
+		pt_mt_resume_attention, 0);
+
+	mutex_lock(&md->mt_lock);
+	if (!md->is_suspended) {
+		pm_runtime_put(dev);
+		md->is_suspended = true;
+	}
+	mutex_unlock(&md->mt_lock);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_setup_input_device
+ *
+ * SUMMARY: Set up resolution, event signal capabilities and
+ *  register input device for touch.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *     *dev - pointer to device structure
+ ******************************************************************************/
+static int pt_setup_input_device(struct device *dev)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_mt_data *md = &cd->md;
+	int signal = PT_IGNORE_VALUE;
+	int max_x, max_y, max_p, min, max;
+	int max_x_tmp, max_y_tmp;
+	int i;
+	int rc;
+
+	pt_debug(dev, DL_INFO, "%s: Initialize event signals\n",
+		__func__);
+	__set_bit(EV_ABS, md->input->evbit);
+	__set_bit(EV_REL, md->input->evbit);
+	__set_bit(EV_KEY, md->input->evbit);
+#ifdef INPUT_PROP_DIRECT
+	__set_bit(INPUT_PROP_DIRECT, md->input->propbit);
+#endif
+
+	/* If virtualkeys enabled, don't use all screen */
+	if (md->pdata->flags & PT_MT_FLAG_VKEYS) {
+		max_x_tmp = md->pdata->vkeys_x;
+		max_y_tmp = md->pdata->vkeys_y;
+	} else {
+		max_x_tmp = md->si->sensing_conf_data.res_x;
+		max_y_tmp = md->si->sensing_conf_data.res_y;
+	}
+
+	/* get maximum values from the sysinfo data */
+	if (md->pdata->flags & PT_MT_FLAG_FLIP) {
+		max_x = max_y_tmp - 1;
+		max_y = max_x_tmp - 1;
+	} else {
+		max_x = max_x_tmp - 1;
+		max_y = max_y_tmp - 1;
+	}
+	max_p = md->si->sensing_conf_data.max_z;
+
+	/* set event signal capabilities */
+	for (i = 0; i < NUM_SIGNALS(md->pdata->frmwrk); i++) {
+		signal = MT_PARAM_SIGNAL(md, i);
+		if (signal != PT_IGNORE_VALUE) {
+			__set_bit(signal, md->input->absbit);
+
+			min = MT_PARAM_MIN(md, i);
+			max = MT_PARAM_MAX(md, i);
+			if (i == PT_ABS_ID_OST) {
+				/* shift track ids down to start at 0 */
+				max = max - min;
+				min = min - min;
+			} else if (i == PT_ABS_X_OST)
+				max = max_x;
+			else if (i == PT_ABS_Y_OST)
+				max = max_y;
+			else if (i == PT_ABS_P_OST)
+				max = max_p;
+
+			input_set_abs_params(md->input, signal, min, max,
+				MT_PARAM_FUZZ(md, i), MT_PARAM_FLAT(md, i));
+			pt_debug(dev, DL_INFO,
+				"%s: register signal=%02X min=%d max=%d\n",
+				__func__, signal, min, max);
+		}
+	}
+
+	md->or_min = MT_PARAM_MIN(md, PT_ABS_OR_OST);
+	md->or_max = MT_PARAM_MAX(md, PT_ABS_OR_OST);
+
+	md->t_min = MT_PARAM_MIN(md, PT_ABS_ID_OST);
+	md->t_max = MT_PARAM_MAX(md, PT_ABS_ID_OST);
+
+	rc = md->mt_function.input_register_device(md->input,
+			md->si->tch_abs[PT_TCH_T].max);
+	if (rc < 0)
+		pt_debug(dev, DL_ERROR, "%s: Error, failed register input device r=%d\n",
+			__func__, rc);
+	else
+		md->input_device_registered = true;
+
+#ifdef EASYWAKE_TSG6
+	input_set_capability(md->input, EV_KEY, KEY_F1);
+	input_set_capability(md->input, EV_KEY, KEY_F2);
+	input_set_capability(md->input, EV_KEY, KEY_F3);
+	input_set_capability(md->input, EV_KEY, KEY_F4);
+	input_set_capability(md->input, EV_KEY, KEY_F5);
+	input_set_capability(md->input, EV_KEY, KEY_F6);
+	input_set_capability(md->input, EV_KEY, KEY_F7);
+	input_set_capability(md->input, EV_KEY, KEY_F8);
+	input_set_capability(md->input, EV_KEY, KEY_WAKEUP);
+#endif
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_setup_input_attention
+ *
+ * SUMMARY: Wrapper function for pt_setup_input_device() register to TTDL
+ *  attention list.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *     *dev - pointer to device structure
+ ******************************************************************************/
+static int pt_setup_input_attention(struct device *dev)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_mt_data *md = &cd->md;
+	int rc;
+
+	md->si = _pt_request_sysinfo(dev);
+	if (!md->si)
+		return -EINVAL;
+
+	rc = pt_setup_input_device(dev);
+
+	_pt_unsubscribe_attention(dev, PT_ATTEN_STARTUP, PT_MT_NAME,
+		pt_setup_input_attention, 0);
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_mt_probe
+ *
+ * SUMMARY: The probe function for touch input device
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*dev   - pointer to device structure
+ ******************************************************************************/
+int pt_mt_probe(struct device *dev)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_mt_data *md = &cd->md;
+	struct pt_platform_data *pdata = dev_get_platdata(dev);
+	struct pt_mt_platform_data *mt_pdata;
+	int rc = 0;
+
+	pt_debug(dev, DL_INFO,
+		"%s: >>>>>> Register MT <<<<<<\n", __func__);
+	if (!pdata || !pdata->mt_pdata) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Missing platform data\n", __func__);
+		rc = -ENODEV;
+		goto error_no_pdata;
+	}
+	mt_pdata = pdata->mt_pdata;
+
+	pt_init_function_ptrs(md);
+
+	mutex_init(&md->mt_lock);
+	md->dev = dev;
+	md->pdata = mt_pdata;
+
+	/* Create the input device and register it. */
+	pt_debug(dev, DL_INFO,
+		"%s: Create the input device and register it\n", __func__);
+	md->input = input_allocate_device();
+	if (!md->input) {
+		pt_debug(dev, DL_ERROR, "%s: Error, failed to allocate input device\n",
+			__func__);
+		rc = -ENODEV;
+		goto error_alloc_failed;
+	} else
+		md->input_device_allocated = true;
+
+	if (md->pdata->inp_dev_name)
+		md->input->name = md->pdata->inp_dev_name;
+	else
+		md->input->name = PT_MT_NAME;
+	scnprintf(md->phys, sizeof(md->phys), "%s/input%d", dev_name(dev),
+			cd->phys_num++);
+	md->input->phys = md->phys;
+	md->input->dev.parent = md->dev;
+	md->input->open = pt_mt_open;
+	md->input->close = pt_mt_close;
+	input_set_drvdata(md->input, md);
+
+	/* get sysinfo */
+	md->si = _pt_request_sysinfo(dev);
+
+	if (md->si) {
+		rc = pt_setup_input_device(dev);
+		if (rc)
+			goto error_init_input;
+	} else {
+		pt_debug(dev, DL_ERROR, "%s: Fail get sysinfo pointer from core p=%p\n",
+			__func__, md->si);
+		_pt_subscribe_attention(dev, PT_ATTEN_STARTUP,
+			PT_MT_NAME, pt_setup_input_attention, 0);
+	}
+
+	return 0;
+
+error_init_input:
+	input_free_device(md->input);
+	md->input_device_allocated = false;
+error_alloc_failed:
+error_no_pdata:
+	pt_debug(dev, DL_ERROR, "%s failed.\n", __func__);
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_mt_release
+ *
+ * SUMMARY: The release function for touch input device
+ *
+ * RETURN:
+ *	 0 = success
+ *
+ * PARAMETERS:
+ *	*dev   - pointer to device structure
+ ******************************************************************************/
+int pt_mt_release(struct device *dev)
+{
+	struct pt_core_data *cd;
+	struct pt_mt_data *md;
+
+	/* Ensure valid pointers before de-referencing them */
+	if (dev) {
+		cd = dev_get_drvdata(dev);
+		if (cd)
+			md = &cd->md;
+		else
+			return 0;
+	} else {
+		return 0;
+	}
+
+	/*
+	 * Second call this function may cause kernel panic if probe fail.
+	 * Use input_device_registered & input_device_allocated variable to
+	 * avoid unregister or free unavailable devive.
+	 */
+	if (md && md->input_device_registered) {
+		md->input_device_registered = false;
+		input_unregister_device(md->input);
+		/* Unregistering device will free the device too */
+		md->input_device_allocated = false;
+	} else if (md && md->input_device_allocated) {
+		md->input_device_allocated = false;
+		input_free_device(md->input);
+		_pt_unsubscribe_attention(dev, PT_ATTEN_STARTUP,
+			PT_MT_NAME, pt_setup_input_attention, 0);
+	}
+
+	return 0;
+}

+ 143 - 0
qcom/opensource/touch-drivers/pt/pt_mta.c

@@ -0,0 +1,143 @@
+/*
+ * pt_mta.c
+ * Parade TrueTouch(TM) Standard Product Multi-Touch Protocol A Module.
+ * For use with Parade touchscreen controllers.
+ * Supported parts include:
+ * TMA5XX
+ * TMA448
+ * TMA445A
+ * TT21XXX
+ * TT31XXX
+ * TT4XXXX
+ * TT7XXX
+ * TC3XXX
+ *
+ * Copyright (C) 2015-2020 Parade Technologies
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * Contact Parade Technologies at www.paradetech.com <[email protected]>
+ */
+
+#include "pt_regs.h"
+
+/*******************************************************************************
+ * FUNCTION: pt_final_sync
+ *
+ * SUMMARY: Function to create SYN_REPORT
+ *
+ * PARAMETERS:
+ *        *input - pointer to input device structure
+ *     max_slots - max support touch number
+ * mt_sync_count - current valid touch number
+ *           ids - bit map value
+ ******************************************************************************/
+static void pt_final_sync(struct input_dev *input, int max_slots,
+		int mt_sync_count, unsigned long *ids)
+{
+	if (mt_sync_count)
+		input_sync(input);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_input_sync
+ *
+ * SUMMARY: Function to create SYN_MT_REPORT
+ *
+ * PARAMETERS:
+ *        *input - pointer to input device structure
+ ******************************************************************************/
+static void pt_input_sync(struct input_dev *input)
+{
+	input_mt_sync(input);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_input_report
+ *
+ * SUMMARY: Function to report coordinate information of touch point
+ * protocol
+ *
+ * PARAMETERS:
+ *        *input - pointer to input device structure
+ *           sig - track id to allow tracking of a touch
+ *             t - event id to indicate an event associated with touch instance
+ *          type - indicate the touch object
+ ******************************************************************************/
+static void pt_input_report(struct input_dev *input, int sig,
+		int t, int type)
+{
+	if (type == PT_OBJ_STANDARD_FINGER || type == PT_OBJ_GLOVE) {
+		input_report_key(input, BTN_TOOL_FINGER, PT_BTN_PRESSED);
+		input_report_key(input, BTN_TOOL_PEN, PT_BTN_RELEASED);
+	} else if (type == PT_OBJ_STYLUS) {
+		input_report_key(input, BTN_TOOL_PEN, PT_BTN_PRESSED);
+		input_report_key(input, BTN_TOOL_FINGER, PT_BTN_RELEASED);
+	}
+
+	input_report_key(input, BTN_TOUCH, PT_BTN_PRESSED);
+
+	input_report_abs(input, sig, t);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_report_slot_liftoff
+ *
+ * SUMMARY: Function to report all touches are lifted
+ * protocol
+ *
+ * PARAMETERS:
+ *        *md - pointer to input device structure
+ *  max_slots - indicate max number of touch id
+ ******************************************************************************/
+static void pt_report_slot_liftoff(struct pt_mt_data *md,
+		int max_slots)
+{
+	input_report_key(md->input, BTN_TOUCH, PT_BTN_RELEASED);
+	input_report_key(md->input, BTN_TOOL_FINGER, PT_BTN_RELEASED);
+	input_report_key(md->input, BTN_TOOL_PEN, PT_BTN_RELEASED);
+
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_input_register_device
+ *
+ * SUMMARY: Function to register input device
+ * protocol
+ *
+ * PARAMETERS:
+ *     *input - pointer to input device structure
+ *  max_slots - indicate max number of touch id
+ ******************************************************************************/
+static int pt_input_register_device(struct input_dev *input, int max_slots)
+{
+	__set_bit(BTN_TOUCH, input->keybit);
+	__set_bit(BTN_TOOL_FINGER, input->keybit);
+	__set_bit(BTN_TOOL_PEN, input->keybit);
+	return input_register_device(input);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_init_function_ptrs
+ *
+ * SUMMARY: Function to init function pointer
+ *
+ * PARAMETERS:
+ *     *md - pointer to touch data structure
+ ******************************************************************************/
+void pt_init_function_ptrs(struct pt_mt_data *md)
+{
+	md->mt_function.report_slot_liftoff = pt_report_slot_liftoff;
+	md->mt_function.final_sync = pt_final_sync;
+	md->mt_function.input_sync = pt_input_sync;
+	md->mt_function.input_report = pt_input_report;
+	md->mt_function.input_register_device = pt_input_register_device;
+}

+ 144 - 0
qcom/opensource/touch-drivers/pt/pt_mtb.c

@@ -0,0 +1,144 @@
+/*
+ * pt_mtb.c
+ * Parade TrueTouch(TM) Standard Product Multi-Touch Protocol B Module.
+ * For use with Parade touchscreen controllers.
+ * Supported parts include:
+ * TMA5XX
+ * TMA448
+ * TMA445A
+ * TT21XXX
+ * TT31XXX
+ * TT4XXXX
+ * TT7XXX
+ * TC3XXX
+ *
+ * Copyright (C) 2015-2020 Parade Technologies
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * Contact Parade Technologies at www.paradetech.com <[email protected]>
+ */
+
+#include "pt_regs.h"
+#include <linux/input/mt.h>
+#include <linux/version.h>
+
+/*******************************************************************************
+ * FUNCTION: pt_final_sync
+ *
+ * SUMMARY: Function to create SYN_REPORT
+ *
+ * PARAMETERS:
+ *        *input - pointer to input device structure
+ *     max_slots - max support touch number
+ * mt_sync_count - current valid touch number
+ *           ids - bit map value
+ ******************************************************************************/
+static void pt_final_sync(struct input_dev *input, int max_slots,
+		int mt_sync_count, unsigned long *ids)
+{
+	int t;
+
+	for (t = 0; t < max_slots; t++) {
+		if (test_bit(t, ids))
+			continue;
+		input_mt_slot(input, t);
+		input_mt_report_slot_state(input, MT_TOOL_FINGER, false);
+	}
+
+	input_sync(input);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_input_report
+ *
+ * SUMMARY: Function to report coordinate information of touch point
+ * protocol
+ *
+ * PARAMETERS:
+ *        *input - pointer to input device structure
+ *           sig - track id to allow tracking of a touch
+ *             t - event id to indicate an event associated with touch instance
+ *          type - indicate the touch object
+ ******************************************************************************/
+static void pt_input_report(struct input_dev *input, int sig,
+		int t, int type)
+{
+	input_mt_slot(input, t);
+
+	if (type == PT_OBJ_STANDARD_FINGER || type == PT_OBJ_GLOVE)
+		input_mt_report_slot_state(input, MT_TOOL_FINGER, true);
+	else if (type == PT_OBJ_STYLUS)
+		input_mt_report_slot_state(input, MT_TOOL_PEN, true);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_report_slot_liftoff
+ *
+ * SUMMARY: Function to report all touches are lifted
+ * protocol
+ *
+ * PARAMETERS:
+ *        *md - pointer to input device structure
+ *  max_slots - indicate max number of touch id
+ ******************************************************************************/
+static void pt_report_slot_liftoff(struct pt_mt_data *md,
+		int max_slots)
+{
+	int t;
+
+	if (md->num_prv_rec == 0)
+		return;
+
+	for (t = 0; t < max_slots; t++) {
+		input_mt_slot(md->input, t);
+		input_mt_report_slot_state(md->input,
+			MT_TOOL_FINGER, false);
+	}
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_input_register_device
+ *
+ * SUMMARY: Function to register input device
+ * protocol
+ *
+ * PARAMETERS:
+ *     *input - pointer to input device structure
+ *  max_slots - indicate max number of touch id
+ ******************************************************************************/
+static int pt_input_register_device(struct input_dev *input, int max_slots)
+{
+#if (KERNEL_VERSION(3, 7, 0) <= LINUX_VERSION_CODE)
+	input_mt_init_slots(input, max_slots, 0);
+#else
+	input_mt_init_slots(input, max_slots);
+#endif
+	return input_register_device(input);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_init_function_ptrs
+ *
+ * SUMMARY: Function to init function pointer
+ *
+ * PARAMETERS:
+ *     *md - pointer to touch data structure
+ ******************************************************************************/
+void pt_init_function_ptrs(struct pt_mt_data *md)
+{
+	md->mt_function.report_slot_liftoff = pt_report_slot_liftoff;
+	md->mt_function.final_sync = pt_final_sync;
+	md->mt_function.input_sync = NULL;
+	md->mt_function.input_report = pt_input_report;
+	md->mt_function.input_register_device = pt_input_register_device;
+}
+

+ 572 - 0
qcom/opensource/touch-drivers/pt/pt_pen.c

@@ -0,0 +1,572 @@
+#ifndef TTDL_KERNEL_SUBMISSION
+/*
+ * pt_pen.c
+ * Parade TrueTouch(TM) Standard Product CapSense Reports Module.
+ * For use with Parade touchscreen controllers.
+ * Supported parts include:
+ * TMA5XX
+ * TMA448
+ * TMA445A
+ * TT21XXX
+ * TT31XXX
+ * TT4XXXX
+ * TT7XXX
+ * TC3XXX
+ *
+ * Copyright (C) 2015-2021 Parade Technologies
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * Contact Parade Technologies at www.paradetech.com <[email protected]>
+ */
+
+#include "pt_regs.h"
+
+/*******************************************************************************
+ * FUNCTION: pt_pen_lift_all
+ *
+ * SUMMARY: Reports pen liftoff action
+ *
+ * PARAMETERS:
+ *     *pend  - pointer to pen data structure
+ ******************************************************************************/
+static void pt_pen_lift_all(struct pt_pen_data *pend)
+{
+	input_report_key(pend->input, BTN_STYLUS, 0);
+	input_report_key(pend->input, BTN_STYLUS2, 0);
+	input_report_key(pend->input, BTN_TOUCH, 0);
+	input_report_key(pend->input, BTN_TOOL_PEN, 0);
+	input_sync(pend->input);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_get_pen_data
+ *
+ * SUMMARY: Gets axis of pen report
+ *
+ * PARAMETERS:
+ *     *pend    - pointer to pen data structure
+ *     *xy_data - pointer to touch data
+ ******************************************************************************/
+static void pt_get_pen(struct pt_pen_data *pend,
+	struct pt_pen *pen, u8 *xy_data)
+{
+	struct device *dev = pend->dev;
+	struct pt_sysinfo *si = pend->si;
+	enum pt_pen_abs abs;
+
+	for (abs = PT_PEN_X; abs < PT_PEN_NUM_ABS; abs++) {
+		if (!si->pen_abs[abs].report)
+			continue;
+		pt_get_touch_field(dev, &pen->abs[abs],
+			si->pen_abs[abs].size,
+			si->pen_abs[abs].max,
+			xy_data + si->tch_abs[abs].ofs,
+			si->pen_abs[abs].bofs);
+		pt_debug(dev, DL_DEBUG, "%s: get %s=%04X(%d)\n",
+			__func__, pt_pen_abs_string[abs],
+			pen->abs[abs], pen->abs[abs]);
+	}
+}
+
+
+/*******************************************************************************
+ * FUNCTION: pt_xy_worker
+ *
+ * SUMMARY: Read xy_data for current pen touch
+ *
+ * RETURN:
+ *	 0 = success
+ *
+ * PARAMETERS:
+ *     *pend  - pointer to pen data structure
+ ******************************************************************************/
+static int pt_xy_worker(struct pt_pen_data *pend)
+{
+	struct pt_sysinfo *si = pend->si;
+	struct pt_pen pen;
+	bool tool;
+
+	pt_get_pen(pend, &pen, si->xy_data + 3);
+
+	tool = pen.abs[PT_PEN_IV] ?
+		BTN_TOOL_RUBBER : BTN_TOOL_PEN;
+
+	input_report_abs(pend->input, ABS_X, pen.abs[PT_PEN_X]);
+	input_report_abs(pend->input, ABS_Y, pen.abs[PT_PEN_Y]);
+	input_report_abs(pend->input, ABS_PRESSURE, pen.abs[PT_PEN_P]);
+	input_report_key(pend->input, BTN_STYLUS, pen.abs[PT_PEN_BS]);
+
+	if (si->pen_abs[PT_PEN_2ND_BS].report)
+		input_report_key(pend->input, BTN_STYLUS2,
+			pen.abs[PT_PEN_2ND_BS]);
+
+	input_report_key(pend->input, BTN_TOUCH, pen.abs[PT_PEN_TS]);
+	input_report_key(pend->input, tool, pen.abs[PT_PEN_IR]);
+
+	if (si->pen_abs[PT_PEN_X_TILT].report)
+		input_report_abs(pend->input, ABS_TILT_X,
+			pen.abs[PT_PEN_X_TILT]);
+
+	if (si->pen_abs[PT_PEN_Y_TILT].report)
+		input_report_abs(pend->input, ABS_TILT_Y,
+			pen.abs[PT_PEN_Y_TILT]);
+
+	input_sync(pend->input);
+
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pen_attention
+ *
+ * SUMMARY: Wrapper function for pt_xy_worker() that register to TTDL attention
+ *  list.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *     *dev - pointer to device structure
+ ******************************************************************************/
+static int pt_pen_attention(struct device *dev)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_pen_data *pend = &cd->pend;
+	int rc;
+
+	if (pend->si->xy_mode[2] != pend->si->desc.pen_report_id)
+		return 0;
+
+	/* core handles handshake */
+	mutex_lock(&pend->pen_lock);
+	rc = pt_xy_worker(pend);
+	mutex_unlock(&pend->pen_lock);
+	if (rc < 0)
+		pt_debug(dev, DL_ERROR,
+			"%s: xy_worker error r=%d\n", __func__, rc);
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_startup_attention
+ *
+ * SUMMARY: Wrapper function for pt_pen_lift_all() that register to TTDL
+ *  attention list.
+ *
+ * RETURN:
+ *	 0 = success
+ *
+ * PARAMETERS:
+ *     *dev - pointer to device structure
+ ******************************************************************************/
+static int pt_startup_attention(struct device *dev)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_pen_data *pend = &cd->pend;
+
+	mutex_lock(&pend->pen_lock);
+	pt_pen_lift_all(pend);
+	mutex_unlock(&pend->pen_lock);
+
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pen_suspend_attention
+ *
+ * SUMMARY: Function for pen to enter suspend state that as following steps:
+ *          1) Lift pen touch
+ *          2) Set flag with suspend state
+ *          3) Decrese pm system count
+ *
+ * RETURN:
+ *	 0 = success
+ *
+ * PARAMETERS:
+ *     *dev - pointer to device structure
+ ******************************************************************************/
+static int pt_pen_suspend_attention(struct device *dev)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_pen_data *pend = &cd->pend;
+
+	mutex_lock(&pend->pen_lock);
+	pt_pen_lift_all(pend);
+	pend->is_suspended = true;
+	mutex_unlock(&pend->pen_lock);
+
+	pm_runtime_put(dev);
+
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pen_resume_attention
+ *
+ * SUMMARY: Function for pen to leave suspend state that as following steps:
+ *          1) Increse pm system count
+ *          2) Clear suspend state flag
+ *
+ * RETURN:
+ *	 0 = success
+ *
+ * PARAMETERS:
+ *     *dev - pointer to device structure
+ ******************************************************************************/
+static int pt_pen_resume_attention(struct device *dev)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_pen_data *pend = &cd->pend;
+
+	pm_runtime_get(dev);
+
+	mutex_lock(&pend->pen_lock);
+	pend->is_suspended = false;
+	mutex_unlock(&pend->pen_lock);
+
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pen_open
+ *
+ * SUMMARY: Open method for input device(pen) that sets up call back
+ *  functions to TTDL attention list
+ *
+ * RETURN:
+ *	 0 = success
+ *
+ * PARAMETERS:
+ *     *input - pointer to input_dev structure
+ ******************************************************************************/
+static int pt_pen_open(struct input_dev *input)
+{
+	struct device *dev = input->dev.parent;
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_pen_data *pend = &cd->pend;
+
+	pm_runtime_get_sync(dev);
+
+	mutex_lock(&pend->pen_lock);
+	pend->is_suspended = false;
+	mutex_unlock(&pend->pen_lock);
+
+	pt_debug(dev, DL_INFO, "%s: setup subscriptions\n", __func__);
+
+	/* set up touch call back */
+	_pt_subscribe_attention(dev, PT_ATTEN_IRQ, PT_PEN_NAME,
+		pt_pen_attention, PT_MODE_OPERATIONAL);
+
+	/* set up startup call back */
+	_pt_subscribe_attention(dev, PT_ATTEN_STARTUP, PT_PEN_NAME,
+		pt_startup_attention, 0);
+
+	/* set up suspend call back */
+	_pt_subscribe_attention(dev, PT_ATTEN_SUSPEND, PT_PEN_NAME,
+		pt_pen_suspend_attention, 0);
+
+	/* set up resume call back */
+	_pt_subscribe_attention(dev, PT_ATTEN_RESUME, PT_PEN_NAME,
+		pt_pen_resume_attention, 0);
+
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pen_close
+ *
+ * SUMMARY: Close method for input device(pen) that clears call back
+ *  functions from TTDL attention list.
+ *
+ * PARAMETERS:
+ *     *input - pointer to input_dev structure
+ ******************************************************************************/
+static void pt_pen_close(struct input_dev *input)
+{
+	struct device *dev = input->dev.parent;
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_pen_data *pend = &cd->pend;
+
+	_pt_unsubscribe_attention(dev, PT_ATTEN_IRQ, PT_PEN_NAME,
+		pt_pen_attention, PT_MODE_OPERATIONAL);
+
+	_pt_unsubscribe_attention(dev, PT_ATTEN_STARTUP, PT_PEN_NAME,
+		pt_startup_attention, 0);
+
+	_pt_unsubscribe_attention(dev, PT_ATTEN_SUSPEND, PT_PEN_NAME,
+		pt_pen_suspend_attention, 0);
+
+	_pt_unsubscribe_attention(dev, PT_ATTEN_RESUME, PT_PEN_NAME,
+		pt_pen_resume_attention, 0);
+
+	mutex_lock(&pend->pen_lock);
+	if (!pend->is_suspended) {
+		pm_runtime_put(dev);
+		pend->is_suspended = true;
+	}
+	mutex_unlock(&pend->pen_lock);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_setup_input_device
+ *
+ * SUMMARY: Set up resolution, event signal capabilities and register input
+ *  device for pen.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *     *dev - pointer to device structure
+ ******************************************************************************/
+static int pt_setup_input_device(struct device *dev)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_pen_data *pend = &cd->pend;
+	int i;
+	int rc;
+	u32 usage;
+	int max_x, max_y, max_p;
+
+	pt_debug(dev, DL_INFO, "%s: Initialize event signals\n",
+		__func__);
+	__set_bit(EV_ABS, pend->input->evbit);
+	__set_bit(EV_KEY, pend->input->evbit);
+
+	for (i = PT_PEN_X; i < PT_PEN_NUM_ABS; i++) {
+		usage = pt_pen_abs_field_map[i];
+
+		switch (usage) {
+		case HID_GD_X:
+			max_x = pend->si->sensing_conf_data.res_x;
+			input_set_abs_params(pend->input,
+				ABS_X, 0, max_x, 0, 0);
+			break;
+		case HID_GD_Y:
+			max_y = pend->si->sensing_conf_data.res_y;
+			input_set_abs_params(pend->input,
+				ABS_Y, 0, max_y, 0, 0);
+			break;
+		case HID_DG_TIPPRESSURE:
+			max_p = pend->si->sensing_conf_data.max_z;
+			input_set_abs_params(pend->input,
+				ABS_PRESSURE, 0, max_p, 0, 0);
+			break;
+		case HID_DG_INRANGE:
+			input_set_capability(pend->input,
+				EV_KEY, BTN_TOOL_PEN);
+			break;
+		case HID_DG_INVERT:
+			input_set_capability(pend->input,
+				EV_KEY, BTN_TOOL_RUBBER);
+			break;
+		case HID_DG_TILT_X:
+			max_x = pend->si->pen_abs[i].max;
+			input_set_abs_params(pend->input,
+				ABS_TILT_X, 0, max_x, 0, 0);
+			break;
+		case HID_DG_TILT_Y:
+			max_x = pend->si->pen_abs[i].max;
+			input_set_abs_params(pend->input,
+				ABS_TILT_Y, 0, max_x, 0, 0);
+			break;
+		case HID_DG_ERASER:
+		case HID_DG_TIPSWITCH:
+			input_set_capability(pend->input,
+				EV_KEY, BTN_TOUCH);
+			break;
+		case HID_DG_BARRELSWITCH:
+			input_set_capability(pend->input,
+				EV_KEY, BTN_STYLUS);
+			break;
+		case HID_DG_BARRELSWITCH2:
+			input_set_capability(pend->input,
+				EV_KEY, BTN_STYLUS2);
+			break;
+		}
+	}
+
+	rc = input_register_device(pend->input);
+	if (rc < 0)
+		pt_debug(dev, DL_ERROR,
+			"%s: Error, failed register input device r=%d\n",
+			__func__, rc);
+	else
+		pend->input_device_registered = true;
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_setup_input_attention
+ *
+ * SUMMARY: Wrapper function for pt_setup_input_device() register to TTDL
+ *  attention list.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *     *dev - pointer to device structure
+ ******************************************************************************/
+static int pt_setup_input_attention(struct device *dev)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_pen_data *pend = &cd->pend;
+	int rc;
+
+	pend->si = _pt_request_sysinfo(dev);
+	if (!pend->si)
+		return -1;
+
+	rc = pt_setup_input_device(dev);
+
+	_pt_unsubscribe_attention(dev, PT_ATTEN_STARTUP, PT_PEN_NAME,
+		pt_setup_input_attention, 0);
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pen_probe
+ *
+ * SUMMARY: The probe function for pen input device
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*dev   - pointer to device structure
+ ******************************************************************************/
+int pt_pen_probe(struct device *dev)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_pen_data *pend = &cd->pend;
+	struct pt_platform_data *pdata = dev_get_platdata(dev);
+	struct pt_pen_platform_data *pen_pdata;
+	int rc = 0;
+
+	if (!pdata || !pdata->pen_pdata) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Missing platform data\n", __func__);
+		rc = -ENODEV;
+		goto error_no_pdata;
+	}
+	pen_pdata = pdata->pen_pdata;
+
+	mutex_init(&pend->pen_lock);
+	pend->dev = dev;
+	pend->pdata = pen_pdata;
+
+	/* Create the input device and register it. */
+	pt_debug(dev, DL_INFO,
+		"%s: Create the input device and register it\n", __func__);
+	pend->input = input_allocate_device();
+	if (!pend->input) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Error, failed to allocate input device\n",
+			__func__);
+		rc = -ENODEV;
+		goto error_alloc_failed;
+	} else
+		pend->input_device_allocated = true;
+
+	if (pend->pdata->inp_dev_name)
+		pend->input->name = pend->pdata->inp_dev_name;
+	else
+		pend->input->name = PT_PEN_NAME;
+	scnprintf(pend->phys, sizeof(pend->phys), "%s/input%d", dev_name(dev),
+			cd->phys_num++);
+	pend->input->phys = pend->phys;
+	pend->input->dev.parent = pend->dev;
+	pend->input->open = pt_pen_open;
+	pend->input->close = pt_pen_close;
+	input_set_drvdata(pend->input, pend);
+
+	/* get sysinfo */
+	pend->si = _pt_request_sysinfo(dev);
+
+	if (pend->si) {
+		rc = pt_setup_input_device(dev);
+		if (rc)
+			goto error_init_input;
+	} else {
+		pt_debug(dev, DL_ERROR,
+			"%s: Fail get sysinfo pointer from core p=%p\n",
+			__func__, pend->si);
+		_pt_subscribe_attention(dev, PT_ATTEN_STARTUP,
+			PT_PEN_NAME, pt_setup_input_attention, 0);
+	}
+
+	return 0;
+
+error_init_input:
+	input_free_device(pend->input);
+	pend->input_device_allocated = false;
+error_alloc_failed:
+error_no_pdata:
+	pt_debug(dev, DL_ERROR, "%s failed.\n", __func__);
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pen_release
+ *
+ * SUMMARY: The release function for pen input device
+ *
+ * RETURN:
+ *	 0 = success
+ *
+ * PARAMETERS:
+ *	*dev   - pointer to device structure
+ ******************************************************************************/
+int pt_pen_release(struct device *dev)
+{
+	struct pt_core_data *cd;
+	struct pt_pen_data *pend;
+
+	/* Ensure valid pointers before de-referencing them */
+	if (dev) {
+		cd = dev_get_drvdata(dev);
+		if (cd)
+			pend = &cd->pend;
+		else
+			return 0;
+	} else {
+		return 0;
+	}
+
+	/*
+	 * Second call this function may cause kernel panic if probe fail.
+	 * Use input_device_registered & input_device_allocated variable to
+	 * avoid unregister or free unavailable devive.
+	 */
+	if (pend && pend->input_device_registered) {
+		pend->input_device_registered = false;
+		input_unregister_device(pend->input);
+		/* Unregistering device will free the device too */
+		pend->input_device_allocated = false;
+	} else if (pend && pend->input_device_allocated) {
+		pend->input_device_allocated = false;
+		input_free_device(pend->input);
+		_pt_unsubscribe_attention(dev, PT_ATTEN_STARTUP,
+			PT_PEN_NAME, pt_setup_input_attention, 0);
+	}
+
+	return 0;
+}
+#endif /*!TTDL_KERNEL_SUBMISSION */

+ 1051 - 0
qcom/opensource/touch-drivers/pt/pt_platform.c

@@ -0,0 +1,1051 @@
+/*
+ * pt_platform.c
+ * Parade TrueTouch(TM) Standard Product Platform Module.
+ * For use with Parade touchscreen controllers.
+ * Supported parts include:
+ * TMA5XX
+ * TMA448
+ * TMA445A
+ * TT21XXX
+ * TT31XXX
+ * TT4XXXX
+ * TT7XXX
+ * TC3XXX
+ *
+ * Copyright (C) 2015-2020 Parade Technologies
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * Contact Parade Technologies at www.paradetech.com <[email protected]>
+ */
+
+#include "pt_regs.h"
+#include "pt_platform.h"
+
+#ifdef CONFIG_TOUCHSCREEN_PARADE_PLATFORM_FW_UPGRADE
+/* FW for Panel ID = 0x00 */
+#include "pt_fw_pid00.h"
+static struct pt_touch_firmware pt_firmware_pid00 = {
+	.img = pt_img_pid00,
+	.size = ARRAY_SIZE(pt_img_pid00),
+	.ver = pt_ver_pid00,
+	.vsize = ARRAY_SIZE(pt_ver_pid00),
+	.panel_id = 0x00,
+};
+
+/* FW for Panel ID = 0x01 */
+#include "pt_fw_pid01.h"
+static struct pt_touch_firmware pt_firmware_pid01 = {
+	.img = pt_img_pid01,
+	.size = ARRAY_SIZE(pt_img_pid01),
+	.ver = pt_ver_pid01,
+	.vsize = ARRAY_SIZE(pt_ver_pid01),
+	.panel_id = 0x01,
+};
+
+/* FW for Panel ID not enabled (legacy) */
+#include "pt_fw.h"
+static struct pt_touch_firmware pt_firmware = {
+	.img = pt_img,
+	.size = ARRAY_SIZE(pt_img),
+	.ver = pt_ver,
+	.vsize = ARRAY_SIZE(pt_ver),
+};
+#else
+/* FW for Panel ID not enabled (legacy) */
+static struct pt_touch_firmware pt_firmware = {
+	.img = NULL,
+	.size = 0,
+	.ver = NULL,
+	.vsize = 0,
+};
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_PARADE_PLATFORM_TTCONFIG_UPGRADE
+/* TT Config for Panel ID = 0x00 */
+#include "pt_params_pid00.h"
+static struct touch_settings pt_sett_param_regs_pid00 = {
+	.data = (uint8_t *)&pt_param_regs_pid00[0],
+	.size = ARRAY_SIZE(pt_param_regs_pid00),
+	.tag = 0,
+};
+
+static struct touch_settings pt_sett_param_size_pid00 = {
+	.data = (uint8_t *)&pt_param_size_pid00[0],
+	.size = ARRAY_SIZE(pt_param_size_pid00),
+	.tag = 0,
+};
+
+static struct pt_touch_config pt_ttconfig_pid00 = {
+	.param_regs = &pt_sett_param_regs_pid00,
+	.param_size = &pt_sett_param_size_pid00,
+	.fw_ver = ttconfig_fw_ver_pid00,
+	.fw_vsize = ARRAY_SIZE(ttconfig_fw_ver_pid00),
+	.panel_id = 0x00,
+};
+
+/* TT Config for Panel ID = 0x01 */
+#include "pt_params_pid01.h"
+static struct touch_settings pt_sett_param_regs_pid01 = {
+	.data = (uint8_t *)&pt_param_regs_pid01[0],
+	.size = ARRAY_SIZE(pt_param_regs_pid01),
+	.tag = 0,
+};
+
+static struct touch_settings pt_sett_param_size_pid01 = {
+	.data = (uint8_t *)&pt_param_size_pid01[0],
+	.size = ARRAY_SIZE(pt_param_size_pid01),
+	.tag = 0,
+};
+
+static struct pt_touch_config pt_ttconfig_pid01 = {
+	.param_regs = &pt_sett_param_regs_pid01,
+	.param_size = &pt_sett_param_size_pid01,
+	.fw_ver = ttconfig_fw_ver_pid01,
+	.fw_vsize = ARRAY_SIZE(ttconfig_fw_ver_pid01),
+	.panel_id = 0x01,
+};
+
+/* TT Config for Panel ID not enabled (legacy)*/
+#include "pt_params.h"
+static struct touch_settings pt_sett_param_regs = {
+	.data = (uint8_t *)&pt_param_regs[0],
+	.size = ARRAY_SIZE(pt_param_regs),
+	.tag = 0,
+};
+
+static struct touch_settings pt_sett_param_size = {
+	.data = (uint8_t *)&pt_param_size[0],
+	.size = ARRAY_SIZE(pt_param_size),
+	.tag = 0,
+};
+
+static struct pt_touch_config pt_ttconfig = {
+	.param_regs = &pt_sett_param_regs,
+	.param_size = &pt_sett_param_size,
+	.fw_ver = ttconfig_fw_ver,
+	.fw_vsize = ARRAY_SIZE(ttconfig_fw_ver),
+};
+#else
+/* TT Config for Panel ID not enabled (legacy)*/
+static struct pt_touch_config pt_ttconfig = {
+	.param_regs = NULL,
+	.param_size = NULL,
+	.fw_ver = NULL,
+	.fw_vsize = 0,
+};
+#endif
+
+static struct pt_touch_firmware *pt_firmwares[] = {
+#ifdef CONFIG_TOUCHSCREEN_PARADE_PLATFORM_FW_UPGRADE
+	&pt_firmware_pid00,
+	&pt_firmware_pid01,
+#endif
+	NULL, /* Last item should always be NULL */
+};
+
+static struct pt_touch_config *pt_ttconfigs[] = {
+#ifdef CONFIG_TOUCHSCREEN_PARADE_PLATFORM_TTCONFIG_UPGRADE
+	&pt_ttconfig_pid00,
+	&pt_ttconfig_pid01,
+#endif
+	NULL, /* Last item should always be NULL */
+};
+
+struct pt_loader_platform_data _pt_loader_platform_data = {
+	.fw = &pt_firmware,
+	.ttconfig = &pt_ttconfig,
+	.fws = pt_firmwares,
+	.ttconfigs = pt_ttconfigs,
+	.flags = PT_LOADER_FLAG_NONE,
+};
+
+/*******************************************************************************
+ * FUNCTION: pt_xres
+ *
+ * SUMMARY: Toggles the reset gpio (TP_XRES) to perform a HW reset
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = fail
+ *
+ * PARAMETERS:
+ *  *pdata - pointer to the platform data structure
+ *  *dev   - pointer to Device structure
+ ******************************************************************************/
+int pt_xres(struct pt_core_platform_data *pdata,
+		struct device *dev)
+{
+	int rst_gpio = pdata->rst_gpio;
+	int rc = 0;
+	int ddi_rst_gpio = pdata->ddi_rst_gpio;
+
+	pt_debug(dev, DL_WARN, "%s: 20ms HARD RESET on gpio=%d\n",
+		__func__, pdata->rst_gpio);
+
+	/* Toggling only TP_XRES as DDI_XRES resets the entire part */
+	gpio_set_value(rst_gpio, 1);
+	if (ddi_rst_gpio)
+		gpio_set_value(ddi_rst_gpio, 1);
+	usleep_range(3000, 4000);
+	gpio_set_value(rst_gpio, 0);
+	usleep_range(6000, 7000);
+	gpio_set_value(rst_gpio, 1);
+	if (ddi_rst_gpio)
+		gpio_set_value(ddi_rst_gpio, 1);
+
+	/* Sleep to allow the DUT to boot */
+	usleep_range(3000, 4000);
+	return rc;
+}
+
+#ifdef PT_PINCTRL_EN
+/*******************************************************************************
+ * FUNCTION: pt_pinctrl_init
+ *
+ * SUMMARY: Pinctrl method to obtain pin state handler for TP_RST, IRQ
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = fail
+ *
+ * PARAMETERS:
+ *  *pdata - pointer to the platform data structure
+ *  *dev   - pointer to Device structure
+ ******************************************************************************/
+static int pt_pinctrl_init(struct pt_core_platform_data *pdata,
+			   struct device *dev)
+{
+	int ret = 0;
+
+	pdata->pinctrl = devm_pinctrl_get(dev);
+	if (IS_ERR_OR_NULL(pdata->pinctrl)) {
+		pt_debug(dev, DL_ERROR,
+			 "Failed to get pinctrl, please check dts");
+		ret = PTR_ERR(pdata->pinctrl);
+		goto err_pinctrl_get;
+	}
+
+	pdata->pins_active =
+	    pinctrl_lookup_state(pdata->pinctrl, "pmx_ts_active");
+	if (IS_ERR_OR_NULL(pdata->pins_active)) {
+		pt_debug(dev, DL_ERROR, "pmx_ts_active not found");
+		ret = PTR_ERR(pdata->pins_active);
+		goto err_pinctrl_lookup;
+	}
+
+	pdata->pins_suspend =
+	    pinctrl_lookup_state(pdata->pinctrl, "pmx_ts_suspend");
+	if (IS_ERR_OR_NULL(pdata->pins_suspend)) {
+		pt_debug(dev, DL_ERROR, "pmx_ts_suspend not found");
+		ret = PTR_ERR(pdata->pins_suspend);
+		goto err_pinctrl_lookup;
+	}
+
+	pdata->pins_release =
+	    pinctrl_lookup_state(pdata->pinctrl, "pmx_ts_release");
+	if (IS_ERR_OR_NULL(pdata->pins_release)) {
+		pt_debug(dev, DL_ERROR, "pmx_ts_release not found");
+		ret = PTR_ERR(pdata->pins_release);
+		goto err_pinctrl_lookup;
+	}
+
+	return 0;
+
+err_pinctrl_lookup:
+	devm_pinctrl_put(pdata->pinctrl);
+
+err_pinctrl_get:
+	pdata->pinctrl = NULL;
+	pdata->pins_release = NULL;
+	pdata->pins_suspend = NULL;
+	pdata->pins_active = NULL;
+	return ret;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pinctrl_select_normal
+ *
+ * SUMMARY: Pinctrl method to configure drive mode for TP_RST, IRQ - normal
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = fail
+ *
+ * PARAMETERS:
+ *  *pdata - pointer to the platform data structure
+ *  *dev   - pointer to Device structure
+ ******************************************************************************/
+static int pt_pinctrl_select_normal(struct pt_core_platform_data *pdata,
+				    struct device *dev)
+{
+	int ret = 0;
+
+	if (pdata->pinctrl && pdata->pins_active) {
+		ret = pinctrl_select_state(pdata->pinctrl, pdata->pins_active);
+		if (ret < 0) {
+			pt_debug(dev, DL_ERROR, "Set normal pin state error=%d",
+				 ret);
+		}
+	}
+
+	return ret;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pinctrl_select_suspend
+ *
+ * SUMMARY:  Pinctrl method to configure drive mode for TP_RST, IRQ - suspend
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = fail
+ *
+ * PARAMETERS:
+ *  *pdata - pointer to the platform data structure
+ *  *dev   - pointer to Device structure
+ ******************************************************************************/
+static int pt_pinctrl_select_suspend(struct pt_core_platform_data *pdata,
+				     struct device *dev)
+{
+	int ret = 0;
+
+	if (pdata->pinctrl && pdata->pins_suspend) {
+		ret = pinctrl_select_state(pdata->pinctrl, pdata->pins_suspend);
+		if (ret < 0) {
+			pt_debug(dev, DL_ERROR,
+				 "Set suspend pin state error=%d", ret);
+		}
+	}
+
+	return ret;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_pinctrl_select_release
+ *
+ * SUMMARY:  Pinctrl method to configure drive mode for TP_RST, IRQ - release
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = fail
+ *
+ * PARAMETERS:
+ *  *pdata - pointer to the platform data structure
+ *  *dev   - pointer to Device structure
+ ******************************************************************************/
+static int pt_pinctrl_select_release(struct pt_core_platform_data *pdata,
+				     struct device *dev)
+{
+	int ret = 0;
+
+	if (pdata->pinctrl) {
+		if (IS_ERR_OR_NULL(pdata->pins_release)) {
+			devm_pinctrl_put(pdata->pinctrl);
+			pdata->pinctrl = NULL;
+		} else {
+			ret = pinctrl_select_state(pdata->pinctrl,
+						   pdata->pins_release);
+			if (ret < 0)
+				pt_debug(dev, DL_ERROR,
+					 "Set gesture pin state error=%d", ret);
+		}
+	}
+
+	return ret;
+}
+#endif /* PT_PINCTRL_EN */
+
+/*******************************************************************************
+ * FUNCTION: pt_init
+ *
+ * SUMMARY: Set up/free gpios for TP_RST, IRQ, DDI_RST.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = fail
+ *
+ * PARAMETERS:
+ *  *pdata - pointer to the platform data structure
+ *   on    - flag to set up or free gpios(0:free; !0:set up)
+ *  *dev   - pointer to Device structure
+ ******************************************************************************/
+int pt_init(struct pt_core_platform_data *pdata,
+		int on, struct device *dev)
+{
+	int rst_gpio     = pdata->rst_gpio;
+	int irq_gpio     = pdata->irq_gpio;
+	int ddi_rst_gpio = pdata->ddi_rst_gpio;
+	int rc = 0;
+
+#ifdef PT_PINCTRL_EN
+	if (on) {
+		rc = pt_pinctrl_init(pdata, dev);
+		if (!rc) {
+			pt_pinctrl_select_normal(pdata, dev);
+		} else {
+			pt_debug(dev, DL_ERROR,
+				 "%s: Failed to request pinctrl\n", __func__);
+		}
+	}
+#endif
+	if (on && rst_gpio) {
+		/* Configure RST GPIO */
+		pt_debug(dev, DL_WARN, "%s: Request RST GPIO %d",
+			__func__, rst_gpio);
+		rc = gpio_request(rst_gpio, NULL);
+		if (rc < 0) {
+			gpio_free(rst_gpio);
+			rc = gpio_request(rst_gpio, NULL);
+		}
+		if (rc < 0) {
+			pt_debug(dev, DL_ERROR,
+				"%s: Failed requesting RST GPIO %d\n",
+				__func__, rst_gpio);
+			goto fail_rst_gpio;
+		} else {
+			/*
+			 * Set the GPIO direction and the starting level
+			 * The start level is high because the DUT needs
+			 * to stay in reset during power up.
+			 */
+			rc = gpio_direction_output(rst_gpio, 1);
+			if (rc < 0) {
+				pt_debug(dev, DL_ERROR,
+					"%s: Output Setup ERROR: RST GPIO %d\n",
+					__func__, rst_gpio);
+				goto fail_rst_gpio;
+			}
+		}
+	}
+
+	if (on && irq_gpio) {
+		/* Configure IRQ GPIO */
+		pt_debug(dev, DL_WARN, "%s: Request IRQ GPIO %d",
+			__func__, irq_gpio);
+		rc = gpio_request(irq_gpio, NULL);
+		if (rc < 0) {
+			gpio_free(irq_gpio);
+			rc = gpio_request(irq_gpio, NULL);
+		}
+		if (rc < 0) {
+			pt_debug(dev, DL_ERROR,
+				"%s: Failed requesting IRQ GPIO %d\n",
+				__func__, irq_gpio);
+			goto fail_irq_gpio;
+		} else {
+			/* Set the GPIO direction */
+			rc = gpio_direction_input(irq_gpio);
+			if (rc < 0) {
+				pt_debug(dev, DL_ERROR,
+					"%s: Input Setup ERROR: IRQ GPIO %d\n",
+					__func__, irq_gpio);
+				goto fail_irq_gpio;
+			}
+		}
+	}
+
+	if (on && ddi_rst_gpio) {
+		/* Configure DDI RST GPIO */
+		pt_debug(dev, DL_WARN, "%s: Request DDI RST GPIO %d",
+			__func__, ddi_rst_gpio);
+		rc = gpio_request(ddi_rst_gpio, NULL);
+		if (rc < 0) {
+			gpio_free(ddi_rst_gpio);
+			rc = gpio_request(ddi_rst_gpio, NULL);
+		}
+		if (rc < 0) {
+			pt_debug(dev, DL_ERROR,
+				"%s: Failed requesting DDI RST GPIO %d\n",
+				__func__, ddi_rst_gpio);
+			goto fail_ddi_rst_gpio;
+		} else {
+			/* Set the GPIO direction and the starting level */
+			rc = gpio_direction_output(ddi_rst_gpio, 0);
+			if (rc < 0) {
+				pt_debug(dev, DL_ERROR,
+					"%s: Output Setup ERROR: RST GPIO %d\n",
+					__func__, ddi_rst_gpio);
+				goto fail_ddi_rst_gpio;
+			}
+		}
+	}
+
+	if (!on) {
+		/* "on" not set, therefore free all gpio's */
+		if (ddi_rst_gpio)
+			gpio_free(ddi_rst_gpio);
+		if (irq_gpio)
+			gpio_free(irq_gpio);
+		if (rst_gpio)
+			gpio_free(rst_gpio);
+#ifdef PT_PINCTRL_EN
+		pt_pinctrl_select_release(pdata, dev);
+#endif
+	}
+
+	/* All GPIO's created successfully */
+	goto success;
+
+fail_ddi_rst_gpio:
+	pt_debug(dev, DL_ERROR,
+		"%s: ERROR - GPIO setup Failure, freeing DDI_XRES GPIO %d\n",
+		__func__, ddi_rst_gpio);
+	gpio_free(ddi_rst_gpio);
+fail_irq_gpio:
+	pt_debug(dev, DL_ERROR,
+		"%s: ERROR - GPIO setup Failure, freeing IRQ GPIO %d\n",
+		__func__, irq_gpio);
+	gpio_free(irq_gpio);
+fail_rst_gpio:
+	pt_debug(dev, DL_ERROR,
+		"%s: ERROR - GPIO setup Failure, freeing TP_XRES GPIO %d\n",
+		__func__, rst_gpio);
+	gpio_free(rst_gpio);
+
+success:
+	pt_debug(dev, DL_INFO,
+		"%s: SUCCESS - Configured DDI_XRES GPIO %d, IRQ GPIO %d, TP_XRES GPIO %d\n",
+		__func__, ddi_rst_gpio, irq_gpio, rst_gpio);
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_wakeup
+ *
+ * SUMMARY: Resume power for "power on/off" sleep strategy which against to
+ *  "deepsleep" strategy.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = fail
+ *
+ * PARAMETERS:
+ *  *pdata       - pointer to the platform data structure
+ *  *dev         - pointer to Device structure
+ *  *ignore_irq  - pointer to atomic structure to allow the host ignoring false
+ *                 IRQ during power up
+ ******************************************************************************/
+static int pt_wakeup(struct pt_core_platform_data *pdata,
+		struct device *dev, atomic_t *ignore_irq)
+{
+	/* Example for TT7XXX */
+	int rc = 0;
+
+#ifdef PT_PINCTRL_EN
+	rc = pt_pinctrl_select_normal(pdata, dev);
+	if (rc)
+		pr_err("%s: GPIO pins activation  error: rc=%d\n",
+			__func__, rc);
+#endif
+
+#ifdef TT7XXX_EXAMPLE
+	pt_debug(dev, DL_INFO,
+		"%s: Enable defined pwr: VDDI, VCC\n", __func__);
+	/*
+	 * Force part into RESET by holding XRES#(TP_XRES)
+	 * while powering it up
+	 */
+	if (pdata->rst_gpio)
+		gpio_set_value(pdata->rst_gpio, 0);
+
+	/* Turn on VDDI [Digital Interface] (+1.8v) */
+	if (pdata->vddi_gpio) {
+		rc = gpio_request(pdata->vddi_gpio, NULL);
+		if (rc < 0) {
+			gpio_free(pdata->vddi_gpio);
+			rc = gpio_request(pdata->vddi_gpio, NULL);
+		}
+		if (rc < 0) {
+			pr_err("%s: Failed requesting VDDI GPIO %d\n",
+				__func__, pdata->vddi_gpio);
+		}
+		rc = gpio_direction_output(pdata->vddi_gpio, 1);
+		if (rc)
+			pr_err("%s: setcfg for VDDI GPIO %d failed\n",
+				__func__, pdata->vddi_gpio);
+		gpio_free(pdata->vddi_gpio);
+		usleep_range(3000, 4000);
+	}
+
+	/* Turn on VCC */
+	if (pdata->vcc_gpio) {
+		rc = gpio_request(pdata->vcc_gpio, NULL);
+		if (rc < 0) {
+			gpio_free(pdata->vcc_gpio);
+			rc = gpio_request(pdata->vcc_gpio, NULL);
+		}
+		if (rc < 0) {
+			pr_err("%s: Failed requesting VCC GPIO %d\n",
+				__func__, pdata->vcc_gpio);
+		}
+		rc = gpio_direction_output(pdata->vcc_gpio, 1);
+		if (rc)
+			pr_err("%s: setcfg for VCC GPIO %d failed\n",
+				__func__, pdata->vcc_gpio);
+		gpio_free(pdata->vcc_gpio);
+		usleep_range(3000, 4000);
+	}
+
+	usleep_range(12000, 15000);
+	/* Force part out of RESET by releasing XRES#(TP_XRES) */
+	if (pdata->rst_gpio)
+		gpio_set_value(pdata->rst_gpio, 1);
+#else
+	pt_debug(dev, DL_INFO, "%s: Enable defined pwr\n", __func__);
+#endif
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_sleep
+ *
+ * SUMMARY: Suspend power for "power on/off" sleep strategy which against to
+ *  "deepsleep" strategy.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = fail
+ *
+ * PARAMETERS:
+ *  *pdata       - pointer to the platform data structure
+ *  *dev         - pointer to Device structure
+ *  *ignore_irq  - pointer to atomic structure to allow the host ignoring false
+ *                 IRQ during power down
+ ******************************************************************************/
+static int pt_sleep(struct pt_core_platform_data *pdata,
+		struct device *dev, atomic_t *ignore_irq)
+{
+	/* Example for TT7XXX */
+	int rc = 0;
+
+#ifdef TT7XXX_EXAMPLE
+	pt_debug(dev, DL_INFO,
+		"%s: Turn off defined pwr: VCC, VDDI\n", __func__);
+	/*
+	 * Force part into RESET by holding XRES#(TP_XRES)
+	 * while powering it up
+	 */
+	if (pdata->rst_gpio)
+		gpio_set_value(pdata->rst_gpio, 0);
+
+	/* Turn off VCC */
+	if (pdata->vcc_gpio) {
+		rc = gpio_request(pdata->vcc_gpio, NULL);
+		if (rc < 0) {
+			gpio_free(pdata->vcc_gpio);
+			rc = gpio_request(pdata->vcc_gpio, NULL);
+		}
+		if (rc < 0) {
+			pr_err("%s: Failed requesting VCC GPIO %d\n",
+				__func__, pdata->vcc_gpio);
+		}
+		rc = gpio_direction_output(pdata->vcc_gpio, 0);
+		if (rc)
+			pr_err("%s: setcfg for VCC GPIO %d failed\n",
+				__func__, pdata->vcc_gpio);
+		gpio_free(pdata->vcc_gpio);
+	}
+
+	/* Turn off VDDI [Digital Interface] (+1.8v) */
+	if (pdata->vddi_gpio) {
+		rc = gpio_request(pdata->vddi_gpio, NULL);
+		if (rc < 0) {
+			gpio_free(pdata->vddi_gpio);
+			rc = gpio_request(pdata->vddi_gpio, NULL);
+		}
+		if (rc < 0) {
+			pr_err("%s: Failed requesting VDDI GPIO %d\n",
+				__func__, pdata->vddi_gpio);
+		}
+		rc = gpio_direction_output(pdata->vddi_gpio, 0);
+		if (rc)
+			pr_err("%s: setcfg for VDDI GPIO %d failed\n",
+				__func__, pdata->vddi_gpio);
+		gpio_free(pdata->vddi_gpio);
+		usleep_range(10000, 12000);
+	}
+#else
+	pt_debug(dev, DL_INFO, "%s: Turn off defined pwr\n", __func__);
+#endif
+#ifdef PT_PINCTRL_EN
+	rc = pt_pinctrl_select_suspend(pdata, dev);
+	if (rc)
+		pr_err("%s: GPIO pins suspend error: rc=%d\n",
+			__func__, rc);
+#endif
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_power
+ *
+ * SUMMARY: Wrapper function to resume/suspend power with function
+ *  pt_wakeup()/pt_sleep().
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = fail
+ *
+ * PARAMETERS:
+ *  *pdata       - pointer to the platform data structure
+ *   on          - flag to remsume/suspend power(0:resume; 1:suspend)
+ *  *dev         - pointer to Device structure
+ *  *ignore_irq  - pointer to atomic structure to allow the host ignoring false
+ *                 IRQ during power up/down
+ ******************************************************************************/
+int pt_power(struct pt_core_platform_data *pdata,
+		int on, struct device *dev, atomic_t *ignore_irq)
+{
+	if (on)
+		return pt_wakeup(pdata, dev, ignore_irq);
+
+	return pt_sleep(pdata, dev, ignore_irq);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_irq_stat
+ *
+ * SUMMARY: Obtain the level state of IRQ gpio.
+ *
+ * RETURN:
+ *	 level state of IRQ gpio
+ *
+ * PARAMETERS:
+ *  *pdata       - pointer to the platform data structure
+ *  *dev         - pointer to Device structure
+ ******************************************************************************/
+int pt_irq_stat(struct pt_core_platform_data *pdata,
+		struct device *dev)
+{
+	return gpio_get_value(pdata->irq_gpio);
+}
+
+#ifdef PT_DETECT_HW
+/*******************************************************************************
+ * FUNCTION: pt_detect
+ *
+ * SUMMARY: Detect the I2C device by reading one byte(FW sentiel) after the
+ *  reset operation.
+ *
+ * RETURN:
+ *	 0 - detected
+ *  !0 - undetected
+ *
+ * PARAMETERS:
+ *  *pdata - pointer to the platform data structure
+ *  *dev   - pointer to Device structure
+ *   read  - pointer to the function to perform a read operation
+ ******************************************************************************/
+int pt_detect(struct pt_core_platform_data *pdata,
+		struct device *dev, pt_platform_read read)
+{
+	int retry = 3;
+	int rc;
+	char buf[1];
+
+	while (retry--) {
+		/* Perform reset, wait for 100 ms and perform read */
+		pt_debug(dev, DL_WARN, "%s: Performing a reset\n",
+			__func__);
+		pdata->xres(pdata, dev);
+		msleep(100);
+		rc = read(dev, buf, 1);
+		if (!rc)
+			return 0;
+
+		pt_debug(dev, DL_ERROR, "%s: Read unsuccessful, try=%d\n",
+			__func__, 3 - retry);
+	}
+
+	return rc;
+}
+#endif
+
+/*******************************************************************************
+ * FUNCTION: pt_setup_power
+ *
+ * SUMMARY: Turn on/turn off voltage regulator
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*pdata - pointer to  core platform data
+ *	on     - flag to decide power state,PT_MT_POWER_ON/PT_MT_POWER_OFF
+ *	*dev   - pointer to device
+ ******************************************************************************/
+int pt_setup_power(struct pt_core_platform_data *pdata, int on,
+		struct device *dev)
+{
+	int en_vcc  = pdata->vcc_gpio;
+	int en_vddi = pdata->vddi_gpio;
+	int en_avdd = pdata->avdd_gpio;
+	int en_avee = pdata->avee_gpio;
+	int rc = 0;
+
+	/*
+	 * For TDDI parts, force part into RESET by holding DDI XRES
+	 * while powering it up
+	 */
+	if (pdata->ddi_rst_gpio)
+		gpio_set_value(pdata->ddi_rst_gpio, 0);
+
+	/*
+	 * Force part into RESET by holding XRES#(TP_XRES)
+	 * while powering it up
+	 */
+	if (pdata->rst_gpio)
+		gpio_set_value(pdata->rst_gpio, 0);
+
+	if (on == PT_MT_POWER_ON) {
+		/*
+		 * Enable GPIOs to turn on voltage regulators to pwr up DUT
+		 * - TC device power up order: VDDI, VCC, AVDD, AVEE
+		 * - TT device power up order: VDDI, VCC
+		 * NOTE: VDDI must be stable for >10ms before XRES is released
+		 */
+		pt_debug(dev, DL_INFO,
+		"%s: Enable defined pwr: VDDI, VCC, AVDD, AVEE\n", __func__);
+
+		/* Turn on VDDI [Digital Interface] (+1.8v) */
+		if (pdata->vddi_gpio) {
+			rc = gpio_request(en_vddi, NULL);
+			if (rc < 0) {
+				gpio_free(en_vddi);
+				rc = gpio_request(en_vddi, NULL);
+			}
+			if (rc < 0) {
+				pr_err("%s: Failed requesting VDDI GPIO %d\n",
+					__func__, en_vddi);
+			}
+			rc = gpio_direction_output(en_vddi, 1);
+			if (rc)
+				pr_err("%s: setcfg for VDDI GPIO %d failed\n",
+					__func__, en_vddi);
+			gpio_free(en_vddi);
+			usleep_range(3000, 4000);
+		}
+
+		/* Turn on VCC */
+		if (pdata->vcc_gpio) {
+			rc = gpio_request(en_vcc, NULL);
+			if (rc < 0) {
+				gpio_free(en_vcc);
+				rc = gpio_request(en_vcc, NULL);
+			}
+			if (rc < 0) {
+				pr_err("%s: Failed requesting VCC GPIO %d\n",
+					__func__, en_vcc);
+			}
+			rc = gpio_direction_output(en_vcc, 1);
+			if (rc)
+				pr_err("%s: setcfg for VCC GPIO %d failed\n",
+					__func__, en_vcc);
+			gpio_free(en_vcc);
+			usleep_range(3000, 4000);
+		}
+
+		/* Turn on AVDD (+5.0v) */
+		if (pdata->avdd_gpio) {
+			rc = gpio_request(en_avdd, NULL);
+			if (rc < 0) {
+				gpio_free(en_avdd);
+				rc = gpio_request(en_avdd, NULL);
+			}
+			if (rc < 0) {
+				pr_err("%s: Failed requesting AVDD GPIO %d\n",
+					__func__, en_avdd);
+			}
+			rc = gpio_direction_output(en_avdd, 1);
+			if (rc)
+				pr_err("%s: setcfg for AVDD GPIO %d failed\n",
+					__func__, en_avdd);
+			gpio_free(en_avdd);
+			usleep_range(3000, 4000);
+		}
+
+		/* Turn on AVEE (-5.0v) */
+		if (pdata->avee_gpio) {
+			rc = gpio_request(en_avee, NULL);
+			if (rc < 0) {
+				gpio_free(en_avee);
+				rc = gpio_request(en_avee, NULL);
+			}
+			if (rc < 0) {
+				pr_err("%s: Failed requesting AVEE GPIO %d\n",
+					__func__, en_avee);
+			}
+			rc = gpio_direction_output(en_avee, 1);
+			if (rc)
+				pr_err("%s: setcfg for AVEE GPIO %d failed\n",
+					__func__, en_avee);
+			gpio_free(en_avee);
+			usleep_range(3000, 4000);
+		}
+	} else {
+		/*
+		 * Disable GPIOs to turn off voltage regulators to pwr down
+		 * TC device The power down order is: AVEE, AVDD, VDDI
+		 * TT device The power down order is: VCC, VDDI
+		 *
+		 * Note:Turn off some of regulators may effect display
+		 * parts for TDDI chip
+		 */
+		pt_debug(dev, DL_INFO,
+		"%s: Turn off defined pwr: VCC, AVEE, AVDD, VDDI\n", __func__);
+
+		/* Turn off VCC */
+		if (pdata->vcc_gpio) {
+			rc = gpio_request(en_vcc, NULL);
+			if (rc < 0) {
+				gpio_free(en_vcc);
+				rc = gpio_request(en_vcc, NULL);
+			}
+			if (rc < 0) {
+				pr_err("%s: Failed requesting VCC GPIO %d\n",
+					__func__, en_vcc);
+			}
+			rc = gpio_direction_output(en_vcc, 0);
+			if (rc)
+				pr_err("%s: setcfg for VCC GPIO %d failed\n",
+					__func__, en_vcc);
+			gpio_free(en_vcc);
+		}
+
+		/* Turn off AVEE (-5.0v) */
+		if (pdata->avee_gpio) {
+			rc = gpio_request(en_avee, NULL);
+			if (rc < 0) {
+				gpio_free(en_avee);
+				rc = gpio_request(en_avee, NULL);
+			}
+			if (rc < 0) {
+				pr_err("%s: Failed requesting AVEE GPIO %d\n",
+					__func__, en_avee);
+			}
+			rc = gpio_direction_output(en_avee, 0);
+			if (rc)
+				pr_err("%s: setcfg for AVEE GPIO %d failed\n",
+					__func__, en_avee);
+			gpio_free(en_avee);
+		}
+
+		/* Turn off AVDD (+5.0v) */
+		if (pdata->avdd_gpio) {
+			rc = gpio_request(en_avdd, NULL);
+			if (rc < 0) {
+				gpio_free(en_avdd);
+				rc = gpio_request(en_avdd, NULL);
+			}
+			if (rc < 0) {
+				pr_err("%s: Failed requesting AVDD GPIO %d\n",
+					__func__, en_avdd);
+			}
+			rc = gpio_direction_output(en_avdd, 0);
+			if (rc)
+				pr_err("%s: setcfg for AVDD GPIO %d failed\n",
+					__func__, en_avdd);
+			gpio_free(en_avdd);
+		}
+
+		/* Turn off VDDI [Digital Interface] (+1.8v) */
+		if (pdata->vddi_gpio) {
+			rc = gpio_request(en_vddi, NULL);
+			if (rc < 0) {
+				gpio_free(en_vddi);
+				rc = gpio_request(en_vddi, NULL);
+			}
+			if (rc < 0) {
+				pr_err("%s: Failed requesting VDDI GPIO %d\n",
+					__func__, en_vddi);
+			}
+			rc = gpio_direction_output(en_vddi, 0);
+			if (rc)
+				pr_err("%s: setcfg for VDDI GPIO %d failed\n",
+					__func__, en_vddi);
+			gpio_free(en_vddi);
+			usleep_range(10000, 12000);
+		}
+	}
+
+	/* Force part out of RESET by releasing XRES#(TP_XRES) */
+	if (pdata->rst_gpio)
+		gpio_set_value(pdata->rst_gpio, 1);
+
+	/* Force part out of RESET by releasing DDI XRES */
+	if (pdata->ddi_rst_gpio)
+		gpio_set_value(pdata->ddi_rst_gpio, 1);
+
+	return rc;
+}
+
+
+/*******************************************************************************
+ * FUNCTION: pt_setup_irq
+ *
+ * SUMMARY: Configure the IRQ GPIO used by the TT DUT
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*pdata - pointer to core platform data
+ *	 on    - flag to setup interrupt process work(PT_MT_IRQ_FREE/)
+ *	*dev   - pointer to device
+ ******************************************************************************/
+int pt_setup_irq(struct pt_core_platform_data *pdata, int on,
+	struct device *dev)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	unsigned long irq_flags;
+	int rc = 0;
+
+	if (on == PT_MT_IRQ_REG) {
+		/*
+		 * When TTDL has direct access to the GPIO the irq_stat function
+		 * will be defined and the gpio_to_irq conversion must be
+		 * performed. e.g. For CHROMEOS this is not the case, the irq is
+		 * passed in directly.
+		 */
+		if (pdata->irq_stat) {
+			/* Initialize IRQ */
+			dev_vdbg(dev, "%s: Value Passed to gpio_to_irq =%d\n",
+				__func__, pdata->irq_gpio);
+			cd->irq = gpio_to_irq(pdata->irq_gpio);
+			dev_vdbg(dev,
+				"%s: Value Returned from gpio_to_irq =%d\n",
+				__func__, cd->irq);
+		}
+		if (cd->irq < 0)
+			return -EINVAL;
+		cd->irq_enabled = true;
+		pt_debug(dev, DL_INFO, "%s: initialize threaded irq=%d\n",
+			__func__, cd->irq);
+		if (pdata->level_irq_udelay > 0)
+			/* use level triggered interrupts */
+			irq_flags = IRQF_TRIGGER_LOW;
+		else
+			/* use edge triggered interrupts */
+			irq_flags = IRQF_TRIGGER_FALLING;
+		rc = request_threaded_irq(cd->irq, NULL, pt_irq,
+			irq_flags | IRQF_ONESHOT, dev_name(dev), cd);
+		if (rc < 0)
+			pt_debug(dev, DL_ERROR,
+				"%s: Error, could not request irq\n", __func__);
+	} else {
+		disable_irq_nosync(cd->irq);
+		free_irq(cd->irq, cd);
+	}
+	return rc;
+}

+ 69 - 0
qcom/opensource/touch-drivers/pt/pt_platform.h

@@ -0,0 +1,69 @@
+/*
+ * pt_platform.h
+ * Parade TrueTouch(TM) Standard Product Platform Module.
+ * For use with Parade touchscreen controllers.
+ * Supported parts include:
+ * TMA5XX
+ * TMA448
+ * TMA445A
+ * TT21XXX
+ * TT31XXX
+ * TT4XXXX
+ * TT7XXX
+ * TC3XXX
+ *
+ * Copyright (C) 2015-2020 Parade Technologies
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * Contact Parade Technologies at www.parade.com <[email protected]>
+ */
+
+#ifndef _LINUX_PT_PLATFORM_H
+#define _LINUX_PT_PLATFORM_H
+
+#include "pt_core.h"
+#include <linux/irq.h>
+
+#if defined(CONFIG_TOUCHSCREEN_PARADE) \
+	|| defined(CONFIG_TOUCHSCREEN_PARADE_MODULE)
+extern struct pt_loader_platform_data _pt_loader_platform_data;
+extern irqreturn_t pt_irq(int irq, void *handle);
+
+int pt_xres(struct pt_core_platform_data *pdata, struct device *dev);
+int pt_init(struct pt_core_platform_data *pdata, int on,
+		struct device *dev);
+int pt_power(struct pt_core_platform_data *pdata, int on,
+		struct device *dev, atomic_t *ignore_irq);
+#ifdef PT_DETECT_HW
+int pt_detect(struct pt_core_platform_data *pdata,
+		struct device *dev, pt_platform_read read);
+#else
+#define pt_detect      NULL
+#endif
+int pt_irq_stat(struct pt_core_platform_data *pdata,
+		struct device *dev);
+int pt_setup_power(struct pt_core_platform_data *pdata, int on,
+		struct device *dev);
+int pt_setup_irq(struct pt_core_platform_data *pdata, int on,
+		struct device *dev);
+#else /* !CONFIG_TOUCHSCREEN_PARADE */
+static struct pt_loader_platform_data _pt_loader_platform_data;
+#define pt_xres         NULL
+#define pt_init         NULL
+#define pt_power        NULL
+#define pt_irq_stat     NULL
+#define pt_detect       NULL
+#define pt_setup_power  NULL
+#define pt_setup_irq    NULL
+#endif /* CONFIG_TOUCHSCREEN_PARADE */
+
+#endif /* _LINUX_PT_PLATFORM_H */

+ 815 - 0
qcom/opensource/touch-drivers/pt/pt_proximity.c

@@ -0,0 +1,815 @@
+/*
+ * pt_proximity.c
+ * Parade TrueTouch(TM) Standard Product Proximity Module.
+ * For use with Parade touchscreen controllers.
+ * Supported parts include:
+ * TMA5XX
+ * TMA448
+ * TMA445A
+ * TT21XXX
+ * TT31XXX
+ * TT4XXXX
+ * TT7XXX
+ * TC3XXX
+ *
+ * Copyright (C) 2015-2020 Parade Technologies
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * Contact Parade Technologies at www.paradetech.com <[email protected]>
+ *
+ */
+
+#include "pt_regs.h"
+
+#define PT_PROXIMITY_NAME "pt_proximity"
+
+/* Timeout value in ms. */
+#define PT_PROXIMITY_REQUEST_EXCLUSIVE_TIMEOUT		1000
+
+#define PT_PROXIMITY_ON 0
+#define PT_PROXIMITY_OFF 1
+
+/*******************************************************************************
+ * FUNCTION: get_prox_data
+ *
+ * SUMMARY: Gets pointer of proximity data from core data structure
+ *
+ * RETURN:
+ *	 pointer of pt_proximity_data structure in core data structure
+ *
+ * PARAMETERS:
+ *     *dev - pointer to device structure
+ ******************************************************************************/
+static inline struct pt_proximity_data *get_prox_data(struct device *dev)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+
+	return &cd->pd;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_report_proximity
+ *
+ * SUMMARY: Reports proximity event
+ *
+ * PARAMETERS:
+ *     *pd  - pointer to proximity data structure
+ *      on  - state of proximity(true:on; false:off)
+ ******************************************************************************/
+static void pt_report_proximity(struct pt_proximity_data *pd,
+	bool on)
+{
+	int val = on ? PT_PROXIMITY_ON : PT_PROXIMITY_OFF;
+
+	input_report_abs(pd->input, ABS_DISTANCE, val);
+	input_sync(pd->input);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_get_touch_axis
+ *
+ * SUMMARY: Calculates touch axis
+ *
+ * PARAMETERS:
+ *     *pd      - pointer to proximity data structure
+ *     *axis    - pointer to axis calculation result
+ *      size    - size in bytes
+ *      max     - max value of result
+ *     *xy_data - pointer to input data to be parsed
+ *      bofs    - bit offset
+ ******************************************************************************/
+static void pt_get_touch_axis(struct pt_proximity_data *pd,
+	int *axis, int size, int max, u8 *xy_data, int bofs)
+{
+	int nbyte;
+	int next;
+
+	for (nbyte = 0, *axis = 0, next = 0; nbyte < size; nbyte++) {
+		pt_debug(pd->dev, DL_INFO,
+			"%s: *axis=%02X(%d) size=%d max=%08X xy_data=%p xy_data[%d]=%02X(%d) bofs=%d\n",
+			__func__, *axis, *axis, size, max, xy_data, next,
+			xy_data[next], xy_data[next], bofs);
+		*axis = *axis + ((xy_data[next] >> bofs) << (nbyte * 8));
+		next++;
+	}
+
+	*axis &= max - 1;
+
+	pt_debug(pd->dev, DL_INFO,
+		"%s: *axis=%02X(%d) size=%d max=%08X xy_data=%p xy_data[%d]=%02X(%d)\n",
+		__func__, *axis, *axis, size, max, xy_data, next,
+		xy_data[next], xy_data[next]);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_get_touch_hdr
+ *
+ * SUMMARY: Gets header of touch report
+ *
+ * PARAMETERS:
+ *     *pd      - pointer to proximity data structure
+ *     *touch   - pointer to pt_touch structure
+ *     *xy_mode - pointer to input mode data
+ ******************************************************************************/
+static void pt_get_touch_hdr(struct pt_proximity_data *pd,
+	struct pt_touch *touch, u8 *xy_mode)
+{
+	struct device *dev = pd->dev;
+	struct pt_sysinfo *si = pd->si;
+	enum pt_tch_hdr hdr;
+
+	for (hdr = PT_TCH_TIME; hdr < PT_TCH_NUM_HDR; hdr++) {
+		if (!si->tch_hdr[hdr].report)
+			continue;
+		pt_get_touch_axis(pd, &touch->hdr[hdr],
+			si->tch_hdr[hdr].size,
+			si->tch_hdr[hdr].max,
+			xy_mode + si->tch_hdr[hdr].ofs,
+			si->tch_hdr[hdr].bofs);
+		pt_debug(dev, DL_INFO, "%s: get %s=%04X(%d)\n",
+			__func__, pt_tch_hdr_string[hdr],
+			touch->hdr[hdr], touch->hdr[hdr]);
+	}
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_get_touch
+ *
+ * SUMMARY: Parse proximity touch event
+ *
+ * PARAMETERS:
+ *     *pd       - pointer to proximity data structure
+ *     *touch    - pointer to touch structure
+ *      xy_data  - pointer to touch data
+ ******************************************************************************/
+static void pt_get_touch(struct pt_proximity_data *pd,
+	struct pt_touch *touch, u8 *xy_data)
+{
+	struct device *dev = pd->dev;
+	struct pt_sysinfo *si = pd->si;
+	enum pt_tch_abs abs;
+
+	for (abs = PT_TCH_X; abs < PT_TCH_NUM_ABS; abs++) {
+		if (!si->tch_abs[abs].report)
+			continue;
+		pt_get_touch_axis(pd, &touch->abs[abs],
+			si->tch_abs[abs].size,
+			si->tch_abs[abs].max,
+			xy_data + si->tch_abs[abs].ofs,
+			si->tch_abs[abs].bofs);
+		pt_debug(dev, DL_INFO, "%s: get %s=%04X(%d)\n",
+			__func__, pt_tch_abs_string[abs],
+			touch->abs[abs], touch->abs[abs]);
+	}
+
+	pt_debug(dev, DL_INFO, "%s: x=%04X(%d) y=%04X(%d)\n",
+		__func__, touch->abs[PT_TCH_X], touch->abs[PT_TCH_X],
+		touch->abs[PT_TCH_Y], touch->abs[PT_TCH_Y]);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_get_proximity_touch
+ *
+ * SUMMARY: Parse and report proximity touch event
+ *
+ * PARAMETERS:
+ *     *pd      - pointer to proximity data structure
+ *     *touch   - pointer to pt_touch structure
+ ******************************************************************************/
+static void pt_get_proximity_touch(struct pt_proximity_data *pd,
+		struct pt_touch *tch, int num_cur_tch)
+{
+	struct pt_sysinfo *si = pd->si;
+	int i;
+
+	for (i = 0; i < num_cur_tch; i++) {
+		pt_get_touch(pd, tch, si->xy_data +
+			(i * si->desc.tch_record_size));
+
+		/* Check for proximity event */
+		if (tch->abs[PT_TCH_O] == PT_OBJ_PROXIMITY) {
+			if (tch->abs[PT_TCH_E] == PT_EV_TOUCHDOWN)
+				pt_report_proximity(pd, true);
+			else if (tch->abs[PT_TCH_E] == PT_EV_LIFTOFF)
+				pt_report_proximity(pd, false);
+			break;
+		}
+	}
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_xy_worker
+ *
+ * SUMMARY: Read xy_data for all current touches
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *     *pd - pointer to proximity data structure
+ ******************************************************************************/
+static int pt_xy_worker(struct pt_proximity_data *pd)
+{
+	struct device *dev = pd->dev;
+	struct pt_sysinfo *si = pd->si;
+	struct pt_touch tch;
+	u8 num_cur_tch;
+
+	pt_get_touch_hdr(pd, &tch, si->xy_mode + 3);
+
+	num_cur_tch = tch.hdr[PT_TCH_NUM];
+	if (num_cur_tch > si->sensing_conf_data.max_tch) {
+		pt_debug(dev, DL_ERROR, "%s: Num touch err detected (n=%d)\n",
+			__func__, num_cur_tch);
+		num_cur_tch = si->sensing_conf_data.max_tch;
+	}
+
+	if (tch.hdr[PT_TCH_LO])
+		pt_debug(dev, DL_WARN, "%s: Large area detected\n",
+		__func__);
+
+	/* extract xy_data for all currently reported touches */
+	pt_debug(dev, DL_INFO, "%s: extract data num_cur_rec=%d\n",
+		__func__, num_cur_tch);
+	if (num_cur_tch)
+		pt_get_proximity_touch(pd, &tch, num_cur_tch);
+	else
+		pt_report_proximity(pd, false);
+
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_mt_attention
+ *
+ * SUMMARY: Wrapper function for pt_xy_worker() that subscribe into the TTDL
+ *  attention list.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *     *dev - pointer to device structure
+ ******************************************************************************/
+static int pt_proximity_attention(struct device *dev)
+{
+	struct pt_proximity_data *pd = get_prox_data(dev);
+	int rc = 0;
+
+	if (pd->si->xy_mode[2] != pd->si->desc.tch_report_id)
+		return 0;
+
+	mutex_lock(&pd->prox_lock);
+	rc = pt_xy_worker(pd);
+	mutex_unlock(&pd->prox_lock);
+	if (rc < 0)
+		pt_debug(dev, DL_ERROR, "%s: xy_worker error r=%d\n",
+			__func__, rc);
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_startup_attention
+ *
+ * SUMMARY: Wrapper function for pt_report_proximity() that subcribe into the
+ *  TTDL attention list.
+ *
+ * RETURN:
+ *	 0 = success
+ *
+ * PARAMETERS:
+ *     *dev - pointer to device structure
+ ******************************************************************************/
+static int pt_startup_attention(struct device *dev)
+{
+	struct pt_proximity_data *pd = get_prox_data(dev);
+
+	mutex_lock(&pd->prox_lock);
+	pt_report_proximity(pd, false);
+	mutex_unlock(&pd->prox_lock);
+
+	return 0;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_set_proximity_via_touchmode_enabled
+ *
+ * SUMMARY: Enable/Disable proximity via touchmode parameter
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *     *pd     - pointer to proximity data structure
+ *      enable - enable or disable proximity(true:enable; false:disable)
+ ******************************************************************************/
+static int _pt_set_proximity_via_touchmode_enabled(
+		struct pt_proximity_data *pd, bool enable)
+{
+	struct device *dev = pd->dev;
+	u32 touchmode_enabled;
+	int rc;
+
+	rc = _pt_request_pip_get_param(dev, 0,
+			PT_RAM_ID_TOUCHMODE_ENABLED, &touchmode_enabled);
+	if (rc)
+		return rc;
+
+	if (enable)
+		touchmode_enabled |= 0x80;
+	else
+		touchmode_enabled &= 0x7F;
+
+	rc = _pt_request_pip_set_param(dev, 0,
+			PT_RAM_ID_TOUCHMODE_ENABLED, touchmode_enabled,
+			PT_RAM_ID_TOUCHMODE_ENABLED_SIZE);
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_set_proximity_via_proximity_enable
+ *
+ * SUMMARY: Enable/Disable proximity via proximity parameter
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *     *pd     - pointer to proximity data structure
+ *      enable - enable or disable proximity(true:enable; false:disable)
+ ******************************************************************************/
+static int _pt_set_proximity_via_proximity_enable(
+		struct pt_proximity_data *pd, bool enable)
+{
+	struct device *dev = pd->dev;
+	u32 proximity_enable;
+	int rc;
+
+	rc = _pt_request_pip_get_param(dev, 0,
+			PT_RAM_ID_PROXIMITY_ENABLE, &proximity_enable);
+	if (rc)
+		return rc;
+
+	if (enable)
+		proximity_enable |= 0x01;
+	else
+		proximity_enable &= 0xFE;
+
+	rc = _pt_request_pip_set_param(dev, 0,
+			PT_RAM_ID_PROXIMITY_ENABLE, proximity_enable,
+			PT_RAM_ID_PROXIMITY_ENABLE_SIZE);
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_set_proximity
+ *
+ * SUMMARY: Set proximity mode via touchmode parameter or proximity parameter.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *     *pd     - pointer to proximity data structure
+ *      enable - enable or disable proximity(true:enable; false:disable)
+ ******************************************************************************/
+static int _pt_set_proximity(struct pt_proximity_data *pd,
+		bool enable)
+{
+	if (!IS_PIP_VER_GE(pd->si, 1, 4))
+		return _pt_set_proximity_via_touchmode_enabled(pd,
+				enable);
+
+	return _pt_set_proximity_via_proximity_enable(pd, enable);
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_set_proximity
+ *
+ * SUMMARY: Enable proximity mode and subscribe into IRQ and STARTUP TTDL
+ *  attention list.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *     *pd     - pointer to proximity data structure
+ ******************************************************************************/
+static int _pt_proximity_enable(struct pt_proximity_data *pd)
+{
+	struct device *dev = pd->dev;
+	int rc = 0;
+
+	pm_runtime_get_sync(dev);
+
+	rc = pt_request_exclusive(dev,
+			PT_PROXIMITY_REQUEST_EXCLUSIVE_TIMEOUT);
+	if (rc < 0) {
+		pt_debug(dev, DL_ERROR, "%s: Error on request exclusive r=%d\n",
+				__func__, rc);
+		goto exit;
+	}
+
+	rc = _pt_set_proximity(pd, true);
+	if (rc < 0) {
+		pt_debug(dev, DL_ERROR, "%s: Error on request enable proximity scantype r=%d\n",
+				__func__, rc);
+		goto exit_release;
+	}
+
+	pt_debug(dev, DL_INFO, "%s: setup subscriptions\n", __func__);
+
+	/* set up touch call back */
+	_pt_subscribe_attention(dev, PT_ATTEN_IRQ, PT_PROXIMITY_NAME,
+		pt_proximity_attention, PT_MODE_OPERATIONAL);
+
+	/* set up startup call back */
+	_pt_subscribe_attention(dev, PT_ATTEN_STARTUP,
+		PT_PROXIMITY_NAME, pt_startup_attention, 0);
+
+exit_release:
+	pt_release_exclusive(dev);
+exit:
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: _pt_proximity_disable
+ *
+ * SUMMARY: Disable proximity mode and unsubscribe from IRQ and STARTUP TTDL
+ *  attention list.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *     *pd     - pointer to proximity data structure
+ ******************************************************************************/
+static int _pt_proximity_disable(struct pt_proximity_data *pd,
+		bool force)
+{
+	struct device *dev = pd->dev;
+	int rc = 0;
+
+	rc = pt_request_exclusive(dev,
+			PT_PROXIMITY_REQUEST_EXCLUSIVE_TIMEOUT);
+	if (rc < 0) {
+		pt_debug(dev, DL_ERROR, "%s: Error on request exclusive r=%d\n",
+				__func__, rc);
+		goto exit;
+	}
+
+	rc = _pt_set_proximity(pd, false);
+	if (rc < 0) {
+		pt_debug(dev, DL_ERROR, "%s: Error on request disable proximity scan r=%d\n",
+				__func__, rc);
+		goto exit_release;
+	}
+
+exit_release:
+	pt_release_exclusive(dev);
+
+exit:
+	if (!rc || force) {
+		_pt_unsubscribe_attention(dev, PT_ATTEN_IRQ,
+			PT_PROXIMITY_NAME, pt_proximity_attention,
+			PT_MODE_OPERATIONAL);
+
+		_pt_unsubscribe_attention(dev, PT_ATTEN_STARTUP,
+			PT_PROXIMITY_NAME, pt_startup_attention, 0);
+	}
+
+	pm_runtime_put(dev);
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_proximity_enable_show
+ *
+ * SUMMARY: Show method for the prox_enable sysfs node that will show the
+ *	enable_count of proximity
+ *
+ * RETURN: Size of printed buffer
+ *
+ * PARAMETERS:
+ *	*dev  - pointer to device structure
+ *	*attr - pointer to device attributes
+ *	*buf  - pointer to output buffer
+ ******************************************************************************/
+static ssize_t pt_proximity_enable_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct pt_proximity_data *pd = get_prox_data(dev);
+	int val = 0;
+
+	mutex_lock(&pd->sysfs_lock);
+	val = pd->enable_count;
+	mutex_unlock(&pd->sysfs_lock);
+
+	return scnprintf(buf, PT_MAX_PRBUF_SIZE, "%d\n", val);
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_proximity_enable_store
+ *
+ * SUMMARY: The store method for the prox_enable sysfs node that allows to
+ *  enable or disable proxmity mode.
+ *
+ * RETURN: Size of passed in buffer
+ *
+ * PARAMETERS:
+ *	*dev  - pointer to device structure
+ *	*attr - pointer to device attributes
+ *	*buf  - pointer to buffer that hold the command parameters
+ *	 size - size of buf
+ ******************************************************************************/
+static ssize_t pt_proximity_enable_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	struct pt_proximity_data *pd = get_prox_data(dev);
+	unsigned long value;
+	int rc;
+
+	rc = kstrtoul(buf, 10, &value);
+	if (rc < 0 || (value != 0 && value != 1)) {
+		pt_debug(dev, DL_ERROR, "%s: Invalid value\n", __func__);
+		return -EINVAL;
+	}
+
+	mutex_lock(&pd->sysfs_lock);
+	if (value) {
+		if (pd->enable_count++) {
+			pt_debug(dev, DL_WARN, "%s: '%s' already enabled\n",
+				__func__, pd->input->name);
+		} else {
+			rc = _pt_proximity_enable(pd);
+			if (rc)
+				pd->enable_count--;
+		}
+	} else {
+		if (--pd->enable_count) {
+			if (pd->enable_count < 0) {
+				pt_debug(dev, DL_ERROR, "%s: '%s' unbalanced disable\n",
+					__func__, pd->input->name);
+				pd->enable_count = 0;
+			}
+		} else {
+			rc = _pt_proximity_disable(pd, false);
+			if (rc)
+				pd->enable_count++;
+		}
+	}
+	mutex_unlock(&pd->sysfs_lock);
+
+	if (rc)
+		return rc;
+
+	return size;
+}
+
+static DEVICE_ATTR(prox_enable, 0600,
+		pt_proximity_enable_show,
+		pt_proximity_enable_store);
+
+/*******************************************************************************
+ * FUNCTION: pt_setup_input_device_and_sysfs
+ *
+ * SUMMARY: Create sysnode, set event signal capabilities and register input
+ *  device for proximity.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *     *dev - pointer to device structure
+ ******************************************************************************/
+static int pt_setup_input_device_and_sysfs(struct device *dev)
+{
+	struct pt_proximity_data *pd = get_prox_data(dev);
+	int signal = PT_IGNORE_VALUE;
+	int i;
+	int rc;
+
+	rc = device_create_file(dev, &dev_attr_prox_enable);
+	if (rc) {
+		pt_debug(dev, DL_ERROR, "%s: Error, could not create enable\n",
+				__func__);
+		goto exit;
+	}
+
+	pt_debug(dev, DL_INFO, "%s: Initialize event signals\n",
+				__func__);
+
+	__set_bit(EV_ABS, pd->input->evbit);
+
+	/* set event signal capabilities */
+	for (i = 0; i < NUM_SIGNALS(pd->pdata->frmwrk); i++) {
+		signal = PARAM_SIGNAL(pd->pdata->frmwrk, i);
+		if (signal != PT_IGNORE_VALUE) {
+			input_set_abs_params(pd->input, signal,
+				PARAM_MIN(pd->pdata->frmwrk, i),
+				PARAM_MAX(pd->pdata->frmwrk, i),
+				PARAM_FUZZ(pd->pdata->frmwrk, i),
+				PARAM_FLAT(pd->pdata->frmwrk, i));
+		}
+	}
+
+	rc = input_register_device(pd->input);
+	if (rc) {
+		pt_debug(dev, DL_ERROR, "%s: Error, failed register input device r=%d\n",
+			__func__, rc);
+		goto unregister_enable;
+	}
+
+	pd->input_device_registered = true;
+	return rc;
+
+unregister_enable:
+	device_remove_file(dev, &dev_attr_prox_enable);
+exit:
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_setup_input_attention
+ *
+ * SUMMARY: Wrapper function for pt_setup_input_device_and_sysfs() that
+ *  subscribe into TTDL attention list.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *     *dev - pointer to device structure
+ ******************************************************************************/
+static int pt_setup_input_attention(struct device *dev)
+{
+	struct pt_proximity_data *pd = get_prox_data(dev);
+	int rc;
+
+	pd->si = _pt_request_sysinfo(dev);
+	if (!pd->si)
+		return -EINVAL;
+
+	rc = pt_setup_input_device_and_sysfs(dev);
+	if (!rc)
+		rc = _pt_set_proximity(pd, false);
+
+	_pt_unsubscribe_attention(dev, PT_ATTEN_STARTUP,
+		PT_PROXIMITY_NAME, pt_setup_input_attention, 0);
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_proximity_probe
+ *
+ * SUMMARY: The probe function for proximity input device
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *	*dev   - pointer to device structure
+ ******************************************************************************/
+int pt_proximity_probe(struct device *dev)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+	struct pt_proximity_data *pd = &cd->pd;
+	struct pt_platform_data *pdata = dev_get_platdata(dev);
+	struct pt_proximity_platform_data *prox_pdata;
+	int rc = 0;
+
+	if (!pdata ||  !pdata->prox_pdata) {
+		pt_debug(dev, DL_ERROR,
+			"%s: Missing platform data\n", __func__);
+		rc = -ENODEV;
+		goto error_no_pdata;
+	}
+	prox_pdata = pdata->prox_pdata;
+
+	mutex_init(&pd->prox_lock);
+	mutex_init(&pd->sysfs_lock);
+	pd->dev = dev;
+	pd->pdata = prox_pdata;
+
+	/* Create the input device and register it. */
+	pt_debug(dev, DL_INFO,
+		"%s: Create the input device and register it\n", __func__);
+	pd->input = input_allocate_device();
+	if (!pd->input) {
+		pt_debug(dev, DL_ERROR, "%s: Error, failed to allocate input device\n",
+			__func__);
+		rc = -ENODEV;
+		goto error_alloc_failed;
+	} else
+		pd->input_device_allocated = true;
+
+	if (pd->pdata->inp_dev_name)
+		pd->input->name = pd->pdata->inp_dev_name;
+	else
+		pd->input->name = PT_PROXIMITY_NAME;
+	scnprintf(pd->phys, sizeof(pd->phys), "%s/input%d", dev_name(dev),
+			cd->phys_num++);
+	pd->input->phys = pd->phys;
+	pd->input->dev.parent = pd->dev;
+	input_set_drvdata(pd->input, pd);
+
+	/* get sysinfo */
+	pd->si = _pt_request_sysinfo(dev);
+
+	if (pd->si) {
+		rc = pt_setup_input_device_and_sysfs(dev);
+		if (rc)
+			goto error_init_input;
+
+		rc = _pt_set_proximity(pd, false);
+	} else {
+		pt_debug(dev, DL_ERROR, "%s: Fail get sysinfo pointer from core p=%p\n",
+			__func__, pd->si);
+		_pt_subscribe_attention(dev, PT_ATTEN_STARTUP,
+			PT_PROXIMITY_NAME, pt_setup_input_attention,
+			0);
+	}
+
+	return 0;
+
+error_init_input:
+	input_free_device(pd->input);
+	pd->input_device_allocated = false;
+error_alloc_failed:
+error_no_pdata:
+	pt_debug(dev, DL_ERROR, "%s failed.\n", __func__);
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_proximity_release
+ *
+ * SUMMARY: The release function for proximity input device
+ *
+ * RETURN:
+ *	 0 = success
+ *
+ * PARAMETERS:
+ *	*dev   - pointer to device structure
+ ******************************************************************************/
+int pt_proximity_release(struct device *dev)
+{
+	struct pt_proximity_data *pd;
+
+	/* Ensure valid pointers before de-referencing them */
+	if (dev)
+		pd = get_prox_data(dev);
+	else
+		return 0;
+
+	/*
+	 * Second call this function may cause kernel panic if probe fail.
+	 * Use input_device_registered & input_device_allocated variable to
+	 * avoid unregister or free unavailable devive.
+	 */
+	if (pd && pd->input_device_registered) {
+		/* Disable proximity sensing */
+		pd->input_device_registered = false;
+		mutex_lock(&pd->sysfs_lock);
+		if (pd->enable_count)
+			_pt_proximity_disable(pd, true);
+		mutex_unlock(&pd->sysfs_lock);
+		device_remove_file(dev, &dev_attr_prox_enable);
+		input_unregister_device(pd->input);
+		/* Unregistering device will free the device too */
+		pd->input_device_allocated = false;
+	} else if (pd && pd->input_device_allocated) {
+		pd->input_device_allocated = false;
+		input_free_device(pd->input);
+		_pt_unsubscribe_attention(dev, PT_ATTEN_STARTUP,
+			PT_PROXIMITY_NAME, pt_setup_input_attention,
+			0);
+	}
+
+	return 0;
+}

+ 1769 - 0
qcom/opensource/touch-drivers/pt/pt_regs.h

@@ -0,0 +1,1769 @@
+/*
+ * pt_regs.h
+ * Parade TrueTouch(TM) Standard Product Registers.
+ * For use with Parade touchscreen controllers.
+ * Supported parts include:
+ * TMA5XX
+ * TMA448
+ * TMA445A
+ * TT21XXX
+ * TT31XXX
+ * TT4XXXX
+ * TT7XXX
+ * TC3XXX
+ *
+ * Copyright (C) 2015-2020 Parade Technologies
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * Contact Parade Technologies at www.paradetech.com <[email protected]>
+ *
+ */
+
+#ifndef _PT_REGS_H
+#define _PT_REGS_H
+
+#define PT_PANEL_ID_DEFAULT     0
+
+#define PT_MAX_PATH_SIZE 128
+#define PT_PIP2_BIN_FILE_PATH "/data/ttdl/pt_fw.bin"
+#define PT_SUPPRESS_AUTO_BL 0
+#define PT_ALLOW_AUTO_BL    1
+
+#define PT_PIP2_MAX_FILE_SIZE           0x18000
+#define PT_PIP2_FILE_SECTOR_SIZE        0x1000
+
+#ifndef CONFIG_DRM
+#define CONFIG_DRM
+#endif
+
+#if IS_ENABLED(CONFIG_QCOM_PANEL_EVENT_NOTIFIER)
+#define CONFIG_PANEL_NOTIFIER
+#endif
+
+#include <linux/device.h>
+#include <linux/fb.h>
+#include <linux/notifier.h>
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#include <linux/earlysuspend.h>
+#elif defined(CONFIG_DRM) || defined(CONFIG_PANEL_NOTIFIER)
+#include <drm/drm_panel.h>
+#endif
+
+#include <asm/unaligned.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/hid.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/limits.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of.h>
+#include <linux/pm_runtime.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/suspend.h>
+#include <linux/stringify.h>
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <asm/uaccess.h>
+#include <linux/workqueue.h>
+#include <linux/version.h>
+#include "pt_core.h"
+
+#include <linux/i2c.h>
+#include <linux/of_gpio.h>
+#include <linux/timer.h>
+#include <linux/timex.h>
+#include <linux/rtc.h>
+#include <linux/regulator/consumer.h>
+
+#define STATUS_SUCCESS   0
+#define STATUS_FAIL     -1
+
+#define PT_FW_FILE_PREFIX	"tt_fw"
+#define PT_FW_FILE_SUFFIX	".bin"
+#define PT_FW_FILE_NAME		"tt_fw.bin"
+#define PT_FW_RAM_FILE_NAME	"tt_fw_ram.bin"
+/* Enable special TTDL features */
+#ifndef TTHE_TUNER_SUPPORT
+#define TTHE_TUNER_SUPPORT
+#endif
+
+#ifndef TTDL_DIAGNOSTICS
+#define TTDL_DIAGNOSTICS
+#endif
+
+#ifndef EASYWAKE_TSG6
+#define EASYWAKE_TSG6
+#endif
+
+#ifdef TTHE_TUNER_SUPPORT
+#define PT_TTHE_TUNER_FILE_NAME "tthe_tuner"
+#endif
+#define PT_MAX_PRBUF_SIZE       PIPE_BUF
+#define PT_PR_TRUNCATED         " truncated..."
+
+#define PT_DEFAULT_CORE_ID      "pt_core0"
+#define PT_MAX_NUM_CORE_DEVS    5
+#define PT_IRQ_ASSERTED_VALUE	0
+
+#ifdef PT_ENABLE_MAX_ELEN
+#define PT_MAX_ELEN 100
+#endif
+
+/* Power Management Macros Enablement */
+
+#ifndef CONFIG_PM
+#define CONFIG_PM
+#endif
+
+#ifndef CONFIG_PM_RUNTIME
+#define CONFIG_PM_RUNTIME
+#endif
+
+#ifndef CONFIG_PM_SLEEP
+#define CONFIG_PM_SLEEP
+#endif
+
+/* Pin Control Macro Enablement */
+#ifndef PT_PINCTRL_EN
+#define PT_PINCTRL_EN
+#endif
+
+#ifndef TT7XXX_EXAMPLE
+#define TT7XXX_EXAMPLE
+#endif
+
+#define TOUCH_TO_WAKE_POWER_FEATURE_WORK_AROUND
+
+/*
+ * The largest PIP message is the PIP2 FILE_WRITE which has:
+ *     2 byte register
+ *     4 byte header
+ *   256 byte payload
+ *     2 byte CRC
+ */
+#define PT_MAX_PIP2_MSG_SIZE      264
+#define PT_MAX_PIP1_MSG_SIZE      255
+
+/*
+ * The minimun size of PIP2 packet includes:
+ *     2 byte length
+ *     1 byte sequence
+ *     1 byte command ID
+ *     2 byte CRC
+ */
+#define PT_MIN_PIP2_PACKET_SIZE   6
+
+static const u8 pt_data_block_security_key[] = {
+	0xA5, 0x01, 0x02, 0x03, 0xFF, 0xFE, 0xFD, 0x5A
+};
+
+/* Enum for debug reporting levels */
+enum PT_DEBUG_LEVEL {
+	DL_QUIET	= 0,
+	DL_ERROR	= 1,
+	DL_WARN		= 2,
+	DL_INFO		= 3,
+	DL_DEBUG	= 4,
+	DL_MAX
+};
+#define PT_INITIAL_DEBUG_LEVEL DL_WARN
+
+/* Startup DUT enum status bitmask */
+enum PT_STARTUP_STATUS {
+	STARTUP_STATUS_START              = 0,
+	STARTUP_STATUS_BL_RESET_SENTINEL  = 0x001,
+	STARTUP_STATUS_FW_RESET_SENTINEL  = 0x002,
+	STARTUP_STATUS_GET_DESC           = 0x004,
+	STARTUP_STATUS_FW_OUT_OF_BOOT     = 0x008,
+	STARTUP_STATUS_GET_RPT_DESC       = 0x010,
+	STARTUP_STATUS_GET_SYS_INFO       = 0x020,
+	STARTUP_STATUS_GET_CFG_CRC        = 0x040,
+	STARTUP_STATUS_RESTORE_PARM       = 0x080,
+	STARTUP_STATUS_COMPLETE           = 0x100,
+	STARTUP_STATUS_FULL               = 0x1FF
+};
+
+#define PT_INITIAL_SHOW_TIME_STAMP 0
+
+/*
+ * Print out all debug prints that are less then or equal to set level.
+ */
+#define pt_debug(dev, dlevel, format, arg...)	 \
+	do { \
+		struct pt_core_data *cd_tmp = dev_get_drvdata(dev);\
+		if (cd_tmp->debug_level >= dlevel) {\
+			if (dlevel == DL_ERROR)\
+				dev_err(dev, "[%d] "format, dlevel, ##arg);\
+			else\
+				dev_info(dev, "[%d] "format, dlevel, ##arg);\
+		} \
+	} while (0)
+
+enum PT_PIP_REPORT_ID {
+	PT_PIP_INVALID_REPORT_ID           = 0x00,
+	PT_PIP_TOUCH_REPORT_ID             = 0x01,
+	PT_PIP_TOUCH_REPORT_WIN8_ID        = 0x02,
+	PT_PIP_CAPSENSE_BTN_REPORT_ID      = 0x03,
+	PT_PIP_WAKEUP_REPORT_ID            = 0x04,
+	PT_PIP_NOISE_METRIC_REPORT_ID      = 0x05,
+	PT_PIP_PUSH_BUTTON_REPORT_ID       = 0x06,
+	PT_PIP_SELFCAP_INPUT_REPORT_ID     = 0x0D,
+	PT_PIP_TRACKING_HEATMAP_REPORT_ID  = 0x0E,
+	PT_PIP_SENSOR_DATA_REPORT_ID       = 0x0F,
+	PT_PIP_NON_HID_RESPONSE_ID         = 0x1F,
+	PT_PIP_NON_HID_COMMAND_ID          = 0x2F,
+	PT_PIP_BL_RESPONSE_REPORT_ID       = 0x30,
+	PT_PIP_BL_COMMAND_REPORT_ID        = 0x40
+};
+
+enum PT_HID_REPORT_ID {
+	PT_HID_FINGER_REPORT_ID            = 0x01,
+	PT_HID_PEN_REPORT_ID               = 0x02
+};
+
+
+/* HID IDs and commands */
+#define HID_VENDOR_ID                         0x04B4
+#define HID_APP_PRODUCT_ID                    0xC101
+#define HID_VERSION                           0x0100
+#define HID_APP_REPORT_ID                       0xF7
+#define HID_BL_REPORT_ID                        0xFF
+#define HID_RESPONSE_REPORT_ID                  0xF0
+#define HID_POWER_ON                             0x0
+#define HID_POWER_SLEEP                          0x1
+#define HID_POWER_STANDBY                        0x2
+
+/* PIP1 offsets and masks */
+#define PIP1_RESP_REPORT_ID_OFFSET                 2
+#define PIP1_RESP_COMMAND_ID_OFFSET                4
+#define PIP1_RESP_COMMAND_ID_MASK               0x7F
+#define PIP1_CMD_COMMAND_ID_OFFSET                 6
+#define PIP1_CMD_COMMAND_ID_MASK                0x7F
+
+#define PIP1_SYSINFO_TTDATA_OFFSET                 5
+#define PIP1_SYSINFO_SENSING_OFFSET               33
+#define PIP1_SYSINFO_BTN_OFFSET                   48
+#define PIP1_SYSINFO_BTN_MASK                   0xFF
+#define PIP1_SYSINFO_MAX_BTN                       8
+
+/*  Timeouts in ms */
+#define PT_PTSBC_INIT_WATCHDOG_TIMEOUT         20000
+#define PT_REQUEST_EXCLUSIVE_TIMEOUT            8000
+#define PT_WATCHDOG_TIMEOUT                     2000
+#define PT_FW_EXIT_BOOT_MODE_TIMEOUT            1000
+#define PT_BL_WAIT_FOR_SENTINEL                  500
+#define PT_REQUEST_ENUM_TIMEOUT                 4000
+#define PT_GET_HID_DESCRIPTOR_TIMEOUT            500
+#define PT_HID_CMD_DEFAULT_TIMEOUT               500
+#define PT_PIP_CMD_DEFAULT_TIMEOUT              2000
+#define PT_PIP1_CMD_DEFAULT_TIMEOUT             1000
+#define PT_PIP1_START_BOOTLOADER_TIMEOUT        2000
+#define PT_PIP1_CMD_GET_SYSINFO_TIMEOUT          500
+#define PT_PIP1_CMD_CALIBRATE_IDAC_TIMEOUT      5000
+#define PT_PIP1_CMD_CALIBRATE_EXT_TIMEOUT       5000
+#define PT_PIP1_CMD_WRITE_CONF_BLOCK_TIMEOUT     400
+#define PT_PIP1_CMD_RUN_SELF_TEST_TIMEOUT      10000
+#define PT_PIP1_CMD_INITIATE_BL_TIMEOUT        20000
+#define PT_PIP1_CMD_PROGRAM_AND_VERIFY_TIMEOUT   400
+#define PT_PIP2_CMD_FILE_ERASE_TIMEOUT          3000
+
+/* Max counts */
+#define PT_WATCHDOG_RETRY_COUNT                   30
+#define PT_BUS_READ_INPUT_RETRY_COUNT              2
+
+#define PT_FLUSH_BUS_BASED_ON_LEN                  0
+#define PT_FLUSH_BUS_FULL_256_READ                 1
+
+/* maximum number of concurrent tracks */
+#define TOUCH_REPORT_SIZE                         10
+#define TOUCH_INPUT_HEADER_SIZE                    7
+#define TOUCH_COUNT_BYTE_OFFSET                    5
+#define BTN_REPORT_SIZE                            9
+#define BTN_INPUT_HEADER_SIZE                      5
+#define SENSOR_REPORT_SIZE                       150
+#define SENSOR_HEADER_SIZE                         4
+
+/* helpers */
+#define GET_NUM_TOUCHES(x)          ((x) & 0x1F)
+#define IS_LARGE_AREA(x)            ((x) & 0x20)
+#define IS_BAD_PKT(x)               ((x) & 0x20)
+#define IS_TMO(t)                   ((t) == 0)
+#define HI_BYTE(x)                  (u8)(((x) >> 8) & 0xFF)
+#define LOW_BYTE(x)                 (u8)((x) & 0xFF)
+#define SET_CMD_LOW(byte, bits)	\
+	((byte) = (((byte) & 0xF0) | ((bits) & 0x0F)))
+#define SET_CMD_HIGH(byte, bits)\
+	((byte) = (((byte) & 0x0F) | ((bits) & 0xF0)))
+
+#define GET_MASK(length) \
+	((1 << length) - 1)
+#define GET_FIELD(name, length, shift) \
+	((name >> shift) & GET_MASK(length))
+
+#define _base(x) ((x >= '0' && x <= '9') ? '0' : \
+	(x >= 'a' && x <= 'f') ? 'a' - 10 : \
+	(x >= 'A' && x <= 'F') ? 'A' - 10 : \
+	'\255')
+#define HEXOF(x) (x - _base(x))
+
+#define IS_EASY_WAKE_CONFIGURED(x) \
+		((x) != 0 && (x) != 0xFF)
+
+#define IS_PIP_VER_GE(p, maj, min) \
+		((p)->ttdata.pip_ver_major > (maj) ? \
+			1 : \
+			(((p)->ttdata.pip_ver_major == (maj) ? \
+				((p)->ttdata.pip_ver_minor >= (min) ? \
+				1 : 0) : \
+				0)))
+#define IS_PIP_VER_EQ(p, maj, min) \
+		((p)->ttdata.pip_ver_major == (maj) ? \
+			((p)->ttdata.pip_ver_minor == (min) ? \
+			1 : \
+				0 : \
+				0))
+#define PT_PANEL_ID_BITMASK  0x0000000C
+#define PT_PANEL_ID_SHIFT    2
+
+#define TTDL_PTVIRTDUT_SUPPORT  1
+
+/* DUT Debug commands (dut_debug sysfs) */
+#define PT_DUT_DBG_HID_RESET                  50
+#define PT_DUT_DBG_HID_GET_REPORT             51 /* depricated */
+#define PT_DUT_DBG_HID_SET_REPORT             52 /* depricated */
+#define PT_DUT_DBG_HID_SET_POWER_ON           53
+#define PT_DUT_DBG_HID_SET_POWER_SLEEP        54
+#define PT_DUT_DBG_HID_SET_POWER_STANDBY      55
+#define PT_DUT_DBG_PIP_SOFT_RESET             97
+#define PT_DUT_DBG_RESET                      98
+#define PT_DUT_DBG_PIP_NULL                  100
+#define PT_DUT_DBG_PIP_ENTER_BL              101
+#define PT_DUT_DBG_HID_SYSINFO               102
+#define PT_DUT_DBG_PIP_SUSPEND_SCAN          103
+#define PT_DUT_DBG_PIP_RESUME_SCAN           104
+#define	PT_DUT_DBG_HID_DESC                  109
+
+/* Driver Debug commands (drv_debug sysfs) */
+#define PT_DRV_DBG_SUSPEND                     4
+#define PT_DRV_DBG_RESUME                      5
+#define PT_DRV_DBG_STOP_WD                   105
+#define PT_DRV_DBG_START_WD                  106
+#define PT_DRV_DBG_TTHE_TUNER_EXIT           107
+#define	PT_DRV_DBG_TTHE_BUF_CLEAN            108
+#define	PT_DRV_DBG_CLEAR_PARM_LIST           110
+#define PT_DRV_DBG_FORCE_BUS_READ            111
+#define PT_DRV_DBG_CLEAR_CAL_DATA            112
+
+/*
+ * Commands that require additional parameters
+ * will be in the 200 range. Commands that do not
+ * require additional parameters remain below 200.
+ */
+#define PT_DRV_DBG_REPORT_LEVEL              200
+#define PT_DRV_DBG_WATCHDOG_INTERVAL         201
+#define PT_DRV_DBG_SHOW_TIMESTAMP            202
+#define PT_DRV_DBG_SET_GENERATION            210
+
+#ifdef TTDL_DIAGNOSTICS
+#define PT_DRV_DBG_FLUSH_BUS                 204 /* deprecated */
+#define PT_DRV_DBG_SETUP_PWR                 205
+#define PT_DRV_DBG_GET_PUT_SYNC              206
+#define PT_DRV_DBG_SET_PIP2_LAUNCH_APP       207 /* deprecated */
+#define PT_DRV_DBG_SET_TT_DATA               208
+#define PT_DRV_DBG_SET_RUN_FW_PIN            209 /* deprecated */
+#define PT_DRV_DBG_SET_BRIDGE_MODE           211
+#define PT_DRV_DBG_SET_I2C_ADDRESS           212
+#define PT_DRV_DBG_SET_FLASHLESS_DUT         213
+#define PT_DRV_DBG_SET_FORCE_SEQ             214
+#define PT_DRV_DBG_BL_WITH_NO_INT            215
+#define PT_DRV_DBG_CAL_CACHE_IN_HOST         216
+#define PT_DRV_DBG_MULTI_CHIP                217
+#define PT_DRV_DBG_SET_PANEL_ID_TYPE         218
+#define PT_DRV_DBG_PIP_TIMEOUT               219
+#define PT_DRV_DBG_TTHE_HID_USB_FORMAT       220
+#ifdef TTDL_PTVIRTDUT_SUPPORT
+#define PT_DRV_DBG_SET_HW_DETECT             298
+#define PT_DRV_DBG_VIRTUAL_I2C_DUT           299
+#endif /* TTDL_PTVIRTDUT_SUPPORT */
+
+/* TTDL Built In Self Test selection bit masks */
+#define PT_TTDL_BIST_BUS_TEST           0x01
+#define PT_TTDL_BIST_IRQ_TEST           0x02
+#define PT_TTDL_BIST_TP_XRES_TEST       0x04
+#define PT_TTDL_BIST_SLAVE_BUS_TEST     0x08
+#define PT_TTDL_BIST_SLAVE_IRQ_TEST     0x10
+#define PT_TTDL_BIST_SLAVE_XRES_TEST    0x20
+
+#define SLAVE_DETECT_MASK               0x01
+
+#define VIRT_MAX_IRQ_RELEASE_TIME_US    500000
+#endif /* TTDL DIAGNOSTICS */
+
+/* Recognized usages */
+/* undef them first for possible redefinition in Linux */
+#undef HID_DI_PRESSURE
+#undef HID_DI_TIP
+#undef HID_DI_CONTACTID
+#undef HID_DI_CONTACTCOUNT
+#undef HID_DI_SCANTIME
+#define HID_DI_PRESSURE                   0x000d0030
+#define HID_DI_TIP                        0x000d0042
+#define HID_DI_CONTACTID                  0x000d0051
+#define HID_DI_CONTACTCOUNT               0x000d0054
+#define HID_DI_SCANTIME                   0x000d0056
+
+/* Parade vendor specific usages */
+#define HID_PT_UNDEFINED                  0xff010000
+#define HID_PT_BOOTLOADER                 0xff010001
+#define HID_PT_TOUCHAPPLICATION           0xff010002
+#define HID_PT_BUTTONS                    0xff010020
+#define HID_PT_GENERICITEM                0xff010030
+#define HID_PT_LARGEOBJECT                0xff010040
+#define HID_PT_NOISEEFFECTS               0xff010041
+#define HID_PT_REPORTCOUNTER              0xff010042
+#define HID_PT_TOUCHTYPE                  0xff010060
+#define HID_PT_EVENTID                    0xff010061
+#define HID_PT_MAJORAXISLENGTH            0xff010062
+#define HID_PT_MINORAXISLENGTH            0xff010063
+#define HID_PT_ORIENTATION                0xff010064
+#define HID_PT_BUTTONSIGNAL               0xff010065
+#define HID_PT_MAJOR_CONTACT_AXIS_LENGTH  0xff010066
+#define HID_PT_MINOR_CONTACT_AXIS_LENGTH  0xff010067
+#define HID_PT_TCH_COL_USAGE_PG           0x000D0022
+#define HID_PT_BTN_COL_USAGE_PG           0xFF010020
+
+#define PANEL_ID_NOT_ENABLED	0xFF
+
+#ifdef EASYWAKE_TSG6
+#define GESTURE_DOUBLE_TAP         (1)
+#define GESTURE_TWO_FINGERS_SLIDE  (2)
+#define GESTURE_TOUCH_DETECTED     (3)
+#define GESTURE_PUSH_BUTTON        (4)
+#define GESTURE_SINGLE_SLIDE_DE_TX (5)
+#define GESTURE_SINGLE_SLIDE_IN_TX (6)
+#define GESTURE_SINGLE_SLIDE_DE_RX (7)
+#define GESTURE_SINGLE_SLIDE_IN_RX (8)
+#endif
+
+/* FW RAM parameters */
+#define PT_RAM_ID_TOUCHMODE_ENABLED	0x02
+#define PT_RAM_ID_PROXIMITY_ENABLE	0x20
+#define PT_RAM_ID_TOUCHMODE_ENABLED_SIZE	1
+#define PT_RAM_ID_PROXIMITY_ENABLE_SIZE	1
+
+/* abs signal capabilities offsets in the frameworks array */
+enum pt_sig_caps {
+	PT_SIGNAL_OST,
+	PT_MIN_OST,
+	PT_MAX_OST,
+	PT_FUZZ_OST,
+	PT_FLAT_OST,
+	PT_NUM_ABS_SET	/* number of signal capability fields */
+};
+
+/* helpers */
+#define NUM_SIGNALS(frmwrk)		((frmwrk)->size / PT_NUM_ABS_SET)
+#define PARAM(frmwrk, sig_ost, cap_ost) \
+		((frmwrk)->abs[((sig_ost) * PT_NUM_ABS_SET) + (cap_ost)])
+
+#define PARAM_SIGNAL(frmwrk, sig_ost)	PARAM(frmwrk, sig_ost, PT_SIGNAL_OST)
+#define PARAM_MIN(frmwrk, sig_ost)	PARAM(frmwrk, sig_ost, PT_MIN_OST)
+#define PARAM_MAX(frmwrk, sig_ost)	PARAM(frmwrk, sig_ost, PT_MAX_OST)
+#define PARAM_FUZZ(frmwrk, sig_ost)	PARAM(frmwrk, sig_ost, PT_FUZZ_OST)
+#define PARAM_FLAT(frmwrk, sig_ost)	PARAM(frmwrk, sig_ost, PT_FLAT_OST)
+
+/* abs axis signal offsets in the framworks array  */
+enum pt_sig_ost {
+	PT_ABS_X_OST,
+	PT_ABS_Y_OST,
+	PT_ABS_P_OST,
+	PT_ABS_W_OST,
+	PT_ABS_ID_OST,
+	PT_ABS_MAJ_OST,
+	PT_ABS_MIN_OST,
+	PT_ABS_OR_OST,
+	PT_ABS_TOOL_OST,
+	PT_ABS_D_OST,
+	PT_NUM_ABS_OST	/* number of abs signals */
+};
+
+enum hid_command {
+	HID_CMD_RESERVED       = 0x0,
+	HID_CMD_RESET          = 0x1,
+	HID_CMD_GET_REPORT     = 0x2,
+	HID_CMD_SET_REPORT     = 0x3,
+	HID_CMD_GET_IDLE       = 0x4,
+	HID_CMD_SET_IDLE       = 0x5,
+	HID_CMD_GET_PROTOCOL   = 0x6,
+	HID_CMD_SET_PROTOCOL   = 0x7,
+	HID_CMD_SET_POWER      = 0x8,
+	HID_CMD_VENDOR         = 0xE,
+};
+
+enum PIP1_cmd_type {
+	PIP1_CMD_TYPE_FW,
+	PIP1_CMD_TYPE_BL,
+};
+
+/* PIP BL cmd IDs and input for dut_debug sysfs */
+enum pip1_bl_cmd_id {
+	PIP1_BL_CMD_ID_VERIFY_APP_INTEGRITY     = 0x31, /* 49 */
+	PIP1_BL_CMD_ID_GET_INFO                 = 0x38, /* 56 */
+	PIP1_BL_CMD_ID_PROGRAM_AND_VERIFY       = 0x39, /* 57 */
+	PIP1_BL_CMD_ID_LAUNCH_APP               = 0x3B, /* 59 */
+	PIP1_BL_CMD_ID_GET_PANEL_ID             = 0x3E, /* 62 */
+	PIP1_BL_CMD_ID_INITIATE_BL              = 0x48, /* 72 */
+	PIP1_BL_CMD_ID_LAST,
+};
+#define PIP1_BL_SOP	0x1
+#define PIP1_BL_EOP	0x17
+
+/* PIP1 Command/Response IDs */
+enum PIP1_CMD_ID {
+	PIP1_CMD_ID_NULL                        = 0x00,
+	PIP1_CMD_ID_START_BOOTLOADER            = 0x01,
+	PIP1_CMD_ID_GET_SYSINFO                 = 0x02,
+	PIP1_CMD_ID_SUSPEND_SCANNING            = 0x03,
+	PIP1_CMD_ID_RESUME_SCANNING             = 0x04,
+	PIP1_CMD_ID_GET_PARAM                   = 0x05,
+	PIP1_CMD_ID_SET_PARAM                   = 0x06,
+	PIP1_CMD_ID_GET_NOISE_METRICS           = 0x07,
+	PIP1_CMD_ID_RESERVED                    = 0x08,
+	PIP1_CMD_ID_ENTER_EASYWAKE_STATE        = 0x09,
+	PIP1_CMD_ID_VERIFY_CONFIG_BLOCK_CRC     = 0x20,
+	PIP1_CMD_ID_GET_CONFIG_ROW_SIZE         = 0x21,
+	PIP1_CMD_ID_READ_DATA_BLOCK             = 0x22,
+	PIP1_CMD_ID_WRITE_DATA_BLOCK            = 0x23,
+	PIP1_CMD_ID_GET_DATA_STRUCTURE          = 0x24,
+	PIP1_CMD_ID_LOAD_SELF_TEST_PARAM        = 0x25,
+	PIP1_CMD_ID_RUN_SELF_TEST               = 0x26,
+	PIP1_CMD_ID_GET_SELF_TEST_RESULT        = 0x27,
+	PIP1_CMD_ID_CALIBRATE_IDACS             = 0x28,
+	PIP1_CMD_ID_INITIALIZE_BASELINES        = 0x29,
+	PIP1_CMD_ID_EXEC_PANEL_SCAN             = 0x2A,
+	PIP1_CMD_ID_RETRIEVE_PANEL_SCAN         = 0x2B,
+	PIP1_CMD_ID_START_SENSOR_DATA_MODE      = 0x2C,
+	PIP1_CMD_ID_STOP_SENSOR_DATA_MODE       = 0x2D,
+	PIP1_CMD_ID_START_TRACKING_HEATMAP_MODE = 0x2E,
+	PIP1_CMD_ID_START_SELF_CAP_RPT_MODE     = 0x2F,
+	PIP1_CMD_ID_CALIBRATE_DEVICE_EXTENDED   = 0x30,
+	PIP1_CMD_ID_INT_PIN_OVERRIDE            = 0x40,
+	PIP1_CMD_ID_STORE_PANEL_SCAN            = 0x60,
+	PIP1_CMD_ID_PROCESS_PANEL_SCAN          = 0x61,
+	PIP1_CMD_ID_DISCARD_INPUT_REPORT,
+	PIP1_CMD_ID_LAST,
+	PIP1_CMD_ID_USER_CMD,
+};
+
+/* PIP2 Command/Response data and structures */
+enum PIP2_CMD_ID {
+	PIP2_CMD_ID_PING                = 0x00,
+	PIP2_CMD_ID_STATUS              = 0x01,
+	PIP2_CMD_ID_CTRL                = 0x02,
+	PIP2_CMD_ID_CONFIG              = 0x03,
+	PIP2_CMD_ID_RESERVE             = 0x04,
+	PIP2_CMD_ID_CLEAR               = 0x05,
+	PIP2_CMD_ID_RESET               = 0x06,
+	PIP2_CMD_ID_VERSION             = 0x07,
+	PIP2_CMD_ID_FILE_OPEN           = 0x10,
+	PIP2_CMD_ID_FILE_CLOSE          = 0x11,
+	PIP2_CMD_ID_FILE_READ           = 0x12,
+	PIP2_CMD_ID_FILE_WRITE          = 0x13,
+	PIP2_CMD_ID_FILE_IOCTL          = 0x14,
+	PIP2_CMD_ID_FLASH_INFO          = 0x15,
+	PIP2_CMD_ID_EXECUTE             = 0x16,
+	PIP2_CMD_ID_GET_LAST_ERRNO      = 0x17,
+	PIP2_CMD_ID_EXIT_HOST_MODE      = 0x18,
+	PIP2_CMD_ID_READ_GPIO           = 0x19,
+	PIP2_CMD_EXECUTE_SCAN           = 0x21,
+	PIP2_CMD_SET_PARAMETER          = 0x40,
+	PIP2_CMD_GET_PARAMETER          = 0x41,
+	PIP2_CMD_SET_DDI_REG            = 0x42,
+	PIP2_CMD_GET_DDI_REG            = 0x43,
+	PIP2_CMD_ID_END                 = 0x7F
+};
+
+enum PIP2_STATUS_EXEC_RUNNING {
+	PIP2_STATUS_BOOT_EXEC           = 0x00,
+	PIP2_STATUS_APP_EXEC            = 0x01,
+};
+
+/* FW_SYS_MODE_UNDEFINED must be 1 greater than FW_SYS_MODE_MAX */
+enum PIP2_FW_SYSTEM_MODE {
+	FW_SYS_MODE_BOOT                = 0x00,
+	FW_SYS_MODE_SCANNING            = 0x01,
+	FW_SYS_MODE_DEEP_SLEEP          = 0x02,
+	FW_SYS_MODE_TEST                = 0x03,
+	FW_SYS_MODE_DEEP_STANDBY        = 0x04,
+	FW_SYS_MODE_MAX                 = FW_SYS_MODE_DEEP_STANDBY,
+	FW_SYS_MODE_UNDEFINED           = FW_SYS_MODE_MAX + 1,
+};
+
+/* PIP2 Command/Response data and structures */
+enum PIP2_FILE_ID {
+	PIP2_RAM_FILE			= 0x00,
+	PIP2_FW_FILE			= 0x01,
+	PIP2_CONFIG_FILE		= 0x02,
+	PIP2_FILE_3			= 0x03,
+	PIP2_FILE_4			= 0x04,
+	PIP2_FILE_5			= 0x05,
+	PIP2_FILE_6			= 0x06,
+	PIP2_FILE_7			= 0x07,
+	PIP2_FILE_MAX			= PIP2_FILE_7,
+};
+
+/* Optimize packet sizes per Allwinner H3 bus drivers */
+#define PIP2_FILE_WRITE_LEN_PER_PACKET		245
+#define PIP2_BL_I2C_FILE_WRITE_LEN_PER_PACKET	245
+#define PIP2_BL_SPI_FILE_WRITE_LEN_PER_PACKET	256
+
+enum DUT_GENERATION {
+	DUT_UNKNOWN                     = 0x00,
+	DUT_PIP1_ONLY			= 0x01,
+	DUT_PIP2_CAPABLE		= 0x02,
+};
+
+enum PIP2_RSP_ERR {
+	PIP2_RSP_ERR_NONE               = 0x00,
+	PIP2_RSP_ERR_BUSY               = 0x01,
+	PIP2_RSP_ERR_INIT_FAILURE       = 0x02,
+	PIP2_RSP_ERR_ALREADY_OPEN       = 0x03,
+	PIP2_RSP_ERR_NOT_OPEN           = 0x04,
+	PIP2_RSP_ERR_IO_FAILURE         = 0x05,
+	PIP2_RSP_ERR_UNKNOWN_IOCTL      = 0x06,
+	PIP2_RSP_ERR_BAD_ADDRESS        = 0x07,
+	PIP2_RSP_ERR_BAD_FILE           = 0x08,
+	PIP2_RSP_ERR_END_OF_FILE        = 0x09,
+	PIP2_RSP_ERR_TOO_MANY_FILES     = 0x0A,
+	PIP2_RSP_ERR_TIMEOUT            = 0x0B,
+	PIP2_RSP_ERR_ABORTED            = 0x0C,
+	PIP2_RSP_ERR_CRC                = 0x0D,
+	PIP2_RSP_ERR_UNKNOWN_REC_TYPE   = 0x0E,
+	PIP2_RSP_ERR_BAD_FRAME          = 0x0F,
+	PIP2_RSP_ERR_NO_PERMISSION      = 0x10,
+	PIP2_RSP_ERR_UNKNOWN_COMMAND    = 0x11,
+	PIP2_RSP_ERR_INVALID_PARAM      = 0x12,
+	PIP2_RSP_ERR_IO_ALREADY_ACTIVE  = 0x13,
+	PIP2_RSP_ERR_SHUTDOWN           = 0x14,
+	PIP2_RSP_ERR_INVALID_IMAGE      = 0x15,
+	PIP2_RSP_ERR_UNKNOWN_REGISTER   = 0x16,
+	PIP2_RSP_ERR_BAD_LENGTH         = 0x17,
+	PIP2_RSP_ERR_TRIM_FAILURE       = 0x18,
+};
+
+/*
+ * Extra bytes for PIP2 = 4 + 2:
+ * 4 byte header - (len_lsb, len_msb, report ID, Tag, Sequence)
+ * 2 byte footer - (crc_lsb, crc_msb)
+ */
+#define PIP2_CMD_COMMAND_ID_OFFSET           5
+#define PIP2_CMD_COMMAND_ID_MASK          0x7F
+#define PIP2_RESP_COMMAND_ID_OFFSET          3
+#define PIP2_RESP_SEQUENCE_OFFSET            2
+#define PIP2_RESP_SEQUENCE_MASK           0x0F
+#define PIP2_RESP_REPORT_ID_OFFSET           3
+#define PIP2_RESP_STATUS_OFFSET              4
+#define PIP2_RESP_BODY_OFFSET                5
+#define PIP2_CRC_SIZE                        2
+#define PIP2_LEN_FIELD_SIZE                  2
+#define PIP2_VERSION_CHIP_REV_OFFSET        14
+#define PIP2_EXTRA_BYTES_NUM (PIP2_RESP_STATUS_OFFSET + PIP2_CRC_SIZE)
+
+/* File IOCTL commands */
+#define PIP2_FILE_IOCTL_CODE_ERASE_FILE		0
+#define PIP2_FILE_IOCTL_CODE_SEEK_POINTER	1
+#define PIP2_FILE_IOCTL_CODE_AES_CONTROL	2
+#define PIP2_FILE_IOCTL_CODE_FILE_STATS		3
+#define PIP2_FILE_IOCTL_CODE_FILE_CRC		4
+
+struct pip2_cmd_structure {
+	u8 reg[2];
+	u16 len;
+	u8 id;
+	u8 seq;
+	u8 *data;
+	u8 crc[2]; /* MSB:crc[0], LSB:crc[1] */
+};
+
+struct pip2_cmd_response_structure {
+	u8 id;
+	u16 response_len;
+	u32 response_time_min;
+	u32 response_time_max;
+};
+
+enum pip1_bl_status {
+	ERROR_SUCCESS,
+	ERROR_KEY,
+	ERROR_VERIFICATION,
+	ERROR_LENGTH,
+	ERROR_DATA,
+	ERROR_COMMAND,
+	ERROR_CRC                = 8,
+	ERROR_FLASH_ARRAY,
+	ERROR_FLASH_ROW,
+	ERROR_FLASH_PROTECTION,
+	ERROR_UNKNOWN             = 15,
+	ERROR_INVALID,
+};
+
+enum pt_mode {
+	PT_MODE_UNKNOWN      = 0,
+	PT_MODE_BOOTLOADER   = 1,
+	PT_MODE_OPERATIONAL  = 2,
+	PT_MODE_IGNORE       = 255,
+};
+
+enum PT_ENTER_BL_RESULT {
+	PT_ENTER_BL_PASS                = 0,
+	PT_ENTER_BL_ERROR               = 1,
+	PT_ENTER_BL_RESET_FAIL          = 2,
+	PT_ENTER_BL_HID_START_BL_FAIL   = 3,
+	PT_ENTER_BL_CONFIRM_FAIL        = 4,
+	PT_ENTER_BL_GET_FLASH_INFO_FAIL = 5,
+};
+
+enum TTDL_EXTENDED_ERROR_CODES {
+	EX_ERR_FREAD            = 400,
+	EX_ERR_FWRITE           = 401,
+	EX_ERR_FOPEN            = 402,
+	EX_ERR_FCLOSE           = 403,
+	EX_ERR_FLEN             = 404,
+};
+
+enum pt_cmd_status {
+	PT_CMD_STATUS_SUCCESS        = 0,
+	PT_CMD_STATUS_FAILURE        = 1,
+};
+
+enum {
+	PT_IC_GRPNUM_RESERVED,
+	PT_IC_GRPNUM_CMD_REGS,
+	PT_IC_GRPNUM_TCH_REP,
+	PT_IC_GRPNUM_DATA_REC,
+	PT_IC_GRPNUM_TEST_REC,
+	PT_IC_GRPNUM_PCFG_REC,
+	PT_IC_GRPNUM_TCH_PARM_VAL,
+	PT_IC_GRPNUM_TCH_PARM_SIZE,
+	PT_IC_GRPNUM_RESERVED1,
+	PT_IC_GRPNUM_RESERVED2,
+	PT_IC_GRPNUM_OPCFG_REC,
+	PT_IC_GRPNUM_DDATA_REC,
+	PT_IC_GRPNUM_MDATA_REC,
+	PT_IC_GRPNUM_TEST_REGS,
+	PT_IC_GRPNUM_BTN_KEYS,
+	PT_IC_GRPNUM_TTHE_REGS,
+	PT_IC_GRPNUM_SENSING_CONF,
+	PT_IC_GRPNUM_NUM,
+};
+
+enum pt_event_id {
+	PT_EV_NO_EVENT,
+	PT_EV_TOUCHDOWN,
+	PT_EV_MOVE,		/* significant displacement (> act dist) */
+	PT_EV_LIFTOFF,		/* record reports last position */
+};
+
+enum pt_object_id {
+	PT_OBJ_STANDARD_FINGER,
+	PT_OBJ_PROXIMITY,
+	PT_OBJ_STYLUS,
+	PT_OBJ_GLOVE,
+};
+
+enum pt_self_test_result {
+	PT_ST_RESULT_PASS                = 0,
+	PT_ST_RESULT_FAIL                = 1,
+	PT_ST_RESULT_ABORTED             = 2,
+	PT_ST_RESULT_PARAM_ERR           = 3,
+	PT_ST_RESULT_CFG_ERR             = 4,
+	PT_ST_RESULT_CAL_ERR             = 5,
+	PT_ST_RESULT_DDI_STATE_ERR       = 6,
+	PT_ST_RESULT_HOST_MUST_INTERPRET = 0xFF,
+};
+#define PT_ST_PRINT_RESULTS    true
+#define PT_ST_NOPRINT          false
+#define PT_ST_GET_RESULTS      true
+#define PT_ST_DONT_GET_RESULTS false
+
+/*
+ * Maximum number of parameters for the fw_self_test sysfs (255 - 12 + 2)
+ * 255 - Max PIP message size
+ *  12 - Header size for PIP message 0x25 (Load Self Test Parameters)
+ *   2 - Additional parameters for fw_self_test for test_id and format
+ */
+#define PT_FW_SELF_TEST_MAX_PARM 245
+
+enum pt_self_test_id {
+	PT_ST_ID_NULL                        = 0,
+	PT_ST_ID_BIST                        = 1,
+	PT_ST_ID_SHORTS                      = 2,
+	PT_ST_ID_OPENS                       = 3,
+	PT_ST_ID_AUTOSHORTS                  = 4,
+	PT_ST_ID_CM_PANEL                    = 5,
+	PT_ST_ID_CP_PANEL                    = 6,
+	PT_ST_ID_CM_BUTTON                   = 7,
+	PT_ST_ID_CP_BUTTON                   = 8,
+	PT_ST_ID_FORCE                       = 9,
+	PT_ST_ID_OPENS_HIZ                   = 10,
+	PT_ST_ID_OPENS_GND                   = 11,
+	PT_ST_ID_CP_LFT                      = 12,
+	PT_ST_ID_SC_NOISE                    = 13,
+	PT_ST_ID_LFT_NOISE                   = 14,
+	PT_ST_ID_CP_CHIP_ROUTE_PARASITIC_CAP = 15,
+	PT_ST_ID_NORMALIZED_RAW_CNT_PANEL    = 16,
+	PT_ST_ID_NORMALIZED_RAW_CNT_LFT      = 17,
+	PT_ST_ID_INVALID                     = 255
+};
+
+enum pt_scan_state {
+	PT_SCAN_STATE_UNKNOWN    = 0,
+	PT_SCAN_STATE_ACTIVE     = 1,
+	PT_SCAN_STATE_INACTIVE   = 2,
+};
+
+#define PT_CAL_DATA_MAX_SIZE    2048
+#define PT_CAL_DATA_ROW_SIZE     128
+#define PT_WAFER_LOT_SIZE          5
+#define PT_UID_SIZE               12
+
+enum pt_cal_data_actions {
+	PT_CAL_DATA_SAVE         = 0,
+	PT_CAL_DATA_RESTORE      = 1,
+	PT_CAL_DATA_CLEAR        = 2,
+	PT_CAL_DATA_INFO         = 3
+};
+
+enum pt_feature_enable_state {
+	PT_FEATURE_DISABLE     = 0,
+	PT_FEATURE_ENABLE      = 1
+};
+
+#define PT_NUM_MFGID               8
+/* System Information interface definitions */
+struct pt_ttdata_dev {
+	u8 pip_ver_major;
+	u8 pip_ver_minor;
+	__le16 fw_pid;
+	u8 fw_ver_major;
+	u8 fw_ver_minor;
+	__le32 revctrl;
+	__le16 fw_ver_conf;
+	u8 bl_ver_major;
+	u8 bl_ver_minor;
+	__le16 jtag_si_id_l;
+	__le16 jtag_si_id_h;
+	u8 mfg_id[PT_NUM_MFGID];
+	__le16 post_code;
+} __packed;
+
+/* Struct to cast over PIP2 VERSION response */
+struct pt_pip2_version_full {
+	u8 status_code;
+	u8 pip2_version_lsb;
+	u8 pip2_version_msb;
+	u8 fw_version_lsb;
+	u8 fw_version_msb;
+	u8 bl_version_lsb;
+	u8 bl_version_msb;
+	__le16 chip_rev;
+	__le16 chip_id;
+	u8 uid[PT_UID_SIZE];
+} __packed;
+
+struct pt_pip2_version {
+	u8 status_code;
+	u8 pip2_version_lsb;
+	u8 pip2_version_msb;
+	u8 bl_version_lsb;
+	u8 bl_version_msb;
+	u8 fw_version_lsb;
+	u8 fw_version_msb;
+	__le16 chip_id;
+	__le16 chip_rev;
+} __packed;
+
+struct pt_sensing_conf_data_dev {
+	u8 electrodes_x;
+	u8 electrodes_y;
+	__le16 len_x;
+	__le16 len_y;
+	__le16 res_x;
+	__le16 res_y;
+	__le16 max_z;
+	u8 origin_x;
+	u8 origin_y;
+	u8 panel_id;
+	u8 btn;
+	u8 scan_mode;
+	u8 max_num_of_tch_per_refresh_cycle;
+} __packed;
+
+struct pt_ttdata {
+	u8 pip_ver_major;
+	u8 pip_ver_minor;
+	u8 bl_ver_major;
+	u8 bl_ver_minor;
+	u8 fw_ver_major;
+	u8 fw_ver_minor;
+	u16 fw_pid;
+	u16 fw_ver_conf;
+	u16 post_code;
+	u32 revctrl;
+	u16 jtag_id_l;
+	u16 jtag_id_h;
+	u8 mfg_id[PT_NUM_MFGID];
+	u16 chip_rev;
+	u16 chip_id;
+	u8 uid[PT_UID_SIZE];
+};
+
+struct pt_sensing_conf_data {
+	u16 res_x;
+	u16 res_y;
+	u16 max_z;
+	u16 len_x;
+	u16 len_y;
+	u8 electrodes_x;
+	u8 electrodes_y;
+	u8 origin_x;
+	u8 origin_y;
+	u8 panel_id;
+	u8 btn;
+	u8 scan_mode;
+	u8 max_tch;
+	u8 rx_num;
+	u8 tx_num;
+};
+
+enum pt_tch_abs {	/* for ordering within the extracted touch data array */
+	PT_TCH_X,	/* X */
+	PT_TCH_Y,	/* Y */
+	PT_TCH_P,	/* P (Z) */
+	PT_TCH_T,	/* TOUCH ID */
+	PT_TCH_E,	/* EVENT ID */
+	PT_TCH_O,	/* OBJECT ID */
+	PT_TCH_TIP,	/* OBJECT ID */
+	PT_TCH_MAJ,	/* TOUCH_MAJOR */
+	PT_TCH_MIN,	/* TOUCH_MINOR */
+	PT_TCH_OR,	/* ORIENTATION */
+	PT_TCH_NUM_ABS,
+};
+
+enum pt_tch_hdr {
+	PT_TCH_TIME,	/* SCAN TIME */
+	PT_TCH_NUM,	/* NUMBER OF RECORDS */
+	PT_TCH_LO,	/* LARGE OBJECT */
+	PT_TCH_NOISE,	/* NOISE EFFECT */
+	PT_TCH_COUNTER,	/* REPORT_COUNTER */
+	PT_TCH_NUM_HDR,
+};
+
+static const char * const pt_tch_abs_string[] = {
+	[PT_TCH_X]	= "X",
+	[PT_TCH_Y]	= "Y",
+	[PT_TCH_P]	= "P",
+	[PT_TCH_T]	= "T",
+	[PT_TCH_E]	= "E",
+	[PT_TCH_O]	= "O",
+	[PT_TCH_TIP]	= "TIP",
+	[PT_TCH_MAJ]	= "MAJ",
+	[PT_TCH_MIN]	= "MIN",
+	[PT_TCH_OR]	= "OR",
+	[PT_TCH_NUM_ABS] = "INVALID",
+};
+
+static const char * const pt_tch_hdr_string[] = {
+	[PT_TCH_TIME]	= "SCAN TIME",
+	[PT_TCH_NUM]	= "NUMBER OF RECORDS",
+	[PT_TCH_LO]	= "LARGE OBJECT",
+	[PT_TCH_NOISE]	= "NOISE EFFECT",
+	[PT_TCH_COUNTER] = "REPORT_COUNTER",
+	[PT_TCH_NUM_HDR] = "INVALID",
+};
+
+static const int pt_tch_abs_field_map[] = {
+	[PT_TCH_X]	= 0x00010030 /* HID_GD_X */,
+	[PT_TCH_Y]	= 0x00010031 /* HID_GD_Y */,
+	[PT_TCH_P]	= HID_DI_PRESSURE,
+	[PT_TCH_T]	= HID_DI_CONTACTID,
+	[PT_TCH_E]	= HID_PT_EVENTID,
+	[PT_TCH_O]	= HID_PT_TOUCHTYPE,
+	[PT_TCH_TIP]	= HID_DI_TIP,
+	[PT_TCH_MAJ]	= HID_PT_MAJORAXISLENGTH,
+	[PT_TCH_MIN]	= HID_PT_MINORAXISLENGTH,
+	[PT_TCH_OR]	= HID_PT_ORIENTATION,
+	[PT_TCH_NUM_ABS] = 0,
+};
+
+static const int pt_tch_hdr_field_map[] = {
+	[PT_TCH_TIME]	= HID_DI_SCANTIME,
+	[PT_TCH_NUM]	= HID_DI_CONTACTCOUNT,
+	[PT_TCH_LO]	= HID_PT_LARGEOBJECT,
+	[PT_TCH_NOISE]	= HID_PT_NOISEEFFECTS,
+	[PT_TCH_COUNTER] = HID_PT_REPORTCOUNTER,
+	[PT_TCH_NUM_HDR] = 0,
+};
+
+#define PT_TOUCH_ID_MAX         32
+#define PT_NUM_EXT_TCH_FIELDS   3
+
+struct pt_tch_abs_params {
+	size_t ofs;	/* abs byte offset */
+	size_t size;	/* size in bits */
+	size_t min;	/* min value */
+	size_t max;	/* max value */
+	size_t bofs;	/* bit offset */
+	u8 report;
+};
+
+struct pt_touch {
+	int hdr[PT_TCH_NUM_HDR];
+	int abs[PT_TCH_NUM_ABS];
+};
+
+/* button to keycode support */
+#define PT_BITS_PER_BTN		1
+#define PT_NUM_BTN_EVENT_ID	((1 << PT_BITS_PER_BTN) - 1)
+
+enum pt_btn_state {
+	PT_BTN_RELEASED = 0,
+	PT_BTN_PRESSED = 1,
+	PT_BTN_NUM_STATE
+};
+
+struct pt_btn {
+	bool enabled;
+	int state;	/* PT_BTN_PRESSED, PT_BTN_RELEASED */
+	int key_code;
+};
+
+enum pt_ic_ebid {
+	PT_TCH_PARM_EBID  = 0x00,
+	PT_MDATA_EBID     = 0x01,
+	PT_DDATA_EBID     = 0x02,
+	PT_CAL_EBID       = 0xF0,
+};
+
+/* ttconfig block */
+#define PT_TTCONFIG_VERSION_OFFSET	8
+#define PT_TTCONFIG_VERSION_SIZE	2
+#define PT_TTCONFIG_VERSION_ROW		0
+
+struct pt_ttconfig {
+	u16 version;
+	u16 crc;
+};
+
+struct pt_report_desc_data {
+	u16 tch_report_id;
+	u16 tch_record_size;
+	u16 tch_header_size;
+	u16 btn_report_id;
+};
+
+struct pt_sysinfo {
+	bool ready;
+	struct pt_ttdata ttdata;
+	struct pt_sensing_conf_data sensing_conf_data;
+	struct pt_report_desc_data desc;
+	int num_btns;
+	struct pt_btn *btn;
+	struct pt_ttconfig ttconfig;
+	struct pt_tch_abs_params tch_hdr[PT_TCH_NUM_HDR];
+	struct pt_tch_abs_params tch_abs[PT_TCH_NUM_ABS];
+	u8 *xy_mode;
+	u8 *xy_data;
+};
+
+struct pt_bl_info {
+	bool ready;
+	u16 chip_id;
+};
+
+enum pt_atten_type {
+	PT_ATTEN_IRQ,
+	PT_ATTEN_STARTUP,
+	PT_ATTEN_EXCLUSIVE,
+	PT_ATTEN_WAKE,
+	PT_ATTEN_LOADER,
+	PT_ATTEN_SUSPEND,
+	PT_ATTEN_RESUME,
+	PT_ATTEN_CANCEL_LOADER,
+	PT_ATTEN_NUM_ATTEN,
+};
+
+enum pt_sleep_state {
+	SS_SLEEP_NONE,
+	SS_SLEEP_OFF,
+	SS_SLEEP_ON,
+	SS_SLEEPING,
+	SS_WAKING,
+	SS_EASY_WAKING_ON,
+	SS_EASY_WAKING_OFF,
+};
+
+enum pt_fb_state {
+	FB_NONE,
+	FB_ON,
+	FB_OFF,
+};
+
+enum pt_startup_state {
+	STARTUP_NONE,
+	STARTUP_QUEUED,
+	STARTUP_RUNNING,
+	STARTUP_ILLEGAL,
+};
+
+struct pt_hid_desc {
+	__le16 hid_desc_len;
+	u8 packet_id;
+	u8 reserved_byte;
+	__le16 bcd_version;
+	__le16 report_desc_len;
+	__le16 report_desc_register;
+	__le16 input_register;
+	__le16 max_input_len;
+	__le16 output_register;
+	__le16 max_output_len;
+	__le16 command_register;
+	__le16 data_register;
+	__le16 vendor_id;
+	__le16 product_id;
+	__le16 version_id;
+	u8 reserved[4];
+} __packed;
+
+struct pt_hid_core {
+	u16 hid_vendor_id;
+	u16 hid_product_id;
+	__le16 hid_desc_register;
+	u16 hid_report_desc_len;
+	u16 hid_max_input_len;
+	u16 hid_max_output_len;
+};
+
+#define PT_HID_MAX_REPORTS		8
+#define PT_HID_MAX_FIELDS		128
+#define PT_HID_MAX_COLLECTIONS		3
+#define PT_HID_MAX_NESTED_COLLECTIONS	PT_HID_MAX_COLLECTIONS
+
+/* Max input is for ASCII representation of hex characters */
+#define PT_MAX_INPUT            (PT_MAX_PIP2_MSG_SIZE * 2)
+#define PT_PIP_1P7_EMPTY_BUF    0xFF00
+
+enum pt_module_id {
+	PT_MODULE_MT,
+	PT_MODULE_BTN,
+	PT_MODULE_PROX,
+	PT_MODULE_LAST,
+};
+
+struct pt_mt_data;
+struct pt_mt_function {
+	int (*mt_release)(struct device *dev);
+	int (*mt_probe)(struct device *dev, struct pt_mt_data *md);
+	void (*report_slot_liftoff)(struct pt_mt_data *md, int max_slots);
+	void (*input_sync)(struct input_dev *input);
+	void (*input_report)(struct input_dev *input, int sig, int t, int type);
+	void (*final_sync)(struct input_dev *input, int max_slots,
+			int mt_sync_count, unsigned long *ids);
+	int (*input_register_device)(struct input_dev *input, int max_slots);
+};
+
+struct pt_mt_data {
+	struct device *dev;
+	struct pt_mt_platform_data *pdata;
+	struct pt_sysinfo *si;
+	struct input_dev *input;
+	struct pt_mt_function mt_function;
+	struct mutex mt_lock;
+	bool is_suspended;
+	bool input_device_registered;
+	bool input_device_allocated;
+	char phys[NAME_MAX];
+	int num_prv_rec;
+	int or_min;
+	int or_max;
+	int t_min;
+	int t_max;
+};
+
+struct pt_btn_data {
+	struct device *dev;
+	struct pt_btn_platform_data *pdata;
+	struct pt_sysinfo *si;
+	struct input_dev *input;
+	struct mutex btn_lock;
+	bool is_suspended;
+	bool input_device_registered;
+	bool input_device_allocated;
+	char phys[NAME_MAX];
+};
+
+struct pt_proximity_data {
+	struct device *dev;
+	struct pt_proximity_platform_data *pdata;
+	struct pt_sysinfo *si;
+	struct input_dev *input;
+	struct mutex prox_lock;
+	struct mutex sysfs_lock;
+	int enable_count;
+	bool input_device_registered;
+	bool input_device_allocated;
+	char phys[NAME_MAX];
+};
+
+enum pt_calibrate_idacs_sensing_mode {
+	PT_CI_SM_MUTCAP_FINE,
+	PT_CI_SM_MUTCAP_BUTTON,
+	PT_CI_SM_SELFCAP,
+};
+
+enum pt_initialize_baselines_sensing_mode {
+	PT_IB_SM_MUTCAP = 1,
+	PT_IB_SM_BUTTON = 2,
+	PT_IB_SM_SELFCAP = 4,
+	PT_IB_SM_BALANCED = 8,
+};
+
+/* parameters for extended calibrate command(0x30)*/
+struct pt_cal_ext_data {
+	u8 mode;
+	u8 data0;
+	u8 data1;
+	u8 data2;
+} __packed;
+#define PT_CAL_EXT_MODE_UNDEFINED  0xFF
+
+#define PT_BIN_FILE_MIN_HDR_LENGTH 14
+#define PT_BIN_FILE_MAX_HDR_LENGTH 18
+struct pt_bin_file_hdr {
+	u8 length;
+	u16 ttpid;
+	u8  fw_major;
+	u8  fw_minor;
+	u32 fw_rev_ctrl;
+	u32 fw_crc;
+	u16 si_rev;
+	u16 si_id;
+	u16 config_ver;
+	u32 hex_file_size;
+};
+
+struct pt_core_nonhid_cmd {
+	int (*start_bl)(struct device *dev, int protect);
+	int (*suspend_scanning)(struct device *dev, int protect);
+	int (*resume_scanning)(struct device *dev, int protect);
+	int (*get_param)(struct device *dev, int protect, u8 param_id,
+			u32 *value);
+	int (*set_param)(struct device *dev, int protect, u8 param_id,
+			u32 value, u8 size);
+	int (*verify_cfg_block_crc)(struct device *dev, int protect,
+			u8 ebid, u8 *status, u16 *calculated_crc,
+			u16 *stored_crc);
+	int (*get_config_row_size)(struct device *dev, int protect,
+			u16 *row_size);
+	int (*get_data_structure)(struct device *dev, int protect,
+			u16 read_offset, u16 read_length, u8 data_id,
+			u8 *status, u8 *data_format, u16 *actual_read_len,
+			u8 *data);
+	int (*run_selftest)(struct device *dev, int protect, u8 test_id,
+		u8 write_idacs_to_flash, u8 *status, u8 *summary_result,
+		u8 *results_available);
+	int (*get_selftest_result)(struct device *dev, int protect,
+		u16 read_offset, u16 read_length, u8 test_id, u8 *status,
+		u16 *actual_read_len, u8 *data);
+	int (*load_self_test_param)(struct device *dev, int protect,
+		u8 self_test_id, u16 load_offset, u16 load_length,
+		u8 *parameters, u8 *status, u8 *ret_test_id, u16 *act_load_len);
+	int (*calibrate_idacs)(struct device *dev, int protect, u8 mode,
+			u8 *status);
+	int (*calibrate_ext)(struct device *dev,
+		int protect, struct pt_cal_ext_data *cal_data, u8 *status);
+	int (*initialize_baselines)(struct device *dev, int protect,
+			u8 test_id, u8 *status);
+	int (*exec_panel_scan)(struct device *dev, int protect, u8 scan_type);
+	int (*retrieve_panel_scan)(struct device *dev, int protect,
+			u16 read_offset, u16 read_count, u8 data_id,
+			u8 *response, u8 *config, u16 *actual_read_len,
+			u8 *read_buf);
+	int (*read_data_block)(struct device *dev, u16 row_number,
+			u16 length, u8 ebid, u16 *actual_read_len,
+			u8 *read_buf, u16 read_buf_size, u16 *crc);
+	int (*write_data_block)(struct device *dev, u16 row_number,
+			u16 write_length, u8 ebid, u8 *write_buf,
+			u8 *security_key, u16 *actual_write_len);
+	int (*user_cmd)(struct device *dev, int protect, u16 read_len,
+			u8 *read_buf, u16 write_len, u8 *write_buf,
+			u16 *actual_read_len);
+	int (*get_bl_info)(struct device *dev, int protect, u8 *return_data);
+	int (*initiate_bl)(struct device *dev, int protect, u16 key_size,
+			u8 *key_buf, u16 row_size, u8 *metadata_row_buf);
+	int (*launch_app)(struct device *dev, int protect);
+	int (*prog_and_verify)(struct device *dev, int protect, u16 data_len,
+			u8 *data_buf);
+	int (*verify_app_integrity)(struct device *dev, int protect,
+			u8 *result);
+	int (*get_panel_id)(struct device *dev, int protect, u8 *panel_id);
+	int (*pip2_send_cmd)(struct device *dev, int protect,
+			u8 id, u8 *data, u16 report_body_len, u8 *read_buf,
+			u16 *actual_read_len);
+	int (*pip2_send_cmd_no_int)(struct device *dev, int protect,
+			u8 id, u8 *data, u16 report_body_len, u8 *read_buf,
+			u16 *actual_read_len);
+	int (*get_bl_pip2_version)(struct device *dev);
+	int (*pip2_file_open)(struct device *dev, u8 file_no);
+	int (*pip2_file_close)(struct device *dev, u8 file_no);
+	int (*pip2_file_erase)(struct device *dev, u8 file_no, int *status);
+	int (*read_us_file)(struct device *dev, u8 *file_path, u8 *buf,
+			int *size);
+	int (*pip2_file_read)(struct device *dev, u8 file_no,
+			u16 num_bytes, u8 *read_buf);
+	int (*pip2_file_seek_offset)(struct device *dev, u8 file_no,
+			u32 read_offset, u32 write_offset);
+	int (*pip2_file_get_stats)(struct device *dev, u8 file_no,
+			u32 *address, u32 *file_size);
+	int (*pip2_file_crc)(struct device *dev, u8 file_no,
+			u32 offset, u32 length, u8 *read_buf);
+	int (*manage_cal_data)(struct device *dev, u8 action, u16 *size,
+			unsigned short *crc);
+	unsigned short (*calc_crc)(unsigned char *q, int len);
+};
+
+typedef int (*pt_atten_func) (struct device *);
+
+struct pt_core_commands {
+	int (*subscribe_attention)(struct device *dev,
+			enum pt_atten_type type, char *id,
+			pt_atten_func func, int flags);
+	int (*unsubscribe_attention)(struct device *dev,
+			enum pt_atten_type type, char *id,
+			pt_atten_func func, int flags);
+	int (*request_exclusive)(struct device *dev, int timeout_ms);
+	int (*release_exclusive)(struct device *dev);
+	int (*request_reset)(struct device *dev, int protect);
+	int (*request_pip2_launch_app)(struct device *dev, int protect);
+	int (*request_enum)(struct device *dev, bool wait);
+	struct pt_sysinfo * (*request_sysinfo)(struct device *dev);
+	struct pt_loader_platform_data
+		*(*request_loader_pdata)(struct device *dev);
+	int (*request_stop_wd)(struct device *dev);
+	int (*request_start_wd)(struct device *dev);
+	int (*request_get_mode)(struct device *dev, int protect, u8 *mode);
+	int (*request_pip2_get_mode_sysmode)(struct device *dev, int protect,
+		u8 *mode, u8 *sys_mode);
+	int (*request_active_pip_prot)(struct device *dev, int protect,
+		u8 *pip_version_major, u8 *pip_version_minor);
+	int (*request_enable_scan_type)(struct device *dev, u8 scan_type);
+	int (*request_disable_scan_type)(struct device *dev, u8 scan_type);
+	int (*request_pip2_enter_bl)(struct device *dev, u8 *start_mode,
+		int *result);
+	int (*request_pip2_bin_hdr)(struct device *dev,
+		struct pt_bin_file_hdr *hdr);
+	int (*request_dut_generation)(struct device *dev);
+	int (*request_hw_version)(struct device *dev, char *hw_version);
+	int (*parse_sysfs_input)(struct device *dev,
+		const char *buf, size_t buf_size,
+		u32 *out_buf, size_t out_buf_size);
+#ifdef TTHE_TUNER_SUPPORT
+	int (*request_tthe_print)(struct device *dev, u8 *buf, int buf_len,
+			const u8 *data_name);
+#endif
+#ifdef TTDL_DIAGNOSTICS
+	void (*request_toggle_err_gpio)(struct device *dev, u8 type);
+#endif
+	struct pt_core_nonhid_cmd *nonhid_cmd;
+	int (*request_get_fw_mode)(struct device *dev, int protect,
+		u8 *sys_mode, u8 *mode);
+};
+
+enum core_command_protected_status {
+	PT_CORE_CMD_UNPROTECTED = 0,
+	PT_CORE_CMD_PROTECTED = 1
+};
+
+enum pt_err_gpio_type {
+	PT_ERR_GPIO_NONE             = 0,
+	PT_ERR_GPIO_I2C_TRANS        = 1,
+	PT_ERR_GPIO_IRQ_STUCK        = 2,
+	PT_ERR_GPIO_EXCLUSIVE_ACCESS = 3,
+	PT_ERR_GPIO_EMPTY_PACKET     = 4,
+	PT_ERR_GPIO_BL_RETRY_PACKET  = 5,
+	PT_ERR_GPIO_MAX_TYPE         = PT_ERR_GPIO_BL_RETRY_PACKET,
+};
+
+struct pt_features {
+	uint8_t easywake;
+	uint8_t noise_metric;
+	uint8_t tracking_heatmap;
+	uint8_t sensor_data;
+};
+
+#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_PM_RUNTIME)
+#if (KERNEL_VERSION(3, 3, 0) > LINUX_VERSION_CODE)
+#define NEED_SUSPEND_NOTIFIER
+#endif /* CONFIG_PM_SLEEP && CONFIG_PM_RUNTIME */
+#endif /* LINUX_VERSION_CODE */
+
+struct pt_module {
+	struct list_head node;
+	char *name;
+	int (*probe)(struct device *dev, void **data);
+	void (*release)(struct device *dev, void *data);
+};
+
+struct pt_bus_ops {
+	u16 bustype;
+	int (*read_default)(struct device *dev, void *buf, int size);
+	int (*read_default_nosize)(struct device *dev, u8 *buf, u32 max);
+	int (*write_read_specific)(struct device *dev, u16 write_len,
+			u8 *write_buf, u8 *read_buf);
+};
+
+struct pt_core_data {
+	struct pinctrl *ts_pinctrl;
+	struct pinctrl_state *pinctrl_state_active;
+	struct pinctrl_state *pinctrl_state_suspend;
+	struct pinctrl_state *pinctrl_state_release;
+
+	struct regulator *vdd;
+	struct regulator *vcc_i2c;
+
+	struct list_head node;
+	struct list_head module_list; /* List of probed modules */
+	char core_id[20];
+	struct device *dev;
+	struct workqueue_struct *pt_workqueue;
+	struct work_struct	resume_offload_work;
+	struct work_struct	suspend_offload_work;
+	struct work_struct	suspend_work;
+	struct work_struct	resume_work;
+
+	struct list_head atten_list[PT_ATTEN_NUM_ATTEN];
+	struct list_head param_list;
+	struct mutex module_list_lock;
+	struct mutex system_lock;
+	struct mutex sysfs_lock;
+	struct mutex ttdl_restart_lock;
+	struct mutex firmware_class_lock;
+	enum pt_mode mode;
+	spinlock_t spinlock;
+	struct pt_mt_data md;
+	struct pt_btn_data bd;
+	struct pt_proximity_data pd;
+	int phys_num;
+	int pip_cmd_timeout;
+	int pip_cmd_timeout_default;
+	void *pt_dynamic_data[PT_MODULE_LAST];
+	struct pt_platform_data *pdata;
+	struct pt_core_platform_data *cpdata;
+	const struct pt_bus_ops *bus_ops;
+	wait_queue_head_t wait_q;
+	enum pt_sleep_state sleep_state;
+	enum pt_startup_state startup_state;
+	int irq;
+	bool irq_enabled;
+	bool irq_wake;
+	bool irq_disabled;
+	bool hw_detected;
+	u8 easy_wakeup_gesture;
+#ifdef EASYWAKE_TSG6
+	u8 gesture_id;
+	u8 gesture_data_length;
+	u8 gesture_data[80];
+#endif
+	bool wait_until_wake;
+	u8 pid_for_loader;
+	char hw_version[13];
+#ifdef NEED_SUSPEND_NOTIFIER
+	/*
+	 * This notifier is used to receive suspend prepare events
+	 * When device is PM runtime suspended, pm_generic_suspend()
+	 * does not call our PM suspend callback for kernels with
+	 * version less than 3.3.0.
+	 */
+	struct notifier_block pm_notifier;
+#endif
+	struct pt_sysinfo sysinfo;
+	struct pt_bl_info bl_info;
+	void *exclusive_dev;
+	int exclusive_waits;
+	struct timer_list watchdog_timer;
+	struct work_struct watchdog_work;
+	struct work_struct enum_work;
+	struct work_struct ttdl_restart_work;
+	u16 startup_retry_count;
+	struct pt_hid_core hid_core;
+	int hid_cmd_state;
+	int hid_reset_cmd_state; /* reset can happen any time */
+	struct pt_hid_desc hid_desc;
+	struct pt_features features;
+#define PT_PREALLOCATED_CMD_BUFFER 32
+	u8 cmd_buf[PT_PREALLOCATED_CMD_BUFFER];
+	u8 input_buf[PT_MAX_INPUT];
+	u8 response_buf[PT_MAX_INPUT];
+	u8 cmd_rsp_buf[PT_MAX_INPUT];
+	u16 cmd_rsp_buf_len;
+	int raw_cmd_status;
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	struct early_suspend es;
+#elif defined(CONFIG_PANEL_NOTIFIER)
+	struct panel_event_notifier_entry *entry;
+	enum pt_fb_state fb_state;
+#elif defined(CONFIG_FB) || defined(CONFIG_DRM) || defined(CONFIG_PANEL_NOTIFIER)
+	struct notifier_block fb_notifier;
+	enum pt_fb_state fb_state;
+#endif
+#ifdef TTHE_TUNER_SUPPORT
+	struct dentry *tthe_debugfs;
+	u8 *tthe_buf;
+	u32 tthe_buf_len;
+	u32 tthe_buf_size;
+	struct mutex tthe_lock;
+	u8 tthe_exit;
+#endif
+	u8 debug_level;
+	u8 watchdog_enabled;
+	bool watchdog_force_stop;
+	u32 watchdog_interval;
+	u8 show_timestamp;
+	u32 startup_status;
+	u8 pip2_cmd_tag_seq;
+	u8 pip2_prot_active;
+	u8 pip2_send_user_cmd;
+	u8 get_param_id;
+	bool bl_pip_ver_ready;
+	bool app_pip_ver_ready;
+	u8 core_probe_complete;
+	u8 active_dut_generation;
+	bool set_dut_generation;
+	u8 fw_system_mode;
+	u8 flashless_dut;
+	u8 bl_with_no_int;
+	u8 cal_cache_in_host;
+	u8 multi_chip;
+	u8 tthe_hid_usb_format;
+	u8 flashless_auto_bl;
+	u8 pip2_us_file_path[PT_MAX_PATH_SIZE];
+	bool fw_updating;
+	bool fw_sys_mode_in_standby_state;
+#ifdef TTDL_PTVIRTDUT_SUPPORT
+	u8 route_bus_virt_dut;
+#endif /* TTDL_PTVIRTDUT_SUPPORT */
+	u8 panel_id_support;
+#ifdef TTDL_DIAGNOSTICS
+	u8 t_refresh_active;
+	u8 flush_bus_type;
+	u8 ttdl_bist_select;
+	u8 force_pip2_seq;
+	u16 ping_test_size;
+	u16 pip2_crc_error_count;
+	u16 t_refresh_count;
+	u16 t_refresh_total;
+	u16 wd_xres_count;
+	u32 watchdog_count;
+	u32 watchdog_irq_stuck_count;
+	u32 watchdog_failed_access_count;
+	u32 bus_transmit_error_count;
+	u32 irq_count;
+	u32 bl_retry_packet_count;
+	u32 file_erase_timeout_count;
+	unsigned long t_refresh_time;
+	u16 err_gpio;
+	u16 err_gpio_type;
+	bool show_tt_data;
+	bool bridge_mode;
+	bool hw_detect_enabled;
+#endif
+	bool quick_boot;
+	bool drv_debug_suspend;
+	bool touch_offload;
+};
+
+struct gd_sensor {
+	int32_t cm_min;
+	int32_t cm_max;
+	int32_t cm_ave;
+	int32_t cm_min_exclude_edge;
+	int32_t cm_max_exclude_edge;
+	int32_t cm_ave_exclude_edge;
+	int32_t gradient_val;
+};
+
+#ifdef TTHE_TUNER_SUPPORT
+#define PT_CMD_RET_PANEL_IN_DATA_OFFSET	0
+#define PT_CMD_RET_PANEL_ELMNT_SZ_MASK	0x07
+#define PT_CMD_RET_PANEL_HDR		0x0A
+#define PT_CMD_RET_PANEL_ELMNT_SZ_MAX	0x2
+
+enum scan_data_type_list {
+	PT_MUT_RAW,
+	PT_MUT_BASE,
+	PT_MUT_DIFF,
+	PT_SELF_RAW,
+	PT_SELF_BASE,
+	PT_SELF_DIFF,
+	PT_BAL_RAW,
+	PT_BAL_BASE,
+	PT_BAL_DIFF,
+};
+#endif
+
+static inline int pt_adap_read_default(struct pt_core_data *cd,
+		void *buf, int size)
+{
+	return cd->bus_ops->read_default(cd->dev, buf, size);
+}
+
+static inline int pt_adap_read_default_nosize(struct pt_core_data *cd,
+		void *buf, int max)
+{
+	return cd->bus_ops->read_default_nosize(cd->dev, buf, max);
+}
+
+static inline int pt_adap_write_read_specific(struct pt_core_data *cd,
+		u16 write_len, u8 *write_buf, u8 *read_buf)
+{
+	return cd->bus_ops->write_read_specific(cd->dev, write_len, write_buf,
+			read_buf);
+}
+
+static inline void *pt_get_dynamic_data(struct device *dev, int id)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+
+	return cd->pt_dynamic_data[id];
+}
+
+int request_exclusive(struct pt_core_data *cd, void *ownptr,
+		int timeout_ms);
+int release_exclusive(struct pt_core_data *cd, void *ownptr);
+int _pt_request_pip_get_param(struct device *dev,
+		int protect, u8 param_id, u32 *value);
+int _pt_request_pip_set_param(struct device *dev,
+		int protect, u8 param_id, u32 value, u8 size);
+
+static inline int pt_request_exclusive(struct device *dev, int timeout_ms)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+
+	return request_exclusive(cd, dev, timeout_ms);
+}
+
+static inline int pt_release_exclusive(struct device *dev)
+{
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+
+	return release_exclusive(cd, dev);
+}
+
+static inline int pt_request_nonhid_get_param(struct device *dev,
+		int protect, u8 param_id, u32 *value)
+{
+	return _pt_request_pip_get_param(dev, protect, param_id,
+			value);
+}
+
+static inline int pt_request_nonhid_set_param(struct device *dev,
+		int protect, u8 param_id, u32 value, u8 size)
+{
+	return _pt_request_pip_set_param(dev, protect, param_id,
+			value, size);
+}
+
+void pt_pr_buf(struct device *dev, u8 debug_level, u8 *buf,
+	u16 buf_len, const char *data_name);
+
+#ifdef CONFIG_TOUCHSCREEN_PARADE_DEVICETREE_SUPPORT
+int pt_devtree_create_and_get_pdata(struct device *adap_dev);
+int pt_devtree_clean_pdata(struct device *adap_dev);
+#else
+static inline int pt_devtree_create_and_get_pdata(struct device *adap_dev)
+{
+	return 0;
+}
+
+static inline int pt_devtree_clean_pdata(struct device *adap_dev)
+{
+	return 0;
+}
+#endif
+
+int pt_probe(const struct pt_bus_ops *ops, struct device *dev,
+		u16 irq, size_t xfer_buf_size);
+int pt_release(struct pt_core_data *cd);
+
+struct pt_core_commands *pt_get_commands(void);
+struct pt_core_data *pt_get_core_data(char *id);
+
+
+int pt_mt_release(struct device *dev);
+int pt_mt_probe(struct device *dev);
+
+#ifdef CONFIG_TOUCHSCREEN_PARADE_BUTTON
+int pt_btn_probe(struct device *dev);
+int pt_btn_release(struct device *dev);
+#else
+static inline int pt_btn_probe(struct device *dev) { return 0; }
+static inline int pt_btn_release(struct device *dev) { return 0; }
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_PARADE_PROXIMITY
+int pt_proximity_probe(struct device *dev);
+int pt_proximity_release(struct device *dev);
+#else
+static inline int pt_proximity_probe(struct device *dev) { return 0; }
+static inline int pt_proximity_release(struct device *dev) { return 0; }
+#endif
+
+static inline unsigned int pt_get_time_stamp(void)
+{
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0))
+	struct timespec64 ts;
+
+	ktime_get_real_ts64(&ts);
+	return (ts.tv_sec*1000 + ts.tv_nsec/1000000);
+#else
+	struct timeval tv;
+
+	do_gettimeofday(&tv);
+	return (tv.tv_sec*1000 + tv.tv_usec/1000);
+#endif
+}
+
+void pt_init_function_ptrs(struct pt_mt_data *md);
+int _pt_subscribe_attention(struct device *dev,
+	enum pt_atten_type type, char *id, int (*func)(struct device *),
+	int mode);
+int _pt_unsubscribe_attention(struct device *dev,
+	enum pt_atten_type type, char *id, int (*func)(struct device *),
+	int mode);
+struct pt_sysinfo *_pt_request_sysinfo(struct device *dev);
+
+extern const struct dev_pm_ops pt_pm_ops;
+
+int pt_register_module(struct pt_module *module);
+void pt_unregister_module(struct pt_module *module);
+
+void *pt_get_module_data(struct device *dev,
+		struct pt_module *module);
+
+#endif /* _PT_REGS_H */

+ 429 - 0
qcom/opensource/touch-drivers/pt/pt_spi.c

@@ -0,0 +1,429 @@
+/*
+ * pt_spi.c
+ * Parade TrueTouch(TM) Standard Product SPI Module.
+ * For use with Parade touchscreen controllers.
+ * Supported parts include:
+ * TMA5XX
+ * TMA448
+ * TMA445A
+ * TT21XXX
+ * TT31XXX
+ * TT4XXXX
+ * TT7XXX
+ * TC3XXX
+ *
+ * Copyright (C) 2015-2020 Parade Technologies
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * Contact Parade Technologies at www.paradetech.com <[email protected]>
+ */
+
+#include "pt_regs.h"
+
+#include <linux/spi/spi.h>
+#include <linux/version.h>
+
+/* TC3315 - DUT Address (0x24 & 0x07) << 1 = 0x08 for write and 0x09 for read */
+#define PT_SPI_WR_OP		0x08 /* r/~w */
+#define PT_SPI_RD_OP		0x09
+#define PT_SPI_BITS_PER_WORD	8
+#define PT_SPI_SYNC_ACK         0x62
+
+#define PT_SPI_CMD_BYTES	0
+#define PT_SPI_DATA_SIZE	(2 * 256)
+#define PT_SPI_DATA_BUF_SIZE	(PT_SPI_CMD_BYTES + PT_SPI_DATA_SIZE)
+
+#define PT_SPI_OP_SIZE     (1)
+#define PT_SPI_DUMMY_READ  (1)
+#define PT_SPI_BUFFER_SIZE  \
+	(PT_MAX_PIP2_MSG_SIZE + PT_SPI_OP_SIZE + PT_SPI_DUMMY_READ)
+
+static u8 *tmp_rbuf;
+static u8 *tmp_wbuf;
+DEFINE_MUTEX(pt_spi_bus_lock);
+
+
+/*******************************************************************************
+ * FUNCTION: pt_spi_xfer
+ *
+ * SUMMARY: Read or write date for SPI device.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *      *dev - pointer to device structure
+ *        op - flag to write or read data
+ *      *buf - pointer to data buffer
+ *    length - data length
+ ******************************************************************************/
+static int pt_spi_xfer(struct device *dev, u8 op, u8 *buf, int length)
+{
+	struct spi_device *spi = to_spi_device(dev);
+	struct spi_message msg;
+	struct spi_transfer xfer;
+	int rc;
+
+	memset(&xfer, 0, sizeof(xfer));
+	spi_message_init(&msg);
+
+	switch (op) {
+	case PT_SPI_RD_OP:
+		/* Clear tmp_wbuf with additional OP Code and dummy byte */
+		memset(tmp_wbuf, 0, length + 2);
+		tmp_wbuf[0] = op;
+		/* Total read/write = Read length + Op code + dummy byte */
+		xfer.tx_buf = tmp_wbuf;
+		xfer.rx_buf = tmp_rbuf;
+		xfer.len = length + 2;
+		break;
+	case PT_SPI_WR_OP:
+		memcpy(&tmp_wbuf[1], buf, length);
+		tmp_wbuf[0] = op;
+		/* Write length + size of Op code */
+		xfer.tx_buf = tmp_wbuf;
+		xfer.len = length + 1;
+		break;
+	default:
+		rc = -EIO;
+		goto exit;
+	}
+
+	spi_message_add_tail(&xfer, &msg);
+	rc = spi_sync(spi, &msg);
+
+	/* On reads copy only the data content back into the passed in buf */
+	if (op == PT_SPI_RD_OP)
+		memcpy(buf, &tmp_rbuf[2], length);
+exit:
+	if (rc < 0)
+		pt_debug(dev, DL_ERROR, "%s: spi_sync() error %d\n",
+			__func__, rc);
+
+#if 0 /* TODO TC3315 - need to verify the ACK byte */
+	if (tmp_rbuf[0] != PT_SPI_SYNC_ACK) {
+		pt_debug(dev, DL_ERROR, "%s: r_header = 0x%02X\n", __func__,
+			r_header[0]);
+		return -EIO;
+	}
+#endif
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_spi_read_default
+ *
+ * SUMMARY: Read a certain number of bytes from the SPI bus
+ *	NOTE: For TC3315 every response includes a "dummy" prefix byte that
+ *		needs to be stipped off before returning buf.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *      *dev  - pointer to Device structure
+ *      *buf  - pointer to buffer where the data read will be stored
+ *       size - size to be read
+ ******************************************************************************/
+static int pt_spi_read_default(struct device *dev, void *buf, int size)
+{
+	int rc = 0;
+
+	if (!buf || !size || size > PT_MAX_PIP2_MSG_SIZE)
+		return -EINVAL;
+
+	mutex_lock(&pt_spi_bus_lock);
+	rc = pt_spi_xfer(dev, PT_SPI_RD_OP, buf, size);
+	mutex_unlock(&pt_spi_bus_lock);
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_spi_read_default_nosize
+ *
+ * SUMMARY: Read from the SPI bus in two transactions first reading the HID
+ *	packet size (2 bytes) followed by reading the rest of the packet based
+ *	on the size initially read.
+ *	NOTE: The empty buffer 'size' was redefined in PIP version 1.7.
+ *	NOTE: For TC3315 every response includes a "dummy" prefix byte that
+ *		needs to be stipped off before returning buf.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *      *dev  - pointer to Device structure
+ *      *buf  - pointer to buffer where the data read will be stored
+ *       max  - max size that can be read
+ ******************************************************************************/
+static int pt_spi_read_default_nosize(struct device *dev, u8 *buf, u32 max)
+{
+	u32 size;
+	int rc = 0;
+
+	if (!buf)
+		return 0;
+
+	mutex_lock(&pt_spi_bus_lock);
+
+	/* Separate transaction to retrieve only the length to read */
+	rc = pt_spi_xfer(dev, PT_SPI_RD_OP, buf, 2);
+	if (rc < 0) {
+		pt_debug(dev, DL_ERROR, "%s: SPI transfer error rc = %d\n",
+			 __func__, rc);
+		goto exit;
+	}
+
+	size = get_unaligned_le16(&buf[0]);
+	if (!size || size == 2 || size >= PT_PIP_1P7_EMPTY_BUF)
+		goto exit;
+
+	if (size > max || size > PT_MAX_PIP2_MSG_SIZE) {
+		pt_debug(dev, DL_ERROR, "%s: Invalid size %d !\n", __func__,
+			 size);
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	rc = pt_spi_xfer(dev, PT_SPI_RD_OP, buf, size);
+	if (rc)
+		pt_debug(dev, DL_ERROR, "%s: SPI transfer error rc = %d\n",
+		 __func__, rc);
+
+exit:
+	mutex_unlock(&pt_spi_bus_lock);
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_spi_write_read_specific
+ *
+ * SUMMARY: Write the contents of write_buf to the SPI device and then read
+ *	the response using pt_spi_read_default_nosize()
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *      *dev       - pointer to Device structure
+ *       write_len - length of data buffer write_buf
+ *      *write_buf - pointer to buffer to write
+ *      *read_buf  - pointer to buffer to read response into
+ ******************************************************************************/
+static int pt_spi_write_read_specific(struct device *dev, u16 write_len,
+		u8 *write_buf, u8 *read_buf)
+{
+	int rc = 0;
+
+	/* Ensure no packet larger than what the PIP spec allows */
+	if (write_len > PT_MAX_PIP2_MSG_SIZE)
+		return -EINVAL;
+
+	if (!write_buf || !write_len) {
+		if (!write_buf)
+			pt_debug(dev, DL_ERROR,
+				"%s write_buf is NULL", __func__);
+		if (!write_len)
+			pt_debug(dev, DL_ERROR,
+				"%s write_len is NULL", __func__);
+		return -EINVAL;
+	}
+
+	mutex_lock(&pt_spi_bus_lock);
+	rc = pt_spi_xfer(dev, PT_SPI_WR_OP, write_buf, write_len);
+	if (rc < 0)
+		goto error;
+	mutex_unlock(&pt_spi_bus_lock);
+
+	if (read_buf)
+		rc = pt_spi_read_default_nosize(dev, read_buf,
+				PT_SPI_DATA_SIZE);
+	return rc;
+
+error:
+	mutex_unlock(&pt_spi_bus_lock);
+	return rc;
+}
+
+static struct pt_bus_ops pt_spi_bus_ops = {
+	.bustype = BUS_SPI,
+	.read_default = pt_spi_read_default,
+	.read_default_nosize = pt_spi_read_default_nosize,
+	.write_read_specific = pt_spi_write_read_specific,
+};
+
+#ifdef CONFIG_TOUCHSCREEN_PARADE_DEVICETREE_SUPPORT
+static const struct of_device_id pt_spi_of_match[] = {
+	{ .compatible = "parade,pt_spi_adapter", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, pt_spi_of_match);
+#endif
+
+/*******************************************************************************
+ * FUNCTION: pt_spi_probe
+ *
+ * SUMMARY: Probe functon for the SPI module
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *      *spi - pointer to spi device structure
+ ******************************************************************************/
+static int pt_spi_probe(struct spi_device *spi)
+{
+	struct device *dev = &spi->dev;
+#ifdef CONFIG_TOUCHSCREEN_PARADE_DEVICETREE_SUPPORT
+	const struct of_device_id *match;
+#endif
+	int rc;
+
+	/* Set up SPI*/
+	spi->bits_per_word = PT_SPI_BITS_PER_WORD;
+	spi->mode = SPI_MODE_0;
+	rc = spi_setup(spi);
+	if (rc < 0) {
+		pt_debug(dev, DL_ERROR, "%s: SPI setup error %d\n",
+			__func__, rc);
+		return rc;
+	}
+
+#ifdef CONFIG_TOUCHSCREEN_PARADE_DEVICETREE_SUPPORT
+	match = of_match_device(of_match_ptr(pt_spi_of_match), dev);
+	if (match) {
+		rc = pt_devtree_create_and_get_pdata(dev);
+		if (rc < 0)
+			return rc;
+	}
+#endif
+
+	/* Add 2 to the length for the 'OP Code' & 'Dummy' prefix bytes */
+	tmp_wbuf = kzalloc(PT_SPI_BUFFER_SIZE, GFP_KERNEL);
+	tmp_rbuf = kzalloc(PT_SPI_BUFFER_SIZE, GFP_KERNEL);
+	if (!tmp_wbuf || !tmp_rbuf)
+		return -ENOMEM;
+
+
+	rc = pt_probe(&pt_spi_bus_ops, &spi->dev, spi->irq,
+			  PT_SPI_DATA_BUF_SIZE);
+
+#ifdef CONFIG_TOUCHSCREEN_PARADE_DEVICETREE_SUPPORT
+	if (rc && match)
+		pt_devtree_clean_pdata(dev);
+#endif
+
+
+	return rc;
+}
+
+/*******************************************************************************
+ * FUNCTION: pt_spi_remove
+ *
+ * SUMMARY: Remove functon for the SPI module
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ *
+ * PARAMETERS:
+ *      *spi - pointer to spi device structure
+ ******************************************************************************/
+static int pt_spi_remove(struct spi_device *spi)
+{
+#ifdef CONFIG_TOUCHSCREEN_PARADE_DEVICETREE_SUPPORT
+	const struct of_device_id *match;
+#endif
+	struct device *dev = &spi->dev;
+	struct pt_core_data *cd = dev_get_drvdata(dev);
+
+	kfree(tmp_rbuf);
+	kfree(tmp_wbuf);
+
+
+	pt_release(cd);
+
+#ifdef CONFIG_TOUCHSCREEN_PARADE_DEVICETREE_SUPPORT
+	match = of_match_device(of_match_ptr(pt_spi_of_match), dev);
+	if (match)
+		pt_devtree_clean_pdata(dev);
+#endif
+
+	return 0;
+}
+
+static const struct spi_device_id pt_spi_id[] = {
+	{ PT_SPI_NAME, 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(spi, pt_spi_id);
+
+static struct spi_driver pt_spi_driver = {
+	.driver = {
+		.name = PT_SPI_NAME,
+		.bus = &spi_bus_type,
+		.owner = THIS_MODULE,
+		.pm = &pt_pm_ops,
+#ifdef CONFIG_TOUCHSCREEN_PARADE_DEVICETREE_SUPPORT
+		.of_match_table = pt_spi_of_match,
+#endif
+	},
+	.probe = pt_spi_probe,
+	.remove = (pt_spi_remove),
+	.id_table = pt_spi_id,
+};
+
+#if (KERNEL_VERSION(3, 3, 0) <= LINUX_VERSION_CODE)
+module_spi_driver(pt_spi_driver);
+#else
+/*******************************************************************************
+ * FUNCTION: pt_spi_init
+ *
+ * SUMMARY: Initialize function to register spi module to kernel.
+ *
+ * RETURN:
+ *	 0 = success
+ *	!0 = failure
+ ******************************************************************************/
+static int __init pt_spi_init(void)
+{
+	int err = spi_register_driver(&pt_spi_driver);
+
+	pr_info("%s: Parade TTDL SPI Driver (Build %s) rc=%d\n",
+		 __func__, PT_DRIVER_VERSION, err);
+	return err;
+}
+module_init(pt_spi_init);
+
+/*******************************************************************************
+ * FUNCTION: pt_spi_exit
+ *
+ * SUMMARY: Exit function to unregister spi module from kernel.
+ *
+ ******************************************************************************/
+static void __exit pt_spi_exit(void)
+{
+	spi_unregister_driver(&pt_spi_driver);
+}
+module_exit(pt_spi_exit);
+#endif
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Parade TrueTouch(R) Standard Product SPI Driver");
+MODULE_AUTHOR("Parade Technologies <[email protected]>");

+ 1814 - 0
qcom/opensource/touch-drivers/qts/qts_core.c

@@ -0,0 +1,1814 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022-2023, Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#define pr_fmt(fmt)	"%s: " fmt, __func__
+
+#include <linux/vmalloc.h>
+#include <linux/uaccess.h>
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/of_gpio.h>
+#include <linux/of_graph.h>
+#include <linux/of_device.h>
+#include <linux/sysfs.h>
+#include <linux/sort.h>
+#include <linux/atomic.h>
+#include <linux/pinctrl/qcom-pinctrl.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/component.h>
+#include <linux/sched.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/regulator/consumer.h>
+#include <linux/debugfs.h>
+#include <linux/wait.h>
+#include <linux/time.h>
+#include "qts_core.h"
+
+static struct qts_data_entries *qts_data_entries;
+
+struct drm_panel *active_panel;
+
+static void qts_trusted_touch_abort_handler(struct qts_data *qts_data, int error);
+
+static struct gh_acl_desc *qts_vm_get_acl(enum gh_vm_names vm_name)
+{
+	struct gh_acl_desc *acl_desc;
+	gh_vmid_t vmid;
+
+#if (KERNEL_VERSION(6, 1, 0) <= LINUX_VERSION_CODE)
+	ghd_rm_get_vmid(vm_name, &vmid);
+#else
+	gh_rm_get_vmid(vm_name, &vmid);
+#endif
+
+	acl_desc = kzalloc(offsetof(struct gh_acl_desc, acl_entries[1]),
+			GFP_KERNEL);
+	if (!acl_desc)
+		return ERR_PTR(ENOMEM);
+
+	acl_desc->n_acl_entries = 1;
+	acl_desc->acl_entries[0].vmid = vmid;
+	acl_desc->acl_entries[0].perms = GH_RM_ACL_R | GH_RM_ACL_W;
+
+	return acl_desc;
+}
+
+static struct gh_sgl_desc *qts_vm_get_sgl(struct trusted_touch_vm_info *vm_info)
+{
+	struct gh_sgl_desc *sgl_desc;
+	int i;
+
+	sgl_desc = kzalloc(offsetof(struct gh_sgl_desc,
+			sgl_entries[vm_info->iomem_list_size]), GFP_KERNEL);
+	if (!sgl_desc)
+		return ERR_PTR(ENOMEM);
+
+	sgl_desc->n_sgl_entries = vm_info->iomem_list_size;
+
+	for (i = 0; i < vm_info->iomem_list_size; i++) {
+		sgl_desc->sgl_entries[i].ipa_base = vm_info->iomem_bases[i];
+		sgl_desc->sgl_entries[i].size = vm_info->iomem_sizes[i];
+	}
+
+	return sgl_desc;
+}
+
+static int qts_populate_vm_info_iomem(struct qts_data *qts_data)
+{
+	int i, gpio, rc = 0;
+	int num_regs, num_sizes, num_gpios, list_size;
+	struct resource res;
+	struct device_node *np = qts_data->dev->of_node;
+	struct trusted_touch_vm_info *vm_info = qts_data->vm_info;
+
+	num_regs = of_property_count_u32_elems(np, "qts,trusted-touch-io-bases");
+	if (num_regs < 0) {
+		pr_err("Invalid number of IO regions specified\n");
+		return -EINVAL;
+	}
+
+	num_sizes = of_property_count_u32_elems(np, "qts,trusted-touch-io-sizes");
+	if (num_sizes < 0) {
+		pr_err("Invalid number of IO regions specified\n");
+		return -EINVAL;
+	}
+
+	if (num_regs != num_sizes) {
+		pr_err("IO bases and sizes array lengths mismatch\n");
+		return -EINVAL;
+	}
+
+	num_gpios = of_gpio_named_count(np, "qts,trusted-touch-vm-gpio-list");
+	if (num_gpios < 0) {
+		pr_warn("Ignoring invalid trusted gpio list: %d\n", num_gpios);
+		num_gpios = 0;
+	}
+
+	list_size = num_regs + num_gpios;
+	vm_info->iomem_list_size = list_size;
+	vm_info->iomem_bases = devm_kcalloc(qts_data->dev, list_size, sizeof(*vm_info->iomem_bases),
+			GFP_KERNEL);
+	if (!vm_info->iomem_bases)
+		return -ENOMEM;
+
+	vm_info->iomem_sizes = devm_kcalloc(qts_data->dev, list_size, sizeof(*vm_info->iomem_sizes),
+			GFP_KERNEL);
+	if (!vm_info->iomem_sizes)
+		return -ENOMEM;
+
+	for (i = 0; i < num_gpios; ++i) {
+		gpio = of_get_named_gpio(np, "qts,trusted-touch-vm-gpio-list", i);
+		if (gpio < 0 || !gpio_is_valid(gpio)) {
+			pr_err("Invalid gpio %d at position %d\n", gpio, i);
+			return gpio;
+		}
+
+		if (!msm_gpio_get_pin_address(gpio, &res)) {
+			pr_err("Failed to retrieve gpio-%d resource\n", gpio);
+			return -ENODATA;
+		}
+
+		vm_info->iomem_bases[i] = res.start;
+		vm_info->iomem_sizes[i] = resource_size(&res);
+	}
+
+	rc = of_property_read_u32_array(np, "qts,trusted-touch-io-bases",
+			&vm_info->iomem_bases[i], list_size - i);
+	if (rc) {
+		pr_err("Failed to read trusted touch io bases:%d\n", rc);
+		return rc;
+	}
+
+	rc = of_property_read_u32_array(np, "qts,trusted-touch-io-sizes",
+			&vm_info->iomem_sizes[i], list_size - i);
+	if (rc) {
+		pr_err("Failed to read trusted touch io sizes:%d\n", rc);
+		return rc;
+	}
+
+	return 0;
+}
+
+static int qts_populate_vm_info(struct qts_data *qts_data)
+{
+	int rc;
+	struct trusted_touch_vm_info *vm_info;
+	struct device_node *np = qts_data->dev->of_node;
+
+	vm_info = devm_kzalloc(qts_data->dev, sizeof(struct trusted_touch_vm_info), GFP_KERNEL);
+	if (!vm_info)
+		return -ENOMEM;
+
+	qts_data->vm_info = vm_info;
+	vm_info->vm_name = GH_TRUSTED_VM;
+	rc = of_property_read_u32(np, "qts,trusted-touch-spi-irq", &vm_info->hw_irq);
+	if (rc) {
+		pr_err("Failed to read trusted touch SPI irq:%d\n", rc);
+		return rc;
+	}
+
+	rc = qts_populate_vm_info_iomem(qts_data);
+	if (rc) {
+		pr_err("Failed to read trusted touch mmio ranges:%d\n", rc);
+		return rc;
+	}
+
+	rc = of_property_read_string(np, "qts,trusted-touch-type",
+						&vm_info->trusted_touch_type);
+	if (rc) {
+		pr_warn("No trusted touch type selection mode\n");
+		vm_info->mem_tag = GH_MEM_NOTIFIER_TAG_TOUCH_PRIMARY;
+		vm_info->irq_label = GH_IRQ_LABEL_TRUSTED_TOUCH_PRIMARY;
+		rc = 0;
+	} else if (!strcmp(vm_info->trusted_touch_type, "primary")) {
+		vm_info->mem_tag = GH_MEM_NOTIFIER_TAG_TOUCH_PRIMARY;
+		vm_info->irq_label = GH_IRQ_LABEL_TRUSTED_TOUCH_PRIMARY;
+	} else if (!strcmp(vm_info->trusted_touch_type, "secondary")) {
+		vm_info->mem_tag = GH_MEM_NOTIFIER_TAG_TOUCH_SECONDARY;
+		vm_info->irq_label = GH_IRQ_LABEL_TRUSTED_TOUCH_SECONDARY;
+	}
+
+	return 0;
+}
+
+static void qts_destroy_vm_info(struct qts_data *qts_data)
+{
+	kfree(qts_data->vm_info->iomem_sizes);
+	kfree(qts_data->vm_info->iomem_bases);
+	kfree(qts_data->vm_info);
+}
+
+static void qts_vm_deinit(struct qts_data *qts_data)
+{
+	if (qts_data->vm_info->mem_cookie)
+		gh_mem_notifier_unregister(qts_data->vm_info->mem_cookie);
+	qts_destroy_vm_info(qts_data);
+}
+
+static int qts_trusted_touch_get_vm_state(struct qts_data *qts_data)
+{
+	return atomic_read(&qts_data->vm_info->vm_state);
+}
+
+static void qts_trusted_touch_set_vm_state(struct qts_data *qts_data,
+							int state)
+{
+	pr_debug("state %d\n", state);
+	atomic_set(&qts_data->vm_info->vm_state, state);
+}
+
+#ifdef CONFIG_ARCH_QTI_VM
+static int qts_vm_mem_release(struct qts_data *qts_data);
+static void qts_trusted_touch_tvm_vm_mode_disable(struct qts_data *qts_data);
+static void qts_trusted_touch_abort_tvm(struct qts_data *qts_data);
+static void qts_trusted_touch_event_notify(struct qts_data *qts_data, int event);
+
+static void qts_irq_enable(struct qts_data *qts_data, bool en)
+{
+	if (en) {
+		if (qts_data->irq_disabled) {
+			pr_debug("qts irq enable\n");
+			enable_irq(qts_data->irq);
+			qts_data->irq_disabled = false;
+		}
+	} else {
+		if (!qts_data->irq_disabled) {
+			pr_debug("qts irq disable\n");
+			disable_irq_nosync(qts_data->irq);
+			qts_data->irq_disabled = true;
+		}
+	}
+}
+
+static irqreturn_t  qts_irq_handler(int irq, void *data)
+{
+	struct qts_data *qts_data = data;
+
+	if (!mutex_trylock(&qts_data->transition_lock))
+		return IRQ_HANDLED;
+
+	qts_data->vendor_ops.irq_handler(irq, qts_data->vendor_data);
+	mutex_unlock(&qts_data->transition_lock);
+	return IRQ_HANDLED;
+}
+
+static int qts_irq_registration(struct qts_data *qts_data)
+{
+	int ret = 0;
+
+	qts_data->irq_gpio_flags = IRQF_TRIGGER_RISING;
+	pr_debug("irq:%d, flag:%x\n", qts_data->irq, qts_data->irq_gpio_flags);
+	ret = request_threaded_irq(qts_data->irq, NULL, qts_irq_handler,
+				qts_data->irq_gpio_flags | IRQF_ONESHOT,
+				QTS_NAME, qts_data);
+	if (ret != 0)
+		pr_err("request_threaded_irq failed\n");
+	if (ret == 0)
+		qts_irq_enable(qts_data, false);
+
+	return ret;
+}
+
+void qts_trusted_touch_tvm_i2c_failure_report(struct qts_data *qts_data)
+{
+	pr_warn("initiating trusted touch abort due to i2c failure\n");
+	qts_trusted_touch_abort_handler(qts_data, TRUSTED_TOUCH_EVENT_I2C_FAILURE);
+}
+
+static void qts_trusted_touch_reset_gpio_toggle(struct qts_data *qts_data)
+{
+	void __iomem *base;
+
+	if (qts_data->bus_type != QTS_BUS_TYPE_I2C)
+		return;
+
+	base = ioremap(TOUCH_RESET_GPIO_BASE, TOUCH_RESET_GPIO_SIZE);
+	writel_relaxed(0x1, base + TOUCH_RESET_GPIO_OFFSET);
+	/* wait until toggle to finish*/
+	wmb();
+	writel_relaxed(0x0, base + TOUCH_RESET_GPIO_OFFSET);
+	/* wait until toggle to finish*/
+	wmb();
+	iounmap(base);
+}
+
+static void qts_trusted_touch_intr_gpio_toggle(struct qts_data *qts_data,
+		bool enable)
+{
+	void __iomem *base;
+	u32 val;
+
+	if (qts_data->bus_type != QTS_BUS_TYPE_I2C)
+		return;
+
+	base = ioremap(TOUCH_INTR_GPIO_BASE, TOUCH_INTR_GPIO_SIZE);
+	val = readl_relaxed(base + TOUCH_RESET_GPIO_OFFSET);
+	if (enable) {
+		val |= BIT(0);
+		writel_relaxed(val, base + TOUCH_INTR_GPIO_OFFSET);
+		/* wait until toggle to finish*/
+		wmb();
+	} else {
+		val &= ~BIT(0);
+		writel_relaxed(val, base + TOUCH_INTR_GPIO_OFFSET);
+		/* wait until toggle to finish*/
+		wmb();
+	}
+	iounmap(base);
+}
+
+static int qts_sgl_cmp(const void *a, const void *b)
+{
+	struct gh_sgl_entry *left = (struct gh_sgl_entry *)a;
+	struct gh_sgl_entry *right = (struct gh_sgl_entry *)b;
+
+	return (left->ipa_base - right->ipa_base);
+}
+
+static int qts_vm_compare_sgl_desc(struct gh_sgl_desc *expected,
+		struct gh_sgl_desc *received)
+{
+	int idx;
+
+	if (expected->n_sgl_entries != received->n_sgl_entries)
+		return -E2BIG;
+	sort(received->sgl_entries, received->n_sgl_entries,
+			sizeof(received->sgl_entries[0]), qts_sgl_cmp, NULL);
+	sort(expected->sgl_entries, expected->n_sgl_entries,
+			sizeof(expected->sgl_entries[0]), qts_sgl_cmp, NULL);
+
+	for (idx = 0; idx < expected->n_sgl_entries; idx++) {
+		struct gh_sgl_entry *left = &expected->sgl_entries[idx];
+		struct gh_sgl_entry *right = &received->sgl_entries[idx];
+
+		if ((left->ipa_base != right->ipa_base) ||
+				(left->size != right->size)) {
+			pr_err("sgl mismatch: left_base:%d right base:%d left size:%d right size:%d\n",
+				left->ipa_base, right->ipa_base, left->size, right->size);
+
+			return -EINVAL;
+		}
+	}
+	return 0;
+}
+
+static int qts_vm_handle_vm_hardware(struct qts_data *qts_data)
+{
+	int rc = 0;
+
+	if (atomic_read(&qts_data->delayed_tvm_probe_pending)) {
+		rc = qts_irq_registration(qts_data);
+		if (rc) {
+			pr_err("irq registration failure on TVM!\n");
+			return rc;
+		}
+		atomic_set(&qts_data->delayed_tvm_probe_pending, 0);
+	}
+
+	qts_irq_enable(qts_data, true);
+	qts_trusted_touch_set_vm_state(qts_data, TVM_INTERRUPT_ENABLED);
+	return rc;
+}
+
+static void qts_trusted_touch_tvm_vm_mode_enable(struct qts_data *qts_data)
+{
+
+	struct gh_sgl_desc *sgl_desc, *expected_sgl_desc;
+	struct gh_acl_desc *acl_desc;
+	struct irq_data *irq_data;
+	int rc = 0;
+	int irq = 0;
+
+	mutex_lock(&qts_data->transition_lock);
+	if (qts_trusted_touch_get_vm_state(qts_data) != TVM_ALL_RESOURCES_LENT_NOTIFIED) {
+		pr_info("All lend notifications not received\n");
+		qts_trusted_touch_event_notify(qts_data,
+				TRUSTED_TOUCH_EVENT_NOTIFICATIONS_PENDING);
+		mutex_unlock(&qts_data->transition_lock);
+		return;
+	}
+
+	if (qts_data->vendor_ops.pre_le_tui_enable)
+		qts_data->vendor_ops.pre_le_tui_enable(qts_data->vendor_data);
+
+	acl_desc = qts_vm_get_acl(GH_TRUSTED_VM);
+	if (IS_ERR(acl_desc)) {
+		pr_err("failed to populated acl data:rc=%d\n", PTR_ERR(acl_desc));
+		goto accept_fail;
+	}
+
+	sgl_desc = gh_rm_mem_accept(qts_data->vm_info->vm_mem_handle,
+			GH_RM_MEM_TYPE_IO,
+			GH_RM_TRANS_TYPE_LEND,
+			GH_RM_MEM_ACCEPT_VALIDATE_ACL_ATTRS |
+			GH_RM_MEM_ACCEPT_VALIDATE_LABEL |
+			GH_RM_MEM_ACCEPT_DONE,  TRUSTED_TOUCH_MEM_LABEL,
+			acl_desc, NULL, NULL, 0);
+	if (IS_ERR_OR_NULL(sgl_desc)) {
+		pr_err("failed to do mem accept :rc=%d\n", PTR_ERR(sgl_desc));
+		goto acl_fail;
+	}
+	qts_trusted_touch_set_vm_state(qts_data, TVM_IOMEM_ACCEPTED);
+
+	/* Initiate session on tvm */
+	if (qts_data->bus_type == QTS_BUS_TYPE_I2C)
+		rc = pm_runtime_get_sync(qts_data->client->adapter->dev.parent);
+	else
+		rc = pm_runtime_get_sync(qts_data->spi->master->dev.parent);
+
+	if (rc < 0) {
+		pr_err("failed to get sync rc:%d\n", rc);
+		goto sgl_fail;
+	}
+	qts_trusted_touch_set_vm_state(qts_data, TVM_I2C_SESSION_ACQUIRED);
+
+	expected_sgl_desc = qts_vm_get_sgl(qts_data->vm_info);
+	if (qts_vm_compare_sgl_desc(expected_sgl_desc, sgl_desc)) {
+		pr_err("IO sg list does not match\n");
+		goto sgl_cmp_fail;
+	}
+
+	kfree(expected_sgl_desc);
+	kfree(acl_desc);
+	kfree(sgl_desc);
+
+	irq = gh_irq_accept(qts_data->vm_info->irq_label, -1, IRQ_TYPE_EDGE_RISING);
+	qts_trusted_touch_intr_gpio_toggle(qts_data, false);
+	if (irq < 0) {
+		pr_err("failed to accept irq\n");
+		goto accept_fail;
+	}
+	qts_trusted_touch_set_vm_state(qts_data, TVM_IRQ_ACCEPTED);
+
+
+	irq_data = irq_get_irq_data(irq);
+	if (!irq_data) {
+		pr_err("Invalid irq data for trusted touch\n");
+		goto accept_fail;
+	}
+	if (!irq_data->hwirq) {
+		pr_err("Invalid irq in irq data\n");
+		goto accept_fail;
+	}
+	if (irq_data->hwirq != qts_data->vm_info->hw_irq) {
+		pr_err("Invalid irq lent\n");
+		goto accept_fail;
+	}
+
+	pr_debug("irq:returned from accept:%d\n", irq);
+	qts_data->irq = irq;
+
+	rc = qts_vm_handle_vm_hardware(qts_data);
+	if (rc) {
+		pr_err("Delayed probe failure on TVM!\n");
+		goto accept_fail;
+	}
+	atomic_set(&qts_data->trusted_touch_enabled, 1);
+
+	if (qts_data->vendor_ops.post_le_tui_enable)
+		qts_data->vendor_ops.post_le_tui_enable(qts_data->vendor_data);
+
+	pr_info("Irq, iomem are accepted and trusted touch enabled\n");
+
+	mutex_unlock(&qts_data->transition_lock);
+	return;
+sgl_cmp_fail:
+	kfree(expected_sgl_desc);
+sgl_fail:
+	kfree(sgl_desc);
+acl_fail:
+	kfree(acl_desc);
+accept_fail:
+	qts_trusted_touch_abort_handler(qts_data,
+			TRUSTED_TOUCH_EVENT_ACCEPT_FAILURE);
+	mutex_unlock(&qts_data->transition_lock);
+}
+
+static void qts_vm_irq_on_lend_callback(void *data,
+					unsigned long notif_type,
+					enum gh_irq_label label)
+{
+	struct qts_data *qts_data = data;
+
+	pr_debug("received irq lend request for label:%d\n", label);
+	if (qts_trusted_touch_get_vm_state(qts_data) == TVM_IOMEM_LENT_NOTIFIED)
+		qts_trusted_touch_set_vm_state(qts_data, TVM_ALL_RESOURCES_LENT_NOTIFIED);
+	else
+		qts_trusted_touch_set_vm_state(qts_data, TVM_IRQ_LENT_NOTIFIED);
+}
+
+static void qts_vm_mem_on_lend_handler(enum gh_mem_notifier_tag tag,
+		unsigned long notif_type, void *entry_data, void *notif_msg)
+{
+	struct gh_rm_notif_mem_shared_payload *payload;
+	struct trusted_touch_vm_info *vm_info;
+	struct qts_data *qts_data;
+
+	qts_data = (struct qts_data *)entry_data;
+	vm_info = qts_data->vm_info;
+	if (!vm_info) {
+		pr_err("Invalid vm_info\n");
+		return;
+	}
+
+	if (notif_type != GH_RM_NOTIF_MEM_SHARED ||
+			tag != vm_info->mem_tag) {
+		pr_err("Invalid command passed from rm\n");
+		return;
+	}
+
+	if (!entry_data || !notif_msg) {
+		pr_err("Invalid entry data passed from rm\n");
+		return;
+	}
+
+	payload = (struct gh_rm_notif_mem_shared_payload  *)notif_msg;
+	if (payload->trans_type != GH_RM_TRANS_TYPE_LEND ||
+			payload->label != TRUSTED_TOUCH_MEM_LABEL) {
+		pr_err("Invalid label or transaction type\n");
+		return;
+	}
+
+	vm_info->vm_mem_handle = payload->mem_handle;
+	pr_debug("received mem lend request with handle:%d\n", vm_info->vm_mem_handle);
+
+	if (qts_trusted_touch_get_vm_state(qts_data) == TVM_IRQ_LENT_NOTIFIED)
+		qts_trusted_touch_set_vm_state(qts_data, TVM_ALL_RESOURCES_LENT_NOTIFIED);
+	else
+		qts_trusted_touch_set_vm_state(qts_data, TVM_IOMEM_LENT_NOTIFIED);
+}
+
+static int qts_vm_mem_release(struct qts_data *qts_data)
+{
+	int rc = 0;
+
+	if (!qts_data->vm_info->vm_mem_handle) {
+		pr_err("Invalid memory handle\n");
+		return -EINVAL;
+	}
+
+	rc = gh_rm_mem_release(qts_data->vm_info->vm_mem_handle, 0);
+	if (rc)
+		pr_err("VM mem release failed: rc=%d\n", rc);
+
+	rc = gh_rm_mem_notify(qts_data->vm_info->vm_mem_handle,
+				GH_RM_MEM_NOTIFY_OWNER_RELEASED,
+				qts_data->vm_info->mem_tag, 0);
+	if (rc)
+		pr_err("Failed to notify mem release to PVM: rc=%d\n", rc);
+
+	pr_debug("vm mem release success\n");
+
+	qts_data->vm_info->vm_mem_handle = 0;
+	return rc;
+}
+
+static void qts_trusted_touch_tvm_vm_mode_disable(struct qts_data *qts_data)
+{
+	int rc = 0;
+
+	mutex_lock(&qts_data->transition_lock);
+	if (atomic_read(&qts_data->trusted_touch_abort_status)) {
+		qts_trusted_touch_abort_tvm(qts_data);
+		mutex_unlock(&qts_data->transition_lock);
+		return;
+	}
+
+	if (qts_data->vendor_ops.pre_le_tui_disable)
+		qts_data->vendor_ops.pre_le_tui_disable(qts_data->vendor_data);
+
+	qts_irq_enable(qts_data, false);
+	qts_trusted_touch_set_vm_state(qts_data, TVM_INTERRUPT_DISABLED);
+
+	rc = gh_irq_release(qts_data->vm_info->irq_label);
+	if (rc) {
+		pr_err("Failed to release irq rc:%d\n", rc);
+		goto error;
+	} else {
+		qts_trusted_touch_set_vm_state(qts_data, TVM_IRQ_RELEASED);
+	}
+	rc = gh_irq_release_notify(qts_data->vm_info->irq_label);
+	if (rc)
+		pr_err("Failed to notify release irq rc:%d\n", rc);
+
+	pr_debug("vm irq release success\n");
+
+	if (qts_data->bus_type == QTS_BUS_TYPE_I2C)
+		pm_runtime_put_sync(qts_data->client->adapter->dev.parent);
+	else
+		pm_runtime_put_sync(qts_data->spi->master->dev.parent);
+
+	qts_trusted_touch_set_vm_state(qts_data, TVM_I2C_SESSION_RELEASED);
+	rc = qts_vm_mem_release(qts_data);
+	if (rc) {
+		pr_err("Failed to release mem rc:%d\n", rc);
+		goto error;
+	} else {
+		qts_trusted_touch_set_vm_state(qts_data, TVM_IOMEM_RELEASED);
+	}
+	qts_trusted_touch_set_vm_state(qts_data, TRUSTED_TOUCH_TVM_INIT);
+	atomic_set(&qts_data->trusted_touch_enabled, 0);
+
+	if (qts_data->vendor_ops.post_le_tui_disable)
+		qts_data->vendor_ops.post_le_tui_disable(qts_data->vendor_data);
+
+	pr_info("Irq, iomem are released and trusted touch disabled\n");
+	mutex_unlock(&qts_data->transition_lock);
+	return;
+error:
+	qts_trusted_touch_abort_handler(qts_data,
+			TRUSTED_TOUCH_EVENT_RELEASE_FAILURE);
+	mutex_unlock(&qts_data->transition_lock);
+}
+
+static int qts_handle_trusted_touch_tvm(struct qts_data *qts_data, int value)
+{
+	int err = 0;
+
+	switch (value) {
+	case 0:
+		if ((atomic_read(&qts_data->trusted_touch_enabled) == 0) &&
+			(atomic_read(&qts_data->trusted_touch_abort_status) == 0)) {
+			pr_err("Trusted touch is already disabled\n");
+			break;
+		}
+		if (atomic_read(&qts_data->trusted_touch_mode) ==
+				TRUSTED_TOUCH_VM_MODE) {
+			qts_trusted_touch_tvm_vm_mode_disable(qts_data);
+		} else {
+			pr_err("Unsupported trusted touch mode\n");
+		}
+		break;
+
+	case 1:
+		if (atomic_read(&qts_data->trusted_touch_enabled)) {
+			pr_err("Trusted touch usecase underway\n");
+			err = -EBUSY;
+			break;
+		}
+		if (atomic_read(&qts_data->trusted_touch_mode) ==
+				TRUSTED_TOUCH_VM_MODE) {
+			qts_trusted_touch_tvm_vm_mode_enable(qts_data);
+		} else {
+			pr_err("Unsupported trusted touch mode\n");
+		}
+		break;
+
+	default:
+		pr_err("unsupported value: %lu\n", value);
+		err = -EINVAL;
+		break;
+	}
+
+	return err;
+}
+
+static void qts_trusted_touch_abort_tvm(struct qts_data *qts_data)
+{
+	int rc = 0;
+	int vm_state = qts_trusted_touch_get_vm_state(qts_data);
+
+	if (vm_state >= TRUSTED_TOUCH_TVM_STATE_MAX) {
+		pr_err("invalid tvm driver state: %d\n", vm_state);
+		return;
+	}
+
+	switch (vm_state) {
+	case TVM_INTERRUPT_ENABLED:
+		qts_irq_enable(qts_data, false);
+		fallthrough;
+	case TVM_IRQ_ACCEPTED:
+	case TVM_INTERRUPT_DISABLED:
+		rc = gh_irq_release(qts_data->vm_info->irq_label);
+		if (rc)
+			pr_err("Failed to release irq rc:%d\n", rc);
+		rc = gh_irq_release_notify(qts_data->vm_info->irq_label);
+		if (rc)
+			pr_err("Failed to notify irq release rc:%d\n", rc);
+		fallthrough;
+	case TVM_I2C_SESSION_ACQUIRED:
+	case TVM_IOMEM_ACCEPTED:
+	case TVM_IRQ_RELEASED:
+		if (qts_data->bus_type == QTS_BUS_TYPE_I2C)
+			pm_runtime_put_sync(qts_data->client->adapter->dev.parent);
+		else
+			pm_runtime_put_sync(qts_data->spi->master->dev.parent);
+		fallthrough;
+	case TVM_I2C_SESSION_RELEASED:
+		rc = qts_vm_mem_release(qts_data);
+		if (rc)
+			pr_err("Failed to release mem rc:%d\n", rc);
+		fallthrough;
+	case TVM_IOMEM_RELEASED:
+	case TVM_ALL_RESOURCES_LENT_NOTIFIED:
+	case TRUSTED_TOUCH_TVM_INIT:
+	case TVM_IRQ_LENT_NOTIFIED:
+	case TVM_IOMEM_LENT_NOTIFIED:
+		atomic_set(&qts_data->trusted_touch_enabled, 0);
+	}
+
+	atomic_set(&qts_data->trusted_touch_abort_status, 0);
+	qts_trusted_touch_set_vm_state(qts_data, TRUSTED_TOUCH_TVM_INIT);
+}
+
+#else
+
+static void qts_bus_put(struct qts_data *qts_data);
+static int qts_enable_reg(struct qts_data *qts_data, bool enable);
+
+static void qts_trusted_touch_abort_pvm(struct qts_data *qts_data)
+{
+	int rc = 0;
+	int vm_state = qts_trusted_touch_get_vm_state(qts_data);
+
+	if (vm_state >= TRUSTED_TOUCH_PVM_STATE_MAX) {
+		pr_err("Invalid driver state: %d\n", vm_state);
+		return;
+	}
+
+	switch (vm_state) {
+	case PVM_IRQ_RELEASE_NOTIFIED:
+	case PVM_ALL_RESOURCES_RELEASE_NOTIFIED:
+	case PVM_IRQ_LENT:
+	case PVM_IRQ_LENT_NOTIFIED:
+		rc = gh_irq_reclaim(qts_data->vm_info->irq_label);
+		if (rc) {
+			pr_err("failed to reclaim irq on pvm rc:%d\n", rc);
+			return;
+		}
+		fallthrough;
+	case PVM_IRQ_RECLAIMED:
+	case PVM_IOMEM_LENT:
+	case PVM_IOMEM_LENT_NOTIFIED:
+	case PVM_IOMEM_RELEASE_NOTIFIED:
+#if (KERNEL_VERSION(6, 1, 0) <= LINUX_VERSION_CODE)
+		rc = ghd_rm_mem_reclaim(qts_data->vm_info->vm_mem_handle, 0);
+#else
+		rc = gh_rm_mem_reclaim(qts_data->vm_info->vm_mem_handle, 0);
+#endif
+
+		if (rc) {
+			pr_err("failed to reclaim iomem on pvm rc:%d\n", rc);
+			qts_trusted_touch_set_vm_state(qts_data, PVM_IOMEM_RELEASE_NOTIFIED);
+			return;
+		}
+		qts_data->vm_info->vm_mem_handle = 0;
+		fallthrough;
+	case PVM_IOMEM_RECLAIMED:
+	case PVM_INTERRUPT_DISABLED:
+		if (qts_data->vendor_ops.enable_touch_irq)
+			qts_data->vendor_ops.enable_touch_irq(qts_data->vendor_data, true);
+		fallthrough;
+	case PVM_I2C_RESOURCE_ACQUIRED:
+	case PVM_INTERRUPT_ENABLED:
+		qts_bus_put(qts_data);
+		fallthrough;
+	case TRUSTED_TOUCH_PVM_INIT:
+	case PVM_I2C_RESOURCE_RELEASED:
+		atomic_set(&qts_data->trusted_touch_enabled, 0);
+		atomic_set(&qts_data->trusted_touch_transition, 0);
+	}
+
+	atomic_set(&qts_data->trusted_touch_abort_status, 0);
+
+	qts_trusted_touch_set_vm_state(qts_data, TRUSTED_TOUCH_PVM_INIT);
+}
+
+static int qts_clk_prepare_enable(struct qts_data *qts_data)
+{
+	int ret;
+
+	ret = clk_prepare_enable(qts_data->iface_clk);
+	if (ret) {
+		pr_err("error on clk_prepare_enable(iface_clk):%d\n", ret);
+		return ret;
+	}
+
+	ret = clk_prepare_enable(qts_data->core_clk);
+	if (ret) {
+		clk_disable_unprepare(qts_data->iface_clk);
+		pr_err("error clk_prepare_enable(core_clk):%d\n", ret);
+	}
+	return ret;
+}
+
+static void qts_clk_disable_unprepare(struct qts_data *qts_data)
+{
+	clk_disable_unprepare(qts_data->core_clk);
+	clk_disable_unprepare(qts_data->iface_clk);
+}
+
+static int qts_bus_get(struct qts_data *qts_data)
+{
+	int rc = 0;
+	struct device *dev = NULL;
+
+	if (qts_data->schedule_suspend)
+		cancel_work_sync(&qts_data->suspend_work);
+	if (qts_data->schedule_resume)
+		cancel_work_sync(&qts_data->resume_work);
+
+	reinit_completion(&qts_data->trusted_touch_powerdown);
+
+	qts_enable_reg(qts_data, true);
+
+	if (qts_data->bus_type == QTS_BUS_TYPE_I2C)
+		dev = qts_data->client->adapter->dev.parent;
+	else
+		dev = qts_data->spi->master->dev.parent;
+
+	mutex_lock(&qts_data->qts_clk_io_ctrl_mutex);
+	rc = pm_runtime_get_sync(dev);
+	if (rc >= 0 &&  qts_data->core_clk != NULL &&
+				qts_data->iface_clk != NULL) {
+		rc = qts_clk_prepare_enable(qts_data);
+		if (rc)
+			pm_runtime_put_sync(dev);
+	}
+
+	mutex_unlock(&qts_data->qts_clk_io_ctrl_mutex);
+	return rc;
+}
+
+static void qts_bus_put(struct qts_data *qts_data)
+{
+	struct device *dev = NULL;
+
+	if (qts_data->bus_type == QTS_BUS_TYPE_I2C)
+		dev = qts_data->client->adapter->dev.parent;
+	else
+		dev = qts_data->spi->master->dev.parent;
+
+	mutex_lock(&qts_data->qts_clk_io_ctrl_mutex);
+	if (qts_data->core_clk != NULL && qts_data->iface_clk != NULL)
+		qts_clk_disable_unprepare(qts_data);
+	pm_runtime_put_sync(dev);
+	mutex_unlock(&qts_data->qts_clk_io_ctrl_mutex);
+	complete(&qts_data->trusted_touch_powerdown);
+	qts_enable_reg(qts_data, false);
+}
+
+static struct gh_notify_vmid_desc *qts_vm_get_vmid(gh_vmid_t vmid)
+{
+	struct gh_notify_vmid_desc *vmid_desc;
+
+	vmid_desc = kzalloc(offsetof(struct gh_notify_vmid_desc,
+				vmid_entries[1]), GFP_KERNEL);
+	if (!vmid_desc)
+		return ERR_PTR(ENOMEM);
+
+	vmid_desc->n_vmid_entries = 1;
+	vmid_desc->vmid_entries[0].vmid = vmid;
+	return vmid_desc;
+}
+
+static void qts_trusted_touch_pvm_vm_mode_disable(struct qts_data *qts_data)
+{
+	int rc = 0;
+
+	atomic_set(&qts_data->trusted_touch_transition, 1);
+
+	if (atomic_read(&qts_data->trusted_touch_abort_status)) {
+		qts_trusted_touch_abort_pvm(qts_data);
+		return;
+	}
+
+	if (qts_trusted_touch_get_vm_state(qts_data) != PVM_ALL_RESOURCES_RELEASE_NOTIFIED)
+		pr_info("all release notifications are not received yet\n");
+
+	if (qts_data->vendor_ops.pre_la_tui_disable)
+		qts_data->vendor_ops.pre_la_tui_disable(qts_data->vendor_data);
+
+#if (KERNEL_VERSION(6, 1, 0) <= LINUX_VERSION_CODE)
+	rc = ghd_rm_mem_reclaim(qts_data->vm_info->vm_mem_handle, 0);
+#else
+	rc = gh_rm_mem_reclaim(qts_data->vm_info->vm_mem_handle, 0);
+#endif
+
+	if (rc) {
+		pr_err("Trusted touch VM mem reclaim failed rc:%d\n", rc);
+		goto error;
+	}
+	qts_trusted_touch_set_vm_state(qts_data, PVM_IOMEM_RECLAIMED);
+	qts_data->vm_info->vm_mem_handle = 0;
+	pr_debug("vm mem reclaim success!\n");
+
+	rc = gh_irq_reclaim(qts_data->vm_info->irq_label);
+	if (rc) {
+		pr_err("failed to reclaim irq on pvm rc:%d\n", rc);
+		goto error;
+	}
+	qts_trusted_touch_set_vm_state(qts_data, PVM_IRQ_RECLAIMED);
+	pr_debug("vm irq reclaim success!\n");
+
+	if (qts_data->vendor_ops.enable_touch_irq)
+		qts_data->vendor_ops.enable_touch_irq(qts_data->vendor_data, true);
+
+	qts_trusted_touch_set_vm_state(qts_data, PVM_INTERRUPT_ENABLED);
+	qts_bus_put(qts_data);
+	atomic_set(&qts_data->trusted_touch_transition, 0);
+	qts_trusted_touch_set_vm_state(qts_data, PVM_I2C_RESOURCE_RELEASED);
+	qts_trusted_touch_set_vm_state(qts_data, TRUSTED_TOUCH_PVM_INIT);
+	atomic_set(&qts_data->trusted_touch_enabled, 0);
+
+	if (qts_data->vendor_ops.post_la_tui_disable)
+		qts_data->vendor_ops.post_la_tui_disable(qts_data->vendor_data);
+
+	pr_info("Irq, iomem are reclaimed and trusted touch disabled\n");
+	return;
+error:
+	qts_trusted_touch_abort_handler(qts_data,
+			TRUSTED_TOUCH_EVENT_RECLAIM_FAILURE);
+}
+
+static void qts_vm_irq_on_release_callback(void *data,
+					unsigned long notif_type,
+					enum gh_irq_label label)
+{
+	struct qts_data *qts_data = data;
+
+	if (notif_type != GH_RM_NOTIF_VM_IRQ_RELEASED) {
+		pr_err("invalid notification type\n");
+		return;
+	}
+
+	if (qts_trusted_touch_get_vm_state(qts_data) == PVM_IOMEM_RELEASE_NOTIFIED)
+		qts_trusted_touch_set_vm_state(qts_data, PVM_ALL_RESOURCES_RELEASE_NOTIFIED);
+	else
+		qts_trusted_touch_set_vm_state(qts_data, PVM_IRQ_RELEASE_NOTIFIED);
+}
+
+static void qts_vm_mem_on_release_handler(enum gh_mem_notifier_tag tag,
+		unsigned long notif_type, void *entry_data, void *notif_msg)
+{
+	struct gh_rm_notif_mem_released_payload *release_payload;
+	struct trusted_touch_vm_info *vm_info;
+	struct qts_data *qts_data;
+
+	qts_data = (struct qts_data *)entry_data;
+	vm_info = qts_data->vm_info;
+	if (!vm_info) {
+		pr_err("Invalid vm_info\n");
+		return;
+	}
+
+	if (notif_type != GH_RM_NOTIF_MEM_RELEASED) {
+		pr_err("Invalid notification type\n");
+		return;
+	}
+
+	if (tag != vm_info->mem_tag) {
+		pr_err("Invalid tag\n");
+		return;
+	}
+
+	if (!entry_data || !notif_msg) {
+		pr_err("Invalid data or notification message\n");
+		return;
+	}
+
+	release_payload = (struct gh_rm_notif_mem_released_payload  *)notif_msg;
+	if (release_payload->mem_handle != vm_info->vm_mem_handle) {
+		pr_err("Invalid mem handle detected\n");
+		return;
+	}
+
+	if (qts_trusted_touch_get_vm_state(qts_data) == PVM_IRQ_RELEASE_NOTIFIED)
+		qts_trusted_touch_set_vm_state(qts_data, PVM_ALL_RESOURCES_RELEASE_NOTIFIED);
+	else
+		qts_trusted_touch_set_vm_state(qts_data, PVM_IOMEM_RELEASE_NOTIFIED);
+}
+
+static int qts_vm_mem_lend(struct qts_data *qts_data)
+{
+	struct gh_acl_desc *acl_desc;
+	struct gh_sgl_desc *sgl_desc;
+	struct gh_notify_vmid_desc *vmid_desc;
+	gh_memparcel_handle_t mem_handle;
+	gh_vmid_t trusted_vmid;
+	int rc = 0;
+
+	acl_desc = qts_vm_get_acl(GH_TRUSTED_VM);
+	if (IS_ERR(acl_desc)) {
+		pr_err("Failed to get acl of IO memories for Trusted touch\n");
+		rc = PTR_ERR(acl_desc);
+		return rc;
+	}
+
+	sgl_desc = qts_vm_get_sgl(qts_data->vm_info);
+	if (IS_ERR(sgl_desc)) {
+		pr_err("Failed to get sgl of IO memories for Trusted touch\n");
+		rc = PTR_ERR(sgl_desc);
+		goto sgl_error;
+	}
+
+#if (KERNEL_VERSION(6, 1, 0) <= LINUX_VERSION_CODE)
+	rc = ghd_rm_mem_lend(GH_RM_MEM_TYPE_IO, 0, TRUSTED_TOUCH_MEM_LABEL,
+			acl_desc, sgl_desc, NULL, &mem_handle);
+#else
+	rc = gh_rm_mem_lend(GH_RM_MEM_TYPE_IO, 0, TRUSTED_TOUCH_MEM_LABEL,
+			acl_desc, sgl_desc, NULL, &mem_handle);
+#endif
+
+	if (rc) {
+		pr_err("Failed to lend IO memories for Trusted touch rc:%d\n", rc);
+		goto error;
+	}
+
+	pr_debug("vm mem lend success\n");
+
+	qts_trusted_touch_set_vm_state(qts_data, PVM_IOMEM_LENT);
+
+#if (KERNEL_VERSION(6, 1, 0) <= LINUX_VERSION_CODE)
+	ghd_rm_get_vmid(GH_TRUSTED_VM, &trusted_vmid);
+#else
+	gh_rm_get_vmid(GH_TRUSTED_VM, &trusted_vmid);
+#endif
+
+	vmid_desc = qts_vm_get_vmid(trusted_vmid);
+
+	rc = gh_rm_mem_notify(mem_handle, GH_RM_MEM_NOTIFY_RECIPIENT_SHARED,
+			qts_data->vm_info->mem_tag, vmid_desc);
+	if (rc) {
+		pr_err("Failed to notify mem lend to hypervisor rc:%d\n", rc);
+		goto vmid_error;
+	}
+
+	qts_trusted_touch_set_vm_state(qts_data, PVM_IOMEM_LENT_NOTIFIED);
+
+	qts_data->vm_info->vm_mem_handle = mem_handle;
+vmid_error:
+	kfree(vmid_desc);
+error:
+	kfree(sgl_desc);
+sgl_error:
+	kfree(acl_desc);
+
+	return rc;
+}
+
+static int qts_trusted_touch_pvm_vm_mode_enable(struct qts_data *qts_data)
+{
+	int rc = 0;
+	struct trusted_touch_vm_info *vm_info = qts_data->vm_info;
+
+	atomic_set(&qts_data->trusted_touch_transition, 1);
+	mutex_lock(&qts_data->transition_lock);
+
+	if (qts_data->suspended) {
+		pr_err("Invalid power state for operation\n");
+		atomic_set(&qts_data->trusted_touch_transition, 0);
+		rc =  -EPERM;
+		goto error;
+	}
+
+	if (qts_data->vendor_ops.pre_la_tui_enable)
+		qts_data->vendor_ops.pre_la_tui_enable(qts_data->vendor_data);
+
+	/* i2c session start and resource acquire */
+	if (qts_bus_get(qts_data) < 0) {
+		pr_err("qts_bus_get failed\n");
+		rc = -EIO;
+		goto error;
+	}
+
+	qts_trusted_touch_set_vm_state(qts_data, PVM_I2C_RESOURCE_ACQUIRED);
+	if (qts_data->vendor_ops.enable_touch_irq)
+		qts_data->vendor_ops.enable_touch_irq(qts_data->vendor_data, false);
+	qts_trusted_touch_set_vm_state(qts_data, PVM_INTERRUPT_DISABLED);
+
+	rc = qts_vm_mem_lend(qts_data);
+	if (rc) {
+		pr_err("Failed to lend memory\n");
+		goto abort_handler;
+	}
+	pr_debug("vm mem lend success\n");
+
+	if (atomic_read(&qts_data->delayed_pvm_probe_pending)) {
+		if (qts_data->vendor_ops.get_irq_num)
+			qts_data->irq = qts_data->vendor_ops.get_irq_num(qts_data->vendor_data);
+
+		atomic_set(&qts_data->delayed_tvm_probe_pending, 0);
+	}
+
+	rc = gh_irq_lend_v2(vm_info->irq_label, vm_info->vm_name,
+		qts_data->irq, &qts_vm_irq_on_release_callback, qts_data);
+	if (rc) {
+		pr_err("Failed to lend irq\n");
+		goto abort_handler;
+	}
+
+	pr_debug("vm irq lend success for irq:%d\n", qts_data->irq);
+	qts_trusted_touch_set_vm_state(qts_data, PVM_IRQ_LENT);
+
+	rc = gh_irq_lend_notify(vm_info->irq_label);
+	if (rc) {
+		pr_err("Failed to notify irq\n");
+		goto abort_handler;
+	}
+	qts_trusted_touch_set_vm_state(qts_data, PVM_IRQ_LENT_NOTIFIED);
+
+	if (qts_data->vendor_ops.post_la_tui_enable)
+		qts_data->vendor_ops.post_la_tui_enable(qts_data->vendor_data);
+
+	mutex_unlock(&qts_data->transition_lock);
+	atomic_set(&qts_data->trusted_touch_transition, 0);
+	atomic_set(&qts_data->trusted_touch_enabled, 1);
+	pr_info("Irq, iomem are lent and trusted touch enabled\n");
+	return rc;
+
+abort_handler:
+	qts_trusted_touch_abort_handler(qts_data, TRUSTED_TOUCH_EVENT_LEND_FAILURE);
+
+error:
+	mutex_unlock(&qts_data->transition_lock);
+	return rc;
+}
+
+static int qts_handle_trusted_touch_pvm(struct qts_data *qts_data, int value)
+{
+	int err = 0;
+
+	switch (value) {
+	case 0:
+		if (atomic_read(&qts_data->trusted_touch_enabled) == 0 &&
+			(atomic_read(&qts_data->trusted_touch_abort_status) == 0)) {
+			pr_err("Trusted touch is already disabled\n");
+			break;
+		}
+		if (atomic_read(&qts_data->trusted_touch_mode) ==
+				TRUSTED_TOUCH_VM_MODE) {
+			qts_trusted_touch_pvm_vm_mode_disable(qts_data);
+		} else {
+			pr_err("Unsupported trusted touch mode\n");
+		}
+		break;
+
+	case 1:
+		if (atomic_read(&qts_data->trusted_touch_enabled)) {
+			pr_err("Trusted touch usecase underway\n");
+			err = -EBUSY;
+			break;
+		}
+		if (atomic_read(&qts_data->trusted_touch_mode) ==
+				TRUSTED_TOUCH_VM_MODE) {
+			err = qts_trusted_touch_pvm_vm_mode_enable(qts_data);
+		} else {
+			pr_err("Unsupported trusted touch mode\n");
+		}
+		break;
+
+	default:
+		pr_err("unsupported value: %lu\n", value);
+		err = -EINVAL;
+		break;
+	}
+	return err;
+}
+
+#endif
+
+static void qts_trusted_touch_event_notify(struct qts_data *qts_data, int event)
+{
+	atomic_set(&qts_data->trusted_touch_event, event);
+	sysfs_notify(&qts_data->dev->kobj, NULL, "trusted_touch_event");
+}
+
+static void qts_trusted_touch_abort_handler(struct qts_data *qts_data, int error)
+{
+	atomic_set(&qts_data->trusted_touch_abort_status, error);
+	pr_info("TUI session aborted with failure:%d\n", error);
+	qts_trusted_touch_event_notify(qts_data, error);
+#ifdef CONFIG_ARCH_QTI_VM
+	pr_info("Resetting touch controller\n");
+	if (qts_trusted_touch_get_vm_state(qts_data) >= TVM_IOMEM_ACCEPTED &&
+			error == TRUSTED_TOUCH_EVENT_I2C_FAILURE) {
+		pr_info("Resetting touch controller\n");
+		qts_trusted_touch_reset_gpio_toggle(qts_data);
+	}
+#endif
+}
+
+static int qts_vm_init(struct qts_data *qts_data)
+{
+	int rc = 0;
+	struct trusted_touch_vm_info *vm_info;
+	void *mem_cookie;
+
+	rc = qts_populate_vm_info(qts_data);
+	if (rc) {
+		pr_err("Cannot setup vm pipeline\n");
+		rc = -EINVAL;
+		goto fail;
+	}
+
+	vm_info = qts_data->vm_info;
+#ifdef CONFIG_ARCH_QTI_VM
+	mem_cookie = gh_mem_notifier_register(vm_info->mem_tag,
+			qts_vm_mem_on_lend_handler, qts_data);
+	if (!mem_cookie) {
+		pr_err("Failed to register on lend mem notifier\n");
+		rc = -EINVAL;
+		goto init_fail;
+	}
+	vm_info->mem_cookie = mem_cookie;
+	rc = gh_irq_wait_for_lend_v2(vm_info->irq_label, GH_PRIMARY_VM,
+			&qts_vm_irq_on_lend_callback, qts_data);
+	qts_trusted_touch_set_vm_state(qts_data, TRUSTED_TOUCH_TVM_INIT);
+#else
+	mem_cookie = gh_mem_notifier_register(vm_info->mem_tag,
+			qts_vm_mem_on_release_handler, qts_data);
+	if (!mem_cookie) {
+		pr_err("Failed to register on release mem notifier\n");
+		rc = -EINVAL;
+		goto init_fail;
+	}
+	vm_info->mem_cookie = mem_cookie;
+	qts_trusted_touch_set_vm_state(qts_data, TRUSTED_TOUCH_PVM_INIT);
+#endif
+	return rc;
+init_fail:
+	qts_vm_deinit(qts_data);
+fail:
+	return rc;
+}
+
+static void qts_dt_parse_trusted_touch_info(struct qts_data *qts_data)
+{
+	struct device_node *np = qts_data->dev->of_node;
+	int rc = 0;
+	const char *selection;
+	const char *environment;
+
+	rc = of_property_read_string(np, "qts,trusted-touch-mode", &selection);
+	if (rc) {
+		pr_err("No trusted touch mode selection made\n");
+		atomic_set(&qts_data->trusted_touch_mode,
+						TRUSTED_TOUCH_MODE_NONE);
+		return;
+	}
+
+	if (!strcmp(selection, "vm_mode")) {
+		atomic_set(&qts_data->trusted_touch_mode,
+						TRUSTED_TOUCH_VM_MODE);
+		pr_debug("Selected trusted touch mode to VM mode\n");
+	} else {
+		atomic_set(&qts_data->trusted_touch_mode,
+						TRUSTED_TOUCH_MODE_NONE);
+		pr_err("Invalid trusted_touch mode\n");
+	}
+
+	rc = of_property_read_string(np, "qts,touch-environment",
+						&environment);
+	if (rc)
+		pr_err("No trusted touch mode environment\n");
+
+	qts_data->touch_environment = environment;
+	qts_data->tui_supported = true;
+	pr_debug("Trusted touch environment:%s\n", qts_data->touch_environment);
+}
+
+static void qts_trusted_touch_init(struct qts_data *qts_data)
+{
+	int rc = 0;
+
+	atomic_set(&qts_data->trusted_touch_initialized, 0);
+	qts_dt_parse_trusted_touch_info(qts_data);
+
+	if (atomic_read(&qts_data->trusted_touch_mode) == TRUSTED_TOUCH_MODE_NONE)
+		return;
+
+	init_completion(&qts_data->trusted_touch_powerdown);
+
+	/* Get clocks */
+	qts_data->core_clk = devm_clk_get(qts_data->dev->parent, "m-ahb");
+
+	if (IS_ERR(qts_data->core_clk)) {
+		qts_data->core_clk = NULL;
+		pr_err("core_clk is not defined\n");
+	}
+
+	qts_data->iface_clk = devm_clk_get(qts_data->dev->parent, "se-clk");
+
+	if (IS_ERR(qts_data->iface_clk)) {
+		qts_data->iface_clk = NULL;
+		pr_err("iface_clk is not defined\n");
+	}
+
+	if (atomic_read(&qts_data->trusted_touch_mode) ==
+						TRUSTED_TOUCH_VM_MODE) {
+		rc = qts_vm_init(qts_data);
+		if (rc)
+			pr_err("Failed to init VM\n");
+	}
+	atomic_set(&qts_data->trusted_touch_initialized, 1);
+}
+
+static bool qts_ts_is_primary(struct kobject *kobj)
+{
+	char *path = NULL;
+
+	if (!kobj)
+		return true;
+
+	path = kobject_get_path(kobj, GFP_KERNEL);
+
+	if (strstr(path, "primary"))
+		return true;
+	else
+		return false;
+}
+
+static ssize_t trusted_touch_enable_show(struct kobject *kobj, struct kobj_attribute *attr,
+				char *buf)
+{
+	struct qts_data *qts_data;
+	u32 idx = qts_ts_is_primary(kobj) ? 0 : 1;
+
+	qts_data = &qts_data_entries->info[idx];
+
+	return scnprintf(buf, PAGE_SIZE, "%d",
+			atomic_read(&qts_data->trusted_touch_enabled));
+}
+
+static ssize_t trusted_touch_enable_store(struct kobject *kobj, struct kobj_attribute *attr,
+				const char *buf, size_t count)
+{
+	struct qts_data *qts_data;
+	unsigned long value;
+	int err = 0;
+	u32 idx = qts_ts_is_primary(kobj) ? 0 : 1;
+
+	if (count > 2)
+		return -EINVAL;
+
+	err = kstrtoul(buf, 10, &value);
+	if (err != 0)
+		return err;
+
+	qts_data = &qts_data_entries->info[idx];
+
+	if (!atomic_read(&qts_data->trusted_touch_initialized))
+		return -EIO;
+
+	pr_info("TUI trusted_touch_enable:%d\n", value);
+
+#ifdef CONFIG_ARCH_QTI_VM
+	err = qts_handle_trusted_touch_tvm(qts_data, value);
+	if (err) {
+		pr_err("Failed to handle trusted touch in tvm\n");
+		return -EINVAL;
+	}
+#else
+	err = qts_handle_trusted_touch_pvm(qts_data, value);
+	if (err) {
+		pr_err("Failed to handle trusted touch in pvm\n");
+		return -EINVAL;
+	}
+#endif
+	err = count;
+	return err;
+}
+
+static ssize_t trusted_touch_event_show(struct kobject *kobj, struct kobj_attribute *attr,
+				char *buf)
+{
+	struct qts_data *qts_data;
+	u32 idx = qts_ts_is_primary(kobj) ? 0 : 1;
+
+	qts_data = &qts_data_entries->info[idx];
+
+	return scnprintf(buf, PAGE_SIZE, "%d",
+			atomic_read(&qts_data->trusted_touch_event));
+}
+
+static ssize_t trusted_touch_event_store(struct kobject *kobj, struct kobj_attribute *attr,
+				const char *buf, size_t count)
+{
+	struct qts_data *qts_data;
+	unsigned long value;
+	int err = 0;
+	u32 idx = qts_ts_is_primary(kobj) ? 0 : 1;
+
+	if (count > 2)
+		return -EINVAL;
+
+	err = kstrtoul(buf, 10, &value);
+	if (err != 0)
+		return err;
+
+	qts_data = &qts_data_entries->info[idx];
+
+	if (!atomic_read(&qts_data->trusted_touch_initialized))
+		return -EIO;
+
+	if (value)
+		return -EIO;
+
+	atomic_set(&qts_data->trusted_touch_event, value);
+
+	return count;
+}
+
+static ssize_t trusted_touch_type_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+{
+	struct qts_data *qts_data;
+	u32 idx = qts_ts_is_primary(kobj) ? 0 : 1;
+
+	qts_data = &qts_data_entries->info[idx];
+
+	return scnprintf(buf, PAGE_SIZE, "%s", qts_data->vm_info->trusted_touch_type);
+}
+
+static ssize_t trusted_touch_device_path_show(struct kobject *kobj,
+			struct kobj_attribute *attr, char *buf)
+{
+	struct qts_data *qts_data;
+	char *path = NULL;
+	u32 idx = qts_ts_is_primary(kobj) ? 0 : 1;
+
+	qts_data = &qts_data_entries->info[idx];
+
+	if (qts_data && qts_data->dev)
+		path = kobject_get_path(&qts_data->dev->kobj, GFP_KERNEL);
+
+	return scnprintf(buf, PAGE_SIZE, "%s", path ? path : "");
+}
+
+static struct kobj_attribute trusted_touch_enable_attr =
+	__ATTR(trusted_touch_enable, 0664, trusted_touch_enable_show, trusted_touch_enable_store);
+
+static struct kobj_attribute trusted_touch_event_attr =
+	__ATTR(trusted_touch_event, 0664, trusted_touch_event_show, trusted_touch_event_store);
+
+static struct kobj_attribute trusted_touch_type_attr =
+	__ATTR(trusted_touch_type, 0664, trusted_touch_type_show, NULL);
+
+static struct kobj_attribute trusted_touch_device_path_attr =
+	__ATTR(trusted_touch_device_path, 0444, trusted_touch_device_path_show, NULL);
+
+static struct attribute *qts_attributes[] = {
+	&trusted_touch_enable_attr.attr,
+	&trusted_touch_event_attr.attr,
+	&trusted_touch_type_attr.attr,
+	&trusted_touch_device_path_attr.attr,
+	NULL,
+};
+
+static struct attribute_group qts_attribute_group = {
+	.attrs = qts_attributes,
+};
+
+static int qts_create_sysfs(struct qts_data *qts_data)
+{
+	int ret = 0;
+	struct kobject *qts_kobj;
+	struct kobject *client_kobj;
+
+	qts_kobj = &qts_data_entries->qts_kset->kobj;
+
+	if (qts_data->client_type == QTS_CLIENT_PRIMARY_TOUCH) {
+
+		client_kobj = kobject_create_and_add("primary", qts_kobj);
+		if (!client_kobj) {
+			pr_err("primary kobject_create_and_add failed\n");
+			return -ENOMEM;
+		}
+
+		ret = sysfs_create_group(client_kobj, &qts_attribute_group);
+		if (ret) {
+			pr_err("[EX]: sysfs_create_group() failed!!\n");
+			sysfs_remove_group(client_kobj, &qts_attribute_group);
+			return -ENOMEM;
+		}
+	} else if (qts_data->client_type == QTS_CLIENT_SECONDARY_TOUCH) {
+
+		client_kobj = kobject_create_and_add("secondary", qts_kobj);
+		if (!client_kobj) {
+			pr_err("secondary kobject_create_and_add failed\n");
+			return -ENOMEM;
+		}
+
+		ret = sysfs_create_group(client_kobj, &qts_attribute_group);
+		if (ret) {
+			pr_err("[EX]: sysfs_create_group() failed!!\n");
+			sysfs_remove_group(client_kobj, &qts_attribute_group);
+			return -ENOMEM;
+		}
+	}
+
+	pr_debug("sysfs_create_group() succeeded\n");
+	return ret;
+}
+
+static int qts_ts_check_dt(struct device_node *np)
+{
+	int i;
+	int count;
+	struct device_node *node;
+	struct drm_panel *panel;
+
+	count = of_count_phandle_with_args(np, "panel", NULL);
+	if (count <= 0)
+		return 0;
+
+	for (i = 0; i < count; i++) {
+		node = of_parse_phandle(np, "panel", i);
+		panel = of_drm_find_panel(node);
+		of_node_put(node);
+		if (!IS_ERR(panel)) {
+			active_panel = panel;
+			return 0;
+		}
+	}
+	return PTR_ERR(panel);
+}
+
+static void qts_power_source_init(struct qts_data *qts_data)
+{
+	qts_data->vdd = regulator_get(qts_data->dev, "vdd");
+	if (IS_ERR_OR_NULL(qts_data->vdd))
+		pr_debug("get vdd regulator failed\n");
+
+	qts_data->avdd = regulator_get(qts_data->dev, "avdd");
+	if (IS_ERR_OR_NULL(qts_data->avdd))
+		pr_debug("get avdd regulator failed\n");
+}
+
+#ifndef CONFIG_ARCH_QTI_VM
+static int qts_enable_reg(struct qts_data *qts_data, bool enable)
+{
+	int ret = 0;
+
+	if (IS_ERR_OR_NULL(qts_data->vdd)) {
+		pr_err("vdd is invalid\n");
+		return ret;
+	}
+
+	if (enable) {
+		ret = regulator_enable(qts_data->vdd);
+		if (ret) {
+			pr_err("enable vdd regulator failed,ret=%d\n", ret);
+			goto error;
+		}
+
+		if (!IS_ERR_OR_NULL(qts_data->avdd)) {
+			ret = regulator_enable(qts_data->avdd);
+			if (ret) {
+				pr_err("enable avdd regulator failed,ret=%d\n", ret);
+				goto error_avdd_en;
+			}
+		}
+	} else {
+		ret = regulator_disable(qts_data->vdd);
+		if (ret)
+			pr_err("disable vdd regulator failed,ret=%d\n", ret);
+
+		if (!IS_ERR_OR_NULL(qts_data->avdd)) {
+			ret = regulator_disable(qts_data->avdd);
+			if (ret)
+				pr_err("disable avdd regulator failed,ret=%d\n", ret);
+		}
+	}
+
+	pr_debug("enable %d completed\n", enable);
+	return ret;
+
+error_avdd_en:
+	(void)regulator_disable(qts_data->vdd);
+error:
+	return ret;
+}
+
+#endif
+
+static void qts_ts_suspend(struct qts_data *qts_data)
+{
+	int rc = 0;
+
+	if (qts_data->suspended) {
+		pr_warn("already in suspend state\n");
+		return;
+	}
+
+	if (qts_data->tui_supported) {
+		if (atomic_read(&qts_data->trusted_touch_transition)
+			|| atomic_read(&qts_data->trusted_touch_enabled))
+			wait_for_completion_interruptible(&qts_data->trusted_touch_powerdown);
+	}
+	mutex_lock(&qts_data->transition_lock);
+
+	rc = qts_data->vendor_ops.suspend(qts_data->vendor_data);
+	if (rc)
+		pr_err("suspend failed, rc = %d\n", rc);
+
+	qts_data->suspended = true;
+	mutex_unlock(&qts_data->transition_lock);
+}
+
+static void qts_ts_resume(struct qts_data *qts_data)
+{
+	int rc = 0;
+
+	if (!qts_data->suspended) {
+		pr_warn("Already in awake state\n");
+		return;
+	}
+
+	if (qts_data->tui_supported)
+		if (atomic_read(&qts_data->trusted_touch_transition))
+			wait_for_completion_interruptible(&qts_data->trusted_touch_powerdown);
+
+	mutex_lock(&qts_data->transition_lock);
+
+	rc = qts_data->vendor_ops.resume(qts_data->vendor_data);
+	if (rc)
+		pr_err("resume failed, rc = %d\n", rc);
+
+	qts_data->suspended = false;
+	mutex_unlock(&qts_data->transition_lock);
+}
+
+static void qts_resume_work(struct work_struct *work)
+{
+	struct qts_data *qts_data = container_of(work, struct qts_data,
+			resume_work);
+
+	qts_ts_resume(qts_data);
+}
+
+static void qts_suspend_work(struct work_struct *work)
+{
+	struct qts_data *qts_data = container_of(work, struct qts_data,
+			suspend_work);
+
+	qts_ts_suspend(qts_data);
+}
+
+static void qts_panel_notifier_callback(enum panel_event_notifier_tag tag,
+		struct panel_event_notification *notification, void *client_data)
+{
+	struct qts_data *qts_data = client_data;
+
+	if (!notification) {
+		pr_err("Invalid notification\n");
+		return;
+	}
+
+	pr_debug("Notification type:%d, early_trigger:%d\n",
+		notification->notif_type, notification->notif_data.early_trigger);
+
+	switch (notification->notif_type) {
+	case DRM_PANEL_EVENT_UNBLANK:
+		if (notification->notif_data.early_trigger) {
+			pr_debug("resume notification pre commit\n");
+		} else {
+			if (qts_data->schedule_resume)
+				queue_work(qts_data->ts_workqueue, &qts_data->resume_work);
+			else
+				qts_ts_resume(qts_data);
+		}
+		break;
+	case DRM_PANEL_EVENT_BLANK:
+		if (notification->notif_data.early_trigger) {
+			if (qts_data->schedule_resume)
+				cancel_work_sync(&qts_data->resume_work);
+			if (qts_data->schedule_suspend)
+				queue_work(qts_data->ts_workqueue, &qts_data->suspend_work);
+			else
+				qts_ts_suspend(qts_data);
+		} else {
+			pr_debug("suspend notification post commit\n");
+		}
+		break;
+	case DRM_PANEL_EVENT_BLANK_LP:
+		pr_debug("received lp event\n");
+		break;
+	case DRM_PANEL_EVENT_FPS_CHANGE:
+		pr_debug("Received fps change old fps:%d new fps:%d\n",
+			notification->notif_data.old_fps,
+			notification->notif_data.new_fps);
+		break;
+	default:
+		pr_debug("notification serviced :%d\n",
+			notification->notif_type);
+		break;
+	}
+}
+
+static void qts_ts_register_for_panel_events(struct qts_data *qts_data)
+{
+	void *cookie = NULL;
+
+	if (qts_data->client_type != QTS_CLIENT_PRIMARY_TOUCH) {
+		pr_err("Invalid touch type\n");
+		return;
+	}
+
+	cookie = panel_event_notifier_register(PANEL_EVENT_NOTIFICATION_PRIMARY,
+		PANEL_EVENT_NOTIFIER_CLIENT_PRIMARY_TOUCH, qts_data->panel,
+		&qts_panel_notifier_callback, qts_data);
+	if (!cookie) {
+		pr_err("Failed to register for panel events\n");
+		return;
+	}
+
+	pr_debug("registered for panel notifications panel: 0x%x\n", qts_data->panel);
+
+	qts_data->notifier_cookie = cookie;
+}
+
+int qts_client_register(struct qts_vendor_data qts_vendor_data)
+{
+	struct qts_data *qts_data;
+	struct device_node *dp;
+	int rc = 0;
+
+	if (!qts_data_entries) {
+		pr_debug("QTS client register\n");
+		qts_data_entries = kzalloc(sizeof(*qts_data_entries), GFP_KERNEL);
+		if (!qts_data_entries) {
+			pr_err("mem allocation failed\n");
+			return -EPROBE_DEFER;
+		}
+		mutex_init(&qts_data_entries->qts_data_entries_lock);
+		qts_data_entries->qts_kset = kset_create_and_add("qts", NULL, kernel_kobj);
+		if (!qts_data_entries->qts_kset) {
+			pr_err("qts kset create failed\n");
+			return -ENOMEM;
+		}
+	}
+
+	mutex_lock(&qts_data_entries->qts_data_entries_lock);
+
+	if (qts_vendor_data.bus_type == QTS_BUS_TYPE_I2C)
+		dp = qts_vendor_data.client->dev.of_node;
+	else
+		dp = qts_vendor_data.spi->dev.of_node;
+
+	rc = qts_ts_check_dt(dp);
+	if (rc) {
+		pr_debug("qts_ts_check_dt failed, rc = %d\n", rc);
+		goto qts_register_end;
+	}
+
+	pr_debug("QTS client register starts\n");
+	qts_data = &qts_data_entries->info[qts_vendor_data.client_type];
+
+	qts_data->client = qts_vendor_data.client;
+	qts_data->spi = qts_vendor_data.spi;
+
+	if (qts_vendor_data.bus_type == QTS_BUS_TYPE_I2C)
+		qts_data->dev = &qts_data->client->dev;
+	else
+		qts_data->dev = &qts_data->spi->dev;
+
+	qts_data->bus_type = qts_vendor_data.bus_type;
+	qts_data->client_type = qts_vendor_data.client_type;
+	qts_data->dp = dp;
+	qts_data->vendor_data = qts_vendor_data.vendor_data;
+	qts_data->panel = active_panel;
+	qts_data->vendor_ops = qts_vendor_data.qts_vendor_ops;
+	qts_data->schedule_suspend = qts_vendor_data.schedule_suspend;
+	qts_data->schedule_resume = qts_vendor_data.schedule_resume;
+
+	qts_trusted_touch_init(qts_data);
+
+	mutex_init(&(qts_data->qts_clk_io_ctrl_mutex));
+	if (qts_data->tui_supported)
+		qts_create_sysfs(qts_data);
+
+	mutex_init(&qts_data->transition_lock);
+
+#ifdef CONFIG_ARCH_QTI_VM
+	atomic_set(&qts_data->delayed_tvm_probe_pending, 1);
+	goto qts_register_end;
+#else
+	atomic_set(&qts_data->delayed_pvm_probe_pending, 1);
+#endif
+
+	qts_power_source_init(qts_data);
+
+	qts_data->ts_workqueue = create_singlethread_workqueue("qts_wq");
+	if (!qts_data->ts_workqueue)
+		pr_err("create qts workqueue fail\n");
+
+	if (qts_data->ts_workqueue && qts_data->schedule_resume)
+		INIT_WORK(&qts_data->resume_work, qts_resume_work);
+
+	if (qts_data->ts_workqueue && qts_data->schedule_suspend)
+		INIT_WORK(&qts_data->suspend_work, qts_suspend_work);
+
+	qts_ts_register_for_panel_events(qts_data);
+
+qts_register_end:
+	pr_debug("client register end\n");
+	mutex_unlock(&qts_data_entries->qts_data_entries_lock);
+	return rc;
+}
+EXPORT_SYMBOL(qts_client_register);

+ 169 - 0
qcom/opensource/touch-drivers/qts/qts_core.h

@@ -0,0 +1,169 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+#include <linux/soc/qcom/panel_event_notifier.h>
+#include <linux/export.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/spi/spi.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/firmware.h>
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+#include <linux/jiffies.h>
+#include <linux/fs.h>
+#include <linux/proc_fs.h>
+#include <linux/kthread.h>
+#include <linux/dma-mapping.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include "linux/gunyah/gh_msgq.h"
+#include "linux/gunyah/gh_rm_drv.h"
+#include <linux/gunyah/gh_irq_lend.h>
+#include <linux/gunyah/gh_mem_notifier.h>
+#include "qts_core_common.h"
+#include <linux/clk.h>
+#include <linux/kobject.h>
+
+#define QTS_NAME "qti-ts"
+
+enum trusted_touch_mode_config {
+	TRUSTED_TOUCH_VM_MODE,
+	TRUSTED_TOUCH_MODE_NONE
+};
+
+enum trusted_touch_pvm_states {
+	TRUSTED_TOUCH_PVM_INIT,
+	PVM_I2C_RESOURCE_ACQUIRED,
+	PVM_INTERRUPT_DISABLED,
+	PVM_IOMEM_LENT,
+	PVM_IOMEM_LENT_NOTIFIED,
+	PVM_IRQ_LENT,
+	PVM_IRQ_LENT_NOTIFIED,
+	PVM_IOMEM_RELEASE_NOTIFIED,
+	PVM_IRQ_RELEASE_NOTIFIED,
+	PVM_ALL_RESOURCES_RELEASE_NOTIFIED,
+	PVM_IRQ_RECLAIMED,
+	PVM_IOMEM_RECLAIMED,
+	PVM_INTERRUPT_ENABLED,
+	PVM_I2C_RESOURCE_RELEASED,
+	TRUSTED_TOUCH_PVM_STATE_MAX
+};
+
+enum trusted_touch_tvm_states {
+	TRUSTED_TOUCH_TVM_INIT,
+	TVM_IOMEM_LENT_NOTIFIED,
+	TVM_IRQ_LENT_NOTIFIED,
+	TVM_ALL_RESOURCES_LENT_NOTIFIED,
+	TVM_IOMEM_ACCEPTED,
+	TVM_I2C_SESSION_ACQUIRED,
+	TVM_IRQ_ACCEPTED,
+	TVM_INTERRUPT_ENABLED,
+	TVM_INTERRUPT_DISABLED,
+	TVM_IRQ_RELEASED,
+	TVM_I2C_SESSION_RELEASED,
+	TVM_IOMEM_RELEASED,
+	TRUSTED_TOUCH_TVM_STATE_MAX
+};
+
+#define TRUSTED_TOUCH_MEM_LABEL 0x7
+
+#define TOUCH_RESET_GPIO_BASE 0xf118000
+#define TOUCH_RESET_GPIO_SIZE 0x1000
+#define TOUCH_RESET_GPIO_OFFSET 0x4
+#define TOUCH_INTR_GPIO_BASE 0xf119000
+#define TOUCH_INTR_GPIO_SIZE 0x1000
+#define TOUCH_INTR_GPIO_OFFSET 0x8
+
+#define TRUSTED_TOUCH_EVENT_LEND_FAILURE -1
+#define TRUSTED_TOUCH_EVENT_LEND_NOTIFICATION_FAILURE -2
+#define TRUSTED_TOUCH_EVENT_ACCEPT_FAILURE -3
+#define TRUSTED_TOUCH_EVENT_FUNCTIONAL_FAILURE -4
+#define TRUSTED_TOUCH_EVENT_RELEASE_FAILURE -5
+#define TRUSTED_TOUCH_EVENT_RECLAIM_FAILURE -6
+#define TRUSTED_TOUCH_EVENT_I2C_FAILURE -7
+#define TRUSTED_TOUCH_EVENT_NOTIFICATIONS_PENDING 5
+
+struct trusted_touch_vm_info {
+	enum gh_irq_label irq_label;
+	enum gh_mem_notifier_tag mem_tag;
+	enum gh_vm_names vm_name;
+	const char *trusted_touch_type;
+	u32 hw_irq;
+	gh_memparcel_handle_t vm_mem_handle;
+	u32 *iomem_bases;
+	u32 *iomem_sizes;
+	u32 iomem_list_size;
+	void *mem_cookie;
+	atomic_t vm_state;
+};
+
+struct qts_data;
+
+struct qts_data {
+	struct i2c_client *client;
+	struct spi_device *spi;
+	struct device *dev;
+	struct device_node *dp;
+	void *vendor_data; /* vendor touch driver data */
+	u32 bus_type; /*i2c or spi*/
+	u32 client_type; /* primary or secondary */
+	struct drm_panel *panel;
+	struct workqueue_struct *ts_workqueue;
+	struct work_struct resume_work;
+	struct work_struct suspend_work;
+	bool schedule_suspend;
+	bool schedule_resume;
+	void *notifier_cookie;
+
+	/* Resources */
+	struct regulator *vdd;
+	struct regulator *avdd;
+	u32 irq_gpio;
+	u32 irq_gpio_flags;
+	u32 reset_gpio;
+	u32 reset_gpio_flags;
+	int irq;
+	bool irq_disabled;
+	bool power_disabled;
+
+	struct mutex transition_lock;
+	bool suspended;
+
+	/* vendor callback ops */
+	struct qts_vendor_callback_ops vendor_ops;
+
+	/* TUI */
+	bool tui_supported;
+	struct trusted_touch_vm_info *vm_info;
+	struct mutex qts_clk_io_ctrl_mutex;
+	const char *touch_environment;
+	struct completion trusted_touch_powerdown;
+	struct clk *core_clk;
+	struct clk *iface_clk;
+	atomic_t trusted_touch_initialized;
+	atomic_t trusted_touch_enabled;
+	atomic_t trusted_touch_transition;
+	atomic_t trusted_touch_event;
+	atomic_t trusted_touch_abort_status;
+	atomic_t delayed_tvm_probe_pending;
+	atomic_t delayed_pvm_probe_pending;
+	atomic_t trusted_touch_mode;
+};
+
+struct qts_data_entries;
+
+struct qts_data_entries {
+	struct kset *qts_kset;
+	struct qts_data info[QTS_CLIENT_MAX];
+	struct mutex qts_data_entries_lock;
+};
+
+#ifdef CONFIG_ARCH_QTI_VM
+void qts_trusted_touch_tvm_i2c_failure_report(struct qts_data *qts_data);
+#endif

+ 46 - 0
qcom/opensource/touch-drivers/qts/qts_core_common.h

@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+enum qts_client {
+	QTS_CLIENT_PRIMARY_TOUCH,
+	QTS_CLIENT_SECONDARY_TOUCH,
+	QTS_CLIENT_MAX
+};
+
+enum qts_bus_type {
+	QTS_BUS_TYPE_NONE,
+	QTS_BUS_TYPE_I2C,
+	QTS_BUS_TYPE_SPI,
+	QTS_BUS_TYPE_SPI_V2,
+	QTS_BUS_TYPE_MAX
+};
+
+struct qts_vendor_callback_ops {
+	int (*suspend)(void *data);
+	int (*resume)(void *data);
+	int (*enable_touch_irq)(void *data, bool en);
+	irqreturn_t (*irq_handler)(int irq, void *data);
+	int (*get_irq_num)(void *data);
+	int (*pre_la_tui_enable)(void *data);
+	int (*post_la_tui_enable)(void *data);
+	int (*pre_la_tui_disable)(void *data);
+	int (*post_la_tui_disable)(void *data);
+	int (*pre_le_tui_enable)(void *data);
+	int (*post_le_tui_enable)(void *data);
+	int (*pre_le_tui_disable)(void *data);
+	int (*post_le_tui_disable)(void *data);
+};
+
+struct qts_vendor_data {
+	struct i2c_client *client;
+	struct spi_device *spi;
+	void *vendor_data;
+	struct qts_vendor_callback_ops qts_vendor_ops;
+	u32 client_type;
+	u32 bus_type;
+	bool schedule_suspend;
+	bool schedule_resume;
+};
+
+int qts_client_register(struct qts_vendor_data qts_vendor_data);

+ 54 - 0
qcom/opensource/touch-drivers/raydium/Config.h

@@ -0,0 +1,54 @@
+/* config.h
+ *
+ * Raydium TouchScreen driver.
+ *
+ * Copyright (c) 2021  Raydium tech Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __CONFIG_H
+#define __CONFIG_H
+
+
+/*****************************************************************************
+**                            GLOBAL MARCO DEFINITION
+******************************************************************************/
+#define MINIMUM(x, y) (((x) < (y)) ? (x) : (y)) //  Compares two parameters, return minimum.
+#define MAXIMUM(x, y) (((x) > (y)) ? (x) : (y)) //  Compares two parameters, return maximum.
+#define ABS(x) ((x) >= 0 ? (x) : -(x))          // return absolute value
+
+/*****************************************************************************
+**                            GLOBAL FUNCTIONAL DEFINITION
+******************************************************************************/
+
+//Bootloader
+#define BOOTLOADER                          1  //1: with bootloader, 0: without bootloader
+#define SELFTEST                       	     1  //1: For System Selftest, 0:For Dongle Open/Short Tool
+#if SELFTEST
+#define SELFTEST_3X	1
+#define	SELFTEST_2X	0
+#else
+#define SELFTEST_3X	0
+#define	SELFTEST_2X	0
+#endif
+#define ENABLE_TEST_TIME_MEASURMENT		1
+#define ENABLE_TEST_TIME_MEASURMENT_CC		0
+#define ENABLE_WDT 				0
+#define ENABLE_TEST_RSU_DATA_SHOW		0
+#define ENABLE_AUO_VERIFY_LOG			0
+#define ENABLE_CONTROL_OPENSHORT_WDT		0
+#define ENABLE_TEST_GPIO_MEASURMENT		0
+#endif /* end __CONFIG_H */
+/*****************************************************************************
+**                            End Of File
+******************************************************************************/

+ 4 - 0
qcom/opensource/touch-drivers/raydium/Makefile

@@ -0,0 +1,4 @@
+#
+# Makefile for the touchscreen raydium drivers.
+#
+obj-$(CONFIG_TOUCHSCREEN_RM_TS) += raydium_driver.o raydium_sysfs.o raydium_fw_update.o drv_interface.o raydium_selftest.o chip_raydium/ic_drv_global.o chip_raydium/ic_drv_interface.o chip_raydium/f303_ic_control.o chip_raydium/f303_ic_test.o

+ 516 - 0
qcom/opensource/touch-drivers/raydium/chip_raydium/f303_ic_control.c

@@ -0,0 +1,516 @@
+/* f303_ic_control.c
+ *
+ * Raydium TouchScreen driver.
+ *
+ * Copyright (c) 2021  Raydium tech Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifdef __KERNEL__
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/unistd.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/fs.h>
+#include <asm/uaccess.h>
+#else
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#endif
+
+#include "../drv_interface.h"
+#include "ic_drv_global.h"
+#include "ic_drv_interface.h"
+#include "f303_ic_control.h"
+#include "f303_ic_reg.h"
+
+unsigned char check_dev_id_3x(unsigned short u16_dev_id)
+{
+	unsigned int u32_read;
+
+	if (handle_ic_read(REG_FLASHCTL_DEVID_ADDR, 4, (unsigned char *)(&u32_read), g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+		return ERROR;
+	if (((u32_read & 0xFFFF0000) >> 16) == u16_dev_id) {
+		g_u16_dev_id = DEVICE_ID_3X;
+		return SUCCESS;
+	}
+	DEBUGOUT("Device ID NG! 0x%x:0x%x\r\n", ((u32_read & 0xFFFF0000) >> 16), u16_dev_id);
+	return ERROR;
+}
+
+unsigned char check_dev_sub_version_3x(unsigned char u8_version)
+{
+	unsigned int u32_read;
+
+	if (handle_ic_read(REG_FLASHCTL_DEVID_ADDR, 4, (unsigned char *)(&u32_read), g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+		return ERROR;
+	DEBUGOUT("Device Sub Version 0x%x\r\n", u32_read);
+	if ((u32_read & 0x000000FF) == u8_version)
+		return SUCCESS;
+	DEBUGOUT("Device Sub Version NG! 0x%x:0x%x\r\n", (u32_read & 0x000000FF), u8_version);
+	return ERROR;
+}
+
+unsigned char enable_ic_block_3x(void)
+{
+	unsigned int u32_read;
+
+	if (handle_ic_read(REG_SYSCON_BLKEN_ADDR, 4, (unsigned char *)(&u32_read), g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+		return ERROR;
+
+	u32_read |= 0xc8000000;
+	/*u32_read |= (BLKEN_FIC_RB_EN | BLKEN_GPIO_RB_EN | BLKEN_SYS_RB_EN);*/
+	if (handle_ic_write(REG_SYSCON_BLKEN_ADDR, 4, (unsigned char *)&u32_read, g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+		return ERROR;
+
+	if (handle_ic_read(REG_SYSCON_MISCIER_ADDR, 4, (unsigned char *)(&u32_read), g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+		return ERROR;
+
+	u32_read |= 0x00000404;
+	/*u32_read |= (MISCIER_RB_MCU_INTO | MISCIER_RB_MCU_INT_EN);*/
+	if (handle_ic_write(REG_SYSCON_MISCIER_ADDR, 4, (unsigned char *)&u32_read, g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+		return ERROR;
+	return SUCCESS;
+}
+
+unsigned char disable_i2c_deglitch_3x(void)
+{
+	unsigned int u32_buf = 0;
+	unsigned char u8_retry = 3, u8_comfirm_time = 3;
+	unsigned char u8_check = 0, u8_i = 0;
+	unsigned int u32_i2c_deglitch = I2CENG_AUTO_I2C_DGF_MODE2 | I2CENG_DGF_OSC_AUTOSEL | I2CENG_AUTO_I2C_DGF_MODE | I2CENG_I2CS_DGFEN_DLY_NUM(6);
+
+	/*check I2C mode*/
+	while (u8_retry--) {
+		u32_buf = 0;
+		handle_ic_read(REG_FLASHCTL_DEVID_ADDR, 4, (unsigned char *)(&u32_buf), g_u8_drv_interface, I2C_WORD_MODE);
+		if ((u32_buf & 0xFFFF0000) == 0xF3030000)
+			u8_check++;
+		else
+			break;
+	}
+	if (u8_check == 3) {
+		if (!g_u8_mute_i2c_err_log)
+			DEBUGOUT("PDA2 OK\r\n");
+		return SUCCESS;
+	}
+
+	u8_retry = 100;
+	while (u8_retry--) {
+		if (handle_ic_write(REG_I2CENG_ADDR, 4, (unsigned char *)(&u32_i2c_deglitch), I2C_PDA_MODE, I2C_WORD_MODE) == ERROR) {
+			/*DEBUGOUT("[disable_i2c_deglitch_3x] handle_ic_write I2C NG!\r\n");*/
+			if (!g_u8_mute_i2c_err_log)
+				DEBUGOUT("[DI2CDG]-W");
+			continue;
+		}
+
+		u8_check = 0;
+		for (u8_i = 0; u8_i < u8_comfirm_time; u8_i++) {
+			/*check I2C*/
+			u32_buf = 0;
+			if (handle_ic_read(REG_I2CENG_ADDR, 4, (unsigned char *)(&u32_buf), I2C_PDA_MODE, I2C_WORD_MODE) == ERROR) {
+				/*DEBUGOUT("[disable_i2c_deglitch_3x] 2.handle_ic_read I2C NG!\r\n");*/
+				if (!g_u8_mute_i2c_err_log)
+					DEBUGOUT("[DI2CDG]-R");
+				break;
+			}
+
+			if (u32_buf == u32_i2c_deglitch)
+				u8_check++;
+			else
+				break;
+		}
+
+		if (u8_check == u8_comfirm_time)
+			break;
+	}
+	if (!g_u8_mute_i2c_err_log)
+		DEBUGOUT("\r\n");
+
+	if (u8_retry == 0)
+		return ERROR;
+
+	u32_buf = GPIO_DEGLITCH_EN(3);
+	if (handle_ic_write(REG_GPIO_DEGLITCH_ENABLE, 4, (unsigned char *)(&u32_buf), I2C_PDA_MODE, I2C_WORD_MODE) == ERROR) {
+		DEBUGOUT("[DI2CDG_3x] 3.handle_ic_write I2C NG!\r\n");
+		return ERROR;
+	}
+
+	/*Enable PDA2*/
+	u32_buf = PDA2CTL_PDA2_EN | PDA2CTL_SIE2;
+	if (handle_ic_write(REG_PDA2CTL_ADDR, 4, (unsigned char *)(&u32_buf),  I2C_PDA_MODE, I2C_WORD_MODE) == ERROR) {
+		DEBUGOUT("[DI2CDG_3x] 4.i2c_write_pda I2C NG!\r\n");
+		return ERROR;
+	}
+
+	/*Disable PDA*/
+#if SELFTEST
+#ifdef __KERNEL__
+	raydium_i2c_pda_set_address(RAYDIUM_PDA_I2CREG, DISABLE);
+#else
+	sysfs_mode_control(ENABLE_PDA2);
+#endif
+#else
+	i2c_set_pda_address(I2C_EID, 0x500006, I2C_PDA2_WORD_MODE);
+#endif
+	return SUCCESS;
+}
+
+unsigned char stop_mcu_3x(unsigned char u8_is_tp_reset)
+{
+	unsigned short u16_time_out = 100;
+	unsigned int u32_read_data;
+	unsigned int u32_write = 0;
+
+	g_u8_mute_i2c_err_log = TRUE;
+	if (u8_is_tp_reset) {
+		gpio_touch_hw_reset();
+		delay_ms(35);
+		if (disable_i2c_deglitch_3x() == ERROR) {
+			DEBUGOUT("[%s] 2.DI2CDG NG!\r\n", __func__);
+			goto EXIT_ERROR;
+		}
+	}
+
+	/*DEBUGOUT("[stop_mcu_3x] 1\r\n");*/
+
+	/*Stop MCU*/
+	/*memset(wData, 0, sizeof(wData));*/
+	u32_write = (MCU_HOLD | SKIP_LOAD);
+	if (handle_ic_write(REG_FLASHCTL_FLASH_STATE_REG_ADDR, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+		goto EXIT_ERROR;
+
+	u32_write = BLKRST_SW_RST;
+	if (handle_ic_write(REG_SYSCON_BLKRST_ADDR, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+		goto EXIT_ERROR;
+	delay_ms(20);
+
+	if (disable_i2c_deglitch_3x() == ERROR) {
+		/*DEBUGOUT("[stop_mcu_3x] 3.disable_i2c_deglitch_3x NG!\r\n");*/
+		DEBUGOUT("[%s] 3.DI2CDG NG!\r\n", __func__);
+		goto EXIT_ERROR;
+	}
+	/*DEBUGOUT("[stop_mcu_3x] 2\r\n");*/
+
+	if (handle_ic_read(REG_FLASHCTL_FLASH_STATE_REG_ADDR, 4, (unsigned char *)(&u32_read_data), g_u8_drv_interface, I2C_WORD_MODE) == ERROR) {
+		DEBUGOUT("[%s] 4.Flash State NG!\r\n", __func__);
+		goto EXIT_ERROR;
+	}
+
+	while ((u32_read_data & MCU_HOLD_STATUS) == 0 && u16_time_out-- > 0) {
+		delay_ms(10);
+		if (handle_ic_read(REG_FLASHCTL_FLASH_STATE_REG_ADDR, 4, (unsigned char *)(&u32_read_data), g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+			goto EXIT_ERROR;
+
+#if ENABLE_WDT
+		Chip_WWDT_Feed(LPC_WWDT);/* reload the Watchdog timer*/
+#endif
+	}
+
+	DEBUGOUT("Stop MCU=0x%X(0x%x)(%d)!!\r\n", u32_read_data, (u32_read_data & MCU_HOLD_STATUS), u16_time_out);
+
+	if ((u32_read_data & MCU_HOLD_STATUS) == 0) {
+		DEBUGOUT("[%s] 4.STOP MCU NG!\r\n", __func__);
+		goto EXIT_ERROR;
+	}
+
+	g_u8_mute_i2c_err_log = FALSE;
+	return SUCCESS;
+
+EXIT_ERROR:
+	g_u8_mute_i2c_err_log = FALSE;
+	return ERROR;
+}
+
+unsigned char hardware_reset_3x(unsigned char u8_enable_ic_block)
+{
+	unsigned char u8_time_out = 200;
+
+	DEBUGOUT("HW Reseting...\r\n");
+
+	gpio_touch_hw_reset();
+	delay_ms(100);
+
+	g_u8_mute_i2c_err_log = TRUE;
+	if (disable_i2c_deglitch_3x() == ERROR) {
+		/*DEBUGOUT("[hardware_reset_3x] disable_i2c_deglitch_3x NG!\r\n");*/
+		DEBUGOUT("[%s] DI2CDG NG!\r\n, __func__");
+		g_u8_mute_i2c_err_log = FALSE;
+		return ERROR;
+	}
+	g_u8_mute_i2c_err_log = FALSE;
+
+	if (u8_enable_ic_block) {
+		if (enable_ic_block_3x() == ERROR) {
+			DEBUGOUT("HW Reset NG!!\r\n");
+			return ERROR;
+		}
+	}
+	while (u8_time_out--) {
+		if (gpio_touch_int_pin_state_access())
+			break;
+		if (u8_time_out == 0)
+			return ERROR;
+		delay_ms(1);
+	}
+	return SUCCESS;
+}
+
+unsigned char set_fw_system_cmd_3x(unsigned int u32_sysm_cmd)
+{
+	unsigned char u8_time_out = 100, u8_value;
+
+	if (handle_ic_write(FW_SYS_CMD_ADDR, 4, (unsigned char *)&u32_sysm_cmd, g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+		return ERROR;
+
+	/* Wait Test Command ready*/
+	while (--u8_time_out) {
+		delay_ms(1);
+		if (handle_ic_read(FW_SYS_CMD_ADDR, 1, &u8_value, g_u8_drv_interface, I2C_BYTE_MODE) == ERROR)
+			return ERROR;
+		else if (u8_value == 0)
+			break;
+	}
+
+	if (u8_time_out == 0)
+		return ERROR;
+
+	return SUCCESS;
+}
+
+unsigned char wait_fw_state_3x(unsigned int u32_addr, unsigned int u32_state, unsigned short u16_delay, unsigned short u16_retry)
+{
+	unsigned int u32_read_data = 0;
+
+	do {
+		if (handle_ic_read(u32_addr, 4, (unsigned char *)(&u32_read_data), g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+			return ERROR;
+
+		delay_ms(u16_delay);
+		u16_retry--;
+
+#if ENABLE_WDT
+		/* reload the Watchdog timer*/
+		Chip_WWDT_Feed(LPC_WWDT);
+#endif
+	} while ((u32_read_data != u32_state) && (u16_retry != 0));
+
+	if (u32_read_data != u32_state)
+		return ERROR;
+	return SUCCESS;
+}
+
+unsigned char wait_T2D_done_state_3x(unsigned int u32_addr, unsigned short u16_delay, unsigned short u16_retry)
+{
+	unsigned int u32_read_data = 0;
+
+	do {
+		if (handle_ic_read(u32_addr, 4, (unsigned char *)(&u32_read_data), g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+			return ERROR;
+		/*DEBUGOUT("[T2DW] ready 0x%x\r\n", u32_read_data);*/
+
+		delay_ms(u16_delay);
+		u16_retry--;
+	} while (((u32_read_data & 0x01) == 0) && (u16_retry != 0));
+
+	if ((u32_read_data & 0x01) == 0)
+		return ERROR;
+	return SUCCESS;
+}
+
+unsigned char WriteToDriver_3x(unsigned char *p_u8_data, unsigned char u8_data_length)
+{
+	unsigned int u32_write = 0;
+
+
+	if (p_u8_data != NULL) {
+
+		if (p_u8_data[0] == 0xFE) {
+			g_u8_PAGE_ADDR = p_u8_data[1];
+			return SUCCESS;
+		}
+
+		if (p_u8_data[0] == 0x35) {
+			u32_write = ((0x00 << 24) | (0x00 << 16) | (p_u8_data[0] << 8) | g_u8_PAGE_ADDR);
+			if (handle_ic_write(REG_T2D_CONFIG_2, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+				return ERROR;
+			u32_write  = 0x0000015A;
+			if (handle_ic_write(REG_T2D_CONFIG_3, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+				return ERROR;
+		}
+
+		if (u8_data_length == 1)  /*cmd mode*/
+			u32_write = ((0x00 << 24) | (p_u8_data[1] << 16) | (p_u8_data[0] << 8) | g_u8_PAGE_ADDR);
+		else if (u8_data_length == 2)
+			u32_write = ((0x01 << 24) | (p_u8_data[1] << 16) | (p_u8_data[0] << 8) | g_u8_PAGE_ADDR);
+		else if (u8_data_length == 5) {
+			/* 2A 2B*/
+
+			u32_write = ((0x01 << 24) | (p_u8_data[1] << 16) | (p_u8_data[0] << 8) | 0x00);
+			if (handle_ic_write(REG_T2D_CONFIG_2, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+				return ERROR;
+			u32_write  = 0x0000015A;
+			if (handle_ic_write(REG_T2D_CONFIG_3, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+				return ERROR;
+
+			u32_write = ((0x01 << 24) | (p_u8_data[2] << 16) | (p_u8_data[0] << 8) | 0x01);
+			if (handle_ic_write(REG_T2D_CONFIG_2, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+				return ERROR;
+			u32_write  = 0x0000015A;
+			if (handle_ic_write(REG_T2D_CONFIG_3, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+				return ERROR;
+
+			u32_write = ((0x01 << 24) | (p_u8_data[3] << 16) | (p_u8_data[0] << 8) | 0x02);
+			if (handle_ic_write(REG_T2D_CONFIG_2, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+				return ERROR;
+			u32_write  = 0x0000015A;
+			if (handle_ic_write(REG_T2D_CONFIG_3, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+				return ERROR;
+
+			u32_write = ((0x01 << 24) | (p_u8_data[4] << 16) | (p_u8_data[0] << 8) | 0x03);
+			if (handle_ic_write(REG_T2D_CONFIG_2, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+				return ERROR;
+			u32_write  = 0x0000015A;
+			if (handle_ic_write(REG_T2D_CONFIG_3, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+				return ERROR;
+
+			return SUCCESS;
+		}
+
+		if (handle_ic_write(REG_T2D_CONFIG_2, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+			return ERROR;
+		u32_write  = 0x0000015A;
+		if (handle_ic_write(REG_T2D_CONFIG_3, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+			return ERROR;
+	}
+	return SUCCESS;
+}
+
+unsigned char ReadFromDriver_3x(unsigned char *p_u8_addr, unsigned char u8_read_len, unsigned char *p_u8_output_buf)
+{
+	unsigned int u32_write = 0;
+
+	if ((p_u8_addr[0] == 0xFE) || (p_u8_addr[0] == 0xFF) || (u8_read_len > 1)) {
+		DEBUGOUT("[%s] no use\r\n", __func__);
+		return FALSE;
+	}
+
+	u32_write = ((p_u8_addr[0] << 24) | (g_u8_PAGE_ADDR << 16) | (0x01 << 8) | 0x82);
+	DEBUGOUT("read address 0x%x\r\n", u32_write);
+	if (handle_ic_write(REG_T2D_R_CONFIG_1, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+		return ERROR;
+	if (handle_ic_read(REG_T2D_R_CONFIG_2, 4, p_u8_output_buf, g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+		return ERROR;
+
+	DEBUGOUT("read data 0x%x\r\n", p_u8_output_buf[0]);
+	return TRUE;
+
+}
+
+unsigned char WriteDriverByTouchMode(const unsigned char *p_u8_data, uint16_t u16DataLength)
+{
+	unsigned short TotalDataLen = (p_u8_data[3] << 8) + p_u8_data[4] - 7;/*Valid data not include 7 byte header*/
+	unsigned short u16_i = 0/*, u8_j = 0*/;
+	unsigned char  u8_CurCmdLen = 0;
+	unsigned int u32_write = 0;
+
+/*
+ *	unsigned char u8_read_buf[4];
+ *	signed char i8_retry_cnt = 3;
+ *	unsigned char u8_error_flag = 0;
+ */
+
+	for (u16_i = 0; u16_i < (u16DataLength - 7) && u16_i < sizeof(g_u8_data_buf); u16_i++)
+		g_u8_data_buf[u16_i] = p_u8_data[u16_i + 7];
+	if (p_u8_data[0] == 0xF3 && p_u8_data[1] == 0x01) {
+		/*Check Command type*/
+		DEBUGOUT("Write Key\r\n");
+		u32_write  = 0x015AFABC;
+		if (handle_ic_write(REG_T2D_CONFIG_2, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+			return ERROR;
+		u32_write  = 0x0000015A;
+		if (handle_ic_write(REG_T2D_CONFIG_3, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+			return ERROR;
+
+		if (p_u8_data[2] == 0x01 ||
+		    p_u8_data[2] == 0x10) {			/*SPI short multiple command*/
+			for (u16_i = 0; u16_i < TotalDataLen; u16_i += (u8_CurCmdLen + 1)) {
+				u8_CurCmdLen = g_u8_data_buf[u16_i];
+
+/*
+ *				DEBUGOUT(" i = %d\r\n", u16_i);
+ *				if (u8_CurCmdLen == 2)
+ *	DEBUGOUT("-Before Parse Data[%d] = 0x%02X, 0x%02X, 0x%02X, %d\r\n", u16_i, g_u8_data_buf[u16_i], g_u8_data_buf[u16_i + 1], g_u8_data_buf[u16_i + 2], u8_CurCmdLen);
+ *				else if (u8_CurCmdLen == 3)
+ *	DEBUGOUT("-Before Parse Data[%d] = 0x%02X, 0x%02X, 0x%02X, 0x%02X, %d\r\n", u16_i, g_u8_data_buf[u16_i], g_u8_data_buf[u16_i + 1], g_u8_data_buf[u16_i + 2], g_u8_data_b	 uf[u16_i + 3], u8_CurCmdLen);
+ *	else
+ *	DEBUGOUT("-Before Parse Data[%d] = 0x%02X, 0x%02X, %d\r\n", u16_i, g_u8_data_buf[u16_i], g_u8_data_buf[u16_i + 1], u8_CurCmdLen);
+ */
+
+				/*Check is Delay command*/
+				if (g_u8_data_buf[u16_i + 1] != 0xFF) { /*Check Command ID*/
+
+					if (wait_T2D_done_state_3x(0x50001220, 1, 100) == ERROR) {
+						DEBUGOUT("[T2DW] Check T2D idle Fail\r\n");
+						return ERROR;
+					}
+
+					WriteToDriver_3x((unsigned char *)&g_u8_data_buf[u16_i + 1], u8_CurCmdLen);
+					/*delay_ms(1);*/
+/*
+ *					i8_retry_cnt = 3;
+ *					while (i8_retry_cnt) {
+ *						u8_error_flag = 0;
+ *						if (ReadFromDriver_3x((unsigned char *)&g_u8_data_buf[u16_i + 1], u8_CurCmdLen - 1 , u8_read_buf)) {
+ *							for (u8_j = 0 ; u8_j < u8_CurCmdLen - 1 ; u8_j++) {
+ *								if (g_u8_data_buf[u16_i + 2 + u8_j] != u8_read_buf[u8_j]) {
+ *									u8_error_flag = 1;
+ *									DEBUGOUT("write address:%x\r\n", g_u8_data_buf[u16_i + 1]);
+ *									DEBUGOUT("Data:%d\r\n", g_u8_data_buf[u16_i + 2 + u8_j]);
+ *									DEBUGOUT("g_u8_spi_cmd_read_buf[%d]:%x\r\n", u8_j, u8_read_buf[u8_j]);
+ *								}
+ *							}
+ *							if (u8_error_flag == 0)
+ *								break;
+ *							if (u8_error_flag == 1)
+ *								i8_retry_cnt--;
+ *							if (i8_retry_cnt == 0) {
+ *								DEBUGOUT("i8_retry_cnt error!\r\n");
+ *								return ERROR;
+ *							}
+ *						} else
+ *							break;
+ *					}
+ */
+				} else {
+					DEBUGOUT("Delay\r\n");
+					delay_ms(g_u8_data_buf[u16_i + 2]);
+				}
+			}
+		} else {
+			DEBUGOUT("[%s] command type not support\r\n", __func__);
+			return ERROR;
+		}
+		u32_write  = 0x0100FABC;
+		if (handle_ic_write(REG_T2D_CONFIG_2, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+			return ERROR;
+		u32_write  = 0x0000015A;
+		if (handle_ic_write(REG_T2D_CONFIG_3, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+			return ERROR;
+	}
+	return SUCCESS;
+}
+

+ 31 - 0
qcom/opensource/touch-drivers/raydium/chip_raydium/f303_ic_control.h

@@ -0,0 +1,31 @@
+/* f303_ic_control.h
+ *
+ * Raydium TouchScreen driver.
+ *
+ * Copyright (c) 2021  Raydium tech Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "ic_drv_global.h"
+
+#define HEADER_LENGTH                            (4)//length + checksum
+
+extern unsigned char enable_ic_block_3x(void);
+extern unsigned char stop_mcu_3x(unsigned char u8_is_tp_reset);
+extern unsigned char hardware_reset_3x(unsigned char u8_enable_ic_block);
+extern unsigned char check_dev_id_3x(unsigned short u16_dev_id);
+extern unsigned char check_dev_sub_version_3x(unsigned char u8_version);
+extern unsigned char set_fw_system_cmd_3x(unsigned int u32_sysm_cmd);
+extern unsigned char  wait_fw_state_3x(unsigned int u32_addr, unsigned int u32_state, unsigned short u16_delay, unsigned short u16_retry);
+extern unsigned char disable_i2c_deglitch_3x(void);
+extern unsigned char WriteDriverByTouchMode(const unsigned char *p_u8_data, uint16_t u16DataLength);

+ 281 - 0
qcom/opensource/touch-drivers/raydium/chip_raydium/f303_ic_reg.h

@@ -0,0 +1,281 @@
+/* f303_ic_reg.h
+ *
+ * Raydium TouchScreen driver.
+ *
+ * Copyright (c) 2021  Raydium tech Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+ // ['h5000_0900], [32'hFFFF_FFFF], PRAM_LOCK
+#define I2CTB_LOCK                          (0x00000001<<6)      // [0] (R/W) I2C Table lock
+#define BOTLR_LOCK                          (0x00000001<<5)      // [0] (R/W) Boot loader lock
+#define USEFW_LOCK                          (0x00000001<<4)      // [0] (R/W) User fw lock
+#define CONFIG_LOCK                         (0x00000001<<3)      // [0] (R/W) Configuration lock
+#define COMP_LOCK                           (0x00000001<<2)      // [0] (R/W) compensation lock
+#define BASEL_LOCK                          (0x00000001<<1)      // [0] (R/W) baseline lock
+#define INICO_LOCK                          (0x00000001<<0)      // [0] (R/W) Initial code lock
+
+// ['h5000_0904], [32'h0000_0000], Program RAM store type, PRAM_STORE_TYPE
+#define BOTLR_AREA                          (0x00000001<<5)      // [0] (R/W) Boot loader  area
+#define USEFW_AREA                          (0x00000001<<4)      // [0] (R/W) User fw area
+#define CONFIG_AREA                         (0x00000001<<3)      // [0] (R/W) Configuration area
+#define COMP_AREA                           (0x00000001<<2)      // [0] (R/W) compensation area
+#define BASEL_AREA                          (0x00000001<<1)      // [0] (R/W) baseline area
+#define INICO_AREA                          (0x00000001<<0)      // [0] (R/W) Initial code area
+
+// ['h5000_0918], [32'h0000_0000],Flash state control register ,FLASH_STATE_REG
+#define BLDR_FINISH							(0x00000001<<14)	// [0] (R) MCU_HOLD status
+#define MCU_HOLD_STATUS						(0x00000001<<13)	// [0] (R) MCU_HOLD status
+#define BOOT_REGION							(0x00000001<<12)	// [0] (R) boot region index
+#define BL_CRC_CHK							(0x00000001<<11)	// [0] (R/W) boot-loader area CRC check
+#define FW_CRC_CHK							(0x00000001<<10)	// [0] (R/W) user FW CRC check
+#define PARA_CRC_CHK						(0x00000001<<9)		// [0] (R/W) parameter area CRC check
+#define COMP_CRC_CHK						(0x00000001<<8)		// [0] (R/W) compensation area CRC check
+#define BASELINE_CRC_CHK					(0x00000001<<7)		// [0] (R/W) baseline area CRC check
+#define INITIAL_CRC_CHK						(0x00000001<<6)		// [0] (R/W) initial code CRC check
+#define MCU_HOLD							(0x00000001<<5)		// [0] (R/W) MCU hold
+#define SKIP_LOAD  							(0x00000001<<4)		// [0] (R/W) Skip all load flash action
+#define FW_INICO_ERR						(0x00000001<<3)		// [0] (R) FW_INICO_ERR
+#define FW_CG_ERR    						(0x00000001<<2)		// [0] (R) cc bl CRC error
+#define FW_CRC_ERR							(0x00000001<<1)		// [0] (R) FW_CRC_ERR
+#define BL0_CRC_ERR							(0x00000001<<0)		// [0] (R) BL0_CRC_ERR
+
+// ['h5000_0934], [32'h0000_0000], FLASH Lock and Key Register (main) (FLKEY1)
+#define FLKEY1_LOCK                     	(0x00000000<<0)      // [7:0] (R/W) FLASH Lock and Key1 Register
+#define FLKEY1_KEY                      	(0x000000A5<<0)      // [7:0] (R/W) FLASH Unlock and Key1 Register:0xA5
+
+// ['h5000_0938], [32'h0000_0000], FLASH Lock and Key Register (information) (FLKEY2)
+#define FLKEY2_LOCK                     	(0x00000000<<0)      // [7:0] (R/W) FLASH Lock and Key3 Register
+#define FLKEY2_KEY                      	(0x000000D7<<0)      // [7:0] (R/W) FLASH Unlock and Key3 Register:0xD7
+
+//#define MAX_SENSING_PIN_NUM  				30
+
+// ['h5000_0610], [32'h0106_0300], I2C eng Register (I2CENG)
+#define REG_I2CENG_ADDR						0x50000610
+#define I2CENG_AUTO_I2C_DGF_MODE2			(0x00000001<<26)					// [26:26]		(R/W)	auto_i2c_dgf_mode2,, 1: switch def_en after next posedge SCL_dgf, 0: switch def_en control auto_i2c_dgf_mode (bit[24]).
+#define I2CENG_DGF_OSC_AUTOSEL				(0x00000001<<25)					// [25:25]		(R/W)	dgf_osc_autosel,, 0: TP OSC control, 1: TP OSC/DRIVER OSC control
+#define I2CENG_AUTO_I2C_DGF_MODE				(0x00000001<<24)					// [24:24]		(R/W)	dgf_en signal delay timing select 0: switch def_en after STOP signal 1: switch def_en after next posedge SCL.
+#define I2CENG_I2CS_DGFEN_DLY_NUM(u7x)		((u7x&0x0000007F)<<16)				// [22:16]		(R/W)	dgf_en signal delay number (clock by system clock ).
+#define I2CENG_I2CS_DEGFIR_NUM(u7x)			((u7x&0x0000007F)<<8)				// [14:8]		(R/W)	I2C Pad (SCL/SDA) deglitch filter number.
+#define I2CENG_RB_MANUAL_I2C_DGF				(0x00000001<<7)						// [7:7]		(R/W)	I2C Pad (SCL/SDA) deglitch filter manual mode control 1: Enable 0: Disable.
+#define I2CENG_RB_I2C_DGF_EN					(0x00000001<<6)						// [6:6]		(R/W)	I2C Pad (SCL/SDA) deglitch filter enable control register when rb_manual_i2c_dgf =1, 1: Enable 0: Disable.
+#define I2CENG_FIRST_DAT_SEL(u2x)				((u2x&0x00000003)<<0)				// [1:0]		(R/W)	First data request select for PDA2 read. 0: at 2nd SCL; 1: at 3th SCL; 2:at 4th SCL; 3:at 5th SCL.
+
+#define REG_I2C_I2CFLASHPRO					0x50000624
+
+//['h5000_0628], [32'h0000_0000], PDA2 Control Register (PDA2CTL)
+#define REG_PDA2CTL_ADDR						0x50000628
+#define PDA2CTL_PDA2_EN						(0x00000001UL<<2)					// [2:2]		(R/W)	PDA2 enable bit. 1: enable 0: disable.
+#define PDA2CTL_SIE2							(0x00000001<<1)						// [1:1]		(R/W)	SIE2 enable register . 1: enable 0: mask.
+#define PDA2CTL_SI2								(0x00000001<<0)						// [0:0]		(R/W)	SI2 interrupt flag (write 1 to clear).
+
+
+//['h5000_0E1C], [32'h0000_0000], GPIO deglitch enable(GPIO_DEGLITCH)
+#define REG_GPIO_DEGLITCH_ENABLE				0x50000E1C
+#define GPIO_PULLH_EN(u2x)						((u2x&0x00000003)<<0)				// [3:2]		(R/W)	1: enable pull-high of GPIO, 0: disable pull-high of GPIO
+#define GPIO_DEGLITCH_EN(u2x)					((u2x&0x00000003)<<0)				// [1:0]		(R/W)	1: enable deglitch function of GPIO, 0: disable deglitch function of GPIO
+
+#define REG_SYSCON_BLKEN_ADDR				0x40000000
+#define REG_SYSCON_BLKRST_ADDR				0x40000004
+//#define REG_SYSCON_MISCIER_ADDR				0x40000014
+
+#define REG_T2D_CONFIG_1					0x5000145c
+#define REG_T2D_CONFIG_2					0x50001460
+#define REG_T2D_CONFIG_3					0x50001464
+#define REG_T2D_R_CONFIG_1					0x50001468
+#define REG_T2D_R_CONFIG_2					0x5000146C
+
+#define MCU_HOLD                    		(0x00000001<<5)		// [0] (R/W) MCU hold
+#define SKIP_LOAD                   		(0x00000001<<4)		// [0] (R/W) Skip all load flash action
+#define BLKRST_SW_RST               		(0x00000001<<0)      	// [0] (R/W) 1: Software reset, all digital block will be reset
+#define MCU_HOLD_STATUS             		(0x00000001<<13)	// [0] (R) MCU_HOLD status
+#define FLH_RELEASE_PD              		(0x00000001<<5)      	// [0] (R/W) Release from deep power down mode
+#define BL_CRC_CHK                  		(0x00000001<<11)	// [0] (R/W) boot-loader area CRC check
+
+/* Base addresses                                                             */
+#define RM_PRAM_BASE                    	(0x00000000UL)             // Program, AHB
+#define RM_RAM_BASE                     	(0x20000000UL)             // SRAM, AHB
+#define RM_AHB_BASE                     	(0x40000000UL)             // Peripheral, AHB
+#define RM_APB_BASE                     	(0x50000000UL)             // Peripheral, APB
+
+//#define FW_SYS_CMD_ADDR             		0x20000288
+#define FW_FT_CMD_ADDR              		0x20000289
+#define FW_FT_ARG0_ADDR             		0x2000028A
+#define FW_FT_ARG1_ADDR             		0x2000028C
+//#define FW_FT_IMG_ADDR              		0x2000019C
+//#define FW_TP_SEQ_NUM_ADDR          		0x20000290
+
+#define SYS_CMD_FUNC_DIS_BS_UPDATE  		0x20
+#define DIS_BASELINE_UPDATE         		0x00010000
+
+#define SYS_CMD_DO_BL_CAL                   0x5A
+#define SYS_CMD_READ_CAL_FLAG               0x5B
+#define SYS_CMD_DO_CC_CAL                   0x5C
+#define SYS_CMD_CAL_WAIT                    0x5D
+
+#define SYS_CMD_WAKEUP_GESTURE_ENABLE       0x40
+#define SYS_CMD_WAKEUP_GESTURE_DISABLE      0x41
+
+#define SYS_CMD_FT_GET_DSP_NS_PARAM         0x60
+#define SYS_CMD_FT_FUN_FLAG                 0x62
+#define SYS_CMD_FT_DC_DISABLE          	 	0x010000
+#define SYS_CMD_FT_DIG_GAIN_ENABLE      	0x020000
+#define SYS_CMD_FT_TEST_LOG_EN          	0x800000
+
+#define FW_FT_CHANNEL_X_ADDR        		(PRAM_PARA_START + 24)
+#define FW_FT_CHANNEL_Y_ADDR        		(PRAM_PARA_START + 25)
+#define FW_FT_PIN_ADDR              		(PRAM_PARA_START + 27)
+#define FW_FT_PWR_MODE_ADDR         		(PRAM_PARA_START + 26)
+#define FW_FT_FW_VERSION            		(PRAM_PARA_START + 4)
+#define FW_FT_SRAM_FW_VERSION       		0x200006E0
+
+
+#define BOOT_SYNC_DATA_ADDR					0x20000200
+#define BOOT_MAIN_STATE_ADDR				0x20000204
+#define BOOT_NORMAL_STATE_ADDR				0x20000208
+#define BOOT_BURNING_STATE_ADDR				0x2000020C
+#define BOOT_CMD_TYPE_ADDR					0x20000210
+#define BOOT_RET_DATA_ADDR					0x20000214
+#define BOOT_TEST_MODE_ADDR					0x20000218
+
+#define FLASH_OFFSET                    	(0x7800)
+#define PRAM_BASELINE_LENGTH            	(0x130)
+#define PRAM_COMP_LENGTH                	(0x304)
+#define PRAM_DIS_INIT_LENGTH            	(0x80)
+#define PRAM_PARA_LENGTH                	(0x174)
+#define PRAM_FW_LENGTH                  	(0x7300)
+#define PRAM_BOOT_LENGTH                	(0x800)
+
+#define PRAM_BOOT_START                 	(0x0000)
+#define PRAM_DIS_INIT_START             	(0x7F80)
+#define PRAM_COMP_START                 	(0x7C78)
+#define PRAM_PARA_START	                	(0x7B00)
+#define PRAM_FW_START                   	(0x0800)
+#define PRAM_RESERVE_START              	(0x0800)
+#define PRAM_PARA_DC_THD_ADDR           	(PRAM_PARA_START + 136)
+#define PRAM_CC_TABLE_ADDR              	(0x7F78)
+
+#define PRAM_BASEINE_START 					(0x6CCC)
+#define PRAM_BOOT_CRC_LENGTH				(PRAM_BOOT_LENGTH - HEADER_LENGTH)
+#define PRAM_FW_CRC_START					(0x6B5C)
+#define PRAM_FW_CRC_LENGTH					(PRAM_FW_LENGTH + PRAM_PARA_LENGTH)	//0x7474
+#define PRAM_CB_CRC_START					(0x6DFC)
+#define PRAM_CB_CRC_LENGTH					(PRAM_COMP_LENGTH + PRAM_BASELINE_LENGTH)
+
+
+#define FT_RAWDATA1_SHORT_BUF_ADDR  		0x200002E4                        //((at(0x200002E4)));
+#define FT_RAWDATA2_OPEN_BUF_ADDR   		(FT_RAWDATA1_SHORT_BUF_ADDR+100)     //((at(0x20000348)));
+#define FT_RAWDATA3_CC_BUF_ADDR     		(FT_RAWDATA2_OPEN_BUF_ADDR+100)      //((at(0x200003AC)));
+#define FT_UC_BUF_ADDR              		(FT_RAWDATA3_CC_BUF_ADDR+100)        //((at(0x20000410)));
+
+#define FT_OPEN_BL_BUF_ADDR         		(FT_UC_BUF_ADDR+100)                 //((at(0x20000474)));
+#define FT_TEST_RESULT_BUF_ADDR     		(FT_OPEN_BL_BUF_ADDR+100)           //((at(0x200004D8)));
+#define FT_TEST_ITEM_RESULT         		(FT_TEST_RESULT_BUF_ADDR+50+2)       //((at(0x2000050C)));
+#define FT_TEST_INFO_ADDR           		0x20000674//FT_IMG2PIN_BUF_ADDR+72//((at(0x20000674)));
+#define FT_TEST_THD_ADDR            		(FT_TEST_INFO_ADDR+16)              //((at(0x20000684)));
+#define FT_TEST_PARA_ADDR           		(FT_TEST_THD_ADDR+36)               //((at(0x200006A8)));
+
+
+#define SRAM_FT_RAWDATA_3_CC_ADDR       	(RM_RAM_BASE + 0x0000074C)	//0x2000074C
+#define SRAM_FT_UC_CC_ADDR              	(RM_RAM_BASE + 0x000006EC)	//0x200006EC
+
+#define FLASH_NORMAL_FW_FW_VERSION_ADDR     (FW_FT_FW_VERSION)
+#define FLASH_NORMAL_FW_CUST_VERSION_ADDR   (PRAM_PARA_START + 10)
+#define FLASH_TEST_FW_FW_VERSION_ADDR       (FW_FT_FW_VERSION + FLASH_OFFSET)
+#define FLASH_NORMAL_FW_CC_TABLE_ADDR       0x9300
+
+#define REG_FLASHCTL_FLASH_PRAM_LOCK		0X50000900
+#define REG_FLASHCTL_FLASH_PRAM_STORE_TYPE 	0X50000904
+#define REG_FLASHCTL_FLASH_PRAM_ADDR		0X50000908
+#define REG_FLASHCTL_FLASH_PRAM_LENGTH 		0X5000090C
+#define REG_FLASHCTL_FLASH_ADDR				0X50000910
+#define REG_FLASHCTL_FLASH_ISPCTL			0X50000914
+#define REG_FLASHCTL_FLASH_STATE_REG_ADDR	0x50000918
+#define REG_FLASHCTL_FLASH_FLKEY1 			0x50000934
+#define REG_FLASHCTL_FLASH_FLKEY2 			0x50000938
+#define REG_FLASHCTL_FLASH_DATA				0x5000093C
+#define REG_FLASHCTL_FLASH_ENG3				0x5000094C
+#define REG_FLASHCTL_FLASH_PRGCHKSUM_ADDR 	0x50000974
+#define REG_FLASHCTL_FLASH_PRGCHKSUM_RESULT 0x50000978
+#define REG_FLASHCTL_DEVID_ADDR				0x500009BC
+
+#define REG_SPI_SLAVE_SPIFLASHPRO			0x50000524
+
+#define PRAM_ADDR_CC_INFO					0x00007F78
+
+#define RAM_WRITE_TEST_ADDR1 				0x50000950
+#define RAM_WRITE_TEST_ADDR2 				0x50000B10
+#define RAM_WRITE_TEST_ADDR3 				0x50000B00
+#define RAM_READ_TEST_ADDR1					0x50000954
+#define RAM_READ_TEST_ADDR2					0x50000B04
+#define RAM_READ_TEST_ADDR3					0x50000B08
+
+#define FT_UPDATE                   0x01
+#define FT_BASELINE_SF              0x02
+#define FT_BASELINE_PS              0x42
+#define FT_COMPENSATION_SF          0x04
+#define FT_COMPENSATION_PS          0x44
+#define FT_RAWDATA_W_BL_SF          0x08
+#define FT_RAWDATA_W_BL_PS          0x48
+#define FT_RAWDATA_WO_BL_SF         0x10
+#define FT_RAWDATA_WO_BL_PS		    0x50
+#define FT_ALG_RAWDATA              0x20
+#define FT_PS_SEL          			0x40
+#define FT_DEBUG_MESSAGE            0x80
+#define FT_UPDATE_CASE              0xFE
+
+#define FT_STATUS_PURE_RAW          0x01
+#define FT_STATUS_PURE_AF_DC        0x02
+
+typedef enum {
+	CRC_CHECK_FAIL = 0x80,
+	CRC_CHECK_PASS = 0x81,
+	WAIT_TEST_MODE = 0x82,
+	PARTITION_CRC = 0xA0,
+	SET_ADDR_READY = 0xA1,
+	SET_ADDR_FAIL = 0xA2,
+	WRT_PRAM_DATA = 0xA3,
+	WRT_PRAM_FAIL = 0xA4,
+	WAIT_WRT_ACK = 0xA5,
+	WAIT_ACK_FAIL = 0xA6,
+	GET_WRT_ACK = 0xA7,
+	GET_WRT_UNLOCK = 0xA8,
+	GET_UNLOCK_FAIL = 0xA9,
+} I2C_SYNC_CMD;
+
+typedef enum {
+	MAIN_STATE_NORMAL_MODE = 0,
+	MAIN_STATE_BURNING_MODE,
+	MAIN_STATE_FIRWARE_MODE,
+} TCH_BOOTLOADER_STATE;
+
+typedef enum {
+	BURNING_STATE_INIT = 0,
+	BURNING_CHECK_ADDR,
+	BURNING_UNLOCK_PRAM,
+	BURNING_WRT_PRAM,
+	BURNING_WRT_FLASH_PREPARE,
+	BURNING_WRT_FLASH_EXCUTE,
+	BURNING_WRT_FLASH_FINISH,
+	BURNING_STATE_HALT,
+} BL_BURNING_STATE;
+
+typedef enum {
+	NORMAL_STATE_CHECK = 0,
+	NORMAL_CRC_CALC,
+	NORMAL_CRC_NOTIFY,
+	NORMAL_FW_CRC,
+	NORMAL_MODE_CHANGE,
+	NORMAL_MODE_IDLE,
+} BL_NORMAL_STATE;
+

+ 2277 - 0
qcom/opensource/touch-drivers/raydium/chip_raydium/f303_ic_test.c

@@ -0,0 +1,2277 @@
+/* f303_ic_test.c
+ *
+ * Raydium TouchScreen driver.
+ *
+ * Copyright (c) 2021  Raydium tech Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifdef __KERNEL__
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/unistd.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/fs.h>
+#include <asm/uaccess.h>
+#else
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#endif
+
+#define RM_F303_MAX_STR_LENGTH		8
+#include "../drv_interface.h"
+#include "ic_drv_global.h"
+#include "ic_drv_interface.h"
+#include "f303_ic_control.h"
+#include "f303_ic_test.h"
+#include "f303_ic_reg.h"
+#if !SELFTEST_3X
+#include "ic_drv_global_ft.h"
+#include "f303_ic_test_ft.h"
+#include "ic_drv_interface_ft.h"
+#endif
+
+STATUS ft_test_panel_model_check_3x(unsigned short u16_version);
+
+#if !SELFTEST_3X
+STATUS burn_data_log_to_flash_3x(void)
+{
+	unsigned short u16_pram_start_addr = 0x1000;
+	unsigned char  u8_read_buf[4], u8_fw_version[4];
+	unsigned short u16_index = 0, u16_retry = 0;
+	unsigned int u32_read = 0, u32_flash_header_start_addr = 0xA000, u32_header_size = 0x400;
+	unsigned int u32_write = 0;
+	unsigned char *p_data = (unsigned char *)g_i16_raw_data_frame_buffer;
+
+	if (g_u8_drv_interface == SPI_INTERFACE || g_u8_test_log_burn_times > 3) {
+		/*flashless*/
+		DEBUGOUT("return burn test log\r\n");
+		return SUCCESS;
+	}
+
+	memset(g_i16_raw_data_frame_buffer, 0x0, 1024);
+	memset(u8_fw_version, 0x0, sizeof(u8_fw_version));
+	hardware_reset_3x(false);
+	/*read info*/
+	/*wearable_ic_test_read_info();*/
+
+	/*read FW Version*/
+	if (handle_ic_read(FW_FT_SRAM_FW_VERSION, 4, u8_fw_version, g_u8_drv_interface, I2C_BYTE_MODE) == ERROR)
+		return ERROR;
+
+	/* -------------*/
+	/*read Header*/
+	if (handle_ic_read(REG_SYSCON_BLKEN_ADDR, 4, (unsigned char *)(&u32_read), g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+		return ERROR;
+
+	u32_read |= 0x40;
+	if (handle_ic_write(REG_SYSCON_BLKEN_ADDR, 4, (unsigned char *)&u32_read, g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+		return ERROR;
+
+	u32_write = 0x00000000;
+	if (handle_ic_write(REG_I2C_I2CFLASHPRO, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+		return ERROR;
+
+	/*Release flash power down mode*/
+	u32_write = 0x28;
+	if (handle_ic_write(REG_FLASHCTL_FLASH_ISPCTL, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+		return ERROR;
+
+
+	/*----write data-----*/
+	memset(p_data, 0xFF, 1024);
+
+	if (!stop_mcu_3x(0)) {
+		DEBUGOUT("Stop MCU NG\r\n");
+		return ERROR;
+	}
+
+	u32_write = 0x00000000;
+	if (handle_ic_write(REG_I2C_I2CFLASHPRO, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+		return ERROR;
+
+	/*Unlock PRAM*/
+	u32_write = 0x00000000;
+	if (handle_ic_write(REG_FLASHCTL_FLASH_PRAM_LOCK, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+		return ERROR;
+
+	/* unlock key*/
+	u32_write = FLKEY2_KEY;
+	if (handle_ic_write(REG_FLASHCTL_FLASH_FLKEY2, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+		return ERROR;
+
+	u32_write = FLKEY1_KEY;
+	if (handle_ic_write(REG_FLASHCTL_FLASH_FLKEY1, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+		return ERROR;
+
+	u32_write = FLKEY1_LOCK;
+	if (handle_ic_write(REG_FLASHCTL_FLASH_FLKEY1, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+		return ERROR;
+
+	u32_write = FLKEY1_KEY;
+	if (handle_ic_write(REG_FLASHCTL_FLASH_FLKEY1, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+		return ERROR;
+
+	u32_write = FLKEY2_LOCK;
+	if (handle_ic_write(REG_FLASHCTL_FLASH_FLKEY2, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+		return ERROR;
+
+	/*clear Pram*/
+	u16_index = 0;
+	while (u16_index < 1024) {
+		if (handle_ic_write(u16_pram_start_addr + u16_index, 64, p_data, g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+			return ERROR;
+
+		u16_index += 64;
+	}
+
+
+	/*DEBUGOUT("[u8_fw_version]:%X\r\n", u8_fw_version);*/
+	/*DEBUGOUT("[g_u32_wearable_test_result]:%X\r\n", g_u32_wearable_test_result);*/
+
+	/*fill data*/
+
+	memset(g_u16_raw_data_tmp, 0, sizeof(g_u16_raw_data_tmp));
+	test_log_raw_data_remap_3x(g_i16_raw_data_1_short_buf, (short *)g_u16_raw_data_tmp, DATA_REMAP_TO_SENSOR_PAD);
+	memcpy(p_data, g_u16_raw_data_tmp, 96);
+	memset(g_u16_raw_data_tmp, 0, sizeof(g_u16_raw_data_tmp));
+	test_log_raw_data_remap_3x(g_i16_raw_data_2_open_buf, (short *)g_u16_raw_data_tmp, DATA_REMAP_TO_SENSOR_PAD);
+	memcpy((p_data + 96), g_u16_raw_data_tmp, 96);
+	memset(g_u16_raw_data_tmp, 0, sizeof(g_u16_raw_data_tmp));
+	test_log_raw_data_remap_3x((short *)g_u16_raw_data3_cc_buf, (short *)g_u16_raw_data_tmp, DATA_REMAP_TO_SENSOR_PAD);
+	memcpy((p_data + 192), g_u16_raw_data_tmp, 96);
+	memset(g_u16_raw_data_tmp, 0, sizeof(g_u16_raw_data_tmp));
+	test_log_raw_data_remap_3x(g_i16_noise_peek_raw_data, (short *)g_u16_raw_data_tmp, DATA_REMAP_TO_SENSOR_PAD);
+	memcpy((p_data + 288), g_u16_raw_data_tmp, 96);
+	memset(g_u16_raw_data_tmp, 0, sizeof(g_u16_raw_data_tmp));
+	test_log_raw_data_remap_3x(g_i16_display_pattern_avg_raw_data, (short *)g_u16_raw_data_tmp, DATA_REMAP_TO_SENSOR_PAD);
+	memcpy((p_data + 384), g_u16_raw_data_tmp, 96);
+	memset(g_u16_raw_data_tmp, 0, sizeof(g_u16_raw_data_tmp));
+	test_log_raw_data_remap_3x(g_i16_display_pattern_no_dc_avg_raw_data, (short *)g_u16_raw_data_tmp, DATA_REMAP_TO_SENSOR_PAD);
+	memcpy((p_data + 480), g_u16_raw_data_tmp, 96);
+	memset(g_u16_raw_data_tmp, 0, sizeof(g_u16_raw_data_tmp));
+	test_log_raw_data_remap_3x(g_i16_display_pattern_avg_raw_data_s2, (short *)g_u16_raw_data_tmp, DATA_REMAP_TO_SENSOR_PAD);
+	memcpy((p_data + 576), g_u16_raw_data_tmp, 96);
+	memset(g_u16_raw_data_tmp, 0, sizeof(g_u16_raw_data_tmp));
+	test_log_raw_data_remap_3x(g_i16_display_pattern_no_dc_avg_raw_data_s2, (short *)g_u16_raw_data_tmp, DATA_REMAP_TO_SENSOR_PAD);
+	memcpy((p_data + 672), g_u16_raw_data_tmp, 96);
+	memset(g_u16_raw_data_tmp, 0, sizeof(g_u16_raw_data_tmp));
+	test_log_raw_data_remap_3x((short *)g_u16_uc_buf, (short *)g_u16_raw_data_tmp, DATA_REMAP_TO_SENSOR_PAD);
+	memcpy((p_data + 768), g_u16_raw_data_tmp, 96);
+
+	/*---write data section---*/
+	u16_index = 0;
+	while (u16_index < 1024) {
+		if (handle_ic_write(u16_pram_start_addr  + u16_index, 64, (p_data + u16_index), g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+			return ERROR;
+
+		u16_index += 64;
+	}
+
+
+	u32_write = u16_pram_start_addr;
+	if (handle_ic_write(REG_FLASHCTL_FLASH_PRAM_ADDR, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+		return ERROR;
+
+	u32_write = 0x300;
+	if (handle_ic_write(REG_FLASHCTL_FLASH_PRAM_LENGTH, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+		return ERROR;
+
+
+	DEBUGOUT("g_st_test_info.u8_station_id:%x\r\n", g_st_test_info.u8_station_id);
+	DEBUGOUT("u8_burn_times:%d\r\n", g_u8_test_log_burn_times);
+
+	DEBUGOUT("ADDR:%x\r\n", u32_flash_header_start_addr + u32_header_size + (g_st_test_info.u8_station_id - 1) * 4096
+		 + (g_u8_test_log_burn_times - 1) * 0x400);
+
+	/*flash addr , wrtie data section*/
+	u32_write = u32_flash_header_start_addr + u32_header_size + (g_st_test_info.u8_station_id - 1) * 4096
+		    + (g_u8_test_log_burn_times - 1) * 0x400;
+	if (handle_ic_write(REG_FLASHCTL_FLASH_ADDR, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+		return ERROR;
+
+	/*write trigger*/
+	u32_write = 0x4;
+	if (handle_ic_write(REG_FLASHCTL_FLASH_ISPCTL, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+		return ERROR;
+
+	/* check read trigger finished*/
+	u16_retry = 1000;
+	memset(u8_read_buf, 0, sizeof(u8_read_buf));
+	if (handle_ic_read(REG_FLASHCTL_FLASH_ISPCTL, 4, u8_read_buf, g_u8_drv_interface, I2C_BYTE_MODE) == ERROR)
+		return ERROR;
+
+	while ((u8_read_buf[0] & 0x08) == 0 && u16_retry > 0) {
+		if (handle_ic_read(REG_FLASHCTL_FLASH_ISPCTL, 4, u8_read_buf, g_u8_drv_interface, I2C_BYTE_MODE) == ERROR)
+			return ERROR;
+		delay_ms(1);
+		u16_retry--;
+	}
+	if (u16_retry == 0) {
+		DEBUGOUT("Write Trigger Error\r\n");
+		return ERROR;
+	}
+
+	/*---end write data section---*/
+
+	hardware_reset_3x(TRUE);
+
+	return SUCCESS;
+}
+
+STATUS burn_header_log_to_flash_3x(bool is_test_finish, bool is_in_header_data_page)
+{
+
+	unsigned short u16_pram_start_addr = 0x1000;
+	unsigned char *p_header_data = (unsigned char *)g_u32_save_config;
+	unsigned char  u8_write_buf[64], u8_fw_version[4], u8_read_buf[4],  u8_burn_full_times = 20, u8_count_1 = 0, u8_test_info[16];
+	unsigned short u16_index = 0, u16_retry = 0;
+	unsigned int u32_read = 0, u32_flash_header_start_addr = 0xA000 + (g_st_test_info.u8_station_id - 1) * 4096 + g_u8_test_log_burn_times / 5 * 0x100;
+	unsigned int u32_write = 0, u32_temp = 0;
+	bool b_is_first_read = true;
+
+	DEBUGOUT("[u32_flash_header_start_addr ]:%d\r\n", u32_flash_header_start_addr);
+	/*DEBUGOUT("[is_in_header_data_page ]:%d\r\n", is_in_header_data_page);*/
+	if (!is_test_finish && is_in_header_data_page)
+		memset(g_u32_save_config, 0, sizeof(g_u32_save_config));
+
+	if (is_in_header_data_page)
+		u32_flash_header_start_addr = 0xA000 + (g_st_test_info.u8_station_id - 1) * 4096 + g_u8_test_log_burn_times / 5 * 0x100;
+	else
+		u32_flash_header_start_addr = 0xA000 + (g_st_test_info.u8_station_id - 1) * 4096;
+
+	if (g_u8_drv_interface == SPI_INTERFACE || g_u8_test_log_burn_times > 20)
+		/*flashless*/
+		return SUCCESS;
+
+
+
+	memset(u8_write_buf, 0x0, sizeof(u8_write_buf));
+	/*memset(g_u8_header_buf, 0x0 , sizeof(g_u8_header_buf));*/
+	memset(u8_fw_version, 0x0, sizeof(u8_fw_version));
+
+	hardware_reset_3x(false);
+	/*read info*/
+
+
+
+
+
+	if (read_flash_data(g_u32_dongle_flash_ini_addr, 16) != ERROR)
+		memcpy(&u8_test_info, g_u8_data_buf, sizeof(u8_test_info));
+
+
+
+
+
+	/*read FW Version*/
+	if (handle_ic_read(FW_FT_SRAM_FW_VERSION, 4, u8_fw_version, g_u8_drv_interface, I2C_BYTE_MODE) == ERROR)
+		return ERROR;
+
+	/* -------------*/
+	/*read Header*/
+	if (handle_ic_read(REG_SYSCON_BLKEN_ADDR, 4, (unsigned char *)(&u32_read), g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+		return ERROR;
+
+	u32_read |= 0x40;
+	if (handle_ic_write(REG_SYSCON_BLKEN_ADDR, 4, (unsigned char *)&u32_read, g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+		return ERROR;
+
+
+	u32_write = 0x00000000;
+	if (handle_ic_write(REG_I2C_I2CFLASHPRO, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+		return ERROR;
+
+	/*Release flash power down mode*/
+	u32_write = 0x28;
+	if (handle_ic_write(REG_FLASHCTL_FLASH_ISPCTL, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+		return ERROR;
+	DEBUGOUT("[header addr!]:%X\r\n", u32_flash_header_start_addr);
+
+	while (u16_index < 256) {
+		/*write flash addr*/
+
+
+		u32_write = u32_flash_header_start_addr + u16_index;
+		if (handle_ic_write(REG_FLASHCTL_FLASH_ADDR, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+			return ERROR;
+
+
+		/*Set Read trigger, Flh_read_trg[bit 6]*/
+		u32_write = 0x40;
+		if (handle_ic_write(REG_FLASHCTL_FLASH_ISPCTL, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+			return ERROR;
+
+		/* check read trigger finished*/
+		u16_retry = 1000;
+		memset(u8_read_buf, 0, sizeof(u8_read_buf));
+
+		while ((u8_read_buf[0] & 0x40) != 0 && u16_retry > 0) {
+			if (handle_ic_read(REG_FLASHCTL_FLASH_ISPCTL, 4, u8_read_buf, g_u8_drv_interface, I2C_BYTE_MODE) == ERROR)
+				return ERROR;
+			delay_ms(1);
+			u16_retry--;
+		}
+		if (u16_retry == 0)
+			return ERROR;
+
+		/*Read Flash Data*/
+		if (handle_ic_read(REG_FLASHCTL_FLASH_DATA, 4, (unsigned char *)(&u32_read), g_u8_drv_interface, I2C_BYTE_MODE) == ERROR)
+			return ERROR;
+		/*memcpy(&g_u8_header_buf[0] ,  &u32_read  , 4);*/
+
+
+		if (b_is_first_read && !is_in_header_data_page) {
+
+			u32_temp = u32_read;
+			while (u32_temp) {
+				u8_count_1++;
+				u32_temp = (u32_temp - 1) & u32_temp;
+			}
+			/*DEBUGOUT("u32_temp:%d\r\n", 32 - u8_count_1);*/
+
+
+			if (32 - u8_count_1  >= u8_burn_full_times) {
+				g_u8_test_log_burn_times = 32 - u8_count_1 + 1;
+				DEBUGOUT("Burn Full times\r\n");
+				hardware_reset_3x(TRUE);
+				return SUCCESS;
+			}
+				g_u8_test_log_burn_times = 32 - u8_count_1 + 1;
+				/*count how many times has been tested , each test would right shift one bit.*/
+				u32_read = u32_read >> 1;
+			DEBUGOUT("Burn times: %d\r\n", g_u8_test_log_burn_times);
+			memcpy(&p_header_data[0], &u32_read, 4);
+			b_is_first_read = false;
+
+		} else
+			memcpy(&p_header_data[u16_index],  &u32_read, 4);
+		u16_index += 4;
+
+
+	}
+
+
+
+
+
+/*	u16_auo_jig_cmd = g_u16_panel_jig_set_test_items;*/
+/*	fill data*/
+	if (g_u8_test_log_burn_times <= 5 || is_in_header_data_page) {
+		memcpy(&p_header_data[8 + ((g_u8_test_log_burn_times - 1) % 5) * 48], &u8_test_info, 8);
+		/*if ((g_st_test_info.u16_ft_eng_item & IC_TEST_ENG_ITEMS_PANEL_TEST_JIG))*/
+		/*	memcpy(&p_header_data[8 + ((g_u8_test_log_burn_times - 1) % 5) * 48 + 4] , &u16_auo_jig_cmd , 2);*/
+		memcpy(&p_header_data[8 + ((g_u8_test_log_burn_times - 1) % 5) * 48 + 8], &g_st_test_thd.i16_ft_test_open_lower_thd, 2);
+		memcpy(&p_header_data[8 + ((g_u8_test_log_burn_times - 1) % 5) * 48 + 10], &g_st_test_thd.i16_ft_test_short_upper_thd, 2);
+		memcpy(&p_header_data[8 + ((g_u8_test_log_burn_times - 1) % 5) * 48 + 12], &g_st_test_thd.i16_ft_test_single_cc_lower_thd, 2);
+		memcpy(&p_header_data[8 + ((g_u8_test_log_burn_times - 1) % 5) * 48 + 14], &g_st_test_thd.i16_ft_test_uniformity_cc_upper_thd, 2);
+		memcpy(&p_header_data[8 + ((g_u8_test_log_burn_times - 1) % 5) * 48 + 16], &g_st_test_thd.i16_ft_test_uniformity_cc_lower_thd, 2);
+		memcpy(&p_header_data[8 + ((g_u8_test_log_burn_times - 1) % 5) * 48 + 18], &g_st_test_thd.i16_ft_test_panel_test_1_thd, 2);
+		memcpy(&p_header_data[8 + ((g_u8_test_log_burn_times - 1) % 5) * 48 + 20], &g_st_test_thd.i16_ft_test_panel_test_3_thd, 2);
+		memcpy(&p_header_data[8 + ((g_u8_test_log_burn_times - 1) % 5) * 48 + 22], &g_st_test_thd.i16_ft_test_panel_test_2_thd, 2);
+		memcpy(&p_header_data[8 + ((g_u8_test_log_burn_times - 1) % 5) * 48 + 24], &g_st_test_thd.i16_ft_test_panel_test_2_s2_thd, 2);
+		memcpy(&p_header_data[8 + ((g_u8_test_log_burn_times - 1) % 5) * 48 + 26], &g_st_test_para_resv.u32_normal_fw_version, 4);
+		memcpy(&p_header_data[8 + ((g_u8_test_log_burn_times - 1) % 5) * 48 + 30], &g_st_test_para_resv.u32_test_fw_version, 4);
+		if (is_test_finish) {	/*after testing, burn the result to flash*/
+			DEBUGOUT("g_u8_channel_x: %d\r\n", g_u8_channel_x);
+			DEBUGOUT("g_u8_channel_y: %d\r\n", g_u8_channel_y);
+			memcpy(&p_header_data[8 + ((g_u8_test_log_burn_times - 1) % 5) * 48 + 34], &g_u32_wearable_test_result, 4);
+			p_header_data[8 + ((g_u8_test_log_burn_times - 1) % 5) * 48 + 37] = p_header_data[8 + ((g_u8_test_log_burn_times - 1) % 5) * 48 + 34] ^ p_header_data[8 + ((g_u8_test_log_burn_times - 1) % 5) * 48 + 37];
+			p_header_data[8 + ((g_u8_test_log_burn_times - 1) % 5) * 48 + 34] = p_header_data[8 + ((g_u8_test_log_burn_times - 1) % 5) * 48 + 34] ^ p_header_data[8 + ((g_u8_test_log_burn_times - 1) % 5) * 48 + 37];
+			p_header_data[8 + ((g_u8_test_log_burn_times - 1) % 5) * 48 + 37] = p_header_data[8 + ((g_u8_test_log_burn_times - 1) % 5) * 48 + 34] ^ p_header_data[8 + ((g_u8_test_log_burn_times - 1) % 5) * 48 + 37];
+			p_header_data[8 + ((g_u8_test_log_burn_times - 1) % 5) * 48 + 36] = p_header_data[8 + ((g_u8_test_log_burn_times - 1) % 5) * 48 + 35] ^ p_header_data[8 + ((g_u8_test_log_burn_times - 1) % 5) * 48 + 36];
+			p_header_data[8 + ((g_u8_test_log_burn_times - 1) % 5) * 48 + 35] = p_header_data[8 + ((g_u8_test_log_burn_times - 1) % 5) * 48 + 35] ^ p_header_data[8 + ((g_u8_test_log_burn_times - 1) % 5) * 48 + 36];
+			p_header_data[8 + ((g_u8_test_log_burn_times - 1) % 5) * 48 + 36] = p_header_data[8 + ((g_u8_test_log_burn_times - 1) % 5) * 48 + 35] ^ p_header_data[8 + ((g_u8_test_log_burn_times - 1) % 5) * 48 + 36];
+
+
+			memcpy(&p_header_data[8 + ((g_u8_test_log_burn_times - 1) % 5) * 48 + 38], u8_fw_version, 4);
+			memcpy(&p_header_data[8 + ((g_u8_test_log_burn_times - 1) % 5) * 48 + 42], &g_u8_channel_x, 1);
+			memcpy(&p_header_data[8 + ((g_u8_test_log_burn_times - 1) % 5) * 48 + 43], &g_u8_channel_y, 1);
+		}
+/*		DEBUGOUT("g_u8_test_date0: %d\r\n", g_u8_test_date[0]);
+ *		DEBUGOUT("g_u8_test_date: %d\r\n", g_u8_test_date[1]);
+ *		DEBUGOUT("g_u8_test_date: %d\r\n", g_u8_test_date[2]);
+ *		DEBUGOUT("g_u8_test_date: %d\r\n", g_u8_test_date[3]);
+ */
+		memcpy(&p_header_data[8 + ((g_u8_test_log_burn_times - 1) % 5) * 48 + 44], &g_u8_test_date, 4);
+	}
+	/*----write data-----*/
+
+	if (!stop_mcu_3x(0)) {
+		DEBUGOUT("Stop MCU NG\r\n");
+		return ERROR;
+	}
+
+	u32_write = 0x00000000;
+	if (handle_ic_write(REG_I2C_I2CFLASHPRO, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+		return ERROR;
+
+	/*Unlock PRAM*/
+	u32_write = 0x00000000;
+	if (handle_ic_write(REG_FLASHCTL_FLASH_PRAM_LOCK, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+		return ERROR;
+
+	/* unlock key*/
+	u32_write = FLKEY2_KEY;
+	if (handle_ic_write(REG_FLASHCTL_FLASH_FLKEY2, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+		return ERROR;
+
+	u32_write = FLKEY1_KEY;
+	if (handle_ic_write(REG_FLASHCTL_FLASH_FLKEY1, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+		return ERROR;
+
+	u32_write = FLKEY1_LOCK;
+	if (handle_ic_write(REG_FLASHCTL_FLASH_FLKEY1, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+		return ERROR;
+
+	u32_write = FLKEY1_KEY;
+	if (handle_ic_write(REG_FLASHCTL_FLASH_FLKEY1, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+		return ERROR;
+
+	u32_write = FLKEY2_LOCK;
+	if (handle_ic_write(REG_FLASHCTL_FLASH_FLKEY2, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+		return ERROR;
+
+	/*clear Pram*/
+	u16_index = 0;
+	while (u16_index < 256) {	/*totally 688(768) + 256 header bytes*/
+		if (handle_ic_write(u16_pram_start_addr + u16_index, 64, u8_write_buf, g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+			return ERROR;
+
+		u16_index += 64;
+	}
+
+	/*---write header section---*/
+	u16_index = 0;
+	while (u16_index < 256) {	/*totally 256  bytes*/
+		if (handle_ic_write(u16_pram_start_addr  + u16_index, 64, &p_header_data[u16_index], g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+			return ERROR;
+
+		u16_index += 64;
+	}
+
+	u32_write = u16_pram_start_addr;
+	if (handle_ic_write(REG_FLASHCTL_FLASH_PRAM_ADDR, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+		return ERROR;
+
+	u32_write = 0x00;
+	if (handle_ic_write(REG_FLASHCTL_FLASH_PRAM_LENGTH, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+		return ERROR;
+
+	/*flash addr , wrtie data section*/
+	u32_write = u32_flash_header_start_addr;
+	if (handle_ic_write(REG_FLASHCTL_FLASH_ADDR, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+		return ERROR;
+
+	/*write trigger*/
+	u32_write = 0x4;
+	if (handle_ic_write(REG_FLASHCTL_FLASH_ISPCTL, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+		return ERROR;
+
+	/* check read trigger finished*/
+	u16_retry = 1000;
+	memset(u8_read_buf, 0, sizeof(u8_read_buf));
+	if (handle_ic_read(REG_FLASHCTL_FLASH_ISPCTL, 4, u8_read_buf, g_u8_drv_interface, I2C_BYTE_MODE) == ERROR)
+		return ERROR;
+	while ((u8_read_buf[0] & 0x08) != 0 && u16_retry > 0) {
+		if (handle_ic_read(REG_FLASHCTL_FLASH_ISPCTL, 4, u8_read_buf, g_u8_drv_interface, I2C_BYTE_MODE) == ERROR)
+			return ERROR;
+		delay_ms(1);
+		u16_retry--;
+	}
+	if (u16_retry == 0)
+		return ERROR;
+
+	/*memset(g_u8_test_date , 0 , sizeof(g_u8_test_date)); */
+	/*clear date*/
+	/*---end write header section---*/
+	hardware_reset_3x(TRUE);
+	DEBUGOUT("End burn header\r\n");
+	return SUCCESS;
+}
+#endif
+STATUS turn_on_flash_3x(void)
+{
+	unsigned int u32_read = 0;
+
+	/*Turn on Flash*/
+	if (handle_ic_write(REG_I2C_I2CFLASHPRO, 4, (unsigned char *)&u32_read, g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+		return ERROR;
+
+	u32_read = FLH_RELEASE_PD;
+	if (handle_ic_write(REG_FLASHCTL_FLASH_ISPCTL, 4, (unsigned char *)&u32_read, g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+		return ERROR;
+	return SUCCESS;
+}
+
+STATUS read_fpc_flash_3x(unsigned int u32_addr, unsigned int *p_u32_data)
+{
+	unsigned int u32_read;
+
+	/*Turn on Flash*/
+	if (turn_on_flash_3x() == ERROR)
+		return ERROR;
+
+	if (handle_ic_read(REG_SYSCON_BLKEN_ADDR, 4, (unsigned char *)(&u32_read), g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+		return ERROR;
+
+	u32_read |= 0x40;
+	if (handle_ic_write(REG_SYSCON_BLKEN_ADDR, 4, (unsigned char *)&u32_read, g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+		return ERROR;
+
+	u32_read = 0x00000000;
+	if (handle_ic_write(REG_I2C_I2CFLASHPRO, 4, (unsigned char *)&u32_read, g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+		return ERROR;
+
+	if (handle_ic_write(REG_FLASHCTL_FLASH_ADDR, 4, (unsigned char *)&u32_addr, g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+		return ERROR;
+
+	u32_read = 0x40;
+	if (handle_ic_write(REG_FLASHCTL_FLASH_ISPCTL, 4, (unsigned char *)&u32_read, g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+		return ERROR;
+	delay_ms(1);
+
+	if (handle_ic_read(REG_FLASHCTL_FLASH_DATA, 4, (unsigned char *)(&u32_read), g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+		return ERROR;
+
+	*p_u32_data = u32_read;
+	return SUCCESS;
+}
+
+STATUS set_test_info_thd_para_3x(void)
+{
+	unsigned char u8_read_buf[96], u8_i;
+	unsigned char *p_u8_input_buf;
+
+	if (handle_ic_write(FT_TEST_INFO_ADDR, sizeof(g_st_test_info), (unsigned char *)&g_st_test_info, g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+		return ERROR;
+
+	if (handle_ic_read(FT_TEST_INFO_ADDR, sizeof(g_st_test_info), (unsigned char *)u8_read_buf, g_u8_drv_interface, I2C_BYTE_MODE) == ERROR)
+		return ERROR;
+
+	p_u8_input_buf = (unsigned char *)&g_st_test_info;
+	for (u8_i = 0; u8_i < sizeof(g_st_test_info); u8_i++) {
+		if (p_u8_input_buf[u8_i] != u8_read_buf[u8_i]) {
+			DEBUGOUT("Set INFO NG:[%d]=%d->%d\r\n", u8_i, p_u8_input_buf[u8_i], u8_read_buf[u8_i]);
+			return ERROR;
+		}
+	}
+
+	if (handle_ic_write(FT_TEST_THD_ADDR, sizeof(g_st_test_thd), (unsigned char *)&g_st_test_thd, g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+		return ERROR;
+	if (handle_ic_read(FT_TEST_THD_ADDR, sizeof(g_st_test_thd), (unsigned char *)u8_read_buf, g_u8_drv_interface, I2C_BYTE_MODE) == ERROR)
+		return ERROR;
+	p_u8_input_buf = (unsigned char *)&g_st_test_thd;
+	for (u8_i = 0; u8_i < sizeof(g_st_test_thd); u8_i++) {
+		if (p_u8_input_buf[u8_i] != u8_read_buf[u8_i]) {
+			DEBUGOUT("Set THD NG:[%d]=%d->%d\r\n", u8_i, p_u8_input_buf[u8_i], u8_read_buf[u8_i]);
+			return ERROR;
+		}
+	}
+
+	if (handle_ic_write(FT_TEST_PARA_ADDR, sizeof(g_st_test_para_resv), (unsigned char *)&g_st_test_para_resv, g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+		return ERROR;
+	if (handle_ic_read(FT_TEST_PARA_ADDR, sizeof(g_st_test_para_resv), (unsigned char *)u8_read_buf, g_u8_drv_interface, I2C_BYTE_MODE) == ERROR)
+		return ERROR;
+	p_u8_input_buf = (unsigned char *)&g_st_test_para_resv;
+	for (u8_i = 0; u8_i < sizeof(g_st_test_para_resv); u8_i++) {
+		if (p_u8_input_buf[u8_i] != u8_read_buf[u8_i]) {
+			DEBUGOUT("Set Para NG:[%d]=%d->%d\r\n", u8_i, p_u8_input_buf[u8_i], u8_read_buf[u8_i]);
+			return ERROR;
+		}
+	}
+
+	if (handle_ic_write(SRAM_FT_UC_CC_ADDR, 96, (unsigned char *)g_u16_uc_golden_cc_buf, g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+		return ERROR;
+
+	if (handle_ic_read(SRAM_FT_UC_CC_ADDR, 96, (unsigned char *)u8_read_buf, g_u8_drv_interface, I2C_BYTE_MODE) == ERROR)
+		return ERROR;
+	p_u8_input_buf = (unsigned char *)g_u16_uc_golden_cc_buf;
+	for (u8_i = 0; u8_i < 96; u8_i++) {
+		if (p_u8_input_buf[u8_i] != u8_read_buf[u8_i]) {
+			DEBUGOUT("Set PRAM_FT_FW_GOLDEN_CC_ADDR NG:[%d]=%d->%d\r\n", u8_i, p_u8_input_buf[u8_i], u8_read_buf[u8_i]);
+			return ERROR;
+		}
+	}
+/*DEBUGOUT("g_u16_uc_golden_cc_buf[0]=%X,g_u16_uc_golden_cc_buf[1]=%X,g_u16_uc_golden_cc_buf[2]=%X,g_u16_uc_golden_cc_buf[3]=%X,g_u16_uc_golden_cc_buf[4]=%X,g_u16_uc_golden_cc_buf[5]=%X\r\n",g_u16_uc_golden_cc_buf[0],g_u16_uc_golden_cc_buf[1],g_u16_uc_golden_cc_buf[2],g_u16_uc_golden_cc_buf[3],g_u16_uc_golden_cc_buf[4],g_u16_uc_golden_cc_buf[5]);
+ *	DEBUGOUT("u8_read_buf[0]=%X,u8_read_buf[1]=%X,u8_read_buf[2]=%X,u8_read_buf[3]=%X,u8_read_buf[4]=%X,u8_read_buf[5]=%X\r\n",u8_read_buf[0],u8_read_buf[1],u8_read_buf[2],u8_read_buf[3],u8_read_buf[4],u8_read_buf[5]);
+ */
+
+	if (handle_ic_write(SRAM_FT_RAWDATA_3_CC_ADDR, 96, (unsigned char *)g_u16_raw_data3_golden_cc_buf, g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+		return ERROR;
+
+	if (handle_ic_read(SRAM_FT_RAWDATA_3_CC_ADDR, 96, (unsigned char *)u8_read_buf, g_u8_drv_interface, I2C_BYTE_MODE) == ERROR)
+		return ERROR;
+	p_u8_input_buf = (unsigned char *)g_u16_raw_data3_golden_cc_buf;
+	for (u8_i = 0; u8_i < 96; u8_i++) {
+		if (p_u8_input_buf[u8_i] != u8_read_buf[u8_i]) {
+			DEBUGOUT("Set PRAM_FT_OPEN_GOLDEN_CC_ADDR NG:[%d]=%d->%d\r\n", u8_i, p_u8_input_buf[u8_i], u8_read_buf[u8_i]);
+			return ERROR;
+		}
+	}
+
+	return SUCCESS;
+}
+
+void dump_image_data_3x(short *p_image_buf, unsigned char u8_remap)
+{
+	short *p_i16_buf = (short *)g_u16_raw_data_tmp;
+	unsigned char u8_i, u8_j;
+
+	memset(p_i16_buf, 0, MAX_IMAGE_BUFFER_SIZE * 2);
+	if (u8_remap) {
+		for (u8_i = 0; u8_i < MAX_IMAGE_BUFFER_SIZE; u8_i++) {
+			if (g_u8_wearable_pin_map[u8_i] != F303_NA_P)
+				p_i16_buf[g_u8_wearable_pin_map[u8_i]] = p_image_buf[u8_i];
+		}
+	} else
+		memcpy(p_i16_buf, p_image_buf, MAX_IMAGE_BUFFER_SIZE << 1);
+
+	for (u8_j = 0; u8_j < g_u8_channel_y; u8_j++) {
+		for (u8_i = 0; u8_i < g_u8_channel_x; u8_i++)
+			DEBUGOUT("%4d, ", p_i16_buf[u8_i + u8_j * g_u8_channel_x]);
+		DEBUGOUT("\r\n");
+	}
+}
+
+void dump_image_hex_data_3x(short *p_image_buf)
+{
+
+	short *p_i16_buf = (short *)g_u16_raw_data_tmp;
+	unsigned char u8_i, u8_j;
+
+	memset(p_i16_buf, 0, MAX_IMAGE_BUFFER_SIZE * 2);
+	for (u8_i = 0; u8_i < MAX_IMAGE_BUFFER_SIZE; u8_i++) {
+		if (g_u8_wearable_pin_map[u8_i] != F303_NA_P)
+			p_i16_buf[g_u8_wearable_pin_map[u8_i]] = p_image_buf[u8_i];
+	}
+
+	for (u8_j = 0; u8_j < g_u8_channel_y; u8_j++) {
+		for (u8_i = 0; u8_i < g_u8_channel_x; u8_i++)
+			DEBUGOUT("%4X, ", (p_i16_buf[u8_i + u8_j * g_u8_channel_x] & 0x7ff));
+		DEBUGOUT("\r\n");
+	}
+}
+
+STATUS check_test_fw_status_3x(unsigned char u8_target_status, unsigned char *p_u8_result)
+{
+	unsigned int u32_read;
+
+	if (handle_ic_read(FW_FT_ARG1_ADDR, 4, (unsigned char *)(&u32_read), g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+		return ERROR;
+
+	if ((u32_read & FT_TEST_STUATUS_PATTERN) == FT_TEST_STUATUS_PATTERN) {
+		if (((unsigned char)(u32_read & 0x00FF)) == u8_target_status)
+			*p_u8_result = 1;
+		else {
+			DEBUGOUT("[CTFS] Test STATUS NG!! (%x:%x)\r\n", u8_target_status, u32_read);
+			*p_u8_result = 0;
+			return ERROR;
+		}
+		DEBUGOUT("[CTFS] Test FW STATUS (%x:%x)\r\n", u8_target_status, u32_read);
+	} else {
+		DEBUGOUT("[CTFS] Test STATUS Not Ready!! (%x:%x)\r\n", u8_target_status, u32_read);
+		*p_u8_result = 0;
+	}
+	return SUCCESS;
+}
+
+STATUS ft_test_do_fw_test_3x(unsigned short u16_test_items)
+{
+
+	unsigned char u8_write_data[5], u8_result;
+	unsigned int u32_read_data;
+	short i16_time_out = 0;
+#if ENABLE_TEST_TIME_MEASURMENT
+	unsigned int u32_fun_time = 0;
+
+	u32_fun_time = get_system_time();
+	DEBUGOUT("ft_test_do_fw_test Start Time= %d\r\n", u32_fun_time);
+#endif
+
+	g_u32_wearable_test_result &= ~WEARABLE_FT_TEST_RESULT_TEST_INIT_NG;
+
+	if ((u16_test_items & (IC_TEST_ITEMS_OPEN | IC_TEST_ITEMS_SHORT | IC_TEST_ITEMS_UC | IC_TEST_ITEMS_UB)) == 0)
+		return SUCCESS;
+	memset(u8_write_data, 0, sizeof(u8_write_data));
+	u8_write_data[1] = FT_CMD_DO_FT_TEST | 0x80;
+	u8_write_data[2] = (unsigned char) u16_test_items;
+	u8_write_data[3] = (unsigned char)(u16_test_items >> 8);
+
+	DEBUGOUT("Star FW Test!!\r\n");
+
+	if (handle_ic_write(FW_SYS_CMD_ADDR, 4, u8_write_data, g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+		return ERROR;
+
+	/*Wait test fw finish INT */
+	/*Wait 20 Sec*/
+	i16_time_out = 1000;
+
+	while (i16_time_out--) {
+		delay_ms(20);
+#if ENABLE_WDT
+		Chip_WWDT_Feed(LPC_WWDT);/* reload the Watchdog timer*/
+#endif
+		if (!gpio_touch_int_pin_state_access() || (i16_time_out < 10)) {
+			if (check_test_fw_status_3x(FT_CMD_DO_FT_TEST, &u8_result) == ERROR) {
+				DEBUGOUT("[FWT] Wait Test CMD NG (0x%x:%d)\r\n", FT_CMD_DO_FT_TEST, i16_time_out);
+				return ERROR;
+			} else if (u8_result == TRUE) {
+				break;
+			}
+		}
+	}
+
+	if (handle_ic_read(FW_SYS_CMD_ADDR, 4, (unsigned char *)(&u32_read_data), g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+		return ERROR;
+	DEBUGOUT("[FWT]SysCMD1:0x%x (%d)\r\n", u32_read_data, i16_time_out);
+
+	if (i16_time_out == IC_TEST_TIME_OUT) {
+		DEBUGOUT("[FWT] Wait Test CMD Timeout!! (0x%x)\r\n", FT_CMD_DO_FT_TEST);
+		return ERROR;
+	}
+
+#if ENABLE_TEST_TIME_MEASURMENT
+	DEBUGOUT("[FWT] FW Test Time(%d)\r\n", get_system_time() - u32_fun_time);
+#endif
+
+	return SUCCESS;
+}
+STATUS enter_fw_test_mode_3x(void)
+{
+	unsigned char u8_result = 0;
+	short i16_time_out = 0;
+
+#if ENABLE_TEST_TIME_MEASURMENT
+	unsigned int u32_fun_time = 0;
+
+	u32_fun_time = get_system_time();
+#endif
+
+	g_u32_wearable_test_result &= ~WEARABLE_FT_TEST_RESULT_TEST_INIT_NG;
+
+	DEBUGOUT("[EFTM] Start\r\n");
+
+	if ((g_st_test_info.u16_ft_test_item & (IC_TEST_ITEMS_OPEN | IC_TEST_ITEMS_SHORT |
+						IC_TEST_ITEMS_UC | IC_TEST_ITEMS_UB)) == 0) {
+		return SUCCESS;
+	}
+
+	g_u8_is_normal_fw = FALSE;
+
+	i16_time_out = 300;
+	u8_result = 0;
+	while (i16_time_out--) {
+		if (!gpio_touch_int_pin_state_access() || (i16_time_out < 20)) {
+			if (check_test_fw_status_3x(FT_CMD_INIT, &u8_result) == ERROR)
+				goto IC_INIT_NG;
+			else if (u8_result == TRUE)
+				break;
+		}
+		delay_ms(5);
+	}
+
+	if (i16_time_out == IC_TEST_TIME_OUT) {
+		DEBUGOUT("[EFTM] FW State Check NG\r\n");
+		goto IC_INIT_NG;
+	}
+
+	DEBUGOUT("[EFTM] Enter FT mode\r\n");
+
+	if (set_test_info_thd_para_3x() == ERROR) {
+		DEBUGOUT("[RUPI] Set test info, thd, para NG!\r\n");
+		return ERROR;
+	}
+
+#if ENABLE_TEST_TIME_MEASURMENT
+	DEBUGOUT("[EFTM] (%d)\r\n", get_system_time() - u32_fun_time);
+#endif
+	return SUCCESS;
+
+IC_INIT_NG:
+	g_u32_wearable_test_result = WEARABLE_FT_TEST_RESULT_TEST_INIT_NG;
+	return ERROR;
+
+}
+
+STATUS ft_test_read_used_pin_infor_3x(unsigned char *p_u8_infor)
+{
+	/*unsigned char u8_i;*/
+	unsigned char u8_r_buf[2];
+
+	/*get Pin remap*/
+	if (handle_ic_read(FW_FT_PIN_ADDR, 48, p_u8_infor, g_u8_drv_interface, I2C_BYTE_MODE) == ERROR) {
+		DEBUGOUT("[RUPI] NG!\r\n");
+		return ERROR;
+	}
+/*
+ *	for (u8_i = 0; u8_i < 36; u8_i++) {
+ *		DEBUGOUT("Pin[%d] remap=%d\r\n", u8_i, p_u8_infor[u8_i]);
+ *	}
+ */
+
+	if (handle_ic_read(FW_FT_CHANNEL_X_ADDR, 2, u8_r_buf, g_u8_drv_interface, I2C_BYTE_MODE) == ERROR) {
+		DEBUGOUT("[RUPI] NG!\r\n");
+		return ERROR;
+	}
+	g_u8_channel_x = u8_r_buf[0];
+	g_u8_channel_y = u8_r_buf[1];
+	DEBUGOUT("[RUPI] X:%d,Y:%d\r\n", g_u8_channel_x, g_u8_channel_y);
+
+
+	return SUCCESS;
+}
+
+STATUS ft_test_ctrl_mbist_fun_3x(unsigned char u8_enable)
+{
+	unsigned int u32_read;
+
+	if (u8_enable) {
+		if (handle_ic_read(REG_SYSCON_BLKEN_ADDR, 4, (unsigned char *)(&u32_read), g_u8_drv_interface, I2C_WORD_MODE) == ERROR) {
+			DEBUGOUT("Data RAM Test NG !!!\r\n");
+			return ERROR;
+		}
+
+		u32_read |= (1 << 29);
+		if (handle_ic_write(REG_SYSCON_BLKEN_ADDR, 4, (unsigned char *)&u32_read, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) {
+			DEBUGOUT("Data RAM Test NG !!!\r\n");
+			return ERROR;
+		}
+	} else {
+		if (handle_ic_read(REG_SYSCON_BLKEN_ADDR, 4, (unsigned char *)(&u32_read), g_u8_drv_interface, I2C_WORD_MODE) == ERROR) {
+			DEBUGOUT("Data RAM Test NG !!!\r\n");
+			return ERROR;
+		}
+
+		u32_read &= ~(1 << 29);
+		if (handle_ic_write(REG_SYSCON_BLKEN_ADDR, 4, (unsigned char *)&u32_read, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) {
+			DEBUGOUT("Data RAM Test NG !!!\r\n");
+			return ERROR;
+		}
+	}
+	return SUCCESS;
+}
+
+/*
+ *	STATUS ft_test_ram_test_3x(unsigned char u8_is_stop_mcu)
+ *	{
+ *	unsigned int u32_read;
+ *	unsigned char u8_retry_times = 2;
+ *	unsigned int u32_write = 0;
+
+ *	if ((g_st_test_info.u16_ft_test_info_1) == WEARBLE_FT_PRAM_SYSTEM_NG_CASE) {
+ *		g_u32_wearable_test_result = WEARABLE_FT_TEST_RESULT_PRAM_NG;
+ *		return ERROR;
+ *	}
+ *
+ *	RETRY_RAM_TEST:
+ *
+ *	if (u8_is_stop_mcu) {
+ *		stop_mcu_3x(TRUE);
+ *	}
+ *
+ *	if (ft_test_ctrl_mbist_fun_3x(ENABLE) == ERROR) {
+ *		goto EXIT_ERROR;
+ *	}
+ *
+ *	u32_write = 0x04000080;
+ *	if (handle_ic_write(RAM_WRITE_TEST_ADDR1, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) {
+ *		goto EXIT_ERROR;
+ *	}
+ *
+ *	u32_write = 0x00FE90FE;
+ *	if (handle_ic_write(RAM_WRITE_TEST_ADDR2, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) {
+ *		goto EXIT_ERROR;
+ *	}
+ *
+ *	u32_write = 0x0A800080;
+ *	if (handle_ic_write(RAM_WRITE_TEST_ADDR3, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) {
+ *		goto EXIT_ERROR;
+ *	}
+ *
+ *	delay_ms(5);
+ *
+ *	if (handle_ic_read(RAM_READ_TEST_ADDR1, 4, (unsigned char *)(&u32_read), g_u8_drv_interface, I2C_WORD_MODE) == ERROR) {
+ *		goto EXIT_ERROR;
+ *	}
+ *
+ *	if ((u32_read & 0xFE00) != 0) {
+ *		DEBUGOUT("RAM Test NG 954[0x%x]!!!\r\n", u32_read);
+ *		goto EXIT_ERROR;
+ *	}
+ *
+ *	if (handle_ic_read(RAM_READ_TEST_ADDR2, 4, (unsigned char *)(&u32_read), g_u8_drv_interface, I2C_WORD_MODE) == ERROR) {
+ *		goto EXIT_ERROR;
+ *	}
+ *
+ *	if (u32_read != 0x19007F00) {
+ *		DEBUGOUT("RAM Test NG B04[0x%x]!!!\r\n", u32_read);
+ *		goto EXIT_ERROR;
+ *	}
+ *
+ *	if (handle_ic_read(RAM_READ_TEST_ADDR3, 4, (unsigned char *)(&u32_read), g_u8_drv_interface, I2C_WORD_MODE) == ERROR) {
+ *		goto EXIT_ERROR;
+ *	}
+ *
+ *	if (u32_read != 0x40007F00) {
+ *		DEBUGOUT("RAM Test NG B08[0x%x]!!!\r\n", u32_read);
+ *		goto EXIT_ERROR;
+ *	}
+ *
+ *	if (ft_test_ctrl_mbist_fun_3x(DISABLE) == ERROR) {
+ *		goto EXIT_ERROR;
+ *	} else {
+ *		DEBUGOUT("RAM Test Pass\r\n");
+ *		return SUCCESS;
+ *	}
+ *
+ *	EXIT_ERROR:
+ *	if ((u8_retry_times-- > 0)) {
+ *		goto RETRY_RAM_TEST;
+ *	}
+ *
+ *	ft_test_ctrl_mbist_fun_3x(DISABLE);
+ *	g_u32_wearable_test_result = WEARABLE_FT_TEST_RESULT_PRAM_NG;
+ *	DEBUGOUT("PRAM Test NG !!!\r\n");
+ *	return ERROR;
+ *	}
+ */
+STATUS ft_test_ram_test_3x(unsigned char u8_is_stop_mcu)
+{
+	unsigned int u32_read;
+	unsigned char u8_retry_times = 2, u8_check_time = 8;
+	unsigned int u32_write = 0;
+	unsigned int u32_addr = 0;
+	unsigned short u16_retry, u16DataBufLength;
+
+	if ((g_st_test_info.u16_ft_test_info_1) == WEARBLE_FT_PRAM_SYSTEM_NG_CASE) {
+		g_u32_wearable_test_result = WEARABLE_FT_TEST_RESULT_PRAM_NG;
+		return ERROR;
+	}
+
+RETRY_RAM_TEST:
+
+#if ENABLE_WDT
+	/* reload the Watchdog timer*/
+	Chip_WWDT_Feed(LPC_WWDT);
+#endif
+
+	if (u8_is_stop_mcu)
+		stop_mcu_3x(TRUE);
+
+	u32_write = (USEFW_LOCK | CONFIG_LOCK | COMP_LOCK | BASEL_LOCK | INICO_LOCK);
+	if (handle_ic_write(REG_FLASHCTL_FLASH_PRAM_LOCK, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+		goto EXIT_ERROR;
+
+	/* i2c tx buffer size = 0x38 (56)*/
+	u16DataBufLength = 0x38;
+	u32_addr = 0;
+	while (u32_addr < u16DataBufLength) {
+		g_u8_data_buf[u32_addr] = 0xFF;
+		g_u8_data_buf[u32_addr + 1] = 0x00;
+		g_u8_data_buf[u32_addr + 2] = 0xAA;
+		g_u8_data_buf[u32_addr + 3] = 0x55;
+		u32_addr += 4;
+	}
+	u32_addr = PRAM_BOOT_START;
+
+	while (u32_addr < PRAM_BOOT_LENGTH) {
+		if ((u32_addr + u16DataBufLength) > PRAM_BOOT_LENGTH)
+			u16DataBufLength = PRAM_BOOT_LENGTH - u32_addr;
+
+		if (handle_ic_write(u32_addr, u16DataBufLength, g_u8_data_buf, g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+			goto EXIT_ERROR;
+
+		u32_addr += u16DataBufLength;
+
+#if ENABLE_WDT
+		/* reload the Watchdog timer*/
+		Chip_WWDT_Feed(LPC_WWDT);
+#endif
+	}
+
+	gpio_touch_reset_pin_control(FALSE);/*Low*/
+	delay_ms(1);
+	gpio_touch_reset_pin_control(TRUE);/*High*/
+	delay_ms(25);
+
+	g_u8_mute_i2c_err_log = TRUE;
+
+	u16_retry = 150;
+	do {
+#if ENABLE_WDT
+		/* reload the Watchdog timer*/
+		Chip_WWDT_Feed(LPC_WWDT);
+#endif
+
+		delay_ms(35);
+		if (handle_ic_read(REG_FLASHCTL_FLASH_STATE_REG_ADDR, 4, (unsigned char *)(&u32_read), g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+			goto EXIT_ERROR;
+
+		/*delay_ms(2);*/
+		u16_retry--;
+	} while (((u32_read & BLDR_FINISH) != BLDR_FINISH) && (u16_retry != 0));
+
+	g_u8_mute_i2c_err_log = FALSE;
+
+	if ((u32_read & BLDR_FINISH) != BLDR_FINISH) {
+		g_u32_wearable_test_result |= WEARABLE_FT_TEST_RESULT_PRAM_NG;
+		DEBUGOUT("PRAM Test NG !!!\r\n");
+		return ERROR;
+	}
+
+	if ((u8_check_time-- > 0))
+		goto RETRY_RAM_TEST;
+
+	DEBUGOUT("RAM Test Pass\r\n");
+	return SUCCESS;
+
+EXIT_ERROR:
+	if ((u8_retry_times-- > 0)) {
+		u8_check_time = 4;
+		goto RETRY_RAM_TEST;
+	}
+
+	g_u32_wearable_test_result = WEARABLE_FT_TEST_RESULT_PRAM_NG;
+	DEBUGOUT("PRAM Test NG !!!\r\n");
+
+	g_u8_mute_i2c_err_log = FALSE;
+
+	return ERROR;
+}
+
+STATUS ft_test_connect_test_3x(void)
+{
+	unsigned char u8_retry = IC_TEST_RETRY_TIME;
+	unsigned int u32_w_buf, u32_r_buf;
+	unsigned int u32_test_addr = RM_DATAMEM0_BASE;
+	char i8_str[8];
+
+	if ((g_st_test_info.u16_ft_test_info_1) == WEARBLE_FT_I2C_SYSTEM_NG_CASE) {
+		g_u32_wearable_test_result = WEARABLE_FT_TEST_RESULT_I2C_NG;
+		return ERROR;
+	}
+
+RETRY_CONNECT_TEST:
+
+	if (g_u8_drv_interface == SPI_INTERFACE)
+		snprintf(i8_str, RM_F303_MAX_STR_LENGTH, "SPI");
+	else
+		snprintf(i8_str, RM_F303_MAX_STR_LENGTH, "I2C");
+
+	u32_w_buf = 0x55aa00ff;
+
+	if (handle_ic_write(u32_test_addr, 4, (unsigned char *)&u32_w_buf, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) {
+		DEBUGOUT("%s Test Write NG!!\r\n", i8_str);
+		goto NG_CASE;
+	}
+
+	if (handle_ic_read(u32_test_addr, 4, (unsigned char *)&u32_r_buf, g_u8_drv_interface, I2C_BYTE_MODE) == ERROR) {
+		DEBUGOUT("%s Test Read NG!!\r\n", i8_str);
+		goto NG_CASE;
+	}
+
+
+	if (u32_w_buf != u32_r_buf) {
+		DEBUGOUT("%s Test Compare NG [%d], W=0x%x,R=0x%x\r\n", i8_str, 0, u32_w_buf, u32_r_buf);
+		goto NG_CASE;
+	}
+
+
+#if ENABLE_WDT
+	Chip_WWDT_Feed(LPC_WWDT);/* reload the Watchdog timer*/
+#endif
+
+
+	DEBUGOUT("%s Test Pass\r\n", i8_str);
+	return SUCCESS;
+
+NG_CASE:
+	if (--u8_retry > 0) {
+		DEBUGOUT("%s Test Retry=%d\r\n", i8_str, u8_retry);
+		if (hardware_reset_3x(TRUE) == ERROR)
+			goto NG_CASE2;
+		/*Stop MCU*/
+		stop_mcu_3x(FALSE);
+		goto RETRY_CONNECT_TEST;
+	}
+
+NG_CASE2:
+
+	g_u32_wearable_test_result = WEARABLE_FT_TEST_RESULT_I2C_NG;
+	DEBUGOUT("%s Test NG !!!\r\n", i8_str);
+	return ERROR;
+}
+
+STATUS ft_test_reset_pin_test_3x(void)
+{
+	unsigned int u32_write = 0;
+
+	if ((g_st_test_info.u16_ft_test_info_1) == WEARBLE_FT_RESET_PIN_SYSTEM_NG_CASE) {
+		g_u32_wearable_test_result = WEARABLE_FT_TEST_RESULT_RESET_NG;
+		return ERROR;
+	}
+
+	u32_write = 0x00000404;
+	if (handle_ic_write(REG_SYSCON_MISCIER_ADDR, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+		return ERROR;
+
+	if (!gpio_touch_int_pin_state_access()) { /* check INT in high state*/
+		g_u32_wearable_test_result = WEARABLE_FT_TEST_RESULT_INT_NG;
+		return ERROR;
+	}
+	gpio_touch_reset_pin_control(FALSE);/*Low*/
+	delay_ms(1);
+	if (gpio_touch_int_pin_state_access()) {
+		g_u32_wearable_test_result = WEARABLE_FT_TEST_RESULT_RESET_NG;
+		DEBUGOUT("Reset Pin Test NG(Not High) !!!\r\n");
+		return ERROR;
+	}
+	gpio_touch_reset_pin_control(TRUE);/*High*/
+	delay_ms(25);
+	DEBUGOUT("Reset Pin Test Pass !!!\r\n");
+	return SUCCESS;
+}
+
+STATUS ft_test_panel_model_check_3x(unsigned short u16_version)
+{
+	unsigned int u32_read;
+	unsigned short u16_model_version;
+
+	if (handle_ic_read(FW_FT_SRAM_FW_VERSION, 4, (unsigned char *)(&u32_read), g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+		return ERROR;
+	u16_model_version = (u32_read & 0x0000FFFF);
+	DEBUGOUT("Panel Model Test! 0x%08X:0x%08X\r\n", u32_read, u16_version);
+	DEBUGOUT("Panel Model 0x%08X\r\n", u16_model_version);
+	if (u16_model_version == u16_version)
+		return SUCCESS;
+
+	DEBUGOUT("Panel Model NG! 0x%x:0x%x\r\n", u16_model_version, u16_version);
+	return ERROR;
+}
+
+void ft_raw_data_checksum_cal_3x(unsigned short *u16_buffer)
+{
+	unsigned char u8_i;
+
+	u16_buffer[48] = 0x55AA;
+	u16_buffer[49] = 0;
+	for (u8_i = 0; u8_i < (MAX_SENSING_PIN_NUM - 1); u8_i++)
+		u16_buffer[49] += u16_buffer[u8_i];
+
+	DEBUGOUT("[RDCSA] %x:%x\r\n", u16_buffer[48], u16_buffer[49]);
+}
+
+void ft_test_result_checksum_cal_3x(unsigned char *u8_buffer)
+{
+	unsigned char u8_i;
+
+	u8_buffer[48] = 0x5A;
+	u8_buffer[49] = 0;
+	for (u8_i = 0; u8_i < (MAX_SENSING_PIN_NUM - 1); u8_i++)
+		u8_buffer[49] += u8_buffer[u8_i];
+
+	DEBUGOUT("[TRCSA] %x:%x\r\n", u8_buffer[48], u8_buffer[49]);
+}
+
+STATUS ft_raw_data_checksum_check_3x(unsigned short *u16_buffer)
+{
+	unsigned char u8_i;
+	unsigned short u16_sum = 0;
+
+	if (u16_buffer[48] != 0x55AA) {
+		DEBUGOUT("u16_buffer[34]:%x\r\n", u16_buffer[48]);
+		DEBUGOUT("[RDCSC] Pattern NG! [0x%p](0x%x)\n", u16_buffer, u16_buffer[48]);
+		return ERROR;
+	}
+
+	/*u16_buffer[35]=0;*/
+	for (u8_i = 0; u8_i < (MAX_SENSING_PIN_NUM - 1); u8_i++)
+		u16_sum += u16_buffer[u8_i];
+
+	if (u16_buffer[49] != u16_sum) {
+		DEBUGOUT("[RDCSC] Check SUM NG! [0x%p](0x%x:0x%x)\n", u16_buffer, u16_sum, u16_buffer[49]);
+		return ERROR;
+	}
+
+	DEBUGOUT("[RDCSC] PASS (0x%x:0x%x)\r\n", u16_buffer[48], u16_buffer[49]);
+
+	return SUCCESS;
+}
+
+STATUS ft_test_result_checksum_check_3x(unsigned char *u8_buffer)
+{
+	unsigned char u8_i;
+	unsigned char u8_sum = 0;
+
+	if (u8_buffer[48] != 0x5A) {
+		DEBUGOUT("[TRCSC] Pattern NG! [0x%p](0x%x:0x%x)\n", u8_buffer, u8_buffer[48], u8_buffer[49]);
+		return ERROR;
+	}
+
+	/*u32_buffer[35]=0;*/
+	for (u8_i = 0; u8_i < (MAX_SENSING_PIN_NUM - 1); u8_i++)
+		u8_sum += u8_buffer[u8_i];
+
+	if (u8_buffer[49] != u8_sum) {
+		DEBUGOUT("[TRCSC] Check SUM NG! [0x%p](0x%x:0x%x)\n", u8_buffer, u8_sum, u8_buffer[49]);
+		return ERROR;
+	}
+
+	DEBUGOUT("[TRCSC] PASS (0x%x:0x%x)\r\n", u8_buffer[48], u8_buffer[49]);
+
+	return SUCCESS;
+}
+
+STATUS baseline_update_control_3x(bool b_enable_baseline_update)
+{
+	unsigned int u32_write_buf;
+	unsigned char u8_read;
+	short i16_time_out = 100;
+
+	if (b_enable_baseline_update) {
+		u32_write_buf = (SYS_CMD_FUNC_DIS_BS_UPDATE);
+		DEBUGOUT("[BUC] Enable Baseline update\r\n");
+	} else {
+		u32_write_buf = (DIS_BASELINE_UPDATE | SYS_CMD_FUNC_DIS_BS_UPDATE);
+		DEBUGOUT("[BUC] Disable Baseline update\r\n");
+	}
+
+	if (handle_ic_write(FW_SYS_CMD_ADDR, 4, (unsigned char *)&u32_write_buf, g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+		return ERROR;
+	delay_ms(5);
+	while (i16_time_out--) {
+		if (handle_ic_read(FW_SYS_CMD_ADDR, 1, &u8_read, g_u8_drv_interface, I2C_BYTE_MODE) == SUCCESS) {
+			if (u8_read == 0x00)
+				break;
+		} else {
+			DEBUGOUT("[BUC] I2C Read NG\r\n");
+			return ERROR;
+		}
+		delay_ms(2);
+	}
+
+	if (i16_time_out == IC_TEST_TIME_OUT) {
+		DEBUGOUT("[BUC] Baseline Update Control (%d) NG\r\n", b_enable_baseline_update);
+		return ERROR;
+	}
+
+	return SUCCESS;
+}
+
+STATUS wait_fw_init_ready_3x(void)
+{
+	unsigned short u16_time_out;
+	unsigned char u8_r_buf[2];
+
+	u16_time_out = 100;
+	/*Check FW Ready ?*/
+	do {
+		if (handle_ic_read(FW_FT_ARG0_ADDR, 2, u8_r_buf, g_u8_drv_interface, I2C_BYTE_MODE) == ERROR)
+			return ERROR;
+		DEBUGOUT("FW Flag = 0x%x,0x%x\r\n", u8_r_buf[0], u8_r_buf[1]);
+		if (u8_r_buf[0] == 0xAA && u8_r_buf[1] == 0x55)
+			return SUCCESS;
+		delay_ms(2);
+
+	} while (u16_time_out-- > 0);
+
+	return ERROR;
+}
+
+
+STATUS enter_normal_fw_3x(void)
+{
+
+	unsigned char u8_pattern_noise_only = 0;
+#if ENABLE_TEST_TIME_MEASURMENT
+	unsigned int u32_fun_time = 0;
+
+	u32_fun_time = get_system_time();
+#endif
+	if (g_u8_is_normal_fw)
+		return SUCCESS;
+
+#if !SELFTEST_3X
+	/*Set INT Falling Triggle*/
+	gpio_touch_int_trigger_control(FALSE);
+#endif
+
+	g_u32_wearable_test_result &= ~WEARABLE_FT_TEST_RESULT_NORMAL_FW_RESET_NG;
+
+	if (g_u8_drv_interface == SPI_INTERFACE) {
+		if (hardware_reset_3x(TRUE) == ERROR) {
+			DEBUGOUT("[ENF]No INT, no OTP!!\r\n");
+			g_u32_wearable_test_result |= WEARABLE_FT_TEST_RESULT_NORMAL_FW_RESET_NG;
+			return ERROR;
+		}
+
+#if !SELFTEST_3X
+		if (burn_pram_from_dongle_flash_3x() == ERROR)
+			return ERROR;
+#endif
+	} else {
+		if ((g_st_test_info.u16_ft_test_item & ~(IC_TEST_ITEMS_SYSTEM | IC_TEST_ITEMS_PANEL_TEST_2)) == FALSE) {
+			u8_pattern_noise_only = TRUE;
+			DEBUGOUT("Panel Test!!!\r\n");
+		}
+
+		if (!u8_pattern_noise_only) {
+			/*PRAM Test*/
+			if (ft_test_ram_test_3x(1) == ERROR)
+				return ERROR;
+		}
+
+		if (hardware_reset_3x(FALSE) == ERROR) {
+			DEBUGOUT("[ENF]No INT, no OTP!!\r\n");
+			g_u32_wearable_test_result |= WEARABLE_FT_TEST_RESULT_NORMAL_FW_RESET_NG;
+			return ERROR;
+		}
+	}
+
+	if (wait_fw_init_ready_3x() == ERROR)
+		return ERROR;
+
+	if (ft_test_read_used_pin_infor_3x(g_u8_wearable_pin_map) == ERROR)
+		return ERROR;
+
+#if ENABLE_TEST_TIME_MEASURMENT
+	DEBUGOUT("[ENF] (%d)\r\n", get_system_time() - u32_fun_time);
+#endif
+
+	g_u8_is_normal_fw = TRUE;
+	return SUCCESS;
+}
+
+STATUS check_cc_bl_flag_3x(void)
+{
+	unsigned char u8_read_buf[4];
+	unsigned char u8_is_cc_ready = 0;
+	unsigned char u8_fw_version[4];
+	short i16_time_out;
+
+	DEBUGOUT("[CCBF] Check CC BL Flag\r\n");
+
+	if (handle_ic_read(FW_FT_FW_VERSION, 4, u8_fw_version, g_u8_drv_interface, I2C_BYTE_MODE) == ERROR)
+		return ERROR;
+
+	i16_time_out = 20;
+
+	while (i16_time_out--) {
+		if (!u8_is_cc_ready) {
+			if (handle_ic_read(PRAM_ADDR_CC_INFO, 4, u8_read_buf, g_u8_drv_interface, I2C_BYTE_MODE) == ERROR)
+				return ERROR;
+			if ((u8_read_buf[0] >= 1) && (u8_read_buf[2] == u8_fw_version[2]) && (u8_read_buf[3] == u8_fw_version[3]))
+				u8_is_cc_ready = 1;
+		}
+
+		if (u8_is_cc_ready)
+			break;
+		delay_ms(1);
+	}
+
+	/*Check flag*/
+	if (i16_time_out == IC_TEST_TIME_OUT) {
+		DEBUGOUT("CC Flag=%d NG!!\r\n", u8_read_buf[0]);
+		DEBUGOUT("FW Ver=%X.%X:%X.%X NG!!\r\n", u8_fw_version[2], u8_fw_version[3], u8_read_buf[2], u8_read_buf[3]);
+		return ERROR;
+	}
+
+	return SUCCESS;
+}
+
+STATUS burn_cc_to_ic_flash_3x(void)
+{
+	short i16_time_out = 1000;
+	unsigned int u32_cc_table;
+	unsigned char u8_fw_value[4];
+	unsigned int u32_write = 0;
+#if SELFTEST_3X
+	unsigned int u32_read;
+#endif
+
+#if ENABLE_TEST_TIME_MEASURMENT_CC
+	DEBUGOUT("[BCTF] Before Switch to Bootloader\r\n");
+	g_u32_spend_time = get_system_time();
+#endif
+
+#if !SELFTEST	/*210708 add, for fw use bootloader block*/
+	if (stop_mcu_3x(0) == ERROR)
+		return ERROR;
+
+	/*WRT boot-loader to PRAM first*/
+	u32_write = (USEFW_LOCK | CONFIG_LOCK | COMP_LOCK | BASEL_LOCK);
+	if (handle_ic_write(REG_FLASHCTL_FLASH_PRAM_LOCK, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+		return ERROR;
+
+	/*read flash data and write to pram (Bootloader)*/
+	if (handle_spiFlash_to_pram(PRAM_BOOT_START, PRAM_BOOT_LENGTH, PRAM_BOOT_START) == ERROR) {
+		DEBUGOUT("confirm crc error!!\r\n");
+		return ERROR;
+	}
+
+	/*check pram bootloader crc*/
+	if (check_pram_crc32_3x(PRAM_BOOT_START, PRAM_BOOT_CRC_LENGTH) == ERROR) {
+		DEBUGOUT("confirm bootloader crc error!!\r\n");
+		return ERROR;
+	}
+
+	/*read flash data and write to pram (Initial_code)*/
+	if (handle_spiFlash_to_pram(PRAM_DIS_INIT_START, PRAM_DIS_INIT_LENGTH, PRAM_DIS_INIT_START) == ERROR) {
+		DEBUGOUT("confirm crc error!!\r\n");
+		return ERROR;
+	}
+
+	/*check pram Initial code crc*/
+	if (check_pram_crc32_3x(PRAM_DIS_INIT_START, 0x7C) == ERROR) {
+		DEBUGOUT("confirm init crc error!!\r\n");
+		return ERROR;
+	}
+#endif
+
+	/*Set Skip_Load = 1*/
+	u32_write = (BL_CRC_CHK | SKIP_LOAD);
+	if (handle_ic_write(REG_FLASHCTL_FLASH_STATE_REG_ADDR, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+		return ERROR;
+
+	/*wait sw rst finish*/
+	u32_write = BLKRST_SW_RST;
+	if (handle_ic_write(REG_SYSCON_BLKRST_ADDR, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+		return ERROR;
+
+	delay_ms(2);
+
+	if (wait_fw_state_3x(BOOT_RET_DATA_ADDR, WAIT_TEST_MODE, 1, i16_time_out) == ERROR) {
+		DEBUGOUT("[BCTF] Check Burn CC & BL Fail\r\n");
+		return ERROR;
+	}
+
+#if ENABLE_TEST_TIME_MEASURMENT_CC
+	DEBUGOUT("[BCTF] End of Switch to Bootloader = %d\r\n", get_system_time() - g_u32_spend_time);
+	g_u32_spend_time = get_system_time();
+	DEBUGOUT("TICK=%d\r\n", get_system_time());
+#endif
+
+#if SELFTEST_3X
+	if (sysfs_burn_cc_bl()) {
+		/*Read Flash CC Table*/
+		if (read_fpc_flash_3x(FLASH_NORMAL_FW_CC_TABLE_ADDR, &u32_cc_table) == ERROR)
+			return ERROR;
+		DEBUGOUT("Flash Do CC FW Version=0x%04X\r\n", (u32_cc_table & 0xFFFF0000) >> 16);
+		DEBUGOUT("Flash Do CC Flag=0x%X\r\n", (u32_cc_table & 0x0000FFFF));
+		g_u32_fw_cc_version = u32_cc_table;
+
+		/*Read PRAM FW Version*/
+		if (handle_ic_read(FW_FT_SRAM_FW_VERSION, 4, u8_fw_value, g_u8_drv_interface, I2C_BYTE_MODE) == ERROR)
+			return ERROR;
+		DEBUGOUT("SRAM FW Version=0x%03x,0x%03x\r\n", u8_fw_value[3], u8_fw_value[2]);
+
+		if (((u32_cc_table & 0xFF000000) >> 24) != u8_fw_value[3] || ((u32_cc_table & 0x00FF0000) >> 16) != u8_fw_value[2]) {
+			DEBUGOUT("Flash CC Table FW Version is not match!\r\n");
+			if (handle_ic_read(REG_FLASHCTL_FLASH_STATE_REG_ADDR, 4, (unsigned char *)(&u32_read), g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+				DEBUGOUT("read 5000 0918 error\r\n");
+			DEBUGOUT("Flash 0x50000918=0x%08X\r\n", u32_read);
+			return ERROR;
+		}
+
+		return SUCCESS;
+	} else
+		return ERROR;
+#else
+	if (burn_to_ic_flash_3x(COMP_AREA) == ERROR) {
+		DEBUGOUT("[BCTF] Burn CC & BL Fail\r\n");
+		return ERROR;
+	}
+
+	u32_write = 0x00000000;
+	if (handle_ic_write(BOOT_TEST_MODE_ADDR, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+		return ERROR;
+
+	/* clr sync_data(0x20000200) = 0 as finish*/
+	u32_write = 0x00000000;
+	if (handle_ic_write(BOOT_SYNC_DATA_ADDR, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+		return ERROR;
+	delay_ms(10);
+
+	if (wait_fw_state_3x(REG_FLASHCTL_FLASH_STATE_REG_ADDR, 0x4000, 2, i16_time_out) == ERROR) {
+		DEBUGOUT("[BCTF] Check Burn CC & BL Fail\r\n");
+		return ERROR;
+	}
+
+#if ENABLE_AUO_VERIFY_LOG
+	if (read_fpc_flash_3x((unsigned int)(FLASH_FW_START + PRAM_FW_LENGTH - 4), &u32_i2c_read_data))
+		DEBUGOUT("Normal FW CRC=0x%08X\r\n", u32_i2c_read_data);
+#endif
+
+#if ENABLE_TEST_TIME_MEASURMENT_CC
+	DEBUGOUT("BCTF] Burn to flash finish[Tick=%d]!\r\n", get_system_time() - g_u32_spend_time);
+#endif
+
+	/*Read Flash CC Table*/
+	if (read_fpc_flash_3x(FLASH_NORMAL_FW_CC_TABLE_ADDR, &u32_cc_table) == ERROR)
+		return ERROR;
+	DEBUGOUT("Flash Do CC FW Version=0x%04X\r\n", (u32_cc_table & 0xFFFF0000) >> 16);
+	DEBUGOUT("Flash Do CC Flag=0x%X\r\n", (u32_cc_table & 0x0000FFFF));
+
+	/*Read PRAM FW Version*/
+	if (handle_ic_read(FW_FT_SRAM_FW_VERSION, 4, u8_fw_value, g_u8_drv_interface, I2C_BYTE_MODE) == ERROR)
+		return ERROR;
+	DEBUGOUT("SRAM FW Version=0x%03x,0x%03x\r\n", u8_fw_value[3], u8_fw_value[2]);
+
+	if (((u32_cc_table & 0xFF000000) >> 24) != u8_fw_value[3] || ((u32_cc_table & 0x00FF0000) >> 16) != u8_fw_value[2]) {
+		DEBUGOUT("Flash CC Table FW Version is not match!\r\n");
+		return ERROR;
+	}
+
+
+	return SUCCESS;
+#endif
+}
+
+STATUS do_calibration_3x(unsigned char u8_do_calibration_cmd, unsigned char u8_burn_flash)
+{
+	unsigned char u8_value[4];
+	short u8_time_out = 100;
+	unsigned char u8_retry = IC_TEST_RETRY_TIME;
+
+	g_u32_wearable_test_result &= ~(WEARABLE_FT_TEST_RESULT_CC_CALIBRATION_NG | WEARABLE_FT_TEST_RESULT_BURN_CC_NG);
+
+	if (u8_do_calibration_cmd == TRUE) {
+
+#if ENABLE_TEST_TIME_MEASURMENT_CC
+		DEBUGOUT("[DOCC] Start do calibration (%d)\r\n", get_system_time());
+		g_u32_spend_time = get_system_time();
+#endif
+		while (u8_retry--) {
+			u8_value[0] = SYS_CMD_DO_CC_CAL;
+			if (handle_ic_write(FW_SYS_CMD_ADDR, 1, u8_value, g_u8_drv_interface, I2C_BYTE_MODE) == ERROR) /*Trigger Calibration*/
+				return ERROR;
+
+			u8_time_out = 400;
+
+			/* Wait calibration ready*/
+			while (u8_time_out--) {
+				delay_ms(10);/*20*/
+				if (handle_ic_read(FW_SYS_CMD_ADDR, 4, u8_value, g_u8_drv_interface, I2C_BYTE_MODE) == ERROR)
+					return ERROR;
+				else if (u8_value[0] == 0)
+					break;
+#if ENABLE_WDT
+				Chip_WWDT_Feed(LPC_WWDT);/* reload the Watchdog timer*/
+#endif
+			}
+
+			if (u8_time_out == IC_TEST_TIME_OUT) {
+				if (u8_retry) {
+					DEBUGOUT("[DOCC] Do CC Retry (%d,%d)", u8_retry, u8_value[0]);
+					hardware_reset_3x(TRUE);
+#if !SELFTEST_3X
+				if (g_u8_drv_interface == SPI_INTERFACE) {
+					if (burn_pram_from_dongle_flash_3x() == ERROR)
+						return ERROR;
+				}
+#endif
+					delay_ms(20);
+					continue;
+				} else {
+					g_u32_wearable_test_result |= WEARABLE_FT_TEST_RESULT_CC_CALIBRATION_NG;
+					DEBUGOUT("[DOCC] SYS CMD CC NG\r\n");
+					return ERROR;
+				}
+			} else {
+				if (check_cc_bl_flag_3x() == ERROR) {
+					if (u8_retry) {
+						DEBUGOUT("[DOCC] CC Flag Retry (%d)", u8_retry);
+						hardware_reset_3x(TRUE);
+#if !SELFTEST_3X
+				if (g_u8_drv_interface == SPI_INTERFACE) {
+					if (burn_pram_from_dongle_flash_3x() == ERROR)
+						return ERROR;
+				}
+#endif
+						delay_ms(20);
+						continue;
+					} else {
+						g_u32_wearable_test_result |= WEARABLE_FT_TEST_RESULT_CC_CALIBRATION_NG;
+						DEBUGOUT("[DOCC] CC Flag NG\r\n");
+						return ERROR;
+					}
+				} else /*Calibration OK*/
+					break;
+			}
+		}
+#if ENABLE_TEST_TIME_MEASURMENT_CC
+		DEBUGOUT("[DOCC] Do CC Finish! [%d:%d]\r\n", get_system_time() - g_u32_spend_time, u8_time_out);
+		g_u32_spend_time = get_system_time();
+#endif
+	}
+
+	if (u8_burn_flash == FALSE) {
+		DEBUGOUT("[DOCC] Do Calibration Finish \r\n");
+		return SUCCESS;
+	}
+
+	if (burn_cc_to_ic_flash_3x() == ERROR) {
+		DEBUGOUT("[DOCC] Burn to flash NG!\r\n");
+		hardware_reset_3x(TRUE);
+		g_u32_wearable_test_result |= WEARABLE_FT_TEST_RESULT_BURN_CC_NG;
+		return ERROR;
+	}
+
+#if ENABLE_TEST_TIME_MEASURMENT_CC
+	DEBUGOUT("[DOCC] Burn CC Finish (%d)\r\n", get_system_time() - g_u32_spend_time);
+#endif
+
+	DEBUGOUT("[DOCC] Burn CC Finish \r\n");
+	return SUCCESS;
+}
+
+STATUS hw_int_pin_Test_3x(void)
+{
+	unsigned int u32_write = 0;
+
+	if ((g_st_test_info.u16_ft_test_info_1) == WEARBLE_FT_INT_SYSTEM_NG_CASE) {
+		g_u32_wearable_test_result = WEARABLE_FT_TEST_RESULT_INT_NG;
+		return ERROR;
+	}
+
+	/*Trigger INT to Low*/
+	u32_write = 0x00000004;
+	if (handle_ic_write(REG_SYSCON_MISCIER_ADDR, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+		return ERROR;
+
+	/*Read Pin state*/
+	if (!gpio_touch_int_pin_state_access()) {
+		/*DEBUGOUT("INT is LOW\r\n");*/
+	} else {
+		DEBUGOUT("INT Test NG!\r\n");
+		g_u32_wearable_test_result = WEARABLE_FT_TEST_RESULT_INT_NG;
+		return ERROR;
+	}
+
+	/*Trigger INT to High*/
+	u32_write = 0x00000404;
+	if (handle_ic_write(REG_SYSCON_MISCIER_ADDR, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR)
+		return ERROR;
+
+	/*Read Pin state*/
+	if (gpio_touch_int_pin_state_access()) {
+		/*DEBUGOUT("INT is High\r\n");*/
+	} else {
+		DEBUGOUT("INT Test NG!\r\n");
+		g_u32_wearable_test_result = WEARABLE_FT_TEST_RESULT_INT_NG;
+		return ERROR;
+	}
+	gpio_touch_int_access(TRUE);
+	DEBUGOUT("INT Test Pass\r\n");
+	return SUCCESS;
+}
+
+STATUS read_test_fw_data_3x(unsigned short u16_test_items)
+{
+	unsigned char u8_retry = IC_TEST_RETRY_TIME;
+	unsigned int u32_test_item_result;
+#if ENABLE_TEST_TIME_MEASURMENT
+	unsigned int u32_fun_time = 0;
+
+	u32_fun_time = get_system_time();
+#endif
+
+	g_u32_wearable_test_result &= ~(WEARABLE_FT_TEST_RESULT_GET_DATA_NG | WEARABLE_FT_TEST_RESULT_OPEN_NG
+					| WEARABLE_FT_TEST_RESULT_SHORT_NG | WEARABLE_FT_TEST_RESULT_UB_NG
+					| WEARABLE_FT_TEST_RESULT_UC_NG | WEARABLE_FT_TEST_RESULT_SINGLE_CC_OPEN_NG
+					| WEARABLE_FT_TEST_RESULT_SINGLE_CC_SHORT_NG);
+	if ((g_st_test_info.u16_ft_test_item & (IC_TEST_ITEMS_OPEN | IC_TEST_ITEMS_SHORT |
+						IC_TEST_ITEMS_UC | IC_TEST_ITEMS_UB)) == 0) {
+		return SUCCESS;
+	}
+
+RETRY:
+	memset(g_i16_raw_data_1_short_buf, 0, sizeof(g_i16_raw_data_1_short_buf));
+	memset(g_i16_raw_data_2_open_buf, 0, sizeof(g_i16_raw_data_2_open_buf));
+	memset(g_u16_raw_data3_cc_buf, 0, sizeof(g_u16_raw_data3_cc_buf));
+	memset(g_u16_uc_buf, 0, sizeof(g_u16_uc_buf));
+	memset(g_u8_wearable_pin_map, F303_NA_P, sizeof(g_u8_wearable_pin_map));
+
+	if (handle_ic_read(FT_RAWDATA1_SHORT_BUF_ADDR, MAX_SENSING_PIN_NUM * 2, (unsigned char *)g_i16_raw_data_1_short_buf, g_u8_drv_interface, I2C_BYTE_MODE) == ERROR)
+		goto ERROR_EXIT;
+
+
+	if (handle_ic_read(FT_RAWDATA2_OPEN_BUF_ADDR, MAX_SENSING_PIN_NUM * 2, (unsigned char *)g_i16_raw_data_2_open_buf, g_u8_drv_interface, I2C_BYTE_MODE) == ERROR)
+		goto ERROR_EXIT;
+
+
+	if (handle_ic_read(FT_RAWDATA3_CC_BUF_ADDR, MAX_SENSING_PIN_NUM * 2, (unsigned char *)(g_u16_raw_data3_cc_buf), g_u8_drv_interface, I2C_BYTE_MODE) == ERROR)
+		goto ERROR_EXIT;
+
+	if (handle_ic_read(FT_UC_BUF_ADDR, MAX_SENSING_PIN_NUM * 2, (unsigned char *)g_u16_uc_buf, g_u8_drv_interface, I2C_BYTE_MODE) == ERROR)
+		goto ERROR_EXIT;
+
+	if (handle_ic_read(FT_TEST_RESULT_BUF_ADDR, MAX_SENSING_PIN_NUM, (unsigned char *)(g_u8_test_result), g_u8_drv_interface, I2C_BYTE_MODE) == ERROR)
+		goto ERROR_EXIT;
+
+	if (handle_ic_read(FT_TEST_ITEM_RESULT, 4, (unsigned char *)(&u32_test_item_result), g_u8_drv_interface, I2C_BYTE_MODE) == ERROR)
+		goto ERROR_EXIT;
+
+	if (ft_raw_data_checksum_check_3x((unsigned short *)g_i16_raw_data_1_short_buf) == ERROR)
+		goto ERROR_EXIT;
+
+	if (ft_raw_data_checksum_check_3x((unsigned short *)g_i16_raw_data_2_open_buf) == ERROR)
+		goto ERROR_EXIT;
+
+	if (ft_raw_data_checksum_check_3x((unsigned short *)g_u16_raw_data3_cc_buf) == ERROR)
+		goto ERROR_EXIT;
+
+	if (ft_raw_data_checksum_check_3x(g_u16_uc_buf) == ERROR)
+		goto ERROR_EXIT;
+
+	if (ft_test_result_checksum_check_3x(g_u8_test_result) == ERROR)
+		goto ERROR_EXIT;
+
+	if (ft_test_read_used_pin_infor_3x(g_u8_wearable_pin_map) == ERROR)
+		goto ERROR_EXIT;
+
+#if ENABLE_TEST_RSU_DATA_SHOW
+	DEBUGOUT("Slow Data:\r\n");
+	dump_image_data_3x(g_i16_raw_data_1_short_buf, TRUE);
+	DEBUGOUT("Quick Data:\r\n");
+	dump_image_data_3x(g_i16_raw_data_2_open_buf, TRUE);
+	DEBUGOUT("P CC:\r\n");
+	dump_image_hex_data_3x((short *)g_u16_raw_data3_cc_buf);
+	DEBUGOUT("Open Golden CC:\r\n");
+	dump_image_hex_data_3x((short *)g_u16_raw_data3_golden_cc_buf);
+	DEBUGOUT("UC:\r\n");
+	dump_image_data_3x((short *)g_u16_uc_buf, TRUE);
+	DEBUGOUT("Golden UC:\r\n");
+	dump_image_data_3x((short *)g_u16_uc_golden_cc_buf, TRUE);
+	DEBUGOUT("test item result:0x%x\r\n", u32_test_item_result);
+#endif
+
+	if (g_u8_print_debug_msg & PRINT_DEBUG_MSG_TYPE_4) {
+		DEBUGOUT("Slow Data:\r\n");
+		dump_image_data_3x(g_i16_raw_data_1_short_buf, TRUE);
+		DEBUGOUT("Quick Data:\r\n");
+		dump_image_data_3x(g_i16_raw_data_2_open_buf, TRUE);
+		DEBUGOUT("P CC:\r\n");
+		dump_image_data_3x((short *)g_u16_raw_data3_cc_buf, TRUE);
+
+	}
+	if (g_u8_print_debug_msg & PRINT_DEBUG_MSG_TYPE_5) {
+		DEBUGOUT("UC Data:\r\n");
+		dump_image_data_3x((short *)g_u16_uc_buf, TRUE);
+	}
+
+#if ENABLE_TEST_TIME_MEASURMENT
+	DEBUGOUT("#read_test_data end=%d\r\n", get_system_time() - u32_fun_time);
+#endif
+	g_u32_wearable_test_result |= u32_test_item_result;
+	DEBUGOUT("Read Test FW Result Finish\r\n");
+	return SUCCESS;
+
+ERROR_EXIT:
+	if (u8_retry) {
+		u8_retry--;
+		DEBUGOUT("Read Test FW Result Retry:%d", u8_retry);
+		goto RETRY;
+	}
+
+	g_u32_wearable_test_result |= WEARABLE_FT_TEST_RESULT_GET_DATA_NG;
+	DEBUGOUT("Read Test FW Result NG\r\n");
+	return ERROR;
+}
+
+void test_item_message_3x(void)
+{
+	unsigned short u16_test_item = g_st_test_info.u16_ft_test_item;
+
+	DEBUGOUT("================================\r\n");
+	DEBUGOUT("Enter 3x IC Test,(%d)\r\n", get_system_time());
+	DEBUGOUT("IC Test Items=0x%X\r\n", u16_test_item);
+
+	if (u16_test_item & IC_TEST_ITEMS_SYSTEM)
+		DEBUGOUT("Enable System Test\r\n");
+
+	if (u16_test_item & IC_TEST_ITEMS_OPEN)
+		DEBUGOUT("Enable Open Test\r\n");
+
+	if (u16_test_item & IC_TEST_ITEMS_SHORT)
+		DEBUGOUT("Enable Short Test\r\n");
+
+	if (u16_test_item & IC_TEST_ITEMS_UC)
+		DEBUGOUT("Enable Uniformity CC test\r\n");
+
+	if (u16_test_item & IC_TEST_ITEMS_UB)
+		DEBUGOUT("Enable Uniformity BL test\r\n");
+
+	if (u16_test_item & IC_TEST_ITEMS_BURN_FW)
+		DEBUGOUT("Enable Burn FW\r\n");
+
+	if (u16_test_item & IC_TEST_ITEMS_BURN_CC)
+		DEBUGOUT("Enable Burn CC\r\n");
+}
+
+STATUS burn_cc_3x(unsigned short u16_test_items)
+{
+	unsigned char u8_retry = IC_TEST_RETRY_TIME;
+#if ENABLE_AUO_VERIFY_LOG
+	unsigned int u32_flash_crc = 0;
+#endif
+#if ENABLE_TEST_TIME_MEASURMENT
+	unsigned int u32_fun_time = 0;
+
+	u32_fun_time = get_system_time();
+#endif
+	g_u32_wearable_test_result &= ~(WEARABLE_FT_TEST_RESULT_BURN_CC_NG | WEARABLE_FT_TEST_RESULT_NORMAL_FW_NG
+					| WEARABLE_FT_TEST_RESULT_CC_CALIBRATION_NG);
+
+	if ((g_st_test_info.u16_ft_test_info_1) == WEARBLE_FT_BURN_CC_SYSTEM_NG_CASE) {
+		g_u32_wearable_test_result = WEARABLE_FT_TEST_RESULT_BURN_CC_NG;
+		return ERROR;
+	}
+
+	if ((g_st_test_info.u16_ft_test_item & (IC_TEST_ITEMS_BURN_CC)) == 0) {
+		g_u32_wearable_test_result &= ~WEARABLE_FT_TEST_RESULT_NORMAL_FW_RESET_NG;
+		return SUCCESS;
+	}
+
+	while (u8_retry--) {
+		if (enter_normal_fw_3x() == ERROR) {
+			if (u8_retry && !(g_u32_wearable_test_result & WEARABLE_FT_TEST_RESULT_PRAM_NG))
+				continue;
+			else {
+				g_u32_wearable_test_result |= WEARABLE_FT_TEST_RESULT_NORMAL_FW_NG;
+				DEBUGOUT("Enter Normal FW NG\r\n");
+				return ERROR;
+			}
+		}
+		/*Do CC*/
+#if ENABLE_WDT
+		Chip_WWDT_Feed(LPC_WWDT);/* reload the Watchdog timer*/
+#endif
+
+		DEBUGOUT("[BC] Burn CC Start!!\r\n");
+		if (g_u8_drv_interface == SPI_INTERFACE) {
+			/*flashless, bypass*/
+			DEBUGOUT("[BC]Flashless Burn CC ByPass!!\r\n");
+			return SUCCESS;
+		}
+		/*Burn CC to Flash*/
+		if (do_calibration_3x(TRUE, TRUE) == SUCCESS) {
+			DEBUGOUT("[BC] Burn CC Pass!!(%d)\r\n", u8_retry);
+			break;
+		}
+			if (u8_retry)
+				continue;
+			else {
+				DEBUGOUT("[BC] Burn CC NG!!\r\n");
+				return ERROR;
+			}
+	}
+
+#if ENABLE_TEST_TIME_MEASURMENT
+	DEBUGOUT("[BC] Burn CC Time(%d)\r\n", get_system_time() - u32_fun_time);
+#endif
+
+	return SUCCESS;
+}
+
+/*Use dongle ext.Flash to read test fw and write to pram*/
+STATUS load_test_fw_3x(void)
+{
+	STATUS u8_ret = ERROR;
+#if ENABLE_TEST_TIME_MEASURMENT
+	unsigned int u32_fun_time = 0;
+
+	u32_fun_time = get_system_time();
+	DEBUGOUT("load_test_fw Start Time= %d\r\n", u32_fun_time);
+#endif
+	g_u32_wearable_test_result &= ~(WEARABLE_FT_TEST_RESULT_LOAD_TESTFW_NG);
+
+	if (g_st_test_info.u16_ft_test_info_1 == WEARBLE_FT_LOAD_TEST_FW_SYSTEM_NG_CASE) {
+		g_u32_wearable_test_result = WEARABLE_FT_TEST_RESULT_LOAD_TESTFW_NG;
+		return ERROR;
+	}
+
+	if ((g_st_test_info.u16_ft_test_item & (IC_TEST_ITEMS_OPEN | IC_TEST_ITEMS_SHORT | IC_TEST_ITEMS_UC |
+						IC_TEST_ITEMS_UB)) == 0)
+		return SUCCESS;
+
+	DEBUGOUT("[LTFW] start load test fw\r\n");
+#if SELFTEST_3X
+	u8_ret = raydium_upgrade_test_fw_3x(0);
+#else
+	u8_ret = load_test_fw_ft_3x();
+#endif
+	DEBUGOUT("[LTFW] Load test fw finish!!\r\n");
+#if ENABLE_TEST_TIME_MEASURMENT
+	DEBUGOUT("load_test_fw Spend Time= %d\r\n", get_system_time() - u32_fun_time);
+#endif
+
+	return u8_ret;
+}
+
+STATUS system_test_3x(void)
+{
+	STATUS u8_test_result = SUCCESS;
+	unsigned short u16_panel_mode;
+#if ENABLE_TEST_TIME_MEASURMENT
+	unsigned int u32_fun_time;
+#endif
+
+	/*Clear Test Item Test result*/
+	g_u32_wearable_test_result &= ~(WEARABLE_FT_TEST_RESULT_I2C_NG | WEARABLE_FT_TEST_RESULT_INT_NG
+					| WEARABLE_FT_TEST_RESULT_RESET_NG | WEARABLE_FT_TEST_RESULT_PRAM_NG
+					| WEARABLE_FT_TEST_RESULT_IC_SUB_VERSION_NG | WEARABLE_FT_TEST_RESULT_IC_FW_VERIFY_NG);
+
+	if ((g_st_test_info.u16_ft_test_item & IC_TEST_ITEMS_SYSTEM) == FALSE)
+		return SUCCESS;
+
+	if (enable_ic_block_3x() == ERROR) {
+		DEBUGOUT("enable_ic_block NG!!!\r\n");
+		/*return ERROR;*/
+	}
+
+	stop_mcu_3x(1);
+
+/*
+ *	Check Dongle Ext Flash FW/Test FW version PK INI
+ *
+ *	if (check_ext_flash_fw_version() == ERROR) {
+ *		u8_test_result = ERROR;
+ *		g_u32_wearable_test_result = WEARABLE_FT_TEST_RESULT_IC_FW_VERIFY_NG;
+ *		goto ERROR_EXIT;
+ *	}
+ */
+
+#if ENABLE_TEST_TIME_MEASURMENT
+	u32_fun_time = get_system_time();
+#endif
+	/*I2C or SPI Test*/
+	if (ft_test_connect_test_3x() == ERROR) {
+		u8_test_result = ERROR;
+		goto ERROR_EXIT;
+	}
+#if ENABLE_TEST_TIME_MEASURMENT
+	DEBUGOUT("Interface test Spend Time= %d\r\n", get_system_time() - u32_fun_time);
+#endif
+
+	if ((g_st_test_info.u16_ft_eng_item & IC_TEST_ENG_ITEMS_RESV_12)) {
+		DEBUGOUT("INI Model = 0x%08X\r\n", g_st_test_para_resv.u32_normal_fw_version);
+		u16_panel_mode = (g_st_test_para_resv.u32_normal_fw_version & 0x0000FFFF);
+		if (ft_test_panel_model_check_3x(u16_panel_mode) == ERROR) {
+			u8_test_result = ERROR;
+			g_u32_wearable_test_result = WEARABLE_FT_TEST_RESULT_IC_FW_VERIFY_NG;
+			goto ERROR_EXIT;
+		}
+	}
+
+#if ENABLE_TEST_TIME_MEASURMENT
+	u32_fun_time = get_system_time();
+#endif
+	/*INT Pin Test*/
+	if (hw_int_pin_Test_3x() == ERROR) {
+		u8_test_result = ERROR;
+		goto ERROR_EXIT;
+	}
+#if ENABLE_TEST_TIME_MEASURMENT
+	DEBUGOUT("INT Spend Time= %d\r\n", get_system_time() - u32_fun_time);
+#endif
+
+#if ENABLE_TEST_TIME_MEASURMENT
+	u32_fun_time = get_system_time();
+#endif
+
+/*
+ *	RAM Test
+ *	if (ft_test_ram_test_3x(0) == ERROR) {
+ *		u8_test_result = ERROR;
+ *		goto ERROR_EXIT;
+ *	}
+ */
+
+#if ENABLE_TEST_TIME_MEASURMENT
+	DEBUGOUT("RAM Spend Time= %d\r\n", get_system_time() - u32_fun_time);
+#endif
+
+	if ((g_st_test_info.u16_ft_eng_item & IC_TEST_ENG_ITEMS_RESV_11)) {
+		if (check_dev_sub_version_3x(g_st_test_para_resv.u8_para_resv_0[21]) == ERROR) {
+			u8_test_result = ERROR;
+			g_u32_wearable_test_result = WEARABLE_FT_TEST_RESULT_IC_SUB_VERSION_NG;
+			goto ERROR_EXIT;
+		}
+	}
+
+
+
+#if ENABLE_TEST_TIME_MEASURMENT
+	u32_fun_time = get_system_time();
+#endif
+	/*Reset Pin Test*/
+	if (ft_test_reset_pin_test_3x() == ERROR) {
+		u8_test_result = ERROR;
+		goto ERROR_EXIT;
+	}
+
+#if ENABLE_TEST_TIME_MEASURMENT
+	DEBUGOUT("Reset Spend Time= %d\r\n", get_system_time() - u32_fun_time);
+#endif
+
+ERROR_EXIT:
+
+	if (u8_test_result == ERROR)
+		DEBUGOUT("System Test NG!!\r\n");
+	else
+		DEBUGOUT("System Test PASS\r\n");
+
+	return u8_test_result;
+}
+
+void do_ic_test_3x(void)
+{
+	unsigned short u16_test_items;
+
+#if ENABLE_TEST_TIME_MEASURMENT
+	unsigned char u8_ic_test_state;
+#endif
+
+#if ENABLE_TEST_TIME_MEASURMENT
+	unsigned int u32_fun_time = 0;
+#endif
+	unsigned int u32_read_data = 0;
+#if ENABLE_CONTROL_OPENSHORT_WDT
+	Chip_WWDT_SetTimeOut(LPC_WWDT, (WDT_OSC / 10) * 150);
+#endif
+
+	g_u32_wearable_test_result &= ~(WEARABLE_FT_TEST_RESULT_POWER_ON_NG | WEARABLE_FT_TEST_RESULT_IC_VERSION_NG
+					| WEARABLE_FT_TEST_RESULT_MCU_HOLD_NG | WEARABLE_FT_TEST_RESULT_EXT_FLASH_EMPTY_NG
+					| WEARABLE_FT_TEST_RESULT_FLASH_ID_NG | WEARABLE_FT_TEST_RESULT_NORMAL_FW_VER_NG
+					| WEARABLE_FT_TEST_RESULT_PANEL_TEST_3_NG | WEARABLE_FT_TEST_RESULT_CB_NG | WEARABLE_FT_TEST_RESULT_TEST_FW_VER_NG
+					| WEARABLE_FT_TEST_RESULT_AUO_JIG_NG);
+
+	if (!wearable_ic_test_init()) {
+		g_u32_wearable_test_result |= WEARABLE_FT_TEST_RESULT_TEST_INIT_NG;
+		g_u8_ic_test_state = IC_TEST_EXIT;
+		return;
+	}
+
+	test_item_message_3x();
+#if !SELFTEST_3X
+	if ((g_st_test_info.u16_ft_eng_item & IC_TEST_ENG_ITEMS_RESV_16)) {
+
+
+
+		DEBUGOUT("TEST STATUS= %d\r\n", g_u8_panel_jig_test_status);
+
+		DEBUGOUT("[burn_header_log_to_flash]\r\n");
+		if (!b_is_auo_jig_testing) {
+			b_is_auo_jig_testing = true;
+			g_u8_test_log_burn_times = 0;
+			if (!burn_header_log_to_flash_3x(FALSE, FALSE))
+				DEBUGOUT("[burn_header_log_to_flash_3x ERROR!]\r\n");
+			if (g_u8_test_log_burn_times > 5) {
+				if (!burn_header_log_to_flash_3x(FALSE, TRUE))
+					DEBUGOUT("[burn_header_log_to_flash_3x ERROR!]\r\n");
+			}
+		}
+	}
+#endif
+	if (g_st_test_info.u16_ft_test_item == 0)
+		return;
+/*
+ *	if (g_st_test_info.u8_device_id != 2) {
+ *		g_u32_wearable_test_result = WEARABLE_FT_TEST_RESULT_IC_VERSION_NG;
+ *		return;
+ *	}
+ */
+
+	/*handle_display_write(g_u8_display_pattern_sleep_to_active, sizeof(g_u8_display_pattern_sleep_to_active));*/
+	handle_ic_read(0x50001100, 4, (unsigned char *)(&u32_read_data), g_u8_drv_interface, I2C_WORD_MODE);
+	DEBUGOUT("display mode 0x%x\r\n", u32_read_data);
+
+	g_u8_ic_test_state = IC_TEST_INIT;
+
+	u16_test_items = g_st_test_info.u16_ft_test_item;
+
+	g_u32_wearable_test_result &= ~(WEARABLE_FT_TEST_RESULT_POWER_ON_NG | WEARABLE_FT_TEST_RESULT_IC_VERSION_NG
+					| WEARABLE_FT_TEST_RESULT_MCU_HOLD_NG | WEARABLE_FT_TEST_RESULT_EXT_FLASH_EMPTY_NG
+					| WEARABLE_FT_TEST_RESULT_FLASH_ID_NG | WEARABLE_FT_TEST_RESULT_NORMAL_FW_VER_NG
+					| WEARABLE_FT_TEST_RESULT_PANEL_TEST_3_NG | WEARABLE_FT_TEST_RESULT_CB_NG | WEARABLE_FT_TEST_RESULT_TEST_FW_VER_NG
+
+					| WEARABLE_FT_TEST_RESULT_AUO_JIG_NG);
+
+
+	while (u16_test_items) {
+#if ENABLE_TEST_TIME_MEASURMENT
+		u8_ic_test_state = g_u8_ic_test_state;
+		g_u32_spend_time = get_system_time();
+#endif
+
+		switch (g_u8_ic_test_state) {
+		case IC_TEST_INIT:
+#if ENABLE_TEST_TIME_MEASURMENT
+			u32_fun_time = get_system_time();
+#endif
+			g_u8_ic_test_state = IC_TEST_SYSTEM;
+#if ENABLE_TEST_TIME_MEASURMENT
+			DEBUGOUT("IC_TEST_INIT Spend Time= %d\r\n", get_system_time() - u32_fun_time);
+#endif
+
+			break;
+		case IC_TEST_SYSTEM:
+			if (system_test_3x() == SUCCESS ||
+			    (g_st_test_info.u16_ft_eng_item & IC_TEST_ENG_ITEMS_TEST_ALL)) {
+
+				g_u8_ic_test_state = IC_TEST_BURN_FW;
+			} else {
+				g_u8_ic_test_state = IC_TEST_EXIT;
+			}
+			break;
+		case IC_TEST_BURN_FW:
+			if (burn_fw_3x() == ERROR) {
+				g_u32_wearable_test_result = WEARABLE_FT_TEST_RESULT_BURN_FW_NG;
+				g_u8_ic_test_state = IC_TEST_EXIT;
+			} else {
+				g_u8_ic_test_state = IC_TEST_LOAD_TEST_FW;
+			}
+			break;
+		case IC_TEST_LOAD_TEST_FW:
+			if (load_test_fw_3x() == ERROR) {
+				DEBUGOUT("Load Test FW NG!!\r\n");
+				g_u32_wearable_test_result = WEARABLE_FT_TEST_RESULT_LOAD_TESTFW_NG;
+				g_u8_ic_test_state = IC_TEST_EXIT;
+			} else {
+				g_u8_ic_test_state = IC_TEST_ENTER_FW_TEST_MODE;
+			}
+			break;
+		case IC_TEST_ENTER_FW_TEST_MODE:
+			if (enter_fw_test_mode_3x() == ERROR) {
+				DEBUGOUT("Enter FW Test Mode NG!!\r\n");
+				g_u8_ic_test_state = IC_TEST_EXIT;
+			} else {
+				g_u8_ic_test_state = IC_TEST_OPEN_SHORT;
+			}
+			break;
+		case IC_TEST_OPEN_SHORT:
+			if (ft_test_do_fw_test_3x(u16_test_items) == SUCCESS) {
+				DEBUGOUT("FW Test Finish! \r\n");
+				g_u8_ic_test_state = IC_TEST_READ_TEST_FW_DATA;
+			} else {
+				DEBUGOUT("FW Test NG!!\r\n");
+				g_u32_wearable_test_result = WEARABLE_FT_TEST_RESULT_TEST_INIT_NG;
+				g_u8_ic_test_state = IC_TEST_EXIT;
+			}
+			break;
+		case IC_TEST_READ_TEST_FW_DATA:
+			if (read_test_fw_data_3x(u16_test_items) == SUCCESS) {
+				g_u8_ic_test_state = IC_TEST_BURN_CC;
+			} else {
+				DEBUGOUT("Read Test FW Data NG!!\r\n");
+				g_u8_ic_test_state = IC_TEST_EXIT;
+			}
+			break;
+		case IC_TEST_BURN_CC:
+			if (burn_cc_3x(u16_test_items) == ERROR) {
+				DEBUGOUT("Burn CC NG!!\r\n");
+				g_u32_wearable_test_result |= WEARABLE_FT_TEST_RESULT_BURN_CC_NG;
+				g_u8_ic_test_state = IC_TEST_PANEL_PATTERN_TEST;
+			} else {
+				g_u8_ic_test_state = IC_TEST_PANEL_PATTERN_TEST;
+			}
+			break;
+		case IC_TEST_PANEL_PATTERN_TEST:
+#if ENABLE_TEST_GPIO_MEASURMENT
+			Chip_GPIO_SetPinOutHigh(LPC_GPIO, DONGLE_GPIO_PORT_1, DONGLE_GPIO_PIN_10);
+			delay_ms(1);
+			Chip_GPIO_SetPinOutLow(LPC_GPIO, DONGLE_GPIO_PORT_1, DONGLE_GPIO_PIN_10);
+#endif
+			DEBUGOUT("B g_u32_wearable_test_result = 0x%08x\r\n", g_u32_wearable_test_result);
+
+#if !SELFTEST_3X
+			do_ic_panel_test_3x();
+#endif
+			g_u8_ic_test_state = IC_TEST_EXIT;
+			break;
+		case IC_TEST_EXIT:
+#if ENABLE_TEST_TIME_MEASURMENT
+			u32_fun_time = get_system_time();
+			DEBUGOUT("Final action Start Time= %d\r\n", u32_fun_time);
+#endif
+			DEBUGOUT("g_u32_wearable_test_result = 0x%08x\r\n", g_u32_wearable_test_result);
+			DEBUGOUT("Exit IC Test!\r\n");
+#if !SELFTEST_3X
+			gpio_touch_int_trigger_control(FALSE);
+#endif
+
+			g_u8_ic_test_state = IC_TEST_INIT;
+			if ((g_st_test_info.u16_ft_test_item & (IC_TEST_ITEMS_OPEN | IC_TEST_ITEMS_SHORT)) == 0) {
+				ft_raw_data_checksum_cal_3x((unsigned short *)g_i16_raw_data_1_short_buf);
+				ft_raw_data_checksum_cal_3x((unsigned short *)g_i16_raw_data_2_open_buf);
+				ft_raw_data_checksum_cal_3x(g_u16_raw_data3_cc_buf);
+			}
+			ft_raw_data_checksum_cal_3x(g_u16_uc_buf);
+
+			ft_raw_data_checksum_cal_3x((unsigned short *)g_u16_raw_data3_golden_cc_buf);
+			ft_raw_data_checksum_cal_3x((unsigned short *)g_u16_uc_golden_cc_buf);
+			ft_test_result_checksum_cal_3x(g_u8_test_result);
+#if !SELFTEST_3X
+			DEBUGOUT("g_u8_panel_jig_test_status= %d\r\n", g_u8_panel_jig_test_status);
+			if (g_u8_panel_jig_test_status == STATUS_DATA_PANEL_JIG_FINISH && (g_st_test_info.u16_ft_eng_item & IC_TEST_ENG_ITEMS_ENABLE_BURN_TEST_LOG)) {
+				b_is_auo_jig_testing = false;
+				burn_header_log_to_flash_3x(TRUE, TRUE);
+				memset(g_u32_save_config, 0, sizeof(g_u32_save_config));
+				if (!burn_data_log_to_flash_3x())
+					DEBUGOUT("[burn_log_to_flash ERROR!]\r\n");
+			}
+#endif
+/*
+ *			if (g_u8_panel_jig_test_status == STATUS_DATA_PANEL_JIG_FINISH && (g_st_test_info.u16_ft_eng_item & IC_TEST_ENG_ITEMS_ENABLE_BURN_TEST_LOG)) {
+ *				if (!burn_log_to_flash_3x())
+ *					DEBUGOUT("[burn_log_to_flash ERROR!]\r\n");
+ *			}
+ */
+
+#if ENABLE_CONTROL_OPENSHORT_WDT
+			/*Chip_WWDT_SetTimeOut(LPC_WWDT, (WDT_OSC / 10) * 50);*/
+#endif
+			u16_test_items = 0;
+#if ENABLE_TEST_TIME_MEASURMENT
+			DEBUGOUT("Final action Spend Time= %d\r\n", get_system_time() - u32_fun_time);
+#endif
+			break;
+		}
+
+#if !SELFTEST_3X
+		update_dongle_test_status(g_u8_ic_test_state);
+#endif
+
+#if ENABLE_WDT
+		/* reload the Watchdog timer*/
+		Chip_WWDT_Feed(LPC_WWDT);
+#endif
+
+#if !SELFTEST_3X
+		if (g_u8_ic_power_on_ng) {
+			g_u32_wearable_test_result |= WEARABLE_FT_TEST_RESULT_POWER_ON_NG;
+			g_u8_ic_test_state = IC_TEST_EXIT;
+		}
+#endif
+
+#if ENABLE_TEST_TIME_MEASURMENT
+		DEBUGOUT("Test Item [%d] End (%d)\r\n", u8_ic_test_state, get_system_time() - g_u32_spend_time);
+#endif
+	} /*while(u16_test_items)*/
+}
+
+

+ 65 - 0
qcom/opensource/touch-drivers/raydium/chip_raydium/f303_ic_test.h

@@ -0,0 +1,65 @@
+/* f303_ic_test.h
+ *
+ * Raydium TouchScreen driver.
+ *
+ * Copyright (c) 2021  Raydium tech Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "ic_drv_global.h"
+
+/****************************************************************************
+ *                            Defined Const Value
+ *****************************************************************************/
+#define FT_CMD_DO_FT_TEST					0x14
+
+extern STATUS turn_on_flash_3x(void);
+extern STATUS read_fpc_flash_3x(unsigned int u32_addr, unsigned int *p_u32_data);
+extern STATUS set_test_info_thd_para_3x(void);
+extern STATUS check_test_fw_status_3x(unsigned char u8_target_status, unsigned char *p_u8_result);
+extern STATUS ft_test_do_fw_test_3x(unsigned short u16_test_items);
+extern STATUS enter_fw_test_mode_3x(void);
+extern STATUS system_test_3x(void);
+extern STATUS ft_test_ctrl_mbist_fun_3x(unsigned char u8_enable);
+extern STATUS ft_test_ram_test_3x(unsigned char u8_is_stop_mcu);
+extern STATUS ft_test_connect_test_3x(void);
+extern STATUS ft_test_reset_pin_test_3x(void);
+extern STATUS ft_raw_data_checksum_check_3x(unsigned short *u16_buffer);
+extern STATUS ft_test_result_checksum_check_3x(unsigned char *u8_buffer);
+extern STATUS burn_cc_to_ic_flash_3x(void);
+extern STATUS check_cc_bl_flag_3x(void);
+extern STATUS read_test_fw_data_3x(unsigned short u16_test_items);
+extern STATUS load_test_fw_3x(void);
+
+extern void dump_image_data_3x(short *p_image_buf, unsigned char u8_remap);
+extern void dump_image_hex_data_3x(short *p_image_buf);
+extern STATUS ft_test_read_used_pin_infor_3x(unsigned char *p_u8_infor);
+extern void ft_raw_data_checksum_cal_3x(unsigned short *u16_buffer);
+extern void ft_test_result_checksum_cal_3x(unsigned char *u8_buffer);
+extern STATUS baseline_update_control_3x(bool b_enable_baseline_update);
+extern STATUS enter_normal_fw_3x(void);
+extern STATUS do_calibration_3x(unsigned char u8_do_calibration_cmd, unsigned char u8_burn_flash);
+extern STATUS hw_int_pin_Test_3x(void);
+extern void test_item_message_3x(void);
+extern STATUS burn_cc_3x(unsigned short u16_test_items);
+extern void do_ic_test_3x(void);
+extern STATUS burn_data_log_to_flash_3x(void);
+extern STATUS burn_header_log_to_flash_3x(bool is_test_finish,  bool is_in_header_data_page);
+
+//-----------------------------extern FT function ------------------------------------------
+extern STATUS load_test_fw_ft_3x(void);
+extern void do_ic_panel_test_3x(void);
+extern STATUS burn_fw_3x(void);
+extern STATUS burn_to_ic_flash_3x(unsigned char u8_type);
+extern unsigned char notify_panel_jig_start_test_3x(unsigned char u8_cmd);
+

+ 127 - 0
qcom/opensource/touch-drivers/raydium/chip_raydium/ic_drv_global.c

@@ -0,0 +1,127 @@
+/* i2c_drv_global.c
+ *
+ * Raydium TouchScreen driver.
+ *
+ * Copyright (c) 2021  Raydium tech Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "../Config.h"
+#include "ic_drv_global.h"
+#include "ic_drv_interface.h"
+#ifdef __KERNEL__
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/unistd.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/fs.h>
+#include <asm/uaccess.h>
+#endif
+
+#include "../drv_interface.h"
+
+
+
+#if !SELFTEST
+#include "usb.h"
+#include "Descriptors.h"
+#endif
+/*
+ *****************************************************************************
+ **
+ **                  Declared Global Variable
+ **
+ *****************************************************************************
+ ********************* Global *********************
+ */
+
+
+unsigned short g_u16_dev_id;
+
+unsigned char g_u8_drv_interface;
+
+volatile unsigned char g_u8_gpio_irq_trigger;
+
+unsigned char g_u8_raw_data_buffer[MAX_IMAGE_BUFFER_SIZE * 2];
+unsigned short g_u16_raw_data_tmp[MAX_IMAGE_BUFFER_SIZE];
+
+// IC Test
+short g_i16_raw_data_1_short_buf[MAX_SENSING_PIN_NUM];
+short g_i16_raw_data_2_open_buf[MAX_SENSING_PIN_NUM];
+unsigned short g_u16_raw_data3_cc_buf[MAX_SENSING_PIN_NUM];
+unsigned short g_u16_uc_buf[MAX_SENSING_PIN_NUM];//CC
+unsigned short g_u16_raw_data3_golden_cc_buf[MAX_SENSING_PIN_NUM];
+unsigned short g_u16_uc_golden_cc_buf[MAX_SENSING_PIN_NUM];
+
+unsigned int g_u32_test_result[MAX_SENSING_PIN_NUM]; // each node test result (bit[0]:open ng, bit[1]:short ng, bit[2]:uniformity ng..etc)
+unsigned char g_u8_test_result[MAX_SENSING_PIN_NUM];
+
+
+unsigned int g_u32_wearable_test_result;
+unsigned char g_u8_wearable_pin_map[MAX_IMAGE_BUFFER_SIZE];
+
+
+unsigned char g_u8_data_buf[DATA_BUFFER_SIZE];
+unsigned char g_u8_mipi_read_buf[56];
+
+unsigned char g_u8_channel_x;
+unsigned char g_u8_channel_y;
+
+unsigned char g_u8_is_normal_fw;
+
+volatile unsigned short g_u16_test_items_host_cmd;
+unsigned short g_u16_test_items_tool_cmd;
+unsigned char g_u8_ic_power_on_ng;
+char g_i8_test_baseline_msg[30];
+volatile unsigned short g_u16_panel_jig_set_test_items;
+bool b_is_auo_jig_testing;
+
+#if ENABLE_TEST_TIME_MEASURMENT || ENABLE_TEST_TIME_MEASURMENT_CC
+unsigned int g_u32_spend_time;
+#endif
+
+unsigned int g_u32_fw_cc_version;
+unsigned char g_u8_print_debug_msg;
+unsigned char g_u8_display_interface;
+unsigned char g_u8_PAGE_ADDR;
+
+int g_u32_dongle_flash_ini_addr;//       0xF000
+int  g_u32_ini_threshold_addr;//          DONGLE_FLASH_INI_ADDR + 16
+int  g_u32_ini_para_addr;//               INI_THRESHOLD_ADDR + 36
+//int  u32_ini_raw_data_2_bl_addr;//		INI_PARA_ADDR+48
+int  g_u32_ini_raw_data_3_cc_addr;//      INI_RAW_DATA2_BL_ADDR+72
+int  g_u32_ini_uc_cc_addr;//              INI_RAW_DATA_3_CC_ADDR + 72// INI_RAW_DATA2_ADDR
+
+int g_u32_initial_code_start_addr;
+
+void ic_drv_init(void)
+{
+	g_u16_test_items_host_cmd = 0xFFFF;
+	g_u16_test_items_tool_cmd = 0;
+	g_u16_dev_id = DEVICE_ID_3X;
+	g_u8_print_debug_msg = 0;
+	g_u8_display_interface = DISPLAY_TOUCH_2_DRIVER_MODE;
+	b_is_auo_jig_testing = false;
+#if !SELFTEST
+	g_u8_ic_power_on_ng = FALSE;
+#endif
+}
+
+/*
+ *****************************************************************************
+ **
+ *             End Of File
+ ******************************************************************************
+ */

+ 190 - 0
qcom/opensource/touch-drivers/raydium/chip_raydium/ic_drv_global.h

@@ -0,0 +1,190 @@
+/* i2c_drv_global.h
+ *
+ * Raydium TouchScreen driver.
+ *
+ * Copyright (c) 2021  Raydium tech Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _DRVGLOBAL_H_
+#define _DRVGLOBAL_H_
+#include "../Config.h"
+#ifdef __KERNEL__
+#include <linux/types.h>
+#else
+#include <stdint.h>
+#endif
+
+#if (SELFTEST_2X)
+#include "f302_ic_control.h"
+#endif
+
+#if !SELFTEST
+#include "f302_ic_control.h"
+#include "f303_ic_control.h"
+#include "usb.h"
+#endif
+//********* Basic Parameter Definition ***********//
+/*****************************************************************************
+**
+**                  Declared Global Variable
+**
+*****************************************************************************/
+
+
+#define MAX_IMAGE_BUFFER_SIZE			64
+#define MAX_SENSING_PIN_NUM				50	// (wearable)
+#define F302_MAX_IMAGE_BUFFER_SIZE		36
+#define DATA_BUFFER_SIZE				(0x100)
+
+#define F302_DONGLE_FLASH_INI_ADDR		0xF000
+#define F302_INI_THRESHOLD_ADDR			(F302_DONGLE_FLASH_INI_ADDR + 16)
+#define F302_INI_PARA_ADDR			(F302_INI_THRESHOLD_ADDR + 36)
+#define F302_INI_RAW_DATA2_BL_ADDR		(F302_INI_PARA_ADDR + 48)
+#define F302_INI_RAW_DATA_3_CC_ADDR		(F302_INI_RAW_DATA2_BL_ADDR + 72)
+#define F302_INI_UC_CC_ADDR			(F302_INI_RAW_DATA_3_CC_ADDR + 72) // INI_RAW_DATA2_ADDR
+#define F302_NA_P						36
+
+#define F303_DONGLE_FLASH_INI_ADDR		0x10000
+#define F303_INI_THRESHOLD_ADDR			(F303_DONGLE_FLASH_INI_ADDR + 16)
+#define F303_INI_PARA_ADDR			(F303_INI_THRESHOLD_ADDR + 36)
+#define F303_INI_RAW_DATA2_BL_ADDR		(F303_INI_PARA_ADDR + 48)
+#define F303_INI_RAW_DATA_3_CC_ADDR		(F303_INI_RAW_DATA2_BL_ADDR + 128)
+#define F303_INI_UC_CC_ADDR			(F303_INI_RAW_DATA_3_CC_ADDR + 128) // INI_RAW_DATA2_ADDR
+#define F303_MAX_SENSING_PIN_NUM		48
+#define F303_NA_P						65
+
+#define F302_INITIAL_CODE_START_ADDR	0xF400
+#define F303_INITIAL_CODE_START_ADDR	0x10400
+
+#define PRINT_DEBUG_MSG_TYPE_1 		0x01
+#define PRINT_DEBUG_MSG_TYPE_2 		0x02
+#define PRINT_DEBUG_MSG_TYPE_3 		0x04
+#define PRINT_DEBUG_MSG_TYPE_4 		0x08
+#define PRINT_DEBUG_MSG_TYPE_5 		0x10
+#define PRINT_DEBUG_MSG_TYPE_6 		0x20
+#define PRINT_DEBUG_MSG_TYPE_7 		0x40
+#define PRINT_DEBUG_MSG_TYPE_8 		0x80
+
+#define FW_SYS_CMD_ADDR         	0x20000288
+
+#define SYS_CMD_TP_MANUAL_MODE					0x34
+#define SYS_CMD_DO_BL_CAL                   	0x5A
+#define SYS_CMD_DO_CC_CAL                   	0x5C
+
+#define FW_TP_SEQ_NUM_ADDR          0x20000290
+#define FW_TP_POINT_DATA_ADDR       0x20000294
+#define REG_SYSCON_MISCIER_ADDR		0x40000014
+#define FW_FT_IMG_ADDR				0x2000019C
+#define RM_DATAMEM0_BASE			0x20000000
+
+#define F303_DRAM_FT_DBG_START		0x2000004C
+
+//======================= Basic Hardware Define ==============================
+
+
+//==================== End of Basic Hardware Define ==========================
+
+#if SELFTEST
+
+#ifdef __KERNEL__
+typedef enum {
+	error = -1,
+	success = 1
+} STATUS;
+
+#else
+typedef enum {
+	ERROR = 0,
+	SUCCESS = 1
+} STATUS;
+typedef enum {
+	DISABLE = 0,
+	ENABLE = 1
+} FunctionalState;
+
+typedef enum {
+	false = 0,
+	true = 1
+} bool;
+#endif
+#endif
+
+//========== Basic Parameter Information ==========
+//********************* Global *********************//
+
+// Global variable for dongle
+extern unsigned char g_u8_drv_interface;
+
+extern volatile unsigned char g_u8_gpio_irq_trigger;
+extern unsigned short g_u16_dev_id;
+
+
+extern unsigned char g_u8_raw_data_buffer[MAX_IMAGE_BUFFER_SIZE * 2];
+extern unsigned short g_u16_raw_data_tmp[MAX_IMAGE_BUFFER_SIZE];
+
+extern short g_i16_raw_data_1_short_buf[MAX_SENSING_PIN_NUM];
+extern short g_i16_raw_data_2_open_buf[MAX_SENSING_PIN_NUM];
+extern unsigned short g_u16_raw_data3_cc_buf[MAX_SENSING_PIN_NUM];
+extern unsigned short g_u16_uc_buf[MAX_SENSING_PIN_NUM];
+
+
+extern unsigned short g_u16_raw_data3_golden_cc_buf[MAX_SENSING_PIN_NUM];
+extern unsigned short g_u16_uc_golden_cc_buf[MAX_SENSING_PIN_NUM];
+extern unsigned int g_u32_test_result[MAX_SENSING_PIN_NUM]; // each node test result (open ng, short ng, uniformity ng..etc)
+extern unsigned char g_u8_wearable_pin_map[MAX_IMAGE_BUFFER_SIZE];
+extern 	unsigned char g_u8_test_result[MAX_SENSING_PIN_NUM];
+
+extern volatile unsigned short g_u16_test_items_host_cmd;
+extern unsigned short g_u16_test_items_tool_cmd;
+extern volatile unsigned short g_u16_panel_jig_set_test_items;
+
+extern unsigned int g_u32_wearable_test_result;
+extern unsigned char g_u8_channel_x;
+extern unsigned char g_u8_channel_y;
+extern unsigned char g_u8_is_normal_fw;
+extern unsigned char g_u8_data_buf[DATA_BUFFER_SIZE];
+extern unsigned char g_u8_mipi_read_buf[56];
+
+extern unsigned int g_u32_fw_cc_version;
+extern unsigned char g_u8_print_debug_msg;
+
+extern char g_i8_test_baseline_msg[30];
+extern bool b_is_auo_jig_testing;
+#if ENABLE_TEST_TIME_MEASURMENT || ENABLE_TEST_TIME_MEASURMENT_CC
+extern unsigned int g_u32_spend_time;
+#endif
+
+
+#if !SELFTEST
+extern unsigned char g_u8_ic_power_on_ng;
+#endif
+
+extern unsigned char g_u8_display_interface;
+extern unsigned char g_u8_PAGE_ADDR;
+
+extern int g_u32_dongle_flash_ini_addr;//       0xF000
+extern int  g_u32_ini_threshold_addr;//          DONGLE_FLASH_INI_ADDR + 16
+extern int  g_u32_ini_para_addr;//               INI_THRESHOLD_ADDR + 36
+//int  u32_ini_raw_data_2_bl_addr;//		INI_PARA_ADDR+48
+extern int  g_u32_ini_raw_data_3_cc_addr;//      INI_RAW_DATA2_BL_ADDR+72
+extern int  g_u32_ini_uc_cc_addr;//              INI_RAW_DATA_3_CC_ADDR + 72// INI_RAW_DATA2_ADDR
+
+extern int g_u32_initial_code_start_addr;
+extern void ic_drv_init(void);
+
+#endif  /* end _DRVGLOBAL_H_*/
+
+/******************************************************************************
+**                            End Of File
+******************************************************************************/

+ 229 - 0
qcom/opensource/touch-drivers/raydium/chip_raydium/ic_drv_interface.c

@@ -0,0 +1,229 @@
+/* i2c_drv_interface.c
+ *
+ * Raydium TouchScreen driver.
+ *
+ * Copyright (c) 2021  Raydium tech Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifdef __KERNEL__
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/unistd.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/fs.h>
+#include <asm/uaccess.h>
+#else
+#include <stdint.h>
+#include <string.h>
+#endif
+#include "ic_drv_interface.h"
+#include "../drv_interface.h"
+#include "ic_drv_global.h"
+#if SELFTEST_2X
+#include "f302_ic_control.h"
+#include "f302_ic_test.h"
+#endif
+#if SELFTEST_3X
+#include "f303_ic_control.h"
+#include "f303_ic_test.h"
+#endif
+#if !SELFTEST
+#include "f302_ic_test.h"
+#include "f302_ic_test_ft.h"
+#include "f303_ic_test.h"
+#include "f303_ic_test_ft.h"
+#include "ic_drv_global_ft.h"
+#include "main.h"
+#endif
+
+st_test_threshold g_st_test_thd;
+st_test_info g_st_test_info;
+st_test_para_resv g_st_test_para_resv;
+unsigned char g_u8_ic_test_state;
+
+STATUS wearable_ic_test_read_info(void)
+{
+	if (read_flash_data(g_u32_dongle_flash_ini_addr, 16) != ERROR) {
+		memcpy(&g_st_test_info, g_u8_data_buf, sizeof(g_st_test_info));
+	} else {
+		DEBUGOUT("Read Flash Data ERROR\r\n");
+		return ERROR;
+	}
+
+	return SUCCESS;
+}
+
+STATUS wearable_ic_test_info_init(void)
+{
+#if SELFTEST
+	g_u16_test_items_host_cmd = IC_TEST_ITEMS_SYSTEM | IC_TEST_ITEMS_OPEN | IC_TEST_ITEMS_SHORT |  IC_TEST_ITEMS_UC | IC_TEST_ITEMS_UB | IC_TEST_ITEMS_BURN_CC;
+#endif
+
+	if (!wearable_ic_test_read_info()) {
+		DEBUGOUT("Read Info ERROR\r\n");
+		return ERROR;
+	}
+
+	if (g_u16_test_items_host_cmd != 0)
+		g_st_test_info.u16_ft_test_item &= g_u16_test_items_host_cmd;
+	g_u16_panel_jig_set_test_items |= g_st_test_info.u16_ft_test_item;
+
+	DEBUGOUT("TestItem:0x%x:0x%x\r\n", g_st_test_info.u16_ft_test_item, g_st_test_info.u16_ft_eng_item);
+	DEBUGOUT("g_u16_panel_jig_set_test_items:0x%x\r\n", g_u16_panel_jig_set_test_items);
+	if (read_flash_data(g_u32_ini_threshold_addr, 36)) {
+		memcpy(&g_st_test_thd, g_u8_data_buf, sizeof(g_st_test_thd));
+	} else {
+		DEBUGOUT("Read THD Data ERROR\r\n");
+		return ERROR;
+	}
+
+	DEBUGOUT("THD:\r\n%d,%d,%d,%d,\r\n%d,%d,%d,%d,\r\n%d,%d,%d,%d\r\n",
+		 g_st_test_thd.i16_ft_test_open_lower_thd,
+		 g_st_test_thd.i16_ft_test_short_upper_thd,
+		 g_st_test_thd.i16_ft_test_short_lower_thd,
+		 g_st_test_thd.i16_ft_test_single_cc_upper_thd,
+		 g_st_test_thd.i16_ft_test_single_cc_lower_thd,
+		 g_st_test_thd.i16_ft_test_uniformity_bl_upper_thd,
+		 g_st_test_thd.i16_ft_test_uniformity_bl_lower_thd,
+		 g_st_test_thd.i16_ft_test_uniformity_cc_upper_thd,
+		 g_st_test_thd.i16_ft_test_uniformity_cc_lower_thd,
+		 g_st_test_thd.i16_ft_test_panel_test_1_thd,
+		 g_st_test_thd.i16_ft_test_panel_test_3_thd,
+		 g_st_test_thd.i16_ft_test_panel_test_2_thd);
+
+	if (read_flash_data(g_u32_ini_para_addr, 48)) {
+		memcpy(&g_st_test_para_resv, g_u8_data_buf, sizeof(g_st_test_para_resv));
+	} else {
+		DEBUGOUT("Read INI Para ERROR\r\n");
+		return ERROR;
+	}
+/*
+ *	DEBUGOUT(" g_st_test_para_resv.u32_normal_fw_version = %X ,g_st_test_para_resv.u32_test_fw_version= %X \r\n",
+ *		 g_st_test_para_resv.u32_normal_fw_version,
+ *		 g_st_test_para_resv.u32_test_fw_version
+ *		);
+ */
+
+	if (g_u16_dev_id == DEVICE_ID_3X) {
+		if (read_flash_data(g_u32_ini_raw_data_3_cc_addr, 128)) {
+			memcpy(g_u16_raw_data3_golden_cc_buf, g_u8_data_buf, sizeof(g_u16_raw_data3_golden_cc_buf));
+		} else {
+			DEBUGOUT("read raw data 3 cc ERROR\r\n");
+			return ERROR;
+		}
+/*
+ *		DEBUGOUT(" g_u16_raw_data3_golden_cc_buf[0] = %d,g_u16_raw_data3_golden_cc_buf[1 =%d g_u16_raw_data3_golden_cc_buf[2]=%d,\r\n",
+ *			 g_u16_raw_data3_golden_cc_buf[0],
+ *			 g_u16_raw_data3_golden_cc_buf[1],
+ *			 g_u16_raw_data3_golden_cc_buf[2]
+ *			);
+ */
+		if (read_flash_data(g_u32_ini_uc_cc_addr, 128)) {
+			memcpy(g_u16_uc_golden_cc_buf, g_u8_data_buf, sizeof(g_u16_uc_golden_cc_buf));
+		} else {
+			DEBUGOUT("read uc cc ERROR\r\n");
+			return ERROR;
+		}
+	} else if (g_u16_dev_id == DEVICE_ID_2X) {
+		if (read_flash_data(g_u32_ini_raw_data_3_cc_addr, 72)) {
+			memcpy(g_u16_raw_data3_golden_cc_buf, g_u8_data_buf, sizeof(g_u16_raw_data3_golden_cc_buf));
+		} else {
+			DEBUGOUT("read raw data 3 cc ERROR\r\n");
+			return ERROR;
+		}
+/*
+ *		DEBUGOUT(" g_u16_raw_data3_golden_cc_buf[0] = %d,g_u16_raw_data3_golden_cc_buf[1 =%d g_u16_raw_data3_golden_cc_buf[2]=%d,\r\n",
+ *			 g_u16_raw_data3_golden_cc_buf[0],
+ *			 g_u16_raw_data3_golden_cc_buf[1],
+ *			 g_u16_raw_data3_golden_cc_buf[2]
+ *			);
+ */
+		if (read_flash_data(g_u32_ini_uc_cc_addr, 72)) {
+			memcpy(g_u16_uc_golden_cc_buf, g_u8_data_buf, sizeof(g_u16_uc_golden_cc_buf));
+		} else {
+			DEBUGOUT("read uc cc ERROR\r\n");
+			return ERROR;
+		}
+	}
+/*	if (read_flash_data(INI_RAW_DATA2_BL_ADDR, 72))
+ *		memcpy(g_i16_raw_data2_golden_bl_buf, g_u8_data_buf, sizeof(g_i16_raw_data2_golden_bl_buf));
+ */
+	return SUCCESS;
+}
+
+STATUS wearable_ic_test_init(void)
+{
+	if (!wearable_ic_test_info_init())
+		return ERROR;
+	if (!(g_st_test_info.u16_ft_eng_item & IC_TEST_ENG_ITEMS_PANEL_TEST_JIG))
+		wearable_ic_test_init_buffer();
+
+	return SUCCESS;
+}
+
+void wearable_ic_test_init_buffer(void)
+{
+	g_u32_wearable_test_result = WEARABLE_FT_TEST_RESULT_IC_INIT_STATE;
+	memset(g_u32_test_result, 0, sizeof(g_u32_test_result));
+	memset(g_u8_test_result, 0, sizeof(g_u8_test_result));
+	g_u8_is_normal_fw = FALSE;
+}
+
+STATUS handle_burn_log_to_flash(void)
+{
+	STATUS u8_status = ERROR;
+
+	switch (g_u16_dev_id) {
+#if (!SELFTEST_2X && (!SELFTEST))
+	case DEVICE_ID_2X:
+		burn_data_log_to_flash_2x();
+		break;
+#endif
+#if (!SELFTEST_3X && (!SELFTEST))
+	case DEVICE_ID_3X:
+		b_is_auo_jig_testing = false;
+		burn_header_log_to_flash_3x(TRUE, TRUE);
+		memset(g_u32_save_config, 0, sizeof(g_u32_save_config));
+		if (burn_data_log_to_flash_3x())
+			u8_status = SUCCESS;
+		break;
+#endif
+	}
+	return u8_status;
+}
+
+void handle_ic_test(void)
+{
+
+	switch (g_u16_dev_id) {
+#if (SELFTEST_2X | (!SELFTEST))
+	case DEVICE_ID_2X:
+		do_ic_test_2x();
+		break;
+#endif
+#if (SELFTEST_3X | (!SELFTEST))
+	case DEVICE_ID_3X:
+		do_ic_test_3x();
+		break;
+#endif
+	}
+}
+
+void handle_set_display_interface(unsigned char u8_interface)
+{
+	g_u8_display_interface = u8_interface;
+	DEBUGOUT("%s: 0x%x \r\n", __func__, u8_interface);
+}
+

+ 211 - 0
qcom/opensource/touch-drivers/raydium/chip_raydium/ic_drv_interface.h

@@ -0,0 +1,211 @@
+/* i2c_drv_interface.h
+ *
+ * Raydium TouchScreen driver.
+ *
+ * Copyright (c) 2021  Raydium tech Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "ic_drv_global.h"
+
+#define DEVICE_ID_2X             				 0xF302
+#define DEVICE_ID_3X             				 0xF303
+
+#define I2C_PDA2_BYTE_MODE  								0x03
+#define I2C_PDA2_WORD_MODE  								0x43
+#define I2C_BYTE_MODE 										(0x80|0x20)
+#define I2C_WORD_MODE 										(0xc0|0x20)
+#define I2C_MCU_MODE  										0x40
+#define I2C_PDA2_MODE  										0x10
+#define I2C_PDA_MODE  										0x08
+
+#define SPI_BYTE_MODE 										(0x80|0x03)
+#define SPI_WORD_MODE 										(0xc0|0x03)
+#define SPI_MCU_MODE  										0x40
+
+#define I2C_INTERFACE										0
+#define SPI_INTERFACE										1
+
+#define DISPLAY_SPI_MODE									0
+#define DISPLAY_MIPI_MODE									1
+#define DISPLAY_TOUCH_2_DRIVER_MODE						2
+
+#define DATA_REMAP_TO_IC_PIN    							1
+#define DATA_REMAP_TO_SENSOR_PAD 							2
+
+#define IC_TEST_RETRY_TIME               					3
+#define IC_TEST_TIME_OUT                   					(-1)
+#define FT_TEST_STUATUS_PATTERN            					0x5A00
+
+#define FT_CMD_INIT                         				0x11
+#define FT_CMD_DO_FT_TEST                   				0x14
+
+#define IC_TEST_ITEMS_SYSTEM                                0x0001
+#define IC_TEST_ITEMS_BURN_FW                               0x0002
+#define IC_TEST_ITEMS_OPEN                                  0x0004
+#define IC_TEST_ITEMS_SHORT                                 0x0008
+#define IC_TEST_ITEMS_UC                                    0x0010
+#define IC_TEST_ITEMS_UB                                    0x0020
+#define IC_TEST_ITEMS_BURN_CC                               0x0040
+#define IC_TEST_ITEMS_PANEL_TEST_1                          0x0080
+#define IC_TEST_ITEMS_PANEL_TEST_2                          0x0100
+#define IC_TEST_ITEMS_PANEL_TEST_3                          0x0200
+#define IC_TEST_ITEMS_RESV_0                                0x0400
+#define IC_TEST_ITEMS_RESV_1                                0x0800
+#define IC_TEST_ITEMS_RESV_2                                0x1000
+#define IC_TEST_ITEMS_RESV_3                                0x2000
+#define IC_TEST_ITEMS_RESV_4                                0x4000
+#define IC_TEST_ITEMS_RESV_5                                0x8000
+
+#define IC_TEST_ENG_ITEMS_RESV_1                            0x0001
+#define IC_TEST_ENG_ITEMS_RESV_2                            0x0002
+#define IC_TEST_ENG_ITEMS_RESV_3                            0x0004
+#define IC_TEST_ENG_ITEMS_TEST_ALL                          0x0008
+#define IC_TEST_ENG_ITEMS_RESV_5                            0x0010
+#define IC_TEST_ENG_ITEMS_RESV_6                            0x0020
+#define IC_TEST_ENG_ITEMS_RESV_7                            0x0040
+#define IC_TEST_ENG_ITEMS_RESV_8                            0x0080
+#define IC_TEST_ENG_ITEMS_PANEL_TEST_JIG                    0x0100
+#define IC_TEST_ENG_ITEMS_RESV_10                           0x0200
+#define IC_TEST_ENG_ITEMS_RESV_11                           0x0400
+#define IC_TEST_ENG_ITEMS_RESV_12                           0x0800
+#define IC_TEST_ENG_ITEMS_RESV_13                           0x1000
+#define IC_TEST_ENG_ITEMS_RESV_14                           0x2000
+#define IC_TEST_ENG_ITEMS_RESV_15                           0x4000
+#define IC_TEST_ENG_ITEMS_RESV_16                           0x8000
+
+#define WEARABLE_FT_TEST_RESULT_IC_INIT_STATE 	                0xDFFFE5DF
+#define WEARABLE_FT_TEST_RESULT_PANEL_INIT_STATE                0x20001A20
+#define WEARABLE_FT_TEST_RESULT_PASS        					0x00000000
+#define WEARABLE_FT_TEST_RESULT_OPEN_NG     					0x00000001
+#define WEARABLE_FT_TEST_RESULT_SHORT_NG    					0x00000002
+#define WEARABLE_FT_TEST_RESULT_UB_NG	      					0x00000004
+#define WEARABLE_FT_TEST_RESULT_I2C_NG      					0x00000008
+#define WEARABLE_FT_TEST_RESULT_INT_NG      					0x00000010
+#define WEARABLE_FT_TEST_RESULT_PANEL_TEST_1_NG					0x00000020
+#define WEARABLE_FT_TEST_RESULT_RESET_NG    					0x00000040
+#define WEARABLE_FT_TEST_RESULT_CB_NG       					0x00000080
+#define WEARABLE_FT_TEST_RESULT_PRAM_NG     					0x00000100
+#define WEARABLE_FT_TEST_RESULT_NORMAL_FW_NG     				0x00000200
+#define WEARABLE_FT_TEST_RESULT_BURN_CC_NG     					0x00000400
+#define WEARABLE_FT_TEST_RESULT_PANEL_TEST_3_NG     			0x00000800
+#define WEARABLE_FT_TEST_RESULT_GET_DATA_NG					    0x00001000
+#define WEARABLE_FT_TEST_RESULT_FLASH_ID_NG    					0x00002000
+#define WEARABLE_FT_TEST_RESULT_NORMAL_FW_VER_NG    			0x00004000
+#define WEARABLE_FT_TEST_RESULT_TEST_FW_VER_NG      			0x00008000
+#define WEARABLE_FT_TEST_RESULT_UC_NG  	    					0x00010000
+#define WEARABLE_FT_TEST_RESULT_SINGLE_CC_OPEN_NG				0x00020000
+#define WEARABLE_FT_TEST_RESULT_SINGLE_CC_SHORT_NG				0x00040000
+#define WEARABLE_FT_TEST_RESULT_CC_CALIBRATION_NG				0x00080000
+#define WEARABLE_FT_TEST_RESULT_TEST_INIT_NG				    0x00100000
+#define WEARABLE_FT_TEST_RESULT_LOAD_TESTFW_NG 				    0x00200000
+#define WEARABLE_FT_TEST_RESULT_BURN_FW_NG     					0x00400000
+#define WEARABLE_FT_TEST_RESULT_IC_FW_VERIFY_NG					0x00800000
+#define WEARABLE_FT_TEST_RESULT_EXT_FLASH_EMPTY_NG				0x01000000
+#define WEARABLE_FT_TEST_RESULT_MCU_HOLD_NG						0x02000000
+#if SELFTEST
+#define WEARABLE_FT_TEST_RESULT_SYSFS_NG						0x04000000
+#else
+#define WEARABLE_FT_TEST_RESULT_AUO_JIG_NG						0x04000000
+#endif
+#define WEARABLE_FT_TEST_RESULT_NORMAL_FW_RESET_NG              0x08000000
+#define WEARABLE_FT_TEST_RESULT_POWER_ON_NG						0x10000000
+#define WEARABLE_FT_TEST_RESULT_CMD_NG                          0x20000000
+#define WEARABLE_FT_TEST_RESULT_IC_SUB_VERSION_NG               0x40000000
+#define WEARABLE_FT_TEST_RESULT_IC_VERSION_NG                   0x80000000
+
+#define WEARBLE_FT_BURN_CC_SYSTEM_NG_CASE						1
+#define WEARBLE_FT_FW_VER_SYSTEM_NG_CASE						2
+#define WEARBLE_FT_I2C_SYSTEM_NG_CASE							3
+#define WEARBLE_FT_INT_SYSTEM_NG_CASE							4
+#define WEARBLE_FT_LOAD_TEST_FW_SYSTEM_NG_CASE					5
+#define WEARBLE_FT_PRAM_SYSTEM_NG_CASE							6
+#define WEARBLE_FT_RESET_PIN_SYSTEM_NG_CASE						7
+
+
+// Define ic test state
+typedef enum {
+	IC_TEST_INIT = 0,
+	IC_TEST_SYSTEM,					//1
+	IC_TEST_BURN_FW,				//2
+	IC_TEST_BURN_CC,				//3
+	IC_TEST_LOAD_TEST_FW,			//4
+	IC_TEST_ENTER_FW_TEST_MODE,		//5
+	IC_TEST_OPEN_SHORT,				//6
+	IC_TEST_READ_TEST_FW_DATA,	    //7
+	IC_ENTER_NORMAL_FW,				//8
+	IC_TEST_UC_UB,					//9
+	IC_TEST_PANEL_PATTERN_TEST,     //10
+	IC_TEST_EXIT,					//11
+} IC_TEST_STATE;
+
+#pragma pack(push)
+#pragma pack(1)
+typedef struct {
+	//-------- FT Test Info (Byte 16)-----------------//
+	unsigned char u8_device_id;
+	unsigned char u8_company_id;
+	unsigned char u8_project_id;
+	unsigned char u8_station_id;
+	unsigned short u16_ft_test_item;
+	unsigned short u16_ft_eng_item;
+	unsigned short u16_ft_test_info_1;
+	unsigned short u16_ft_test_info_2;
+	unsigned short u16_ft_test_info_3;
+	unsigned short u16_ft_test_info_4;
+} st_test_info;
+extern st_test_info g_st_test_info;
+
+typedef struct {
+	//-------- FT Test Threshold (Byte 26)----------------//
+	unsigned char u8_ft_test_company_id;
+	unsigned char u8_ft_test_station_id;
+	signed short i16_ft_test_open_lower_thd;              //TEST_THD_01
+	signed short i16_ft_test_short_upper_thd;             //TEST_THD_02
+	signed short i16_ft_test_short_lower_thd;             //TEST_THD_03
+	signed short i16_ft_test_single_cc_upper_thd;         //TEST_THD_04
+	signed short i16_ft_test_single_cc_lower_thd;	      //TEST_THD_05
+	signed short i16_ft_test_uniformity_bl_upper_thd;     //TEST_THD_06
+	signed short i16_ft_test_uniformity_bl_lower_thd;     //TEST_THD_07
+	signed short i16_ft_test_uniformity_cc_upper_thd;	  //TEST_THD_08
+	signed short i16_ft_test_uniformity_cc_lower_thd;     //TEST_THD_09
+	signed short i16_ft_test_panel_test_1_thd;            //TEST_THD_10
+	signed short i16_ft_test_panel_test_3_thd;            //TEST_THD_11
+	signed short i16_ft_test_panel_test_2_thd;            //TEST_THD_12
+	//-------- reserve (Byte 10)-----------------//
+	signed short i16_ft_test_panel_test_2_s2_thd;         //TEST_THD_13
+	signed short i16_ft_test_thd_14;
+	signed short i16_ft_test_thd_15;
+	signed short i16_ft_test_thd_16;
+	signed short i16_ft_test_thd_resv;
+} st_test_threshold;
+extern st_test_threshold g_st_test_thd;
+
+typedef struct {
+	//-------- FT Test Parameter (Byte 48)-----------------//
+	unsigned char u8_para_resv_0[40];
+	unsigned int u32_normal_fw_version;
+	unsigned int u32_test_fw_version;
+} st_test_para_resv;
+extern st_test_para_resv g_st_test_para_resv;
+
+#pragma pack(pop)
+
+extern unsigned char g_u8_ic_test_state;
+extern STATUS wearable_ic_test_read_info(void);
+extern STATUS wearable_ic_test_init(void);
+extern void handle_ic_test(void);
+extern void wearable_ic_test_init_buffer(void);
+extern STATUS handle_burn_log_to_flash(void);
+extern void handle_set_display_interface(unsigned char u8_interface);
+

+ 468 - 0
qcom/opensource/touch-drivers/raydium/drv_interface.c

@@ -0,0 +1,468 @@
+/* drv_interface.c
+ *
+ * Raydium TouchScreen driver.
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ * Qualcomm Innovation Center, Inc. chooses to use it under GPLv2
+ * Copyright (c) 2021  Raydium tech Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * BSD LICENSE
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name of Google Inc. or Linaro Ltd. nor the names of
+ *    its contributors may be used to endorse or promote products
+ *    derived from this software without specific prior written
+ *    permission.
+ * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <linux/timer.h>
+#include <linux/ktime.h>
+#include <linux/delay.h>
+#include <linux/unistd.h>
+#include <linux/string.h>
+#include <linux/of_gpio.h>
+#include <linux/version.h>
+#include "chip_raydium/ic_drv_global.h"
+#include "chip_raydium/ic_drv_interface.h"
+#include "drv_interface.h"
+#include "raydium_selftest.h"
+#include "raydium_driver.h"
+#include "chip_raydium/f303_ic_control.h"
+
+unsigned char g_u8_m_buf[2][128];
+unsigned char g_u8_ini_flash[0x400];
+struct raydium_ts_data *ts;
+unsigned char g_u8_mute_i2c_err_log;
+
+
+STATUS i2c_burst_read_pda2(unsigned char u8_addr, unsigned short u16ReadLen, unsigned char *p_u8_output_buf)
+{
+	return ERROR;
+}
+STATUS i2c_burst_write_pda2(unsigned char u8_addr, unsigned char bWriteLen, unsigned char *bValue)
+{
+	return ERROR;
+}
+
+unsigned char spi_write_pda(unsigned int u32_addr, unsigned char u8_write_len, unsigned char *bValue, unsigned char u8_trans_mode)
+{
+	return ERROR;
+}
+unsigned char spi_read_pda(unsigned int u32_addr, unsigned char u8_read_len, unsigned char *p_u8_output_buf)
+{
+	return ERROR;
+}
+STATUS burn_fw_3x(void)
+{
+	g_u32_wearable_test_result &= ~(WEARABLE_FT_TEST_RESULT_CMD_NG | WEARABLE_FT_TEST_RESULT_BURN_FW_NG);
+	return SUCCESS;
+}
+unsigned char fw_upgrade(unsigned char type)
+{
+	int i32_ret = ERROR;
+
+	i32_ret = raydium_burn_fw(g_raydium_ts->client);
+	if (i32_ret < 0)
+		pr_err("[touch]FW update fail:%d\n", i32_ret);
+	return i32_ret;
+
+}
+unsigned char read_flash_data(unsigned int u32_addr, unsigned short u16_lenth)
+{
+	unsigned int u32_data_offset;
+
+	if (g_u16_dev_id == DEVICE_ID_2X) {
+		u32_data_offset = u32_addr - 0x800;
+
+		if (u32_addr < 0x8000) {
+			if (u32_data_offset >= 0x8000 && u32_addr < F302_DONGLE_FLASH_INI_ADDR) {
+				u32_data_offset = u32_data_offset - 0x6200;
+				memcpy(g_u8_data_buf, g_rad_para_image + u32_data_offset, u16_lenth);
+			} else if (u32_addr >= F302_DONGLE_FLASH_INI_ADDR) {
+				u32_data_offset = u32_addr - F302_DONGLE_FLASH_INI_ADDR;
+				memcpy(g_u8_data_buf, g_u8_ini_flash + u32_data_offset, u16_lenth);
+			}  else
+				memcpy(g_u8_data_buf, g_rad_fw_image + u32_data_offset, u16_lenth);
+		} else {
+			u32_data_offset -= 0x8000;
+			if (u32_data_offset >= 0x6200 && u32_addr < F302_DONGLE_FLASH_INI_ADDR) {
+				u32_data_offset = u32_data_offset - 0x6200;
+				memcpy(g_u8_data_buf, g_rad_testpara_image + u32_data_offset, u16_lenth);
+			} else if (u32_addr >= F302_DONGLE_FLASH_INI_ADDR) {
+				u32_data_offset = u32_addr - F302_DONGLE_FLASH_INI_ADDR;
+				memcpy(g_u8_data_buf, g_u8_ini_flash + u32_data_offset, u16_lenth);
+			} else {
+				memcpy(g_u8_data_buf, g_rad_testfw_image + u32_data_offset, u16_lenth);
+			}
+		}
+		return TRUE;
+	} else if (g_u16_dev_id == DEVICE_ID_3X) {
+		u32_data_offset = u32_addr - 0x800;
+
+		if (u32_addr < 0x7800) {
+			if (u32_data_offset >= 0x7300 && u32_addr < F303_DONGLE_FLASH_INI_ADDR) {
+				u32_data_offset = u32_data_offset - 0x7300;
+				memcpy(g_u8_data_buf, g_rad_para_image + u32_data_offset, u16_lenth);
+			} else
+				memcpy(g_u8_data_buf, g_rad_fw_image + u32_data_offset, u16_lenth);
+		} else {
+			u32_data_offset -= 0x7800;
+			if (u32_data_offset >= 0x7300 && u32_addr < F303_DONGLE_FLASH_INI_ADDR) {
+				u32_data_offset = u32_data_offset - 0x7300;
+				memcpy(g_u8_data_buf, g_rad_testpara_image + u32_data_offset, u16_lenth);
+			} else if (u32_addr >= F303_DONGLE_FLASH_INI_ADDR) {
+				u32_data_offset = u32_addr - F303_DONGLE_FLASH_INI_ADDR;
+				pr_info("ini addr 0x%x offset %d\r\n", u32_addr, u32_data_offset);
+				memcpy(g_u8_data_buf, g_u8_ini_flash + u32_data_offset, u16_lenth);
+			} else {
+				memcpy(g_u8_data_buf, g_rad_testfw_image + u32_data_offset, u16_lenth);
+			}
+		}
+		return TRUE;
+	}
+	return FALSE;
+}
+
+unsigned int get_system_time(void)
+{
+/*	unsigned int u32_timer;
+ *	struct timeval timer;
+ *	do_gettimeofday(&timer);
+ *	u32_timer = (timer.tv_sec % 1000) * 1000 + (timer.tv_usec / 1000);
+ *	return u32_timer;
+ */
+	#if (KERNEL_VERSION(5, 0, 0) <= LINUX_VERSION_CODE)
+	struct timespec64 ts;
+
+	ktime_get_ts64(&ts);
+	return (ts.tv_sec*1000 + ts.tv_nsec/1000000);
+	#else
+	struct timeval tv;
+
+	do_gettimeofday(&tv);
+	return (tv.tv_sec*1000 + tv.tv_usec/1000);
+	#endif
+}
+
+unsigned char gpio_touch_int_pin_state_access(void)
+{
+	return gpio_get_value(ts->irq_gpio);
+}
+
+unsigned char gpio_touch_int_access(unsigned char u8_is_clear_flag)
+{
+	unsigned char u8_flag = g_u8_raydium_flag;
+
+	if (u8_is_clear_flag && u8_flag)
+		g_u8_raydium_flag &= ~INT_FLAG;
+
+	return u8_flag;
+}
+
+unsigned char sysfs_burn_cc_bl(void)
+{
+	unsigned char ret = ERROR;
+
+	DEBUGOUT("%s\r\n", __func__);
+	ret = raydium_burn_comp(ts->client);
+	return ret;
+}
+
+unsigned char raydium_upgrade_test_fw_2x(unsigned long ul_fw_addr)
+{
+	int ret = ERROR;
+	unsigned char u8_retry = 2;
+	unsigned int u32_read;
+	unsigned int u32_write;
+
+RETRY:
+	gpio_touch_reset_pin_control(0);
+	delay_ms(10);
+	gpio_touch_reset_pin_control(1);
+	delay_ms(2);
+	u32_write = 0x00000030;
+	handle_ic_write(0x50000918, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE);
+
+	if (raydium_load_test_fw(ts->client) == SUCCESS) {
+		ret = SUCCESS;
+		DEBUGOUT("### Raydium Load test FW SUCCESS ###\n");
+	}
+
+	handle_ic_read(0x6A04, 4, (unsigned char *)&u32_read, g_u8_drv_interface, I2C_WORD_MODE);
+
+	if (u32_read != g_st_test_para_resv.u32_test_fw_version) {
+		DEBUGOUT("Read FW version NG=0x%x:0x%x!!\r\n", u32_read, g_st_test_para_resv.u32_test_fw_version);
+		goto ERROR_EXIT;
+	}
+
+	return ret;
+
+ERROR_EXIT:
+	if (u8_retry) {
+		u8_retry--;
+		goto RETRY;
+	}
+
+	return ERROR;
+}
+
+unsigned char raydium_upgrade_test_fw_3x(unsigned long ul_fw_addr)
+{
+	int ret = ERROR;
+	unsigned char u8_retry = 2;
+	unsigned int u32_read;
+
+RETRY:
+
+	if (raydium_load_test_fw(ts->client) == SUCCESS) {
+		ret = SUCCESS;
+		DEBUGOUT("### Raydium Load test FW SUCCESS ###\n");
+	}
+
+	handle_ic_read(0x7B04, 4, (unsigned char *)&u32_read, g_u8_drv_interface, I2C_WORD_MODE);
+	if (u32_read != g_st_test_para_resv.u32_test_fw_version) {
+		DEBUGOUT("Read FW version NG=0x%x:0x%x!!\r\n", u32_read, g_st_test_para_resv.u32_test_fw_version);
+		goto ERROR_EXIT;
+	}
+
+	return ret;
+
+ERROR_EXIT:
+	if (u8_retry) {
+		u8_retry--;
+		goto RETRY;
+	}
+
+	return ERROR;
+}
+
+void gpio_touch_reset_pin_control(unsigned char u8_high)
+{
+
+	if (u8_high)
+		gpio_set_value(ts->rst_gpio, 1);
+	else
+		gpio_set_value(ts->rst_gpio, 0);
+
+	return;
+
+}
+
+
+void gpio_touch_hw_reset(void)
+{
+	gpio_touch_reset_pin_control(0);
+	delay_ms(10);
+	gpio_touch_reset_pin_control(1);
+
+}
+void set_raydium_ts_data(struct raydium_ts_data *ts_old)
+{
+	ts = ts_old;
+}
+
+/******************************************************************************
+ **Function name:handle_ic_read
+ **
+ **Descriptions:handle read data from ic
+ **
+ **parameters:u32_addr,Address
+ **	      u8_read_len,Read datalength
+ **	      p_u8_output_buf,Data buffer
+ **	      u8_interface,SPI or I2C
+ **	      u8_trans_modePDA2_MODE, PDA_WORD_MODE, PDA_BYTE_MODE, MCU_MODE
+ **
+ **Returned value:ERROR,SUCCESS
+ **
+ ******************************************************************************
+ */
+unsigned char handle_ic_read(
+	unsigned int u32_addr,
+	unsigned short u8_read_len,
+	unsigned char *p_u8_output_buf,
+	unsigned char u8_interface,
+	unsigned char u8_trans_mode)
+{
+	if (u8_trans_mode == I2C_PDA2_MODE) {
+		/*PDA2 MODE */
+		if (raydium_i2c_pda2_read(g_raydium_ts->client, (unsigned char)u32_addr, p_u8_output_buf, u8_read_len) == ERROR) {
+			DEBUGOUT("%s\r\n", __func__);
+			return ERROR;
+		}
+	} else {
+		/*PDA MODE*/
+		if ((u8_trans_mode == I2C_WORD_MODE) && (u32_addr & 0x00000003)) {
+			DEBUGOUT("[HRW] Handle Read Word ADDR Not Word Align!!\r\n");
+			return ERROR;
+		}
+
+		if (u8_interface == SPI_INTERFACE) {
+			if (spi_read_pda(u32_addr, u8_read_len, p_u8_output_buf) == ERROR) {
+				DEBUGOUT("%s\r\n", __func__);
+				return ERROR;
+			}
+		} else {
+			if (g_u16_dev_id == DEVICE_ID_3X) {
+				if ((u8_interface & I2C_PDA_MODE) != 0)  {
+					if (raydium_i2c_pda_read(g_raydium_ts->client, u32_addr, p_u8_output_buf, u8_read_len) == ERROR) {
+						DEBUGOUT("%s\r\n", __func__);
+						return ERROR;
+					}
+				} else {
+					if (raydium_i2c_read_pda_via_pda2(g_raydium_ts->client, u32_addr, p_u8_output_buf, u8_read_len) == ERROR) {
+						DEBUGOUT("%s\r\n", __func__);
+						return ERROR;
+					}
+				}
+			} else {
+				if (raydium_i2c_pda_read(g_raydium_ts->client, u32_addr, p_u8_output_buf, u8_read_len) == ERROR) {
+					DEBUGOUT("%s\r\n", __func__);
+					return ERROR;
+				}
+			}
+		}
+	}
+
+	return SUCCESS;
+}
+
+/******************************************************************************
+ ** Function name:handle_ic_write
+ **
+ ** Descriptions:handle write data to ic
+ **
+ ** parameters:u32_addr,Address
+ **	       u8_write_len,datalength
+ **	       bValue,Data
+ **	       u8_interface,SPI or I2C
+ **	       u8_trans_modePDA2_MODE, PDA_WORD_MODE, PDA_BYTE_MODE, MCU_MODE
+ **
+ ** Returned value:ERROR,SUCCESS
+ **
+ ******************************************************************************/
+unsigned char handle_ic_write(
+	unsigned int u32_addr,
+	unsigned char u8_write_len,
+	unsigned char *bValue,
+	unsigned char u8_interface,
+	unsigned char u8_trans_mode)
+{
+	if (u8_trans_mode == I2C_PDA2_MODE) {
+		/*PDA2 MODE*/
+		if (raydium_i2c_pda2_write(g_raydium_ts->client, (unsigned char)u32_addr, bValue, u8_write_len) == ERROR) {
+			DEBUGOUT("%s\r\n", __func__);
+			return ERROR;
+		}
+	} else {
+		/*PDA MODE*/
+		if ((u8_trans_mode == I2C_WORD_MODE) && (u32_addr & 0x00000003)) {
+			DEBUGOUT("[I2CRW] Handle Write Word ADDR Not Word Align!!\r\n");
+			return ERROR;
+		}
+
+		if (u8_interface == SPI_INTERFACE) {
+			switch (u8_trans_mode) {
+			case I2C_BYTE_MODE:
+				u8_trans_mode = SPI_BYTE_MODE;
+				break;
+			case I2C_WORD_MODE:
+				u8_trans_mode = SPI_WORD_MODE;
+				break;
+			/*			case I2C_MCU_MODE:
+			 *
+			 *				u8_trans_mode = SPI_MCU_MODE;
+			 *
+			 *				break;
+			 */
+			case SPI_BYTE_MODE:
+			case SPI_WORD_MODE:
+				break;
+			default:
+				DEBUGOUT("%s%d\r\n", __func__, u8_trans_mode);
+				return ERROR;
+			}
+
+			if (spi_write_pda(u32_addr, u8_write_len, bValue, u8_trans_mode) == ERROR) {
+				DEBUGOUT("%s\r\n", __func__);
+				return ERROR;
+			}
+		} else {
+			if (g_u16_dev_id == DEVICE_ID_3X) {
+				if ((u8_interface & I2C_PDA_MODE) != 0)  {
+					if (raydium_i2c_pda_write(g_raydium_ts->client, u32_addr, bValue, u8_write_len) == ERROR) {
+						DEBUGOUT("%s\r\n", __func__);
+						return ERROR;
+					}
+				} else {
+					if (raydium_i2c_write_pda_via_pda2(g_raydium_ts->client, u32_addr, bValue, u8_write_len) == ERROR) {
+						DEBUGOUT("%s\r\n", __func__);
+						return ERROR;
+					}
+				}
+			} else {
+				if (raydium_i2c_pda_write(g_raydium_ts->client, u32_addr, bValue, u8_write_len) == ERROR) {
+					DEBUGOUT("%s\r\n", __func__);
+					return ERROR;
+				}
+			}
+		}
+	}
+
+	return SUCCESS;
+}
+
+/******************************************************************************
+ ** Function name:handle_display_write
+ **
+ ** Descriptions:handle write data to display
+ **
+ ** parameters:p_u8_data,Data
+ **	       u16DataLength,datalength
+ **
+ **
+ ** Returned value:ERROR,SUCCESS
+ **
+ ******************************************************************************/
+unsigned char handle_display_write(
+	unsigned char *p_u8_data,
+	unsigned short u16DataLength)
+{
+
+	if (WriteDriverByTouchMode(p_u8_data, u16DataLength) == ERROR) {
+		DEBUGOUT("[HDW] WriteDriverByTouchMode NG!\r\n");
+		return ERROR;
+	}
+
+	return SUCCESS;
+}

+ 84 - 0
qcom/opensource/touch-drivers/raydium/drv_interface.h

@@ -0,0 +1,84 @@
+/* drv_interface.h
+ *
+ * Raydium TouchScreen driver.
+ *
+ * Copyright (c) 2021  Raydium tech Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "Config.h"
+#include "raydium_driver.h"
+
+#define DEBUGOUT pr_info
+#define delay_ms msleep
+
+#define I2C_INTERFACE			0
+#define SPI_INTERFACE			1
+
+#define M_RX_BUF				1
+#define M_TX_BUF				0
+
+#define     __I     volatile
+#define     __O     volatile
+#define     __IO    volatile
+
+
+#define WORD					4
+
+#define RAD_CMD_START_BURN		0x01
+#define RAD_CMD_LOAD_TESTFW		0x04
+#define RAD_CMD_UPDATE_BIN		0x80
+#define RAD_CMD_UPDATE_END		0x81
+#define RAD_CMD_BURN_FINISH		0x82
+
+#define WEARABLE_FT_TEST_RESULT_AUO_JIG_NG WEARABLE_FT_TEST_RESULT_SYSFS_NG
+
+
+extern unsigned char g_u8_m_buf[2][128];
+extern struct timeval timer;
+extern unsigned char g_rad_fw_flash[32768], g_rad_testfw_flash[32768];
+extern unsigned char g_u8_ini_flash[0x400];
+extern unsigned long g_u32_save_config[64];
+extern unsigned char g_u8_mute_i2c_err_log;
+extern short g_i16_raw_data_frame_buffer[101][50];
+extern unsigned char g_u8_test_log_burn_times;
+
+extern unsigned char read_flash_data(unsigned int u32_addr, unsigned short u16_lenth);
+extern unsigned int get_system_time(void);
+
+extern unsigned char sysfs_mode_control(unsigned char u8_type);
+//extern unsigned char sysfs_read_int_flag(unsigned char *p_u8_flag, unsigned char u8_is_clear_flag);
+extern unsigned char sysfs_write_int_flag(char *p_cmd);
+//extern unsigned char sysfs_i2c_read(char *p_u8_addr, char u8_len, unsigned char *p_u8_data);
+//extern unsigned char sysfs_i2c_write(char *p_u8_addr, char u8_len, unsigned char *p_u8_data);
+extern unsigned char sysfs_do_cal(void);
+extern unsigned char sysfs_burn_cc_bl(void);
+
+extern void gpio_touch_reset_pin_control(unsigned char u8_high);
+extern void gpio_touch_hw_reset(void);
+extern unsigned char fw_upgrade(unsigned char type);
+extern unsigned char gpio_touch_int_access(unsigned char u8_is_clear_flag);
+extern unsigned char gpio_touch_int_pin_state_access(void);
+
+extern unsigned char handle_read_word(unsigned int addr, unsigned int *p_data);
+extern unsigned char handle_write_word(unsigned int u32_addr, unsigned int u32_data);
+extern unsigned char handle_read_pda(unsigned int u32_addr, unsigned char u8_read_len, unsigned char *p_u8_output_buf);
+extern unsigned char handle_write_pda(unsigned int u32_addr, unsigned char u8_write_len, unsigned char *bValue, unsigned char u8_trans_mode);
+extern unsigned char handle_ic_read(unsigned int u32_addr, unsigned short u8_read_len, unsigned char *p_u8_output_buf, unsigned char u8_interface, unsigned char u8_trans_mode);
+extern unsigned char handle_ic_write(unsigned int u32_addr, unsigned char u8_write_len, unsigned char *bValue, unsigned char u8_interface, unsigned char u8_trans_mode);
+
+extern unsigned char  raydium_upgrade_test_fw(unsigned long ul_fw_addr);
+extern unsigned char  raydium_upgrade_test_fw_3x(unsigned long ul_fw_addr);
+extern unsigned char  raydium_upgrade_test_fw_2x(unsigned long ul_fw_addr);
+extern void set_raydium_ts_data(struct raydium_ts_data *ts_old);
+

+ 7791 - 0
qcom/opensource/touch-drivers/raydium/rad_fw_image_30.h

@@ -0,0 +1,7791 @@
+/* rad_fw_image_30.h
+ *
+ * QTI elects to receive under BSD license only.
+ *
+ * Raydium TouchScreen driver.
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+
+ * Copyright (c) 2021  Raydium tech Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.i2c_driver/rad_fw_image_30.h
+ *
+ * BSD LICENSE
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name of Google Inc. or Linaro Ltd. nor the names of
+ *    its contributors may be used to endorse or promote products
+ *    derived from this software without specific prior written
+ *    permission.
+ * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* RAYDIUM FW VERSION : 0x02020001 */
+
+#define RAD_30 0x3202
+const unsigned char u8_rad_boot_30[] = {
+	0x18, 0x01, 0x00, 0x20, 0x95, 0x01, 0x00, 0x00,
+	0x99, 0x01, 0x00, 0x00, 0x9B, 0x01, 0x00, 0x00,
+	0x9D, 0x01, 0x00, 0x00, 0x9F, 0x01, 0x00, 0x00,
+	0xA1, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0xA3, 0x01, 0x00, 0x00,
+	0xA5, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0xA7, 0x01, 0x00, 0x00, 0x81, 0x0F, 0x00, 0x00,
+	0xE1, 0x0C, 0x00, 0x00, 0x01, 0x0A, 0x00, 0x00,
+	0xA1, 0x0F, 0x00, 0x00, 0x61, 0x0E, 0x00, 0x00,
+	0xA1, 0x0D, 0x00, 0x00, 0x41, 0x0A, 0x00, 0x00,
+	0xAB, 0x01, 0x00, 0x00, 0x01, 0x0B, 0x00, 0x00,
+	0xAB, 0x01, 0x00, 0x00, 0x41, 0x0B, 0x00, 0x00,
+	0xE1, 0x0A, 0x00, 0x00, 0xA1, 0x0A, 0x00, 0x00,
+	0xC1, 0x0A, 0x00, 0x00, 0x81, 0x0A, 0x00, 0x00,
+	0xF1, 0x0F, 0x00, 0x00, 0xAB, 0x01, 0x00, 0x00,
+	0x03, 0xF3, 0x00, 0x0B, 0x03, 0x48, 0x85, 0x46,
+	0x00, 0xF0, 0x92, 0xF8, 0x00, 0x48, 0x00, 0x47,
+	0xCB, 0x00, 0x00, 0x00, 0x18, 0x01, 0x00, 0x20,
+	0x80, 0xF3, 0x08, 0x88, 0x70, 0x47, 0x00, 0x00,
+	0x10, 0xB5, 0x25, 0x48, 0x81, 0x69, 0x01, 0x22,
+	0x92, 0x03, 0x11, 0x43, 0x81, 0x61, 0x41, 0x69,
+	0x10, 0x22, 0x11, 0x43, 0x41, 0x61, 0xD4, 0x01,
+	0x20, 0x68, 0xFF, 0xF7, 0xED, 0xFF, 0x60, 0x68,
+	0x1E, 0x49, 0x48, 0x60, 0x08, 0x60, 0x80, 0x47,
+	0x10, 0xBD, 0x01, 0x26, 0xB6, 0x07, 0x30, 0x68,
+	0x1B, 0x49, 0x08, 0x43, 0x30, 0x60, 0x18, 0x49,
+	0x48, 0x69, 0x20, 0x22, 0x10, 0x43, 0x48, 0x61,
+	0x00, 0xF0, 0x34, 0xF8, 0x00, 0xF0, 0xA5, 0xF8,
+	0x16, 0x4F, 0x17, 0x4C, 0x01, 0x25, 0x60, 0x68,
+	0x00, 0x28, 0x06, 0xD0, 0x01, 0x28, 0x16, 0xD0,
+	0x02, 0x28, 0xF8, 0xD1, 0xFF, 0xF7, 0xD0, 0xFF,
+	0xF5, 0xE7, 0x00, 0xF0, 0x5F, 0xF9, 0x03, 0x28,
+	0x07, 0xD0, 0x02, 0x28, 0xEF, 0xD1, 0x65, 0x60,
+	0x7D, 0x20, 0xC0, 0x00, 0x00, 0xF0, 0x84, 0xF8,
+	0xE9, 0xE7, 0xB8, 0x68, 0x00, 0x28, 0xE6, 0xD1,
+	0x02, 0x20, 0x60, 0x60, 0xE3, 0xE7, 0x00, 0xF0,
+	0x91, 0xFA, 0x01, 0x28, 0xDF, 0xD1, 0x00, 0x20,
+	0x60, 0x60, 0x75, 0x60, 0xDB, 0xE7, 0x00, 0x00,
+	0x00, 0x09, 0x00, 0x50, 0x0C, 0x00, 0x00, 0x20,
+	0x40, 0x01, 0x00, 0xC8, 0x10, 0x02, 0x00, 0x20,
+	0x00, 0x02, 0x00, 0x20, 0x0D, 0x48, 0x0C, 0x49,
+	0x01, 0x61, 0x0D, 0x4A, 0x03, 0x21, 0xD1, 0x61,
+	0x01, 0x21, 0x89, 0x07, 0x0A, 0x68, 0x0B, 0x4B,
+	0x1A, 0x43, 0x0A, 0x60, 0x0A, 0x4A, 0x02, 0x63,
+	0x02, 0x6A, 0x52, 0x00, 0x52, 0x08, 0x02, 0x62,
+	0xC2, 0x6A, 0x04, 0x23, 0x1A, 0x43, 0xC2, 0x62,
+	0x10, 0x20, 0x48, 0x60, 0x70, 0x47, 0x00, 0x00,
+	0x00, 0x00, 0x06, 0x07, 0x00, 0x06, 0x00, 0x50,
+	0x00, 0x0E, 0x00, 0x50, 0x10, 0x01, 0x00, 0x80,
+	0x0A, 0x66, 0x00, 0x00, 0x05, 0x48, 0x00, 0x47,
+	0xFE, 0xE7, 0xFE, 0xE7, 0xFE, 0xE7, 0xFE, 0xE7,
+	0xFE, 0xE7, 0xFE, 0xE7, 0xFE, 0xE7, 0xFE, 0xE7,
+	0xFE, 0xE7, 0xFE, 0xE7, 0x85, 0x00, 0x00, 0x00,
+	0x06, 0x4C, 0x01, 0x25, 0x06, 0x4E, 0x05, 0xE0,
+	0xE3, 0x68, 0x07, 0xCC, 0x2B, 0x43, 0x0C, 0x3C,
+	0x98, 0x47, 0x10, 0x34, 0xB4, 0x42, 0xF7, 0xD3,
+	0xFF, 0xF7, 0x60, 0xFF, 0xDC, 0x06, 0x00, 0x00,
+	0xFC, 0x06, 0x00, 0x00, 0x30, 0xB4, 0x74, 0x46,
+	0x64, 0x1E, 0x25, 0x78, 0x64, 0x1C, 0xAB, 0x42,
+	0x00, 0xD2, 0x1D, 0x46, 0x63, 0x5D, 0x5B, 0x00,
+	0xE3, 0x18, 0x30, 0xBC, 0x18, 0x47, 0x02, 0xE0,
+	0x08, 0xC8, 0x12, 0x1F, 0x08, 0xC1, 0x00, 0x2A,
+	0xFA, 0xD1, 0x70, 0x47, 0x70, 0x47, 0x00, 0x00,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x49, 0x00, 0x20,
+	0x88, 0x60, 0xC8, 0x60, 0x48, 0x60, 0x3F, 0x22,
+	0x0A, 0x60, 0x08, 0x61, 0xFB, 0x49, 0x08, 0x60,
+	0xFB, 0x49, 0x08, 0x60, 0x48, 0x60, 0x70, 0x47,
+	0xE1, 0x21, 0x09, 0x01, 0x48, 0x43, 0x00, 0x21,
+	0x00, 0xE0, 0x49, 0x1C, 0x81, 0x42, 0xFC, 0xD3,
+	0x70, 0x47, 0x10, 0xB5, 0xF3, 0x49, 0x00, 0x20,
+	0x48, 0x60, 0x88, 0x60, 0xC8, 0x60, 0xF2, 0x4B,
+	0x98, 0x60, 0xEF, 0x4C, 0xA0, 0x69, 0x01, 0x21,
+	0x89, 0x03, 0x88, 0x43, 0xA0, 0x61, 0xFF, 0xF7,
+	0xD9, 0xFF, 0xA0, 0x69, 0x00, 0x05, 0x05, 0xD5,
+	0x82, 0x20, 0x99, 0x68, 0x00, 0x29, 0x01, 0xD1,
+	0x58, 0x60, 0xFA, 0xE7, 0x10, 0xBD, 0x70, 0xB5,
+	0x05, 0x46, 0x00, 0x20, 0x02, 0x46, 0x01, 0x26,
+	0x09, 0xE0, 0x34, 0x46, 0x94, 0x40, 0x33, 0x46,
+	0x2C, 0x42, 0x03, 0xD0, 0x8C, 0x1A, 0x64, 0x1E,
+	0xA3, 0x40, 0x18, 0x43, 0x52, 0x1C, 0x8A, 0x42,
+	0xF3, 0xD3, 0x70, 0xBD, 0xF0, 0xB5, 0x00, 0x24,
+	0xE4, 0x43, 0x0E, 0x46, 0x07, 0x46, 0x00, 0x25,
+	0x14, 0xE0, 0x78, 0x5D, 0x08, 0x21, 0xFF, 0xF7,
+	0xE2, 0xFF, 0xC1, 0xB2, 0xD9, 0x4A, 0x00, 0x20,
+	0x23, 0x46, 0x4B, 0x40, 0xDB, 0x07, 0x02, 0xD0,
+	0x64, 0x08, 0x54, 0x40, 0x00, 0xE0, 0x64, 0x08,
+	0x40, 0x1C, 0x49, 0x08, 0xC0, 0xB2, 0x08, 0x28,
+	0xF2, 0xD3, 0x6D, 0x1C, 0xB5, 0x42, 0xE8, 0xD3,
+	0x20, 0x21, 0x20, 0x46, 0xFF, 0xF7, 0xCB, 0xFF,
+	0xF0, 0xBD, 0x1F, 0x20, 0x80, 0x01, 0xCE, 0x49,
+	0xC0, 0x6B, 0x08, 0x60, 0xC8, 0x49, 0x80, 0x31,
+	0x0A, 0x68, 0x82, 0x42, 0x15, 0xD1, 0xCB, 0x48,
+	0xCB, 0x4A, 0x40, 0x6B, 0x10, 0x60, 0x4A, 0x68,
+	0x82, 0x42, 0x0E, 0xD1, 0xC9, 0x48, 0xCA, 0x4A,
+	0xC0, 0x6B, 0x10, 0x60, 0x8A, 0x68, 0x82, 0x42,
+	0x07, 0xD1, 0xC6, 0x48, 0xC7, 0x4A, 0x80, 0x30,
+	0xC0, 0x6B, 0x10, 0x60, 0xC9, 0x68, 0x81, 0x42,
+	0x01, 0xD0, 0x00, 0x20, 0x70, 0x47, 0x01, 0x20,
+	0x70, 0x47, 0x70, 0xB5, 0x0D, 0x46, 0x03, 0x46,
+	0xC1, 0x4A, 0xB7, 0x4E, 0x02, 0xE0, 0x01, 0x20,
+	0xFF, 0xF7, 0x7A, 0xFF, 0x74, 0x69, 0x1C, 0x40,
+	0xAC, 0x42, 0x04, 0xD0, 0x10, 0x46, 0x52, 0x1E,
+	0x92, 0xB2, 0x00, 0x28, 0xF3, 0xD1, 0x00, 0x2A,
+	0x01, 0xD0, 0x01, 0x20, 0x70, 0xBD, 0x00, 0x20,
+	0x70, 0xBD, 0xF0, 0xB5, 0x00, 0x23, 0x01, 0x24,
+	0xB6, 0x4D, 0xE4, 0x02, 0x19, 0x46, 0x1A, 0x46,
+	0x01, 0x28, 0x0E, 0xD0, 0x04, 0x28, 0x05, 0xD0,
+	0x10, 0x28, 0x0E, 0xD1, 0x03, 0x02, 0xB2, 0x4A,
+	0x21, 0x46, 0x0A, 0xE0, 0x09, 0x23, 0x61, 0x22,
+	0xA8, 0x49, 0x1B, 0x03, 0xD2, 0x00, 0x38, 0x31,
+	0x03, 0xE0, 0x80, 0x22, 0xFF, 0x21, 0x23, 0x46,
+	0xC9, 0x01, 0x9F, 0x4C, 0x20, 0x68, 0x1D, 0x26,
+	0xB0, 0x43, 0x20, 0x60, 0x23, 0x61, 0x40, 0x27,
+	0x11, 0xE0, 0x60, 0x69, 0x38, 0x43, 0x60, 0x61,
+	0x28, 0x46, 0x63, 0x69, 0x5B, 0x06, 0x04, 0xD5,
+	0x03, 0x46, 0x40, 0x1E, 0x80, 0xB2, 0x00, 0x2B,
+	0xF7, 0xD1, 0xE0, 0x6B, 0x01, 0xC1, 0x20, 0x69,
+	0x00, 0x1D, 0x20, 0x61, 0x12, 0x1F, 0x00, 0x2A,
+	0xEB, 0xD1, 0x20, 0x68, 0x30, 0x43, 0x20, 0x60,
+	0x01, 0x20, 0xF0, 0xBD, 0xF0, 0xB5, 0x8F, 0x4B,
+	0x98, 0x68, 0x01, 0x25, 0x8C, 0x4C, 0x00, 0x28,
+	0x09, 0xD0, 0x80, 0x27, 0x8C, 0x4E, 0x01, 0x28,
+	0x0F, 0xD0, 0x00, 0x21, 0x02, 0x28, 0x1E, 0xD0,
+	0x03, 0x28, 0x1F, 0xD1, 0x24, 0xE0, 0xA0, 0x69,
+	0x80, 0x04, 0x1B, 0xD4, 0xA0, 0x69, 0x00, 0x05,
+	0x01, 0xD5, 0x9D, 0x60, 0x16, 0xE0, 0x03, 0x20,
+	0x0F, 0xE0, 0x1D, 0x60, 0xA0, 0x20, 0x30, 0x60,
+	0x81, 0x20, 0x70, 0x60, 0xFF, 0xF7, 0x65, 0xFF,
+	0x00, 0x28, 0x00, 0xD1, 0x77, 0x60, 0xA0, 0x69,
+	0x01, 0x21, 0xC9, 0x02, 0x88, 0x43, 0xA0, 0x61,
+	0x02, 0x20, 0x98, 0x60, 0x02, 0xE0, 0x18, 0x68,
+	0x00, 0x28, 0x01, 0xD0, 0x00, 0x20, 0xF0, 0xBD,
+	0x99, 0x60, 0xFF, 0xF7, 0xEB, 0xFE, 0x27, 0xE0,
+	0x99, 0x60, 0xA0, 0x69, 0xC0, 0x06, 0x02, 0xD5,
+	0xA0, 0x69, 0x80, 0x05, 0x15, 0xD5, 0xFF, 0xF7,
+	0x48, 0xFF, 0x00, 0x28, 0x1C, 0xD1, 0x10, 0x20,
+	0xFF, 0xF7, 0x7F, 0xFF, 0x04, 0x20, 0xFF, 0xF7,
+	0x7C, 0xFF, 0x01, 0x20, 0xFF, 0xF7, 0x79, 0xFF,
+	0xA0, 0x69, 0x80, 0x05, 0x12, 0xD4, 0x21, 0x20,
+	0x00, 0x01, 0xA0, 0x61, 0x80, 0x06, 0x45, 0x60,
+	0x0C, 0xE0, 0xA0, 0x69, 0x40, 0x05, 0xA0, 0x69,
+	0x04, 0xD5, 0x01, 0x21, 0x89, 0x02, 0x88, 0x43,
+	0xA0, 0x61, 0x01, 0xE0, 0x40, 0x06, 0x01, 0xD5,
+	0x03, 0x20, 0xF0, 0xBD, 0x77, 0x60, 0x02, 0x20,
+	0xF0, 0xBD, 0xF0, 0xB5, 0x69, 0x4F, 0x00, 0x26,
+	0x5B, 0x4C, 0x3E, 0x70, 0x21, 0x68, 0x62, 0x68,
+	0x91, 0x43, 0x21, 0x60, 0x04, 0x28, 0x2C, 0xD0,
+	0x10, 0x28, 0x27, 0xD0, 0x20, 0x28, 0x2C, 0xD1,
+	0x5A, 0x48, 0xC1, 0x21, 0x89, 0x00, 0x38, 0x30,
+	0xFF, 0xF7, 0xEC, 0xFE, 0x59, 0x4D, 0xE9, 0x6B,
+	0x88, 0x42, 0x18, 0xD0, 0x20, 0x68, 0x04, 0x21,
+	0x88, 0x43, 0x20, 0x60, 0x00, 0x20, 0x41, 0x1E,
+	0x1F, 0x22, 0x92, 0x02, 0x83, 0x00, 0x9B, 0x18,
+	0x40, 0x1C, 0xC0, 0xB2, 0x99, 0x67, 0xC1, 0x28,
+	0xF8, 0xD3, 0x4E, 0x48, 0xC1, 0x21, 0x89, 0x00,
+	0x38, 0x30, 0xAE, 0x63, 0xFF, 0xF7, 0xD2, 0xFE,
+	0xE8, 0x63, 0x01, 0x20, 0x38, 0x70, 0x20, 0x68,
+	0x10, 0x21, 0x04, 0xE0, 0x20, 0x68, 0x08, 0x21,
+	0x01, 0xE0, 0x20, 0x68, 0x02, 0x21, 0x88, 0x43,
+	0x20, 0x60, 0xF0, 0xBD, 0xF0, 0xB5, 0x3E, 0x4C,
+	0x0F, 0x46, 0x05, 0x46, 0xC6, 0x19, 0x20, 0x61,
+	0x0E, 0xE0, 0x60, 0x69, 0x02, 0x21, 0x08, 0x43,
+	0x60, 0x61, 0x08, 0x21, 0x08, 0x46, 0xFF, 0xF7,
+	0xFC, 0xFE, 0x00, 0x28, 0x16, 0xD0, 0x21, 0x69,
+	0x01, 0x20, 0x00, 0x03, 0x08, 0x18, 0x20, 0x61,
+	0x20, 0x69, 0xB0, 0x42, 0xED, 0xD3, 0x25, 0x61,
+	0x38, 0x0A, 0x00, 0x02, 0xE0, 0x60, 0x60, 0x69,
+	0x04, 0x21, 0x08, 0x43, 0x60, 0x61, 0x08, 0x21,
+	0x08, 0x46, 0xFF, 0xF7, 0xE6, 0xFE, 0x00, 0x28,
+	0x00, 0xD0, 0x01, 0x20, 0xF0, 0xBD, 0x2A, 0x4A,
+	0x52, 0x68, 0x04, 0x2A, 0x0D, 0xD0, 0x10, 0x2A,
+	0x06, 0xD0, 0x20, 0x2A, 0x0F, 0xD1, 0x00, 0x22,
+	0x02, 0x60, 0x11, 0x20, 0xC0, 0x01, 0x09, 0xE0,
+	0x01, 0x22, 0x12, 0x03, 0x02, 0x60, 0x2E, 0x48,
+	0x04, 0xE0, 0x09, 0x22, 0x12, 0x03, 0x02, 0x60,
+	0x61, 0x20, 0xC0, 0x00, 0x08, 0x60, 0x70, 0x47,
+	0xF0, 0xB5, 0x2B, 0x4C, 0x00, 0x25, 0x20, 0x68,
+	0x01, 0x23, 0x1D, 0x4E, 0x1B, 0x4F, 0x00, 0x28,
+	0x04, 0xD0, 0x01, 0x28, 0x0D, 0xD0, 0x02, 0x28,
+	0x14, 0xD1, 0x15, 0xE0, 0x24, 0x49, 0x08, 0x31,
+	0x08, 0x1F, 0xFF, 0xF7, 0xD4, 0xFF, 0x75, 0x60,
+	0xA8, 0x20, 0x30, 0x60, 0x23, 0x60, 0x3B, 0x60,
+	0x08, 0xE0, 0x38, 0x68, 0x00, 0x28, 0x05, 0xD1,
+	0x3B, 0x60, 0x70, 0x68, 0xA8, 0x28, 0x01, 0xD1,
+	0x02, 0x20, 0x20, 0x60, 0x00, 0x20, 0xF0, 0xBD,
+	0xA1, 0x68, 0x60, 0x68, 0x49, 0x1E, 0xFF, 0xF7,
+	0x95, 0xFF, 0x00, 0x28, 0xF7, 0xD0, 0x15, 0x48,
+	0x07, 0x4E, 0x00, 0x78, 0x01, 0x28, 0x08, 0xD1,
+	0x0A, 0x48, 0x38, 0x30, 0xB0, 0x60, 0x13, 0x49,
+	0x30, 0x01, 0xFF, 0xF7, 0x87, 0xFF, 0x00, 0x28,
+	0xE9, 0xD0, 0x75, 0x63, 0xB5, 0x63, 0x1F, 0xE0,
+	0x00, 0x09, 0x00, 0x50, 0x00, 0x02, 0x00, 0x20,
+	0x10, 0x02, 0x00, 0x20, 0x20, 0x83, 0xB8, 0xED,
+	0x98, 0x01, 0x00, 0x20, 0x40, 0x7C, 0x00, 0x00,
+	0x9C, 0x01, 0x00, 0x20, 0x40, 0x7F, 0x00, 0x00,
+	0xA0, 0x01, 0x00, 0x20, 0xA4, 0x01, 0x00, 0x20,
+	0xB8, 0x0B, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00,
+	0x78, 0x74, 0x00, 0x00, 0x14, 0x00, 0x00, 0x20,
+	0x00, 0x00, 0x00, 0x20, 0x07, 0x03, 0x00, 0x00,
+	0x01, 0x20, 0xC0, 0x02, 0xB0, 0x61, 0x01, 0x20,
+	0x25, 0x60, 0xF0, 0xBD, 0xF0, 0xB5, 0x1C, 0x4C,
+	0x00, 0x27, 0xE0, 0x68, 0x01, 0x25, 0x1B, 0x4E,
+	0x03, 0x00, 0xFF, 0xF7, 0xBB, 0xFD, 0x07, 0x05,
+	0x2E, 0x0F, 0x2E, 0x2E, 0x1B, 0x28, 0x2E, 0x00,
+	0xB0, 0x68, 0x01, 0x28, 0x25, 0xD1, 0xA1, 0x20,
+	0x30, 0x60, 0xFF, 0x20, 0x70, 0x60, 0x25, 0x60,
+	0x02, 0x20, 0x1D, 0xE0, 0x20, 0x68, 0x00, 0x28,
+	0x1B, 0xD1, 0x25, 0x60, 0x10, 0x48, 0x40, 0x68,
+	0xFF, 0xF7, 0xFF, 0xFE, 0xA3, 0x20, 0x30, 0x60,
+	0x05, 0x20, 0x11, 0xE0, 0xFF, 0xF7, 0x78, 0xFF,
+	0x00, 0x28, 0x0E, 0xD0, 0xB0, 0x68, 0x00, 0x28,
+	0x01, 0xD0, 0x25, 0x60, 0x01, 0xE0, 0x00, 0x20,
+	0x20, 0x60, 0x06, 0x20, 0x04, 0xE0, 0x20, 0x68,
+	0x00, 0x28, 0x02, 0xD1, 0x25, 0x60, 0x01, 0x27,
+	0xE0, 0x60, 0x38, 0x46, 0xF0, 0xBD, 0x00, 0x00,
+	0x00, 0x02, 0x00, 0x20, 0x10, 0x02, 0x00, 0x20,
+	0x00, 0x09, 0x00, 0x50, 0x00, 0x20, 0x01, 0xE0,
+	0x01, 0xC1, 0x12, 0x1F, 0x00, 0x2A, 0xFB, 0xD1,
+	0x70, 0x47, 0x00, 0x00, 0xFC, 0x06, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x20, 0x18, 0x00, 0x00, 0x00,
+	0xEE, 0x01, 0x00, 0x00, 0x14, 0x07, 0x00, 0x00,
+	0x18, 0x00, 0x00, 0x20, 0x04, 0x02, 0x00, 0x00,
+	0xCC, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0xD5, 0x4D, 0xDB, 0x52,
+};
+const unsigned char  u8_rad_init_30[] = {
+	0x10, 0x06, 0x00, 0x50, 0x00, 0x00, 0x06, 0x01,
+	0x1C, 0x0E, 0x00, 0x50, 0x03, 0x00, 0x00, 0x00,
+	0x28, 0x06, 0x00, 0x50, 0x06, 0x00, 0x00, 0x00,
+	0x20, 0x06, 0x00, 0x50, 0x00, 0x00, 0x00, 0x40,
+	0x10, 0x07, 0x00, 0x20, 0xFF, 0x00, 0xAA, 0x55,
+	0x14, 0x07, 0x00, 0x20, 0xFF, 0x00, 0xAA, 0x55,
+	0x18, 0x07, 0x00, 0x20, 0xFF, 0x00, 0xAA, 0x55,
+	0x1C, 0x07, 0x00, 0x20, 0xFF, 0x00, 0xAA, 0x55,
+	0x20, 0x07, 0x00, 0x20, 0xFF, 0x00, 0xAA, 0x55,
+	0x24, 0x07, 0x00, 0x20, 0xFF, 0x00, 0xAA, 0x55,
+	0x28, 0x07, 0x00, 0x20, 0xFF, 0x00, 0xAA, 0x55,
+	0x2C, 0x07, 0x00, 0x20, 0xFF, 0x00, 0xAA, 0x55,
+	0x30, 0x07, 0x00, 0x20, 0xFF, 0x00, 0xAA, 0x55,
+	0x34, 0x07, 0x00, 0x20, 0xFF, 0x00, 0xAA, 0x55,
+	0x74, 0x09, 0x00, 0x50, 0xFF, 0x00, 0xAA, 0x55,
+	0x3C, 0x07, 0x00, 0x20, 0x17, 0x26, 0x28, 0x2A,
+};
+const unsigned char u8_rad_fw_30[] = {
+	0xD0, 0x0C, 0x00, 0x20, 0xB1, 0x08, 0x00, 0x00,
+	0xB9, 0x08, 0x00, 0x00, 0x95, 0x08, 0x00, 0x00,
+	0xBD, 0x08, 0x00, 0x00, 0xBF, 0x08, 0x00, 0x00,
+	0xC1, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0xC3, 0x08, 0x00, 0x00,
+	0xC5, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0xC7, 0x08, 0x00, 0x00, 0x81, 0x0F, 0x00, 0x00,
+	0xE1, 0x0C, 0x00, 0x00, 0x01, 0x0A, 0x00, 0x00,
+	0xCB, 0x08, 0x00, 0x00, 0x61, 0x0E, 0x00, 0x00,
+	0xA1, 0x0D, 0x00, 0x00, 0x41, 0x0A, 0x00, 0x00,
+	0xCB, 0x08, 0x00, 0x00, 0x01, 0x0B, 0x00, 0x00,
+	0xCB, 0x08, 0x00, 0x00, 0x41, 0x0B, 0x00, 0x00,
+	0xE1, 0x0A, 0x00, 0x00, 0xA1, 0x0A, 0x00, 0x00,
+	0xC1, 0x0A, 0x00, 0x00, 0x81, 0x0A, 0x00, 0x00,
+	0xCB, 0x08, 0x00, 0x00, 0xCB, 0x08, 0x00, 0x00,
+	0x03, 0x48, 0x85, 0x46, 0x00, 0xF0, 0x80, 0xF8,
+	0x00, 0x48, 0x00, 0x47, 0x19, 0x44, 0x00, 0x00,
+	0xD0, 0x0C, 0x00, 0x20, 0x04, 0x20, 0x71, 0x46,
+	0x08, 0x42, 0x02, 0xD0, 0xEF, 0xF3, 0x09, 0x80,
+	0x01, 0xE0, 0xEF, 0xF3, 0x08, 0x80, 0x71, 0x46,
+	0x00, 0x4A, 0x10, 0x47, 0xC1, 0x42, 0x00, 0x00,
+	0x06, 0x48, 0x80, 0x47, 0x06, 0x48, 0x00, 0x47,
+	0xFE, 0xE7, 0xFE, 0xE7, 0xFE, 0xE7, 0xFE, 0xE7,
+	0xFE, 0xE7, 0xFE, 0xE7, 0xFE, 0xE7, 0xFE, 0xE7,
+	0xFE, 0xE7, 0xFE, 0xE7, 0x15, 0x0A, 0x00, 0x00,
+	0x81, 0x08, 0x00, 0x00, 0x30, 0xB5, 0x0B, 0x46,
+	0x01, 0x46, 0x00, 0x20, 0x20, 0x22, 0x01, 0x24,
+	0x09, 0xE0, 0x0D, 0x46, 0xD5, 0x40, 0x9D, 0x42,
+	0x05, 0xD3, 0x1D, 0x46, 0x95, 0x40, 0x49, 0x1B,
+	0x25, 0x46, 0x95, 0x40, 0x40, 0x19, 0x15, 0x46,
+	0x52, 0x1E, 0x00, 0x2D, 0xF1, 0xDC, 0x30, 0xBD,
+	0x70, 0xB5, 0x00, 0x24, 0x25, 0x46, 0x00, 0x28,
+	0x01, 0xDA, 0x01, 0x24, 0x40, 0x42, 0x00, 0x29,
+	0x01, 0xDA, 0x01, 0x25, 0x49, 0x42, 0xFF, 0xF7,
+	0xDD, 0xFF, 0xAC, 0x42, 0x00, 0xD0, 0x40, 0x42,
+	0x00, 0x2C, 0x00, 0xD0, 0x49, 0x42, 0x70, 0xBD,
+	0x03, 0x46, 0x0B, 0x43, 0x9B, 0x07, 0x03, 0xD0,
+	0x09, 0xE0, 0x08, 0xC9, 0x12, 0x1F, 0x08, 0xC0,
+	0x04, 0x2A, 0xFA, 0xD2, 0x03, 0xE0, 0x0B, 0x78,
+	0x49, 0x1C, 0x03, 0x70, 0x40, 0x1C, 0x52, 0x1E,
+	0xF9, 0xD2, 0x70, 0x47, 0xD2, 0xB2, 0x01, 0xE0,
+	0x02, 0x70, 0x40, 0x1C, 0x49, 0x1E, 0xFB, 0xD2,
+	0x70, 0x47, 0x00, 0x22, 0xF6, 0xE7, 0x10, 0xB5,
+	0x04, 0x46, 0x08, 0x46, 0x11, 0x46, 0x02, 0x46,
+	0x20, 0x46, 0xFF, 0xF7, 0xEF, 0xFF, 0x20, 0x46,
+	0x10, 0xBD, 0x00, 0x1D, 0x03, 0x21, 0x40, 0x1E,
+	0x03, 0x78, 0x12, 0x02, 0x1A, 0x43, 0x49, 0x1E,
+	0xF9, 0xD5, 0x10, 0x46, 0x70, 0x47, 0x00, 0x00,
+	0x06, 0x4C, 0x01, 0x25, 0x06, 0x4E, 0x05, 0xE0,
+	0x20, 0x46, 0xE3, 0x68, 0x07, 0xC8, 0x2B, 0x43,
+	0x98, 0x47, 0x10, 0x34, 0xB4, 0x42, 0xF7, 0xD3,
+	0xFF, 0xF7, 0x72, 0xFF, 0x58, 0x5C, 0x00, 0x00,
+	0x78, 0x5C, 0x00, 0x00, 0xC1, 0x06, 0xC9, 0x0E,
+	0x01, 0x20, 0x88, 0x40, 0x01, 0x49, 0x08, 0x60,
+	0x70, 0x47, 0x00, 0x00, 0x00, 0xE1, 0x00, 0xE0,
+	0x0B, 0x49, 0x10, 0xB5, 0x88, 0x42, 0x01, 0xD9,
+	0x01, 0x20, 0x10, 0xBD, 0x01, 0x02, 0x09, 0x0A,
+	0x08, 0x48, 0x49, 0x1E, 0x41, 0x61, 0x08, 0x49,
+	0x07, 0x23, 0xCA, 0x69, 0x12, 0x02, 0x12, 0x0A,
+	0x04, 0x04, 0x22, 0x43, 0xCA, 0x61, 0x00, 0x21,
+	0x81, 0x61, 0x03, 0x61, 0x08, 0x46, 0x10, 0xBD,
+	0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xE0, 0x00, 0xE0,
+	0x04, 0xED, 0x00, 0xE0, 0x70, 0x47, 0x00, 0x00,
+	0x03, 0x49, 0x02, 0x20, 0x08, 0x60, 0x02, 0x49,
+	0x80, 0x39, 0x08, 0x60, 0x70, 0x47, 0x00, 0x00,
+	0x80, 0xE1, 0x00, 0xE0, 0x62, 0xB6, 0x02, 0x48,
+	0x00, 0x21, 0x01, 0x60, 0x70, 0x47, 0x00, 0x00,
+	0x0C, 0x00, 0x00, 0x40, 0x30, 0xB4, 0x74, 0x46,
+	0x64, 0x1E, 0x25, 0x78, 0x64, 0x1C, 0xAB, 0x42,
+	0x00, 0xD2, 0x1D, 0x46, 0x63, 0x5D, 0x5B, 0x00,
+	0xE3, 0x18, 0x30, 0xBC, 0x18, 0x47, 0x00, 0x00,
+	0x05, 0x48, 0x01, 0x68, 0x49, 0x1C, 0x01, 0x60,
+	0x01, 0x20, 0x80, 0x07, 0x01, 0x6A, 0x03, 0x22,
+	0xD2, 0x03, 0x11, 0x43, 0x01, 0x62, 0x70, 0x47,
+	0xD0, 0x00, 0x00, 0x20, 0x02, 0xE0, 0x08, 0xC8,
+	0x12, 0x1F, 0x08, 0xC1, 0x00, 0x2A, 0xFA, 0xD1,
+	0x70, 0x47, 0x00, 0x20, 0x01, 0xE0, 0x01, 0xC1,
+	0x12, 0x1F, 0x00, 0x2A, 0xFB, 0xD1, 0x70, 0x47,
+	0x01, 0x21, 0x89, 0x07, 0x48, 0x60, 0x70, 0x47,
+	0x01, 0x20, 0x80, 0x07, 0x41, 0x6A, 0x82, 0x13,
+	0x11, 0x43, 0x41, 0x62, 0x41, 0x6A, 0xC2, 0x13,
+	0x11, 0x43, 0x41, 0x62, 0x70, 0x47, 0x10, 0xB5,
+	0xFF, 0xF7, 0xEE, 0xFF, 0x10, 0xBD, 0x00, 0x00,
+	0x03, 0x48, 0x01, 0x68, 0x49, 0x1C, 0x01, 0x60,
+	0x02, 0x49, 0x01, 0x20, 0xC8, 0x60, 0x70, 0x47,
+	0x08, 0x03, 0x00, 0x20, 0x00, 0x03, 0x00, 0x50,
+	0x10, 0xB5, 0x02, 0xF0, 0x8F, 0xF8, 0x10, 0xBD,
+	0x70, 0x47, 0x00, 0x00, 0x04, 0x49, 0x06, 0x22,
+	0x00, 0x28, 0x08, 0x68, 0x01, 0xD0, 0x10, 0x43,
+	0x00, 0xE0, 0x90, 0x43, 0x08, 0x60, 0x70, 0x47,
+	0x00, 0x09, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00,
+	0x70, 0x47, 0x00, 0x00, 0x64, 0x20, 0x05, 0x49,
+	0x02, 0xE0, 0x00, 0xBF, 0x40, 0x1E, 0xC0, 0xB2,
+	0x4A, 0x68, 0x12, 0x06, 0x01, 0xD5, 0x00, 0x28,
+	0xF7, 0xD1, 0x70, 0x47, 0x00, 0x06, 0x00, 0x50,
+	0x00, 0xB5, 0x07, 0x48, 0x01, 0x69, 0x02, 0x29,
+	0x06, 0xD1, 0x01, 0x61, 0x05, 0x49, 0x8A, 0x78,
+	0x00, 0x2A, 0x02, 0xD0, 0x02, 0xF0, 0x28, 0xFB,
+	0x00, 0xBD, 0x01, 0x20, 0xC8, 0x70, 0x00, 0xBD,
+	0x00, 0x02, 0x00, 0x50, 0x5C, 0x04, 0x00, 0x20,
+	0x04, 0x49, 0x00, 0x20, 0x08, 0x60, 0x01, 0x20,
+	0x80, 0x07, 0x41, 0x68, 0x42, 0x14, 0x11, 0x43,
+	0x41, 0x60, 0x70, 0x47, 0xD0, 0x00, 0x00, 0x20,
+	0x30, 0xB5, 0x1E, 0x4B, 0x58, 0x68, 0x99, 0x68,
+	0x00, 0x28, 0x01, 0xDA, 0xDA, 0x04, 0x5A, 0x60,
+	0x1B, 0x4A, 0x15, 0x68, 0x01, 0x24, 0x05, 0x40,
+	0x00, 0x2D, 0x02, 0xD1, 0x50, 0x68, 0x08, 0x42,
+	0x0F, 0xD0, 0x18, 0x48, 0x04, 0x70, 0x00, 0xF0,
+	0x6F, 0xF9, 0x14, 0x72, 0x16, 0x48, 0x00, 0x78,
+	0x00, 0x28, 0x06, 0xD0, 0x18, 0x68, 0x01, 0x21,
+	0x00, 0x09, 0x00, 0x01, 0x89, 0x02, 0x08, 0x43,
+	0x18, 0x60, 0x58, 0x68, 0x80, 0x02, 0x02, 0xD5,
+	0x01, 0x20, 0x40, 0x05, 0x58, 0x60, 0x58, 0x68,
+	0x00, 0x04, 0x03, 0xD5, 0x01, 0x20, 0xC0, 0x03,
+	0x58, 0x60, 0x94, 0x72, 0x58, 0x68, 0x80, 0x00,
+	0x03, 0xD5, 0x01, 0x20, 0x40, 0x07, 0x58, 0x60,
+	0xD4, 0x72, 0x58, 0x68, 0x40, 0x00, 0x03, 0xD5,
+	0x01, 0x20, 0x80, 0x07, 0x58, 0x60, 0x94, 0x72,
+	0x30, 0xBD, 0x00, 0x00, 0x00, 0x10, 0x00, 0x50,
+	0x50, 0x02, 0x00, 0x20, 0x99, 0x01, 0x00, 0x20,
+	0xCF, 0x00, 0x00, 0x20, 0x30, 0xB5, 0x07, 0x49,
+	0x07, 0x4B, 0x0A, 0x68, 0x00, 0x20, 0x41, 0x00,
+	0xCD, 0x18, 0x54, 0x5A, 0x2D, 0x88, 0x40, 0x1C,
+	0x64, 0x1B, 0xC0, 0xB2, 0x54, 0x52, 0x30, 0x28,
+	0xF5, 0xD3, 0x30, 0xBD, 0xF8, 0x02, 0x00, 0x20,
+	0x00, 0x00, 0x01, 0x20, 0x10, 0xB5, 0x02, 0xF0,
+	0x09, 0xF9, 0x10, 0xBD, 0x00, 0x00, 0x00, 0x00,
+	0x70, 0xB5, 0x2B, 0x4B, 0x05, 0x20, 0x19, 0x7A,
+	0x40, 0x04, 0x02, 0x29, 0x4C, 0xD1, 0x29, 0x4C,
+	0x21, 0x78, 0x00, 0x29, 0x45, 0xD1, 0x28, 0x49,
+	0x0A, 0x78, 0x00, 0x2A, 0x44, 0xD0, 0x00, 0x22,
+	0x0A, 0x70, 0x27, 0x49, 0x25, 0x4A, 0x09, 0x78,
+	0x26, 0x4D, 0x05, 0xE0, 0x16, 0x78, 0x81, 0x2E,
+	0x0A, 0xD0, 0x00, 0x29, 0x08, 0xD1, 0x40, 0x1E,
+	0x2E, 0x78, 0x00, 0x2E, 0x04, 0xD0, 0x00, 0x28,
+	0x02, 0xD0, 0x1E, 0x7A, 0x02, 0x2E, 0xF1, 0xD0,
+	0x18, 0x7A, 0x02, 0x28, 0x26, 0xD1, 0x10, 0x78,
+	0x81, 0x28, 0x23, 0xD0, 0x00, 0x29, 0x21, 0xD1,
+	0xFF, 0xF7, 0x44, 0xFF, 0x1A, 0x48, 0x40, 0x68,
+	0x40, 0x01, 0x40, 0x0D, 0xBC, 0x28, 0x01, 0xD9,
+	0xBC, 0x38, 0x80, 0xB2, 0x17, 0x49, 0x49, 0x68,
+	0x09, 0x06, 0x0D, 0xD4, 0x16, 0x49, 0x09, 0x88,
+	0x88, 0x42, 0x03, 0xD3, 0x15, 0x49, 0x09, 0x88,
+	0x88, 0x42, 0x05, 0xD9, 0x01, 0x20, 0x80, 0x07,
+	0x81, 0x68, 0x01, 0x22, 0x11, 0x43, 0x81, 0x60,
+	0x00, 0x20, 0x00, 0xBF, 0x40, 0x1C, 0xC0, 0xB2,
+	0x14, 0x28, 0xFA, 0xD3, 0x20, 0x78, 0x00, 0x28,
+	0x02, 0xD0, 0x20, 0x78, 0x40, 0x1E, 0x20, 0x70,
+	0xFF, 0xF7, 0x3E, 0xFF, 0x70, 0xBD, 0x00, 0x00,
+	0x50, 0x02, 0x00, 0x20, 0xEC, 0x02, 0x00, 0x20,
+	0x99, 0x01, 0x00, 0x20, 0x84, 0x04, 0x00, 0x20,
+	0x72, 0x04, 0x00, 0x20, 0x90, 0x02, 0x00, 0x20,
+	0x00, 0x11, 0x00, 0x50, 0x00, 0x06, 0x00, 0x50,
+	0x60, 0x02, 0x00, 0x20, 0x62, 0x02, 0x00, 0x20,
+	0x10, 0xB5, 0x02, 0xF0, 0xF1, 0xF9, 0x10, 0xBD,
+	0x70, 0xB5, 0x01, 0x25, 0xAD, 0x07, 0xE8, 0x68,
+	0x40, 0x08, 0x40, 0x00, 0xE8, 0x60, 0x1B, 0x48,
+	0x41, 0x68, 0x01, 0x26, 0x49, 0x07, 0x00, 0x29,
+	0x2A, 0xDA, 0x42, 0x68, 0x04, 0x21, 0x0A, 0x43,
+	0x42, 0x60, 0x17, 0x48, 0x00, 0x68, 0x17, 0x4C,
+	0x40, 0x05, 0x40, 0x0F, 0x20, 0x73, 0x20, 0x7B,
+	0x06, 0x28, 0x00, 0xD1, 0x21, 0x73, 0x20, 0x7B,
+	0x02, 0x28, 0x1D, 0xD0, 0x20, 0x7B, 0x06, 0x28,
+	0x05, 0xD8, 0x20, 0x7B, 0x04, 0x28, 0x02, 0xD3,
+	0x01, 0x20, 0x01, 0xF0, 0xA1, 0xF9, 0x20, 0x7B,
+	0x0D, 0x49, 0x09, 0x78, 0x88, 0x42, 0x0B, 0xD0,
+	0x0C, 0x4C, 0xA0, 0x79, 0x00, 0x28, 0x01, 0xD0,
+	0x01, 0xF0, 0xF8, 0xF9, 0x0A, 0x48, 0x01, 0x78,
+	0x31, 0x43, 0x01, 0x70, 0x81, 0x20, 0x20, 0x70,
+	0xE8, 0x68, 0x30, 0x43, 0xE8, 0x60, 0x70, 0xBD,
+	0x00, 0x20, 0xE6, 0xE7, 0x40, 0x00, 0x00, 0x50,
+	0x00, 0x11, 0x00, 0x50, 0x50, 0x02, 0x00, 0x20,
+	0xE5, 0x02, 0x00, 0x20, 0x84, 0x04, 0x00, 0x20,
+	0xCE, 0x00, 0x00, 0x20, 0x10, 0xB5, 0x08, 0x4A,
+	0x00, 0x21, 0x12, 0x68, 0x60, 0x32, 0x12, 0x78,
+	0x52, 0x1C, 0xD4, 0xB2, 0x4A, 0x00, 0x83, 0x5E,
+	0x49, 0x1C, 0x63, 0x43, 0x1B, 0x11, 0xC9, 0xB2,
+	0x83, 0x52, 0x30, 0x29, 0xF6, 0xD3, 0x10, 0xBD,
+	0xB8, 0x02, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00,
+	0x10, 0xB5, 0x14, 0x48, 0x80, 0x69, 0x40, 0x04,
+	0x23, 0xD5, 0x13, 0x4C, 0xE0, 0x68, 0xA2, 0x68,
+	0x02, 0x40, 0xE0, 0x68, 0xA1, 0x68, 0x88, 0x43,
+	0xE0, 0x60, 0x10, 0x07, 0x03, 0xD5, 0x08, 0x20,
+	0xE0, 0x60, 0x04, 0xF0, 0x3F, 0xFB, 0x50, 0x07,
+	0x0E, 0xD5, 0x04, 0x20, 0xE0, 0x60, 0x04, 0xF0,
+	0x39, 0xFB, 0x02, 0xF0, 0xFD, 0xFC, 0x00, 0x28,
+	0x06, 0xD1, 0x08, 0x48, 0x00, 0x78, 0x00, 0x28,
+	0x02, 0xD0, 0x01, 0x20, 0xFF, 0xF7, 0x68, 0xFE,
+	0xFF, 0x20, 0xF3, 0x30, 0xE0, 0x60, 0x01, 0x20,
+	0xE0, 0x60, 0x10, 0xBD, 0x00, 0x09, 0x00, 0x50,
+	0x00, 0x05, 0x00, 0x50, 0x90, 0x02, 0x00, 0x20,
+	0x00, 0xB5, 0x03, 0x46, 0x00, 0x20, 0xFF, 0xF7,
+	0x5D, 0xFE, 0x04, 0x49, 0x01, 0x20, 0x09, 0x68,
+	0x0A, 0x88, 0x9A, 0x43, 0x0A, 0x80, 0xFF, 0xF7,
+	0x55, 0xFE, 0x00, 0xBD, 0x08, 0x00, 0x00, 0x20,
+	0x01, 0x28, 0x05, 0xD0, 0x02, 0x28, 0x05, 0xD0,
+	0x04, 0x28, 0x06, 0xD0, 0x00, 0x20, 0x70, 0x47,
+	0x03, 0x48, 0x70, 0x47, 0x02, 0x48, 0xC0, 0x30,
+	0x70, 0x47, 0x02, 0x48, 0x70, 0x47, 0x00, 0x00,
+	0x78, 0x7C, 0x00, 0x00, 0xF8, 0x7D, 0x00, 0x00,
+	0x03, 0x48, 0x02, 0x49, 0x41, 0x60, 0x03, 0x49,
+	0x81, 0x60, 0x70, 0x47, 0x1F, 0x1F, 0x5F, 0x1F,
+	0x00, 0x10, 0x00, 0x50, 0x1F, 0x1F, 0x1F, 0x1F,
+	0xF8, 0xB5, 0x2B, 0x48, 0x80, 0x69, 0x40, 0x04,
+	0x51, 0xD5, 0x2A, 0x4D, 0xA8, 0x6A, 0xE9, 0x68,
+	0xAC, 0x68, 0x0C, 0x40, 0xE9, 0x68, 0xAA, 0x68,
+	0x91, 0x43, 0xE9, 0x60, 0x26, 0x4E, 0xC0, 0x07,
+	0x15, 0xD0, 0x68, 0x69, 0x89, 0x27, 0xC0, 0xB2,
+	0xEF, 0x60, 0xAA, 0x6A, 0x01, 0x21, 0x0A, 0x43,
+	0xAA, 0x62, 0x0B, 0x28, 0x03, 0xD2, 0x31, 0x70,
+	0x01, 0xF0, 0x04, 0xF8, 0x03, 0xE0, 0x00, 0x21,
+	0x31, 0x70, 0x00, 0xF0, 0xDB, 0xFF, 0xBC, 0x43,
+	0x01, 0x20, 0xFF, 0xF7, 0x05, 0xFE, 0x1B, 0x48,
+	0xA1, 0x04, 0x04, 0xD5, 0x29, 0x6A, 0x81, 0x43,
+	0x29, 0x62, 0x41, 0x14, 0xE9, 0x60, 0xE1, 0x04,
+	0x05, 0xD5, 0x29, 0x6A, 0x81, 0x43, 0x29, 0x62,
+	0x01, 0x20, 0x00, 0x03, 0xE8, 0x60, 0xA0, 0x05,
+	0x02, 0xD5, 0x01, 0x20, 0x40, 0x02, 0xE8, 0x60,
+	0x20, 0x07, 0x09, 0xD5, 0x30, 0x78, 0x01, 0x28,
+	0x02, 0xD1, 0x0A, 0x20, 0x00, 0xF0, 0xBA, 0xFF,
+	0x64, 0x08, 0x64, 0x00, 0x09, 0x20, 0xE8, 0x60,
+	0x60, 0x07, 0x07, 0xD5, 0x30, 0x78, 0x01, 0x28,
+	0x02, 0xD1, 0x0A, 0x20, 0x00, 0xF0, 0xAE, 0xFF,
+	0x05, 0x20, 0xE8, 0x60, 0xFF, 0x20, 0xF3, 0x30,
+	0xE8, 0x60, 0x01, 0x20, 0xE8, 0x60, 0xF8, 0xBD,
+	0x00, 0x09, 0x00, 0x50, 0x00, 0x06, 0x00, 0x50,
+	0x9B, 0x01, 0x00, 0x20, 0x00, 0x00, 0x00, 0x40,
+	0x70, 0xB5, 0x41, 0x18, 0x49, 0x1E, 0x64, 0x24,
+	0x09, 0x04, 0x0B, 0x4D, 0x01, 0x43, 0x69, 0x63,
+	0xE8, 0x68, 0x81, 0x21, 0x09, 0x06, 0x08, 0x43,
+	0xE8, 0x60, 0x02, 0xE0, 0x01, 0x20, 0xFF, 0xF7,
+	0xCB, 0xFE, 0xE8, 0x68, 0xC0, 0x01, 0x04, 0xD5,
+	0x20, 0x46, 0x64, 0x1E, 0xA4, 0xB2, 0x00, 0x28,
+	0xF4, 0xD1, 0xA8, 0x6B, 0x70, 0xBD, 0x00, 0x00,
+	0x40, 0x09, 0x00, 0x50, 0x04, 0x49, 0x29, 0x20,
+	0xC8, 0x60, 0x04, 0x49, 0x35, 0x20, 0x08, 0x63,
+	0x04, 0x20, 0x01, 0x07, 0x88, 0x60, 0x70, 0x47,
+	0x40, 0x00, 0x00, 0x50, 0x80, 0x10, 0x00, 0x50,
+	0x10, 0xB5, 0x00, 0xF0, 0x09, 0xF8, 0x10, 0xBD,
+	0x02, 0x48, 0x01, 0x68, 0x49, 0x1C, 0x01, 0x60,
+	0x70, 0x47, 0x00, 0x00, 0x80, 0x04, 0x00, 0x20,
+	0x01, 0x20, 0x80, 0x07, 0x41, 0x68, 0x82, 0x14,
+	0x11, 0x43, 0x41, 0x60, 0x70, 0x47, 0x00, 0x00,
+	0x05, 0x20, 0x00, 0x07, 0x82, 0x69, 0x0C, 0x49,
+	0x00, 0x2A, 0x07, 0xDA, 0x82, 0x69, 0xC3, 0x00,
+	0x92, 0x00, 0x92, 0x08, 0xD2, 0x18, 0x82, 0x61,
+	0x01, 0x22, 0x4A, 0x72, 0x82, 0x69, 0x52, 0x00,
+	0x08, 0xD5, 0x82, 0x69, 0x01, 0x23, 0x92, 0x00,
+	0x92, 0x08, 0x9B, 0x07, 0xD2, 0x18, 0x82, 0x61,
+	0x00, 0x20, 0x48, 0x72, 0x70, 0x47, 0x00, 0x00,
+	0x84, 0x04, 0x00, 0x20, 0xF0, 0xB5, 0x00, 0x20,
+	0x84, 0x46, 0x91, 0x48, 0x87, 0xB0, 0x00, 0x68,
+	0x90, 0x49, 0xB0, 0x30, 0x00, 0x90, 0x00, 0x20,
+	0x08, 0x5E, 0x8F, 0x49, 0x01, 0x90, 0x00, 0x20,
+	0x08, 0x5E, 0x02, 0x90, 0x0B, 0xE1, 0x34, 0x21,
+	0x48, 0x43, 0x81, 0x19, 0x0C, 0x46, 0x20, 0x34,
+	0x60, 0x7D, 0xFF, 0x28, 0x31, 0xD1, 0x00, 0x22,
+	0x00, 0x20, 0x08, 0xE0, 0x34, 0x25, 0x45, 0x43,
+	0xAD, 0x19, 0x20, 0x35, 0x6D, 0x7D, 0x95, 0x42,
+	0x03, 0xD0, 0x40, 0x1C, 0xC0, 0xB2, 0x83, 0x42,
+	0xF4, 0xD8, 0x83, 0x42, 0x01, 0xD1, 0x62, 0x75,
+	0x03, 0xE0, 0x52, 0x1C, 0xD2, 0xB2, 0x02, 0x2A,
+	0xEA, 0xD3, 0x0A, 0x25, 0x4D, 0x5F, 0x30, 0x20,
+	0x7C, 0x4E, 0x42, 0x43, 0xAB, 0x01, 0x90, 0x19,
+	0x83, 0x60, 0xC3, 0x60, 0xB5, 0x50, 0x0C, 0x22,
+	0x45, 0x60, 0x8A, 0x5E, 0x93, 0x01, 0x83, 0x61,
+	0x02, 0x61, 0xC3, 0x61, 0x42, 0x61, 0x0E, 0x22,
+	0x8A, 0x5E, 0x93, 0x01, 0x83, 0x62, 0x02, 0x62,
+	0xC3, 0x62, 0x42, 0x62, 0x60, 0x7D, 0xFF, 0x28,
+	0x7E, 0xD0, 0x71, 0x4A, 0xC7, 0xB2, 0x12, 0x78,
+	0xC8, 0x69, 0x00, 0x2A, 0x11, 0xD1, 0x00, 0x9A,
+	0xD3, 0x79, 0x94, 0x79, 0x1A, 0x02, 0x22, 0x43,
+	0x82, 0x42, 0x01, 0xD2, 0x02, 0x22, 0x09, 0xE0,
+	0x00, 0x9A, 0x53, 0x7A, 0x14, 0x7A, 0x1A, 0x02,
+	0x22, 0x43, 0x82, 0x42, 0x01, 0xD2, 0x03, 0x22,
+	0x00, 0xE0, 0x04, 0x22, 0x65, 0x4D, 0x28, 0x78,
+	0x82, 0x42, 0x28, 0xD0, 0x82, 0x42, 0x02, 0xD3,
+	0x13, 0x1A, 0x02, 0x2B, 0x23, 0xDA, 0x62, 0x4C,
+	0x23, 0x78, 0x00, 0x2B, 0x14, 0xD0, 0x61, 0x4E,
+	0x33, 0x78, 0x9A, 0x42, 0x02, 0xD0, 0x00, 0x23,
+	0x23, 0x70, 0x32, 0x70, 0x22, 0x78, 0x5D, 0x4B,
+	0x52, 0x1C, 0xD2, 0xB2, 0x22, 0x70, 0x1B, 0x78,
+	0x98, 0x42, 0x08, 0xD9, 0x01, 0x2A, 0x06, 0xD9,
+	0x00, 0x20, 0x20, 0x70, 0x2B, 0x70, 0x0A, 0xE0,
+	0x56, 0x4B, 0x1A, 0x70, 0xEE, 0xE7, 0x98, 0x42,
+	0x05, 0xD2, 0x02, 0x2A, 0x03, 0xD9, 0x00, 0x22,
+	0x22, 0x70, 0x40, 0x1C, 0x28, 0x70, 0x28, 0x78,
+	0x02, 0x28, 0x6B, 0xD0, 0x03, 0x28, 0x6D, 0xD0,
+	0x29, 0x22, 0x64, 0x23, 0x05, 0x24, 0x3E, 0x46,
+	0x30, 0x20, 0x46, 0x43, 0x47, 0x48, 0x30, 0x18,
+	0xC5, 0x68, 0x03, 0x95, 0x87, 0x68, 0x5D, 0x43,
+	0x57, 0x43, 0xED, 0x1B, 0x20, 0x35, 0xAD, 0x11,
+	0xAE, 0x46, 0x0A, 0x25, 0x4D, 0x5F, 0x41, 0x4F,
+	0x04, 0x95, 0xBF, 0x59, 0xEF, 0x19, 0x45, 0x68,
+	0x6D, 0x00, 0x7F, 0x19, 0x67, 0x43, 0xBF, 0x1C,
+	0xBF, 0x10, 0x75, 0x46, 0x7D, 0x19, 0xAE, 0x46,
+	0xC5, 0x69, 0x87, 0x69, 0x5D, 0x43, 0x57, 0x43,
+	0xED, 0x1B, 0x20, 0x35, 0xAD, 0x11, 0x06, 0x95,
+	0x0C, 0x25, 0x4D, 0x5F, 0x05, 0x95, 0x07, 0x69,
+	0xEF, 0x19, 0x45, 0x69, 0x6D, 0x00, 0x7D, 0x19,
+	0x65, 0x43, 0xAD, 0x1C, 0xAF, 0x10, 0x00, 0xE0,
+	0x4D, 0xE0, 0x06, 0x9D, 0x7F, 0x19, 0xC5, 0x6A,
+	0x5D, 0x43, 0x83, 0x6A, 0x53, 0x43, 0xEA, 0x1A,
+	0x0E, 0x23, 0x20, 0x32, 0xCB, 0x5E, 0x95, 0x11,
+	0x06, 0x93, 0x02, 0x6A, 0x9A, 0x18, 0x43, 0x6A,
+	0x5B, 0x00, 0xD2, 0x18, 0x62, 0x43, 0x92, 0x1C,
+	0x92, 0x10, 0x52, 0x19, 0x03, 0x9D, 0x85, 0x60,
+	0x75, 0x46, 0xC5, 0x60, 0xC3, 0x69, 0xC7, 0x61,
+	0x83, 0x61, 0xC3, 0x6A, 0x83, 0x62, 0xC2, 0x62,
+	0x20, 0x4C, 0x43, 0x68, 0xA3, 0x51, 0x04, 0x9D,
+	0x45, 0x60, 0x43, 0x69, 0x03, 0x61, 0x05, 0x9D,
+	0x45, 0x61, 0x43, 0x6A, 0x03, 0x62, 0x06, 0x9B,
+	0x43, 0x62, 0x75, 0x46, 0x01, 0x9C, 0xA8, 0x11,
+	0xBB, 0x11, 0x92, 0x11, 0xA0, 0x42, 0x09, 0xDB,
+	0x60, 0x1E, 0x0A, 0xE0, 0x1C, 0x22, 0x4D, 0x23,
+	0x0F, 0x24, 0x94, 0xE7, 0x22, 0x22, 0x58, 0x23,
+	0x0A, 0x24, 0x90, 0xE7, 0x00, 0x28, 0x00, 0xDA,
+	0x00, 0x20, 0x02, 0x9C, 0xA3, 0x42, 0x01, 0xDB,
+	0x63, 0x1E, 0x02, 0xE0, 0x00, 0x2B, 0x00, 0xDA,
+	0x00, 0x23, 0x00, 0x2A, 0x00, 0xDA, 0x00, 0x22,
+	0x48, 0x81, 0x8B, 0x81, 0xCA, 0x81, 0x60, 0x46,
+	0x40, 0x1C, 0xC0, 0xB2, 0x84, 0x46, 0x0C, 0x4E,
+	0x60, 0x46, 0x33, 0x78, 0x63, 0x45, 0x00, 0xD9,
+	0xED, 0xE6, 0x07, 0xB0, 0xF0, 0xBD, 0x00, 0x00,
+	0xB8, 0x02, 0x00, 0x20, 0x42, 0x00, 0x00, 0x20,
+	0x44, 0x00, 0x00, 0x20, 0x38, 0x01, 0x00, 0x20,
+	0x2A, 0x00, 0x00, 0x20, 0x4A, 0x00, 0x00, 0x20,
+	0x49, 0x00, 0x00, 0x20, 0x4B, 0x00, 0x00, 0x20,
+	0xA0, 0x04, 0x00, 0x20, 0xF0, 0xB5, 0x93, 0xB0,
+	0x02, 0xF0, 0x62, 0xFD, 0x0F, 0x90, 0x8D, 0x48,
+	0x00, 0x25, 0x41, 0x5F, 0x0F, 0x98, 0x81, 0x42,
+	0x7D, 0xDD, 0x8B, 0x48, 0x30, 0x21, 0x00, 0x68,
+	0x06, 0x7E, 0x40, 0x7E, 0x11, 0x90, 0x02, 0xA8,
+	0xFF, 0xF7, 0x73, 0xFB, 0x87, 0x48, 0x00, 0x78,
+	0x06, 0x28, 0x71, 0xD2, 0x01, 0x20, 0x10, 0x90,
+	0x08, 0xA8, 0x0E, 0x90, 0x34, 0x20, 0x29, 0x46,
+	0x41, 0x43, 0x83, 0x48, 0x00, 0x27, 0x0C, 0x18,
+	0x27, 0x76, 0x10, 0x98, 0x00, 0x28, 0x04, 0xD0,
+	0x7C, 0x48, 0x01, 0x88, 0x7F, 0x48, 0x01, 0x80,
+	0x09, 0xE0, 0x01, 0xA9, 0x68, 0x46, 0x0F, 0x9A,
+	0x04, 0xF0, 0x58, 0xF8, 0x00, 0x28, 0x57, 0xD0,
+	0x10, 0x98, 0x00, 0x28, 0x0E, 0xD0, 0x7A, 0x48,
+	0x31, 0x46, 0x00, 0x88, 0x10, 0x90, 0xFF, 0xF7,
+	0x09, 0xFB, 0x69, 0x46, 0x08, 0x70, 0x10, 0x99,
+	0x70, 0x43, 0x08, 0x1A, 0x69, 0x46, 0x08, 0x71,
+	0x00, 0x20, 0x10, 0x90, 0x0F, 0x98, 0x02, 0xAA,
+	0x83, 0xB2, 0x01, 0xA9, 0x68, 0x46, 0x02, 0xF0,
+	0x65, 0xFD, 0x70, 0x49, 0x88, 0x42, 0x7D, 0xD0,
+	0x6C, 0x4A, 0x00, 0x21, 0x51, 0x5E, 0x61, 0x86,
+	0xE0, 0x81, 0x0A, 0x22, 0x50, 0x43, 0xFF, 0xF7,
+	0x03, 0xFB, 0x34, 0x21, 0x08, 0x55, 0xFF, 0x20,
+	0x20, 0x71, 0x00, 0x20, 0xE0, 0x61, 0x68, 0x46,
+	0x01, 0x79, 0x02, 0xAA, 0x01, 0x20, 0x02, 0xF0,
+	0xC7, 0xFC, 0x65, 0x49, 0x60, 0x81, 0x0A, 0x78,
+	0x00, 0x2A, 0x0B, 0xD0, 0x63, 0x4A, 0x00, 0x23,
+	0xD3, 0x5E, 0x63, 0x4A, 0x12, 0x78, 0x9B, 0x1A,
+	0x98, 0x42, 0x03, 0xDA, 0x90, 0x42, 0x01, 0xDD,
+	0x00, 0x22, 0x0A, 0x70, 0x6A, 0x46, 0x12, 0x79,
+	0x00, 0x2A, 0x12, 0xD0, 0x73, 0x1E, 0x9A, 0x42,
+	0x28, 0xD1, 0x5B, 0x4A, 0x00, 0x27, 0x12, 0x78,
+	0x53, 0x00, 0xD2, 0x18, 0x93, 0x08, 0x57, 0x4A,
+	0xD7, 0x5F, 0xFA, 0x1A, 0x90, 0x42, 0x14, 0xDD,
+	0x01, 0x20, 0x08, 0x70, 0x14, 0xE0, 0x7E, 0xE0,
+	0x87, 0xE0, 0x53, 0x4A, 0x12, 0x78, 0x53, 0x00,
+	0xD2, 0x18, 0x92, 0x08, 0x90, 0x42, 0x02, 0xDA,
+	0x01, 0x20, 0x08, 0x70, 0x02, 0xE0, 0x08, 0x78,
+	0x00, 0x28, 0x0B, 0xD0, 0x02, 0xAA, 0x01, 0x21,
+	0x04, 0xE0, 0x08, 0x78, 0x00, 0x28, 0x05, 0xD0,
+	0x02, 0xAA, 0x00, 0x21, 0x01, 0x20, 0x02, 0xF0,
+	0xEF, 0xFB, 0x60, 0x81, 0x68, 0x46, 0x01, 0x78,
+	0x00, 0x20, 0x0E, 0x9A, 0x02, 0xF0, 0x80, 0xFC,
+	0x44, 0x49, 0xA0, 0x81, 0x0A, 0x78, 0x00, 0x2A,
+	0x0B, 0xD0, 0x43, 0x4A, 0x00, 0x23, 0xD3, 0x5E,
+	0x42, 0x4A, 0x12, 0x78, 0x9B, 0x1A, 0x98, 0x42,
+	0x03, 0xDA, 0x90, 0x42, 0x01, 0xDD, 0x00, 0x22,
+	0x0A, 0x70, 0x6A, 0x46, 0x12, 0x78, 0x00, 0x2A,
+	0x13, 0xD0, 0x11, 0x9B, 0x5B, 0x1E, 0x9A, 0x42,
+	0x28, 0xD1, 0x3A, 0x4A, 0x12, 0x78, 0x53, 0x00,
+	0xD2, 0x18, 0x93, 0x08, 0x36, 0x4A, 0x00, 0x27,
+	0xD7, 0x5F, 0x00, 0xE0, 0x37, 0xE0, 0xFA, 0x1A,
+	0x90, 0x42, 0x12, 0xDD, 0x01, 0x20, 0x08, 0x70,
+	0x12, 0xE0, 0x32, 0x4A, 0x12, 0x78, 0x53, 0x00,
+	0xD2, 0x18, 0x92, 0x08, 0x90, 0x42, 0x02, 0xDA,
+	0x01, 0x20, 0x08, 0x70, 0x02, 0xE0, 0x08, 0x78,
+	0x00, 0x28, 0x0B, 0xD0, 0x01, 0x21, 0x0E, 0x9A,
+	0x04, 0xE0, 0x08, 0x78, 0x00, 0x28, 0x05, 0xD0,
+	0x0E, 0x9A, 0x00, 0x21, 0x00, 0x20, 0x02, 0xF0,
+	0xA7, 0xFB, 0xA0, 0x81, 0x07, 0x98, 0xA0, 0x82,
+	0x0D, 0x98, 0xE0, 0x82, 0x68, 0x46, 0x00, 0x79,
+	0x00, 0x28, 0x0A, 0xD0, 0x71, 0x1E, 0x88, 0x42,
+	0x07, 0xD0, 0x68, 0x46, 0x00, 0x78, 0x00, 0x28,
+	0x03, 0xD0, 0x11, 0x99, 0x49, 0x1E, 0x88, 0x42,
+	0x01, 0xD1, 0x01, 0x20, 0x00, 0xE0, 0x00, 0x20,
+	0x6D, 0x1C, 0x20, 0x76, 0xED, 0xB2, 0x03, 0x2D,
+	0x00, 0xD2, 0x13, 0xE7, 0x09, 0xE0, 0x0E, 0x49,
+	0x00, 0x20, 0x08, 0x70, 0x16, 0x48, 0x40, 0x88,
+	0x0A, 0x28, 0x02, 0xD9, 0x00, 0x20, 0x13, 0xB0,
+	0xF0, 0xBD, 0x08, 0x48, 0x00, 0x78, 0x03, 0x28,
+	0x03, 0xD3, 0x07, 0x49, 0x00, 0x20, 0x08, 0x70,
+	0x01, 0xE0, 0x05, 0x48, 0x05, 0x70, 0x01, 0x20,
+	0xF1, 0xE7, 0x00, 0x00, 0x24, 0x00, 0x00, 0x20,
+	0xB8, 0x02, 0x00, 0x20, 0x73, 0x04, 0x00, 0x20,
+	0xA0, 0x04, 0x00, 0x20, 0x28, 0x00, 0x00, 0x20,
+	0x32, 0x00, 0x00, 0x20, 0x00, 0x80, 0xFF, 0xFF,
+	0xCC, 0x00, 0x00, 0x20, 0x42, 0x00, 0x00, 0x20,
+	0x2E, 0x00, 0x00, 0x20, 0xCD, 0x00, 0x00, 0x20,
+	0x44, 0x00, 0x00, 0x20, 0x2F, 0x00, 0x00, 0x20,
+	0x84, 0x04, 0x00, 0x20, 0xF0, 0xB5, 0x8E, 0x46,
+	0x02, 0x21, 0x4B, 0x00, 0xC3, 0x5E, 0x00, 0x2B,
+	0x03, 0xDD, 0x49, 0x1E, 0x09, 0xB2, 0x00, 0x29,
+	0xF7, 0xDA, 0x49, 0x1C, 0x00, 0x27, 0x0B, 0xB2,
+	0x9C, 0x46, 0x3C, 0x46, 0x01, 0x25, 0x0E, 0xE0,
+	0x5E, 0x00, 0x86, 0x5F, 0x00, 0x2E, 0x02, 0xDC,
+	0x5B, 0x1E, 0x1B, 0xB2, 0x09, 0xE0, 0x31, 0x46,
+	0x69, 0x43, 0x6D, 0x1C, 0xCF, 0x19, 0x34, 0x19,
+	0x5B, 0x1C, 0x2D, 0xB2, 0x1B, 0xB2, 0x05, 0x2B,
+	0xEE, 0xDB, 0x61, 0x46, 0x59, 0x1A, 0x49, 0x1C,
+	0x66, 0x08, 0x63, 0x46, 0x00, 0x25, 0x51, 0x61,
+	0x06, 0xE0, 0x59, 0x00, 0x41, 0x5E, 0x4D, 0x19,
+	0xB5, 0x42, 0x03, 0xD8, 0x5B, 0x1C, 0x1B, 0xB2,
+	0x05, 0x2B, 0xF6, 0xDB, 0x5E, 0x00, 0x81, 0x5F,
+	0x69, 0x1A, 0x11, 0x60, 0x80, 0x5F, 0x50, 0x60,
+	0x60, 0x1B, 0xD4, 0x60, 0x90, 0x60, 0x60, 0x46,
+	0x18, 0x1A, 0x60, 0x43, 0x38, 0x1A, 0x10, 0x61,
+	0x70, 0x46, 0x00, 0x78, 0x9B, 0x1E, 0xC0, 0x18,
+	0x71, 0x46, 0x08, 0x70, 0x20, 0xB2, 0xF0, 0xBD,
+	0xF8, 0xB5, 0x8D, 0x4E, 0x00, 0x27, 0x30, 0x78,
+	0x3C, 0x46, 0x00, 0x28, 0x01, 0xD0, 0x03, 0xF0,
+	0xE7, 0xF9, 0x00, 0x20, 0x05, 0x46, 0x89, 0x4A,
+	0x0D, 0xE0, 0x34, 0x21, 0x41, 0x43, 0x89, 0x18,
+	0x0B, 0x79, 0x1B, 0x06, 0x01, 0xD5, 0xCD, 0x71,
+	0x03, 0xE0, 0xCB, 0x79, 0x5B, 0x1C, 0xCB, 0x71,
+	0x01, 0x27, 0x40, 0x1C, 0xC0, 0xB2, 0x11, 0x78,
+	0x81, 0x42, 0xEE, 0xD8, 0x7F, 0x49, 0x30, 0x78,
+	0x09, 0x78, 0x88, 0x42, 0x01, 0xD1, 0x00, 0x2F,
+	0x02, 0xD0, 0x03, 0xF0, 0x63, 0xFD, 0x0E, 0xE0,
+	0xB0, 0x70, 0x00, 0x20, 0x34, 0x21, 0x41, 0x43,
+	0x89, 0x19, 0x8D, 0x71, 0x40, 0x1C, 0xCD, 0x71,
+	0xC0, 0xB2, 0x0D, 0x72, 0x03, 0x28, 0xF5, 0xD3,
+	0x74, 0x48, 0x75, 0x70, 0x45, 0x70, 0x30, 0x78,
+	0xB1, 0x78, 0x88, 0x42, 0x01, 0xD0, 0x03, 0xF0,
+	0x61, 0xFB, 0x02, 0xF0, 0x89, 0xFE, 0x70, 0x49,
+	0x70, 0x4A, 0x08, 0x88, 0x10, 0x80, 0x84, 0x46,
+	0x00, 0x20, 0x08, 0x80, 0x6A, 0x49, 0x6E, 0x4D,
+	0x0A, 0x78, 0x2B, 0xE0, 0x34, 0x23, 0x43, 0x43,
+	0x5E, 0x18, 0x69, 0x4B, 0xF1, 0x69, 0x1F, 0x88,
+	0xB9, 0x42, 0x00, 0xD9, 0x19, 0x80, 0x61, 0x46,
+	0x00, 0x29, 0x0B, 0xD1, 0x29, 0x68, 0xB0, 0x31,
+	0x4F, 0x7A, 0x09, 0x7A, 0x3F, 0x02, 0x0F, 0x43,
+	0x19, 0x88, 0x8F, 0x42, 0x02, 0xD2, 0x63, 0x4B,
+	0x01, 0x21, 0x19, 0x70, 0x29, 0x68, 0x20, 0x36,
+	0xA0, 0x31, 0x33, 0x7D, 0x49, 0x78, 0x8B, 0x42,
+	0x17, 0xD9, 0x5F, 0x4B, 0x01, 0x24, 0x19, 0x78,
+	0x49, 0x1C, 0xC9, 0xB2, 0x19, 0x70, 0x02, 0x29,
+	0x02, 0xD9, 0x5C, 0x4B, 0x01, 0x21, 0x19, 0x70,
+	0x40, 0x1C, 0xC0, 0xB2, 0x52, 0x49, 0x82, 0x42,
+	0xD0, 0xD8, 0x59, 0x49, 0x00, 0x2C, 0x08, 0x78,
+	0x06, 0xD0, 0x64, 0x28, 0x08, 0xD2, 0x40, 0x1C,
+	0x05, 0xE0, 0x00, 0x21, 0x52, 0x4B, 0xEE, 0xE7,
+	0x00, 0x28, 0x01, 0xD0, 0x40, 0x1E, 0x08, 0x70,
+	0x03, 0x20, 0x81, 0x1A, 0x34, 0x20, 0x48, 0x4E,
+	0x41, 0x43, 0x42, 0x43, 0x90, 0x19, 0x00, 0x1D,
+	0xFF, 0xF7, 0x6F, 0xF9, 0x00, 0x24, 0x45, 0x4F,
+	0x0F, 0xE0, 0x38, 0x78, 0x34, 0x21, 0x00, 0x19,
+	0x48, 0x43, 0xC1, 0x19, 0x30, 0x78, 0x34, 0x22,
+	0x00, 0x19, 0x50, 0x43, 0x80, 0x19, 0x09, 0x1D,
+	0x00, 0x1D, 0xFF, 0xF7, 0x45, 0xF9, 0x64, 0x1C,
+	0xE4, 0xB2, 0x71, 0x78, 0xA1, 0x42, 0xEC, 0xD8,
+	0x32, 0x78, 0x00, 0x20, 0x53, 0x18, 0x0E, 0xE0,
+	0x34, 0x22, 0x42, 0x43, 0x91, 0x19, 0x06, 0x22,
+	0x42, 0x43, 0xD2, 0x19, 0xA0, 0x32, 0x4C, 0x89,
+	0x14, 0x80, 0x8C, 0x89, 0x54, 0x80, 0xC9, 0x89,
+	0x40, 0x1C, 0x91, 0x80, 0xC0, 0xB2, 0x31, 0x46,
+	0x83, 0x42, 0xED, 0xD8, 0x00, 0x20, 0x37, 0x4C,
+	0x01, 0x22, 0x20, 0x80, 0x0C, 0xE0, 0x34, 0x26,
+	0x46, 0x43, 0x76, 0x18, 0x36, 0x79, 0x77, 0x06,
+	0x7F, 0x0E, 0x16, 0x46, 0xBE, 0x40, 0x27, 0x88,
+	0x3E, 0x43, 0x40, 0x1C, 0x26, 0x80, 0xC0, 0xB2,
+	0x83, 0x42, 0xF0, 0xD8, 0x23, 0x88, 0x00, 0x20,
+	0x16, 0x46, 0x32, 0x46, 0x82, 0x40, 0x1A, 0x42,
+	0x05, 0xD0, 0x40, 0x1C, 0xC0, 0xB2, 0x02, 0x28,
+	0xF7, 0xD3, 0x00, 0x26, 0x37, 0xE0, 0x28, 0x4A,
+	0x10, 0x70, 0xFA, 0xE7, 0x31, 0x46, 0x34, 0x22,
+	0x51, 0x43, 0x0C, 0x18, 0x27, 0x46, 0x20, 0x37,
+	0x38, 0x7C, 0x02, 0x28, 0x29, 0xD2, 0x0C, 0x23,
+	0x0A, 0x22, 0x2E, 0x21, 0x2C, 0x20, 0xE3, 0x5E,
+	0xA2, 0x5E, 0x61, 0x5E, 0x20, 0x5E, 0x04, 0xF0,
+	0x95, 0xF9, 0x29, 0x68, 0xB0, 0x31, 0xCA, 0x7B,
+	0x8B, 0x7B, 0x11, 0x02, 0x19, 0x43, 0x81, 0x42,
+	0x14, 0xD2, 0x38, 0x7C, 0x2C, 0x22, 0x0A, 0x23,
+	0xA2, 0x5E, 0x02, 0x21, 0xE3, 0x5E, 0x09, 0x1A,
+	0x4A, 0x43, 0x43, 0x43, 0xD2, 0x18, 0x52, 0x10,
+	0x62, 0x81, 0x2E, 0x22, 0xA2, 0x5E, 0x4A, 0x43,
+	0x0C, 0x21, 0x61, 0x5E, 0x41, 0x43, 0x50, 0x18,
+	0x40, 0x10, 0xA0, 0x81, 0x38, 0x7C, 0x40, 0x1C,
+	0x38, 0x74, 0x76, 0x1C, 0xF6, 0xB2, 0x02, 0x48,
+	0x01, 0x78, 0xB1, 0x42, 0xC6, 0xD8, 0xF8, 0xBD,
+	0xA0, 0x04, 0x00, 0x20, 0x40, 0x05, 0x00, 0x20,
+	0x34, 0x00, 0x00, 0x20, 0x36, 0x00, 0x00, 0x20,
+	0xB8, 0x02, 0x00, 0x20, 0x48, 0x00, 0x00, 0x20,
+	0x46, 0x00, 0x00, 0x20, 0x47, 0x00, 0x00, 0x20,
+	0x2A, 0x00, 0x00, 0x20, 0x3E, 0x00, 0x00, 0x20,
+	0x31, 0x00, 0x00, 0x20, 0xF1, 0xB5, 0x59, 0x48,
+	0x8A, 0xB0, 0x00, 0x68, 0x08, 0x90, 0x1B, 0x30,
+	0x57, 0x4B, 0x09, 0x90, 0x00, 0x20, 0x18, 0x5E,
+	0xC1, 0x0F, 0x08, 0x18, 0xC0, 0x03, 0x01, 0x0C,
+	0x08, 0x98, 0x00, 0x91, 0x90, 0x30, 0x41, 0x7B,
+	0x02, 0x7B, 0x08, 0x02, 0x10, 0x43, 0x00, 0x99,
+	0x07, 0x90, 0x88, 0x42, 0x00, 0xD9, 0x00, 0x90,
+	0x4F, 0x48, 0x4E, 0x4A, 0x02, 0x80, 0x4F, 0x49,
+	0xD0, 0x43, 0x08, 0x80, 0x4E, 0x49, 0x00, 0x23,
+	0x0A, 0x80, 0x49, 0x49, 0x1E, 0x46, 0x08, 0x80,
+	0x4C, 0x49, 0x0B, 0x70, 0x01, 0x21, 0xC9, 0x03,
+	0x74, 0x00, 0x01, 0xA8, 0x01, 0x53, 0x48, 0x1E,
+	0x03, 0xA9, 0x08, 0x53, 0x00, 0x20, 0x01, 0x46,
+	0x35, 0x46, 0x1C, 0xE0, 0x09, 0x9A, 0x52, 0x5D,
+	0x41, 0x2A, 0x16, 0xD0, 0x0A, 0x9A, 0x6B, 0x00,
+	0xD2, 0x5E, 0x01, 0xAB, 0x49, 0x1C, 0x1F, 0x5F,
+	0x80, 0x18, 0xC9, 0xB2, 0x97, 0x42, 0x00, 0xDA,
+	0x1A, 0x53, 0x03, 0xAB, 0x1F, 0x5F, 0x97, 0x42,
+	0x00, 0xDD, 0x1A, 0x53, 0x00, 0x9B, 0x9A, 0x42,
+	0x03, 0xDD, 0x3A, 0x4B, 0x1A, 0x78, 0x52, 0x1C,
+	0x1A, 0x70, 0x2D, 0x1D, 0xED, 0xB2, 0x30, 0x2D,
+	0xE0, 0xD3, 0x00, 0x29, 0x02, 0xD0, 0xFF, 0xF7,
+	0x4B, 0xF8, 0x00, 0xE0, 0x00, 0x20, 0x05, 0xA9,
+	0x08, 0x53, 0x2D, 0x49, 0x01, 0xAD, 0x00, 0x22,
+	0x28, 0x5F, 0x8A, 0x5E, 0x90, 0x42, 0x00, 0xDD,
+	0x08, 0x80, 0x2D, 0x49, 0x03, 0xAF, 0x00, 0x22,
+	0x38, 0x5F, 0x8A, 0x5E, 0x90, 0x42, 0x00, 0xDA,
+	0x08, 0x80, 0x76, 0x1C, 0xF6, 0xB2, 0x04, 0x2E,
+	0xB8, 0xD3, 0x07, 0x98, 0x26, 0x49, 0x43, 0x08,
+	0x00, 0x20, 0x08, 0x5E, 0x26, 0x4A, 0x83, 0x42,
+	0x06, 0xDA, 0x1F, 0x49, 0x00, 0x23, 0xCB, 0x5E,
+	0x19, 0x1A, 0x07, 0x98, 0x81, 0x42, 0x06, 0xDB,
+	0x08, 0x98, 0x80, 0x30, 0x81, 0x7B, 0x1F, 0x48,
+	0x00, 0x78, 0x81, 0x42, 0x01, 0xD9, 0x01, 0x20,
+	0x00, 0xE0, 0x00, 0x20, 0x10, 0x70, 0x00, 0x24,
+	0x05, 0xAE, 0x60, 0x00, 0x31, 0x5E, 0x00, 0x91,
+	0x3B, 0x5E, 0x2A, 0x5E, 0x21, 0x46, 0x0A, 0x98,
+	0x00, 0xF0, 0x5C, 0xF9, 0x64, 0x1C, 0xE4, 0xB2,
+	0x04, 0x2C, 0xF2, 0xD3, 0x0D, 0x48, 0x00, 0x68,
+	0x90, 0x30, 0x41, 0x7B, 0x02, 0x7B, 0x08, 0x02,
+	0x10, 0x43, 0x0E, 0x49, 0x00, 0x22, 0x8A, 0x5E,
+	0x82, 0x42, 0x0D, 0xDA, 0x0F, 0x48, 0x41, 0x88,
+	0x0A, 0x29, 0x09, 0xD9, 0x40, 0x88, 0x0B, 0x28,
+	0x03, 0xD1, 0x80, 0x21, 0x0C, 0x48, 0xFF, 0xF7,
+	0x20, 0xF8, 0x00, 0x20, 0x0B, 0xB0, 0xF0, 0xBD,
+	0x01, 0x20, 0xFB, 0xE7, 0xB8, 0x02, 0x00, 0x20,
+	0x7C, 0x04, 0x00, 0x20, 0xFF, 0x7F, 0x00, 0x00,
+	0x76, 0x04, 0x00, 0x20, 0x78, 0x04, 0x00, 0x20,
+	0x7A, 0x04, 0x00, 0x20, 0x8F, 0x02, 0x00, 0x20,
+	0x9A, 0x01, 0x00, 0x20, 0x84, 0x04, 0x00, 0x20,
+	0xF4, 0x05, 0x00, 0x20, 0xF0, 0xB5, 0x00, 0x20,
+	0x41, 0x1F, 0x8C, 0x46, 0x1E, 0x49, 0x05, 0x24,
+	0x09, 0x78, 0xC9, 0x07, 0x36, 0xD1, 0x1D, 0x49,
+	0x09, 0x68, 0x90, 0x31, 0x4A, 0x7B, 0x0B, 0x7B,
+	0x11, 0x02, 0x1B, 0x4A, 0x19, 0x43, 0x13, 0x5E,
+	0x99, 0x42, 0x06, 0xDA, 0x19, 0x49, 0x09, 0x88,
+	0x00, 0x29, 0x01, 0xD0, 0x0A, 0x24, 0x00, 0xE0,
+	0x5A, 0x24, 0x17, 0x4F, 0x17, 0x4D, 0x00, 0x26,
+	0x17, 0x4A, 0x41, 0x00, 0x52, 0x5E, 0xC9, 0x19,
+	0x00, 0x23, 0xCB, 0x5E, 0x9A, 0x42, 0x02, 0xDD,
+	0x2A, 0x5C, 0x52, 0x1C, 0x03, 0xE0, 0x9A, 0x42,
+	0x03, 0xDA, 0x2A, 0x5C, 0x52, 0x1E, 0x2A, 0x54,
+	0x00, 0xE0, 0x2E, 0x54, 0x2A, 0x56, 0xA2, 0x42,
+	0x02, 0xDB, 0x0A, 0x88, 0x52, 0x1C, 0x03, 0xE0,
+	0x62, 0x45, 0x03, 0xDC, 0x0A, 0x88, 0x52, 0x1E,
+	0x0A, 0x80, 0x2E, 0x54, 0x40, 0x1C, 0xC0, 0xB2,
+	0x30, 0x28, 0xDD, 0xD3, 0xF0, 0xBD, 0x00, 0x00,
+	0xEB, 0x02, 0x00, 0x20, 0xB8, 0x02, 0x00, 0x20,
+	0x78, 0x04, 0x00, 0x20, 0xF0, 0x02, 0x00, 0x20,
+	0x00, 0x00, 0x01, 0x20, 0x0C, 0x03, 0x00, 0x20,
+	0xD4, 0x00, 0x00, 0x20, 0xFE, 0xB5, 0x04, 0x46,
+	0x37, 0x48, 0x00, 0x78, 0x00, 0x28, 0x46, 0xD0,
+	0x1E, 0x20, 0x00, 0x90, 0x40, 0x42, 0x01, 0x90,
+	0x00, 0x29, 0x12, 0xD0, 0x33, 0x48, 0x00, 0x78,
+	0x15, 0x28, 0x40, 0xD0, 0x32, 0x4B, 0x00, 0x20,
+	0x41, 0x00, 0x5A, 0x5A, 0x40, 0x1C, 0xD5, 0x00,
+	0xAA, 0x1A, 0x65, 0x5A, 0xC0, 0xB2, 0x52, 0x19,
+	0x12, 0xB2, 0xD2, 0x10, 0x5A, 0x52, 0x30, 0x28,
+	0xF2, 0xD3, 0x00, 0x25, 0x00, 0x21, 0x0A, 0x46,
+	0x08, 0x46, 0x2A, 0x4E, 0x2B, 0x18, 0x36, 0x68,
+	0x9E, 0x19, 0xF6, 0x7E, 0x41, 0x2E, 0x19, 0xD0,
+	0x5F, 0x00, 0x25, 0x4B, 0xE6, 0x5F, 0xDB, 0x5F,
+	0xB4, 0x46, 0xF3, 0x1A, 0xDE, 0x0F, 0xF3, 0x18,
+	0x5B, 0x10, 0x00, 0x9E, 0x1B, 0xB2, 0xB3, 0x42,
+	0x02, 0xDA, 0x66, 0x46, 0xF6, 0x1A, 0xE6, 0x53,
+	0x00, 0x9E, 0xB3, 0x42, 0x04, 0xDA, 0x01, 0x9E,
+	0xB3, 0x42, 0x01, 0xDD, 0xD2, 0x18, 0x12, 0xB2,
+	0x49, 0x1C, 0xC9, 0xB2, 0x40, 0x1C, 0xC0, 0xB2,
+	0x03, 0x28, 0xDA, 0xD3, 0x00, 0x29, 0x21, 0xD0,
+	0x01, 0x29, 0x0A, 0xD0, 0x0A, 0xE0, 0x0F, 0x20,
+	0x00, 0x90, 0x40, 0x42, 0xB7, 0xE7, 0x60, 0x22,
+	0x21, 0x46, 0x11, 0x48, 0xFE, 0xF7, 0x48, 0xFF,
+	0xC7, 0xE7, 0x02, 0x21, 0x10, 0x46, 0xFE, 0xF7,
+	0x2F, 0xFF, 0x02, 0xB2, 0x0D, 0x4B, 0x00, 0x20,
+	0x1E, 0x68, 0x29, 0x18, 0x8E, 0x19, 0xF6, 0x7E,
+	0x41, 0x2E, 0x03, 0xD0, 0x49, 0x00, 0x66, 0x5A,
+	0xB6, 0x1A, 0x66, 0x52, 0x40, 0x1C, 0xC0, 0xB2,
+	0x03, 0x28, 0xF1, 0xD3, 0x2D, 0x1D, 0xED, 0xB2,
+	0x30, 0x2D, 0xAF, 0xD3, 0xFE, 0xBD, 0x00, 0x00,
+	0x85, 0x02, 0x00, 0x20, 0x70, 0x04, 0x00, 0x20,
+	0x3C, 0x03, 0x00, 0x20, 0xB8, 0x02, 0x00, 0x20,
+	0x10, 0xB5, 0x21, 0x49, 0x1F, 0x48, 0xC8, 0x61,
+	0x20, 0x48, 0x81, 0x68, 0x01, 0x22, 0x52, 0x02,
+	0x11, 0x43, 0x81, 0x60, 0x1E, 0x4C, 0x20, 0x68,
+	0x30, 0x21, 0x88, 0x43, 0x20, 0x60, 0x1D, 0x48,
+	0x00, 0x78, 0x00, 0x28, 0x11, 0xD0, 0x1C, 0xA0,
+	0x03, 0xF0, 0xA4, 0xFA, 0x1C, 0x48, 0x01, 0x68,
+	0x4A, 0x06, 0x90, 0x21, 0x00, 0x2A, 0x02, 0x68,
+	0x02, 0xDA, 0x0A, 0x43, 0x02, 0x60, 0x10, 0xBD,
+	0x8A, 0x43, 0x02, 0x60, 0x17, 0x49, 0x41, 0x61,
+	0x10, 0xBD, 0x17, 0xA0, 0x03, 0xF0, 0x92, 0xFA,
+	0x05, 0x21, 0x17, 0x48, 0x09, 0x07, 0x48, 0x62,
+	0x80, 0x07, 0x41, 0x6A, 0x82, 0x13, 0x11, 0x43,
+	0x41, 0x62, 0x14, 0x49, 0x0A, 0x68, 0x03, 0x12,
+	0x1A, 0x43, 0x0A, 0x60, 0xE2, 0x68, 0x09, 0x68,
+	0x0A, 0x43, 0xE2, 0x60, 0x01, 0x68, 0x02, 0x14,
+	0x11, 0x43, 0x01, 0x60, 0x0E, 0x49, 0x40, 0x14,
+	0x08, 0x60, 0x10, 0xBD, 0x0B, 0x0B, 0x0B, 0x0B,
+	0x40, 0x00, 0x00, 0x50, 0x00, 0x06, 0x00, 0x50,
+	0x00, 0x10, 0x00, 0x50, 0xCF, 0x00, 0x00, 0x20,
+	0x41, 0x53, 0x49, 0x0D, 0x0A, 0x00, 0x00, 0x00,
+	0x00, 0x11, 0x00, 0x50, 0x16, 0x00, 0x03, 0x00,
+	0x41, 0x53, 0x54, 0x0D, 0x0A, 0x00, 0x00, 0x00,
+	0xF1, 0x2A, 0x02, 0x00, 0x50, 0x02, 0x00, 0x20,
+	0x00, 0xE1, 0x00, 0xE0, 0xFF, 0xB5, 0x04, 0x46,
+	0x55, 0x48, 0x1D, 0x46, 0x02, 0x68, 0x00, 0x21,
+	0x17, 0x46, 0x1B, 0x32, 0x94, 0x46, 0x3A, 0x46,
+	0x80, 0x32, 0x53, 0x7A, 0x16, 0x7A, 0x1A, 0x06,
+	0x12, 0x14, 0x03, 0x23, 0x32, 0x43, 0xDB, 0x03,
+	0x5A, 0x43, 0x12, 0x14, 0x96, 0x46, 0x4D, 0x4A,
+	0x83, 0xB0, 0x16, 0x78, 0x3A, 0x46, 0x90, 0x32,
+	0x57, 0x7B, 0x12, 0x7B, 0x3F, 0x02, 0x3A, 0x43,
+	0x08, 0x46, 0x00, 0x2E, 0x03, 0xD1, 0x48, 0x4B,
+	0x1B, 0x78, 0x14, 0x2B, 0x01, 0xD2, 0x53, 0x08,
+	0x00, 0xE0, 0x93, 0x08, 0x00, 0x93, 0x45, 0x4B,
+	0x76, 0x46, 0x1E, 0x80, 0x0C, 0x9B, 0x00, 0x2B,
+	0x07, 0xDA, 0x43, 0x4E, 0x00, 0x27, 0xF7, 0x5F,
+	0x00, 0x9E, 0xB7, 0x42, 0x01, 0xDA, 0x53, 0x42,
+	0x01, 0xE0, 0x53, 0x08, 0x5B, 0x42, 0x0C, 0x9F,
+	0x1B, 0xB2, 0x05, 0x9E, 0x01, 0x93, 0xF6, 0x1B,
+	0x3B, 0x46, 0xEF, 0x1B, 0x00, 0x2E, 0x00, 0xDA,
+	0x76, 0x42, 0x00, 0x2F, 0x00, 0xDA, 0x7F, 0x42,
+	0xBE, 0x42, 0x01, 0xDA, 0x00, 0x2B, 0x15, 0xDD,
+	0x00, 0x9B, 0x0C, 0x9E, 0xDF, 0x00, 0xF3, 0x1B,
+	0xAB, 0x42, 0x06, 0xDD, 0x00, 0x2D, 0x04, 0xDA,
+	0x05, 0x9B, 0x5B, 0x19, 0xD2, 0x18, 0x52, 0x10,
+	0x01, 0xE0, 0x00, 0x9A, 0xAA, 0x18, 0x13, 0xB2,
+	0x72, 0x46, 0x73, 0x45, 0x00, 0xDD, 0x13, 0x46,
+	0x04, 0x9A, 0x19, 0xE0, 0x05, 0x9B, 0x9A, 0x1A,
+	0x13, 0xB2, 0x72, 0x46, 0x52, 0x42, 0x93, 0x42,
+	0x00, 0xDA, 0x13, 0xB2, 0x04, 0x9A, 0x1F, 0xE0,
+	0x66, 0x46, 0xB6, 0x5C, 0x41, 0x2E, 0x09, 0xD0,
+	0x56, 0x00, 0xA6, 0x5F, 0x9E, 0x42, 0x05, 0xDA,
+	0x01, 0x9F, 0xBE, 0x42, 0x02, 0xDD, 0x30, 0x18,
+	0x49, 0x1C, 0xC9, 0xB2, 0x12, 0x1D, 0xD2, 0xB2,
+	0x30, 0x2A, 0xED, 0xD3, 0x0E, 0xE0, 0x66, 0x46,
+	0xB6, 0x5C, 0x41, 0x2E, 0x06, 0xD0, 0x56, 0x00,
+	0xA6, 0x5F, 0x9E, 0x42, 0x02, 0xDD, 0x30, 0x18,
+	0x49, 0x1C, 0xC9, 0xB2, 0x12, 0x1D, 0xD2, 0xB2,
+	0x30, 0x2A, 0xF0, 0xD3, 0x00, 0x29, 0x02, 0xD0,
+	0xFE, 0xF7, 0x22, 0xFE, 0x01, 0x46, 0x04, 0x98,
+	0x05, 0xE0, 0x42, 0x00, 0xA3, 0x5A, 0x5B, 0x1A,
+	0x00, 0x1D, 0xA3, 0x52, 0xC0, 0xB2, 0x30, 0x28,
+	0xF7, 0xD3, 0x0E, 0x4A, 0x00, 0x23, 0x68, 0x1A,
+	0xD3, 0x5E, 0x00, 0xB2, 0x98, 0x42, 0x00, 0xDA,
+	0x10, 0x80, 0x05, 0x98, 0x00, 0x22, 0x40, 0x1A,
+	0x09, 0x49, 0x00, 0xB2, 0x8A, 0x5E, 0x90, 0x42,
+	0x00, 0xDD, 0x08, 0x80, 0x07, 0xB0, 0xF0, 0xBD,
+	0xB8, 0x02, 0x00, 0x20, 0x9A, 0x01, 0x00, 0x20,
+	0xEA, 0x02, 0x00, 0x20, 0xEE, 0x02, 0x00, 0x20,
+	0x7C, 0x04, 0x00, 0x20, 0x76, 0x04, 0x00, 0x20,
+	0x78, 0x04, 0x00, 0x20, 0xF8, 0xB5, 0x06, 0x46,
+	0x0D, 0x46, 0x30, 0x23, 0xFF, 0xF7, 0x80, 0xF8,
+	0x04, 0x00, 0x28, 0xD0, 0x19, 0x4F, 0x00, 0x2A,
+	0x21, 0xD0, 0x00, 0x20, 0xFE, 0xF7, 0xCA, 0xFE,
+	0x02, 0xE0, 0x98, 0x00, 0x29, 0x58, 0x21, 0x50,
+	0x18, 0x46, 0x5B, 0x1E, 0xDB, 0xB2, 0x00, 0x28,
+	0xF7, 0xD1, 0x38, 0x68, 0x01, 0x88, 0x31, 0x43,
+	0x01, 0x80, 0x11, 0x49, 0x09, 0x68, 0xCA, 0x79,
+	0x8B, 0x79, 0x11, 0x02, 0x19, 0x43, 0x41, 0x80,
+	0xC1, 0x21, 0x89, 0x00, 0x0D, 0x48, 0xFF, 0xF7,
+	0xDF, 0xF8, 0x0D, 0x49, 0xC8, 0x63, 0x01, 0x20,
+	0xFE, 0xF7, 0xAC, 0xFE, 0x0C, 0xE0, 0x38, 0x68,
+	0x00, 0x88, 0x30, 0x40, 0x03, 0xD1, 0xF8, 0xBD,
+	0x98, 0x00, 0x21, 0x58, 0x29, 0x50, 0x18, 0x46,
+	0x5B, 0x1E, 0xDB, 0xB2, 0x00, 0x28, 0xF7, 0xD1,
+	0x01, 0x20, 0xF8, 0xBD, 0x08, 0x00, 0x00, 0x20,
+	0xB8, 0x02, 0x00, 0x20, 0x78, 0x7C, 0x00, 0x00,
+	0x40, 0x7F, 0x00, 0x00, 0x70, 0xB5, 0x17, 0x48,
+	0x5A, 0x25, 0x01, 0x78, 0x16, 0x48, 0x00, 0x29,
+	0x25, 0xD0, 0x09, 0x21, 0x01, 0x70, 0x15, 0x4C,
+	0x01, 0x21, 0xE0, 0x89, 0x89, 0x02, 0x88, 0x42,
+	0x01, 0xD0, 0x03, 0x20, 0x60, 0x71, 0x11, 0x48,
+	0x30, 0x21, 0x28, 0x30, 0xFE, 0xF7, 0xC9, 0xFD,
+	0x00, 0x20, 0x60, 0x72, 0xA0, 0x71, 0x60, 0x62,
+	0x16, 0x21, 0x21, 0x72, 0x0C, 0x49, 0x20, 0x71,
+	0x08, 0x70, 0xA5, 0x81, 0x60, 0x81, 0x60, 0x8A,
+	0x09, 0x21, 0x09, 0x03, 0x08, 0x43, 0x60, 0x82,
+	0x03, 0x20, 0x40, 0x02, 0xE0, 0x61, 0x0D, 0x20,
+	0xC0, 0x01, 0x20, 0x62, 0x70, 0xBD, 0x05, 0x70,
+	0xD9, 0xE7, 0x00, 0x00, 0x85, 0x02, 0x00, 0x20,
+	0xB5, 0x02, 0x00, 0x20, 0xE4, 0x02, 0x00, 0x20,
+	0x87, 0x02, 0x00, 0x20, 0x0E, 0x48, 0x03, 0x21,
+	0x41, 0x71, 0x0E, 0x49, 0x41, 0x61, 0x0D, 0x49,
+	0x60, 0x31, 0x81, 0x61, 0x01, 0x21, 0x01, 0x70,
+	0x07, 0x22, 0x42, 0x70, 0x0A, 0x4B, 0x05, 0x22,
+	0x1A, 0x70, 0x0A, 0x4B, 0x1A, 0x70, 0x0A, 0x4B,
+	0x55, 0x22, 0xDA, 0x70, 0x04, 0x22, 0x02, 0x82,
+	0x00, 0x22, 0xC2, 0x70, 0x09, 0x22, 0x12, 0x03,
+	0x42, 0x82, 0x81, 0x70, 0x70, 0x47, 0x00, 0x00,
+	0xE4, 0x02, 0x00, 0x20, 0x00, 0x00, 0x04, 0x20,
+	0x85, 0x02, 0x00, 0x20, 0x71, 0x04, 0x00, 0x20,
+	0x90, 0x02, 0x00, 0x20, 0x04, 0x22, 0x0F, 0x49,
+	0x0C, 0x28, 0x10, 0xD0, 0x8B, 0x05, 0x0D, 0x28,
+	0x08, 0x6A, 0x10, 0xD0, 0x18, 0x43, 0x08, 0x62,
+	0x88, 0x6A, 0x10, 0x43, 0x88, 0x62, 0x0A, 0x4A,
+	0x01, 0x20, 0x10, 0x70, 0xC8, 0x68, 0xC8, 0x60,
+	0x88, 0x6A, 0x88, 0x62, 0x70, 0x47, 0x08, 0x6A,
+	0x40, 0x00, 0x40, 0x08, 0x00, 0xE0, 0x18, 0x43,
+	0x08, 0x62, 0x88, 0x6A, 0x90, 0x43, 0x88, 0x62,
+	0xF0, 0xE7, 0x00, 0x00, 0x00, 0x06, 0x00, 0x50,
+	0x9B, 0x01, 0x00, 0x20, 0x00, 0xB5, 0x08, 0x49,
+	0x0A, 0x28, 0x05, 0xD0, 0x07, 0x48, 0x00, 0x0C,
+	0x48, 0x63, 0x07, 0x48, 0x08, 0x63, 0x00, 0xBD,
+	0x06, 0x48, 0x00, 0x68, 0x08, 0x62, 0x0D, 0x20,
+	0xFF, 0xF7, 0xCC, 0xFF, 0x00, 0xBD, 0x00, 0x00,
+	0x00, 0x06, 0x00, 0x50, 0xBC, 0x02, 0x00, 0x20,
+	0xCC, 0x02, 0x00, 0x20, 0xC0, 0x02, 0x00, 0x20,
+	0x10, 0xB5, 0x1B, 0x49, 0x00, 0x20, 0x03, 0x00,
+	0xFE, 0xF7, 0xA0, 0xFD, 0x0C, 0x07, 0x0A, 0x0E,
+	0x26, 0x26, 0x11, 0x14, 0x17, 0x1A, 0x1D, 0x20,
+	0x23, 0x26, 0x16, 0x4A, 0x0A, 0x80, 0x1E, 0xE0,
+	0x14, 0x4A, 0x12, 0x1D, 0x4A, 0x80, 0x1A, 0xE0,
+	0x13, 0x4A, 0x8A, 0x80, 0x17, 0xE0, 0x13, 0x4A,
+	0x4A, 0x81, 0x14, 0xE0, 0x12, 0x4A, 0x8A, 0x81,
+	0x11, 0xE0, 0x12, 0x4A, 0xCA, 0x81, 0x0E, 0xE0,
+	0x11, 0x4A, 0x0A, 0x82, 0x0B, 0xE0, 0x11, 0x4A,
+	0x4A, 0x82, 0x08, 0xE0, 0x10, 0x4A, 0x8A, 0x82,
+	0x05, 0xE0, 0x10, 0x4A, 0xCA, 0x82, 0x02, 0xE0,
+	0x0F, 0x4A, 0x43, 0x00, 0xCA, 0x52, 0x40, 0x1C,
+	0x80, 0xB2, 0x0C, 0x28, 0xCF, 0xD3, 0x0D, 0xA0,
+	0x03, 0xF0, 0x88, 0xF8, 0x10, 0xBD, 0x00, 0x00,
+	0xCC, 0x02, 0x00, 0x20, 0x90, 0x02, 0x00, 0x20,
+	0x88, 0x02, 0x00, 0x20, 0x34, 0x01, 0x00, 0x20,
+	0xE0, 0x06, 0x00, 0x20, 0xE4, 0x06, 0x00, 0x20,
+	0xD8, 0x06, 0x00, 0x20, 0xC0, 0x02, 0x00, 0x20,
+	0xBC, 0x02, 0x00, 0x20, 0x9C, 0x01, 0x00, 0x20,
+	0xC4, 0x02, 0x00, 0x20, 0x49, 0x32, 0x43, 0x20,
+	0x4F, 0x4B, 0x0D, 0x0A, 0x00, 0x00, 0x00, 0x00,
+	0x70, 0xB5, 0x01, 0x25, 0xCA, 0x07, 0x0A, 0xD0,
+	0x00, 0x20, 0x70, 0xBD, 0x93, 0x00, 0xC4, 0x58,
+	0x66, 0x1C, 0x02, 0xD0, 0x1B, 0x18, 0x5B, 0x68,
+	0x23, 0x60, 0x92, 0x1C, 0x92, 0xB2, 0x8A, 0x42,
+	0xF4, 0xD3, 0x28, 0x46, 0x70, 0xBD, 0x00, 0x00,
+	0x10, 0xB5, 0x1D, 0x49, 0x0A, 0x68, 0x1C, 0x48,
+	0x40, 0x30, 0x02, 0x61, 0x4A, 0x68, 0x42, 0x61,
+	0x8A, 0x68, 0x82, 0x61, 0xC9, 0x68, 0xC1, 0x61,
+	0x82, 0x69, 0xF0, 0x21, 0x8A, 0x43, 0x82, 0x61,
+	0x82, 0x69, 0x0A, 0x43, 0x82, 0x61, 0x41, 0x69,
+	0x49, 0x04, 0x04, 0xD5, 0x41, 0x69, 0x01, 0x22,
+	0x52, 0x03, 0x89, 0x1A, 0x41, 0x61, 0x10, 0x48,
+	0x40, 0x38, 0x01, 0x68, 0x01, 0x22, 0x12, 0x06,
+	0x11, 0x43, 0x01, 0x60, 0x30, 0x21, 0x0D, 0x48,
+	0xFF, 0xF7, 0xC6, 0xFF, 0x0B, 0x48, 0x40, 0x21,
+	0xC0, 0x30, 0xFF, 0xF7, 0xC1, 0xFF, 0x0B, 0x20,
+	0xFE, 0xF7, 0xD4, 0xFC, 0x03, 0x20, 0xFE, 0xF7,
+	0xD1, 0xFC, 0x00, 0x20, 0xFE, 0xF7, 0xCE, 0xFC,
+	0x05, 0x20, 0xFE, 0xF7, 0xCB, 0xFC, 0x09, 0x20,
+	0xFE, 0xF7, 0xC8, 0xFC, 0x01, 0x20, 0x10, 0xBD,
+	0x40, 0x14, 0x00, 0x50, 0x98, 0x5A, 0x00, 0x00,
+	0x0E, 0x4A, 0x00, 0x21, 0x11, 0x60, 0x0E, 0x4A,
+	0x20, 0x21, 0x11, 0x60, 0x5A, 0x28, 0x13, 0xD0,
+	0x0C, 0x48, 0x01, 0x23, 0x00, 0x05, 0x00, 0x0D,
+	0x5B, 0x03, 0xC0, 0x18, 0x05, 0x22, 0x12, 0x07,
+	0x10, 0x62, 0x90, 0x00, 0xC2, 0x68, 0x0A, 0x43,
+	0xC2, 0x60, 0x05, 0x4A, 0x80, 0x3A, 0x11, 0x60,
+	0x41, 0x68, 0x19, 0x43, 0x41, 0x60, 0x70, 0x47,
+	0xFF, 0x20, 0xEA, 0xE7, 0xD0, 0x00, 0x00, 0x20,
+	0x80, 0xE1, 0x00, 0xE0, 0x24, 0x09, 0x00, 0x00,
+	0x10, 0xB5, 0x17, 0x4A, 0x11, 0x78, 0x81, 0x42,
+	0x29, 0xD0, 0x16, 0x49, 0x0B, 0x68, 0x01, 0x24,
+	0xA4, 0x02, 0x23, 0x43, 0x0B, 0x60, 0x10, 0x70,
+	0x05, 0x22, 0x40, 0x24, 0x12, 0x49, 0x12, 0x07,
+	0x80, 0x23, 0x00, 0x28, 0x09, 0xD0, 0x08, 0x68,
+	0x40, 0x06, 0x0C, 0xD4, 0x08, 0x68, 0x20, 0x43,
+	0x08, 0x60, 0xD0, 0x68, 0x98, 0x43, 0xD0, 0x60,
+	0x05, 0xE0, 0xD0, 0x68, 0x18, 0x43, 0xD0, 0x60,
+	0x08, 0x68, 0xA0, 0x43, 0x08, 0x60, 0x08, 0x68,
+	0x18, 0x43, 0x08, 0x60, 0xD0, 0x68, 0x01, 0x21,
+	0xC9, 0x03, 0x08, 0x43, 0xD0, 0x60, 0xE1, 0x20,
+	0x00, 0x02, 0x01, 0xF0, 0x61, 0xF8, 0x10, 0xBD,
+	0x04, 0x00, 0x00, 0x20, 0x00, 0x10, 0x00, 0x50,
+	0x00, 0x11, 0x00, 0x50, 0x10, 0xB5, 0x10, 0x4C,
+	0xA0, 0x79, 0x00, 0x28, 0x18, 0xD1, 0x00, 0xF0,
+	0xA5, 0xFF, 0x00, 0xF0, 0xA7, 0xFE, 0xFF, 0xF7,
+	0xF7, 0xFC, 0x0C, 0x48, 0x01, 0x78, 0x0C, 0x48,
+	0x00, 0x29, 0x02, 0xD0, 0x09, 0x21, 0x09, 0x02,
+	0x00, 0xE0, 0x0A, 0x49, 0x01, 0x60, 0x01, 0x20,
+	0xA0, 0x71, 0xE0, 0x71, 0x08, 0x48, 0x00, 0x78,
+	0xC0, 0x07, 0x02, 0xD0, 0x00, 0xF0, 0x0E, 0xF8,
+	0x10, 0xBD, 0x00, 0xF0, 0x77, 0xFE, 0x10, 0xBD,
+	0x84, 0x04, 0x00, 0x20, 0xCF, 0x00, 0x00, 0x20,
+	0x00, 0x10, 0x00, 0x50, 0x04, 0x08, 0x00, 0x00,
+	0xCE, 0x00, 0x00, 0x20, 0x10, 0xB5, 0x1D, 0x4C,
+	0xA0, 0x79, 0x00, 0x28, 0x35, 0xD0, 0x1C, 0xA0,
+	0x02, 0xF0, 0x88, 0xFF, 0x00, 0x20, 0xA0, 0x71,
+	0xE0, 0x71, 0x1E, 0x48, 0x81, 0x68, 0x01, 0x22,
+	0x52, 0x02, 0x91, 0x43, 0x81, 0x60, 0x1C, 0x48,
+	0x01, 0x68, 0x92, 0x00, 0x91, 0x43, 0x01, 0x60,
+	0x1A, 0x48, 0x01, 0x68, 0x10, 0x22, 0x91, 0x43,
+	0x01, 0x60, 0x01, 0x68, 0x80, 0x22, 0x11, 0x43,
+	0x01, 0x60, 0x17, 0x49, 0x41, 0x61, 0x17, 0x48,
+	0x00, 0x78, 0x00, 0x28, 0x15, 0xD1, 0x05, 0x20,
+	0x00, 0x07, 0x41, 0x6A, 0x92, 0x02, 0x91, 0x43,
+	0x41, 0x62, 0x41, 0x6A, 0x09, 0x22, 0x12, 0x05,
+	0x11, 0x43, 0x41, 0x62, 0x80, 0x00, 0xC2, 0x68,
+	0x41, 0x14, 0x8A, 0x43, 0xC2, 0x60, 0x02, 0x68,
+	0x03, 0x14, 0x9A, 0x43, 0x02, 0x60, 0x0C, 0x48,
+	0x01, 0x60, 0x10, 0xBD, 0x84, 0x04, 0x00, 0x20,
+	0x4C, 0x65, 0x61, 0x76, 0x65, 0x20, 0x41, 0x75,
+	0x74, 0x6F, 0x53, 0x63, 0x61, 0x6E, 0x0D, 0x0A,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x50,
+	0x00, 0x10, 0x00, 0x50, 0x00, 0x11, 0x00, 0x50,
+	0xC8, 0x03, 0x60, 0x00, 0xCF, 0x00, 0x00, 0x20,
+	0x80, 0xE1, 0x00, 0xE0, 0x00, 0xB5, 0x08, 0x49,
+	0x83, 0x20, 0x08, 0x70, 0x07, 0x49, 0x00, 0x20,
+	0x08, 0x70, 0x07, 0x48, 0x00, 0x68, 0x07, 0x49,
+	0x40, 0x05, 0x40, 0x0F, 0x08, 0x73, 0x5A, 0x20,
+	0xFF, 0xF7, 0x16, 0xFF, 0x00, 0xBD, 0x00, 0x00,
+	0x84, 0x04, 0x00, 0x20, 0xCE, 0x00, 0x00, 0x20,
+	0x00, 0x11, 0x00, 0x50, 0x50, 0x02, 0x00, 0x20,
+	0xF0, 0xB5, 0x04, 0x46, 0x8F, 0xB0, 0xA6, 0x48,
+	0x0A, 0x90, 0xA5, 0x48, 0xA6, 0x4D, 0x60, 0x30,
+	0x0B, 0x90, 0xA4, 0x48, 0x03, 0x90, 0xA5, 0x48,
+	0x05, 0x95, 0x01, 0x68, 0x22, 0x46, 0xA4, 0xA0,
+	0x02, 0xF0, 0x10, 0xFF, 0x03, 0x98, 0x08, 0x90,
+	0xA5, 0x48, 0xA6, 0x4A, 0x00, 0x21, 0x09, 0x95,
+	0x8B, 0x00, 0x08, 0x9D, 0x49, 0x1C, 0xE8, 0x50,
+	0x09, 0x9D, 0x89, 0xB2, 0xEA, 0x50, 0x18, 0x29,
+	0xF6, 0xD3, 0x01, 0x20, 0x80, 0x02, 0x06, 0x90,
+	0x20, 0x46, 0x07, 0x94, 0x50, 0x30, 0x00, 0xB2,
+	0x50, 0x3C, 0x0C, 0x90, 0x20, 0xB2, 0x0D, 0x90,
+	0x00, 0xF0, 0x6E, 0xFC, 0x00, 0xF0, 0xE2, 0xFE,
+	0x00, 0xF0, 0x6E, 0xF9, 0x98, 0x48, 0x00, 0x78,
+	0xC0, 0x07, 0x18, 0xD1, 0x01, 0x90, 0x00, 0x20,
+	0x08, 0x9A, 0x81, 0x00, 0x53, 0x58, 0x0A, 0x9A,
+	0x40, 0x1C, 0x53, 0x50, 0x09, 0x9A, 0x80, 0xB2,
+	0x53, 0x58, 0x0B, 0x9A, 0x18, 0x28, 0x53, 0x50,
+	0xF2, 0xD3, 0x00, 0xF0, 0xB7, 0xFD, 0x00, 0xF0,
+	0xC9, 0xFE, 0x00, 0xF0, 0x55, 0xF9, 0x8C, 0x48,
+	0x00, 0x78, 0xC0, 0x07, 0x02, 0xD0, 0x00, 0x20,
+	0x0F, 0xB0, 0xF0, 0xBD, 0x3B, 0x46, 0x89, 0xA0,
+	0x00, 0x9A, 0x01, 0x99, 0x02, 0xF0, 0xCA, 0xFE,
+	0x00, 0x25, 0x2D, 0xE0, 0x00, 0x2D, 0x2B, 0xD0,
+	0x01, 0x98, 0x05, 0x28, 0x73, 0xD9, 0x30, 0x2F,
+	0x71, 0xD2, 0x7A, 0x4C, 0x05, 0x98, 0x40, 0x3C,
+	0x04, 0x90, 0x60, 0x6B, 0x03, 0x26, 0x36, 0x05,
+	0x01, 0x21, 0xB0, 0x43, 0x49, 0x05, 0x40, 0x18,
+	0x60, 0x63, 0x00, 0xF0, 0x8F, 0xFD, 0x00, 0xF0,
+	0xA1, 0xFE, 0x00, 0xF0, 0x2D, 0xF9, 0x60, 0x6B,
+	0x01, 0x21, 0xB0, 0x43, 0x09, 0x05, 0x40, 0x18,
+	0x60, 0x63, 0x00, 0x27, 0x00, 0x26, 0x79, 0x48,
+	0x00, 0x68, 0x80, 0x19, 0xC0, 0x7E, 0x41, 0x28,
+	0x0E, 0xD0, 0x70, 0x00, 0x04, 0x99, 0x02, 0x90,
+	0x0C, 0x5A, 0x16, 0x2E, 0x12, 0xD0, 0x1A, 0xE0,
+	0x00, 0x98, 0x30, 0x28, 0x77, 0xD0, 0x03, 0x98,
+	0x04, 0x90, 0x00, 0x20, 0x00, 0x90, 0xE9, 0xE7,
+	0x00, 0x2D, 0x02, 0xD0, 0x7F, 0x1C, 0xBF, 0xB2,
+	0x68, 0xE0, 0x00, 0x98, 0x40, 0x1C, 0x80, 0xB2,
+	0x00, 0x90, 0x63, 0xE0, 0x6A, 0x48, 0x2C, 0x23,
+	0x00, 0x68, 0x22, 0x46, 0xC3, 0x5E, 0x31, 0x46,
+	0x68, 0xA0, 0x02, 0xF0, 0x7F, 0xFE, 0x01, 0x98,
+	0x0A, 0x28, 0x0C, 0xD2, 0x64, 0x49, 0x02, 0x98,
+	0x09, 0x68, 0x09, 0x5E, 0x07, 0x98, 0x81, 0x42,
+	0x01, 0xDA, 0x06, 0x98, 0x84, 0x43, 0x06, 0x98,
+	0x40, 0x08, 0x04, 0x43, 0x32, 0xE0, 0x5E, 0x48,
+	0x02, 0x99, 0x00, 0x68, 0x41, 0x5E, 0x00, 0x29,
+	0x01, 0xDB, 0x08, 0x46, 0x00, 0xE0, 0x48, 0x42,
+	0x00, 0xB2, 0x40, 0x30, 0xC2, 0x17, 0x52, 0x0E,
+	0x10, 0x18, 0x0D, 0x9A, 0xC0, 0x11, 0x91, 0x42,
+	0x10, 0xDA, 0x21, 0x05, 0x06, 0xD5, 0x20, 0x1A,
+	0x84, 0xB2, 0x01, 0x20, 0xC0, 0x02, 0x84, 0x42,
+	0x11, 0xD3, 0x17, 0xE0, 0x84, 0x42, 0x03, 0xDD,
+	0x20, 0x1A, 0x84, 0xB2, 0x12, 0xE0, 0x2E, 0xE0,
+	0x00, 0x24, 0x0F, 0xE0, 0x0C, 0x9A, 0x91, 0x42,
+	0x15, 0xDD, 0x21, 0x05, 0x05, 0xD5, 0x20, 0x18,
+	0x84, 0xB2, 0x4D, 0x48, 0x04, 0xE0, 0x04, 0x46,
+	0x04, 0xE0, 0x20, 0x18, 0x84, 0xB2, 0x4B, 0x48,
+	0x84, 0x42, 0xF8, 0xD8, 0x00, 0x2D, 0x0F, 0xD0,
+	0x01, 0x20, 0xC0, 0x02, 0x04, 0x43, 0x05, 0x99,
+	0x02, 0x98, 0x0C, 0x52, 0x0E, 0xE0, 0x00, 0x2D,
+	0x02, 0xD0, 0x7F, 0x1C, 0xBF, 0xB2, 0xF3, 0xE7,
+	0x00, 0x98, 0x40, 0x1C, 0x80, 0xB2, 0x00, 0x90,
+	0x03, 0x99, 0x02, 0x98, 0x0C, 0x52, 0x01, 0x98,
+	0x05, 0x28, 0xE9, 0xD9, 0x76, 0x1C, 0xB6, 0xB2,
+	0x30, 0x2E, 0x00, 0xD2, 0x77, 0xE7, 0x6D, 0x1C,
+	0xED, 0xB2, 0x02, 0x2D, 0x00, 0xD2, 0x51, 0xE7,
+	0x06, 0x98, 0x40, 0x08, 0x06, 0x90, 0x38, 0xA0,
+	0x02, 0xF0, 0x14, 0xFE, 0x00, 0x98, 0x30, 0x28,
+	0x03, 0xD1, 0x30, 0x2F, 0x01, 0xD1, 0x40, 0x20,
+	0x01, 0x90, 0x01, 0x98, 0x40, 0x1C, 0x80, 0xB2,
+	0x01, 0x90, 0x40, 0x28, 0x00, 0xD8, 0x1A, 0xE7,
+	0x1C, 0x4C, 0x40, 0x3C, 0x60, 0x6B, 0x03, 0x25,
+	0x2D, 0x05, 0x01, 0x21, 0xA8, 0x43, 0x09, 0x05,
+	0x40, 0x18, 0x60, 0x63, 0x00, 0xF0, 0xD6, 0xFC,
+	0x00, 0xF0, 0xE8, 0xFD, 0x00, 0xF0, 0x74, 0xF8,
+	0x21, 0x4E, 0x01, 0x21, 0x30, 0x68, 0x00, 0xF0,
+	0x31, 0xFE, 0x60, 0x6B, 0x01, 0x21, 0xA8, 0x43,
+	0x49, 0x05, 0x40, 0x18, 0x60, 0x63, 0x00, 0xF0,
+	0xC5, 0xFC, 0x00, 0xF0, 0xD7, 0xFD, 0x00, 0xF0,
+	0x63, 0xF8, 0x01, 0x21, 0x30, 0x68, 0x00, 0xF0,
+	0x21, 0xFE, 0x60, 0x6B, 0x28, 0x43, 0x60, 0x63,
+	0x00, 0xF0, 0xB8, 0xFC, 0x00, 0xF0, 0xCA, 0xFD,
+	0x00, 0xF0, 0x56, 0xF8, 0x01, 0x21, 0x30, 0x68,
+	0x00, 0xF0, 0x14, 0xFE, 0x01, 0x20, 0xFF, 0xE6,
+	0x00, 0x20, 0x00, 0x50, 0xF4, 0x05, 0x00, 0x20,
+	0x00, 0x00, 0x01, 0x20, 0xC0, 0x10, 0x00, 0x50,
+	0x5B, 0x43, 0x46, 0x42, 0x3A, 0x25, 0x78, 0x3A,
+	0x25, 0x64, 0x5D, 0x0D, 0x0A, 0x00, 0x00, 0x00,
+	0x00, 0x04, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x0C,
+	0xCE, 0x00, 0x00, 0x20, 0x5B, 0x25, 0x64, 0x3A,
+	0x25, 0x64, 0x3A, 0x25, 0x64, 0x5D, 0x20, 0x0D,
+	0x0A, 0x00, 0x00, 0x00, 0xB8, 0x02, 0x00, 0x20,
+	0xF8, 0x02, 0x00, 0x20, 0x25, 0x64, 0x2C, 0x30,
+	0x78, 0x25, 0x78, 0x2C, 0x25, 0x64, 0x2C, 0x00,
+	0xFF, 0x0F, 0x00, 0x00, 0xFF, 0x07, 0x00, 0x00,
+	0x0D, 0x0A, 0x00, 0x00, 0x10, 0xB5, 0x0B, 0x49,
+	0x30, 0x24, 0x00, 0x28, 0x0A, 0x4A, 0x0B, 0x4B,
+	0x08, 0x68, 0x06, 0xD0, 0x20, 0x43, 0x08, 0x60,
+	0x09, 0x48, 0x10, 0x60, 0x08, 0x48, 0x60, 0x30,
+	0x05, 0xE0, 0xA0, 0x43, 0x08, 0x60, 0x07, 0x48,
+	0x10, 0x60, 0x06, 0x48, 0x60, 0x30, 0x18, 0x60,
+	0x10, 0xBD, 0x00, 0x00, 0x00, 0x10, 0x00, 0x50,
+	0xF8, 0x02, 0x00, 0x20, 0xFC, 0x02, 0x00, 0x20,
+	0x00, 0x00, 0x04, 0x20, 0x00, 0x10, 0x04, 0x20,
+	0xF0, 0xB5, 0x11, 0x48, 0x01, 0x68, 0x11, 0x4D,
+	0x89, 0x06, 0x30, 0x22, 0x60, 0x35, 0x00, 0x29,
+	0x0E, 0x4B, 0x0F, 0x4C, 0x0F, 0x4E, 0x10, 0x4F,
+	0x01, 0x68, 0x0A, 0xDA, 0x91, 0x43, 0x01, 0x60,
+	0x23, 0x60, 0x0E, 0x48, 0x35, 0x60, 0x07, 0x60,
+	0x0B, 0x48, 0x0D, 0x49, 0x60, 0x30, 0x08, 0x60,
+	0xF0, 0xBD, 0x11, 0x43, 0x01, 0x60, 0x08, 0x48,
+	0x27, 0x60, 0x60, 0x30, 0x30, 0x60, 0x07, 0x48,
+	0x03, 0x60, 0x07, 0x48, 0x05, 0x60, 0xF0, 0xBD,
+	0x00, 0x10, 0x00, 0x50, 0x00, 0x00, 0x04, 0x20,
+	0x64, 0x02, 0x00, 0x20, 0x68, 0x02, 0x00, 0x20,
+	0x00, 0x10, 0x04, 0x20, 0xF8, 0x02, 0x00, 0x20,
+	0xFC, 0x02, 0x00, 0x20, 0x00, 0xB5, 0x09, 0x48,
+	0x00, 0x78, 0x00, 0x28, 0x08, 0xD0, 0x01, 0x28,
+	0x09, 0xD0, 0x02, 0x28, 0x03, 0xD1, 0x06, 0x49,
+	0x04, 0x20, 0x00, 0xF0, 0x65, 0xF8, 0x00, 0xBD,
+	0x04, 0x49, 0x01, 0x20, 0xF9, 0xE7, 0x04, 0x49,
+	0x02, 0x20, 0xF6, 0xE7, 0x71, 0x04, 0x00, 0x20,
+	0xFC, 0x03, 0x00, 0x20, 0x9C, 0x03, 0x00, 0x20,
+	0xEC, 0x06, 0x00, 0x20, 0xF8, 0xB5, 0x07, 0x46,
+	0x00, 0xF0, 0xAA, 0xFA, 0x21, 0x4D, 0x60, 0x21,
+	0x28, 0x46, 0xFE, 0xF7, 0xAE, 0xF9, 0x00, 0x24,
+	0x00, 0xF0, 0x18, 0xFD, 0xFF, 0xF7, 0xA4, 0xFF,
+	0xFE, 0xF7, 0xF4, 0xFA, 0x00, 0x2C, 0x0C, 0xD0,
+	0x1B, 0x49, 0x00, 0x20, 0x0E, 0x68, 0x42, 0x00,
+	0x51, 0x19, 0x0B, 0x88, 0xB2, 0x5A, 0x40, 0x1C,
+	0x9A, 0x18, 0xC0, 0xB2, 0x0A, 0x80, 0x30, 0x28,
+	0xF5, 0xD3, 0x64, 0x1C, 0xE4, 0xB2, 0x05, 0x2C,
+	0xE6, 0xD3, 0x00, 0x20, 0x41, 0x00, 0x49, 0x19,
+	0x00, 0x22, 0x8A, 0x5E, 0x40, 0x1C, 0x92, 0x10,
+	0xC0, 0xB2, 0x0A, 0x80, 0x30, 0x28, 0xF5, 0xD3,
+	0x39, 0x46, 0x0E, 0xA0, 0x02, 0xF0, 0x02, 0xFD,
+	0x01, 0x21, 0x28, 0x46, 0x00, 0xF0, 0x3E, 0xFD,
+	0x01, 0x20, 0xFE, 0xF7, 0x3D, 0xFB, 0x0C, 0x4C,
+	0x20, 0x78, 0x38, 0x42, 0x08, 0xD1, 0x0B, 0x48,
+	0x60, 0x22, 0x29, 0x46, 0x00, 0x68, 0xFE, 0xF7,
+	0x5B, 0xF9, 0x20, 0x78, 0x38, 0x43, 0x20, 0x70,
+	0x01, 0x20, 0xF8, 0xBD, 0x00, 0x00, 0x01, 0x20,
+	0xF8, 0x02, 0x00, 0x20, 0x42, 0x43, 0x5F, 0x4F,
+	0x4B, 0x5F, 0x25, 0x64, 0x0D, 0x0A, 0x00, 0x00,
+	0x98, 0x01, 0x00, 0x20, 0x6C, 0x02, 0x00, 0x20,
+	0xF0, 0xB5, 0x21, 0x48, 0x00, 0x22, 0x00, 0x68,
+	0x20, 0x4C, 0xA0, 0x42, 0x28, 0xD8, 0x20, 0x48,
+	0x00, 0x78, 0x04, 0x28, 0x27, 0xD1, 0x1F, 0x4D,
+	0x1F, 0x4C, 0x2D, 0x68, 0x00, 0x20, 0x2E, 0x18,
+	0xF6, 0x7E, 0x41, 0x2E, 0x08, 0xD0, 0x43, 0x00,
+	0x1F, 0x19, 0x00, 0x26, 0xBE, 0x5F, 0xCB, 0x5E,
+	0xF3, 0x1A, 0x00, 0xD5, 0x5B, 0x42, 0x1B, 0xB2,
+	0x9A, 0x42, 0x00, 0xDA, 0x1A, 0x46, 0x40, 0x1C,
+	0xC0, 0xB2, 0x30, 0x28, 0xEB, 0xD3, 0x60, 0x20,
+	0x40, 0x5D, 0x90, 0x35, 0x40, 0x1C, 0x50, 0x43,
+	0x6A, 0x7B, 0x2B, 0x7B, 0x00, 0x11, 0x12, 0x02,
+	0x00, 0xB2, 0x1A, 0x43, 0x82, 0x42, 0x03, 0xDA,
+	0x0E, 0x49, 0x00, 0x20, 0x08, 0x70, 0xF0, 0xBD,
+	0x00, 0x20, 0x42, 0x00, 0x8B, 0x5E, 0x00, 0x26,
+	0xDD, 0x00, 0xEB, 0x1A, 0x15, 0x19, 0xAE, 0x5F,
+	0x40, 0x1C, 0x9B, 0x19, 0xDB, 0x10, 0xC0, 0xB2,
+	0x8B, 0x52, 0x30, 0x28, 0xF1, 0xD3, 0xF0, 0xBD,
+	0x08, 0x03, 0x00, 0x20, 0x40, 0x77, 0x1B, 0x00,
+	0x87, 0x02, 0x00, 0x20, 0xB8, 0x02, 0x00, 0x20,
+	0x00, 0x00, 0x01, 0x20, 0x98, 0x01, 0x00, 0x20,
+	0x70, 0xB5, 0x21, 0x48, 0x21, 0x49, 0x00, 0x78,
+	0x00, 0x28, 0x06, 0xD0, 0x01, 0x28, 0x08, 0xD0,
+	0x02, 0x28, 0x16, 0xD0, 0x05, 0x28, 0x2F, 0xD1,
+	0x34, 0xE0, 0x01, 0x25, 0x2C, 0x46, 0x1C, 0x48,
+	0x02, 0xE0, 0x02, 0x25, 0x1B, 0x48, 0x2C, 0x46,
+	0x08, 0x60, 0x00, 0x22, 0x1A, 0x49, 0x28, 0x46,
+	0xFF, 0xF7, 0xD4, 0xFA, 0x00, 0x28, 0x18, 0xD0,
+	0x18, 0x48, 0x00, 0x78, 0x00, 0x28, 0x04, 0xD0,
+	0x13, 0xE0, 0x04, 0x25, 0x2C, 0x46, 0x16, 0x48,
+	0xEE, 0xE7, 0x16, 0x48, 0x41, 0x6B, 0x03, 0x22,
+	0x12, 0x05, 0x11, 0x43, 0x41, 0x63, 0x14, 0x48,
+	0x00, 0xF0, 0x30, 0xFA, 0x00, 0x28, 0x0B, 0xD1,
+	0x20, 0x46, 0xFF, 0xF7, 0x27, 0xFF, 0x00, 0x28,
+	0x06, 0xD1, 0x01, 0x22, 0x21, 0x46, 0x28, 0x46,
+	0x00, 0xF0, 0x1E, 0xF8, 0x00, 0x28, 0x04, 0xD0,
+	0x0C, 0x48, 0x00, 0x78, 0xC0, 0x07, 0x01, 0xD0,
+	0x00, 0x20, 0x70, 0xBD, 0x01, 0x20, 0x70, 0xBD,
+	0x85, 0x02, 0x00, 0x20, 0x6C, 0x02, 0x00, 0x20,
+	0x9C, 0x03, 0x00, 0x20, 0xEC, 0x06, 0x00, 0x20,
+	0x00, 0x20, 0x00, 0x50, 0xE7, 0x02, 0x00, 0x20,
+	0xFC, 0x03, 0x00, 0x20, 0x80, 0x10, 0x00, 0x50,
+	0x00, 0x00, 0x01, 0x20, 0xCE, 0x00, 0x00, 0x20,
+	0xF7, 0xB5, 0x07, 0x46, 0xFE, 0xF7, 0xD0, 0xFB,
+	0x14, 0x48, 0x00, 0x25, 0x05, 0x70, 0x78, 0x07,
+	0x1D, 0xD0, 0x01, 0x21, 0x02, 0x20, 0x00, 0xF0,
+	0x33, 0xFB, 0x11, 0x4C, 0x60, 0x6B, 0x03, 0x26,
+	0x36, 0x05, 0x01, 0x21, 0xB0, 0x43, 0x09, 0x05,
+	0x40, 0x18, 0x60, 0x63, 0x00, 0x20, 0xFF, 0xF7,
+	0xFB, 0xFC, 0x00, 0x28, 0x0D, 0xD0, 0x01, 0x98,
+	0xFF, 0xF7, 0xE4, 0xFE, 0x01, 0x22, 0x09, 0x49,
+	0x38, 0x46, 0xFF, 0xF7, 0x73, 0xFA, 0x08, 0x48,
+	0x05, 0x70, 0x08, 0x48, 0x05, 0x70, 0x01, 0x20,
+	0xFE, 0xBD, 0x60, 0x6B, 0x30, 0x43, 0x60, 0x63,
+	0x00, 0x20, 0xFE, 0xBD, 0x82, 0x02, 0x00, 0x20,
+	0x80, 0x10, 0x00, 0x50, 0x00, 0x20, 0x00, 0x50,
+	0xE7, 0x02, 0x00, 0x20, 0xE8, 0x02, 0x00, 0x20,
+	0xF0, 0xB5, 0xB4, 0x48, 0x01, 0x22, 0x23, 0x23,
+	0x13, 0x24, 0x92, 0x02, 0x5B, 0x01, 0xA4, 0x01,
+	0x05, 0x46, 0x1C, 0xC5, 0x29, 0x21, 0x49, 0x01,
+	0xC1, 0x60, 0xAF, 0x48, 0x07, 0x68, 0x38, 0x46,
+	0x40, 0x30, 0x45, 0x7C, 0x06, 0x7C, 0x2D, 0x04,
+	0x36, 0x02, 0x35, 0x43, 0x0C, 0x35, 0x15, 0x60,
+	0x45, 0x7C, 0x06, 0x7C, 0x2D, 0x04, 0x36, 0x02,
+	0x35, 0x43, 0x0C, 0x35, 0x55, 0x60, 0x01, 0x25,
+	0x95, 0x60, 0xD5, 0x60, 0x05, 0x7D, 0xBC, 0x46,
+	0x6E, 0x1E, 0x6D, 0x08, 0x6D, 0x1E, 0xF6, 0x05,
+	0xED, 0x05, 0xF6, 0x09, 0xED, 0x0D, 0x2E, 0x43,
+	0x16, 0x61, 0xA0, 0x4D, 0x55, 0x61, 0x03, 0x25,
+	0x95, 0x61, 0x03, 0x26, 0x36, 0x02, 0x00, 0x25,
+	0x16, 0x62, 0xD5, 0x61, 0x06, 0x7E, 0xD6, 0x62,
+	0x06, 0x7E, 0x87, 0x7E, 0xF6, 0x19, 0x16, 0x63,
+	0x87, 0x7E, 0x06, 0x7E, 0x7F, 0x00, 0xF6, 0x19,
+	0x56, 0x63, 0x95, 0x63, 0x06, 0x7E, 0xBC, 0x36,
+	0xD6, 0x63, 0x06, 0x7E, 0x87, 0x7E, 0xF6, 0x19,
+	0xBC, 0x36, 0x16, 0x64, 0x07, 0x7E, 0x86, 0x7E,
+	0x95, 0x64, 0x76, 0x00, 0xBC, 0x36, 0xBE, 0x19,
+	0x56, 0x64, 0x67, 0x46, 0x55, 0x62, 0x8E, 0x4E,
+	0x50, 0x37, 0x96, 0x62, 0xBC, 0x46, 0xFE, 0x7B,
+	0xBF, 0x7B, 0x36, 0x02, 0x3E, 0x43, 0xF6, 0x1C,
+	0xB7, 0x05, 0x8A, 0x4E, 0xBF, 0x0D, 0xBE, 0x19,
+	0xD6, 0x64, 0x89, 0x4E, 0x16, 0x65, 0x46, 0x7C,
+	0x0C, 0x3E, 0xF7, 0xB2, 0x05, 0x26, 0x76, 0x02,
+	0xBE, 0x19, 0x56, 0x65, 0x06, 0x7D, 0x36, 0x02,
+	0x21, 0x36, 0x96, 0x65, 0xD5, 0x65, 0x42, 0x7C,
+	0x06, 0x7C, 0x12, 0x04, 0x36, 0x02, 0x32, 0x43,
+	0x0C, 0x32, 0x1A, 0x60, 0x42, 0x7C, 0x06, 0x7C,
+	0x12, 0x04, 0x36, 0x02, 0x32, 0x43, 0x0C, 0x32,
+	0x5A, 0x60, 0x01, 0x22, 0x9A, 0x60, 0xDA, 0x60,
+	0x42, 0x7D, 0x56, 0x1E, 0x52, 0x08, 0x52, 0x1E,
+	0xF6, 0x05, 0xD2, 0x05, 0xF7, 0x09, 0xD2, 0x0D,
+	0x17, 0x43, 0x72, 0x4A, 0x1F, 0x61, 0x5A, 0x61,
+	0x03, 0x22, 0xDD, 0x61, 0x9A, 0x61, 0x12, 0x02,
+	0x1A, 0x62, 0x02, 0x7E, 0xDA, 0x62, 0x02, 0x7E,
+	0xC6, 0x7E, 0x92, 0x19, 0x1A, 0x63, 0xC6, 0x7E,
+	0x02, 0x7E, 0x76, 0x00, 0x92, 0x19, 0x9D, 0x63,
+	0x5A, 0x63, 0x02, 0x7E, 0x67, 0x46, 0xBC, 0x32,
+	0xDA, 0x63, 0x02, 0x7E, 0xC6, 0x7E, 0x92, 0x19,
+	0xBC, 0x32, 0x1A, 0x64, 0xC6, 0x7E, 0x02, 0x7E,
+	0x76, 0x00, 0xBC, 0x36, 0x9D, 0x64, 0x92, 0x19,
+	0x5D, 0x62, 0x5A, 0x64, 0x60, 0x4A, 0x9A, 0x62,
+	0xFA, 0x7B, 0xBE, 0x7B, 0x12, 0x02, 0x32, 0x43,
+	0xD2, 0x1C, 0x96, 0x05, 0x5D, 0x4A, 0xB6, 0x0D,
+	0xB2, 0x18, 0xDA, 0x64, 0x5C, 0x4A, 0x1A, 0x65,
+	0x42, 0x7C, 0x0C, 0x3A, 0xD6, 0xB2, 0x05, 0x22,
+	0x52, 0x02, 0xB2, 0x18, 0x5A, 0x65, 0x42, 0x7D,
+	0xDD, 0x65, 0x12, 0x02, 0x21, 0x32, 0x9A, 0x65,
+	0xC2, 0x7C, 0x83, 0x7C, 0x12, 0x04, 0x1B, 0x02,
+	0x1A, 0x43, 0x0C, 0x32, 0x23, 0x46, 0x1A, 0x60,
+	0xC2, 0x7C, 0x83, 0x7C, 0x12, 0x04, 0x1B, 0x02,
+	0x1A, 0x43, 0x0C, 0x32, 0x23, 0x46, 0x5A, 0x60,
+	0x82, 0x7D, 0x52, 0x1E, 0xD3, 0x05, 0xDB, 0x0D,
+	0x22, 0x46, 0x93, 0x60, 0x82, 0x7D, 0x52, 0x08,
+	0x52, 0x1E, 0xD2, 0x05, 0x23, 0x46, 0xD2, 0x0D,
+	0x1D, 0x61, 0xDA, 0x60, 0x47, 0x4A, 0x5A, 0x61,
+	0x26, 0x46, 0x01, 0x23, 0xF5, 0x61, 0xB3, 0x61,
+	0x13, 0x12, 0x33, 0x62, 0x43, 0x7E, 0xF3, 0x62,
+	0x43, 0x7E, 0x06, 0x7F, 0x9B, 0x19, 0x26, 0x46,
+	0x33, 0x63, 0x06, 0x7F, 0x43, 0x7E, 0x76, 0x00,
+	0x9B, 0x19, 0x26, 0x46, 0xB5, 0x63, 0xF5, 0x63,
+	0x35, 0x64, 0x73, 0x63, 0x65, 0x64, 0x38, 0x4B,
+	0xA5, 0x64, 0x3F, 0x33, 0x65, 0x62, 0xA3, 0x62,
+	0x36, 0x4B, 0xDB, 0x1C, 0xE3, 0x64, 0x36, 0x4B,
+	0x23, 0x65, 0xC6, 0x7C, 0x0C, 0x3E, 0xF7, 0xB2,
+	0x05, 0x26, 0x76, 0x02, 0xBF, 0x19, 0x67, 0x65,
+	0x87, 0x7D, 0x7F, 0x08, 0x3F, 0x02, 0x21, 0x37,
+	0xA7, 0x65, 0xE5, 0x65, 0xC4, 0x7C, 0x80, 0x7C,
+	0x24, 0x04, 0x00, 0x02, 0x04, 0x43, 0x0C, 0x34,
+	0x27, 0x48, 0x0C, 0x60, 0x00, 0x68, 0x40, 0x30,
+	0xC4, 0x7C, 0x87, 0x7C, 0x24, 0x04, 0x3F, 0x02,
+	0x3C, 0x43, 0x0C, 0x34, 0x4C, 0x60, 0xC4, 0x7D,
+	0x64, 0x1E, 0xE4, 0x05, 0xE4, 0x0D, 0x8C, 0x60,
+	0xC4, 0x7D, 0x0D, 0x61, 0x64, 0x08, 0x64, 0x1E,
+	0xE4, 0x05, 0xE4, 0x0D, 0xCC, 0x60, 0x4A, 0x61,
+	0x01, 0x22, 0xCD, 0x61, 0x8A, 0x61, 0x12, 0x02,
+	0x0A, 0x62, 0x42, 0x7E, 0xCA, 0x62, 0x42, 0x7E,
+	0x44, 0x7F, 0x12, 0x19, 0x0A, 0x63, 0x44, 0x7F,
+	0x42, 0x7E, 0x64, 0x00, 0x12, 0x19, 0x8D, 0x63,
+	0x4A, 0x63, 0x42, 0x7E, 0xBC, 0x32, 0xCA, 0x63,
+	0x42, 0x7E, 0x44, 0x7F, 0x12, 0x19, 0xBC, 0x32,
+	0x0A, 0x64, 0x42, 0x7F, 0x44, 0x7E, 0x52, 0x00,
+	0xBC, 0x32, 0xA2, 0x18, 0x8D, 0x64, 0x4A, 0x64,
+	0x01, 0x22, 0x92, 0x04, 0x4A, 0x62, 0x0C, 0x4A,
+	0x3F, 0x32, 0x8A, 0x62, 0x0B, 0x4A, 0x0B, 0x65,
+	0xD2, 0x1C, 0xCA, 0x64, 0xC2, 0x7C, 0x0C, 0x3A,
+	0xD2, 0xB2, 0x92, 0x19, 0x4A, 0x65, 0xC0, 0x7D,
+	0xCD, 0x65, 0x40, 0x08, 0x00, 0x02, 0x21, 0x30,
+	0x88, 0x65, 0xF0, 0xBD, 0x70, 0x02, 0x00, 0x20,
+	0xB8, 0x02, 0x00, 0x20, 0x03, 0x00, 0x03, 0x00,
+	0x00, 0x3F, 0x3F, 0x3F, 0x00, 0x30, 0xBC, 0x00,
+	0x00, 0x26, 0x31, 0x00, 0x01, 0x00, 0x01, 0x00,
+	0x10, 0xB5, 0x00, 0xF0, 0x73, 0xFA, 0x00, 0xF0,
+	0x75, 0xF9, 0x01, 0x20, 0x80, 0x07, 0x41, 0x68,
+	0x04, 0x22, 0x11, 0x43, 0x41, 0x60, 0x00, 0x20,
+	0xFF, 0xF7, 0xD4, 0xFC, 0x09, 0x48, 0x01, 0x78,
+	0x09, 0x48, 0x00, 0x29, 0x04, 0xD0, 0x01, 0x68,
+	0xFF, 0x22, 0x01, 0x32, 0x11, 0x43, 0x01, 0x60,
+	0x01, 0x68, 0x01, 0x22, 0x92, 0x02, 0x91, 0x43,
+	0x01, 0x60, 0x04, 0x49, 0x02, 0x20, 0x08, 0x72,
+	0x10, 0xBD, 0x00, 0x00, 0xCF, 0x00, 0x00, 0x20,
+	0x00, 0x10, 0x00, 0x50, 0x50, 0x02, 0x00, 0x20,
+	0x10, 0xB5, 0x01, 0x20, 0x80, 0x07, 0x41, 0x68,
+	0x04, 0x22, 0x11, 0x43, 0x41, 0x60, 0x09, 0x48,
+	0x07, 0x49, 0x41, 0x60, 0x08, 0x49, 0x81, 0x60,
+	0xFF, 0xF7, 0x52, 0xFE, 0x80, 0x21, 0x07, 0x48,
+	0xFD, 0xF7, 0xCF, 0xFE, 0x80, 0x21, 0x06, 0x48,
+	0xFD, 0xF7, 0xCB, 0xFE, 0x10, 0xBD, 0x00, 0x00,
+	0x1F, 0x1F, 0x5F, 0x1F, 0x00, 0x10, 0x00, 0x50,
+	0x1F, 0x1F, 0x1F, 0x1F, 0x4C, 0x07, 0x00, 0x20,
+	0x9C, 0x01, 0x00, 0x20, 0x01, 0x21, 0x89, 0x07,
+	0x0A, 0x15, 0x00, 0x28, 0x48, 0x69, 0x02, 0xD0,
+	0x10, 0x43, 0x48, 0x61, 0x70, 0x47, 0x90, 0x43,
+	0xFB, 0xE7, 0x00, 0x00, 0x10, 0xB5, 0x0F, 0x49,
+	0x0A, 0x78, 0x0F, 0x49, 0x00, 0x2A, 0x09, 0x78,
+	0x04, 0xD0, 0x01, 0x2A, 0x07, 0xD0, 0x02, 0x2A,
+	0x12, 0xD1, 0x09, 0xE0, 0xC9, 0x07, 0x0F, 0xD0,
+	0x60, 0x22, 0x0A, 0x49, 0x08, 0xE0, 0x89, 0x07,
+	0x0A, 0xD5, 0x60, 0x22, 0x08, 0x49, 0x03, 0xE0,
+	0x49, 0x07, 0x05, 0xD5, 0x60, 0x22, 0x07, 0x49,
+	0xFD, 0xF7, 0x7E, 0xFE, 0x01, 0x20, 0x10, 0xBD,
+	0x00, 0x20, 0x10, 0xBD, 0x85, 0x02, 0x00, 0x20,
+	0x98, 0x01, 0x00, 0x20, 0x9C, 0x03, 0x00, 0x20,
+	0xEC, 0x06, 0x00, 0x20, 0xFC, 0x03, 0x00, 0x20,
+	0x10, 0xB5, 0x13, 0x49, 0x13, 0x4B, 0x09, 0x68,
+	0x13, 0x4A, 0x40, 0x31, 0x00, 0x28, 0x0F, 0xD0,
+	0x01, 0x28, 0x12, 0xD0, 0x02, 0x28, 0x15, 0xD0,
+	0x03, 0x28, 0x08, 0xD1, 0x48, 0x7E, 0x84, 0x1E,
+	0x1C, 0x80, 0x49, 0x7F, 0x4B, 0x00, 0xC9, 0x18,
+	0x89, 0x1C, 0x40, 0x18, 0x10, 0x80, 0x10, 0xBD,
+	0x08, 0x7E, 0x84, 0x1E, 0x1C, 0x80, 0x89, 0x7E,
+	0xF4, 0xE7, 0x08, 0x7E, 0x84, 0x1E, 0x1C, 0x80,
+	0xC9, 0x7E, 0xEF, 0xE7, 0x48, 0x7E, 0x84, 0x1E,
+	0x1C, 0x80, 0x09, 0x7F, 0xEA, 0xE7, 0x00, 0x00,
+	0xB8, 0x02, 0x00, 0x20, 0x60, 0x02, 0x00, 0x20,
+	0x62, 0x02, 0x00, 0x20, 0xF8, 0xB5, 0x05, 0x46,
+	0x23, 0x48, 0x00, 0x21, 0x01, 0x60, 0x41, 0x60,
+	0x22, 0x49, 0x01, 0x20, 0x08, 0x70, 0x22, 0x48,
+	0x22, 0x4C, 0x00, 0x78, 0x00, 0x28, 0x06, 0xD0,
+	0x2D, 0x26, 0x09, 0x27, 0x01, 0x28, 0x10, 0xD0,
+	0x02, 0x28, 0x1D, 0xD1, 0x21, 0xE0, 0x1E, 0xA0,
+	0x02, 0xF0, 0xC0, 0xF9, 0x5A, 0x20, 0x00, 0x2D,
+	0x20, 0x70, 0x01, 0xD0, 0x00, 0x20, 0x00, 0xE0,
+	0x01, 0x20, 0x00, 0xF0, 0x23, 0xF9, 0x5A, 0x20,
+	0x0C, 0xE0, 0x1A, 0xA0, 0x02, 0xF0, 0xB2, 0xF9,
+	0x00, 0x2D, 0x02, 0xD0, 0x26, 0x70, 0x02, 0x20,
+	0x01, 0xE0, 0x27, 0x70, 0x03, 0x20, 0x00, 0xF0,
+	0x15, 0xF9, 0x01, 0x20, 0xFF, 0xF7, 0x90, 0xF9,
+	0x05, 0x20, 0x14, 0x49, 0x00, 0x02, 0x08, 0x60,
+	0xF8, 0xBD, 0x13, 0xA0, 0x02, 0xF0, 0x9E, 0xF9,
+	0x00, 0x2D, 0x02, 0xD0, 0x26, 0x70, 0x02, 0x20,
+	0x01, 0xE0, 0x27, 0x70, 0x03, 0x20, 0x00, 0xF0,
+	0x01, 0xF9, 0x10, 0x48, 0x81, 0x6A, 0x10, 0x4A,
+	0x11, 0x40, 0x81, 0x62, 0xE5, 0xE7, 0x00, 0x00,
+	0x50, 0x02, 0x00, 0x20, 0xCF, 0x00, 0x00, 0x20,
+	0x85, 0x02, 0x00, 0x20, 0xB5, 0x02, 0x00, 0x20,
+	0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x0D, 0x0A,
+	0x00, 0x00, 0x00, 0x00, 0x49, 0x64, 0x6C, 0x65,
+	0x0D, 0x0A, 0x00, 0x00, 0x00, 0x10, 0x00, 0x50,
+	0x47, 0x65, 0x73, 0x74, 0x75, 0x72, 0x65, 0x0D,
+	0x0A, 0x00, 0x00, 0x00, 0xC0, 0x11, 0x00, 0x50,
+	0xFF, 0xFF, 0x00, 0xF8, 0x70, 0xB5, 0x1C, 0x4D,
+	0x1C, 0x4C, 0x28, 0x70, 0x02, 0x46, 0x21, 0x78,
+	0x1B, 0xA0, 0x02, 0xF0, 0x67, 0xF9, 0x1E, 0x49,
+	0x01, 0x20, 0x08, 0x70, 0x00, 0xF0, 0x7E, 0xF9,
+	0x00, 0xF0, 0x50, 0xF9, 0x00, 0xF0, 0x52, 0xF8,
+	0x01, 0x20, 0xFD, 0xF7, 0x8D, 0xFE, 0xFF, 0xF7,
+	0x09, 0xFC, 0x01, 0x20, 0xFF, 0xF7, 0x7E, 0xFF,
+	0x00, 0xF0, 0xC6, 0xFF, 0xFE, 0xF7, 0xF6, 0xFF,
+	0x28, 0x78, 0x20, 0x70, 0x05, 0x28, 0x12, 0xD0,
+	0xFF, 0xF7, 0xBE, 0xFC, 0x00, 0x28, 0x0F, 0xD0,
+	0x10, 0x48, 0x00, 0x7A, 0x00, 0x28, 0x03, 0xD1,
+	0x00, 0x21, 0x02, 0x20, 0x00, 0xF0, 0x4C, 0xF8,
+	0x00, 0x20, 0x00, 0xF0, 0x5B, 0xF9, 0x0C, 0x48,
+	0x00, 0x78, 0xC0, 0x07, 0x01, 0xD0, 0x00, 0x20,
+	0x70, 0xBD, 0x01, 0x20, 0x70, 0xBD, 0x00, 0x00,
+	0x85, 0x02, 0x00, 0x20, 0x71, 0x04, 0x00, 0x20,
+	0x53, 0x4D, 0x3D, 0x5B, 0x25, 0x64, 0x3A, 0x25,
+	0x64, 0x5D, 0x0D, 0x0A, 0x00, 0x00, 0x00, 0x00,
+	0x83, 0x02, 0x00, 0x20, 0x50, 0x02, 0x00, 0x20,
+	0xCE, 0x00, 0x00, 0x20, 0x08, 0x49, 0x02, 0x20,
+	0x08, 0x72, 0x08, 0x48, 0x01, 0x78, 0x08, 0x48,
+	0x00, 0x29, 0x01, 0x68, 0x04, 0xD0, 0x01, 0x22,
+	0x92, 0x02, 0x91, 0x43, 0x01, 0x60, 0x70, 0x47,
+	0x01, 0x22, 0x11, 0x43, 0xFA, 0xE7, 0x00, 0x00,
+	0x50, 0x02, 0x00, 0x20, 0xCF, 0x00, 0x00, 0x20,
+	0x00, 0x10, 0x00, 0x50, 0x10, 0xB5, 0x08, 0x48,
+	0x01, 0x68, 0x01, 0x22, 0x92, 0x02, 0x11, 0x43,
+	0x01, 0x60, 0x00, 0x68, 0x05, 0x4C, 0xC0, 0x07,
+	0x03, 0xD0, 0x02, 0x20, 0x20, 0x72, 0x00, 0xF0,
+	0xED, 0xF8, 0x00, 0x20, 0x20, 0x72, 0x10, 0xBD,
+	0x00, 0x10, 0x00, 0x50, 0x50, 0x02, 0x00, 0x20,
+	0x70, 0xB5, 0x05, 0x00, 0x0E, 0x46, 0x16, 0xD0,
+	0xFF, 0xF7, 0x6A, 0xFE, 0x00, 0x24, 0x6D, 0x1E,
+	0x07, 0xE0, 0x00, 0xF0, 0xDB, 0xF8, 0xFF, 0xF7,
+	0x67, 0xFB, 0xFF, 0xF7, 0xC3, 0xFF, 0x64, 0x1C,
+	0xE4, 0xB2, 0xAC, 0x42, 0xF5, 0xDB, 0x00, 0xF0,
+	0xD1, 0xF8, 0xFF, 0xF7, 0x5D, 0xFB, 0x00, 0x2E,
+	0x02, 0xD0, 0xFF, 0xF7, 0xCF, 0xFF, 0x70, 0xBD,
+	0xFF, 0xF7, 0xB4, 0xFF, 0x70, 0xBD, 0x00, 0x00,
+	0x30, 0xB5, 0x01, 0x24, 0x1C, 0x4A, 0xA4, 0x07,
+	0x23, 0x13, 0x1B, 0x49, 0x40, 0x32, 0x00, 0x28,
+	0x21, 0xD0, 0x01, 0x28, 0x1F, 0xD0, 0x02, 0x28,
+	0x01, 0xD0, 0x03, 0x28, 0x1A, 0xD1, 0x25, 0x68,
+	0x1D, 0x43, 0x25, 0x60, 0x62, 0x23, 0x93, 0x61,
+	0xD3, 0x61, 0x14, 0x4B, 0xCB, 0x63, 0x14, 0x49,
+	0x09, 0x68, 0x40, 0x31, 0x02, 0x28, 0x48, 0x7E,
+	0x17, 0xD0, 0x40, 0x1E, 0x40, 0x05, 0x40, 0x0D,
+	0x10, 0x60, 0x4B, 0x7E, 0x48, 0x7F, 0x41, 0x00,
+	0x40, 0x18, 0x80, 0x1C, 0x18, 0x18, 0x40, 0x05,
+	0x40, 0x0D, 0x90, 0x60, 0x30, 0xBD, 0x20, 0x68,
+	0x98, 0x43, 0x20, 0x60, 0x01, 0x20, 0x90, 0x61,
+	0xD0, 0x61, 0x00, 0x20, 0xC8, 0x63, 0x10, 0x60,
+	0xF3, 0xE7, 0x40, 0x1E, 0x40, 0x05, 0x40, 0x0D,
+	0x10, 0x60, 0x4B, 0x7E, 0x08, 0x7F, 0xE6, 0xE7,
+	0x80, 0x11, 0x00, 0x50, 0x01, 0x00, 0x01, 0x00,
+	0xB8, 0x02, 0x00, 0x20, 0x70, 0xB5, 0x04, 0x46,
+	0x81, 0x00, 0x25, 0x48, 0x41, 0x58, 0x25, 0x48,
+	0x0A, 0x68, 0x42, 0x60, 0x4A, 0x68, 0x82, 0x60,
+	0x8A, 0x68, 0x02, 0x61, 0xCA, 0x68, 0x42, 0x61,
+	0x21, 0x4D, 0x08, 0x69, 0x28, 0x62, 0x1F, 0x4A,
+	0x48, 0x69, 0xC0, 0x32, 0x90, 0x61, 0x88, 0x69,
+	0xD0, 0x61, 0x48, 0x6A, 0x10, 0x62, 0x88, 0x6A,
+	0x50, 0x62, 0x1A, 0x48, 0xCB, 0x69, 0x40, 0x38,
+	0xC3, 0x60, 0x0B, 0x6A, 0x03, 0x61, 0xCB, 0x6A,
+	0x93, 0x62, 0x0B, 0x6B, 0xD3, 0x63, 0x16, 0x4B,
+	0x4E, 0x6B, 0x80, 0x3B, 0x1E, 0x61, 0x8E, 0x6B,
+	0x5E, 0x62, 0xCE, 0x6B, 0xD6, 0x62, 0x0A, 0x6C,
+	0x1A, 0x60, 0x4A, 0x6C, 0x5A, 0x61, 0x8A, 0x6C,
+	0x9A, 0x62, 0xCA, 0x6C, 0xAA, 0x62, 0x0D, 0x4B,
+	0x0A, 0x6D, 0x40, 0x33, 0x5A, 0x63, 0x0B, 0x4B,
+	0x4A, 0x6D, 0x80, 0x33, 0x9A, 0x60, 0x0B, 0x4A,
+	0x8B, 0x6D, 0x13, 0x60, 0xC9, 0x6D, 0x51, 0x60,
+	0xC2, 0x68, 0x09, 0x49, 0x0A, 0x60, 0x00, 0x69,
+	0x48, 0x60, 0x20, 0x46, 0xFF, 0xF7, 0x44, 0xFE,
+	0x20, 0x46, 0xFF, 0xF7, 0x71, 0xFF, 0x70, 0xBD,
+	0x70, 0x02, 0x00, 0x20, 0x40, 0x10, 0x00, 0x50,
+	0xC0, 0x11, 0x00, 0x50, 0x00, 0x19, 0x00, 0x50,
+	0x50, 0x02, 0x00, 0x20, 0x10, 0xB5, 0x0E, 0x4C,
+	0x21, 0x78, 0x81, 0x42, 0x17, 0xD0, 0x20, 0x70,
+	0x01, 0x46, 0x0C, 0xA0, 0x02, 0xF0, 0x32, 0xF8,
+	0xFF, 0xF7, 0x24, 0xFF, 0x20, 0x78, 0xFF, 0xF7,
+	0x55, 0xFE, 0xFF, 0xF7, 0xA9, 0xFA, 0x0B, 0x48,
+	0x80, 0x79, 0x01, 0x28, 0x05, 0xD1, 0x0A, 0x48,
+	0x01, 0x68, 0x01, 0x22, 0xD2, 0x02, 0x11, 0x43,
+	0x01, 0x60, 0xFF, 0xF7, 0xFB, 0xFE, 0x10, 0xBD,
+	0x74, 0x04, 0x00, 0x20, 0x54, 0x50, 0x20, 0x53,
+	0x70, 0x65, 0x65, 0x64, 0x20, 0x25, 0x64, 0x0D,
+	0x0A, 0x00, 0x00, 0x00, 0x84, 0x04, 0x00, 0x20,
+	0x00, 0x10, 0x00, 0x50, 0x70, 0xB5, 0x05, 0x24,
+	0x64, 0x04, 0xFD, 0xF7, 0xA9, 0xFF, 0x00, 0x22,
+	0x0C, 0x49, 0x0D, 0x48, 0x0D, 0x4B, 0x05, 0xE0,
+	0x05, 0x78, 0xED, 0x07, 0x01, 0xD0, 0x0A, 0x72,
+	0x09, 0xE0, 0x64, 0x1E, 0x0D, 0x7A, 0x02, 0x2D,
+	0x02, 0xD0, 0x1D, 0x68, 0xED, 0x07, 0x02, 0xD0,
+	0x00, 0x2C, 0xF1, 0xD1, 0x01, 0xE0, 0x00, 0x2C,
+	0x03, 0xD1, 0x0A, 0x72, 0x04, 0xA0, 0x01, 0xF0,
+	0xF1, 0xFF, 0x70, 0xBD, 0x50, 0x02, 0x00, 0x20,
+	0xCE, 0x00, 0x00, 0x20, 0x00, 0x10, 0x00, 0x50,
+	0x57, 0x53, 0x46, 0x20, 0x54, 0x4F, 0x0D, 0x0A,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0xB5, 0x03, 0x46,
+	0x00, 0x20, 0x00, 0xF0, 0xF1, 0xF8, 0x05, 0x21,
+	0x09, 0x07, 0xC8, 0x69, 0x00, 0x2B, 0x03, 0xD0,
+	0xC0, 0x0B, 0xC0, 0x03, 0x05, 0x4A, 0x03, 0xE0,
+	0xC0, 0x0B, 0x04, 0x4A, 0xC0, 0x03, 0xFC, 0x3A,
+	0x80, 0x18, 0xC8, 0x61, 0x01, 0x20, 0x00, 0xF0,
+	0xDF, 0xF8, 0x00, 0xBD, 0x50, 0x71, 0x00, 0x00,
+	0x03, 0x49, 0x0A, 0x68, 0x10, 0x18, 0x0A, 0x68,
+	0x90, 0x42, 0xFC, 0xD1, 0x70, 0x47, 0x00, 0x00,
+	0x08, 0x03, 0x00, 0x20, 0xF8, 0xB5, 0x1B, 0x4E,
+	0x05, 0x46, 0x0C, 0x46, 0x80, 0x21, 0x30, 0x46,
+	0xFD, 0xF7, 0x3B, 0xFC, 0x00, 0x2C, 0x04, 0xD0,
+	0x31, 0x46, 0x28, 0x46, 0x00, 0xF0, 0x5C, 0xFB,
+	0x04, 0xE0, 0x80, 0x22, 0x29, 0x46, 0x30, 0x46,
+	0xFD, 0xF7, 0x16, 0xFC, 0x29, 0x46, 0x12, 0xA0,
+	0x01, 0xF0, 0xA8, 0xFF, 0x00, 0x25, 0x14, 0x4F,
+	0x13, 0xE0, 0x00, 0x24, 0x08, 0xE0, 0x68, 0x43,
+	0x00, 0x19, 0x40, 0x00, 0x31, 0x5E, 0x11, 0xA0,
+	0x01, 0xF0, 0x9C, 0xFF, 0x64, 0x1C, 0xE4, 0xB2,
+	0x38, 0x68, 0x00, 0x7E, 0xA0, 0x42, 0xF2, 0xD8,
+	0x0E, 0xA0, 0x01, 0xF0, 0x93, 0xFF, 0x6D, 0x1C,
+	0xED, 0xB2, 0x38, 0x68, 0x40, 0x7E, 0xA8, 0x42,
+	0xE7, 0xD8, 0x0A, 0xA0, 0x01, 0xF0, 0x8A, 0xFF,
+	0xF8, 0xBD, 0x00, 0x00, 0x4C, 0x00, 0x00, 0x20,
+	0x49, 0x6D, 0x61, 0x67, 0x65, 0x3A, 0x30, 0x78,
+	0x25, 0x78, 0x0D, 0x0A, 0x00, 0x00, 0x00, 0x00,
+	0xB8, 0x02, 0x00, 0x20, 0x25, 0x36, 0x64, 0x2C,
+	0x00, 0x00, 0x00, 0x00, 0x0D, 0x0A, 0x00, 0x00,
+	0x08, 0x49, 0x8A, 0x78, 0x52, 0x1E, 0x8A, 0x70,
+	0x4B, 0x78, 0x0A, 0x1D, 0xD2, 0x5C, 0x02, 0x70,
+	0x48, 0x78, 0x40, 0x1C, 0x48, 0x70, 0x48, 0x78,
+	0x10, 0x28, 0x01, 0xD1, 0x00, 0x20, 0x48, 0x70,
+	0x70, 0x47, 0x00, 0x00, 0x5C, 0x04, 0x00, 0x20,
+	0xF8, 0xB5, 0x01, 0x27, 0x05, 0x46, 0xBF, 0x07,
+	0x38, 0x68, 0x08, 0x21, 0x08, 0x43, 0x38, 0x60,
+	0x01, 0x23, 0x15, 0x48, 0x80, 0x22, 0x02, 0x60,
+	0x14, 0x48, 0x00, 0x21, 0x81, 0x70, 0x01, 0x70,
+	0x41, 0x70, 0xC3, 0x70, 0x12, 0x4B, 0x98, 0x68,
+	0x02, 0x21, 0x88, 0x43, 0x98, 0x60, 0xF8, 0x68,
+	0x10, 0x43, 0xF8, 0x60, 0x0F, 0x4C, 0x61, 0x61,
+	0x0F, 0x48, 0x00, 0x68, 0xE9, 0x00, 0x46, 0x06,
+	0x0E, 0x48, 0xFD, 0xF7, 0x7F, 0xFB, 0xE0, 0x60,
+	0x30, 0x20, 0xA0, 0x60, 0x06, 0x49, 0x80, 0x20,
+	0x80, 0x39, 0x08, 0x60, 0x08, 0x20, 0x78, 0x60,
+	0xE0, 0x68, 0x68, 0x43, 0xC1, 0x00, 0x08, 0xA0,
+	0x01, 0xF0, 0x30, 0xFF, 0xF8, 0xBD, 0x00, 0x00,
+	0x80, 0xE1, 0x00, 0xE0, 0x5C, 0x04, 0x00, 0x20,
+	0x40, 0x09, 0x00, 0x50, 0x00, 0x02, 0x00, 0x50,
+	0x00, 0x11, 0x00, 0x50, 0x00, 0x36, 0x6E, 0x01,
+	0x55, 0x41, 0x52, 0x54, 0x28, 0x25, 0x64, 0x29,
+	0x21, 0x0D, 0x0A, 0x00, 0x70, 0xB5, 0x14, 0x4A,
+	0x91, 0x78, 0x14, 0x4C, 0x0E, 0x29, 0x02, 0xD3,
+	0x61, 0x68, 0x49, 0x07, 0xFC, 0xD5, 0x61, 0x69,
+	0x02, 0x25, 0xA9, 0x43, 0x61, 0x61, 0x91, 0x78,
+	0x00, 0x26, 0x10, 0x29, 0x0D, 0xD2, 0x0C, 0x49,
+	0x13, 0x78, 0x09, 0x1D, 0xC8, 0x54, 0x10, 0x78,
+	0x40, 0x1C, 0x10, 0x70, 0x10, 0x78, 0x10, 0x28,
+	0x00, 0xD1, 0x16, 0x70, 0x90, 0x78, 0x40, 0x1C,
+	0x90, 0x70, 0xD0, 0x78, 0x00, 0x28, 0x03, 0xD0,
+	0xD6, 0x70, 0x20, 0x46, 0xFF, 0xF7, 0x80, 0xFF,
+	0x60, 0x69, 0x28, 0x43, 0x60, 0x61, 0x70, 0xBD,
+	0x5C, 0x04, 0x00, 0x20, 0x00, 0x02, 0x00, 0x50,
+	0x01, 0x21, 0x89, 0x07, 0x00, 0xB5, 0x8A, 0x14,
+	0x00, 0x28, 0x08, 0x68, 0x04, 0xD0, 0x10, 0x43,
+	0x08, 0x60, 0xFD, 0xF7, 0x81, 0xFE, 0x00, 0xBD,
+	0x90, 0x43, 0x08, 0x60, 0x00, 0xBD, 0x00, 0x00,
+	0xFE, 0xB5, 0x63, 0x49, 0x61, 0x48, 0x09, 0x68,
+	0x00, 0x78, 0x90, 0x31, 0x4A, 0x7B, 0x0B, 0x7B,
+	0x11, 0x02, 0x19, 0x43, 0x5F, 0x4B, 0x00, 0x24,
+	0x04, 0x25, 0x0F, 0x26, 0x5A, 0x22, 0x1C, 0x5F,
+	0x00, 0x28, 0x01, 0xD0, 0x01, 0x28, 0x15, 0xD1,
+	0x58, 0x4B, 0x02, 0x27, 0x38, 0x43, 0x18, 0x70,
+	0x59, 0x48, 0x00, 0x78, 0x15, 0x28, 0x14, 0xD1,
+	0x58, 0x48, 0x00, 0x23, 0xC3, 0x5E, 0x99, 0x42,
+	0x0F, 0xDC, 0x13, 0x20, 0xC0, 0x43, 0x84, 0x42,
+	0x01, 0xDA, 0x55, 0x48, 0x02, 0x70, 0x17, 0x20,
+	0x54, 0x4B, 0x18, 0x70, 0x54, 0x48, 0x00, 0x27,
+	0xC7, 0x5F, 0xB9, 0x42, 0x03, 0xDA, 0x04, 0x20,
+	0x02, 0xE0, 0x00, 0x20, 0xF4, 0xE7, 0x02, 0x20,
+	0x00, 0x90, 0x50, 0x48, 0x40, 0x88, 0x00, 0x28,
+	0x67, 0xD0, 0x46, 0x48, 0x00, 0x78, 0x01, 0x90,
+	0x83, 0x07, 0x4D, 0x48, 0x01, 0x78, 0x48, 0x1C,
+	0xC0, 0xB2, 0x00, 0x2B, 0x06, 0xDA, 0x10, 0x2F,
+	0x04, 0xDA, 0x02, 0x21, 0x00, 0x91, 0x48, 0x49,
+	0x08, 0x70, 0x19, 0xE0, 0x47, 0x4B, 0x1B, 0x78,
+	0xDB, 0x07, 0x11, 0xD0, 0x44, 0x49, 0x00, 0x20,
+	0x08, 0x70, 0x13, 0x20, 0xC0, 0x43, 0x84, 0x42,
+	0x05, 0xDA, 0x40, 0x48, 0x80, 0x88, 0x05, 0x28,
+	0x01, 0xD2, 0x41, 0x48, 0x02, 0x80, 0x30, 0x21,
+	0x40, 0x48, 0xFD, 0xF7, 0x02, 0xFB, 0x03, 0xE0,
+	0x3B, 0x4A, 0x05, 0x29, 0x00, 0xD2, 0x10, 0x70,
+	0x39, 0x48, 0x01, 0x78, 0x00, 0x98, 0x81, 0x42,
+	0x37, 0xD3, 0x3B, 0x49, 0x10, 0x2F, 0x19, 0xDA,
+	0x1D, 0x20, 0xC0, 0x43, 0x84, 0x42, 0x15, 0xDD,
+	0x08, 0x78, 0xF0, 0x28, 0x01, 0xD2, 0x40, 0x1C,
+	0x08, 0x70, 0xC0, 0xB2, 0x02, 0x28, 0x0F, 0xD9,
+	0x2C, 0x49, 0x08, 0x78, 0x16, 0x28, 0x03, 0xD2,
+	0x02, 0x28, 0x01, 0xD9, 0x17, 0x20, 0x08, 0x70,
+	0x27, 0x49, 0x00, 0x20, 0x08, 0x70, 0x2C, 0x49,
+	0x08, 0x80, 0x01, 0xE0, 0x00, 0x20, 0x08, 0x70,
+	0x01, 0x98, 0x1E, 0x4A, 0x04, 0x28, 0x12, 0xD0,
+	0x22, 0x49, 0x08, 0x78, 0x18, 0x28, 0x32, 0xD2,
+	0x40, 0x1C, 0xC0, 0xB2, 0x08, 0x70, 0x10, 0x2F,
+	0x02, 0xDB, 0x62, 0x42, 0xBA, 0x42, 0x13, 0xDD,
+	0x1E, 0x4A, 0x12, 0x7A, 0x01, 0x2A, 0x05, 0xD9,
+	0x03, 0x26, 0x02, 0x25, 0x04, 0xE0, 0xFE, 0xF7,
+	0xB9, 0xFA, 0xFE, 0xBD, 0x01, 0x26, 0x35, 0x46,
+	0x09, 0x22, 0xD2, 0x43, 0x94, 0x42, 0x03, 0xDD,
+	0x02, 0x28, 0x01, 0xD9, 0x18, 0x20, 0x08, 0x70,
+	0xC1, 0xB2, 0x2B, 0x46, 0x32, 0x46, 0x19, 0xA0,
+	0x01, 0xF0, 0x2C, 0xFE, 0x1C, 0x4C, 0x1D, 0x4F,
+	0x00, 0x20, 0x42, 0x00, 0x11, 0x19, 0x00, 0x23,
+	0xCB, 0x5E, 0xBA, 0x5E, 0x73, 0x43, 0x9A, 0x18,
+	0x2A, 0x41, 0x40, 0x1C, 0xC0, 0xB2, 0x0A, 0x80,
+	0x30, 0x28, 0xF2, 0xD3, 0xFE, 0xBD, 0x04, 0x20,
+	0x10, 0x70, 0xFE, 0xBD, 0x87, 0x02, 0x00, 0x20,
+	0xB8, 0x02, 0x00, 0x20, 0x7A, 0x04, 0x00, 0x20,
+	0x70, 0x04, 0x00, 0x20, 0x78, 0x04, 0x00, 0x20,
+	0x75, 0x04, 0x00, 0x20, 0x8E, 0x02, 0x00, 0x20,
+	0x7C, 0x04, 0x00, 0x20, 0x84, 0x04, 0x00, 0x20,
+	0xED, 0x02, 0x00, 0x20, 0xEB, 0x02, 0x00, 0x20,
+	0xF0, 0x02, 0x00, 0x20, 0x0C, 0x03, 0x00, 0x20,
+	0xEA, 0x02, 0x00, 0x20, 0x46, 0x61, 0x73, 0x74,
+	0x4B, 0x25, 0x64, 0x28, 0x25, 0x64, 0x3A, 0x25,
+	0x64, 0x29, 0x0D, 0x0A, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0x20, 0xD4, 0x00, 0x00, 0x20,
+	0xF8, 0xB5, 0x3F, 0x4F, 0x01, 0x24, 0x38, 0x7B,
+	0x3E, 0x49, 0x0A, 0x78, 0x3E, 0x4E, 0x3F, 0x4D,
+	0x90, 0x42, 0x05, 0xD1, 0x28, 0x78, 0x81, 0x28,
+	0x02, 0xD0, 0x30, 0x78, 0xC0, 0x07, 0x66, 0xD0,
+	0x00, 0x20, 0x30, 0x70, 0x37, 0x48, 0x00, 0x78,
+	0x07, 0x28, 0x0C, 0xD3, 0x38, 0x48, 0x00, 0x68,
+	0x40, 0x05, 0x40, 0x0F, 0x38, 0x73, 0x01, 0x20,
+	0xFF, 0xF7, 0x06, 0xFE, 0x00, 0x20, 0x30, 0x70,
+	0x38, 0x7B, 0x02, 0x28, 0x18, 0xD0, 0xA8, 0x79,
+	0x00, 0x28, 0x01, 0xD0, 0xFE, 0xF7, 0x36, 0xFE,
+	0x38, 0x7B, 0x2C, 0x4F, 0x38, 0x70, 0x81, 0x20,
+	0x28, 0x70, 0x2E, 0x4A, 0x01, 0x21, 0x10, 0x88,
+	0x09, 0x03, 0x08, 0x43, 0x10, 0x80, 0x38, 0x78,
+	0x03, 0x00, 0xFD, 0xF7, 0x9F, 0xFA, 0x07, 0x25,
+	0x35, 0x35, 0x3B, 0x0B, 0x18, 0x0B, 0x3B, 0x00,
+	0x83, 0x20, 0x28, 0x70, 0x05, 0x20, 0xFF, 0xF7,
+	0x3D, 0xFC, 0xF8, 0xBD, 0x24, 0xA0, 0x01, 0xF0,
+	0xA9, 0xFD, 0x01, 0x20, 0xFE, 0xF7, 0xB4, 0xFD,
+	0x00, 0x20, 0xFF, 0xF7, 0x33, 0xFC, 0x00, 0x28,
+	0x1C, 0xD0, 0x01, 0x20, 0x18, 0xE0, 0x20, 0xA0,
+	0x01, 0xF0, 0x9C, 0xFD, 0x01, 0x20, 0xFE, 0xF7,
+	0xA7, 0xFD, 0x01, 0x20, 0xFF, 0xF7, 0x26, 0xFC,
+	0x00, 0x28, 0x0F, 0xD0, 0x04, 0x20, 0x0B, 0xE0,
+	0x1B, 0xA0, 0x01, 0xF0, 0x8F, 0xFD, 0x00, 0x20,
+	0xFE, 0xF7, 0x9A, 0xFD, 0x02, 0x20, 0xFF, 0xF7,
+	0x19, 0xFC, 0x00, 0x28, 0x02, 0xD0, 0x02, 0x20,
+	0x28, 0x70, 0x07, 0xE0, 0x00, 0x24, 0x05, 0xE0,
+	0x83, 0x20, 0x28, 0x70, 0x05, 0x20, 0xFF, 0xF7,
+	0x0D, 0xFC, 0x04, 0x46, 0x2A, 0x78, 0x39, 0x78,
+	0x11, 0xA0, 0x01, 0xF0, 0x77, 0xFD, 0x30, 0x78,
+	0xC0, 0x07, 0x02, 0xD0, 0x81, 0x20, 0x28, 0x70,
+	0x00, 0x24, 0x20, 0x46, 0xF8, 0xBD, 0x00, 0x00,
+	0x50, 0x02, 0x00, 0x20, 0xE5, 0x02, 0x00, 0x20,
+	0xCE, 0x00, 0x00, 0x20, 0x84, 0x04, 0x00, 0x20,
+	0x00, 0x11, 0x00, 0x50, 0xF6, 0x02, 0x00, 0x20,
+	0x44, 0x41, 0x0D, 0x0A, 0x00, 0x00, 0x00, 0x00,
+	0x44, 0x49, 0x0D, 0x0A, 0x00, 0x00, 0x00, 0x00,
+	0x44, 0x53, 0x54, 0x42, 0x0D, 0x0A, 0x00, 0x00,
+	0x44, 0x53, 0x50, 0x3D, 0x25, 0x64, 0x2C, 0x50,
+	0x57, 0x52, 0x3D, 0x25, 0x64, 0x20, 0x0D, 0x0A,
+	0x00, 0x00, 0x00, 0x00, 0x70, 0xB5, 0x0E, 0x4D,
+	0x28, 0x68, 0xFE, 0xF7, 0xFB, 0xF8, 0x04, 0x00,
+	0x07, 0xD0, 0x80, 0x21, 0x0B, 0x48, 0xFD, 0xF7,
+	0xC4, 0xF9, 0x0A, 0x49, 0x28, 0x68, 0x00, 0xF0,
+	0xE7, 0xF8, 0x09, 0x48, 0x00, 0x88, 0x81, 0x07,
+	0x06, 0xD0, 0x00, 0x07, 0x04, 0xD5, 0x80, 0x21,
+	0x04, 0x48, 0xFD, 0xF7, 0xB6, 0xF9, 0x00, 0x24,
+	0xFF, 0xF7, 0x52, 0xFE, 0x20, 0x46, 0x70, 0xBD,
+	0xF8, 0x02, 0x00, 0x20, 0x4C, 0x07, 0x00, 0x20,
+	0xF6, 0x02, 0x00, 0x20, 0x70, 0xB5, 0x05, 0x46,
+	0x01, 0x24, 0xFE, 0xF7, 0xFF, 0xFB, 0x00, 0x2D,
+	0x09, 0xD0, 0xF0, 0x20, 0xFD, 0xF7, 0xD4, 0xF9,
+	0xFE, 0xF7, 0xC6, 0xFC, 0xFE, 0xF7, 0x5C, 0xFC,
+	0x00, 0x20, 0xFE, 0xF7, 0x25, 0xFD, 0xFE, 0xF7,
+	0xD9, 0xFD, 0xFF, 0xF7, 0xB5, 0xFA, 0x20, 0x46,
+	0x70, 0xBD, 0x00, 0x00, 0x70, 0xB5, 0x35, 0x49,
+	0x08, 0x78, 0x00, 0x28, 0x01, 0xD0, 0x40, 0x1E,
+	0x08, 0x70, 0x33, 0x4B, 0x33, 0x49, 0x18, 0x78,
+	0x09, 0x78, 0x00, 0x25, 0x32, 0x4A, 0x33, 0x4C,
+	0x00, 0x28, 0x07, 0xD1, 0x5E, 0x78, 0x00, 0x2E,
+	0x04, 0xD1, 0x9B, 0x78, 0x00, 0x2B, 0x01, 0xD1,
+	0x00, 0x29, 0x12, 0xD0, 0x65, 0x80, 0xA3, 0x88,
+	0xE1, 0x26, 0xB6, 0x00, 0xB3, 0x42, 0x08, 0xD2,
+	0x01, 0x43, 0x06, 0xD0, 0x2A, 0x49, 0x09, 0x78,
+	0x04, 0x29, 0xA1, 0x88, 0x00, 0xD1, 0x49, 0x1C,
+	0xA1, 0x80, 0x00, 0x28, 0x19, 0xD0, 0x15, 0x70,
+	0x1C, 0xE0, 0xA0, 0x88, 0x00, 0x28, 0x05, 0xD0,
+	0x20, 0x7A, 0xFF, 0x28, 0x02, 0xD2, 0x20, 0x7A,
+	0x40, 0x1C, 0x20, 0x72, 0xA5, 0x80, 0x60, 0x88,
+	0x01, 0x21, 0x09, 0x03, 0x88, 0x42, 0x02, 0xD2,
+	0x60, 0x88, 0x40, 0x1C, 0x60, 0x80, 0x1D, 0x49,
+	0x08, 0x78, 0x00, 0x28, 0x01, 0xD0, 0x40, 0x1E,
+	0x08, 0x70, 0x10, 0x78, 0x00, 0x28, 0x01, 0xD0,
+	0x40, 0x1E, 0x10, 0x70, 0x20, 0x78, 0x01, 0x28,
+	0x16, 0xD0, 0x02, 0x28, 0x14, 0xD0, 0x04, 0x28,
+	0x12, 0xD0, 0x82, 0x28, 0x12, 0xD1, 0x14, 0x4E,
+	0x30, 0x78, 0x30, 0x28, 0x0E, 0xD1, 0x13, 0xA0,
+	0x01, 0xF0, 0xB8, 0xFC, 0x05, 0x20, 0xFF, 0xF7,
+	0xEB, 0xFC, 0xFD, 0xF7, 0x37, 0xFC, 0x13, 0xA0,
+	0x01, 0xF0, 0xB0, 0xFC, 0x35, 0x70, 0x01, 0xE0,
+	0x00, 0xF0, 0xDE, 0xF8, 0x14, 0x48, 0x00, 0x78,
+	0xC0, 0x07, 0x01, 0xD0, 0x81, 0x20, 0x20, 0x70,
+	0x70, 0xBD, 0x00, 0x00, 0x70, 0x04, 0x00, 0x20,
+	0xA0, 0x04, 0x00, 0x20, 0x73, 0x04, 0x00, 0x20,
+	0x72, 0x04, 0x00, 0x20, 0x84, 0x04, 0x00, 0x20,
+	0xE5, 0x02, 0x00, 0x20, 0x75, 0x04, 0x00, 0x20,
+	0x88, 0x02, 0x00, 0x20, 0x73, 0x6C, 0x65, 0x65,
+	0x70, 0x20, 0x6D, 0x6F, 0x64, 0x65, 0x0D, 0x0A,
+	0x00, 0x00, 0x00, 0x00, 0x6C, 0x65, 0x61, 0x76,
+	0x65, 0x20, 0x73, 0x6C, 0x65, 0x65, 0x70, 0x20,
+	0x6D, 0x6F, 0x64, 0x65, 0x0D, 0x0A, 0x00, 0x00,
+	0xCE, 0x00, 0x00, 0x20, 0x70, 0xB5, 0x10, 0x4D,
+	0x68, 0x78, 0x00, 0x07, 0x01, 0xD5, 0x00, 0xF0,
+	0x45, 0xF9, 0x0E, 0x4C, 0x60, 0x22, 0x0E, 0x48,
+	0x21, 0x68, 0xFD, 0xF7, 0xDD, 0xF8, 0xFD, 0xF7,
+	0x2D, 0xFA, 0x68, 0x78, 0x96, 0x21, 0x08, 0x42,
+	0x01, 0xD0, 0x00, 0xF0, 0x37, 0xF9, 0x09, 0x48,
+	0x00, 0x78, 0x00, 0x06, 0x06, 0xD4, 0x01, 0x21,
+	0x20, 0x68, 0xFE, 0xF7, 0x2F, 0xF9, 0x20, 0x68,
+	0xFD, 0xF7, 0xF0, 0xFA, 0x70, 0xBD, 0x00, 0x00,
+	0x88, 0x02, 0x00, 0x20, 0xF8, 0x02, 0x00, 0x20,
+	0xD4, 0x00, 0x00, 0x20, 0x86, 0x02, 0x00, 0x20,
+	0x30, 0xB5, 0x08, 0x4A, 0x14, 0x68, 0x1B, 0x34,
+	0x00, 0x22, 0xA3, 0x5C, 0x41, 0x2B, 0x03, 0xD0,
+	0x55, 0x00, 0x45, 0x5B, 0x5B, 0x00, 0xCD, 0x52,
+	0x52, 0x1C, 0xD2, 0xB2, 0x30, 0x2A, 0xF4, 0xD3,
+	0x30, 0xBD, 0x00, 0x00, 0xB8, 0x02, 0x00, 0x20,
+	0x01, 0x20, 0x80, 0x07, 0x40, 0x69, 0x40, 0x05,
+	0x01, 0xD5, 0x01, 0x20, 0x70, 0x47, 0x00, 0x20,
+	0x70, 0x47, 0x00, 0x00, 0x10, 0xB5, 0xFF, 0xF7,
+	0x25, 0xFC, 0x04, 0x48, 0x00, 0x78, 0x00, 0x28,
+	0x02, 0xD1, 0x01, 0x20, 0xFF, 0xF7, 0xF2, 0xF9,
+	0x10, 0xBD, 0x00, 0x00, 0x90, 0x02, 0x00, 0x20,
+	0x36, 0x03, 0x35, 0x03, 0x2C, 0x03, 0x9C, 0x03,
+	0xE9, 0x02, 0x28, 0x03, 0x29, 0x03, 0x1C, 0x03,
+	0x9D, 0x03, 0x2F, 0x03, 0x4A, 0x03, 0x4D, 0x03,
+	0xFF, 0x03, 0xFF, 0x03, 0xFF, 0x03, 0xFF, 0x03,
+	0x3D, 0x03, 0x3C, 0x03, 0x2C, 0x03, 0x9C, 0x03,
+	0xD3, 0x02, 0x28, 0x03, 0x29, 0x03, 0xD6, 0x02,
+	0x9E, 0x03, 0x30, 0x03, 0x37, 0x03, 0x3A, 0x03,
+	0xFF, 0x03, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x10, 0xB5, 0x00, 0x20, 0xFD, 0xF7, 0xCC, 0xFA,
+	0x05, 0x49, 0x08, 0x20, 0x08, 0x70, 0x05, 0x49,
+	0x06, 0x20, 0x08, 0x70, 0x01, 0x20, 0x04, 0x49,
+	0x80, 0x02, 0x08, 0x80, 0x10, 0xBD, 0x00, 0x00,
+	0xE5, 0x02, 0x00, 0x20, 0x71, 0x04, 0x00, 0x20,
+	0xF2, 0x02, 0x00, 0x20, 0x06, 0x49, 0x01, 0x20,
+	0x08, 0x70, 0x06, 0x49, 0x08, 0x20, 0x08, 0x70,
+	0x05, 0x49, 0x06, 0x20, 0x08, 0x70, 0x01, 0x20,
+	0x04, 0x49, 0x80, 0x02, 0x08, 0x80, 0x70, 0x47,
+	0xE7, 0x02, 0x00, 0x20, 0xE5, 0x02, 0x00, 0x20,
+	0x71, 0x04, 0x00, 0x20, 0xF2, 0x02, 0x00, 0x20,
+	0xF8, 0xB5, 0x3E, 0x4C, 0x3E, 0x4A, 0x20, 0x88,
+	0x3E, 0x4D, 0xC1, 0x04, 0x0C, 0xD5, 0x51, 0x10,
+	0x88, 0x43, 0x20, 0x80, 0x29, 0x78, 0x00, 0x29,
+	0x03, 0xD0, 0x01, 0x29, 0x01, 0xD0, 0x02, 0x29,
+	0x65, 0xD1, 0x10, 0x43, 0x20, 0x80, 0x62, 0xE0,
+	0x81, 0x04, 0x03, 0xD5, 0x90, 0x43, 0x20, 0x80,
+	0xFE, 0xF7, 0xF8, 0xFB, 0x20, 0x88, 0x81, 0x07,
+	0x0E, 0xD0, 0x01, 0x04, 0x57, 0xD5, 0x40, 0x04,
+	0x40, 0x0C, 0x20, 0x80, 0x81, 0x07, 0x01, 0xD5,
+	0x00, 0x20, 0x02, 0xE0, 0xC0, 0x07, 0x4E, 0xD0,
+	0x01, 0x20, 0xFF, 0xF7, 0x67, 0xFB, 0x4A, 0xE0,
+	0x00, 0x04, 0x5A, 0x21, 0x2A, 0x4F, 0x2B, 0x4E,
+	0x00, 0x28, 0x05, 0xDA, 0x38, 0x78, 0x00, 0x28,
+	0x00, 0xD0, 0x31, 0x80, 0x00, 0x20, 0x20, 0x80,
+	0x27, 0x48, 0x40, 0x88, 0x00, 0x28, 0x19, 0xD0,
+	0x26, 0x48, 0x00, 0x78, 0x04, 0x28, 0x15, 0xD3,
+	0x25, 0x4A, 0x00, 0x20, 0x10, 0x5E, 0x27, 0x22,
+	0xD2, 0x43, 0x90, 0x42, 0x0E, 0xDB, 0x23, 0x48,
+	0x00, 0x68, 0x90, 0x30, 0x42, 0x7B, 0x03, 0x7B,
+	0x10, 0x02, 0x18, 0x43, 0x42, 0x00, 0x80, 0x18,
+	0x1F, 0x4A, 0x00, 0x23, 0xD3, 0x5E, 0x80, 0x08,
+	0x98, 0x42, 0x01, 0xDA, 0x31, 0x80, 0x04, 0xE0,
+	0x30, 0x88, 0x00, 0x28, 0x1B, 0xD0, 0x5A, 0x28,
+	0x08, 0xD1, 0x38, 0x78, 0x00, 0x28, 0x05, 0xD1,
+	0x18, 0xA0, 0x01, 0xF0, 0x6B, 0xFB, 0x01, 0x20,
+	0xFF, 0xF7, 0x2C, 0xFB, 0x28, 0x78, 0x02, 0x28,
+	0x04, 0xD1, 0x30, 0x88, 0x5A, 0x28, 0x01, 0xD1,
+	0x2D, 0x20, 0x30, 0x80, 0x30, 0x88, 0x40, 0x1E,
+	0x80, 0xB2, 0x30, 0x80, 0x39, 0x78, 0x00, 0x29,
+	0x01, 0xD0, 0x00, 0x28, 0x02, 0xD0, 0xFD, 0xF7,
+	0x2B, 0xF9, 0xF8, 0xBD, 0x0D, 0xA0, 0x01, 0xF0,
+	0x51, 0xFB, 0xA5, 0xE7, 0xF6, 0x02, 0x00, 0x20,
+	0x00, 0x20, 0x00, 0x00, 0x85, 0x02, 0x00, 0x20,
+	0x74, 0x04, 0x00, 0x20, 0x7E, 0x04, 0x00, 0x20,
+	0x84, 0x04, 0x00, 0x20, 0x87, 0x02, 0x00, 0x20,
+	0x76, 0x04, 0x00, 0x20, 0xB8, 0x02, 0x00, 0x20,
+	0x78, 0x04, 0x00, 0x20, 0x53, 0x50, 0x55, 0x0D,
+	0x0A, 0x00, 0x00, 0x00, 0x53, 0x50, 0x44, 0x0D,
+	0x0A, 0x00, 0x00, 0x00, 0xF8, 0xB5, 0x55, 0x4E,
+	0x30, 0x78, 0x00, 0x28, 0x7E, 0xD1, 0x70, 0x78,
+	0x44, 0x08, 0x70, 0x88, 0x64, 0x00, 0x00, 0x0A,
+	0xC0, 0x07, 0x03, 0xD0, 0x70, 0x88, 0x00, 0x0A,
+	0x00, 0x02, 0x04, 0x43, 0x30, 0x78, 0x00, 0x28,
+	0x01, 0xD1, 0x70, 0x88, 0x04, 0x43, 0x70, 0x78,
+	0xC0, 0x07, 0x6B, 0xD1, 0x4A, 0x48, 0x42, 0x2C,
+	0x41, 0x69, 0x2F, 0xD0, 0x0F, 0xDC, 0x49, 0x4F,
+	0x08, 0x2C, 0x2F, 0xD0, 0x05, 0xDC, 0x02, 0x2C,
+	0x1A, 0xD0, 0x04, 0x2C, 0x7C, 0xD1, 0x46, 0x4F,
+	0x16, 0xE0, 0x10, 0x2C, 0x26, 0xD0, 0x20, 0x2C,
+	0x76, 0xD1, 0x80, 0x22, 0x5B, 0xE0, 0x50, 0x2C,
+	0x04, 0xD0, 0x05, 0xDC, 0x44, 0x2C, 0x1B, 0xD0,
+	0x48, 0x2C, 0x6D, 0xD1, 0x87, 0x69, 0x05, 0xE0,
+	0xFF, 0x3C, 0x09, 0x3C, 0x01, 0xD0, 0x08, 0x2C,
+	0x66, 0xD1, 0x0F, 0x46, 0x00, 0x2F, 0x63, 0xD0,
+	0x80, 0x21, 0x3A, 0x48, 0xFC, 0xF7, 0x75, 0xFF,
+	0x39, 0x48, 0x00, 0x78, 0x00, 0x06, 0x5C, 0xD5,
+	0x60, 0x22, 0x39, 0x46, 0x35, 0x48, 0xFC, 0xF7,
+	0x53, 0xFF, 0x5A, 0xE0, 0x35, 0x4F, 0xEF, 0xE7,
+	0x35, 0x4F, 0xED, 0xE7, 0x60, 0x22, 0x35, 0x48,
+	0xFC, 0xF7, 0x4A, 0xFF, 0x33, 0x4D, 0x20, 0x07,
+	0x0A, 0xD5, 0x2F, 0x20, 0x41, 0x00, 0xCB, 0x19,
+	0x6A, 0x5A, 0x1B, 0x88, 0x40, 0x1E, 0xD2, 0x1A,
+	0x40, 0xB2, 0x6A, 0x52, 0x00, 0x28, 0xF5, 0xDA,
+	0x00, 0x21, 0x2C, 0x48, 0xFD, 0xF7, 0x9A, 0xFF,
+	0x2A, 0x48, 0xFD, 0xF7, 0x5B, 0xF9, 0x26, 0x48,
+	0x00, 0x78, 0x00, 0x06, 0x04, 0xD5, 0x60, 0x22,
+	0x26, 0x49, 0x27, 0x48, 0xFC, 0xF7, 0x2C, 0xFF,
+	0x24, 0x48, 0xFD, 0xF7, 0x73, 0xFE, 0x20, 0x07,
+	0x0A, 0xD5, 0x2F, 0x20, 0x41, 0x00, 0xCB, 0x19,
+	0x6A, 0x5A, 0x1B, 0x88, 0x40, 0x1E, 0xD2, 0x18,
+	0x40, 0xB2, 0x6A, 0x52, 0x00, 0x28, 0xF5, 0xDA,
+	0x19, 0x48, 0x00, 0xE0, 0x1C, 0xE0, 0x00, 0x78,
+	0x00, 0x06, 0x05, 0xD5, 0x60, 0x22, 0x19, 0x49,
+	0x14, 0x48, 0xFC, 0xF7, 0x11, 0xFF, 0x0F, 0xE0,
+	0x18, 0x49, 0x12, 0x4B, 0x0A, 0x68, 0x00, 0x20,
+	0x11, 0x18, 0xC9, 0x7E, 0x41, 0x29, 0x03, 0xD0,
+	0x44, 0x00, 0x2C, 0x5B, 0x49, 0x00, 0x5C, 0x52,
+	0x40, 0x1C, 0x40, 0xB2, 0x30, 0x28, 0xF3, 0xDB,
+	0x70, 0x78, 0x01, 0x21, 0x08, 0x43, 0x70, 0x70,
+	0xF8, 0xBD, 0x08, 0x49, 0x38, 0x46, 0xFF, 0xF7,
+	0x37, 0xFE, 0x71, 0x78, 0x01, 0x20, 0x01, 0x43,
+	0x71, 0x70, 0xF8, 0xBD, 0x88, 0x02, 0x00, 0x20,
+	0xE4, 0x02, 0x00, 0x20, 0x00, 0x00, 0x01, 0x20,
+	0x00, 0x20, 0x00, 0x50, 0x9C, 0x01, 0x00, 0x20,
+	0x86, 0x02, 0x00, 0x20, 0x00, 0x00, 0x02, 0x20,
+	0x00, 0x30, 0x00, 0x50, 0x4C, 0x07, 0x00, 0x20,
+	0x4C, 0x00, 0x00, 0x20, 0xB8, 0x02, 0x00, 0x20,
+	0xF7, 0xB5, 0x45, 0x49, 0x84, 0xB0, 0x00, 0x28,
+	0x13, 0xD0, 0x44, 0x48, 0x00, 0x88, 0x84, 0xB2,
+	0x05, 0x98, 0x00, 0x28, 0x03, 0xD0, 0x90, 0x68,
+	0x01, 0x26, 0x10, 0x60, 0x00, 0xE0, 0x26, 0x46,
+	0x20, 0x20, 0x03, 0x90, 0x08, 0x68, 0x28, 0x27,
+	0x80, 0x30, 0x00, 0x7C, 0x02, 0x90, 0x3C, 0x48,
+	0x13, 0xE0, 0x3C, 0x48, 0x00, 0x88, 0x84, 0xB2,
+	0x05, 0x98, 0x00, 0x28, 0x03, 0xD0, 0x90, 0x68,
+	0x01, 0x26, 0x10, 0x60, 0x00, 0xE0, 0x26, 0x46,
+	0x20, 0x20, 0x03, 0x90, 0x08, 0x68, 0x28, 0x27,
+	0x80, 0x30, 0x40, 0x7C, 0x40, 0x08, 0x02, 0x90,
+	0x33, 0x48, 0x05, 0x78, 0x10, 0x68, 0x00, 0x28,
+	0x01, 0xDC, 0x01, 0x20, 0x10, 0x60, 0x53, 0x68,
+	0x01, 0x46, 0xC0, 0x18, 0x00, 0x04, 0x40, 0x0C,
+	0x83, 0x42, 0x01, 0xDB, 0x18, 0x46, 0x00, 0xE0,
+	0x50, 0x60, 0x41, 0x18, 0x68, 0x43, 0xFC, 0xF7,
+	0x87, 0xFE, 0x00, 0xB2, 0x00, 0x90, 0x29, 0x48,
+	0x00, 0x88, 0xC1, 0x00, 0x40, 0x18, 0xC0, 0x08,
+	0x28, 0x1A, 0x81, 0xB2, 0x00, 0x98, 0x01, 0x91,
+	0x88, 0x42, 0x12, 0xDD, 0x09, 0x21, 0xE8, 0x08,
+	0xFC, 0xF7, 0x76, 0xFE, 0x01, 0x06, 0x09, 0x0E,
+	0x00, 0xD1, 0x01, 0x21, 0x01, 0x9A, 0x00, 0x98,
+	0x80, 0x1A, 0xFC, 0xF7, 0x6D, 0xFE, 0x20, 0x30,
+	0xC0, 0xB2, 0x28, 0x28, 0x02, 0xD2, 0x07, 0x46,
+	0x00, 0xE0, 0x03, 0x9F, 0x00, 0x98, 0x69, 0x00,
+	0x47, 0x43, 0x08, 0x37, 0x38, 0x11, 0x08, 0x1A,
+	0x00, 0xB2, 0x00, 0x28, 0x01, 0xDA, 0x00, 0x20,
+	0x02, 0xE0, 0xA8, 0x42, 0x00, 0xDD, 0x28, 0x46,
+	0x05, 0x99, 0x00, 0x29, 0x01, 0xD0, 0x30, 0x18,
+	0x00, 0xE0, 0x30, 0x1A, 0x02, 0x99, 0x65, 0x08,
+	0x49, 0x00, 0x40, 0x1B, 0x61, 0x1A, 0x60, 0x43,
+	0x4A, 0x10, 0x80, 0x18, 0xFC, 0xF7, 0x48, 0xFE,
+	0x28, 0x18, 0x01, 0x28, 0x01, 0xDA, 0x01, 0x20,
+	0x02, 0xE0, 0xA0, 0x42, 0x00, 0xDD, 0x20, 0x46,
+	0x40, 0x1E, 0x00, 0xB2, 0x07, 0xB0, 0xF0, 0xBD,
+	0xB8, 0x02, 0x00, 0x20, 0x42, 0x00, 0x00, 0x20,
+	0x2E, 0x00, 0x00, 0x20, 0x44, 0x00, 0x00, 0x20,
+	0x2F, 0x00, 0x00, 0x20, 0x40, 0x00, 0x00, 0x20,
+	0xF7, 0xB5, 0x1A, 0x49, 0x09, 0x68, 0x80, 0x31,
+	0x00, 0x28, 0x06, 0xD0, 0x18, 0x48, 0x0E, 0x7C,
+	0x00, 0x88, 0xCD, 0x7C, 0x84, 0xB2, 0x4F, 0x7D,
+	0x05, 0xE0, 0x16, 0x48, 0x4E, 0x7C, 0x00, 0x88,
+	0x0D, 0x7D, 0x8F, 0x7D, 0x84, 0xB2, 0x10, 0x69,
+	0xD1, 0x68, 0x68, 0x43, 0x4A, 0x10, 0x80, 0x18,
+	0xFC, 0xF7, 0x16, 0xFE, 0xC1, 0x19, 0x01, 0x98,
+	0x40, 0x1E, 0x68, 0x43, 0x08, 0x18, 0x69, 0x08,
+	0x40, 0x1A, 0x65, 0x08, 0x71, 0x00, 0x40, 0x1B,
+	0x61, 0x1A, 0x60, 0x43, 0x4A, 0x10, 0x80, 0x18,
+	0xFC, 0xF7, 0x06, 0xFE, 0x28, 0x18, 0x01, 0x28,
+	0x01, 0xDA, 0x01, 0x20, 0x02, 0xE0, 0xA0, 0x42,
+	0x00, 0xDD, 0x20, 0x46, 0x40, 0x1E, 0x00, 0xB2,
+	0xFE, 0xBD, 0x00, 0x00, 0xB8, 0x02, 0x00, 0x20,
+	0x42, 0x00, 0x00, 0x20, 0x44, 0x00, 0x00, 0x20,
+	0x05, 0x48, 0x00, 0x68, 0x90, 0x30, 0x41, 0x7B,
+	0x02, 0x7B, 0x08, 0x02, 0x10, 0x43, 0x03, 0x4A,
+	0x02, 0x21, 0x11, 0x80, 0x00, 0xB2, 0x70, 0x47,
+	0xB8, 0x02, 0x00, 0x20, 0x26, 0x00, 0x00, 0x20,
+	0x70, 0xB5, 0x1A, 0x4D, 0x16, 0x20, 0x28, 0x70,
+	0x68, 0x78, 0x29, 0x46, 0x00, 0x24, 0x14, 0x31,
+	0x02, 0x28, 0x0C, 0xD8, 0x00, 0x28, 0x05, 0xD1,
+	0xA8, 0x78, 0x00, 0x28, 0x02, 0xD0, 0x08, 0x46,
+	0x1C, 0x30, 0x04, 0x70, 0x48, 0x88, 0x00, 0x28,
+	0x01, 0xD1, 0x03, 0x20, 0x68, 0x71, 0xAC, 0x70,
+	0x2C, 0x81, 0xEC, 0x80, 0xAC, 0x81, 0x6C, 0x81,
+	0x4C, 0x80, 0x8C, 0x80, 0x0C, 0x72, 0x0C, 0x48,
+	0x44, 0x70, 0x84, 0x70, 0x04, 0x70, 0x09, 0x48,
+	0xB4, 0x21, 0xD0, 0x30, 0xFC, 0xF7, 0xE9, 0xFD,
+	0x06, 0x48, 0xA0, 0x21, 0x30, 0x30, 0xFC, 0xF7,
+	0xE4, 0xFD, 0xEC, 0x70, 0x5A, 0x20, 0xE8, 0x81,
+	0x2C, 0x71, 0x2C, 0x61, 0x01, 0xF0, 0x66, 0xFC,
+	0x70, 0xBD, 0x00, 0x00, 0x70, 0x04, 0x00, 0x20,
+	0x90, 0x02, 0x00, 0x20, 0xFF, 0xB5, 0xA5, 0xB0,
+	0x18, 0xB2, 0x17, 0x90, 0x00, 0x20, 0x0D, 0x90,
+	0x25, 0x98, 0x01, 0x78, 0x03, 0x91, 0x26, 0x98,
+	0x03, 0x78, 0xFE, 0x4A, 0x04, 0x93, 0x10, 0x68,
+	0x01, 0x46, 0x90, 0x31, 0xCB, 0x7A, 0x89, 0x7A,
+	0x1B, 0x02, 0x0B, 0x43, 0xA0, 0x21, 0x1C, 0x93,
+	0x0B, 0x56, 0x00, 0x21, 0x1F, 0x93, 0x18, 0x91,
+	0x03, 0x7E, 0x07, 0x93, 0x41, 0x7E, 0x1A, 0x91,
+	0x03, 0x99, 0x59, 0x43, 0x04, 0x9B, 0xC9, 0x18,
+	0x89, 0xB2, 0x16, 0x91, 0x55, 0x21, 0x0A, 0x91,
+	0x38, 0x21, 0x13, 0x91, 0x16, 0x99, 0xF0, 0x4B,
+	0x49, 0x00, 0x22, 0x91, 0x59, 0x5E, 0x1C, 0x9B,
+	0x99, 0x42, 0x04, 0xDA, 0x0A, 0x23, 0x59, 0x43,
+	0x49, 0x11, 0x11, 0x91, 0x07, 0xE0, 0xCB, 0x00,
+	0xC9, 0x18, 0x49, 0x11, 0x11, 0x91, 0x40, 0x21,
+	0x0A, 0x91, 0x2A, 0x21, 0x13, 0x91, 0xB0, 0x30,
+	0x41, 0x7A, 0x02, 0x7A, 0x08, 0x02, 0xE5, 0x49,
+	0x10, 0x43, 0x0A, 0x88, 0xE4, 0x49, 0x90, 0x42,
+	0x02, 0xD2, 0x08, 0x78, 0x01, 0x28, 0x03, 0xD0,
+	0xE2, 0x48, 0x00, 0x78, 0x00, 0x28, 0x0B, 0xD0,
+	0x0A, 0x98, 0x42, 0x00, 0x80, 0x18, 0xC0, 0x05,
+	0x00, 0x0E, 0x0A, 0x90, 0x13, 0x98, 0x42, 0x00,
+	0x80, 0x18, 0xC0, 0x05, 0x00, 0x0E, 0x13, 0x90,
+	0x07, 0x9A, 0x04, 0x98, 0x10, 0x1A, 0x40, 0x1E,
+	0x23, 0x90, 0x16, 0x98, 0x02, 0x90, 0x00, 0x20,
+	0x08, 0x90, 0x0C, 0x90, 0x32, 0x21, 0xD6, 0x48,
+	0xFC, 0xF7, 0x6F, 0xFD, 0x0A, 0x21, 0xD5, 0x48,
+	0xFC, 0xF7, 0x6B, 0xFD, 0x0A, 0x21, 0xD4, 0x48,
+	0xFC, 0xF7, 0x67, 0xFD, 0xD2, 0x48, 0x69, 0x46,
+	0x00, 0x1D, 0x12, 0x90, 0xCF, 0x48, 0x00, 0x1D,
+	0x09, 0x90, 0x00, 0x20, 0x48, 0x70, 0xCA, 0x48,
+	0x00, 0x78, 0x01, 0x28, 0x01, 0xD9, 0x02, 0x20,
+	0x00, 0xE0, 0x0E, 0x20, 0x08, 0x70, 0x88, 0x70,
+	0x01, 0x20, 0x00, 0x24, 0x0E, 0x90, 0x2C, 0xE0,
+	0x00, 0x2C, 0x2A, 0xD0, 0x0E, 0x98, 0x00, 0x28,
+	0x03, 0x98, 0x13, 0xD0, 0x84, 0x42, 0x06, 0xD9,
+	0x16, 0x98, 0x00, 0x24, 0x02, 0x90, 0x0E, 0x94,
+	0x68, 0x46, 0x01, 0x70, 0x6D, 0xE1, 0x07, 0x99,
+	0x02, 0x98, 0x40, 0x1A, 0x80, 0xB2, 0x02, 0x90,
+	0x02, 0x20, 0x00, 0x1B, 0x41, 0x00, 0xBB, 0x48,
+	0x08, 0x18, 0x0C, 0xE0, 0x01, 0x19, 0x1A, 0x98,
+	0x81, 0x42, 0x77, 0xD0, 0x07, 0x99, 0x02, 0x98,
+	0x40, 0x18, 0x80, 0xB2, 0x02, 0x90, 0xB5, 0x48,
+	0x61, 0x00, 0x08, 0x18, 0x00, 0x1D, 0x69, 0x46,
+	0x09, 0x90, 0x08, 0x78, 0x48, 0x70, 0x00, 0x20,
+	0x08, 0x70, 0x00, 0x20, 0x19, 0x90, 0x15, 0x90,
+	0x12, 0xE0, 0x00, 0x28, 0x10, 0xD0, 0x04, 0x98,
+	0x14, 0x90, 0x00, 0x20, 0x0F, 0x90, 0x68, 0x46,
+	0x40, 0x78, 0x01, 0x25, 0x08, 0x27, 0x18, 0x21,
+	0x08, 0x40, 0x0B, 0x90, 0x14, 0x98, 0x02, 0x28,
+	0x01, 0xD9, 0x02, 0x20, 0x14, 0x90, 0x17, 0xE1,
+	0x23, 0x98, 0x00, 0x25, 0xC0, 0xB2, 0x14, 0x90,
+	0x01, 0x20, 0x0F, 0x90, 0x68, 0x46, 0x40, 0x78,
+	0x04, 0x27, 0x40, 0x07, 0x40, 0x0F, 0xEC, 0xE7,
+	0x0F, 0x98, 0x00, 0x28, 0x01, 0xD0, 0x10, 0x95,
+	0x01, 0xE0, 0x68, 0x42, 0x10, 0x90, 0x0B, 0x98,
+	0xB8, 0x43, 0x0B, 0x90, 0x10, 0x99, 0x02, 0x98,
+	0x40, 0x18, 0x1B, 0x90, 0x40, 0x00, 0x92, 0x49,
+	0x1D, 0x90, 0x0E, 0x5E, 0x68, 0x46, 0x40, 0x78,
+	0x1E, 0x90, 0x38, 0x42, 0x01, 0xD1, 0x00, 0x2C,
+	0x7E, 0xD1, 0x00, 0x2E, 0x7C, 0xDD, 0xF0, 0x07,
+	0x0B, 0xD1, 0x09, 0x98, 0x09, 0x99, 0x00, 0x88,
+	0x80, 0x19, 0x08, 0x80, 0x10, 0x98, 0x12, 0x99,
+	0x40, 0x00, 0x09, 0x5A, 0x12, 0x9A, 0x89, 0x19,
+	0x11, 0x52, 0x11, 0x98, 0x86, 0x42, 0x6B, 0xDB,
+	0xF0, 0x07, 0x69, 0xD1, 0x0A, 0x98, 0x70, 0x43,
+	0x40, 0x11, 0x00, 0xB2, 0x20, 0x90, 0x13, 0x98,
+	0x70, 0x43, 0x40, 0x11, 0x00, 0xB2, 0x21, 0x90,
+	0x00, 0x20, 0x01, 0x90, 0x06, 0x90, 0x0E, 0x98,
+	0x00, 0x28, 0x08, 0xD0, 0x00, 0x2C, 0x10, 0xD0,
+	0x03, 0x98, 0x00, 0x1B, 0x00, 0x28, 0x0C, 0xDD,
+	0x01, 0x20, 0x09, 0xE0, 0xFC, 0xE0, 0x00, 0x2C,
+	0x07, 0xD0, 0x03, 0x98, 0x01, 0x19, 0x1A, 0x98,
+	0x40, 0x1E, 0x81, 0x42, 0x01, 0xDA, 0x02, 0x20,
+	0x01, 0x90, 0x0F, 0x98, 0x00, 0x28, 0x0A, 0xD0,
+	0x00, 0x2D, 0x12, 0xD0, 0x04, 0x98, 0x41, 0x19,
+	0x07, 0x98, 0x40, 0x1E, 0x81, 0x42, 0x0C, 0xDA,
+	0x08, 0x21, 0x01, 0x98, 0x07, 0xE0, 0x00, 0x2D,
+	0x07, 0xD0, 0x04, 0x98, 0x40, 0x1B, 0x00, 0x28,
+	0x03, 0xDD, 0x01, 0x98, 0x04, 0x21, 0x08, 0x43,
+	0x01, 0x90, 0x03, 0x98, 0x00, 0x1B, 0x00, 0x28,
+	0x01, 0xDC, 0x01, 0x20, 0x06, 0xE0, 0x03, 0x98,
+	0x01, 0x19, 0x1A, 0x98, 0x40, 0x1E, 0x81, 0x42,
+	0x01, 0xDB, 0x02, 0x20, 0x06, 0x90, 0x04, 0x98,
+	0x41, 0x19, 0x07, 0x98, 0x40, 0x1E, 0x81, 0x42,
+	0x02, 0xDB, 0x08, 0x21, 0x06, 0x98, 0x05, 0xE0,
+	0x04, 0x98, 0x40, 0x1B, 0x00, 0x28, 0x03, 0xDC,
+	0x06, 0x98, 0x04, 0x21, 0x08, 0x43, 0x06, 0x90,
+	0x20, 0x46, 0x28, 0x43, 0x4F, 0xD0, 0x00, 0x20,
+	0x05, 0x90, 0x00, 0x2C, 0x14, 0xD0, 0x00, 0x2D,
+	0x18, 0xD0, 0x41, 0x00, 0x3C, 0x20, 0xC8, 0x40,
+	0x01, 0x99, 0x08, 0x40, 0x01, 0x07, 0x00, 0xE0,
+	0x39, 0xE0, 0x09, 0x0F, 0x06, 0x98, 0x81, 0x43,
+	0x1F, 0xD0, 0x1B, 0x98, 0x80, 0xB2, 0x00, 0xF0,
+	0xFF, 0xFB, 0xC1, 0x07, 0x19, 0xD1, 0x0C, 0xE0,
+	0x01, 0x46, 0x04, 0x20, 0xC8, 0x40, 0x81, 0x07,
+	0x89, 0x0F, 0x03, 0xE0, 0x02, 0x21, 0x81, 0x40,
+	0x0C, 0x20, 0x01, 0x40, 0x01, 0x98, 0x01, 0x43,
+	0xE8, 0xE7, 0x18, 0x9A, 0x0D, 0x99, 0x11, 0x43,
+	0x04, 0xD1, 0x21, 0x99, 0x88, 0x42, 0x01, 0xDD,
+	0x01, 0x21, 0x18, 0x91, 0x20, 0x99, 0x88, 0x42,
+	0x05, 0xDC, 0x05, 0x98, 0x40, 0x1C, 0xC0, 0xB2,
+	0x05, 0x90, 0x03, 0x28, 0xC9, 0xD3, 0x05, 0x98,
+	0x03, 0x28, 0x14, 0xD0, 0x09, 0x98, 0x09, 0x9A,
+	0x01, 0x88, 0x70, 0x10, 0x09, 0x1A, 0x11, 0x80,
+	0x10, 0x99, 0x12, 0x9A, 0x49, 0x00, 0x52, 0x5A,
+	0x10, 0x1A, 0x12, 0x9A, 0x50, 0x52, 0x0B, 0x98,
+	0x00, 0x28, 0x39, 0xD0, 0x0F, 0x98, 0x00, 0x28,
+	0x2E, 0xD0, 0x7F, 0x08, 0x2E, 0xE0, 0x08, 0x99,
+	0x1B, 0x98, 0x4A, 0x00, 0x2E, 0x49, 0x88, 0x52,
+	0x08, 0x98, 0x40, 0x1C, 0x80, 0xB2, 0x08, 0x90,
+	0x0C, 0x98, 0x86, 0x42, 0x00, 0xDD, 0x0C, 0x96,
+	0x25, 0x48, 0x1D, 0x99, 0x42, 0x5A, 0x01, 0x21,
+	0x0A, 0x43, 0x1D, 0x99, 0x42, 0x52, 0x69, 0x46,
+	0x08, 0x78, 0x38, 0x43, 0x08, 0x70, 0x00, 0x2C,
+	0x0D, 0xD0, 0x0F, 0x98, 0x00, 0x28, 0x1E, 0x98,
+	0x05, 0xD0, 0x79, 0x08, 0x08, 0x43, 0x69, 0x46,
+	0x48, 0x70, 0x00, 0x2D, 0x03, 0xD1, 0x79, 0x00,
+	0x08, 0x43, 0x69, 0x46, 0x48, 0x70, 0x19, 0x98,
+	0x40, 0x1C, 0xC0, 0xB2, 0x19, 0x90, 0xCD, 0xE7,
+	0x78, 0x06, 0x07, 0x0E, 0x6D, 0x1C, 0xED, 0xB2,
+	0x14, 0x98, 0x85, 0x42, 0x00, 0xD8, 0xEF, 0xE6,
+	0x15, 0x98, 0x40, 0x1C, 0xC0, 0xB2, 0x15, 0x90,
+	0x02, 0x28, 0x00, 0xD2, 0xC9, 0xE6, 0x00, 0x2C,
+	0x02, 0xD1, 0x69, 0x46, 0x08, 0x78, 0x88, 0x70,
+	0x19, 0x98, 0x00, 0x28, 0x01, 0xD0, 0x02, 0x2C,
+	0x09, 0xD1, 0x0E, 0x98, 0x00, 0x28, 0x1F, 0xD0,
+	0x16, 0x98, 0x00, 0x24, 0x69, 0x46, 0x0E, 0x94,
+	0x02, 0x90, 0x88, 0x78, 0x08, 0x70, 0x68, 0x46,
+	0x81, 0x78, 0x64, 0x1C, 0xE4, 0xB2, 0x02, 0x2C,
+	0x10, 0xE0, 0x00, 0x00, 0xB8, 0x02, 0x00, 0x20,
+	0x4C, 0x07, 0x00, 0x20, 0x34, 0x00, 0x00, 0x20,
+	0x40, 0x05, 0x00, 0x20, 0x2A, 0x00, 0x00, 0x20,
+	0x1C, 0x02, 0x00, 0x20, 0x1A, 0x00, 0x00, 0x20,
+	0x10, 0x00, 0x00, 0x20, 0x00, 0xD8, 0x6B, 0xE6,
+	0x08, 0x98, 0x01, 0x28, 0x1B, 0xD0, 0x1F, 0x99,
+	0x88, 0x42, 0x18, 0xDA, 0x2C, 0x48, 0x00, 0x68,
+	0xB0, 0x30, 0x41, 0x7A, 0x02, 0x7A, 0x08, 0x02,
+	0x2A, 0x49, 0x10, 0x43, 0x09, 0x88, 0x88, 0x42,
+	0x03, 0xD2, 0x29, 0x48, 0x00, 0x78, 0x01, 0x28,
+	0x09, 0xD0, 0x0D, 0x98, 0x01, 0x28, 0x06, 0xD0,
+	0x18, 0x98, 0x00, 0x28, 0x03, 0xD0, 0x0D, 0x98,
+	0x00, 0x28, 0x05, 0xD0, 0x2E, 0xE0, 0x17, 0x99,
+	0x0C, 0x98, 0x88, 0x42, 0x2A, 0xDC, 0x38, 0xE0,
+	0x01, 0x20, 0x0D, 0x90, 0x38, 0x20, 0x0A, 0x90,
+	0x1E, 0x4A, 0x22, 0x98, 0x1C, 0x99, 0x10, 0x5E,
+	0x88, 0x42, 0x05, 0xDA, 0x09, 0x21, 0x09, 0x03,
+	0x48, 0x43, 0x00, 0x14, 0x11, 0x90, 0x06, 0xE0,
+	0x05, 0x21, 0x49, 0x03, 0x48, 0x43, 0x00, 0x14,
+	0x11, 0x90, 0x2A, 0x20, 0x0A, 0x90, 0x00, 0x20,
+	0x15, 0x4B, 0x08, 0xE0, 0x41, 0x00, 0x59, 0x5A,
+	0x49, 0x00, 0x54, 0x5A, 0x64, 0x08, 0x64, 0x00,
+	0x40, 0x1C, 0x54, 0x52, 0x80, 0xB2, 0x08, 0x99,
+	0x88, 0x42, 0xF3, 0xD3, 0x0D, 0x98, 0x02, 0x28,
+	0x00, 0xD2, 0xF6, 0xE5, 0x27, 0x9A, 0x0D, 0x48,
+	0x18, 0x32, 0x25, 0x99, 0xFD, 0xF7, 0x1E, 0xF9,
+	0x0B, 0x48, 0x27, 0x9A, 0x26, 0x99, 0xFD, 0xF7,
+	0x19, 0xF9, 0x17, 0x9A, 0x0C, 0x99, 0x91, 0x42,
+	0x00, 0xDC, 0x08, 0x48, 0x29, 0xB0, 0xF0, 0xBD,
+	0xB8, 0x02, 0x00, 0x20, 0x34, 0x00, 0x00, 0x20,
+	0x40, 0x05, 0x00, 0x20, 0x4C, 0x07, 0x00, 0x20,
+	0x1C, 0x02, 0x00, 0x20, 0x1A, 0x00, 0x00, 0x20,
+	0x10, 0x00, 0x00, 0x20, 0x00, 0x80, 0xFF, 0xFF,
+	0x06, 0x4A, 0x07, 0x4B, 0x00, 0x21, 0x00, 0x20,
+	0x40, 0x1C, 0x90, 0x42, 0xFC, 0xDB, 0x49, 0x1C,
+	0x99, 0x42, 0xF8, 0xDB, 0x03, 0x48, 0x01, 0x21,
+	0x01, 0x60, 0x70, 0x47, 0x10, 0x27, 0x00, 0x00,
+	0xB8, 0x0B, 0x00, 0x00, 0x04, 0x00, 0x00, 0x40,
+	0xF0, 0xB5, 0x1A, 0x48, 0x8F, 0xB0, 0x01, 0x78,
+	0x02, 0x29, 0x01, 0xD9, 0x02, 0x21, 0x01, 0x70,
+	0x00, 0x25, 0x24, 0xE0, 0x68, 0x1C, 0xC4, 0xB2,
+	0x0D, 0x90, 0x1A, 0xE0, 0x34, 0x21, 0x69, 0x43,
+	0x34, 0x22, 0x0E, 0x18, 0x62, 0x43, 0x17, 0x18,
+	0x31, 0x79, 0x38, 0x79, 0x81, 0x42, 0x0E, 0xD9,
+	0x31, 0x1D, 0x34, 0x22, 0x68, 0x46, 0xFC, 0xF7,
+	0x03, 0xFB, 0x39, 0x1D, 0x30, 0x1D, 0x34, 0x22,
+	0xFC, 0xF7, 0xFE, 0xFA, 0x38, 0x1D, 0x34, 0x22,
+	0x69, 0x46, 0xFC, 0xF7, 0xF9, 0xFA, 0x64, 0x1C,
+	0xE4, 0xB2, 0x06, 0x48, 0x01, 0x78, 0xA1, 0x42,
+	0xE0, 0xD8, 0x0D, 0x98, 0xC5, 0xB2, 0x03, 0x49,
+	0x08, 0x78, 0xA8, 0x42, 0xD6, 0xD8, 0x0F, 0xB0,
+	0xF0, 0xBD, 0x00, 0x00, 0xA0, 0x04, 0x00, 0x20,
+	0x70, 0xB5, 0x7B, 0x24, 0x21, 0x48, 0x24, 0x02,
+	0x04, 0x60, 0xF7, 0x20, 0xC0, 0x01, 0x20, 0x4A,
+	0xC1, 0x7C, 0x11, 0x70, 0x1F, 0x4A, 0x01, 0x7D,
+	0x11, 0x70, 0x1F, 0x4A, 0x41, 0x7D, 0x11, 0x70,
+	0x1E, 0x49, 0x80, 0x7D, 0x08, 0x70, 0x60, 0x7C,
+	0x21, 0x7C, 0x00, 0x06, 0x00, 0x14, 0x08, 0x43,
+	0x1B, 0x49, 0x05, 0x46, 0x08, 0x80, 0xE0, 0x7C,
+	0xA1, 0x7C, 0x00, 0x06, 0x00, 0x14, 0x08, 0x43,
+	0x18, 0x49, 0x08, 0x80, 0x20, 0x7E, 0x61, 0x7E,
+	0x48, 0x43, 0x17, 0x49, 0x08, 0x80, 0x17, 0x48,
+	0xFC, 0xF7, 0xE3, 0xFA, 0x10, 0x21, 0xC8, 0x41,
+	0x15, 0x49, 0x08, 0x60, 0x04, 0x22, 0x21, 0x1D,
+	0x14, 0x48, 0xFC, 0xF7, 0xB5, 0xFA, 0x11, 0x49,
+	0x06, 0x22, 0x89, 0x1F, 0x12, 0x48, 0xFC, 0xF7,
+	0xAF, 0xFA, 0x60, 0x7D, 0x22, 0x7D, 0x01, 0x02,
+	0x11, 0x43, 0x28, 0x46, 0x0A, 0x22, 0x50, 0x43,
+	0xFC, 0xF7, 0x92, 0xFA, 0x0D, 0x49, 0x08, 0x80,
+	0x70, 0xBD, 0x00, 0x00, 0xB8, 0x02, 0x00, 0x20,
+	0x2C, 0x00, 0x00, 0x20, 0x2D, 0x00, 0x00, 0x20,
+	0x2E, 0x00, 0x00, 0x20, 0x2F, 0x00, 0x00, 0x20,
+	0x42, 0x00, 0x00, 0x20, 0x44, 0x00, 0x00, 0x20,
+	0x3C, 0x00, 0x00, 0x20, 0x10, 0x7B, 0x00, 0x00,
+	0xD8, 0x06, 0x00, 0x20, 0xE0, 0x06, 0x00, 0x20,
+	0xE4, 0x06, 0x00, 0x20, 0x40, 0x00, 0x00, 0x20,
+	0x64, 0x24, 0x00, 0x20, 0x56, 0x49, 0x08, 0x70,
+	0xA9, 0xE0, 0x55, 0x48, 0x00, 0x78, 0x03, 0x00,
+	0xFC, 0xF7, 0xFC, 0xFA, 0x07, 0x05, 0x10, 0x1E,
+	0x18, 0x37, 0x48, 0x50, 0xA3, 0x00, 0x00, 0xF0,
+	0xCB, 0xF9, 0x01, 0x28, 0x02, 0xD1, 0x4E, 0x49,
+	0x08, 0x70, 0x02, 0xE0, 0x03, 0x20, 0x4C, 0x49,
+	0x08, 0x70, 0x93, 0xE0, 0x00, 0xF0, 0xD6, 0xF8,
+	0x01, 0x28, 0x02, 0xD1, 0x02, 0x20, 0x48, 0x49,
+	0x08, 0x70, 0x8B, 0xE0, 0x00, 0xF0, 0xB2, 0xF9,
+	0x00, 0x20, 0x45, 0x49, 0x08, 0x70, 0x85, 0xE0,
+	0xFF, 0xF7, 0x06, 0xF8, 0x01, 0x28, 0x10, 0xD1,
+	0xFF, 0xF7, 0xE0, 0xF8, 0x41, 0x48, 0x00, 0x78,
+	0x80, 0x21, 0x08, 0x42, 0x03, 0xD0, 0x01, 0x20,
+	0x3D, 0x49, 0x08, 0x70, 0x02, 0xE0, 0x04, 0x20,
+	0x3B, 0x49, 0x08, 0x70, 0xFC, 0xF7, 0x74, 0xFD,
+	0x02, 0xE0, 0x01, 0x20, 0x38, 0x49, 0x08, 0x70,
+	0x6C, 0xE0, 0x00, 0xF0, 0xBB, 0xF9, 0x01, 0x28,
+	0x0B, 0xD1, 0x36, 0x48, 0x00, 0x78, 0x80, 0x21,
+	0x08, 0x42, 0x03, 0xD0, 0x01, 0x20, 0x32, 0x49,
+	0x08, 0x70, 0x02, 0xE0, 0x05, 0x20, 0x30, 0x49,
+	0x08, 0x70, 0x5B, 0xE0, 0x00, 0xF0, 0x6A, 0xF8,
+	0x01, 0x28, 0x02, 0xD1, 0x06, 0x20, 0x2C, 0x49,
+	0x08, 0x70, 0x53, 0xE0, 0x2C, 0x48, 0x00, 0x78,
+	0x00, 0x28, 0x09, 0xD1, 0x01, 0x20, 0xFC, 0xF7,
+	0xEF, 0xFA, 0x00, 0x20, 0x29, 0x49, 0x08, 0x70,
+	0x01, 0x20, 0x25, 0x49, 0x08, 0x70, 0x45, 0xE0,
+	0xFF, 0xF7, 0x72, 0xF9, 0x01, 0x28, 0x2E, 0xD1,
+	0x24, 0x48, 0x00, 0x78, 0x00, 0x28, 0x2A, 0xD1,
+	0x01, 0xF0, 0xAA, 0xFA, 0x22, 0x48, 0xC0, 0x78,
+	0x01, 0x28, 0x04, 0xD0, 0x21, 0x48, 0x40, 0x78,
+	0xC0, 0x07, 0xC0, 0x0F, 0x2D, 0xD0, 0x01, 0x25,
+	0xAD, 0x07, 0xA9, 0x14, 0x28, 0x68, 0x08, 0x40,
+	0x00, 0x0A, 0x0A, 0x21, 0x08, 0x43, 0x19, 0x49,
+	0xC8, 0x70, 0xB4, 0x24, 0x1A, 0x48, 0x00, 0x78,
+	0xFF, 0x28, 0x05, 0xDA, 0x18, 0x48, 0x00, 0x78,
+	0x40, 0x1C, 0x17, 0x49, 0x08, 0x70, 0x02, 0xE0,
+	0x01, 0x20, 0x15, 0x49, 0x08, 0x70, 0x14, 0x48,
+	0x00, 0x78, 0x10, 0x49, 0x08, 0x70, 0x00, 0x20,
+	0xFC, 0xF7, 0xB6, 0xFA, 0x0D, 0xE0, 0x00, 0x2C,
+	0x05, 0xD0, 0x60, 0x1E, 0xC4, 0xB2, 0x01, 0x20,
+	0xFC, 0xF7, 0xBE, 0xFB, 0x05, 0xE0, 0x00, 0x20,
+	0x08, 0x49, 0x08, 0x70, 0x01, 0x20, 0xFC, 0xF7,
+	0xA7, 0xFA, 0x01, 0x20, 0x02, 0x49, 0x08, 0x70,
+	0x00, 0xE0, 0x00, 0xBF, 0x00, 0xBF, 0x54, 0xE7,
+	0x80, 0x02, 0x00, 0x20, 0x84, 0x04, 0x00, 0x20,
+	0xE4, 0x02, 0x00, 0x20, 0x90, 0x02, 0x00, 0x20,
+	0xA0, 0x04, 0x00, 0x20, 0x88, 0x02, 0x00, 0x20,
+	0x84, 0x02, 0x00, 0x20, 0x10, 0xB5, 0x18, 0x48,
+	0x00, 0x78, 0x03, 0x00, 0xFC, 0xF7, 0x42, 0xFA,
+	0x06, 0x28, 0x04, 0x0A, 0x16, 0x1C, 0x22, 0x28,
+	0x01, 0xF0, 0xD2, 0xF8, 0x02, 0x20, 0x12, 0x49,
+	0x08, 0x70, 0x1D, 0xE0, 0x01, 0xF0, 0xC0, 0xF8,
+	0x00, 0x28, 0x03, 0xD0, 0x03, 0x20, 0x0E, 0x49,
+	0x08, 0x70, 0x02, 0xE0, 0x05, 0x20, 0x0C, 0x49,
+	0x08, 0x70, 0x11, 0xE0, 0x01, 0xF0, 0xB0, 0xF8,
+	0x04, 0x20, 0x09, 0x49, 0x08, 0x70, 0x0B, 0xE0,
+	0x01, 0xF0, 0x14, 0xF9, 0x05, 0x20, 0x06, 0x49,
+	0x08, 0x70, 0x05, 0xE0, 0x01, 0xF0, 0x3E, 0xFA,
+	0x01, 0x20, 0x03, 0x49, 0x08, 0x70, 0x10, 0xBD,
+	0x00, 0xBF, 0x00, 0x20, 0xFB, 0xE7, 0x00, 0x00,
+	0x0C, 0x00, 0x00, 0x20, 0x70, 0xB5, 0x63, 0x4C,
+	0x20, 0x78, 0x00, 0x28, 0x41, 0xD0, 0x02, 0x20,
+	0xFC, 0xF7, 0x66, 0xFB, 0x62, 0x88, 0x21, 0x78,
+	0x5F, 0xA0, 0x00, 0xF0, 0x1F, 0xFD, 0x60, 0x88,
+	0x61, 0x4A, 0x00, 0x25, 0x90, 0x42, 0x02, 0xD1,
+	0xA5, 0x70, 0x28, 0x0A, 0xE0, 0x70, 0x20, 0x78,
+	0x5E, 0x4E, 0x5F, 0x49, 0x44, 0x28, 0x75, 0xD0,
+	0x13, 0xDC, 0x01, 0x22, 0x33, 0x28, 0x34, 0xD0,
+	0x08, 0xDC, 0x20, 0x28, 0x74, 0xD0, 0x30, 0x28,
+	0x28, 0xD0, 0x31, 0x28, 0x3D, 0xD0, 0x32, 0x28,
+	0x3B, 0xD1, 0x27, 0xE0, 0x34, 0x28, 0x39, 0xD0,
+	0x42, 0x28, 0x6F, 0xD0, 0x43, 0x28, 0x34, 0xD1,
+	0x90, 0xE0, 0x60, 0x28, 0x78, 0xD0, 0x07, 0xDC,
+	0x5D, 0x26, 0x5A, 0x28, 0x0B, 0xD0, 0x5C, 0x28,
+	0x0C, 0xD0, 0x5D, 0x28, 0x29, 0xD1, 0x0D, 0xE0,
+	0x62, 0x28, 0x65, 0xD0, 0x70, 0x28, 0x53, 0xD0,
+	0x71, 0x28, 0x7A, 0xD1, 0x71, 0xE0, 0xFF, 0xF7,
+	0xEF, 0xF8, 0x01, 0xE0, 0xFF, 0xF7, 0x02, 0xF9,
+	0x26, 0x70, 0x73, 0xE0, 0xC8, 0x78, 0x00, 0x28,
+	0x6F, 0xD0, 0x6F, 0xE0, 0x45, 0x49, 0x82, 0x20,
+	0x08, 0x70, 0x6B, 0xE0, 0x60, 0x88, 0x44, 0x49,
+	0x54, 0xE0, 0x60, 0x88, 0x08, 0x82, 0xA5, 0x70,
+	0x28, 0x0A, 0xE0, 0x70, 0x08, 0x8A, 0x04, 0x28,
+	0x5F, 0xD1, 0xFF, 0xF7, 0x8D, 0xF8, 0x00, 0x28,
+	0x02, 0xD1, 0x01, 0x20, 0xFC, 0xF7, 0xFC, 0xF9,
+	0x35, 0x70, 0x56, 0xE0, 0x60, 0x88, 0xC0, 0x07,
+	0x04, 0xD0, 0x48, 0x8A, 0x39, 0x4A, 0x80, 0x08,
+	0x80, 0x00, 0x15, 0xE0, 0x60, 0x88, 0x80, 0x07,
+	0x05, 0xD5, 0x48, 0x8A, 0x35, 0x4A, 0x80, 0x08,
+	0x80, 0x00, 0x52, 0x1C, 0x0C, 0xE0, 0x60, 0x88,
+	0xC0, 0x04, 0x48, 0x8A, 0x04, 0xD5, 0x80, 0x09,
+	0x30, 0x4A, 0x80, 0x01, 0xD2, 0x1C, 0x03, 0xE0,
+	0x80, 0x09, 0x80, 0x01, 0x01, 0x22, 0xD2, 0x03,
+	0x10, 0x43, 0x48, 0x82, 0x60, 0x88, 0xC0, 0x05,
+	0x03, 0xD5, 0x48, 0x8A, 0x08, 0x22, 0x10, 0x43,
+	0x48, 0x82, 0x0D, 0x72, 0xA5, 0x70, 0x28, 0x0A,
+	0xE0, 0x70, 0x2A, 0xE0, 0x30, 0xE0, 0x03, 0xE0,
+	0x25, 0x70, 0x25, 0x48, 0x05, 0x70, 0x25, 0xE0,
+	0x60, 0x88, 0x00, 0x28, 0xC8, 0x79, 0x03, 0xD0,
+	0x10, 0x43, 0x03, 0xE0, 0x20, 0xE0, 0x0B, 0xE0,
+	0x40, 0x08, 0x40, 0x00, 0xC8, 0x71, 0x18, 0xE0,
+	0xC8, 0x69, 0x02, 0x80, 0x60, 0x30, 0xC2, 0x83,
+	0x60, 0x88, 0x1C, 0x49, 0x08, 0x70, 0xE1, 0xE7,
+	0x48, 0x89, 0xA0, 0x70, 0x00, 0x0A, 0xE0, 0x70,
+	0x19, 0x48, 0x00, 0x68, 0x60, 0x30, 0x00, 0x78,
+	0x04, 0xE0, 0x71, 0x20, 0xA0, 0x70, 0x00, 0x20,
+	0xE0, 0x70, 0x0B, 0x20, 0x20, 0x71, 0x00, 0x0A,
+	0x60, 0x71, 0x25, 0x70, 0x01, 0x20, 0x70, 0xBD,
+	0x0A, 0x70, 0xFB, 0xE7, 0x0D, 0x70, 0xF9, 0xE7,
+	0x01, 0x20, 0xFC, 0xF7, 0x99, 0xF9, 0x35, 0x70,
+	0xF4, 0xE7, 0x00, 0x00, 0x88, 0x02, 0x00, 0x20,
+	0x43, 0x4D, 0x44, 0x3A, 0x30, 0x78, 0x25, 0x78,
+	0x2C, 0x30, 0x78, 0x25, 0x78, 0x0D, 0x0A, 0x00,
+	0xAA, 0x55, 0x00, 0x00, 0x90, 0x02, 0x00, 0x20,
+	0xE4, 0x02, 0x00, 0x20, 0x84, 0x04, 0x00, 0x20,
+	0x2B, 0x00, 0x00, 0x20, 0x01, 0x80, 0x00, 0x00,
+	0x80, 0x02, 0x00, 0x20, 0x86, 0x02, 0x00, 0x20,
+	0xB8, 0x02, 0x00, 0x20, 0x10, 0xB5, 0x01, 0x20,
+	0x00, 0x06, 0xFC, 0xF7, 0x64, 0xF9, 0x10, 0xBD,
+	0x10, 0xB5, 0x00, 0x24, 0x00, 0xF0, 0x46, 0xFE,
+	0x01, 0xF0, 0x0E, 0xF8, 0x01, 0x20, 0xFE, 0xF7,
+	0x11, 0xFF, 0x04, 0x46, 0x00, 0x2C, 0x04, 0xD1,
+	0x05, 0xA0, 0x00, 0xF0, 0x33, 0xFC, 0x00, 0x20,
+	0x10, 0xBD, 0x06, 0x49, 0x06, 0x48, 0x81, 0x70,
+	0x09, 0x0A, 0xC1, 0x70, 0x01, 0x20, 0xF7, 0xE7,
+	0x49, 0x4E, 0x49, 0x54, 0x20, 0x4E, 0x47, 0x0D,
+	0x0A, 0x00, 0x00, 0x00, 0xAA, 0x55, 0x00, 0x00,
+	0x88, 0x02, 0x00, 0x20, 0x10, 0xB5, 0x1C, 0x48,
+	0x00, 0x78, 0x01, 0x28, 0x02, 0xD0, 0x02, 0x28,
+	0x2E, 0xD1, 0x1E, 0xE0, 0xFE, 0xF7, 0xDE, 0xFF,
+	0xFD, 0xF7, 0x92, 0xFE, 0x17, 0x48, 0x00, 0x7A,
+	0x00, 0x28, 0x04, 0xD1, 0x01, 0x20, 0x16, 0x49,
+	0x08, 0x70, 0x00, 0x20, 0x10, 0xBD, 0xFC, 0xF7,
+	0xD9, 0xF9, 0xFE, 0xF7, 0x87, 0xFF, 0x13, 0x48,
+	0x00, 0x78, 0x80, 0x21, 0x08, 0x42, 0x04, 0xD0,
+	0x06, 0x20, 0x0F, 0x49, 0x08, 0x70, 0x00, 0x20,
+	0xF0, 0xE7, 0x02, 0x20, 0x0A, 0x49, 0x08, 0x70,
+	0x0E, 0xE0, 0xFE, 0xF7, 0xAB, 0xFE, 0x00, 0x28,
+	0x03, 0xD0, 0x01, 0x20, 0x06, 0x49, 0x08, 0x70,
+	0xE4, 0xE7, 0x01, 0x20, 0x04, 0x49, 0x08, 0x70,
+	0x06, 0x20, 0x05, 0x49, 0x08, 0x70, 0x00, 0xBF,
+	0x00, 0xBF, 0x00, 0x20, 0xDA, 0xE7, 0x00, 0x00,
+	0x81, 0x02, 0x00, 0x20, 0x50, 0x02, 0x00, 0x20,
+	0x80, 0x02, 0x00, 0x20, 0x86, 0x02, 0x00, 0x20,
+	0x0A, 0x07, 0x01, 0xD5, 0x40, 0x1C, 0x02, 0xE0,
+	0x4A, 0x07, 0x01, 0xD5, 0x40, 0x1E, 0x80, 0xB2,
+	0x07, 0x4A, 0xCB, 0x07, 0x12, 0x68, 0x02, 0xD0,
+	0x11, 0x7E, 0x40, 0x1A, 0x03, 0xE0, 0x89, 0x07,
+	0x02, 0xD5, 0x11, 0x7E, 0x08, 0x18, 0x80, 0xB2,
+	0x02, 0x49, 0x40, 0x00, 0x08, 0x5E, 0x70, 0x47,
+	0xB8, 0x02, 0x00, 0x20, 0x4C, 0x07, 0x00, 0x20,
+	0xF0, 0xB5, 0x41, 0x18, 0x15, 0x4C, 0x16, 0x4D,
+	0x8C, 0x46, 0x0E, 0xE0, 0x29, 0x78, 0x01, 0x23,
+	0x22, 0x88, 0x05, 0xE0, 0x1E, 0x46, 0x8E, 0x40,
+	0x16, 0x42, 0x09, 0xD0, 0x49, 0x1C, 0xC9, 0xB2,
+	0x02, 0x29, 0xF7, 0xD3, 0x40, 0x1C, 0x29, 0x70,
+	0xC0, 0xB2, 0x84, 0x45, 0xEE, 0xD8, 0xF0, 0xBD,
+	0x34, 0x27, 0x06, 0x46, 0x7E, 0x43, 0x0B, 0x4F,
+	0xF6, 0x19, 0x1F, 0x46, 0x31, 0x71, 0x8F, 0x40,
+	0x73, 0x72, 0x17, 0x43, 0x27, 0x80, 0x73, 0x71,
+	0x73, 0x89, 0x33, 0x82, 0xB2, 0x89, 0x72, 0x82,
+	0xB3, 0x85, 0xF2, 0x85, 0xFF, 0x22, 0x20, 0x36,
+	0x72, 0x75, 0xE3, 0xE7, 0x3E, 0x00, 0x00, 0x20,
+	0x31, 0x00, 0x00, 0x20, 0xA0, 0x04, 0x00, 0x20,
+	0xF0, 0xB5, 0x95, 0xB0, 0x00, 0x20, 0x0C, 0x90,
+	0x12, 0x90, 0x30, 0x21, 0x68, 0x46, 0xFC, 0xF7,
+	0x08, 0xF8, 0xC9, 0x48, 0x01, 0x78, 0x40, 0x78,
+	0x08, 0x18, 0x01, 0x06, 0x09, 0x0E, 0xC7, 0x48,
+	0x0E, 0x91, 0x01, 0xD0, 0x00, 0x27, 0x99, 0xE0,
+	0x01, 0x78, 0x00, 0x20, 0xFF, 0xF7, 0xB4, 0xFF,
+	0x15, 0xB0, 0xF0, 0xBD, 0x34, 0x20, 0x39, 0x46,
+	0x41, 0x43, 0xC0, 0x48, 0x00, 0x25, 0x08, 0x18,
+	0x0F, 0x90, 0x00, 0x79, 0xFF, 0x28, 0x7D, 0xD1,
+	0x38, 0x01, 0x12, 0x9E, 0x10, 0x90, 0x75, 0xE0,
+	0x10, 0x9A, 0x68, 0x46, 0x14, 0x18, 0x60, 0x19,
+	0xFF, 0x21, 0x13, 0x90, 0x41, 0x70, 0xA8, 0x00,
+	0x20, 0x18, 0xB7, 0x49, 0x11, 0x90, 0x41, 0x60,
+	0x0F, 0x98, 0x0C, 0x21, 0x41, 0x5E, 0x0A, 0x22,
+	0x8C, 0x46, 0x82, 0x5E, 0x06, 0x20, 0x31, 0x46,
+	0x41, 0x43, 0xAF, 0x48, 0x00, 0x23, 0x08, 0x18,
+	0xA0, 0x30, 0x02, 0x21, 0xC3, 0x5E, 0x41, 0x5E,
+	0x18, 0x46, 0x63, 0x46, 0x01, 0xF0, 0x52, 0xF8,
+	0x0D, 0x90, 0xAC, 0x48, 0x01, 0x88, 0x00, 0x29,
+	0x07, 0xD0, 0xAB, 0x48, 0x00, 0x88, 0x88, 0x42,
+	0x03, 0xD9, 0xFB, 0xF7, 0x7B, 0xFF, 0xC0, 0xB2,
+	0x00, 0xE0, 0x00, 0x20, 0x34, 0x21, 0xA2, 0x4A,
+	0x71, 0x43, 0x89, 0x18, 0xC9, 0x69, 0xA6, 0x4B,
+	0x4A, 0x00, 0xA4, 0x49, 0x1B, 0x78, 0x09, 0x78,
+	0x40, 0x1C, 0x59, 0x43, 0x41, 0x43, 0x50, 0x18,
+	0x9D, 0x49, 0x88, 0x42, 0x01, 0xD9, 0x08, 0x46,
+	0x03, 0xE0, 0xA0, 0x49, 0x88, 0x42, 0x00, 0xD2,
+	0x08, 0x46, 0x9F, 0x49, 0x09, 0x68, 0xB0, 0x31,
+	0x4A, 0x7A, 0x0B, 0x7A, 0x11, 0x02, 0x98, 0x4A,
+	0x19, 0x43, 0x12, 0x88, 0x91, 0x42, 0x04, 0xD2,
+	0x91, 0x49, 0x09, 0x78, 0x01, 0x29, 0x00, 0xD1,
+	0x91, 0x48, 0x0D, 0x99, 0x81, 0x42, 0x1B, 0xD8,
+	0x08, 0x46, 0x11, 0x99, 0x6D, 0x1C, 0x48, 0x60,
+	0x13, 0x98, 0xED, 0xB2, 0x46, 0x70, 0x01, 0x2D,
+	0x12, 0xD9, 0x68, 0x1E, 0x0D, 0xE0, 0x81, 0x00,
+	0x62, 0x18, 0x94, 0x46, 0x52, 0x68, 0x63, 0x58,
+	0x9A, 0x42, 0x05, 0xD2, 0x22, 0x18, 0x52, 0x78,
+	0x22, 0x54, 0x62, 0x46, 0x52, 0x68, 0x62, 0x50,
+	0x40, 0x1E, 0x00, 0x06, 0x00, 0x0E, 0xEE, 0xD1,
+	0x76, 0x1C, 0xF6, 0xB2, 0x0E, 0x98, 0x86, 0x42,
+	0x86, 0xD3, 0x00, 0xE0, 0x08, 0xE0, 0x10, 0x98,
+	0x69, 0x46, 0x0D, 0x54, 0x00, 0x2D, 0x03, 0xD0,
+	0x0C, 0x98, 0x40, 0x1C, 0xC0, 0xB2, 0x0C, 0x90,
+	0x7F, 0x1C, 0xFF, 0xB2, 0x77, 0x48, 0x00, 0x78,
+	0x84, 0x46, 0xB8, 0x42, 0x00, 0xD9, 0x65, 0xE7,
+	0x0C, 0x98, 0x00, 0x28, 0x7D, 0xD0, 0x01, 0x28,
+	0x59, 0xD9, 0x00, 0x24, 0x55, 0xE0, 0x60, 0x1C,
+	0xC3, 0xB2, 0x0C, 0x90, 0x4D, 0xE0, 0x26, 0x01,
+	0x69, 0x46, 0x72, 0x18, 0x1D, 0x01, 0x69, 0x18,
+	0x50, 0x78, 0x4F, 0x78, 0xB8, 0x42, 0x42, 0xD1,
+	0x50, 0x68, 0x4F, 0x68, 0xB8, 0x42, 0x1F, 0xD2,
+	0x00, 0x20, 0x0C, 0xE0, 0x0A, 0x18, 0x56, 0x78,
+	0x97, 0x78, 0x57, 0x70, 0x96, 0x70, 0x82, 0x00,
+	0x8A, 0x18, 0x97, 0x68, 0x56, 0x68, 0x40, 0x1C,
+	0x57, 0x60, 0xC0, 0xB2, 0x96, 0x60, 0x6A, 0x46,
+	0x52, 0x5D, 0x52, 0x1E, 0x82, 0x42, 0xED, 0xDC,
+	0x6A, 0x46, 0x50, 0x5D, 0x00, 0x28, 0x04, 0xD0,
+	0x40, 0x1E, 0x00, 0x06, 0x00, 0x0E, 0x50, 0x55,
+	0x21, 0xD1, 0xFF, 0x20, 0x48, 0x70, 0x1E, 0xE0,
+	0x00, 0x20, 0x0C, 0xE0, 0x11, 0x18, 0x4D, 0x78,
+	0x8F, 0x78, 0x4F, 0x70, 0x8D, 0x70, 0x81, 0x00,
+	0x51, 0x18, 0x8F, 0x68, 0x4D, 0x68, 0x40, 0x1C,
+	0x4F, 0x60, 0xC0, 0xB2, 0x8D, 0x60, 0x69, 0x46,
+	0x89, 0x5D, 0x49, 0x1E, 0x81, 0x42, 0xED, 0xDC,
+	0x69, 0x46, 0x88, 0x5D, 0x00, 0x28, 0x04, 0xD0,
+	0x40, 0x1E, 0x00, 0x06, 0x00, 0x0E, 0x88, 0x55,
+	0x01, 0xD1, 0xFF, 0x20, 0x50, 0x70, 0x5B, 0x1C,
+	0xDB, 0xB2, 0x9C, 0x45, 0xAF, 0xD8, 0x0C, 0x98,
+	0xC4, 0xB2, 0xA4, 0x45, 0xA7, 0xD8, 0x00, 0x26,
+	0x6E, 0xE0, 0x32, 0x01, 0x68, 0x46, 0x10, 0x18,
+	0x0D, 0x90, 0x40, 0x78, 0xFF, 0x28, 0x29, 0xD0,
+	0x34, 0x22, 0x50, 0x43, 0x3E, 0x4A, 0x12, 0x23,
+	0x85, 0x18, 0x34, 0x20, 0x70, 0x43, 0x44, 0x18,
+	0x10, 0x22, 0xAA, 0x5E, 0x22, 0x82, 0xEB, 0x5E,
+	0x63, 0x82, 0x0C, 0x21, 0x0A, 0x20, 0x61, 0x5E,
+	0x20, 0x5E, 0x00, 0xF0, 0x6B, 0xFF, 0x0C, 0x90,
+	0x3D, 0x48, 0x07, 0x68, 0x38, 0x46, 0xA7, 0x30,
+	0x00, 0xE0, 0x52, 0xE0, 0xFB, 0xF7, 0xE5, 0xFE,
+	0x01, 0x46, 0x0C, 0x98, 0x81, 0x42, 0x0E, 0xD2,
+	0x38, 0x46, 0xB0, 0x30, 0xC1, 0x79, 0x82, 0x79,
+	0x08, 0x02, 0x31, 0x49, 0x10, 0x43, 0x09, 0x88,
+	0x88, 0x42, 0x04, 0xD9, 0x01, 0x21, 0x30, 0x46,
+	0xFF, 0xF7, 0x82, 0xFE, 0x36, 0xE0, 0x0D, 0x98,
+	0x41, 0x68, 0x68, 0x7A, 0xC8, 0x28, 0x01, 0xD2,
+	0x40, 0x1C, 0x60, 0x72, 0xE8, 0x69, 0x40, 0x18,
+	0x40, 0x08, 0xE0, 0x61, 0x28, 0x79, 0x20, 0x71,
+	0x03, 0x21, 0x61, 0x71, 0x80, 0x21, 0x08, 0x43,
+	0x28, 0x71, 0xA8, 0x79, 0xA0, 0x71, 0xE8, 0x79,
+	0xE0, 0x71, 0x28, 0x7A, 0x20, 0x72, 0x38, 0x46,
+	0xA3, 0x30, 0xFB, 0xF7, 0xB6, 0xFE, 0x01, 0x46,
+	0x0C, 0x98, 0x81, 0x42, 0x03, 0xD2, 0x60, 0x89,
+	0x20, 0x82, 0xA0, 0x89, 0x60, 0x82, 0x60, 0x7A,
+	0x02, 0x28, 0x07, 0xD1, 0x30, 0x20, 0x00, 0x5D,
+	0x00, 0x28, 0x03, 0xD1, 0x60, 0x89, 0xA8, 0x85,
+	0xA0, 0x89, 0xE8, 0x85, 0xA8, 0x8D, 0xA0, 0x85,
+	0xE8, 0x8D, 0xE0, 0x85, 0x20, 0x35, 0x68, 0x7D,
+	0x20, 0x34, 0x60, 0x75, 0x76, 0x1C, 0xF6, 0xB2,
+	0x0C, 0x49, 0x08, 0x78, 0xB0, 0x42, 0x8C, 0xD8,
+	0x8E, 0xE6, 0x00, 0x24, 0x09, 0x4D, 0x0B, 0xE0,
+	0x34, 0x20, 0x60, 0x43, 0x40, 0x19, 0x00, 0x79,
+	0xFF, 0x28, 0x03, 0xD1, 0x01, 0x21, 0x20, 0x46,
+	0xFF, 0xF7, 0x36, 0xFE, 0x64, 0x1C, 0xE4, 0xB2,
+	0x28, 0x78, 0xA0, 0x42, 0xF0, 0xD8, 0x7B, 0xE6,
+	0x40, 0x05, 0x00, 0x20, 0xA0, 0x04, 0x00, 0x20,
+	0xFF, 0xFF, 0x07, 0x00, 0x36, 0x00, 0x00, 0x20,
+	0x34, 0x00, 0x00, 0x20, 0x2C, 0x00, 0x00, 0x20,
+	0x2D, 0x00, 0x00, 0x20, 0xFF, 0xFF, 0x00, 0x00,
+	0xB8, 0x02, 0x00, 0x20, 0xF0, 0xB5, 0x00, 0x24,
+	0x63, 0x49, 0xA9, 0xB0, 0x02, 0x20, 0x08, 0x70,
+	0x62, 0x48, 0x25, 0x46, 0x86, 0x78, 0x97, 0xE0,
+	0x34, 0x21, 0x60, 0x48, 0x71, 0x43, 0x09, 0x18,
+	0x08, 0x7A, 0x00, 0x28, 0x19, 0xD1, 0x5E, 0x4A,
+	0x0A, 0x20, 0x08, 0x5E, 0x12, 0x78, 0x90, 0x42,
+	0x11, 0xDB, 0x5C, 0x4F, 0x00, 0x23, 0xFB, 0x5E,
+	0x9A, 0x1A, 0x90, 0x42, 0x0B, 0xDC, 0x5A, 0x4A,
+	0x0C, 0x20, 0x08, 0x5E, 0x12, 0x78, 0x90, 0x42,
+	0x05, 0xDB, 0x58, 0x4F, 0x00, 0x23, 0xFB, 0x5E,
+	0x9A, 0x1A, 0x90, 0x42, 0x01, 0xDD, 0x02, 0x20,
+	0x08, 0x72, 0x88, 0x79, 0x4F, 0x4A, 0x00, 0x90,
+	0x00, 0x28, 0x06, 0xD0, 0x52, 0x4B, 0x52, 0x78,
+	0x1B, 0x78, 0x00, 0x20, 0x9C, 0x46, 0x96, 0x46,
+	0x66, 0xE0, 0x4F, 0x4F, 0x38, 0x78, 0x00, 0x19,
+	0x03, 0x28, 0x63, 0xD2, 0x01, 0x22, 0x8A, 0x71,
+	0x34, 0x22, 0x50, 0x43, 0xC0, 0x19, 0x09, 0x1D,
+	0x00, 0x1D, 0xFB, 0xF7, 0x05, 0xFE, 0x39, 0x78,
+	0x34, 0x22, 0x09, 0x19, 0x51, 0x43, 0x00, 0x20,
+	0xC9, 0x19, 0x48, 0x71, 0x45, 0x49, 0x64, 0x1C,
+	0x08, 0x70, 0x45, 0x49, 0xE4, 0xB2, 0x08, 0x70,
+	0x4C, 0xE0, 0x0A, 0x79, 0x80, 0x23, 0x1A, 0x43,
+	0x63, 0x46, 0x1B, 0x18, 0x34, 0x27, 0x7B, 0x43,
+	0x3D, 0x4F, 0xDB, 0x19, 0x1B, 0x79, 0x9A, 0x42,
+	0x3C, 0xD1, 0x3E, 0x48, 0x00, 0x78, 0x00, 0x28,
+	0x04, 0xD1, 0x0F, 0x46, 0x20, 0x37, 0x38, 0x7D,
+	0x46, 0x28, 0x02, 0xD9, 0x00, 0x20, 0x88, 0x71,
+	0x34, 0xE0, 0x00, 0x98, 0x40, 0x1C, 0xC0, 0xB2,
+	0x88, 0x71, 0x0A, 0x7E, 0x00, 0x2A, 0x05, 0xD0,
+	0x02, 0x28, 0x03, 0xD1, 0x4A, 0x89, 0x4A, 0x84,
+	0x8A, 0x89, 0x8A, 0x84, 0x28, 0x4A, 0x0B, 0x7A,
+	0x12, 0x78, 0x9A, 0x18, 0x90, 0x42, 0x0F, 0xD3,
+	0x00, 0x20, 0x88, 0x71, 0x83, 0x20, 0x38, 0x70,
+	0x34, 0x20, 0x68, 0x43, 0x01, 0xAA, 0x80, 0x18,
+	0x09, 0x1D, 0x00, 0x1D, 0x34, 0x22, 0xFB, 0xF7,
+	0xBF, 0xFD, 0x6D, 0x1C, 0xED, 0xB2, 0x11, 0xE0,
+	0x60, 0x46, 0x23, 0x4A, 0x00, 0x19, 0x34, 0x23,
+	0x58, 0x43, 0x80, 0x18, 0x09, 0x1D, 0x00, 0x1D,
+	0x1A, 0x46, 0xFB, 0xF7, 0xB1, 0xFD, 0x64, 0x1C,
+	0xE4, 0xB2, 0x03, 0xE0, 0x40, 0x1C, 0xC0, 0xB2,
+	0x86, 0x45, 0xB2, 0xD8, 0x76, 0x1C, 0xF6, 0xB2,
+	0x14, 0x48, 0x01, 0x78, 0xB1, 0x42, 0x00, 0xD9,
+	0x62, 0xE7, 0x06, 0x46, 0xB0, 0x78, 0x2A, 0x46,
+	0x41, 0x19, 0x31, 0x70, 0x34, 0x21, 0x48, 0x43,
+	0x4A, 0x43, 0x80, 0x19, 0x00, 0x1D, 0x02, 0xA9,
+	0xFB, 0xF7, 0x96, 0xFD, 0x10, 0x4D, 0x00, 0x20,
+	0x07, 0x46, 0x2B, 0x78, 0x0A, 0xE0, 0x19, 0x18,
+	0x34, 0x22, 0x51, 0x43, 0x4A, 0x19, 0x12, 0x79,
+	0x12, 0x06, 0x01, 0xD4, 0x89, 0x19, 0x8F, 0x71,
+	0x40, 0x1C, 0xC0, 0xB2, 0x71, 0x78, 0x81, 0x42,
+	0xF1, 0xD8, 0x74, 0x70, 0x29, 0xB0, 0xF0, 0xBD,
+	0x30, 0x00, 0x00, 0x20, 0xA0, 0x04, 0x00, 0x20,
+	0x2C, 0x00, 0x00, 0x20, 0x42, 0x00, 0x00, 0x20,
+	0x2D, 0x00, 0x00, 0x20, 0x44, 0x00, 0x00, 0x20,
+	0x40, 0x05, 0x00, 0x20, 0xCC, 0x00, 0x00, 0x20,
+	0xCD, 0x00, 0x00, 0x20, 0x73, 0x04, 0x00, 0x20,
+	0xF7, 0xB5, 0x00, 0x25, 0x84, 0xB0, 0x0C, 0x46,
+	0x16, 0x46, 0x61, 0x27, 0x5C, 0xE0, 0x25, 0x28,
+	0x54, 0xD1, 0x64, 0x1C, 0x00, 0x22, 0x20, 0x78,
+	0x13, 0x46, 0x00, 0x28, 0x57, 0xD0, 0x25, 0x28,
+	0x4C, 0xD0, 0x2D, 0x28, 0x01, 0xD1, 0x64, 0x1C,
+	0x01, 0x23, 0x02, 0x20, 0x21, 0x78, 0x30, 0x29,
+	0x07, 0xD1, 0x64, 0x1C, 0x03, 0x43, 0xF9, 0xE7,
+	0x0A, 0x21, 0x4A, 0x43, 0x30, 0x3A, 0x82, 0x18,
+	0x64, 0x1C, 0x20, 0x78, 0x01, 0x46, 0x30, 0x39,
+	0x09, 0x29, 0xF5, 0xD9, 0xC1, 0xB2, 0x73, 0x29,
+	0x0A, 0xD0, 0x64, 0x28, 0x10, 0xD0, 0x78, 0x28,
+	0x13, 0xD0, 0x58, 0x28, 0x19, 0xD0, 0x75, 0x28,
+	0x1F, 0xD0, 0x63, 0x28, 0x23, 0xD0, 0x2E, 0xE0,
+	0x02, 0xCE, 0x00, 0x29, 0x00, 0xD1, 0x1D, 0xA1,
+	0x04, 0x98, 0x00, 0xF0, 0x90, 0xF8, 0x0A, 0xE0,
+	0x68, 0x46, 0x8C, 0xC0, 0x02, 0xCE, 0x01, 0x23,
+	0x13, 0xE0, 0x68, 0x46, 0x8C, 0xC0, 0x08, 0xE0,
+	0x04, 0x98, 0x00, 0xF0, 0x3C, 0xF8, 0x45, 0x19,
+	0x19, 0xE0, 0x41, 0x20, 0x01, 0x93, 0x00, 0x92,
+	0x02, 0x90, 0x02, 0xCE, 0x00, 0x23, 0x10, 0x22,
+	0xF2, 0xE7, 0x68, 0x46, 0x8C, 0xC0, 0x02, 0xCE,
+	0x00, 0x23, 0x0A, 0x22, 0xEC, 0xE7, 0x02, 0xCE,
+	0x68, 0x46, 0x01, 0x73, 0x00, 0x21, 0x41, 0x73,
+	0x03, 0xA9, 0xD9, 0xE7, 0xC1, 0xB2, 0x04, 0x98,
+	0x00, 0xF0, 0x14, 0xF8, 0x6D, 0x1C, 0x64, 0x1C,
+	0x20, 0x78, 0x00, 0x28, 0x9F, 0xD1, 0x04, 0x98,
+	0x00, 0x28, 0x03, 0xD0, 0x04, 0x99, 0x00, 0x20,
+	0x09, 0x68, 0x08, 0x70, 0x28, 0x46, 0x07, 0xB0,
+	0xF0, 0xBD, 0x00, 0x00, 0x28, 0x6E, 0x75, 0x6C,
+	0x6C, 0x29, 0x00, 0x00, 0x10, 0xB5, 0x00, 0x28,
+	0x05, 0xD0, 0x02, 0x68, 0x11, 0x70, 0x01, 0x68,
+	0x49, 0x1C, 0x01, 0x60, 0x10, 0xBD, 0xC8, 0xB2,
+	0xFE, 0xF7, 0x64, 0xF9, 0x10, 0xBD, 0xFF, 0xB5,
+	0x00, 0x27, 0x83, 0xB0, 0x0C, 0x9D, 0x3E, 0x46,
+	0x08, 0x00, 0x3A, 0x46, 0x05, 0xD0, 0x00, 0x2B,
+	0x12, 0xD0, 0x05, 0x9B, 0x0A, 0x2B, 0x0B, 0xD0,
+	0x0E, 0xE0, 0x30, 0x20, 0x69, 0x46, 0x08, 0x70,
+	0x4A, 0x70, 0x2A, 0x46, 0x0D, 0x9B, 0x03, 0x98,
+	0x00, 0xF0, 0x31, 0xF8, 0x07, 0xB0, 0xF0, 0xBD,
+	0x00, 0x29, 0x01, 0xDA, 0x01, 0x27, 0x40, 0x42,
+	0x02, 0xAC, 0x69, 0x46, 0x03, 0x34, 0xCA, 0x72,
+	0x0A, 0xE0, 0x05, 0x99, 0xFB, 0xF7, 0x9A, 0xFC,
+	0x0A, 0x29, 0x02, 0xDB, 0x0E, 0x9A, 0x89, 0x18,
+	0x3A, 0x39, 0x30, 0x31, 0x64, 0x1E, 0x21, 0x70,
+	0x00, 0x28, 0xF2, 0xD1, 0x00, 0x2F, 0x0E, 0xD0,
+	0x00, 0x2D, 0x09, 0xD0, 0x0D, 0x98, 0x80, 0x07,
+	0x06, 0xD5, 0x2D, 0x21, 0x03, 0x98, 0xFF, 0xF7,
+	0xB9, 0xFF, 0x76, 0x1C, 0x6D, 0x1E, 0x02, 0xE0,
+	0x2D, 0x20, 0x64, 0x1E, 0x20, 0x70, 0x2A, 0x46,
+	0x21, 0x46, 0x0D, 0x9B, 0x03, 0x98, 0x00, 0xF0,
+	0x02, 0xF8, 0x80, 0x19, 0xCE, 0xE7, 0xFF, 0xB5,
+	0x00, 0x25, 0x20, 0x27, 0x81, 0xB0, 0x0E, 0x46,
+	0x14, 0x46, 0x00, 0x2A, 0x0E, 0xDD, 0x00, 0x20,
+	0x01, 0xE0, 0x40, 0x1C, 0x49, 0x1C, 0x0A, 0x78,
+	0x00, 0x2A, 0xFA, 0xD1, 0xA0, 0x42, 0x01, 0xDB,
+	0x00, 0x24, 0x00, 0xE0, 0x24, 0x1A, 0x98, 0x07,
+	0x00, 0xD5, 0x30, 0x27, 0xD8, 0x07, 0x06, 0xD0,
+	0x0D, 0xE0, 0x39, 0x46, 0x01, 0x98, 0xFF, 0xF7,
+	0x8D, 0xFF, 0x6D, 0x1C, 0x64, 0x1E, 0x00, 0x2C,
+	0xF7, 0xDC, 0x04, 0xE0, 0x01, 0x98, 0xFF, 0xF7,
+	0x85, 0xFF, 0x6D, 0x1C, 0x76, 0x1C, 0x31, 0x78,
+	0x00, 0x29, 0xF7, 0xD1, 0x05, 0xE0, 0x39, 0x46,
+	0x01, 0x98, 0xFF, 0xF7, 0x7B, 0xFF, 0x6D, 0x1C,
+	0x64, 0x1E, 0x00, 0x2C, 0xF7, 0xDC, 0x28, 0x46,
+	0x05, 0xB0, 0xF0, 0xBD, 0x0F, 0xB4, 0x10, 0xB5,
+	0x03, 0xAA, 0x00, 0x20, 0x02, 0x99, 0xFF, 0xF7,
+	0xF7, 0xFE, 0x10, 0xBC, 0x08, 0xBC, 0x04, 0xB0,
+	0x18, 0x47, 0x00, 0x00, 0xF0, 0xB5, 0xA9, 0xB0,
+	0x50, 0x49, 0x00, 0x24, 0x8C, 0x70, 0x9C, 0x21,
+	0x02, 0xA8, 0xFB, 0xF7, 0x6E, 0xFC, 0x00, 0x26,
+	0x66, 0xE0, 0x34, 0x21, 0x71, 0x43, 0x0D, 0x18,
+	0x29, 0x79, 0xFF, 0x29, 0x5E, 0xD0, 0x0A, 0x06,
+	0x36, 0xD5, 0x48, 0x4F, 0x00, 0x20, 0x3A, 0x78,
+	0x94, 0x46, 0x2E, 0xE0, 0x34, 0x22, 0x42, 0x43,
+	0xD2, 0x19, 0x12, 0x79, 0x80, 0x23, 0x1A, 0x43,
+	0x8A, 0x42, 0x24, 0xD1, 0x34, 0x21, 0x48, 0x43,
+	0x40, 0x49, 0x22, 0x46, 0x47, 0x18, 0x34, 0x20,
+	0x42, 0x43, 0x01, 0xA8, 0x10, 0x18, 0x00, 0x90,
+	0x39, 0x1D, 0x00, 0x1D, 0x34, 0x22, 0xFB, 0xF7,
+	0x2F, 0xFC, 0x28, 0x79, 0x64, 0x1C, 0x41, 0x06,
+	0x00, 0x98, 0x49, 0x0E, 0x01, 0x71, 0x00, 0x98,
+	0x69, 0x79, 0x41, 0x71, 0x78, 0x79, 0x80, 0x21,
+	0x08, 0x43, 0x78, 0x71, 0xA8, 0x79, 0xE4, 0xB2,
+	0x00, 0x28, 0x2F, 0xD1, 0x31, 0x48, 0x81, 0x78,
+	0x49, 0x1C, 0x81, 0x70, 0x2A, 0xE0, 0x40, 0x1C,
+	0xC0, 0xB2, 0x84, 0x45, 0xCE, 0xD8, 0x25, 0xE0,
+	0xA9, 0x79, 0x00, 0x29, 0x22, 0xD1, 0xE9, 0x79,
+	0x03, 0x29, 0x02, 0xD9, 0x00, 0x20, 0xE8, 0x71,
+	0x1C, 0xE0, 0x34, 0x20, 0x22, 0x46, 0x42, 0x43,
+	0x01, 0xA8, 0x17, 0x18, 0x29, 0x1D, 0x38, 0x1D,
+	0x34, 0x22, 0xFB, 0xF7, 0x01, 0xFC, 0x06, 0x20,
+	0x23, 0x49, 0x70, 0x43, 0x40, 0x18, 0xA0, 0x30,
+	0x01, 0x88, 0x79, 0x81, 0x40, 0x88, 0xB8, 0x81,
+	0x1E, 0x48, 0x64, 0x1C, 0x81, 0x78, 0xE4, 0xB2,
+	0x49, 0x1C, 0x81, 0x70, 0x28, 0x79, 0x80, 0x21,
+	0x08, 0x43, 0x28, 0x71, 0x76, 0x1C, 0xF6, 0xB2,
+	0x19, 0x48, 0x01, 0x78, 0x42, 0x78, 0x89, 0x18,
+	0xB1, 0x42, 0x92, 0xD8, 0x00, 0x25, 0x15, 0x4E,
+	0x1A, 0xE0, 0x34, 0x20, 0x68, 0x43, 0x81, 0x19,
+	0x48, 0x79, 0x02, 0x06, 0x03, 0xD5, 0x40, 0x06,
+	0x40, 0x0E, 0x48, 0x71, 0x0E, 0xE0, 0x02, 0x2C,
+	0x11, 0xD0, 0x00, 0x20, 0x88, 0x71, 0x34, 0x20,
+	0x60, 0x43, 0x01, 0xAA, 0x80, 0x18, 0x09, 0x1D,
+	0x00, 0x1D, 0x34, 0x22, 0xFB, 0xF7, 0xCC, 0xFB,
+	0x64, 0x1C, 0xE4, 0xB2, 0x6D, 0x1C, 0xED, 0xB2,
+	0x30, 0x78, 0xA8, 0x42, 0xE1, 0xD8, 0x34, 0x70,
+	0x34, 0x20, 0x44, 0x43, 0x22, 0x46, 0x02, 0xA9,
+	0x30, 0x1D, 0xFB, 0xF7, 0xBD, 0xFB, 0x29, 0xB0,
+	0xF0, 0xBD, 0x00, 0x00, 0xA0, 0x04, 0x00, 0x20,
+	0x40, 0x05, 0x00, 0x20, 0xF8, 0xB5, 0x59, 0x4C,
+	0x59, 0x4D, 0x00, 0x26, 0xE6, 0x70, 0x16, 0x21,
+	0x28, 0x1D, 0xFB, 0xF7, 0xC6, 0xFB, 0x57, 0x48,
+	0x57, 0x49, 0x00, 0x78, 0x09, 0x78, 0xC0, 0x01,
+	0x08, 0x43, 0xC1, 0xB2, 0x55, 0x48, 0x01, 0x70,
+	0x55, 0x48, 0x02, 0x78, 0x01, 0x20, 0x00, 0x2A,
+	0x04, 0xD0, 0xAA, 0x7E, 0x8A, 0x42, 0x01, 0xD0,
+	0xA9, 0x76, 0xE0, 0x70, 0x51, 0x49, 0x0A, 0x78,
+	0x00, 0x2A, 0x10, 0xD1, 0x50, 0x4A, 0x12, 0x78,
+	0x18, 0x2A, 0x0C, 0xD1, 0x4F, 0x4A, 0x12, 0x78,
+	0x03, 0x2A, 0x08, 0xD3, 0xA9, 0x78, 0xC9, 0x07,
+	0x04, 0xD1, 0xE0, 0x70, 0xA8, 0x70, 0x6E, 0x70,
+	0x4B, 0x48, 0x06, 0x70, 0xF8, 0xBD, 0xAA, 0x78,
+	0xD3, 0x07, 0x4A, 0x4A, 0x07, 0xD0, 0xE0, 0x70,
+	0xAE, 0x70, 0x6E, 0x70, 0x2D, 0x20, 0x08, 0x70,
+	0x03, 0x20, 0x10, 0x70, 0xF8, 0xBD, 0x11, 0x78,
+	0x00, 0x29, 0xFB, 0xD1, 0x21, 0x78, 0x00, 0x29,
+	0x07, 0xD0, 0xE0, 0x70, 0x69, 0x70, 0x38, 0x48,
+	0x00, 0x24, 0x40, 0x1C, 0x40, 0x4F, 0x00, 0x90,
+	0x63, 0xE0, 0x69, 0x78, 0x00, 0x29, 0x00, 0xD0,
+	0xE0, 0x70, 0x6E, 0x70, 0xF8, 0xBD, 0x34, 0x20,
+	0x30, 0x49, 0x60, 0x43, 0x45, 0x18, 0x20, 0x46,
+	0x0B, 0x26, 0x70, 0x43, 0x2A, 0x79, 0xC6, 0x18,
+	0x32, 0x71, 0x68, 0x89, 0x76, 0x1C, 0x30, 0x71,
+	0x00, 0x0A, 0x70, 0x71, 0xA8, 0x89, 0xB0, 0x71,
+	0x00, 0x0A, 0xF0, 0x71, 0xE8, 0x89, 0x30, 0x72,
+	0x00, 0x0A, 0x70, 0x72, 0x20, 0x20, 0x40, 0x5D,
+	0xC0, 0x07, 0x07, 0xD0, 0xE8, 0x8C, 0x30, 0x71,
+	0x00, 0x0A, 0x70, 0x71, 0x28, 0x8D, 0xB0, 0x71,
+	0x00, 0x0A, 0xF0, 0x71, 0x08, 0x78, 0x01, 0x28,
+	0x2D, 0xD1, 0xC8, 0x79, 0x00, 0x28, 0x2A, 0xD0,
+	0x00, 0x98, 0xC1, 0x79, 0x80, 0x79, 0x09, 0x06,
+	0x0B, 0x14, 0x03, 0x43, 0x00, 0x98, 0x41, 0x79,
+	0x00, 0x79, 0x09, 0x06, 0x0A, 0x14, 0x02, 0x43,
+	0x22, 0x48, 0x00, 0x21, 0x41, 0x5E, 0x00, 0x20,
+	0x38, 0x5E, 0x00, 0xF0, 0xCB, 0xFB, 0x20, 0x49,
+	0x0A, 0x68, 0xB0, 0x32, 0x51, 0x7B, 0x12, 0x7B,
+	0x09, 0x02, 0x11, 0x43, 0x81, 0x42, 0x0E, 0xD2,
+	0x00, 0x98, 0x41, 0x79, 0x02, 0x79, 0x08, 0x06,
+	0x00, 0x14, 0x10, 0x43, 0x38, 0x80, 0x00, 0x98,
+	0xC2, 0x79, 0x81, 0x79, 0x10, 0x06, 0x00, 0x14,
+	0x08, 0x43, 0x14, 0x49, 0x08, 0x80, 0xA8, 0x8A,
+	0xB0, 0x72, 0x00, 0x0A, 0xF0, 0x72, 0xE8, 0x8A,
+	0x30, 0x73, 0x00, 0x0A, 0x70, 0x73, 0x64, 0x1C,
+	0xE4, 0xB2, 0x03, 0x4B, 0x58, 0x78, 0xA0, 0x42,
+	0x9D, 0xD8, 0xF8, 0xBD, 0xA0, 0x04, 0x00, 0x20,
+	0x90, 0x02, 0x00, 0x20, 0x74, 0x04, 0x00, 0x20,
+	0x85, 0x02, 0x00, 0x20, 0x34, 0x01, 0x00, 0x20,
+	0x2B, 0x00, 0x00, 0x20, 0x72, 0x04, 0x00, 0x20,
+	0x8E, 0x02, 0x00, 0x20, 0x73, 0x04, 0x00, 0x20,
+	0x83, 0x02, 0x00, 0x20, 0x75, 0x04, 0x00, 0x20,
+	0x38, 0x00, 0x00, 0x20, 0x3A, 0x00, 0x00, 0x20,
+	0xB8, 0x02, 0x00, 0x20, 0xF7, 0xB5, 0x00, 0x20,
+	0x84, 0xB0, 0x03, 0x90, 0x32, 0x48, 0x00, 0x27,
+	0x01, 0x90, 0x38, 0x46, 0x56, 0xE0, 0x00, 0x24,
+	0x00, 0x29, 0x05, 0xD0, 0x40, 0x1E, 0x88, 0x42,
+	0x00, 0xD1, 0x02, 0x24, 0x00, 0x26, 0x45, 0xE0,
+	0x01, 0x24, 0xFB, 0xE7, 0x78, 0x00, 0x2B, 0x4A,
+	0x02, 0x90, 0x10, 0x5E, 0x06, 0x9B, 0x98, 0x42,
+	0x38, 0xDD, 0xC3, 0x07, 0x36, 0xD1, 0x01, 0x9B,
+	0x98, 0x42, 0x33, 0xDD, 0xA4, 0x07, 0xA4, 0x0F,
+	0x00, 0x2E, 0x0B, 0xD0, 0x49, 0x1E, 0xB1, 0x42,
+	0x01, 0xD1, 0x08, 0x20, 0x04, 0x43, 0x00, 0x25,
+	0x21, 0x48, 0x41, 0x5D, 0x20, 0x46, 0x08, 0x42,
+	0x0F, 0xD1, 0x01, 0xE0, 0x04, 0x20, 0xF5, 0xE7,
+	0x38, 0x46, 0xFF, 0xF7, 0x69, 0xFA, 0x1B, 0x4A,
+	0x02, 0x99, 0x04, 0x2D, 0x51, 0x5E, 0x02, 0xD2,
+	0x81, 0x42, 0x17, 0xDB, 0x01, 0xE0, 0x81, 0x42,
+	0x04, 0xDD, 0x6D, 0x1C, 0xED, 0xB2, 0x08, 0x2D,
+	0xE6, 0xD3, 0x01, 0xE0, 0x08, 0x2D, 0x0D, 0xD3,
+	0x12, 0x49, 0x02, 0x98, 0x08, 0x5E, 0x01, 0x90,
+	0x04, 0x99, 0x00, 0x98, 0x08, 0x70, 0x05, 0x98,
+	0x10, 0x49, 0x06, 0x70, 0x01, 0x20, 0x03, 0x90,
+	0x01, 0x98, 0x08, 0x80, 0x7F, 0x1C, 0x76, 0x1C,
+	0xBF, 0xB2, 0xF6, 0xB2, 0x0C, 0x48, 0x00, 0x68,
+	0x01, 0x7E, 0xB1, 0x42, 0xB6, 0xD8, 0x00, 0x98,
+	0x40, 0x1C, 0xC0, 0xB2, 0x08, 0x4A, 0x00, 0x90,
+	0x10, 0x68, 0x00, 0x99, 0x40, 0x7E, 0x88, 0x42,
+	0xA1, 0xD8, 0x03, 0x98, 0x07, 0xB0, 0xF0, 0xBD,
+	0x00, 0x80, 0xFF, 0xFF, 0x4C, 0x07, 0x00, 0x20,
+	0x8E, 0x5A, 0x00, 0x00, 0x28, 0x00, 0x00, 0x20,
+	0xB8, 0x02, 0x00, 0x20, 0x05, 0x48, 0x01, 0x78,
+	0x82, 0x29, 0x05, 0xD1, 0xC0, 0x79, 0x01, 0x28,
+	0x02, 0xD1, 0x03, 0x49, 0xFF, 0x20, 0x08, 0x70,
+	0x70, 0x47, 0x00, 0x00, 0x84, 0x04, 0x00, 0x20,
+	0xEC, 0x02, 0x00, 0x20, 0x01, 0x20, 0x02, 0x49,
+	0x08, 0x70, 0x02, 0x49, 0x08, 0x70, 0x70, 0x47,
+	0x0C, 0x00, 0x00, 0x20, 0x81, 0x02, 0x00, 0x20,
+	0x70, 0xB5, 0x24, 0x4D, 0x00, 0x21, 0x28, 0x78,
+	0x00, 0x28, 0x2C, 0xD1, 0x22, 0x4A, 0x23, 0x4B,
+	0x10, 0x80, 0x04, 0x22, 0x1A, 0x70, 0x22, 0x4B,
+	0x1A, 0x70, 0x22, 0x4A, 0x10, 0x70, 0x22, 0x4A,
+	0x10, 0x70, 0x22, 0x4A, 0x10, 0x70, 0x22, 0x4A,
+	0x10, 0x70, 0x22, 0x4A, 0x10, 0x70, 0x1A, 0xE0,
+	0x34, 0x20, 0x48, 0x43, 0x40, 0x19, 0x02, 0x46,
+	0x20, 0x32, 0x13, 0x78, 0x1C, 0x06, 0x06, 0xD5,
+	0x5B, 0x06, 0x5B, 0x0E, 0x13, 0x70, 0x43, 0x89,
+	0xC3, 0x84, 0x83, 0x89, 0x03, 0x85, 0x13, 0x78,
+	0x9C, 0x07, 0x06, 0xD5, 0xFD, 0x24, 0x23, 0x40,
+	0x13, 0x70, 0x42, 0x89, 0x42, 0x84, 0x82, 0x89,
+	0x82, 0x84, 0x49, 0x1C, 0xC9, 0xB2, 0x28, 0x78,
+	0x88, 0x42, 0xE1, 0xD8, 0x12, 0x4E, 0x00, 0x24,
+	0x30, 0x70, 0x68, 0x78, 0x70, 0x70, 0x20, 0x46,
+	0x34, 0x21, 0x48, 0x43, 0x41, 0x19, 0x80, 0x19,
+	0x09, 0x1D, 0x00, 0x1D, 0x34, 0x22, 0xFB, 0xF7,
+	0x13, 0xFA, 0x64, 0x1C, 0xE4, 0xB2, 0x03, 0x2C,
+	0xF1, 0xD3, 0x70, 0xBD, 0xA0, 0x04, 0x00, 0x20,
+	0x34, 0x00, 0x00, 0x20, 0x4A, 0x00, 0x00, 0x20,
+	0x4B, 0x00, 0x00, 0x20, 0x49, 0x00, 0x00, 0x20,
+	0x2A, 0x00, 0x00, 0x20, 0x47, 0x00, 0x00, 0x20,
+	0x46, 0x00, 0x00, 0x20, 0x48, 0x00, 0x00, 0x20,
+	0x40, 0x05, 0x00, 0x20, 0x02, 0x49, 0x00, 0x20,
+	0x08, 0x70, 0x02, 0x49, 0x08, 0x80, 0x70, 0x47,
+	0x31, 0x00, 0x00, 0x20, 0x3E, 0x00, 0x00, 0x20,
+	0xF0, 0xB5, 0x3B, 0x4F, 0x00, 0x20, 0x38, 0x70,
+	0x01, 0x21, 0x3A, 0x4C, 0xC9, 0x03, 0x21, 0x80,
+	0x39, 0x4B, 0x3B, 0x49, 0x18, 0x80, 0x39, 0x4E,
+	0x0A, 0x88, 0x0D, 0xE0, 0x43, 0x00, 0x00, 0x25,
+	0xF1, 0x5E, 0x65, 0x5F, 0xA9, 0x42, 0x02, 0xDD,
+	0x33, 0x4D, 0x21, 0x80, 0x28, 0x80, 0x49, 0x08,
+	0x49, 0x00, 0x40, 0x1C, 0xF1, 0x52, 0x80, 0xB2,
+	0x90, 0x42, 0xEF, 0xD3, 0x00, 0x20, 0x20, 0x5E,
+	0x41, 0x00, 0x41, 0x18, 0xCB, 0x17, 0x5B, 0x0F,
+	0x59, 0x18, 0xCB, 0x10, 0x2D, 0x49, 0x09, 0x68,
+	0x90, 0x31, 0x4C, 0x7B, 0x0D, 0x7B, 0x21, 0x02,
+	0x29, 0x43, 0x81, 0x42, 0x0C, 0xDA, 0x00, 0x20,
+	0x08, 0xE0, 0x41, 0x00, 0x71, 0x5E, 0x99, 0x42,
+	0x02, 0xDD, 0x39, 0x78, 0x49, 0x1C, 0x39, 0x70,
+	0x40, 0x1C, 0x80, 0xB2, 0x90, 0x42, 0xF4, 0xD3,
+	0x23, 0x48, 0x24, 0x4A, 0x01, 0x78, 0x21, 0x48,
+	0x00, 0x68, 0x80, 0x30, 0x00, 0x29, 0x81, 0x7B,
+	0x02, 0xD0, 0xCB, 0x00, 0xC9, 0x18, 0xC9, 0x08,
+	0x11, 0x70, 0xC3, 0x7B, 0x1E, 0x49, 0x00, 0x20,
+	0x08, 0x5E, 0x83, 0x42, 0x0C, 0xDA, 0x1D, 0x49,
+	0x09, 0x78, 0x02, 0x29, 0x08, 0xD2, 0x1C, 0x49,
+	0x0B, 0x78, 0x11, 0x78, 0x49, 0x1E, 0x8B, 0x42,
+	0x02, 0xDD, 0x11, 0x78, 0x49, 0x1C, 0x39, 0x70,
+	0x18, 0x49, 0x00, 0x23, 0xCB, 0x5E, 0x59, 0x42,
+	0x81, 0x42, 0x01, 0xDD, 0x00, 0x20, 0x38, 0x70,
+	0x3B, 0x78, 0x12, 0x78, 0x14, 0x49, 0x93, 0x42,
+	0x08, 0x78, 0x06, 0xD9, 0x03, 0x28, 0x01, 0xD2,
+	0x40, 0x1C, 0x00, 0xE0, 0x0C, 0x20, 0x08, 0x70,
+	0xF0, 0xBD, 0x00, 0x28, 0xFC, 0xD0, 0x3A, 0x78,
+	0x03, 0x2A, 0xF9, 0xD2, 0x40, 0x1E, 0xF6, 0xE7,
+	0x0E, 0x00, 0x00, 0x20, 0x24, 0x00, 0x00, 0x20,
+	0x32, 0x00, 0x00, 0x20, 0x4C, 0x07, 0x00, 0x20,
+	0x3C, 0x00, 0x00, 0x20, 0xB8, 0x02, 0x00, 0x20,
+	0x70, 0x04, 0x00, 0x20, 0x0D, 0x00, 0x00, 0x20,
+	0x7C, 0x04, 0x00, 0x20, 0xA0, 0x04, 0x00, 0x20,
+	0x8F, 0x02, 0x00, 0x20, 0x7A, 0x04, 0x00, 0x20,
+	0x73, 0x04, 0x00, 0x20, 0x10, 0xB5, 0x06, 0x4C,
+	0x00, 0x20, 0x20, 0x80, 0x60, 0x80, 0xFF, 0xF7,
+	0x5D, 0xFF, 0x04, 0x48, 0xA0, 0x80, 0xE0, 0x80,
+	0x03, 0x49, 0x03, 0x20, 0x08, 0x70, 0x10, 0xBD,
+	0x34, 0x00, 0x00, 0x20, 0xFF, 0x7F, 0x00, 0x00,
+	0x82, 0x02, 0x00, 0x20, 0x10, 0xB5, 0xFF, 0xF7,
+	0x4D, 0xFF, 0x08, 0x49, 0x5A, 0x20, 0x08, 0x70,
+	0x07, 0x49, 0x00, 0x20, 0x08, 0x70, 0x08, 0x48,
+	0x06, 0x49, 0x81, 0x80, 0xC1, 0x80, 0x07, 0x49,
+	0x03, 0x20, 0x08, 0x70, 0x06, 0x49, 0x01, 0x20,
+	0x08, 0x70, 0x10, 0xBD, 0xB5, 0x02, 0x00, 0x20,
+	0x73, 0x04, 0x00, 0x20, 0xFF, 0x7F, 0x00, 0x00,
+	0x34, 0x00, 0x00, 0x20, 0x82, 0x02, 0x00, 0x20,
+	0x83, 0x02, 0x00, 0x20, 0x70, 0xB5, 0x12, 0x4D,
+	0x06, 0x46, 0x2C, 0x78, 0xE0, 0x07, 0x07, 0xD0,
+	0x80, 0x22, 0x10, 0x49, 0x10, 0x48, 0xFB, 0xF7,
+	0x1F, 0xF9, 0x60, 0x08, 0x40, 0x00, 0x28, 0x70,
+	0x28, 0x78, 0x80, 0x07, 0x12, 0xD5, 0x00, 0x2E,
+	0x10, 0xD1, 0x0C, 0x49, 0x00, 0x20, 0x09, 0x4D,
+	0x09, 0x4C, 0x0E, 0x88, 0x08, 0xE0, 0x41, 0x00,
+	0x62, 0x5E, 0x6B, 0x5E, 0xD2, 0x18, 0x52, 0x10,
+	0x62, 0x52, 0x40, 0x1C, 0x6A, 0x52, 0x80, 0xB2,
+	0xB0, 0x42, 0xF4, 0xD3, 0x70, 0xBD, 0x00, 0x00,
+	0x82, 0x02, 0x00, 0x20, 0x4C, 0x07, 0x00, 0x20,
+	0xF4, 0x05, 0x00, 0x20, 0x3C, 0x00, 0x00, 0x20,
+	0x10, 0xB5, 0xFB, 0xF7, 0x11, 0xFF, 0x10, 0xBD,
+	0x10, 0xB5, 0x04, 0x48, 0x40, 0x78, 0x80, 0x06,
+	0x01, 0xD5, 0xFE, 0xF7, 0x53, 0xF9, 0xFB, 0xF7,
+	0x81, 0xFD, 0x10, 0xBD, 0x88, 0x02, 0x00, 0x20,
+	0xF8, 0xB5, 0x22, 0x49, 0x00, 0x20, 0x03, 0x46,
+	0x1F, 0x4A, 0x0C, 0x88, 0x06, 0xE0, 0x41, 0x00,
+	0x55, 0x5E, 0x00, 0x2D, 0x00, 0xDA, 0x53, 0x52,
+	0x40, 0x1C, 0xC0, 0xB2, 0xA0, 0x42, 0xF6, 0xD3,
+	0x1C, 0x48, 0x1B, 0x4D, 0x01, 0x78, 0x1D, 0x48,
+	0x1B, 0x4A, 0x04, 0x68, 0x2E, 0x78, 0x20, 0x46,
+	0xB0, 0x30, 0x12, 0x88, 0x02, 0x2E, 0x0F, 0xD0,
+	0xC3, 0x79, 0x84, 0x79, 0x18, 0x02, 0x20, 0x43,
+	0x90, 0x42, 0x01, 0xD8, 0x01, 0x29, 0x01, 0xD9,
+	0x03, 0x20, 0x28, 0x70, 0x00, 0x20, 0xFF, 0xF7,
+	0x99, 0xFF, 0xFF, 0xF7, 0xD1, 0xFE, 0xF8, 0xBD,
+	0x90, 0x34, 0x66, 0x7B, 0x27, 0x7B, 0x34, 0x02,
+	0x3C, 0x43, 0x0F, 0x4E, 0x00, 0x27, 0xF7, 0x5F,
+	0xBC, 0x42, 0x05, 0xDA, 0x00, 0x29, 0x03, 0xD1,
+	0x0C, 0x49, 0x09, 0x78, 0x01, 0x29, 0xE7, 0xD0,
+	0xC1, 0x79, 0x84, 0x79, 0x08, 0x02, 0x20, 0x43,
+	0x90, 0x42, 0xE3, 0xD2, 0x2B, 0x70, 0xE1, 0xE7,
+	0x4C, 0x07, 0x00, 0x20, 0x3C, 0x00, 0x00, 0x20,
+	0x82, 0x02, 0x00, 0x20, 0x40, 0x05, 0x00, 0x20,
+	0x34, 0x00, 0x00, 0x20, 0xB8, 0x02, 0x00, 0x20,
+	0x78, 0x04, 0x00, 0x20, 0x84, 0x04, 0x00, 0x20,
+	0x10, 0xB5, 0xFE, 0xF7, 0xAD, 0xFD, 0xFF, 0xF7,
+	0x49, 0xFF, 0x10, 0xBD, 0xF8, 0xB5, 0x00, 0x26,
+	0xFB, 0xF7, 0xE8, 0xFB, 0x83, 0x4C, 0x20, 0x78,
+	0x01, 0x28, 0x2A, 0xD1, 0x60, 0x7A, 0x0A, 0x28,
+	0x27, 0xD2, 0x81, 0x4F, 0x81, 0x49, 0xB8, 0x5F,
+	0x88, 0x42, 0x22, 0xD0, 0x80, 0x4D, 0x0C, 0x23,
+	0x0A, 0x22, 0xE3, 0x5E, 0xA2, 0x5E, 0xA9, 0x5F,
+	0x00, 0xF0, 0x1C, 0xF9, 0x7D, 0x49, 0x0A, 0x68,
+	0xB0, 0x32, 0x51, 0x7B, 0x12, 0x7B, 0x09, 0x02,
+	0x11, 0x43, 0x81, 0x42, 0x11, 0xD3, 0xB8, 0x5F,
+	0x41, 0x00, 0x40, 0x18, 0x0A, 0x21, 0x61, 0x5E,
+	0x40, 0x18, 0x80, 0x10, 0x60, 0x81, 0xA9, 0x5F,
+	0x4A, 0x00, 0x89, 0x18, 0x0C, 0x22, 0xA2, 0x5E,
+	0x89, 0x18, 0x89, 0x10, 0xA1, 0x81, 0x60, 0x84,
+	0xA1, 0x84, 0x00, 0x25, 0xCC, 0xE0, 0x70, 0x4F,
+	0x34, 0x23, 0x00, 0x20, 0x6B, 0x43, 0x39, 0x78,
+	0x9C, 0x18, 0x0A, 0xE0, 0x22, 0x79, 0x80, 0x23,
+	0x1A, 0x43, 0x34, 0x23, 0x43, 0x43, 0xDB, 0x19,
+	0x1B, 0x79, 0x9A, 0x42, 0x03, 0xD0, 0x40, 0x1C,
+	0xC0, 0xB2, 0x81, 0x42, 0xF2, 0xD8, 0x81, 0x42,
+	0x7E, 0xD9, 0x34, 0x21, 0x48, 0x43, 0xC1, 0x19,
+	0x14, 0x20, 0x08, 0x5E, 0x14, 0x22, 0x43, 0x00,
+	0xC0, 0x18, 0xA2, 0x5E, 0x01, 0x23, 0x10, 0x18,
+	0x80, 0x03, 0xDB, 0x03, 0xC0, 0x18, 0x00, 0x14,
+	0xA0, 0x82, 0x16, 0x20, 0x08, 0x5E, 0x16, 0x22,
+	0x47, 0x00, 0xC0, 0x19, 0xA2, 0x5E, 0x27, 0x46,
+	0x10, 0x18, 0x80, 0x03, 0xC0, 0x18, 0x00, 0x14,
+	0xE0, 0x82, 0x20, 0x37, 0x20, 0x31, 0x0A, 0x22,
+	0x38, 0x46, 0xFB, 0xF7, 0x25, 0xF8, 0x38, 0x78,
+	0x40, 0x07, 0x07, 0xD5, 0x00, 0x20, 0x53, 0x49,
+	0x38, 0x70, 0x08, 0x70, 0x52, 0x49, 0x08, 0x70,
+	0x52, 0x49, 0x08, 0x70, 0x24, 0x23, 0x22, 0x22,
+	0x0C, 0x21, 0x0A, 0x20, 0xE3, 0x5E, 0xA2, 0x5E,
+	0x61, 0x5E, 0x20, 0x5E, 0x00, 0xF0, 0xB2, 0xF8,
+	0x39, 0x78, 0xCA, 0x07, 0x67, 0xD0, 0x62, 0x7A,
+	0x2D, 0x2A, 0x09, 0xD2, 0x48, 0x4A, 0x12, 0x78,
+	0x00, 0x2A, 0x05, 0xD0, 0x22, 0x7E, 0x00, 0x2A,
+	0x02, 0xD1, 0x02, 0x22, 0x11, 0x43, 0x39, 0x70,
+	0x40, 0x49, 0x0A, 0x68, 0x92, 0x21, 0x89, 0x5C,
+	0x81, 0x42, 0x01, 0xD2, 0x01, 0x26, 0x16, 0xE0,
+	0x10, 0x46, 0xB0, 0x30, 0x41, 0x7A, 0x03, 0x7A,
+	0x08, 0x02, 0x18, 0x43, 0x81, 0x08, 0x3E, 0x48,
+	0x00, 0x88, 0x81, 0x42, 0x07, 0xD2, 0x39, 0x48,
+	0x00, 0x78, 0x00, 0x28, 0x03, 0xD1, 0x3B, 0x48,
+	0x00, 0x78, 0x00, 0x28, 0x36, 0xD0, 0x00, 0x20,
+	0xB8, 0x72, 0x00, 0x2E, 0x4E, 0xD0, 0x38, 0x78,
+	0x04, 0x21, 0x08, 0x43, 0x38, 0x70, 0xB0, 0x32,
+	0x50, 0x7A, 0x12, 0x7A, 0x00, 0x02, 0x10, 0x43,
+	0x31, 0x4A, 0x79, 0x7D, 0x12, 0x88, 0x90, 0x42,
+	0x40, 0xD9, 0x26, 0x23, 0xE3, 0x5E, 0x30, 0x20,
+	0x2F, 0x4A, 0x9F, 0x01, 0x41, 0x43, 0x88, 0x18,
+	0x87, 0x60, 0xC7, 0x60, 0x2D, 0x4F, 0x00, 0xE0,
+	0x34, 0xE0, 0xBF, 0x88, 0x0A, 0x2F, 0x01, 0xD8,
+	0x0A, 0x23, 0xE3, 0x5E, 0x53, 0x50, 0x0A, 0x21,
+	0x61, 0x5E, 0x41, 0x60, 0x28, 0x21, 0x61, 0x5E,
+	0x8A, 0x01, 0x82, 0x61, 0xC2, 0x61, 0x25, 0x4A,
+	0x92, 0x88, 0x0A, 0x2A, 0x01, 0xD8, 0x0C, 0x21,
+	0x61, 0x5E, 0x01, 0x61, 0x0C, 0x21, 0x61, 0x5E,
+	0x41, 0x61, 0x1B, 0xE0, 0xB8, 0x7A, 0x40, 0x1C,
+	0xC0, 0xB2, 0xB8, 0x72, 0x02, 0x28, 0xC4, 0xD9,
+	0x00, 0x20, 0xB8, 0x72, 0xAA, 0xE7, 0x00, 0x29,
+	0x10, 0xD1, 0x12, 0x49, 0x09, 0x68, 0x80, 0x31,
+	0x89, 0x7C, 0x49, 0x08, 0x81, 0x42, 0x11, 0xD9,
+	0x78, 0x78, 0x40, 0x1C, 0xC0, 0xB2, 0x78, 0x70,
+	0x0A, 0x28, 0x03, 0xD9, 0x00, 0x20, 0x78, 0x70,
+	0x83, 0x20, 0x38, 0x70, 0x6D, 0x1C, 0xED, 0xB2,
+	0x04, 0x4A, 0x10, 0x78, 0xA8, 0x42, 0x00, 0xD9,
+	0x2D, 0xE7, 0xF8, 0xBD, 0x00, 0x20, 0x78, 0x70,
+	0x02, 0x20, 0xF2, 0xE7, 0xA0, 0x04, 0x00, 0x20,
+	0x38, 0x00, 0x00, 0x20, 0xFF, 0x7F, 0x00, 0x00,
+	0x3A, 0x00, 0x00, 0x20, 0xB8, 0x02, 0x00, 0x20,
+	0x40, 0x05, 0x00, 0x20, 0x75, 0x04, 0x00, 0x20,
+	0x47, 0x00, 0x00, 0x20, 0x46, 0x00, 0x00, 0x20,
+	0x34, 0x00, 0x00, 0x20, 0x48, 0x00, 0x00, 0x20,
+	0x38, 0x01, 0x00, 0x20, 0x84, 0x04, 0x00, 0x20,
+	0x10, 0xB5, 0x03, 0x48, 0x00, 0x78, 0x00, 0x06,
+	0x01, 0xD4, 0xFF, 0xF7, 0xAF, 0xFB, 0x10, 0xBD,
+	0x86, 0x02, 0x00, 0x20, 0x10, 0xB5, 0xFF, 0xF7,
+	0x07, 0xFD, 0x10, 0xBD, 0x10, 0x1A, 0x59, 0x1A,
+	0x40, 0x43, 0x49, 0x43, 0xC2, 0x17, 0xCB, 0x17,
+	0x08, 0x18, 0x53, 0x41, 0x00, 0x22, 0xD2, 0x43,
+	0x00, 0x21, 0x12, 0x1A, 0x99, 0x41, 0x01, 0xD2,
+	0x00, 0x20, 0xC0, 0x43, 0x70, 0x47, 0x05, 0x01,
+	0x09, 0x04, 0x08, 0x02, 0x06, 0x0A, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x40, 0x10, 0x03, 0x42, 0x88,
+	0x0C, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x09, 0x00, 0x50, 0x1F, 0x00, 0x00, 0x00,
+	0x04, 0x03, 0x00, 0x50, 0x64, 0x00, 0x00, 0x00,
+	0x00, 0x03, 0x00, 0x50, 0x77, 0x00, 0x01, 0x48,
+	0x44, 0x09, 0x00, 0x50, 0x39, 0x5A, 0x5B, 0x00,
+	0x10, 0x06, 0x00, 0x50, 0x00, 0x00, 0x06, 0x07,
+	0x00, 0x06, 0x00, 0x50, 0x00, 0x00, 0x00, 0x78,
+	0x08, 0x06, 0x00, 0x50, 0x0C, 0x30, 0x00, 0x00,
+	0x28, 0x06, 0x00, 0x50, 0x06, 0x00, 0x00, 0x00,
+	0x2C, 0x06, 0x00, 0x50, 0x0A, 0x66, 0x00, 0x00,
+	0x30, 0x06, 0x00, 0x50, 0xCC, 0x02, 0x00, 0x20,
+	0x34, 0x06, 0x00, 0x50, 0x00, 0x20, 0x00, 0x00,
+	0x44, 0x00, 0x00, 0x50, 0x01, 0x00, 0x00, 0x00,
+	0x1C, 0x00, 0x00, 0x50, 0x50, 0x71, 0x02, 0x00,
+	0x20, 0x00, 0x00, 0x50, 0x24, 0x29, 0x00, 0x00,
+	0x14, 0x00, 0x00, 0x40, 0x04, 0x04, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x40, 0x14, 0x33, 0x43, 0xC8,
+	0x0C, 0x00, 0x00, 0x40, 0x29, 0x0A, 0x00, 0x00,
+	0x04, 0x00, 0x00, 0x40, 0x10, 0x32, 0x00, 0x00,
+	0x1C, 0x0E, 0x00, 0x50, 0x03, 0x00, 0x00, 0x00,
+	0x54, 0x00, 0x00, 0x50, 0x14, 0x07, 0x00, 0x00,
+	0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
+	0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
+	0x2C, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00,
+	0x58, 0x10, 0x00, 0x50, 0x01, 0x00, 0x00, 0x00,
+	0x04, 0x11, 0x00, 0x50, 0x78, 0x11, 0x00, 0x00,
+	0x0C, 0x11, 0x00, 0x50, 0x01, 0x00, 0x00, 0x00,
+	0x10, 0x11, 0x00, 0x50, 0x78, 0x01, 0x00, 0x00,
+	0x14, 0x11, 0x00, 0x50, 0xC8, 0x03, 0x60, 0x00,
+	0x4C, 0x00, 0x00, 0x50, 0x31, 0x00, 0x00, 0x00,
+	0x3C, 0x10, 0x00, 0x50, 0x00, 0x00, 0x10, 0x00,
+	0xB4, 0x10, 0x00, 0x50, 0x00, 0x26, 0x31, 0x00,
+	0xC0, 0x10, 0x00, 0x50, 0x33, 0x03, 0x33, 0x03,
+	0xC4, 0x10, 0x00, 0x50, 0x33, 0x03, 0x33, 0x03,
+	0xC8, 0x10, 0x00, 0x50, 0x0C, 0x0A, 0x00, 0x00,
+	0xCC, 0x10, 0x00, 0x50, 0x1A, 0x00, 0x00, 0x00,
+	0xD0, 0x10, 0x00, 0x50, 0x03, 0x19, 0x19, 0x00,
+	0xF0, 0x11, 0x00, 0x50, 0x12, 0x00, 0x00, 0x00,
+	0xEC, 0x11, 0x00, 0x50, 0x5C, 0x00, 0x00, 0x00,
+	0xF4, 0x11, 0x00, 0x50, 0x01, 0x00, 0x01, 0x00,
+	0x2C, 0x10, 0x00, 0x50, 0x10, 0x00, 0x90, 0x00,
+	0x30, 0x10, 0x00, 0x50, 0x20, 0x0C, 0x90, 0x00,
+	0x34, 0x10, 0x00, 0x50, 0x30, 0x0C, 0x30, 0x0C,
+	0x38, 0x10, 0x00, 0x50, 0xFF, 0x0F, 0x00, 0x00,
+	0x7C, 0x10, 0x00, 0x50, 0x88, 0x88, 0xFE, 0x88,
+	0x80, 0x10, 0x00, 0x50, 0x88, 0xFF, 0x00, 0x00,
+	0x84, 0x10, 0x00, 0x50, 0x55, 0x55, 0x55, 0x55,
+	0x88, 0x10, 0x00, 0x50, 0x55, 0x55, 0x55, 0x55,
+	0x8C, 0x10, 0x00, 0x50, 0x55, 0x55, 0x55, 0x55,
+	0xE8, 0x10, 0x00, 0x50, 0x3F, 0x16, 0x3F, 0x15,
+	0x04, 0x00, 0x00, 0x40, 0x04, 0x00, 0x00, 0x00,
+	0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
+	0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
+	0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
+	0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
+	0x78, 0x5C, 0x00, 0x00, 0x04, 0x00, 0x00, 0x20,
+	0x08, 0x00, 0x00, 0x00, 0x5C, 0x0A, 0x00, 0x00,
+	0x80, 0x5C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x20,
+	0xC4, 0x0C, 0x00, 0x00, 0x6A, 0x0A, 0x00, 0x00,
+	0xFF, 0x00, 0x00, 0x00, 0x78, 0x7F, 0x00, 0x00,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0x6E, 0x53, 0x58, 0x91,
+};
+const unsigned char u8_rad_para_30[] = {
+	0xA1, 0x00, 0x03, 0xF3, 0x02, 0x02, 0x00, 0x01,
+	0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x40, 0x01, 0x68, 0x01, 0x44, 0x01, 0x44, 0x01,
+	0x05, 0x05, 0x00, 0x00, 0x01, 0x05, 0x41, 0x06,
+	0x0A, 0x0B, 0x41, 0x0F, 0x10, 0x14, 0x41, 0x15,
+	0x02, 0x07, 0x41, 0x16, 0x41, 0x41, 0x41, 0x41,
+	0x41, 0x41, 0x41, 0x04, 0x03, 0x09, 0x41, 0x08,
+	0x0E, 0x0D, 0x41, 0x13, 0x12, 0x18, 0x41, 0x17,
+	0x0C, 0x11, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+	0x41, 0x41, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x0C, 0x3E, 0x0C, 0x3E, 0x08, 0x08, 0x02, 0x02,
+	0x14, 0x03, 0x08, 0x08, 0x01, 0x01, 0xC8, 0x00,
+	0x1A, 0x01, 0x08, 0x14, 0x82, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5E, 0x01,
+	0xA0, 0x00, 0x64, 0x00, 0x4A, 0x00, 0x0A, 0x78,
+	0x0F, 0x08, 0x1A, 0x4A, 0x4A, 0x3C, 0x3C, 0x00,
+	0x00, 0x00, 0x6A, 0x00, 0x38, 0x00, 0x1C, 0x00,
+	0x06, 0x32, 0x69, 0xE3, 0x0D, 0x00, 0x00, 0x72,
+	0x45, 0x00, 0x00, 0x06, 0x00, 0xA0, 0x00, 0x78,
+	0x00, 0x64, 0xDB, 0x13, 0xB5, 0x0C, 0x2D, 0x03,
+	0xCB, 0x00, 0x14, 0x0A, 0xB1, 0x01, 0xF1, 0x05,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x55, 0xAA, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xD6, 0x9D, 0x10, 0x3D, 0xCB, 0x25, 0xBF, 0xD8,
+};
+const unsigned char  u8_rad_testfw_30[] = {
+	0x60, 0x0E, 0x00, 0x20, 0xB1, 0x08, 0x00, 0x00,
+	0xB9, 0x08, 0x00, 0x00, 0x95, 0x08, 0x00, 0x00,
+	0xBD, 0x08, 0x00, 0x00, 0xBF, 0x08, 0x00, 0x00,
+	0xC1, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0xC3, 0x08, 0x00, 0x00,
+	0xC5, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0xC7, 0x08, 0x00, 0x00, 0x81, 0x0F, 0x00, 0x00,
+	0xE1, 0x0C, 0x00, 0x00, 0x01, 0x0A, 0x00, 0x00,
+	0xCB, 0x08, 0x00, 0x00, 0x61, 0x0E, 0x00, 0x00,
+	0xA1, 0x0D, 0x00, 0x00, 0x41, 0x0A, 0x00, 0x00,
+	0xCB, 0x08, 0x00, 0x00, 0x01, 0x0B, 0x00, 0x00,
+	0xCB, 0x08, 0x00, 0x00, 0x41, 0x0B, 0x00, 0x00,
+	0xE1, 0x0A, 0x00, 0x00, 0xA1, 0x0A, 0x00, 0x00,
+	0xC1, 0x0A, 0x00, 0x00, 0x81, 0x0A, 0x00, 0x00,
+	0xCB, 0x08, 0x00, 0x00, 0xCB, 0x08, 0x00, 0x00,
+	0x03, 0x48, 0x85, 0x46, 0x00, 0xF0, 0x80, 0xF8,
+	0x00, 0x48, 0x00, 0x47, 0xC1, 0x35, 0x00, 0x00,
+	0x60, 0x0E, 0x00, 0x20, 0x04, 0x20, 0x71, 0x46,
+	0x08, 0x42, 0x02, 0xD0, 0xEF, 0xF3, 0x09, 0x80,
+	0x01, 0xE0, 0xEF, 0xF3, 0x08, 0x80, 0x71, 0x46,
+	0x00, 0x4A, 0x10, 0x47, 0xD9, 0x34, 0x00, 0x00,
+	0x06, 0x48, 0x80, 0x47, 0x06, 0x48, 0x00, 0x47,
+	0xFE, 0xE7, 0xFE, 0xE7, 0xFE, 0xE7, 0xFE, 0xE7,
+	0xFE, 0xE7, 0xFE, 0xE7, 0xFE, 0xE7, 0xFE, 0xE7,
+	0xFE, 0xE7, 0xFE, 0xE7, 0x15, 0x0A, 0x00, 0x00,
+	0x81, 0x08, 0x00, 0x00, 0x30, 0xB5, 0x0B, 0x46,
+	0x01, 0x46, 0x00, 0x20, 0x20, 0x22, 0x01, 0x24,
+	0x09, 0xE0, 0x0D, 0x46, 0xD5, 0x40, 0x9D, 0x42,
+	0x05, 0xD3, 0x1D, 0x46, 0x95, 0x40, 0x49, 0x1B,
+	0x25, 0x46, 0x95, 0x40, 0x40, 0x19, 0x15, 0x46,
+	0x52, 0x1E, 0x00, 0x2D, 0xF1, 0xDC, 0x30, 0xBD,
+	0x70, 0xB5, 0x00, 0x24, 0x25, 0x46, 0x00, 0x28,
+	0x01, 0xDA, 0x01, 0x24, 0x40, 0x42, 0x00, 0x29,
+	0x01, 0xDA, 0x01, 0x25, 0x49, 0x42, 0xFF, 0xF7,
+	0xDD, 0xFF, 0xAC, 0x42, 0x00, 0xD0, 0x40, 0x42,
+	0x00, 0x2C, 0x00, 0xD0, 0x49, 0x42, 0x70, 0xBD,
+	0x03, 0x46, 0x0B, 0x43, 0x9B, 0x07, 0x03, 0xD0,
+	0x09, 0xE0, 0x08, 0xC9, 0x12, 0x1F, 0x08, 0xC0,
+	0x04, 0x2A, 0xFA, 0xD2, 0x03, 0xE0, 0x0B, 0x78,
+	0x49, 0x1C, 0x03, 0x70, 0x40, 0x1C, 0x52, 0x1E,
+	0xF9, 0xD2, 0x70, 0x47, 0xD2, 0xB2, 0x01, 0xE0,
+	0x02, 0x70, 0x40, 0x1C, 0x49, 0x1E, 0xFB, 0xD2,
+	0x70, 0x47, 0x00, 0x22, 0xF6, 0xE7, 0x10, 0xB5,
+	0x04, 0x46, 0x08, 0x46, 0x11, 0x46, 0x02, 0x46,
+	0x20, 0x46, 0xFF, 0xF7, 0xEF, 0xFF, 0x20, 0x46,
+	0x10, 0xBD, 0x00, 0x1D, 0x03, 0x21, 0x40, 0x1E,
+	0x03, 0x78, 0x12, 0x02, 0x1A, 0x43, 0x49, 0x1E,
+	0xF9, 0xD5, 0x10, 0x46, 0x70, 0x47, 0x00, 0x00,
+	0x06, 0x4C, 0x01, 0x25, 0x06, 0x4E, 0x05, 0xE0,
+	0x20, 0x46, 0xE3, 0x68, 0x07, 0xC8, 0x2B, 0x43,
+	0x98, 0x47, 0x10, 0x34, 0xB4, 0x42, 0xF7, 0xD3,
+	0xFF, 0xF7, 0x72, 0xFF, 0x10, 0x3B, 0x00, 0x00,
+	0x30, 0x3B, 0x00, 0x00, 0xC1, 0x06, 0xC9, 0x0E,
+	0x01, 0x20, 0x88, 0x40, 0x01, 0x49, 0x08, 0x60,
+	0x70, 0x47, 0x00, 0x00, 0x00, 0xE1, 0x00, 0xE0,
+	0x0B, 0x49, 0x10, 0xB5, 0x88, 0x42, 0x01, 0xD9,
+	0x01, 0x20, 0x10, 0xBD, 0x01, 0x02, 0x09, 0x0A,
+	0x08, 0x48, 0x49, 0x1E, 0x41, 0x61, 0x08, 0x49,
+	0x07, 0x23, 0xCA, 0x69, 0x12, 0x02, 0x12, 0x0A,
+	0x04, 0x04, 0x22, 0x43, 0xCA, 0x61, 0x00, 0x21,
+	0x81, 0x61, 0x03, 0x61, 0x08, 0x46, 0x10, 0xBD,
+	0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xE0, 0x00, 0xE0,
+	0x04, 0xED, 0x00, 0xE0, 0x70, 0x47, 0x00, 0x00,
+	0x03, 0x49, 0x02, 0x20, 0x08, 0x60, 0x02, 0x49,
+	0x80, 0x39, 0x08, 0x60, 0x70, 0x47, 0x00, 0x00,
+	0x80, 0xE1, 0x00, 0xE0, 0x62, 0xB6, 0x02, 0x48,
+	0x00, 0x21, 0x01, 0x60, 0x70, 0x47, 0x00, 0x00,
+	0x0C, 0x00, 0x00, 0x40, 0x30, 0xB4, 0x74, 0x46,
+	0x64, 0x1E, 0x25, 0x78, 0x64, 0x1C, 0xAB, 0x42,
+	0x00, 0xD2, 0x1D, 0x46, 0x63, 0x5D, 0x5B, 0x00,
+	0xE3, 0x18, 0x30, 0xBC, 0x18, 0x47, 0x00, 0x00,
+	0x05, 0x48, 0x01, 0x68, 0x49, 0x1C, 0x01, 0x60,
+	0x01, 0x20, 0x80, 0x07, 0x01, 0x6A, 0x03, 0x22,
+	0xD2, 0x03, 0x11, 0x43, 0x01, 0x62, 0x70, 0x47,
+	0x34, 0x00, 0x00, 0x20, 0x02, 0xE0, 0x08, 0xC8,
+	0x12, 0x1F, 0x08, 0xC1, 0x00, 0x2A, 0xFA, 0xD1,
+	0x70, 0x47, 0x00, 0x20, 0x01, 0xE0, 0x01, 0xC1,
+	0x12, 0x1F, 0x00, 0x2A, 0xFB, 0xD1, 0x70, 0x47,
+	0x10, 0xB5, 0x00, 0xF0, 0x55, 0xF8, 0x10, 0xBD,
+	0x01, 0x20, 0x80, 0x07, 0x41, 0x6A, 0x82, 0x13,
+	0x11, 0x43, 0x41, 0x62, 0x41, 0x6A, 0xC2, 0x13,
+	0x11, 0x43, 0x41, 0x62, 0x70, 0x47, 0x10, 0xB5,
+	0x02, 0xF0, 0xD4, 0xF8, 0x10, 0xBD, 0x00, 0x00,
+	0x03, 0x48, 0x01, 0x68, 0x49, 0x1C, 0x01, 0x60,
+	0x02, 0x49, 0x01, 0x20, 0xC8, 0x60, 0x70, 0x47,
+	0x5C, 0x01, 0x00, 0x20, 0x00, 0x03, 0x00, 0x50,
+	0x10, 0xB5, 0x00, 0xF0, 0xE7, 0xFA, 0x10, 0xBD,
+	0x70, 0x47, 0x00, 0x00, 0x04, 0x49, 0x06, 0x22,
+	0x00, 0x28, 0x08, 0x68, 0x01, 0xD0, 0x10, 0x43,
+	0x00, 0xE0, 0x90, 0x43, 0x08, 0x60, 0x70, 0x47,
+	0x00, 0x09, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00,
+	0x70, 0x47, 0x00, 0x00, 0x03, 0x48, 0x02, 0x49,
+	0x41, 0x60, 0x03, 0x49, 0x81, 0x60, 0x70, 0x47,
+	0x1F, 0x1F, 0x5F, 0x1F, 0x00, 0x10, 0x00, 0x50,
+	0x1F, 0x1F, 0x1F, 0x1F, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0xB5, 0x07, 0x48, 0x01, 0x69, 0x02, 0x29,
+	0x06, 0xD1, 0x01, 0x61, 0x05, 0x49, 0x8A, 0x78,
+	0x00, 0x2A, 0x02, 0xD0, 0x02, 0xF0, 0x92, 0xFA,
+	0x00, 0xBD, 0x01, 0x20, 0xC8, 0x70, 0x00, 0xBD,
+	0x00, 0x02, 0x00, 0x50, 0xE0, 0x05, 0x00, 0x20,
+	0x01, 0x21, 0x89, 0x07, 0x0A, 0x15, 0x00, 0x28,
+	0x48, 0x69, 0x02, 0xD0, 0x10, 0x43, 0x48, 0x61,
+	0x70, 0x47, 0x90, 0x43, 0xFB, 0xE7, 0x00, 0x00,
+	0x30, 0xB5, 0x1E, 0x4B, 0x58, 0x68, 0x99, 0x68,
+	0x00, 0x28, 0x01, 0xDA, 0xDA, 0x04, 0x5A, 0x60,
+	0x1B, 0x4A, 0x15, 0x68, 0x01, 0x24, 0x05, 0x40,
+	0x00, 0x2D, 0x02, 0xD1, 0x50, 0x68, 0x08, 0x42,
+	0x0F, 0xD0, 0x18, 0x48, 0x04, 0x70, 0xFF, 0xF7,
+	0xBD, 0xFF, 0x14, 0x72, 0x16, 0x48, 0x00, 0x78,
+	0x00, 0x28, 0x06, 0xD0, 0x18, 0x68, 0x01, 0x21,
+	0x00, 0x09, 0x00, 0x01, 0x89, 0x02, 0x08, 0x43,
+	0x18, 0x60, 0x58, 0x68, 0x80, 0x02, 0x02, 0xD5,
+	0x01, 0x20, 0x40, 0x05, 0x58, 0x60, 0x58, 0x68,
+	0x00, 0x04, 0x03, 0xD5, 0x01, 0x20, 0xC0, 0x03,
+	0x58, 0x60, 0x94, 0x72, 0x58, 0x68, 0x80, 0x00,
+	0x03, 0xD5, 0x01, 0x20, 0x40, 0x07, 0x58, 0x60,
+	0xD4, 0x72, 0x58, 0x68, 0x40, 0x00, 0x03, 0xD5,
+	0x01, 0x20, 0x80, 0x07, 0x58, 0x60, 0x94, 0x72,
+	0x30, 0xBD, 0x00, 0x00, 0x00, 0x10, 0x00, 0x50,
+	0x20, 0x00, 0x00, 0x20, 0x39, 0x00, 0x00, 0x20,
+	0x31, 0x00, 0x00, 0x20, 0x01, 0x28, 0x05, 0xD0,
+	0x02, 0x28, 0x05, 0xD0, 0x04, 0x28, 0x06, 0xD0,
+	0x00, 0x20, 0x70, 0x47, 0x03, 0x48, 0x70, 0x47,
+	0x02, 0x48, 0xC0, 0x30, 0x70, 0x47, 0x02, 0x48,
+	0x70, 0x47, 0x00, 0x00, 0x78, 0x7C, 0x00, 0x00,
+	0xF8, 0x7D, 0x00, 0x00, 0x10, 0xB5, 0x00, 0xF0,
+	0x67, 0xF8, 0x10, 0xBD, 0x00, 0x00, 0x00, 0x00,
+	0x70, 0x47, 0x00, 0x00, 0xF8, 0xB5, 0x06, 0x46,
+	0x0D, 0x46, 0x30, 0x23, 0xFF, 0xF7, 0xDE, 0xFF,
+	0x04, 0x00, 0x28, 0xD0, 0x19, 0x4F, 0x00, 0x2A,
+	0x21, 0xD0, 0x00, 0x20, 0xFF, 0xF7, 0x52, 0xFF,
+	0x02, 0xE0, 0x98, 0x00, 0x29, 0x58, 0x21, 0x50,
+	0x18, 0x46, 0x5B, 0x1E, 0xDB, 0xB2, 0x00, 0x28,
+	0xF7, 0xD1, 0x38, 0x68, 0x01, 0x88, 0x31, 0x43,
+	0x01, 0x80, 0x11, 0x49, 0x09, 0x68, 0xCA, 0x79,
+	0x8B, 0x79, 0x11, 0x02, 0x19, 0x43, 0x41, 0x80,
+	0xC1, 0x21, 0x89, 0x00, 0x0D, 0x48, 0x00, 0xF0,
+	0x1D, 0xF8, 0x0D, 0x49, 0xC8, 0x63, 0x01, 0x20,
+	0xFF, 0xF7, 0x34, 0xFF, 0x0C, 0xE0, 0x38, 0x68,
+	0x00, 0x88, 0x30, 0x40, 0x03, 0xD1, 0xF8, 0xBD,
+	0x98, 0x00, 0x21, 0x58, 0x29, 0x50, 0x18, 0x46,
+	0x5B, 0x1E, 0xDB, 0xB2, 0x00, 0x28, 0xF7, 0xD1,
+	0x01, 0x20, 0xF8, 0xBD, 0x08, 0x00, 0x00, 0x20,
+	0x98, 0x01, 0x00, 0x20, 0x78, 0x7C, 0x00, 0x00,
+	0x40, 0x7F, 0x00, 0x00, 0x70, 0xB5, 0x41, 0x18,
+	0x49, 0x1E, 0x64, 0x24, 0x09, 0x04, 0x0B, 0x4D,
+	0x01, 0x43, 0x69, 0x63, 0xE8, 0x68, 0x81, 0x21,
+	0x09, 0x06, 0x08, 0x43, 0xE8, 0x60, 0x02, 0xE0,
+	0x01, 0x20, 0xFF, 0xF7, 0xA3, 0xFF, 0xE8, 0x68,
+	0xC0, 0x01, 0x04, 0xD5, 0x20, 0x46, 0x64, 0x1E,
+	0xA4, 0xB2, 0x00, 0x28, 0xF4, 0xD1, 0xA8, 0x6B,
+	0x70, 0xBD, 0x00, 0x00, 0x40, 0x09, 0x00, 0x50,
+	0x03, 0x49, 0x0A, 0x68, 0x10, 0x18, 0x0A, 0x68,
+	0x90, 0x42, 0xFC, 0xD1, 0x70, 0x47, 0x00, 0x00,
+	0x5C, 0x01, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00,
+	0x70, 0xB5, 0x01, 0x25, 0xAD, 0x07, 0xE8, 0x68,
+	0x40, 0x08, 0x40, 0x00, 0xE8, 0x60, 0x19, 0x48,
+	0x41, 0x68, 0x01, 0x26, 0x49, 0x07, 0x00, 0x29,
+	0x25, 0xDA, 0x42, 0x68, 0x04, 0x21, 0x0A, 0x43,
+	0x42, 0x60, 0x15, 0x48, 0x00, 0x68, 0x15, 0x4C,
+	0x40, 0x05, 0x40, 0x0F, 0x20, 0x73, 0x20, 0x7B,
+	0x06, 0x28, 0x00, 0xD1, 0x21, 0x73, 0x20, 0x7B,
+	0x02, 0x28, 0x18, 0xD0, 0x20, 0x7B, 0x06, 0x28,
+	0x05, 0xD8, 0x20, 0x7B, 0x04, 0x28, 0x02, 0xD3,
+	0x01, 0x20, 0x01, 0xF0, 0x31, 0xF9, 0x20, 0x7B,
+	0x0B, 0x49, 0x09, 0x78, 0x88, 0x42, 0x06, 0xD0,
+	0x0A, 0x48, 0x01, 0x78, 0x31, 0x43, 0x01, 0x70,
+	0x09, 0x49, 0x81, 0x20, 0x08, 0x70, 0xE8, 0x68,
+	0x30, 0x43, 0xE8, 0x60, 0x70, 0xBD, 0x00, 0x20,
+	0xEB, 0xE7, 0x00, 0x00, 0x40, 0x00, 0x00, 0x50,
+	0x00, 0x11, 0x00, 0x50, 0x20, 0x00, 0x00, 0x20,
+	0x39, 0x01, 0x00, 0x20, 0x30, 0x00, 0x00, 0x20,
+	0x20, 0x08, 0x00, 0x20, 0x70, 0xB5, 0x05, 0x46,
+	0x06, 0x4C, 0x01, 0xF0, 0x7B, 0xFC, 0x00, 0x21,
+	0x8A, 0x00, 0x49, 0x1C, 0xA0, 0x58, 0xC9, 0xB2,
+	0xA8, 0x50, 0x18, 0x29, 0xF8, 0xD3, 0x01, 0x20,
+	0x70, 0xBD, 0x00, 0x00, 0x00, 0x20, 0x00, 0x50,
+	0x01, 0x20, 0x80, 0x07, 0x41, 0x68, 0x82, 0x14,
+	0x11, 0x43, 0x41, 0x60, 0x70, 0x47, 0x00, 0x00,
+	0x10, 0xB5, 0x14, 0x48, 0x80, 0x69, 0x40, 0x04,
+	0x23, 0xD5, 0x13, 0x4C, 0xE0, 0x68, 0xA2, 0x68,
+	0x02, 0x40, 0xE0, 0x68, 0xA1, 0x68, 0x88, 0x43,
+	0xE0, 0x60, 0x10, 0x07, 0x03, 0xD5, 0x08, 0x20,
+	0xE0, 0x60, 0x02, 0xF0, 0x85, 0xFD, 0x50, 0x07,
+	0x0E, 0xD5, 0x04, 0x20, 0xE0, 0x60, 0x02, 0xF0,
+	0x7F, 0xFD, 0x00, 0xF0, 0xCB, 0xF8, 0x00, 0x28,
+	0x06, 0xD1, 0x08, 0x48, 0x00, 0x78, 0x00, 0x28,
+	0x02, 0xD0, 0x01, 0x20, 0xFF, 0xF7, 0x48, 0xFE,
+	0xFF, 0x20, 0xF3, 0x30, 0xE0, 0x60, 0x01, 0x20,
+	0xE0, 0x60, 0x10, 0xBD, 0x00, 0x09, 0x00, 0x50,
+	0x00, 0x05, 0x00, 0x50, 0x90, 0x02, 0x00, 0x20,
+	0x70, 0xB5, 0x0C, 0x49, 0x00, 0x20, 0x0C, 0x4B,
+	0x89, 0x25, 0x0C, 0x68, 0xAD, 0x00, 0x21, 0x18,
+	0xC9, 0x7E, 0x41, 0x29, 0x08, 0xD0, 0x42, 0x00,
+	0x99, 0x5A, 0x49, 0x05, 0x49, 0x0D, 0x99, 0x52,
+	0x4E, 0x05, 0x01, 0xD5, 0x49, 0x1B, 0x99, 0x52,
+	0x40, 0x1C, 0xC0, 0xB2, 0x30, 0x28, 0xEE, 0xD3,
+	0x01, 0x20, 0x70, 0xBD, 0x98, 0x01, 0x00, 0x20,
+	0xAC, 0x03, 0x00, 0x20, 0x01, 0x21, 0x89, 0x07,
+	0x00, 0xB5, 0x8A, 0x14, 0x00, 0x28, 0x08, 0x68,
+	0x04, 0xD0, 0x10, 0x43, 0x08, 0x60, 0xFF, 0xF7,
+	0x9F, 0xFF, 0x00, 0xBD, 0x90, 0x43, 0x08, 0x60,
+	0x00, 0xBD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0xF8, 0xB5, 0x33, 0x48, 0x80, 0x69, 0x40, 0x04,
+	0x61, 0xD5, 0x32, 0x4D, 0xA8, 0x6A, 0xE9, 0x68,
+	0xAC, 0x68, 0x0C, 0x40, 0xE9, 0x68, 0xAA, 0x68,
+	0x91, 0x43, 0xE9, 0x60, 0x2E, 0x4E, 0xC0, 0x07,
+	0x15, 0xD0, 0x68, 0x69, 0x89, 0x27, 0xC0, 0xB2,
+	0xEF, 0x60, 0xAA, 0x6A, 0x01, 0x21, 0x0A, 0x43,
+	0xAA, 0x62, 0x0B, 0x28, 0x03, 0xD2, 0x31, 0x70,
+	0x00, 0xF0, 0x92, 0xFD, 0x03, 0xE0, 0x00, 0x21,
+	0x31, 0x70, 0x00, 0xF0, 0x69, 0xFD, 0xBC, 0x43,
+	0x01, 0x20, 0xFF, 0xF7, 0xE5, 0xFD, 0x23, 0x48,
+	0xA1, 0x04, 0x04, 0xD5, 0x29, 0x6A, 0x81, 0x43,
+	0x29, 0x62, 0x41, 0x14, 0xE9, 0x60, 0xE1, 0x04,
+	0x05, 0xD5, 0x29, 0x6A, 0x81, 0x43, 0x29, 0x62,
+	0x01, 0x20, 0x00, 0x03, 0xE8, 0x60, 0xA0, 0x05,
+	0x02, 0xD5, 0x01, 0x20, 0x40, 0x02, 0xE8, 0x60,
+	0x20, 0x07, 0x09, 0xD5, 0x30, 0x78, 0x01, 0x28,
+	0x02, 0xD1, 0x0A, 0x20, 0x00, 0xF0, 0x48, 0xFD,
+	0x64, 0x08, 0x64, 0x00, 0x09, 0x20, 0xE8, 0x60,
+	0x60, 0x07, 0x17, 0xD5, 0x30, 0x78, 0x01, 0x28,
+	0x02, 0xD1, 0x0A, 0x20, 0x00, 0xF0, 0x3C, 0xFD,
+	0x05, 0x20, 0xE8, 0x60, 0xA8, 0x69, 0x0E, 0x49,
+	0x88, 0x42, 0x08, 0xD0, 0xA9, 0x69, 0x0C, 0x48,
+	0x40, 0x1C, 0x81, 0x42, 0x03, 0xD0, 0xA9, 0x69,
+	0xC0, 0x1C, 0x81, 0x42, 0x02, 0xD1, 0x01, 0x20,
+	0xFF, 0xF7, 0xAA, 0xFD, 0xFF, 0x20, 0xF3, 0x30,
+	0xE8, 0x60, 0x01, 0x20, 0xE8, 0x60, 0xF8, 0xBD,
+	0x00, 0x09, 0x00, 0x50, 0x00, 0x06, 0x00, 0x50,
+	0xCC, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x40,
+	0x88, 0x02, 0x00, 0x20, 0x10, 0xB5, 0x02, 0x46,
+	0x07, 0x49, 0x60, 0x32, 0x11, 0x80, 0x00, 0x21,
+	0x51, 0x80, 0x4C, 0x00, 0x53, 0x88, 0x04, 0x5B,
+	0x49, 0x1C, 0x1B, 0x19, 0xC9, 0xB2, 0x53, 0x80,
+	0x31, 0x29, 0xF6, 0xD3, 0x10, 0xBD, 0x00, 0x00,
+	0xAA, 0x55, 0x00, 0x00, 0x01, 0x20, 0x80, 0x07,
+	0x40, 0x69, 0x40, 0x05, 0x01, 0xD5, 0x01, 0x20,
+	0x70, 0x47, 0x00, 0x20, 0x70, 0x47, 0x00, 0x00,
+	0x02, 0x48, 0x01, 0x68, 0x49, 0x1C, 0x01, 0x60,
+	0x70, 0x47, 0x00, 0x00, 0x1C, 0x08, 0x00, 0x20,
+	0x01, 0x20, 0x01, 0x49, 0x08, 0x70, 0x70, 0x47,
+	0xD3, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00,
+	0x05, 0x20, 0x00, 0x07, 0x82, 0x69, 0x0C, 0x49,
+	0x00, 0x2A, 0x07, 0xDA, 0x82, 0x69, 0xC3, 0x00,
+	0x92, 0x00, 0x92, 0x08, 0xD2, 0x18, 0x82, 0x61,
+	0x01, 0x22, 0x4A, 0x72, 0x82, 0x69, 0x52, 0x00,
+	0x08, 0xD5, 0x82, 0x69, 0x01, 0x23, 0x92, 0x00,
+	0x92, 0x08, 0x9B, 0x07, 0xD2, 0x18, 0x82, 0x61,
+	0x00, 0x20, 0x48, 0x72, 0x70, 0x47, 0x00, 0x00,
+	0x20, 0x08, 0x00, 0x20, 0x70, 0xB5, 0x1D, 0x4D,
+	0x68, 0x78, 0x00, 0x06, 0x2A, 0xD5, 0x05, 0x20,
+	0xFF, 0xF7, 0x04, 0xFE, 0x01, 0x20, 0xFF, 0xF7,
+	0x43, 0xFD, 0x68, 0x78, 0x40, 0x06, 0x40, 0x0E,
+	0x68, 0x70, 0x68, 0x78, 0x16, 0x4C, 0x10, 0x38,
+	0x03, 0x00, 0xFF, 0xF7, 0x0F, 0xFD, 0x05, 0x04,
+	0x06, 0x0C, 0x1C, 0x1E, 0x0E, 0x00, 0x06, 0x20,
+	0x06, 0xE0, 0x01, 0x20, 0x20, 0x70, 0x11, 0xA0,
+	0x02, 0xF0, 0xDF, 0xFB, 0x01, 0xE0, 0x02, 0x20,
+	0x20, 0x70, 0x68, 0x78, 0x80, 0x21, 0x08, 0x43,
+	0x28, 0x71, 0x00, 0x0A, 0x68, 0x71, 0x00, 0x20,
+	0xFF, 0xF7, 0x22, 0xFD, 0xA9, 0x88, 0x0C, 0xA0,
+	0x02, 0xF0, 0xCF, 0xFB, 0x70, 0xBD, 0x03, 0x20,
+	0xEE, 0xE7, 0x0D, 0x48, 0x69, 0x78, 0x82, 0x88,
+	0x0C, 0xA0, 0x02, 0xF0, 0xC6, 0xFB, 0x05, 0x20,
+	0xE6, 0xE7, 0x00, 0x00, 0x88, 0x02, 0x00, 0x20,
+	0xCE, 0x00, 0x00, 0x20, 0x43, 0x4D, 0x44, 0x5F,
+	0x49, 0x4E, 0x49, 0x54, 0x0D, 0x0A, 0x00, 0x00,
+	0x43, 0x4D, 0x44, 0x20, 0x45, 0x78, 0x69, 0x74,
+	0x5B, 0x25, 0x78, 0x5D, 0x3D, 0x0D, 0x0A, 0x00,
+	0x74, 0x06, 0x00, 0x20, 0x43, 0x4D, 0x44, 0x3D,
+	0x30, 0x78, 0x25, 0x78, 0x3A, 0x25, 0x78, 0x0D,
+	0x0A, 0x00, 0x00, 0x00, 0xF8, 0xB5, 0x1A, 0x4C,
+	0x01, 0x20, 0x20, 0x70, 0x19, 0x4E, 0x00, 0x20,
+	0x30, 0x71, 0x70, 0x71, 0x18, 0x48, 0xB0, 0x70,
+	0x00, 0x0A, 0xF0, 0x70, 0x00, 0x20, 0xFF, 0xF7,
+	0xC9, 0xFE, 0x16, 0x4F, 0x04, 0x25, 0x20, 0x78,
+	0x03, 0x00, 0xFF, 0xF7, 0xB7, 0xFC, 0x07, 0x14,
+	0x05, 0x12, 0x12, 0x16, 0x19, 0x1F, 0x14, 0x00,
+	0x37, 0x71, 0x38, 0x0A, 0x70, 0x71, 0x25, 0x70,
+	0x70, 0x20, 0x30, 0x70, 0x0E, 0x48, 0x30, 0x71,
+	0x00, 0x0A, 0x70, 0x71, 0x00, 0xF0, 0x92, 0xFB,
+	0xE9, 0xE7, 0x00, 0xF0, 0x8F, 0xFB, 0x25, 0x70,
+	0xE5, 0xE7, 0xFF, 0xF7, 0x7B, 0xFF, 0xE2, 0xE7,
+	0x08, 0xA0, 0x02, 0xF0, 0x76, 0xFB, 0x00, 0xF0,
+	0x11, 0xF8, 0xF2, 0xE7, 0xF8, 0xBD, 0x00, 0x00,
+	0xCE, 0x00, 0x00, 0x20, 0x88, 0x02, 0x00, 0x20,
+	0xCC, 0x55, 0x00, 0x00, 0x55, 0xAA, 0x00, 0x00,
+	0x11, 0x5A, 0x00, 0x00, 0x5B, 0x53, 0x54, 0x5D,
+	0x0D, 0x0A, 0x00, 0x00, 0x70, 0xB5, 0x1F, 0x48,
+	0x00, 0x25, 0x84, 0x88, 0x01, 0x21, 0x00, 0x2C,
+	0x1A, 0xD0, 0x0B, 0x00, 0xFF, 0xF7, 0x7E, 0xFC,
+	0x07, 0x16, 0x05, 0x13, 0x1A, 0x16, 0x0F, 0x1F,
+	0x16, 0x00, 0x00, 0xF0, 0x55, 0xF8, 0x00, 0xF0,
+	0xC1, 0xF9, 0x01, 0x28, 0x01, 0xD1, 0x05, 0x21,
+	0x08, 0xE0, 0x06, 0x21, 0x06, 0xE0, 0x02, 0xF0,
+	0x99, 0xF8, 0x02, 0x21, 0x02, 0xE0, 0x00, 0xF0,
+	0x9F, 0xFE, 0x03, 0x21, 0x00, 0x2C, 0xE4, 0xD1,
+	0x28, 0x46, 0x70, 0xBD, 0x01, 0xF0, 0xF6, 0xFE,
+	0x00, 0xF0, 0x5E, 0xFE, 0xED, 0xE7, 0x0C, 0xA0,
+	0x02, 0xF0, 0x37, 0xFB, 0x0E, 0x48, 0xFF, 0xF7,
+	0xE9, 0xFE, 0x0E, 0x48, 0xFF, 0xF7, 0xE6, 0xFE,
+	0x0D, 0x48, 0xFF, 0xF7, 0xE3, 0xFE, 0x0D, 0x48,
+	0xFF, 0xF7, 0xE0, 0xFE, 0x00, 0xF0, 0x8A, 0xFA,
+	0x0B, 0x48, 0x01, 0x68, 0x0B, 0xA0, 0x02, 0xF0,
+	0x24, 0xFB, 0xE1, 0xE7, 0x74, 0x06, 0x00, 0x20,
+	0x54, 0x45, 0x53, 0x54, 0x5F, 0x43, 0x4D, 0x44,
+	0x5F, 0x45, 0x58, 0x49, 0x54, 0x0D, 0x0A, 0x00,
+	0xE4, 0x02, 0x00, 0x20, 0x48, 0x03, 0x00, 0x20,
+	0xAC, 0x03, 0x00, 0x20, 0x10, 0x04, 0x00, 0x20,
+	0x0C, 0x05, 0x00, 0x20, 0x67, 0x5F, 0x75, 0x33,
+	0x32, 0x5F, 0x77, 0x65, 0x61, 0x72, 0x61, 0x62,
+	0x6C, 0x65, 0x5F, 0x74, 0x65, 0x73, 0x74, 0x5F,
+	0x72, 0x65, 0x73, 0x75, 0x6C, 0x74, 0x3A, 0x20,
+	0x30, 0x78, 0x25, 0x78, 0x0D, 0x0A, 0x00, 0x00,
+	0x10, 0xB5, 0xC8, 0x21, 0x0E, 0x48, 0xFF, 0xF7,
+	0xB8, 0xFB, 0xC8, 0x21, 0x0D, 0x48, 0xFF, 0xF7,
+	0xB4, 0xFB, 0xC8, 0x21, 0x0C, 0x48, 0xFF, 0xF7,
+	0xB0, 0xFB, 0xC8, 0x21, 0x0B, 0x48, 0xFF, 0xF7,
+	0xAC, 0xFB, 0x64, 0x21, 0x0A, 0x48, 0xFF, 0xF7,
+	0xA8, 0xFB, 0x32, 0x21, 0x09, 0x48, 0xFF, 0xF7,
+	0xA4, 0xFB, 0x09, 0x49, 0x00, 0x20, 0x08, 0x60,
+	0x08, 0xA0, 0x02, 0xF0, 0xDE, 0xFA, 0x10, 0xBD,
+	0xE4, 0x02, 0x00, 0x20, 0x48, 0x03, 0x00, 0x20,
+	0xAC, 0x03, 0x00, 0x20, 0x10, 0x04, 0x00, 0x20,
+	0x74, 0x04, 0x00, 0x20, 0xD8, 0x04, 0x00, 0x20,
+	0x0C, 0x05, 0x00, 0x20, 0x46, 0x54, 0x20, 0x52,
+	0x65, 0x73, 0x75, 0x6C, 0x74, 0x20, 0x49, 0x6E,
+	0x69, 0x74, 0x0D, 0x0A, 0x00, 0x00, 0x00, 0x00,
+	0xF3, 0xB5, 0xC1, 0xB0, 0x42, 0x98, 0x02, 0x28,
+	0x01, 0xD2, 0x02, 0x20, 0x42, 0x90, 0xC0, 0x21,
+	0x68, 0x46, 0xFF, 0xF7, 0x7A, 0xFB, 0x01, 0xF0,
+	0xA3, 0xFB, 0x01, 0xF0, 0xD3, 0xFD, 0x01, 0xF0,
+	0x7B, 0xF8, 0x00, 0x24, 0x6E, 0x46, 0x1A, 0xE0,
+	0x01, 0xF0, 0xE4, 0xFC, 0x01, 0xF0, 0xCA, 0xFD,
+	0x01, 0xF0, 0x72, 0xF8, 0x18, 0x49, 0x00, 0x20,
+	0x0A, 0x68, 0x18, 0x49, 0x0F, 0x68, 0x39, 0x18,
+	0xC9, 0x7E, 0x41, 0x29, 0x05, 0xD0, 0x81, 0x00,
+	0x45, 0x00, 0x73, 0x58, 0x55, 0x5F, 0x5B, 0x19,
+	0x73, 0x50, 0x40, 0x1C, 0xC0, 0xB2, 0x30, 0x28,
+	0xF1, 0xD3, 0x64, 0x1C, 0xE4, 0xB2, 0x42, 0x98,
+	0x84, 0x42, 0xE1, 0xD3, 0x01, 0xF0, 0xAE, 0xFD,
+	0x01, 0xF0, 0xDC, 0xFC, 0x0B, 0x4D, 0x00, 0x24,
+	0x28, 0x68, 0x00, 0x19, 0xC0, 0x7E, 0x41, 0x28,
+	0x07, 0xD0, 0xA0, 0x00, 0x30, 0x58, 0x42, 0x99,
+	0xFF, 0xF7, 0x16, 0xFB, 0x41, 0x99, 0x62, 0x00,
+	0x88, 0x52, 0x64, 0x1C, 0xE4, 0xB2, 0x30, 0x2C,
+	0xEE, 0xD3, 0x01, 0x20, 0x43, 0xB0, 0xF0, 0xBD,
+	0x4C, 0x01, 0x00, 0x20, 0x98, 0x01, 0x00, 0x20,
+	0x70, 0xB5, 0x24, 0x48, 0xFF, 0x24, 0x41, 0x6B,
+	0x2D, 0x34, 0x03, 0x22, 0x12, 0x05, 0x91, 0x43,
+	0x01, 0x22, 0x12, 0x05, 0x89, 0x18, 0x41, 0x63,
+	0x1F, 0x4D, 0x20, 0x48, 0xA9, 0x7E, 0xFF, 0xF7,
+	0x9F, 0xFF, 0xE8, 0x7E, 0x29, 0x7E, 0x41, 0x43,
+	0x89, 0x04, 0x28, 0x7F, 0x09, 0x0E, 0x02, 0x04,
+	0x09, 0x02, 0x0A, 0x43, 0x17, 0x49, 0x0C, 0x32,
+	0x40, 0x39, 0x4A, 0x60, 0x0C, 0x38, 0xC0, 0xB2,
+	0xC9, 0x14, 0x40, 0x18, 0x13, 0x49, 0x40, 0x31,
+	0x88, 0x60, 0xA9, 0x7E, 0x14, 0x48, 0xFF, 0xF7,
+	0x87, 0xFF, 0x14, 0x49, 0x12, 0x4B, 0x0A, 0x68,
+	0x00, 0x20, 0x11, 0x18, 0xC9, 0x7E, 0x41, 0x29,
+	0x03, 0xD0, 0x41, 0x00, 0x5D, 0x5A, 0x2D, 0x1B,
+	0x5D, 0x52, 0x40, 0x1C, 0xC0, 0xB2, 0x30, 0x28,
+	0xF3, 0xD3, 0x0D, 0xA0, 0x02, 0xF0, 0x39, 0xFA,
+	0x01, 0x21, 0x08, 0x48, 0x01, 0xF0, 0x1C, 0xFE,
+	0x0C, 0xA0, 0x02, 0xF0, 0x32, 0xFA, 0x01, 0x21,
+	0x05, 0x48, 0x01, 0xF0, 0x15, 0xFE, 0x01, 0x20,
+	0x70, 0xBD, 0x00, 0x00, 0x80, 0x10, 0x00, 0x50,
+	0xA8, 0x06, 0x00, 0x20, 0x74, 0x04, 0x00, 0x20,
+	0x48, 0x03, 0x00, 0x20, 0x98, 0x01, 0x00, 0x20,
+	0x42, 0x61, 0x73, 0x65, 0x6C, 0x69, 0x6E, 0x65,
+	0x0D, 0x0A, 0x00, 0x00, 0x51, 0x75, 0x69, 0x63,
+	0x6B, 0x28, 0x30, 0x78, 0x31, 0x32, 0x43, 0x29,
+	0x0D, 0x0A, 0x00, 0x00, 0xF8, 0xB5, 0x3C, 0x48,
+	0x00, 0x24, 0x60, 0x21, 0x00, 0x68, 0xFF, 0xF7,
+	0xCC, 0xFA, 0x3A, 0x4E, 0x3A, 0x48, 0x00, 0x68,
+	0x00, 0x19, 0xC0, 0x7E, 0x41, 0x28, 0x5A, 0xD0,
+	0xA5, 0x07, 0xAD, 0x0F, 0xA7, 0x08, 0x0C, 0x21,
+	0x20, 0x46, 0xFF, 0xF7, 0x7B, 0xFA, 0x88, 0x08,
+	0x00, 0x21, 0xC9, 0x43, 0x71, 0x62, 0x09, 0x0C,
+	0xB1, 0x62, 0x20, 0x2C, 0x05, 0xD2, 0x72, 0x6A,
+	0x01, 0x21, 0xA1, 0x40, 0x8A, 0x43, 0x72, 0x62,
+	0x06, 0xE0, 0xB3, 0x6A, 0x21, 0x46, 0x20, 0x39,
+	0x01, 0x22, 0x8A, 0x40, 0x93, 0x43, 0xB3, 0x62,
+	0x03, 0x2D, 0x02, 0xD2, 0x69, 0x1C, 0xC9, 0xB2,
+	0x00, 0xE0, 0x08, 0x21, 0xF2, 0x6A, 0x27, 0x4B,
+	0x1A, 0x40, 0x01, 0x23, 0xBB, 0x40, 0x1B, 0x05,
+	0x1B, 0x09, 0x1A, 0x43, 0xF2, 0x62, 0x72, 0x6B,
+	0x0E, 0x23, 0x9A, 0x43, 0x01, 0x23, 0x83, 0x40,
+	0x5B, 0x07, 0x1B, 0x0F, 0x1A, 0x43, 0x72, 0x63,
+	0x40, 0x1C, 0x82, 0x07, 0x92, 0x0F, 0x90, 0x05,
+	0x13, 0x05, 0x18, 0x43, 0x93, 0x04, 0x18, 0x43,
+	0x12, 0x04, 0x18, 0x4F, 0x10, 0x43, 0x40, 0x37,
+	0xFA, 0x6A, 0xFF, 0x23, 0x1B, 0x04, 0x9A, 0x43,
+	0x10, 0x43, 0xF8, 0x62, 0xF0, 0x6B, 0x09, 0x07,
+	0xF4, 0x22, 0x09, 0x0E, 0x90, 0x43, 0x09, 0x1D,
+	0x08, 0x43, 0xF0, 0x63, 0xB0, 0x6B, 0x74, 0x21,
+	0x88, 0x43, 0x6D, 0x1C, 0x69, 0x07, 0x49, 0x0E,
+	0x09, 0x1D, 0x08, 0x43, 0xB0, 0x63, 0x01, 0xF0,
+	0xE1, 0xFB, 0x01, 0xF0, 0xC7, 0xFC, 0x64, 0x1C,
+	0xE4, 0xB2, 0x30, 0x2C, 0x9A, 0xD3, 0x06, 0x48,
+	0x60, 0x22, 0x01, 0x68, 0x08, 0x48, 0xFF, 0xF7,
+	0x47, 0xFA, 0x01, 0x21, 0x06, 0x48, 0x01, 0xF0,
+	0x83, 0xFD, 0x01, 0x20, 0xF8, 0xBD, 0x00, 0x00,
+	0x4C, 0x01, 0x00, 0x20, 0x80, 0x10, 0x00, 0x50,
+	0x98, 0x01, 0x00, 0x20, 0xFF, 0xFF, 0x00, 0xF0,
+	0xE4, 0x02, 0x00, 0x20, 0x10, 0xB5, 0x00, 0xF0,
+	0xC1, 0xF8, 0x0E, 0x48, 0x00, 0x68, 0x0E, 0x4C,
+	0x40, 0x05, 0x40, 0x0F, 0x20, 0x70, 0x00, 0x20,
+	0x01, 0xF0, 0xA2, 0xFF, 0x0B, 0x49, 0x01, 0x20,
+	0x08, 0x70, 0x08, 0x20, 0x20, 0x70, 0x0A, 0x49,
+	0x06, 0x20, 0x08, 0x70, 0x01, 0x20, 0x09, 0x49,
+	0x80, 0x02, 0x08, 0x80, 0x01, 0xF0, 0xF2, 0xFE,
+	0x01, 0xF0, 0x90, 0xFC, 0x01, 0xF0, 0xBE, 0xFB,
+	0x01, 0x20, 0x10, 0xBD, 0x00, 0x11, 0x00, 0x50,
+	0x39, 0x01, 0x00, 0x20, 0x3B, 0x01, 0x00, 0x20,
+	0x0D, 0x08, 0x00, 0x20, 0x46, 0x01, 0x00, 0x20,
+	0xF8, 0xB5, 0x36, 0x48, 0x00, 0x24, 0x36, 0x4D,
+	0x44, 0x80, 0x2C, 0x60, 0x6C, 0x60, 0x35, 0xA0,
+	0x02, 0xF0, 0x5B, 0xF9, 0x32, 0x48, 0x61, 0x1E,
+	0x80, 0x38, 0xC1, 0x61, 0x09, 0x0C, 0x01, 0x62,
+	0x35, 0x49, 0x41, 0x62, 0x81, 0x62, 0xC1, 0x62,
+	0x01, 0x06, 0x0A, 0x68, 0x0B, 0x13, 0x9A, 0x43,
+	0x0A, 0x60, 0x32, 0x49, 0x01, 0x26, 0x8E, 0x61,
+	0xCE, 0x61, 0x30, 0x4A, 0x40, 0x3A, 0xD4, 0x63,
+	0x0C, 0x60, 0x8C, 0x60, 0x2E, 0x4B, 0x1F, 0x7F,
+	0xDB, 0x7E, 0x3A, 0x04, 0x1B, 0x02, 0x1A, 0x43,
+	0x0C, 0x32, 0xBC, 0x46, 0x42, 0x60, 0x82, 0x60,
+	0x1F, 0x22, 0x02, 0x61, 0x44, 0x61, 0x0C, 0x62,
+	0x1F, 0x48, 0x28, 0x4A, 0x40, 0x30, 0x82, 0x61,
+	0x03, 0x22, 0xC2, 0x61, 0x1C, 0x4A, 0xC0, 0x3A,
+	0xD4, 0x60, 0x03, 0x23, 0x1B, 0x02, 0x13, 0x61,
+	0x14, 0x23, 0x83, 0x62, 0x34, 0x23, 0xC3, 0x63,
+	0x17, 0x4B, 0x54, 0x27, 0x80, 0x33, 0x1F, 0x61,
+	0x5C, 0x62, 0xD0, 0x27, 0xC7, 0x62, 0xF0, 0x27,
+	0x1F, 0x60, 0xFF, 0x27, 0x11, 0x37, 0x5F, 0x61,
+	0x9C, 0x62, 0x04, 0x62, 0x1A, 0x4B, 0x43, 0x62,
+	0x1A, 0x48, 0x88, 0x62, 0x0E, 0x49, 0x1A, 0x48,
+	0x40, 0x39, 0x48, 0x63, 0x67, 0x46, 0x0C, 0x3F,
+	0xF8, 0xB2, 0xC9, 0x14, 0x40, 0x18, 0xA8, 0x60,
+	0x17, 0x48, 0x16, 0x49, 0x01, 0x60, 0x44, 0x60,
+	0xD1, 0x68, 0x16, 0x48, 0x01, 0x60, 0x11, 0x69,
+	0x41, 0x60, 0x15, 0x48, 0x06, 0x70, 0x10, 0x15,
+	0x10, 0x60, 0x14, 0xA0, 0x02, 0xF0, 0xF9, 0xF8,
+	0xF8, 0xBD, 0x00, 0x00, 0xCE, 0x00, 0x00, 0x20,
+	0xC0, 0x10, 0x00, 0x50, 0x4F, 0x70, 0x65, 0x6E,
+	0x20, 0x49, 0x6E, 0x69, 0x74, 0x20, 0x53, 0x74,
+	0x61, 0x72, 0x74, 0x0D, 0x0A, 0x00, 0x00, 0x00,
+	0x55, 0x55, 0x55, 0x55, 0xC0, 0x11, 0x00, 0x50,
+	0xA8, 0x06, 0x00, 0x20, 0x03, 0x00, 0x03, 0x00,
+	0x00, 0x3F, 0x3F, 0x3F, 0x03, 0x30, 0xBC, 0x00,
+	0x00, 0x26, 0x30, 0x00, 0x21, 0x20, 0x00, 0x00,
+	0x00, 0x19, 0x00, 0x50, 0x20, 0x00, 0x00, 0x20,
+	0x31, 0x00, 0x00, 0x20, 0x4F, 0x70, 0x65, 0x6E,
+	0x20, 0x49, 0x6E, 0x69, 0x74, 0x20, 0x45, 0x6E,
+	0x64, 0x0D, 0x0A, 0x00, 0x10, 0xB5, 0x90, 0xB0,
+	0x40, 0x21, 0x68, 0x46, 0xFF, 0xF7, 0x85, 0xF9,
+	0x41, 0x22, 0x40, 0x21, 0x68, 0x46, 0xFF, 0xF7,
+	0x79, 0xF9, 0x0E, 0x49, 0x00, 0x20, 0x0B, 0x68,
+	0x6A, 0x46, 0x19, 0x18, 0xC9, 0x7E, 0x41, 0x29,
+	0x00, 0xD0, 0x50, 0x54, 0x40, 0x1C, 0xC0, 0xB2,
+	0x30, 0x28, 0xF6, 0xD3, 0x08, 0x49, 0x00, 0x20,
+	0x13, 0x5C, 0x44, 0x00, 0x40, 0x1C, 0xC0, 0xB2,
+	0x0B, 0x53, 0x40, 0x28, 0xF8, 0xD3, 0x00, 0x21,
+	0x03, 0x48, 0x01, 0xF0, 0x8D, 0xFC, 0x01, 0x20,
+	0x10, 0xB0, 0x10, 0xBD, 0x98, 0x01, 0x00, 0x20,
+	0x9C, 0x01, 0x00, 0x20, 0x0E, 0x49, 0x10, 0xB5,
+	0x5A, 0x20, 0x08, 0x74, 0x00, 0x20, 0x0A, 0x46,
+	0x48, 0x74, 0x20, 0x3A, 0x14, 0x5C, 0x4B, 0x7C,
+	0x40, 0x1C, 0x1B, 0x19, 0xC0, 0xB2, 0x4B, 0x74,
+	0x31, 0x28, 0xF7, 0xD3, 0x07, 0x49, 0x00, 0x20,
+	0x13, 0x5C, 0x44, 0x00, 0x40, 0x1C, 0xC0, 0xB2,
+	0x0B, 0x53, 0x40, 0x28, 0xF8, 0xD3, 0x01, 0x21,
+	0x02, 0x48, 0x01, 0xF0, 0x69, 0xFC, 0x10, 0xBD,
+	0xF8, 0x04, 0x00, 0x20, 0x9C, 0x01, 0x00, 0x20,
+	0x70, 0xB5, 0x2E, 0xA0, 0x02, 0xF0, 0x79, 0xF8,
+	0x32, 0x49, 0x31, 0x48, 0x08, 0x60, 0x33, 0x4B,
+	0x31, 0x48, 0x58, 0x60, 0x31, 0x4C, 0x32, 0x48,
+	0x80, 0x34, 0xA0, 0x60, 0x31, 0x48, 0x40, 0x78,
+	0x41, 0x1E, 0xC9, 0x05, 0xC9, 0x0D, 0x19, 0x61,
+	0x2F, 0x4A, 0x00, 0x21, 0x11, 0x62, 0x2B, 0x4A,
+	0x2E, 0x4D, 0x40, 0x32, 0x55, 0x63, 0x05, 0x02,
+	0x2D, 0x48, 0x6D, 0x1C, 0x05, 0x60, 0x41, 0x60,
+	0x09, 0x25, 0x26, 0x48, 0x2D, 0x05, 0x40, 0x38,
+	0x05, 0x63, 0x41, 0x63, 0x81, 0x63, 0xD9, 0x63,
+	0x11, 0x60, 0xA1, 0x62, 0x27, 0x4B, 0x19, 0x70,
+	0x04, 0x23, 0x03, 0x60, 0x83, 0x02, 0xC3, 0x60,
+	0x01, 0x61, 0xC3, 0x68, 0x24, 0x49, 0x0B, 0x60,
+	0x03, 0x69, 0x4B, 0x60, 0x03, 0x21, 0x23, 0x4B,
+	0x09, 0x06, 0x19, 0x60, 0x01, 0x21, 0x49, 0x02,
+	0x11, 0x62, 0x81, 0x04, 0xD1, 0x62, 0x81, 0x69,
+	0x03, 0x23, 0x9B, 0x03, 0x19, 0x43, 0x81, 0x61,
+	0xC1, 0x69, 0x1D, 0x4B, 0x19, 0x43, 0xC1, 0x61,
+	0xD0, 0x21, 0x11, 0x63, 0xC1, 0x6A, 0x10, 0x22,
+	0x11, 0x43, 0xC1, 0x62, 0x55, 0x20, 0xE0, 0x62,
+	0x60, 0x21, 0x18, 0x48, 0xFF, 0xF7, 0xE9, 0xF8,
+	0x16, 0x48, 0x60, 0x21, 0x60, 0x30, 0xFF, 0xF7,
+	0xE4, 0xF8, 0x15, 0xA0, 0x02, 0xF0, 0x21, 0xF8,
+	0x70, 0xBD, 0x00, 0x00, 0x53, 0x68, 0x6F, 0x72,
+	0x74, 0x20, 0x49, 0x6E, 0x69, 0x74, 0x20, 0x53,
+	0x74, 0x61, 0x72, 0x74, 0x0D, 0x0A, 0x00, 0x00,
+	0x00, 0x00, 0x04, 0x20, 0x4C, 0x01, 0x00, 0x20,
+	0x0C, 0x0D, 0xF2, 0x00, 0x40, 0x10, 0x00, 0x50,
+	0xE6, 0x0A, 0x00, 0x00, 0xC8, 0x06, 0x00, 0x20,
+	0xC0, 0x11, 0x00, 0x50, 0x00, 0x36, 0x30, 0x04,
+	0x00, 0x19, 0x00, 0x50, 0x31, 0x00, 0x00, 0x20,
+	0x20, 0x00, 0x00, 0x20, 0x00, 0x07, 0x00, 0x50,
+	0x00, 0xC0, 0x00, 0xC0, 0x00, 0x20, 0x00, 0x50,
+	0x53, 0x68, 0x6F, 0x72, 0x74, 0x20, 0x49, 0x6E,
+	0x69, 0x74, 0x20, 0x45, 0x6E, 0x64, 0x0D, 0x0A,
+	0x00, 0x00, 0x00, 0x00, 0x10, 0xB5, 0x1C, 0x48,
+	0x64, 0x24, 0x41, 0x79, 0x02, 0x79, 0x09, 0x02,
+	0x11, 0x43, 0x80, 0x22, 0x91, 0x43, 0x01, 0x71,
+	0x09, 0x0A, 0x41, 0x71, 0x41, 0x79, 0x02, 0x79,
+	0x09, 0x02, 0x11, 0x43, 0x2D, 0x22, 0x52, 0x02,
+	0x11, 0x43, 0x01, 0x71, 0x09, 0x0A, 0x41, 0x71,
+	0x81, 0x88, 0x12, 0xA0, 0x01, 0xF0, 0xD5, 0xFF,
+	0x06, 0xE0, 0x21, 0x46, 0x16, 0xA0, 0x01, 0xF0,
+	0xD0, 0xFF, 0x01, 0x20, 0xFF, 0xF7, 0xDA, 0xF9,
+	0xFF, 0xF7, 0x94, 0xFB, 0x00, 0x28, 0x04, 0xD1,
+	0x20, 0x46, 0x64, 0x1E, 0x24, 0xB2, 0x00, 0x28,
+	0xEF, 0xD1, 0x64, 0x1C, 0x08, 0xD1, 0x01, 0x20,
+	0xFF, 0xF7, 0x0E, 0xF9, 0x10, 0xA0, 0x01, 0xF0,
+	0xBC, 0xFF, 0x0A, 0x20, 0xFF, 0xF7, 0xC6, 0xF9,
+	0x00, 0x20, 0xFF, 0xF7, 0x05, 0xF9, 0x10, 0xBD,
+	0x88, 0x02, 0x00, 0x20, 0x46, 0x54, 0x20, 0x55,
+	0x70, 0x64, 0x61, 0x74, 0x65, 0x20, 0x53, 0x74,
+	0x61, 0x74, 0x75, 0x73, 0x20, 0x28, 0x30, 0x78,
+	0x25, 0x34, 0x78, 0x29, 0x0D, 0x0A, 0x00, 0x00,
+	0x49, 0x4E, 0x54, 0x20, 0x4C, 0x6F, 0x77, 0x21,
+	0x21, 0x28, 0x25, 0x64, 0x29, 0x0D, 0x0A, 0x00,
+	0x49, 0x4E, 0x54, 0x20, 0x70, 0x61, 0x64, 0x20,
+	0x69, 0x73, 0x20, 0x6C, 0x6F, 0x77, 0x20, 0x6C,
+	0x65, 0x76, 0x65, 0x6C, 0x20, 0x21, 0x21, 0x00,
+	0x70, 0xB5, 0x17, 0x48, 0x5A, 0x25, 0x01, 0x78,
+	0x16, 0x48, 0x00, 0x29, 0x25, 0xD0, 0x09, 0x21,
+	0x01, 0x70, 0x15, 0x4C, 0x01, 0x21, 0xE0, 0x89,
+	0x89, 0x02, 0x88, 0x42, 0x01, 0xD0, 0x03, 0x20,
+	0x60, 0x71, 0x11, 0x48, 0x30, 0x21, 0x28, 0x30,
+	0xFF, 0xF7, 0x3B, 0xF8, 0x00, 0x20, 0x60, 0x72,
+	0xA0, 0x71, 0x60, 0x62, 0x16, 0x21, 0x21, 0x72,
+	0x0C, 0x49, 0x20, 0x71, 0x08, 0x70, 0xA5, 0x81,
+	0x60, 0x81, 0x60, 0x8A, 0x09, 0x21, 0x09, 0x03,
+	0x08, 0x43, 0x60, 0x82, 0x03, 0x20, 0x40, 0x02,
+	0xE0, 0x61, 0x0D, 0x20, 0xC0, 0x01, 0x20, 0x62,
+	0x70, 0xBD, 0x05, 0x70, 0xD9, 0xE7, 0x00, 0x00,
+	0x92, 0x01, 0x00, 0x20, 0x94, 0x01, 0x00, 0x20,
+	0x38, 0x01, 0x00, 0x20, 0x93, 0x01, 0x00, 0x20,
+	0x0E, 0x48, 0x03, 0x21, 0x41, 0x71, 0x0E, 0x49,
+	0x41, 0x61, 0x0D, 0x49, 0x60, 0x31, 0x81, 0x61,
+	0x01, 0x21, 0x01, 0x70, 0x07, 0x22, 0x42, 0x70,
+	0x0A, 0x4B, 0x05, 0x22, 0x1A, 0x70, 0x0A, 0x4B,
+	0x1A, 0x70, 0x0A, 0x4B, 0x55, 0x22, 0xDA, 0x70,
+	0x04, 0x22, 0x02, 0x82, 0x00, 0x22, 0xC2, 0x70,
+	0x09, 0x22, 0x12, 0x03, 0x42, 0x82, 0x81, 0x70,
+	0x70, 0x47, 0x00, 0x00, 0x38, 0x01, 0x00, 0x20,
+	0x00, 0x00, 0x04, 0x20, 0x92, 0x01, 0x00, 0x20,
+	0x0D, 0x08, 0x00, 0x20, 0x90, 0x02, 0x00, 0x20,
+	0x04, 0x22, 0x0F, 0x49, 0x0C, 0x28, 0x10, 0xD0,
+	0x8B, 0x05, 0x0D, 0x28, 0x08, 0x6A, 0x10, 0xD0,
+	0x18, 0x43, 0x08, 0x62, 0x88, 0x6A, 0x10, 0x43,
+	0x88, 0x62, 0x0A, 0x4A, 0x01, 0x20, 0x10, 0x70,
+	0xC8, 0x68, 0xC8, 0x60, 0x88, 0x6A, 0x88, 0x62,
+	0x70, 0x47, 0x08, 0x6A, 0x40, 0x00, 0x40, 0x08,
+	0x00, 0xE0, 0x18, 0x43, 0x08, 0x62, 0x88, 0x6A,
+	0x90, 0x43, 0x88, 0x62, 0xF0, 0xE7, 0x00, 0x00,
+	0x00, 0x06, 0x00, 0x50, 0xCC, 0x00, 0x00, 0x20,
+	0x00, 0xB5, 0x08, 0x49, 0x0A, 0x28, 0x05, 0xD0,
+	0x07, 0x48, 0x00, 0x0C, 0x48, 0x63, 0x07, 0x48,
+	0x08, 0x63, 0x00, 0xBD, 0x06, 0x48, 0x00, 0x68,
+	0x08, 0x62, 0x0D, 0x20, 0xFF, 0xF7, 0xCC, 0xFF,
+	0x00, 0xBD, 0x00, 0x00, 0x00, 0x06, 0x00, 0x50,
+	0xBC, 0x02, 0x00, 0x20, 0xCC, 0x02, 0x00, 0x20,
+	0xC0, 0x02, 0x00, 0x20, 0x10, 0xB5, 0x1B, 0x49,
+	0x00, 0x20, 0x03, 0x00, 0xFF, 0xF7, 0x12, 0xF8,
+	0x0C, 0x07, 0x0A, 0x0E, 0x26, 0x26, 0x11, 0x14,
+	0x17, 0x1A, 0x1D, 0x20, 0x23, 0x26, 0x16, 0x4A,
+	0x0A, 0x80, 0x1E, 0xE0, 0x14, 0x4A, 0x12, 0x1D,
+	0x4A, 0x80, 0x1A, 0xE0, 0x13, 0x4A, 0x8A, 0x80,
+	0x17, 0xE0, 0x13, 0x4A, 0x4A, 0x81, 0x14, 0xE0,
+	0x12, 0x4A, 0x8A, 0x81, 0x11, 0xE0, 0x12, 0x4A,
+	0xCA, 0x81, 0x0E, 0xE0, 0x11, 0x4A, 0x0A, 0x82,
+	0x0B, 0xE0, 0x11, 0x4A, 0x4A, 0x82, 0x08, 0xE0,
+	0x10, 0x4A, 0x8A, 0x82, 0x05, 0xE0, 0x10, 0x4A,
+	0xCA, 0x82, 0x02, 0xE0, 0x0F, 0x4A, 0x43, 0x00,
+	0xCA, 0x52, 0x40, 0x1C, 0x80, 0xB2, 0x0C, 0x28,
+	0xCF, 0xD3, 0x0D, 0xA0, 0x01, 0xF0, 0xBD, 0xFE,
+	0x10, 0xBD, 0x00, 0x00, 0xCC, 0x02, 0x00, 0x20,
+	0x90, 0x02, 0x00, 0x20, 0x88, 0x02, 0x00, 0x20,
+	0x34, 0x01, 0x00, 0x20, 0xE0, 0x06, 0x00, 0x20,
+	0xE4, 0x06, 0x00, 0x20, 0xD8, 0x06, 0x00, 0x20,
+	0xC0, 0x02, 0x00, 0x20, 0xBC, 0x02, 0x00, 0x20,
+	0x9C, 0x01, 0x00, 0x20, 0xC4, 0x02, 0x00, 0x20,
+	0x49, 0x32, 0x43, 0x20, 0x4F, 0x4B, 0x0D, 0x0A,
+	0x00, 0x00, 0x00, 0x00, 0x70, 0xB5, 0x01, 0x25,
+	0xCA, 0x07, 0x0A, 0xD0, 0x00, 0x20, 0x70, 0xBD,
+	0x93, 0x00, 0xC4, 0x58, 0x66, 0x1C, 0x02, 0xD0,
+	0x1B, 0x18, 0x5B, 0x68, 0x23, 0x60, 0x92, 0x1C,
+	0x92, 0xB2, 0x8A, 0x42, 0xF4, 0xD3, 0x28, 0x46,
+	0x70, 0xBD, 0x00, 0x00, 0x10, 0xB5, 0x1D, 0x49,
+	0x0A, 0x68, 0x1C, 0x48, 0x40, 0x30, 0x02, 0x61,
+	0x4A, 0x68, 0x42, 0x61, 0x8A, 0x68, 0x82, 0x61,
+	0xC9, 0x68, 0xC1, 0x61, 0x82, 0x69, 0xF0, 0x21,
+	0x8A, 0x43, 0x82, 0x61, 0x82, 0x69, 0x0A, 0x43,
+	0x82, 0x61, 0x41, 0x69, 0x49, 0x04, 0x04, 0xD5,
+	0x41, 0x69, 0x01, 0x22, 0x52, 0x03, 0x89, 0x1A,
+	0x41, 0x61, 0x10, 0x48, 0x40, 0x38, 0x01, 0x68,
+	0x01, 0x22, 0x12, 0x06, 0x11, 0x43, 0x01, 0x60,
+	0x30, 0x21, 0x0D, 0x48, 0xFF, 0xF7, 0xC6, 0xFF,
+	0x0B, 0x48, 0x40, 0x21, 0xC0, 0x30, 0xFF, 0xF7,
+	0xC1, 0xFF, 0x0B, 0x20, 0xFE, 0xF7, 0x46, 0xFF,
+	0x03, 0x20, 0xFE, 0xF7, 0x43, 0xFF, 0x00, 0x20,
+	0xFE, 0xF7, 0x40, 0xFF, 0x05, 0x20, 0xFE, 0xF7,
+	0x3D, 0xFF, 0x09, 0x20, 0xFE, 0xF7, 0x3A, 0xFF,
+	0x01, 0x20, 0x10, 0xBD, 0x40, 0x14, 0x00, 0x50,
+	0x50, 0x39, 0x00, 0x00, 0xFE, 0xB5, 0x27, 0x48,
+	0x0A, 0x25, 0x45, 0x5F, 0x01, 0x26, 0x29, 0x46,
+	0x25, 0xA0, 0x01, 0xF0, 0x42, 0xFE, 0x2A, 0x48,
+	0x72, 0x04, 0x01, 0x68, 0x29, 0x4F, 0x91, 0x43,
+	0x00, 0x24, 0x01, 0x60, 0x28, 0x48, 0x00, 0x68,
+	0x00, 0x19, 0xC0, 0x7E, 0x41, 0x28, 0x18, 0xD0,
+	0x26, 0x49, 0x60, 0x00, 0x0A, 0x5E, 0x26, 0x49,
+	0x0B, 0x5A, 0xD0, 0x1A, 0x00, 0xB2, 0xA8, 0x42,
+	0x0B, 0xDA, 0x00, 0x90, 0x21, 0x46, 0x01, 0x95,
+	0x22, 0xA0, 0x01, 0xF0, 0x26, 0xFE, 0x38, 0x5D,
+	0x20, 0x21, 0x08, 0x43, 0x38, 0x55, 0x00, 0x26,
+	0x03, 0xE0, 0x38, 0x5D, 0xDF, 0x21, 0x08, 0x40,
+	0x38, 0x55, 0x64, 0x1C, 0xE4, 0xB2, 0x30, 0x2C,
+	0xDC, 0xD3, 0x01, 0x2E, 0x18, 0xD0, 0x14, 0x4A,
+	0x01, 0x21, 0x10, 0x68, 0x49, 0x04, 0x08, 0x43,
+	0x10, 0x60, 0x23, 0xA0, 0x01, 0xF0, 0x0D, 0xFE,
+	0x27, 0xA0, 0x01, 0xF0, 0x0A, 0xFE, 0x01, 0x21,
+	0x10, 0x48, 0x01, 0xF0, 0xED, 0xF9, 0x27, 0xA0,
+	0x01, 0xF0, 0x03, 0xFE, 0x01, 0x21, 0x0E, 0x48,
+	0x01, 0xF0, 0xE6, 0xF9, 0x30, 0x46, 0xFE, 0xBD,
+	0x27, 0xA0, 0xEB, 0xE7, 0x84, 0x06, 0x00, 0x20,
+	0x4F, 0x70, 0x65, 0x6E, 0x20, 0x43, 0x43, 0x20,
+	0x43, 0x68, 0x65, 0x63, 0x6B, 0x20, 0x54, 0x48,
+	0x44, 0x3D, 0x25, 0x64, 0x20, 0x0D, 0x0A, 0x00,
+	0x0C, 0x05, 0x00, 0x20, 0xD8, 0x04, 0x00, 0x20,
+	0x98, 0x01, 0x00, 0x20, 0xAC, 0x03, 0x00, 0x20,
+	0x4C, 0x07, 0x00, 0x20, 0x4F, 0x70, 0x65, 0x6E,
+	0x20, 0x43, 0x43, 0x20, 0x4E, 0x47, 0x20, 0x50,
+	0x69, 0x6E, 0x5B, 0x25, 0x64, 0x5D, 0x2C, 0x20,
+	0x44, 0x69, 0x66, 0x66, 0x3D, 0x25, 0x64, 0x2D,
+	0x25, 0x64, 0x3D, 0x25, 0x64, 0x2C, 0x20, 0x4C,
+	0x6F, 0x77, 0x54, 0x48, 0x44, 0x20, 0x3D, 0x20,
+	0x25, 0x64, 0x0D, 0x0A, 0x00, 0x00, 0x00, 0x00,
+	0x53, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20, 0x43,
+	0x43, 0x20, 0x54, 0x65, 0x73, 0x74, 0x20, 0x5B,
+	0x4E, 0x47, 0x5D, 0x0D, 0x0A, 0x00, 0x00, 0x00,
+	0x4F, 0x70, 0x65, 0x6E, 0x20, 0x43, 0x43, 0x0D,
+	0x0A, 0x00, 0x00, 0x00, 0x47, 0x6F, 0x6C, 0x64,
+	0x65, 0x6E, 0x20, 0x4F, 0x70, 0x65, 0x6E, 0x20,
+	0x43, 0x43, 0x0D, 0x0A, 0x00, 0x00, 0x00, 0x00,
+	0x53, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20, 0x43,
+	0x43, 0x20, 0x54, 0x65, 0x73, 0x74, 0x20, 0x5B,
+	0x50, 0x61, 0x73, 0x73, 0x5D, 0x0D, 0x0A, 0x00,
+	0xFE, 0xB5, 0x32, 0x49, 0x02, 0x20, 0x08, 0x5E,
+	0x00, 0x90, 0x0A, 0x20, 0x08, 0x5E, 0x03, 0x21,
+	0x89, 0x03, 0x48, 0x43, 0x00, 0x14, 0x01, 0x90,
+	0x2D, 0x48, 0x01, 0x27, 0x01, 0x68, 0xB9, 0x43,
+	0x00, 0x24, 0x01, 0x60, 0x2B, 0x48, 0x00, 0x68,
+	0x00, 0x19, 0xC0, 0x7E, 0x41, 0x28, 0x30, 0xD0,
+	0x65, 0x00, 0x29, 0x4A, 0x29, 0x48, 0x56, 0x5F,
+	0x41, 0x5B, 0x00, 0x20, 0x71, 0x1A, 0x0B, 0xB2,
+	0x53, 0x53, 0x00, 0x99, 0x8B, 0x42, 0x05, 0xDA,
+	0x32, 0x46, 0x21, 0x46, 0x24, 0xA0, 0x01, 0xF0,
+	0x78, 0xFD, 0x01, 0x20, 0x00, 0x2E, 0x0E, 0xD1,
+	0x2B, 0x49, 0x2C, 0x4A, 0x49, 0x5B, 0x52, 0x5B,
+	0x89, 0x1A, 0x0A, 0xB2, 0x01, 0x99, 0x8A, 0x42,
+	0x05, 0xDA, 0x0B, 0x46, 0x21, 0x46, 0x28, 0xA0,
+	0x01, 0xF0, 0x67, 0xFD, 0x01, 0xE0, 0x00, 0x28,
+	0x06, 0xD0, 0x2F, 0x48, 0x01, 0x22, 0x01, 0x5D,
+	0x11, 0x43, 0x01, 0x55, 0x00, 0x27, 0x04, 0xE0,
+	0x2B, 0x48, 0x01, 0x5D, 0x49, 0x08, 0x49, 0x00,
+	0x01, 0x55, 0x64, 0x1C, 0xE4, 0xB2, 0x30, 0x2C,
+	0xC4, 0xD3, 0x00, 0x99, 0x01, 0x2F, 0x10, 0xD0,
+	0x26, 0xA0, 0x01, 0xF0, 0x4E, 0xFD, 0x0A, 0x49,
+	0x01, 0x22, 0x08, 0x68, 0x10, 0x43, 0x08, 0x60,
+	0x29, 0xA0, 0x01, 0xF0, 0x46, 0xFD, 0x01, 0x21,
+	0x07, 0x48, 0x01, 0xF0, 0x29, 0xF9, 0x38, 0x46,
+	0xFE, 0xBD, 0x2A, 0xA0, 0x01, 0xF0, 0x3D, 0xFD,
+	0xF2, 0xE7, 0x00, 0x00, 0x84, 0x06, 0x00, 0x20,
+	0x0C, 0x05, 0x00, 0x20, 0x98, 0x01, 0x00, 0x20,
+	0x48, 0x03, 0x00, 0x20, 0x74, 0x04, 0x00, 0x20,
+	0x47, 0x6C, 0x6F, 0x62, 0x61, 0x6C, 0x20, 0x54,
+	0x48, 0x44, 0x20, 0x4F, 0x70, 0x65, 0x6E, 0x20,
+	0x4E, 0x47, 0x20, 0x44, 0x61, 0x74, 0x61, 0x5B,
+	0x25, 0x64, 0x5D, 0x5B, 0x25, 0x64, 0x3A, 0x25,
+	0x64, 0x5D, 0x0D, 0x0A, 0x00, 0x00, 0x00, 0x00,
+	0xAC, 0x03, 0x00, 0x20, 0x4C, 0x07, 0x00, 0x20,
+	0x4F, 0x70, 0x65, 0x6E, 0x20, 0x4E, 0x47, 0x20,
+	0x50, 0x69, 0x6E, 0x5B, 0x25, 0x64, 0x5D, 0x2C,
+	0x20, 0x43, 0x43, 0x5F, 0x44, 0x69, 0x66, 0x66,
+	0x3D, 0x25, 0x64, 0x2C, 0x20, 0x54, 0x48, 0x44,
+	0x3D, 0x25, 0x64, 0x0D, 0x0A, 0x00, 0x00, 0x00,
+	0xD8, 0x04, 0x00, 0x20, 0x4F, 0x70, 0x65, 0x6E,
+	0x20, 0x54, 0x65, 0x73, 0x74, 0x28, 0x54, 0x48,
+	0x44, 0x3A, 0x25, 0x64, 0x29, 0x20, 0x5B, 0x4E,
+	0x47, 0x5D, 0x0D, 0x0A, 0x00, 0x00, 0x00, 0x00,
+	0x51, 0x75, 0x69, 0x63, 0x6B, 0x20, 0x2D, 0x20,
+	0x42, 0x61, 0x73, 0x65, 0x6C, 0x69, 0x6E, 0x65,
+	0x0D, 0x0A, 0x00, 0x00, 0x4F, 0x70, 0x65, 0x6E,
+	0x20, 0x54, 0x65, 0x73, 0x74, 0x28, 0x54, 0x48,
+	0x44, 0x3A, 0x25, 0x64, 0x29, 0x20, 0x5B, 0x50,
+	0x61, 0x73, 0x73, 0x5D, 0x0D, 0x0A, 0x00, 0x00,
+	0xF0, 0xB5, 0x17, 0x49, 0x00, 0x20, 0x7D, 0x27,
+	0x09, 0x68, 0xFF, 0x00, 0x02, 0x46, 0x89, 0x07,
+	0x24, 0xD5, 0x14, 0x49, 0x14, 0x4D, 0x09, 0x68,
+	0xCE, 0x26, 0x0B, 0x18, 0xDB, 0x7E, 0x41, 0x2B,
+	0x0A, 0xD0, 0x12, 0x4B, 0x44, 0x00, 0x1B, 0x5F,
+	0xBB, 0x42, 0x00, 0xDA, 0x01, 0x22, 0x2B, 0x5C,
+	0x9C, 0x07, 0x01, 0xD5, 0x33, 0x40, 0x2B, 0x54,
+	0x40, 0x1C, 0xC0, 0xB2, 0x30, 0x28, 0xEC, 0xD3,
+	0x00, 0x2A, 0x0B, 0xD0, 0x00, 0x20, 0x0A, 0x18,
+	0xD2, 0x7E, 0x41, 0x2A, 0x02, 0xD0, 0x2A, 0x5C,
+	0x32, 0x40, 0x2A, 0x54, 0x40, 0x1C, 0xC0, 0xB2,
+	0x30, 0x28, 0xF4, 0xD3, 0x01, 0x20, 0xF0, 0xBD,
+	0x0C, 0x05, 0x00, 0x20, 0x98, 0x01, 0x00, 0x20,
+	0xD8, 0x04, 0x00, 0x20, 0xE4, 0x02, 0x00, 0x20,
+	0x70, 0xB5, 0x20, 0x48, 0x01, 0x25, 0xC4, 0x7D,
+	0x1F, 0x48, 0x80, 0x88, 0x40, 0x07, 0x01, 0xD4,
+	0x01, 0x20, 0x70, 0xBD, 0xFF, 0xF7, 0x34, 0xFB,
+	0x1C, 0x48, 0xFE, 0xF7, 0x5F, 0xFF, 0x00, 0x28,
+	0x0F, 0xD1, 0x1B, 0xA0, 0x01, 0xF0, 0x91, 0xFC,
+	0x01, 0x20, 0x20, 0x49, 0x00, 0x25, 0x08, 0x70,
+	0x00, 0x2C, 0x06, 0xD0, 0x64, 0x1E, 0xE4, 0xB2,
+	0x21, 0x46, 0x1D, 0xA0, 0x01, 0xF0, 0x85, 0xFC,
+	0xE8, 0xE7, 0xFE, 0xF7, 0x95, 0xFF, 0xFF, 0xF7,
+	0x0B, 0xFA, 0xFF, 0xF7, 0xDD, 0xFE, 0x00, 0x28,
+	0x07, 0xD1, 0x00, 0x25, 0x00, 0x2C, 0x04, 0xD0,
+	0x64, 0x1E, 0xE4, 0xB2, 0x21, 0x46, 0x1C, 0xA0,
+	0x0A, 0xE0, 0xFF, 0xF7, 0x27, 0xFE, 0x00, 0x28,
+	0x0A, 0xD1, 0x00, 0x25, 0x00, 0x2C, 0x07, 0xD0,
+	0x64, 0x1E, 0xE4, 0xB2, 0x21, 0x46, 0x1C, 0xA0,
+	0x01, 0xF0, 0x67, 0xFC, 0x01, 0x25, 0xC9, 0xE7,
+	0x28, 0x46, 0x70, 0xBD, 0xA8, 0x06, 0x00, 0x20,
+	0x74, 0x06, 0x00, 0x20, 0xAC, 0x03, 0x00, 0x20,
+	0x4F, 0x70, 0x65, 0x6E, 0x20, 0x54, 0x65, 0x73,
+	0x74, 0x20, 0x43, 0x61, 0x6C, 0x69, 0x62, 0x72,
+	0x61, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x4E, 0x47,
+	0x21, 0x0D, 0x0A, 0x00, 0xD8, 0x04, 0x00, 0x20,
+	0x4F, 0x70, 0x65, 0x6E, 0x20, 0x43, 0x61, 0x6C,
+	0x69, 0x62, 0x72, 0x61, 0x74, 0x69, 0x6F, 0x6E,
+	0x20, 0x52, 0x65, 0x74, 0x72, 0x79, 0x20, 0x28,
+	0x25, 0x64, 0x29, 0x0D, 0x0A, 0x00, 0x00, 0x00,
+	0x4F, 0x70, 0x65, 0x6E, 0x20, 0x54, 0x65, 0x73,
+	0x74, 0x20, 0x52, 0x65, 0x74, 0x72, 0x79, 0x20,
+	0x28, 0x25, 0x64, 0x29, 0x0D, 0x0A, 0x00, 0x00,
+	0x4F, 0x70, 0x65, 0x6E, 0x20, 0x43, 0x43, 0x20,
+	0x52, 0x65, 0x74, 0x72, 0x79, 0x20, 0x28, 0x25,
+	0x64, 0x29, 0x0D, 0x0A, 0x00, 0x00, 0x00, 0x00,
+	0x10, 0xB5, 0x17, 0x4A, 0x11, 0x78, 0x81, 0x42,
+	0x29, 0xD0, 0x16, 0x49, 0x0B, 0x68, 0x01, 0x24,
+	0xA4, 0x02, 0x23, 0x43, 0x0B, 0x60, 0x10, 0x70,
+	0x05, 0x22, 0x40, 0x24, 0x12, 0x49, 0x12, 0x07,
+	0x80, 0x23, 0x00, 0x28, 0x09, 0xD0, 0x08, 0x68,
+	0x40, 0x06, 0x0C, 0xD4, 0x08, 0x68, 0x20, 0x43,
+	0x08, 0x60, 0xD0, 0x68, 0x98, 0x43, 0xD0, 0x60,
+	0x05, 0xE0, 0xD0, 0x68, 0x18, 0x43, 0xD0, 0x60,
+	0x08, 0x68, 0xA0, 0x43, 0x08, 0x60, 0x08, 0x68,
+	0x18, 0x43, 0x08, 0x60, 0xD0, 0x68, 0x01, 0x21,
+	0xC9, 0x03, 0x08, 0x43, 0xD0, 0x60, 0xE1, 0x20,
+	0x00, 0x02, 0x01, 0xF0, 0x3B, 0xF8, 0x10, 0xBD,
+	0x04, 0x00, 0x00, 0x20, 0x00, 0x10, 0x00, 0x50,
+	0x00, 0x11, 0x00, 0x50, 0x06, 0x49, 0x83, 0x20,
+	0x08, 0x70, 0x06, 0x49, 0x00, 0x20, 0x08, 0x70,
+	0x05, 0x48, 0x00, 0x68, 0x05, 0x49, 0x40, 0x05,
+	0x40, 0x0F, 0x08, 0x73, 0x70, 0x47, 0x00, 0x00,
+	0x20, 0x08, 0x00, 0x20, 0x30, 0x00, 0x00, 0x20,
+	0x00, 0x11, 0x00, 0x50, 0x20, 0x00, 0x00, 0x20,
+	0xF0, 0xB5, 0x04, 0x46, 0x8F, 0xB0, 0xA6, 0x48,
+	0x0A, 0x90, 0xA5, 0x48, 0xA6, 0x4D, 0x60, 0x30,
+	0x0B, 0x90, 0xA4, 0x48, 0x03, 0x90, 0xA5, 0x48,
+	0x05, 0x95, 0x01, 0x68, 0x22, 0x46, 0xA4, 0xA0,
+	0x01, 0xF0, 0xC7, 0xFB, 0x03, 0x98, 0x08, 0x90,
+	0xA5, 0x48, 0xA6, 0x4A, 0x00, 0x21, 0x09, 0x95,
+	0x8B, 0x00, 0x08, 0x9D, 0x49, 0x1C, 0xE8, 0x50,
+	0x09, 0x9D, 0x89, 0xB2, 0xEA, 0x50, 0x18, 0x29,
+	0xF6, 0xD3, 0x01, 0x20, 0x80, 0x02, 0x06, 0x90,
+	0x20, 0x46, 0x07, 0x94, 0x50, 0x30, 0x00, 0xB2,
+	0x50, 0x3C, 0x0C, 0x90, 0x20, 0xB2, 0x0D, 0x90,
+	0x00, 0xF0, 0x96, 0xFC, 0x00, 0xF0, 0xC6, 0xFE,
+	0x00, 0xF0, 0x6E, 0xF9, 0x98, 0x48, 0x00, 0x78,
+	0xC0, 0x07, 0x18, 0xD1, 0x01, 0x90, 0x00, 0x20,
+	0x08, 0x9A, 0x81, 0x00, 0x53, 0x58, 0x0A, 0x9A,
+	0x40, 0x1C, 0x53, 0x50, 0x09, 0x9A, 0x80, 0xB2,
+	0x53, 0x58, 0x0B, 0x9A, 0x18, 0x28, 0x53, 0x50,
+	0xF2, 0xD3, 0x00, 0xF0, 0xC7, 0xFD, 0x00, 0xF0,
+	0xAD, 0xFE, 0x00, 0xF0, 0x55, 0xF9, 0x8C, 0x48,
+	0x00, 0x78, 0xC0, 0x07, 0x02, 0xD0, 0x00, 0x20,
+	0x0F, 0xB0, 0xF0, 0xBD, 0x3B, 0x46, 0x89, 0xA0,
+	0x00, 0x9A, 0x01, 0x99, 0x01, 0xF0, 0x81, 0xFB,
+	0x00, 0x25, 0x2D, 0xE0, 0x00, 0x2D, 0x2B, 0xD0,
+	0x01, 0x98, 0x05, 0x28, 0x73, 0xD9, 0x30, 0x2F,
+	0x71, 0xD2, 0x7A, 0x4C, 0x05, 0x98, 0x40, 0x3C,
+	0x04, 0x90, 0x60, 0x6B, 0x03, 0x26, 0x36, 0x05,
+	0x01, 0x21, 0xB0, 0x43, 0x49, 0x05, 0x40, 0x18,
+	0x60, 0x63, 0x00, 0xF0, 0x9F, 0xFD, 0x00, 0xF0,
+	0x85, 0xFE, 0x00, 0xF0, 0x2D, 0xF9, 0x60, 0x6B,
+	0x01, 0x21, 0xB0, 0x43, 0x09, 0x05, 0x40, 0x18,
+	0x60, 0x63, 0x00, 0x27, 0x00, 0x26, 0x79, 0x48,
+	0x00, 0x68, 0x80, 0x19, 0xC0, 0x7E, 0x41, 0x28,
+	0x0E, 0xD0, 0x70, 0x00, 0x04, 0x99, 0x02, 0x90,
+	0x0C, 0x5A, 0x16, 0x2E, 0x12, 0xD0, 0x1A, 0xE0,
+	0x00, 0x98, 0x30, 0x28, 0x77, 0xD0, 0x03, 0x98,
+	0x04, 0x90, 0x00, 0x20, 0x00, 0x90, 0xE9, 0xE7,
+	0x00, 0x2D, 0x02, 0xD0, 0x7F, 0x1C, 0xBF, 0xB2,
+	0x68, 0xE0, 0x00, 0x98, 0x40, 0x1C, 0x80, 0xB2,
+	0x00, 0x90, 0x63, 0xE0, 0x6A, 0x48, 0x2C, 0x23,
+	0x00, 0x68, 0x22, 0x46, 0xC3, 0x5E, 0x31, 0x46,
+	0x68, 0xA0, 0x01, 0xF0, 0x36, 0xFB, 0x01, 0x98,
+	0x0A, 0x28, 0x0C, 0xD2, 0x64, 0x49, 0x02, 0x98,
+	0x09, 0x68, 0x09, 0x5E, 0x07, 0x98, 0x81, 0x42,
+	0x01, 0xDA, 0x06, 0x98, 0x84, 0x43, 0x06, 0x98,
+	0x40, 0x08, 0x04, 0x43, 0x32, 0xE0, 0x5E, 0x48,
+	0x02, 0x99, 0x00, 0x68, 0x41, 0x5E, 0x00, 0x29,
+	0x01, 0xDB, 0x08, 0x46, 0x00, 0xE0, 0x48, 0x42,
+	0x00, 0xB2, 0x40, 0x30, 0xC2, 0x17, 0x52, 0x0E,
+	0x10, 0x18, 0x0D, 0x9A, 0xC0, 0x11, 0x91, 0x42,
+	0x10, 0xDA, 0x21, 0x05, 0x06, 0xD5, 0x20, 0x1A,
+	0x84, 0xB2, 0x01, 0x20, 0xC0, 0x02, 0x84, 0x42,
+	0x11, 0xD3, 0x17, 0xE0, 0x84, 0x42, 0x03, 0xDD,
+	0x20, 0x1A, 0x84, 0xB2, 0x12, 0xE0, 0x2E, 0xE0,
+	0x00, 0x24, 0x0F, 0xE0, 0x0C, 0x9A, 0x91, 0x42,
+	0x15, 0xDD, 0x21, 0x05, 0x05, 0xD5, 0x20, 0x18,
+	0x84, 0xB2, 0x4D, 0x48, 0x04, 0xE0, 0x04, 0x46,
+	0x04, 0xE0, 0x20, 0x18, 0x84, 0xB2, 0x4B, 0x48,
+	0x84, 0x42, 0xF8, 0xD8, 0x00, 0x2D, 0x0F, 0xD0,
+	0x01, 0x20, 0xC0, 0x02, 0x04, 0x43, 0x05, 0x99,
+	0x02, 0x98, 0x0C, 0x52, 0x0E, 0xE0, 0x00, 0x2D,
+	0x02, 0xD0, 0x7F, 0x1C, 0xBF, 0xB2, 0xF3, 0xE7,
+	0x00, 0x98, 0x40, 0x1C, 0x80, 0xB2, 0x00, 0x90,
+	0x03, 0x99, 0x02, 0x98, 0x0C, 0x52, 0x01, 0x98,
+	0x05, 0x28, 0xE9, 0xD9, 0x76, 0x1C, 0xB6, 0xB2,
+	0x30, 0x2E, 0x00, 0xD2, 0x77, 0xE7, 0x6D, 0x1C,
+	0xED, 0xB2, 0x02, 0x2D, 0x00, 0xD2, 0x51, 0xE7,
+	0x06, 0x98, 0x40, 0x08, 0x06, 0x90, 0x38, 0xA0,
+	0x01, 0xF0, 0xCB, 0xFA, 0x00, 0x98, 0x30, 0x28,
+	0x03, 0xD1, 0x30, 0x2F, 0x01, 0xD1, 0x40, 0x20,
+	0x01, 0x90, 0x01, 0x98, 0x40, 0x1C, 0x80, 0xB2,
+	0x01, 0x90, 0x40, 0x28, 0x00, 0xD8, 0x1A, 0xE7,
+	0x1C, 0x4C, 0x40, 0x3C, 0x60, 0x6B, 0x03, 0x25,
+	0x2D, 0x05, 0x01, 0x21, 0xA8, 0x43, 0x09, 0x05,
+	0x40, 0x18, 0x60, 0x63, 0x00, 0xF0, 0xE6, 0xFC,
+	0x00, 0xF0, 0xCC, 0xFD, 0x00, 0xF0, 0x74, 0xF8,
+	0x21, 0x4E, 0x01, 0x21, 0x30, 0x68, 0x00, 0xF0,
+	0x8F, 0xFE, 0x60, 0x6B, 0x01, 0x21, 0xA8, 0x43,
+	0x49, 0x05, 0x40, 0x18, 0x60, 0x63, 0x00, 0xF0,
+	0xD5, 0xFC, 0x00, 0xF0, 0xBB, 0xFD, 0x00, 0xF0,
+	0x63, 0xF8, 0x01, 0x21, 0x30, 0x68, 0x00, 0xF0,
+	0x7F, 0xFE, 0x60, 0x6B, 0x28, 0x43, 0x60, 0x63,
+	0x00, 0xF0, 0xC8, 0xFC, 0x00, 0xF0, 0xAE, 0xFD,
+	0x00, 0xF0, 0x56, 0xF8, 0x01, 0x21, 0x30, 0x68,
+	0x00, 0xF0, 0x72, 0xFE, 0x01, 0x20, 0xFF, 0xE6,
+	0x00, 0x20, 0x00, 0x50, 0xF4, 0x05, 0x00, 0x20,
+	0x00, 0x00, 0x01, 0x20, 0xC0, 0x10, 0x00, 0x50,
+	0x5B, 0x43, 0x46, 0x42, 0x3A, 0x25, 0x78, 0x3A,
+	0x25, 0x64, 0x5D, 0x0D, 0x0A, 0x00, 0x00, 0x00,
+	0x00, 0x04, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x0C,
+	0x30, 0x00, 0x00, 0x20, 0x5B, 0x25, 0x64, 0x3A,
+	0x25, 0x64, 0x3A, 0x25, 0x64, 0x5D, 0x20, 0x0D,
+	0x0A, 0x00, 0x00, 0x00, 0x98, 0x01, 0x00, 0x20,
+	0x4C, 0x01, 0x00, 0x20, 0x25, 0x64, 0x2C, 0x30,
+	0x78, 0x25, 0x78, 0x2C, 0x25, 0x64, 0x2C, 0x00,
+	0xFF, 0x0F, 0x00, 0x00, 0xFF, 0x07, 0x00, 0x00,
+	0x0D, 0x0A, 0x00, 0x00, 0x10, 0xB5, 0x0B, 0x49,
+	0x30, 0x24, 0x00, 0x28, 0x0A, 0x4A, 0x0B, 0x4B,
+	0x08, 0x68, 0x06, 0xD0, 0x20, 0x43, 0x08, 0x60,
+	0x09, 0x48, 0x10, 0x60, 0x08, 0x48, 0x60, 0x30,
+	0x05, 0xE0, 0xA0, 0x43, 0x08, 0x60, 0x07, 0x48,
+	0x10, 0x60, 0x06, 0x48, 0x60, 0x30, 0x18, 0x60,
+	0x10, 0xBD, 0x00, 0x00, 0x00, 0x10, 0x00, 0x50,
+	0x4C, 0x01, 0x00, 0x20, 0x50, 0x01, 0x00, 0x20,
+	0x00, 0x00, 0x04, 0x20, 0x00, 0x10, 0x04, 0x20,
+	0xF0, 0xB5, 0x11, 0x48, 0x01, 0x68, 0x11, 0x4D,
+	0x89, 0x06, 0x30, 0x22, 0x60, 0x35, 0x00, 0x29,
+	0x0E, 0x4B, 0x0F, 0x4C, 0x0F, 0x4E, 0x10, 0x4F,
+	0x01, 0x68, 0x0A, 0xDA, 0x91, 0x43, 0x01, 0x60,
+	0x23, 0x60, 0x0E, 0x48, 0x35, 0x60, 0x07, 0x60,
+	0x0B, 0x48, 0x0D, 0x49, 0x60, 0x30, 0x08, 0x60,
+	0xF0, 0xBD, 0x11, 0x43, 0x01, 0x60, 0x08, 0x48,
+	0x27, 0x60, 0x60, 0x30, 0x30, 0x60, 0x07, 0x48,
+	0x03, 0x60, 0x07, 0x48, 0x05, 0x60, 0xF0, 0xBD,
+	0x00, 0x10, 0x00, 0x50, 0x00, 0x00, 0x04, 0x20,
+	0x40, 0x00, 0x00, 0x20, 0x44, 0x00, 0x00, 0x20,
+	0x00, 0x10, 0x04, 0x20, 0x4C, 0x01, 0x00, 0x20,
+	0x50, 0x01, 0x00, 0x20, 0x00, 0xB5, 0x09, 0x48,
+	0x00, 0x78, 0x00, 0x28, 0x08, 0xD0, 0x01, 0x28,
+	0x09, 0xD0, 0x02, 0x28, 0x03, 0xD1, 0x06, 0x49,
+	0x04, 0x20, 0x00, 0xF0, 0x65, 0xF8, 0x00, 0xBD,
+	0x04, 0x49, 0x01, 0x20, 0xF9, 0xE7, 0x04, 0x49,
+	0x02, 0x20, 0xF6, 0xE7, 0x0D, 0x08, 0x00, 0x20,
+	0x70, 0x05, 0x00, 0x20, 0x1C, 0x02, 0x00, 0x20,
+	0x10, 0x05, 0x00, 0x20, 0xF8, 0xB5, 0x07, 0x46,
+	0x00, 0xF0, 0xD2, 0xFA, 0x21, 0x4D, 0x60, 0x21,
+	0x28, 0x46, 0xFE, 0xF7, 0xA2, 0xFA, 0x00, 0x24,
+	0x00, 0xF0, 0xFC, 0xFC, 0xFF, 0xF7, 0xA4, 0xFF,
+	0xFE, 0xF7, 0x39, 0xFB, 0x00, 0x2C, 0x0C, 0xD0,
+	0x1B, 0x49, 0x00, 0x20, 0x0E, 0x68, 0x42, 0x00,
+	0x51, 0x19, 0x0B, 0x88, 0xB2, 0x5A, 0x40, 0x1C,
+	0x9A, 0x18, 0xC0, 0xB2, 0x0A, 0x80, 0x30, 0x28,
+	0xF5, 0xD3, 0x64, 0x1C, 0xE4, 0xB2, 0x05, 0x2C,
+	0xE6, 0xD3, 0x00, 0x20, 0x41, 0x00, 0x49, 0x19,
+	0x00, 0x22, 0x8A, 0x5E, 0x40, 0x1C, 0x92, 0x10,
+	0xC0, 0xB2, 0x0A, 0x80, 0x30, 0x28, 0xF5, 0xD3,
+	0x39, 0x46, 0x0E, 0xA0, 0x01, 0xF0, 0xB9, 0xF9,
+	0x01, 0x21, 0x28, 0x46, 0x00, 0xF0, 0x9C, 0xFD,
+	0x01, 0x20, 0xFE, 0xF7, 0xBF, 0xFB, 0x0C, 0x4C,
+	0x20, 0x78, 0x38, 0x42, 0x08, 0xD1, 0x0B, 0x48,
+	0x60, 0x22, 0x29, 0x46, 0x00, 0x68, 0xFE, 0xF7,
+	0x4F, 0xFA, 0x20, 0x78, 0x38, 0x43, 0x20, 0x70,
+	0x01, 0x20, 0xF8, 0xBD, 0x00, 0x00, 0x01, 0x20,
+	0x4C, 0x01, 0x00, 0x20, 0x42, 0x43, 0x5F, 0x4F,
+	0x4B, 0x5F, 0x25, 0x64, 0x0D, 0x0A, 0x00, 0x00,
+	0x38, 0x00, 0x00, 0x20, 0x48, 0x00, 0x00, 0x20,
+	0xF0, 0xB5, 0x21, 0x48, 0x00, 0x22, 0x00, 0x68,
+	0x20, 0x4C, 0xA0, 0x42, 0x28, 0xD8, 0x20, 0x48,
+	0x00, 0x78, 0x04, 0x28, 0x27, 0xD1, 0x1F, 0x4D,
+	0x1F, 0x4C, 0x2D, 0x68, 0x00, 0x20, 0x2E, 0x18,
+	0xF6, 0x7E, 0x41, 0x2E, 0x08, 0xD0, 0x43, 0x00,
+	0x1F, 0x19, 0x00, 0x26, 0xBE, 0x5F, 0xCB, 0x5E,
+	0xF3, 0x1A, 0x00, 0xD5, 0x5B, 0x42, 0x1B, 0xB2,
+	0x9A, 0x42, 0x00, 0xDA, 0x1A, 0x46, 0x40, 0x1C,
+	0xC0, 0xB2, 0x30, 0x28, 0xEB, 0xD3, 0x60, 0x20,
+	0x40, 0x5D, 0x90, 0x35, 0x40, 0x1C, 0x50, 0x43,
+	0x6A, 0x7B, 0x2B, 0x7B, 0x00, 0x11, 0x12, 0x02,
+	0x00, 0xB2, 0x1A, 0x43, 0x82, 0x42, 0x03, 0xDA,
+	0x0E, 0x49, 0x00, 0x20, 0x08, 0x70, 0xF0, 0xBD,
+	0x00, 0x20, 0x42, 0x00, 0x8B, 0x5E, 0x00, 0x26,
+	0xDD, 0x00, 0xEB, 0x1A, 0x15, 0x19, 0xAE, 0x5F,
+	0x40, 0x1C, 0x9B, 0x19, 0xDB, 0x10, 0xC0, 0xB2,
+	0x8B, 0x52, 0x30, 0x28, 0xF1, 0xD3, 0xF0, 0xBD,
+	0x5C, 0x01, 0x00, 0x20, 0x40, 0x77, 0x1B, 0x00,
+	0x93, 0x01, 0x00, 0x20, 0x98, 0x01, 0x00, 0x20,
+	0x00, 0x00, 0x01, 0x20, 0x38, 0x00, 0x00, 0x20,
+	0x70, 0xB5, 0x21, 0x48, 0x21, 0x49, 0x00, 0x78,
+	0x00, 0x28, 0x06, 0xD0, 0x01, 0x28, 0x08, 0xD0,
+	0x02, 0x28, 0x16, 0xD0, 0x05, 0x28, 0x2F, 0xD1,
+	0x34, 0xE0, 0x01, 0x25, 0x2C, 0x46, 0x1C, 0x48,
+	0x02, 0xE0, 0x02, 0x25, 0x1B, 0x48, 0x2C, 0x46,
+	0x08, 0x60, 0x00, 0x22, 0x1A, 0x49, 0x28, 0x46,
+	0xFE, 0xF7, 0x40, 0xFB, 0x00, 0x28, 0x18, 0xD0,
+	0x18, 0x48, 0x00, 0x78, 0x00, 0x28, 0x04, 0xD0,
+	0x13, 0xE0, 0x04, 0x25, 0x2C, 0x46, 0x16, 0x48,
+	0xEE, 0xE7, 0x16, 0x48, 0x41, 0x6B, 0x03, 0x22,
+	0x12, 0x05, 0x11, 0x43, 0x41, 0x63, 0x14, 0x48,
+	0x00, 0xF0, 0x4C, 0xFA, 0x00, 0x28, 0x0B, 0xD1,
+	0x20, 0x46, 0xFF, 0xF7, 0x27, 0xFF, 0x00, 0x28,
+	0x06, 0xD1, 0x01, 0x22, 0x21, 0x46, 0x28, 0x46,
+	0x00, 0xF0, 0x1E, 0xF8, 0x00, 0x28, 0x04, 0xD0,
+	0x0C, 0x48, 0x00, 0x78, 0xC0, 0x07, 0x01, 0xD0,
+	0x00, 0x20, 0x70, 0xBD, 0x01, 0x20, 0x70, 0xBD,
+	0x92, 0x01, 0x00, 0x20, 0x48, 0x00, 0x00, 0x20,
+	0x1C, 0x02, 0x00, 0x20, 0x10, 0x05, 0x00, 0x20,
+	0x00, 0x20, 0x00, 0x50, 0x3B, 0x01, 0x00, 0x20,
+	0x70, 0x05, 0x00, 0x20, 0x80, 0x10, 0x00, 0x50,
+	0x00, 0x00, 0x01, 0x20, 0x30, 0x00, 0x00, 0x20,
+	0xF7, 0xB5, 0x07, 0x46, 0x14, 0x48, 0x00, 0x25,
+	0x05, 0x70, 0x78, 0x07, 0x1D, 0xD0, 0x01, 0x21,
+	0x02, 0x20, 0x00, 0xF0, 0x45, 0xFB, 0x11, 0x4C,
+	0x60, 0x6B, 0x03, 0x26, 0x36, 0x05, 0x01, 0x21,
+	0xB0, 0x43, 0x09, 0x05, 0x40, 0x18, 0x60, 0x63,
+	0x00, 0x20, 0xFF, 0xF7, 0xFD, 0xFC, 0x00, 0x28,
+	0x0D, 0xD0, 0x01, 0x98, 0xFF, 0xF7, 0xE6, 0xFE,
+	0x01, 0x22, 0x09, 0x49, 0x38, 0x46, 0xFE, 0xF7,
+	0xE1, 0xFA, 0x08, 0x48, 0x05, 0x70, 0x08, 0x48,
+	0x05, 0x70, 0x01, 0x20, 0xFE, 0xBD, 0x60, 0x6B,
+	0x30, 0x43, 0x60, 0x63, 0x00, 0x20, 0xFE, 0xBD,
+	0x90, 0x01, 0x00, 0x20, 0x80, 0x10, 0x00, 0x50,
+	0x00, 0x20, 0x00, 0x50, 0x3B, 0x01, 0x00, 0x20,
+	0x3C, 0x01, 0x00, 0x20, 0x70, 0xB5, 0x10, 0x4C,
+	0x60, 0x6B, 0x03, 0x25, 0x2D, 0x05, 0x01, 0x26,
+	0xA8, 0x43, 0x36, 0x05, 0x80, 0x19, 0x60, 0x63,
+	0x0C, 0x49, 0x02, 0x20, 0x08, 0x5E, 0xFF, 0xF7,
+	0xCF, 0xFC, 0x00, 0x28, 0x60, 0x6B, 0x09, 0xD0,
+	0xA8, 0x43, 0x80, 0x19, 0x60, 0x63, 0x08, 0x49,
+	0x00, 0x20, 0x08, 0x70, 0x07, 0x49, 0x08, 0x70,
+	0x01, 0x20, 0x70, 0xBD, 0xA8, 0x43, 0x80, 0x19,
+	0x60, 0x63, 0x00, 0x20, 0x70, 0xBD, 0x00, 0x00,
+	0x80, 0x10, 0x00, 0x50, 0xCE, 0x00, 0x00, 0x20,
+	0x3B, 0x01, 0x00, 0x20, 0x3C, 0x01, 0x00, 0x20,
+	0xF0, 0xB5, 0xB4, 0x48, 0x01, 0x22, 0x23, 0x23,
+	0x13, 0x24, 0x92, 0x02, 0x5B, 0x01, 0xA4, 0x01,
+	0x05, 0x46, 0x1C, 0xC5, 0x29, 0x21, 0x49, 0x01,
+	0xC1, 0x60, 0xAF, 0x48, 0x07, 0x68, 0x38, 0x46,
+	0x40, 0x30, 0x45, 0x7C, 0x06, 0x7C, 0x2D, 0x04,
+	0x36, 0x02, 0x35, 0x43, 0x0C, 0x35, 0x15, 0x60,
+	0x45, 0x7C, 0x06, 0x7C, 0x2D, 0x04, 0x36, 0x02,
+	0x35, 0x43, 0x0C, 0x35, 0x55, 0x60, 0x01, 0x25,
+	0x95, 0x60, 0xD5, 0x60, 0x05, 0x7D, 0xBC, 0x46,
+	0x6E, 0x1E, 0x6D, 0x08, 0x6D, 0x1E, 0xF6, 0x05,
+	0xED, 0x05, 0xF6, 0x09, 0xED, 0x0D, 0x2E, 0x43,
+	0x16, 0x61, 0xA0, 0x4D, 0x55, 0x61, 0x03, 0x25,
+	0x95, 0x61, 0x03, 0x26, 0x36, 0x02, 0x00, 0x25,
+	0x16, 0x62, 0xD5, 0x61, 0x06, 0x7E, 0xD6, 0x62,
+	0x06, 0x7E, 0x87, 0x7E, 0xF6, 0x19, 0x16, 0x63,
+	0x87, 0x7E, 0x06, 0x7E, 0x7F, 0x00, 0xF6, 0x19,
+	0x56, 0x63, 0x95, 0x63, 0x06, 0x7E, 0xBC, 0x36,
+	0xD6, 0x63, 0x06, 0x7E, 0x87, 0x7E, 0xF6, 0x19,
+	0xBC, 0x36, 0x16, 0x64, 0x07, 0x7E, 0x86, 0x7E,
+	0x95, 0x64, 0x76, 0x00, 0xBC, 0x36, 0xBE, 0x19,
+	0x56, 0x64, 0x67, 0x46, 0x55, 0x62, 0x8E, 0x4E,
+	0x50, 0x37, 0x96, 0x62, 0xBC, 0x46, 0xFE, 0x7B,
+	0xBF, 0x7B, 0x36, 0x02, 0x3E, 0x43, 0xF6, 0x1C,
+	0xB7, 0x05, 0x8A, 0x4E, 0xBF, 0x0D, 0xBE, 0x19,
+	0xD6, 0x64, 0x89, 0x4E, 0x16, 0x65, 0x46, 0x7C,
+	0x0C, 0x3E, 0xF7, 0xB2, 0x05, 0x26, 0x76, 0x02,
+	0xBE, 0x19, 0x56, 0x65, 0x06, 0x7D, 0x36, 0x02,
+	0x21, 0x36, 0x96, 0x65, 0xD5, 0x65, 0x42, 0x7C,
+	0x06, 0x7C, 0x12, 0x04, 0x36, 0x02, 0x32, 0x43,
+	0x0C, 0x32, 0x1A, 0x60, 0x42, 0x7C, 0x06, 0x7C,
+	0x12, 0x04, 0x36, 0x02, 0x32, 0x43, 0x0C, 0x32,
+	0x5A, 0x60, 0x01, 0x22, 0x9A, 0x60, 0xDA, 0x60,
+	0x42, 0x7D, 0x56, 0x1E, 0x52, 0x08, 0x52, 0x1E,
+	0xF6, 0x05, 0xD2, 0x05, 0xF7, 0x09, 0xD2, 0x0D,
+	0x17, 0x43, 0x72, 0x4A, 0x1F, 0x61, 0x5A, 0x61,
+	0x03, 0x22, 0xDD, 0x61, 0x9A, 0x61, 0x12, 0x02,
+	0x1A, 0x62, 0x02, 0x7E, 0xDA, 0x62, 0x02, 0x7E,
+	0xC6, 0x7E, 0x92, 0x19, 0x1A, 0x63, 0xC6, 0x7E,
+	0x02, 0x7E, 0x76, 0x00, 0x92, 0x19, 0x9D, 0x63,
+	0x5A, 0x63, 0x02, 0x7E, 0x67, 0x46, 0xBC, 0x32,
+	0xDA, 0x63, 0x02, 0x7E, 0xC6, 0x7E, 0x92, 0x19,
+	0xBC, 0x32, 0x1A, 0x64, 0xC6, 0x7E, 0x02, 0x7E,
+	0x76, 0x00, 0xBC, 0x36, 0x9D, 0x64, 0x92, 0x19,
+	0x5D, 0x62, 0x5A, 0x64, 0x60, 0x4A, 0x9A, 0x62,
+	0xFA, 0x7B, 0xBE, 0x7B, 0x12, 0x02, 0x32, 0x43,
+	0xD2, 0x1C, 0x96, 0x05, 0x5D, 0x4A, 0xB6, 0x0D,
+	0xB2, 0x18, 0xDA, 0x64, 0x5C, 0x4A, 0x1A, 0x65,
+	0x42, 0x7C, 0x0C, 0x3A, 0xD6, 0xB2, 0x05, 0x22,
+	0x52, 0x02, 0xB2, 0x18, 0x5A, 0x65, 0x42, 0x7D,
+	0xDD, 0x65, 0x12, 0x02, 0x21, 0x32, 0x9A, 0x65,
+	0xC2, 0x7C, 0x83, 0x7C, 0x12, 0x04, 0x1B, 0x02,
+	0x1A, 0x43, 0x0C, 0x32, 0x23, 0x46, 0x1A, 0x60,
+	0xC2, 0x7C, 0x83, 0x7C, 0x12, 0x04, 0x1B, 0x02,
+	0x1A, 0x43, 0x0C, 0x32, 0x23, 0x46, 0x5A, 0x60,
+	0x82, 0x7D, 0x52, 0x1E, 0xD3, 0x05, 0xDB, 0x0D,
+	0x22, 0x46, 0x93, 0x60, 0x82, 0x7D, 0x52, 0x08,
+	0x52, 0x1E, 0xD2, 0x05, 0x23, 0x46, 0xD2, 0x0D,
+	0x1D, 0x61, 0xDA, 0x60, 0x47, 0x4A, 0x5A, 0x61,
+	0x26, 0x46, 0x01, 0x23, 0xF5, 0x61, 0xB3, 0x61,
+	0x13, 0x12, 0x33, 0x62, 0x43, 0x7E, 0xF3, 0x62,
+	0x43, 0x7E, 0x06, 0x7F, 0x9B, 0x19, 0x26, 0x46,
+	0x33, 0x63, 0x06, 0x7F, 0x43, 0x7E, 0x76, 0x00,
+	0x9B, 0x19, 0x26, 0x46, 0xB5, 0x63, 0xF5, 0x63,
+	0x35, 0x64, 0x73, 0x63, 0x65, 0x64, 0x38, 0x4B,
+	0xA5, 0x64, 0x3F, 0x33, 0x65, 0x62, 0xA3, 0x62,
+	0x36, 0x4B, 0xDB, 0x1C, 0xE3, 0x64, 0x36, 0x4B,
+	0x23, 0x65, 0xC6, 0x7C, 0x0C, 0x3E, 0xF7, 0xB2,
+	0x05, 0x26, 0x76, 0x02, 0xBF, 0x19, 0x67, 0x65,
+	0x87, 0x7D, 0x7F, 0x08, 0x3F, 0x02, 0x21, 0x37,
+	0xA7, 0x65, 0xE5, 0x65, 0xC4, 0x7C, 0x80, 0x7C,
+	0x24, 0x04, 0x00, 0x02, 0x04, 0x43, 0x0C, 0x34,
+	0x27, 0x48, 0x0C, 0x60, 0x00, 0x68, 0x40, 0x30,
+	0xC4, 0x7C, 0x87, 0x7C, 0x24, 0x04, 0x3F, 0x02,
+	0x3C, 0x43, 0x0C, 0x34, 0x4C, 0x60, 0xC4, 0x7D,
+	0x64, 0x1E, 0xE4, 0x05, 0xE4, 0x0D, 0x8C, 0x60,
+	0xC4, 0x7D, 0x0D, 0x61, 0x64, 0x08, 0x64, 0x1E,
+	0xE4, 0x05, 0xE4, 0x0D, 0xCC, 0x60, 0x4A, 0x61,
+	0x01, 0x22, 0xCD, 0x61, 0x8A, 0x61, 0x12, 0x02,
+	0x0A, 0x62, 0x42, 0x7E, 0xCA, 0x62, 0x42, 0x7E,
+	0x44, 0x7F, 0x12, 0x19, 0x0A, 0x63, 0x44, 0x7F,
+	0x42, 0x7E, 0x64, 0x00, 0x12, 0x19, 0x8D, 0x63,
+	0x4A, 0x63, 0x42, 0x7E, 0xBC, 0x32, 0xCA, 0x63,
+	0x42, 0x7E, 0x44, 0x7F, 0x12, 0x19, 0xBC, 0x32,
+	0x0A, 0x64, 0x42, 0x7F, 0x44, 0x7E, 0x52, 0x00,
+	0xBC, 0x32, 0xA2, 0x18, 0x8D, 0x64, 0x4A, 0x64,
+	0x01, 0x22, 0x92, 0x04, 0x4A, 0x62, 0x0C, 0x4A,
+	0x3F, 0x32, 0x8A, 0x62, 0x0B, 0x4A, 0x0B, 0x65,
+	0xD2, 0x1C, 0xCA, 0x64, 0xC2, 0x7C, 0x0C, 0x3A,
+	0xD2, 0xB2, 0x92, 0x19, 0x4A, 0x65, 0xC0, 0x7D,
+	0xCD, 0x65, 0x40, 0x08, 0x00, 0x02, 0x21, 0x30,
+	0x88, 0x65, 0xF0, 0xBD, 0xD0, 0x05, 0x00, 0x20,
+	0x98, 0x01, 0x00, 0x20, 0x03, 0x00, 0x03, 0x00,
+	0x00, 0x3F, 0x3F, 0x3F, 0x00, 0x30, 0xBC, 0x00,
+	0x00, 0x26, 0x31, 0x00, 0x01, 0x00, 0x01, 0x00,
+	0x10, 0xB5, 0x00, 0xF0, 0x2F, 0xFA, 0x00, 0xF0,
+	0x5D, 0xF9, 0x01, 0x20, 0x80, 0x07, 0x41, 0x68,
+	0x04, 0x22, 0x11, 0x43, 0x41, 0x60, 0x00, 0x20,
+	0xFF, 0xF7, 0xAC, 0xFC, 0x09, 0x48, 0x01, 0x78,
+	0x09, 0x48, 0x00, 0x29, 0x04, 0xD0, 0x01, 0x68,
+	0xFF, 0x22, 0x01, 0x32, 0x11, 0x43, 0x01, 0x60,
+	0x01, 0x68, 0x01, 0x22, 0x92, 0x02, 0x91, 0x43,
+	0x01, 0x60, 0x04, 0x49, 0x02, 0x20, 0x08, 0x72,
+	0x10, 0xBD, 0x00, 0x00, 0x31, 0x00, 0x00, 0x20,
+	0x00, 0x10, 0x00, 0x50, 0x20, 0x00, 0x00, 0x20,
+	0x10, 0xB5, 0x01, 0x20, 0x80, 0x07, 0x41, 0x68,
+	0x04, 0x22, 0x11, 0x43, 0x41, 0x60, 0x09, 0x48,
+	0x07, 0x49, 0x41, 0x60, 0x08, 0x49, 0x81, 0x60,
+	0xFF, 0xF7, 0x52, 0xFE, 0x80, 0x21, 0x07, 0x48,
+	0xFD, 0xF7, 0x9B, 0xFF, 0x80, 0x21, 0x06, 0x48,
+	0xFD, 0xF7, 0x97, 0xFF, 0x10, 0xBD, 0x00, 0x00,
+	0x1F, 0x1F, 0x5F, 0x1F, 0x00, 0x10, 0x00, 0x50,
+	0x1F, 0x1F, 0x1F, 0x1F, 0xDC, 0x08, 0x00, 0x20,
+	0x9C, 0x01, 0x00, 0x20, 0x10, 0xB5, 0x0F, 0x49,
+	0x0A, 0x78, 0x0F, 0x49, 0x00, 0x2A, 0x09, 0x78,
+	0x04, 0xD0, 0x01, 0x2A, 0x07, 0xD0, 0x02, 0x2A,
+	0x12, 0xD1, 0x09, 0xE0, 0xC9, 0x07, 0x0F, 0xD0,
+	0x60, 0x22, 0x0A, 0x49, 0x08, 0xE0, 0x89, 0x07,
+	0x0A, 0xD5, 0x60, 0x22, 0x08, 0x49, 0x03, 0xE0,
+	0x49, 0x07, 0x05, 0xD5, 0x60, 0x22, 0x07, 0x49,
+	0xFD, 0xF7, 0x56, 0xFF, 0x01, 0x20, 0x10, 0xBD,
+	0x00, 0x20, 0x10, 0xBD, 0x92, 0x01, 0x00, 0x20,
+	0x38, 0x00, 0x00, 0x20, 0x1C, 0x02, 0x00, 0x20,
+	0x10, 0x05, 0x00, 0x20, 0x70, 0x05, 0x00, 0x20,
+	0x10, 0xB5, 0x13, 0x49, 0x13, 0x4B, 0x09, 0x68,
+	0x13, 0x4A, 0x40, 0x31, 0x00, 0x28, 0x0F, 0xD0,
+	0x01, 0x28, 0x12, 0xD0, 0x02, 0x28, 0x15, 0xD0,
+	0x03, 0x28, 0x08, 0xD1, 0x48, 0x7E, 0x84, 0x1E,
+	0x1C, 0x80, 0x49, 0x7F, 0x4B, 0x00, 0xC9, 0x18,
+	0x89, 0x1C, 0x40, 0x18, 0x10, 0x80, 0x10, 0xBD,
+	0x08, 0x7E, 0x84, 0x1E, 0x1C, 0x80, 0x89, 0x7E,
+	0xF4, 0xE7, 0x08, 0x7E, 0x84, 0x1E, 0x1C, 0x80,
+	0xC9, 0x7E, 0xEF, 0xE7, 0x48, 0x7E, 0x84, 0x1E,
+	0x1C, 0x80, 0x09, 0x7F, 0xEA, 0xE7, 0x00, 0x00,
+	0x98, 0x01, 0x00, 0x20, 0x3A, 0x00, 0x00, 0x20,
+	0x3C, 0x00, 0x00, 0x20, 0xF8, 0xB5, 0x05, 0x46,
+	0x20, 0x48, 0x00, 0x21, 0x01, 0x60, 0x41, 0x60,
+	0x1F, 0x49, 0x01, 0x20, 0x08, 0x70, 0x1F, 0x48,
+	0x1F, 0x4C, 0x00, 0x78, 0x00, 0x28, 0x06, 0xD0,
+	0x2D, 0x26, 0x09, 0x27, 0x01, 0x28, 0x0D, 0xD0,
+	0x02, 0x28, 0x29, 0xD1, 0x17, 0xE0, 0x1B, 0xA0,
+	0x00, 0xF0, 0x5B, 0xFE, 0x5A, 0x20, 0x00, 0x2D,
+	0x20, 0x70, 0x01, 0xD0, 0x00, 0x20, 0x0B, 0xE0,
+	0x01, 0x20, 0x09, 0xE0, 0x18, 0xA0, 0x00, 0xF0,
+	0x50, 0xFE, 0x00, 0x2D, 0x02, 0xD0, 0x26, 0x70,
+	0x02, 0x20, 0x01, 0xE0, 0x27, 0x70, 0x03, 0x20,
+	0x00, 0xF0, 0x0C, 0xF9, 0x10, 0xE0, 0x14, 0xA0,
+	0x00, 0xF0, 0x43, 0xFE, 0x00, 0x2D, 0x02, 0xD0,
+	0x26, 0x70, 0x02, 0x20, 0x01, 0xE0, 0x27, 0x70,
+	0x03, 0x20, 0x00, 0xF0, 0xFF, 0xF8, 0x11, 0x48,
+	0x81, 0x6A, 0x11, 0x4A, 0x11, 0x40, 0x81, 0x62,
+	0x05, 0x20, 0x10, 0x49, 0x00, 0x02, 0x08, 0x60,
+	0xF8, 0xBD, 0x00, 0x00, 0x20, 0x00, 0x00, 0x20,
+	0x31, 0x00, 0x00, 0x20, 0x92, 0x01, 0x00, 0x20,
+	0x94, 0x01, 0x00, 0x20, 0x41, 0x63, 0x74, 0x69,
+	0x76, 0x65, 0x0D, 0x0A, 0x00, 0x00, 0x00, 0x00,
+	0x49, 0x64, 0x6C, 0x65, 0x0D, 0x0A, 0x00, 0x00,
+	0x47, 0x65, 0x73, 0x74, 0x75, 0x72, 0x65, 0x0D,
+	0x0A, 0x00, 0x00, 0x00, 0xC0, 0x11, 0x00, 0x50,
+	0xFF, 0xFF, 0x00, 0xF8, 0x00, 0x10, 0x00, 0x50,
+	0x70, 0xB5, 0x19, 0x4D, 0x19, 0x4C, 0x28, 0x70,
+	0x02, 0x46, 0x21, 0x78, 0x18, 0xA0, 0x00, 0xF0,
+	0x08, 0xFE, 0x1B, 0x49, 0x01, 0x20, 0x08, 0x70,
+	0x00, 0xF0, 0x20, 0xF9, 0x00, 0xF0, 0x4E, 0xF8,
+	0x01, 0x20, 0xFD, 0xF7, 0x4D, 0xFF, 0xFF, 0xF7,
+	0xF5, 0xFB, 0x01, 0x20, 0xFF, 0xF7, 0x86, 0xFF,
+	0x00, 0xF0, 0x42, 0xFC, 0xFE, 0xF7, 0x64, 0xFE,
+	0x28, 0x78, 0x20, 0x70, 0x05, 0x28, 0x0F, 0xD0,
+	0xFF, 0xF7, 0xAA, 0xFC, 0x00, 0x28, 0x0C, 0xD0,
+	0x0E, 0x48, 0x00, 0x7A, 0x00, 0x28, 0x03, 0xD1,
+	0x00, 0x21, 0x02, 0x20, 0x00, 0xF0, 0x48, 0xF8,
+	0x0B, 0x48, 0x00, 0x78, 0xC0, 0x07, 0x01, 0xD0,
+	0x00, 0x20, 0x70, 0xBD, 0x01, 0x20, 0x70, 0xBD,
+	0x92, 0x01, 0x00, 0x20, 0x0D, 0x08, 0x00, 0x20,
+	0x53, 0x4D, 0x3D, 0x5B, 0x25, 0x64, 0x3A, 0x25,
+	0x64, 0x5D, 0x0D, 0x0A, 0x00, 0x00, 0x00, 0x00,
+	0x91, 0x01, 0x00, 0x20, 0x20, 0x00, 0x00, 0x20,
+	0x30, 0x00, 0x00, 0x20, 0x08, 0x49, 0x02, 0x20,
+	0x08, 0x72, 0x08, 0x48, 0x01, 0x78, 0x08, 0x48,
+	0x00, 0x29, 0x01, 0x68, 0x04, 0xD0, 0x01, 0x22,
+	0x92, 0x02, 0x91, 0x43, 0x01, 0x60, 0x70, 0x47,
+	0x01, 0x22, 0x11, 0x43, 0xFA, 0xE7, 0x00, 0x00,
+	0x20, 0x00, 0x00, 0x20, 0x31, 0x00, 0x00, 0x20,
+	0x00, 0x10, 0x00, 0x50, 0x10, 0xB5, 0x08, 0x48,
+	0x01, 0x68, 0x01, 0x22, 0x92, 0x02, 0x11, 0x43,
+	0x01, 0x60, 0x00, 0x68, 0x05, 0x4C, 0xC0, 0x07,
+	0x03, 0xD0, 0x02, 0x20, 0x20, 0x72, 0x00, 0xF0,
+	0xC1, 0xF8, 0x00, 0x20, 0x20, 0x72, 0x10, 0xBD,
+	0x00, 0x10, 0x00, 0x50, 0x20, 0x00, 0x00, 0x20,
+	0x70, 0xB5, 0x05, 0x00, 0x0E, 0x46, 0x16, 0xD0,
+	0xFF, 0xF7, 0x82, 0xFE, 0x00, 0x24, 0x6D, 0x1E,
+	0x07, 0xE0, 0x00, 0xF0, 0xAF, 0xF8, 0xFF, 0xF7,
+	0x57, 0xFB, 0xFF, 0xF7, 0xC3, 0xFF, 0x64, 0x1C,
+	0xE4, 0xB2, 0xAC, 0x42, 0xF5, 0xDB, 0x00, 0xF0,
+	0xA5, 0xF8, 0xFF, 0xF7, 0x4D, 0xFB, 0x00, 0x2E,
+	0x02, 0xD0, 0xFF, 0xF7, 0xCF, 0xFF, 0x70, 0xBD,
+	0xFF, 0xF7, 0xB4, 0xFF, 0x70, 0xBD, 0x00, 0x00,
+	0x30, 0xB5, 0x01, 0x24, 0x1C, 0x4A, 0xA4, 0x07,
+	0x23, 0x13, 0x1B, 0x49, 0x40, 0x32, 0x00, 0x28,
+	0x21, 0xD0, 0x01, 0x28, 0x1F, 0xD0, 0x02, 0x28,
+	0x01, 0xD0, 0x03, 0x28, 0x1A, 0xD1, 0x25, 0x68,
+	0x1D, 0x43, 0x25, 0x60, 0x62, 0x23, 0x93, 0x61,
+	0xD3, 0x61, 0x14, 0x4B, 0xCB, 0x63, 0x14, 0x49,
+	0x09, 0x68, 0x40, 0x31, 0x02, 0x28, 0x48, 0x7E,
+	0x17, 0xD0, 0x40, 0x1E, 0x40, 0x05, 0x40, 0x0D,
+	0x10, 0x60, 0x4B, 0x7E, 0x48, 0x7F, 0x41, 0x00,
+	0x40, 0x18, 0x80, 0x1C, 0x18, 0x18, 0x40, 0x05,
+	0x40, 0x0D, 0x90, 0x60, 0x30, 0xBD, 0x20, 0x68,
+	0x98, 0x43, 0x20, 0x60, 0x01, 0x20, 0x90, 0x61,
+	0xD0, 0x61, 0x00, 0x20, 0xC8, 0x63, 0x10, 0x60,
+	0xF3, 0xE7, 0x40, 0x1E, 0x40, 0x05, 0x40, 0x0D,
+	0x10, 0x60, 0x4B, 0x7E, 0x08, 0x7F, 0xE6, 0xE7,
+	0x80, 0x11, 0x00, 0x50, 0x01, 0x00, 0x01, 0x00,
+	0x98, 0x01, 0x00, 0x20, 0x70, 0xB5, 0x04, 0x46,
+	0x81, 0x00, 0x25, 0x48, 0x41, 0x58, 0x25, 0x48,
+	0x0A, 0x68, 0x42, 0x60, 0x4A, 0x68, 0x82, 0x60,
+	0x8A, 0x68, 0x02, 0x61, 0xCA, 0x68, 0x42, 0x61,
+	0x21, 0x4D, 0x08, 0x69, 0x28, 0x62, 0x1F, 0x4A,
+	0x48, 0x69, 0xC0, 0x32, 0x90, 0x61, 0x88, 0x69,
+	0xD0, 0x61, 0x48, 0x6A, 0x10, 0x62, 0x88, 0x6A,
+	0x50, 0x62, 0x1A, 0x48, 0xCB, 0x69, 0x40, 0x38,
+	0xC3, 0x60, 0x0B, 0x6A, 0x03, 0x61, 0xCB, 0x6A,
+	0x93, 0x62, 0x0B, 0x6B, 0xD3, 0x63, 0x16, 0x4B,
+	0x4E, 0x6B, 0x80, 0x3B, 0x1E, 0x61, 0x8E, 0x6B,
+	0x5E, 0x62, 0xCE, 0x6B, 0xD6, 0x62, 0x0A, 0x6C,
+	0x1A, 0x60, 0x4A, 0x6C, 0x5A, 0x61, 0x8A, 0x6C,
+	0x9A, 0x62, 0xCA, 0x6C, 0xAA, 0x62, 0x0D, 0x4B,
+	0x0A, 0x6D, 0x40, 0x33, 0x5A, 0x63, 0x0B, 0x4B,
+	0x4A, 0x6D, 0x80, 0x33, 0x9A, 0x60, 0x0B, 0x4A,
+	0x8B, 0x6D, 0x13, 0x60, 0xC9, 0x6D, 0x51, 0x60,
+	0xC2, 0x68, 0x09, 0x49, 0x0A, 0x60, 0x00, 0x69,
+	0x48, 0x60, 0x20, 0x46, 0xFF, 0xF7, 0x50, 0xFE,
+	0x20, 0x46, 0xFF, 0xF7, 0x71, 0xFF, 0x70, 0xBD,
+	0xD0, 0x05, 0x00, 0x20, 0x40, 0x10, 0x00, 0x50,
+	0xC0, 0x11, 0x00, 0x50, 0x00, 0x19, 0x00, 0x50,
+	0x20, 0x00, 0x00, 0x20, 0x70, 0xB5, 0x05, 0x20,
+	0x40, 0x04, 0x00, 0x23, 0x0C, 0x4A, 0x0D, 0x49,
+	0x0D, 0x4C, 0x05, 0xE0, 0x0D, 0x78, 0xED, 0x07,
+	0x01, 0xD0, 0x13, 0x72, 0x09, 0xE0, 0x40, 0x1E,
+	0x15, 0x7A, 0x02, 0x2D, 0x02, 0xD0, 0x25, 0x68,
+	0xED, 0x07, 0x02, 0xD0, 0x00, 0x28, 0xF1, 0xD1,
+	0x01, 0xE0, 0x00, 0x28, 0x03, 0xD1, 0x13, 0x72,
+	0x04, 0xA0, 0x00, 0xF0, 0xC6, 0xFC, 0x70, 0xBD,
+	0x20, 0x00, 0x00, 0x20, 0x30, 0x00, 0x00, 0x20,
+	0x00, 0x10, 0x00, 0x50, 0x57, 0x53, 0x46, 0x20,
+	0x54, 0x4F, 0x0D, 0x0A, 0x00, 0x00, 0x00, 0x00,
+	0xF8, 0xB5, 0x1D, 0x48, 0x01, 0x25, 0x01, 0x68,
+	0x00, 0x24, 0x02, 0x22, 0x91, 0x43, 0x1B, 0x4F,
+	0x1B, 0x4E, 0x01, 0x60, 0x1B, 0x48, 0x00, 0x68,
+	0x00, 0x19, 0xC0, 0x7E, 0x41, 0x28, 0x14, 0xD0,
+	0x19, 0x48, 0x61, 0x00, 0x42, 0x5E, 0x04, 0x20,
+	0x38, 0x5E, 0x82, 0x42, 0x09, 0xDA, 0x21, 0x46,
+	0x16, 0xA0, 0x00, 0xF0, 0x9E, 0xFC, 0x30, 0x5D,
+	0x02, 0x21, 0x08, 0x43, 0x30, 0x55, 0x00, 0x25,
+	0x03, 0xE0, 0x30, 0x5D, 0xFD, 0x21, 0x08, 0x40,
+	0x30, 0x55, 0x64, 0x1C, 0xE4, 0xB2, 0x30, 0x2C,
+	0xE0, 0xD3, 0x15, 0x49, 0x01, 0x2D, 0x79, 0x5E,
+	0x09, 0xD0, 0x14, 0xA0, 0x00, 0xF0, 0x89, 0xFC,
+	0x05, 0x49, 0x02, 0x22, 0x08, 0x68, 0x10, 0x43,
+	0x08, 0x60, 0x28, 0x46, 0xF8, 0xBD, 0x16, 0xA0,
+	0x00, 0xF0, 0x7F, 0xFC, 0xF9, 0xE7, 0x00, 0x00,
+	0x0C, 0x05, 0x00, 0x20, 0x84, 0x06, 0x00, 0x20,
+	0xD8, 0x04, 0x00, 0x20, 0x98, 0x01, 0x00, 0x20,
+	0xE4, 0x02, 0x00, 0x20, 0x53, 0x68, 0x6F, 0x72,
+	0x74, 0x20, 0x4E, 0x47, 0x20, 0x44, 0x61, 0x74,
+	0x61, 0x5B, 0x25, 0x64, 0x5D, 0x20, 0x3D, 0x20,
+	0x25, 0x64, 0x0D, 0x0A, 0x00, 0x00, 0x00, 0x00,
+	0x04, 0x00, 0x00, 0x00, 0x53, 0x68, 0x6F, 0x72,
+	0x74, 0x20, 0x54, 0x65, 0x73, 0x74, 0x28, 0x54,
+	0x48, 0x44, 0x3A, 0x25, 0x64, 0x29, 0x20, 0x5B,
+	0x4E, 0x47, 0x5D, 0x0D, 0x0A, 0x00, 0x00, 0x00,
+	0x53, 0x68, 0x6F, 0x72, 0x74, 0x20, 0x54, 0x65,
+	0x73, 0x74, 0x28, 0x54, 0x48, 0x44, 0x3A, 0x25,
+	0x64, 0x29, 0x20, 0x5B, 0x50, 0x61, 0x73, 0x73,
+	0x5D, 0x0D, 0x0A, 0x00, 0x70, 0xB5, 0x0F, 0x48,
+	0x01, 0x25, 0xC4, 0x7F, 0x0E, 0x48, 0x80, 0x88,
+	0x00, 0x07, 0x01, 0xD4, 0x01, 0x20, 0x70, 0xBD,
+	0xFE, 0xF7, 0xBE, 0xFB, 0xFE, 0xF7, 0x26, 0xFA,
+	0xFF, 0xF7, 0x7E, 0xFF, 0x00, 0x28, 0x0A, 0xD1,
+	0x00, 0x25, 0x00, 0x2C, 0x07, 0xD0, 0x64, 0x1E,
+	0xE4, 0xB2, 0x21, 0x46, 0x05, 0xA0, 0x00, 0xF0,
+	0x2C, 0xFC, 0x01, 0x25, 0xEE, 0xE7, 0x28, 0x46,
+	0x70, 0xBD, 0x00, 0x00, 0xA8, 0x06, 0x00, 0x20,
+	0x74, 0x06, 0x00, 0x20, 0x53, 0x68, 0x6F, 0x72,
+	0x74, 0x20, 0x52, 0x65, 0x74, 0x72, 0x79, 0x20,
+	0x28, 0x25, 0x64, 0x29, 0x0D, 0x0A, 0x00, 0x00,
+	0xF8, 0xB5, 0x1B, 0x4E, 0x05, 0x46, 0x0C, 0x46,
+	0x80, 0x21, 0x30, 0x46, 0xFD, 0xF7, 0xD1, 0xFC,
+	0x00, 0x2C, 0x04, 0xD0, 0x31, 0x46, 0x28, 0x46,
+	0x00, 0xF0, 0x42, 0xFA, 0x04, 0xE0, 0x80, 0x22,
+	0x29, 0x46, 0x30, 0x46, 0xFD, 0xF7, 0xAC, 0xFC,
+	0x29, 0x46, 0x12, 0xA0, 0x00, 0xF0, 0x01, 0xFC,
+	0x00, 0x25, 0x14, 0x4F, 0x13, 0xE0, 0x00, 0x24,
+	0x08, 0xE0, 0x68, 0x43, 0x00, 0x19, 0x40, 0x00,
+	0x31, 0x5E, 0x11, 0xA0, 0x00, 0xF0, 0xF5, 0xFB,
+	0x64, 0x1C, 0xE4, 0xB2, 0x38, 0x68, 0x00, 0x7E,
+	0xA0, 0x42, 0xF2, 0xD8, 0x0E, 0xA0, 0x00, 0xF0,
+	0xEC, 0xFB, 0x6D, 0x1C, 0xED, 0xB2, 0x38, 0x68,
+	0x40, 0x7E, 0xA8, 0x42, 0xE7, 0xD8, 0x0A, 0xA0,
+	0x00, 0xF0, 0xE3, 0xFB, 0xF8, 0xBD, 0x00, 0x00,
+	0x4C, 0x00, 0x00, 0x20, 0x49, 0x6D, 0x61, 0x67,
+	0x65, 0x3A, 0x30, 0x78, 0x25, 0x78, 0x0D, 0x0A,
+	0x00, 0x00, 0x00, 0x00, 0x98, 0x01, 0x00, 0x20,
+	0x25, 0x36, 0x64, 0x2C, 0x00, 0x00, 0x00, 0x00,
+	0x0D, 0x0A, 0x00, 0x00, 0x08, 0x49, 0x8A, 0x78,
+	0x52, 0x1E, 0x8A, 0x70, 0x4B, 0x78, 0x0A, 0x1D,
+	0xD2, 0x5C, 0x02, 0x70, 0x48, 0x78, 0x40, 0x1C,
+	0x48, 0x70, 0x48, 0x78, 0x10, 0x28, 0x01, 0xD1,
+	0x00, 0x20, 0x48, 0x70, 0x70, 0x47, 0x00, 0x00,
+	0xE0, 0x05, 0x00, 0x20, 0xF8, 0xB5, 0x01, 0x27,
+	0x05, 0x46, 0xBF, 0x07, 0x38, 0x68, 0x08, 0x21,
+	0x08, 0x43, 0x38, 0x60, 0x01, 0x23, 0x15, 0x48,
+	0x80, 0x22, 0x02, 0x60, 0x14, 0x48, 0x00, 0x21,
+	0x81, 0x70, 0x01, 0x70, 0x41, 0x70, 0xC3, 0x70,
+	0x12, 0x4B, 0x98, 0x68, 0x02, 0x21, 0x88, 0x43,
+	0x98, 0x60, 0xF8, 0x68, 0x10, 0x43, 0xF8, 0x60,
+	0x0F, 0x4C, 0x61, 0x61, 0x0F, 0x48, 0x00, 0x68,
+	0xE9, 0x00, 0x46, 0x06, 0x0E, 0x48, 0xFD, 0xF7,
+	0x15, 0xFC, 0xE0, 0x60, 0x30, 0x20, 0xA0, 0x60,
+	0x06, 0x49, 0x80, 0x20, 0x80, 0x39, 0x08, 0x60,
+	0x08, 0x20, 0x78, 0x60, 0xE0, 0x68, 0x68, 0x43,
+	0xC1, 0x00, 0x08, 0xA0, 0x00, 0xF0, 0x89, 0xFB,
+	0xF8, 0xBD, 0x00, 0x00, 0x80, 0xE1, 0x00, 0xE0,
+	0xE0, 0x05, 0x00, 0x20, 0x40, 0x09, 0x00, 0x50,
+	0x00, 0x02, 0x00, 0x50, 0x00, 0x11, 0x00, 0x50,
+	0x00, 0x36, 0x6E, 0x01, 0x55, 0x41, 0x52, 0x54,
+	0x28, 0x25, 0x64, 0x29, 0x21, 0x0D, 0x0A, 0x00,
+	0x70, 0xB5, 0x14, 0x4A, 0x91, 0x78, 0x14, 0x4C,
+	0x0E, 0x29, 0x02, 0xD3, 0x61, 0x68, 0x49, 0x07,
+	0xFC, 0xD5, 0x61, 0x69, 0x02, 0x25, 0xA9, 0x43,
+	0x61, 0x61, 0x91, 0x78, 0x00, 0x26, 0x10, 0x29,
+	0x0D, 0xD2, 0x0C, 0x49, 0x13, 0x78, 0x09, 0x1D,
+	0xC8, 0x54, 0x10, 0x78, 0x40, 0x1C, 0x10, 0x70,
+	0x10, 0x78, 0x10, 0x28, 0x00, 0xD1, 0x16, 0x70,
+	0x90, 0x78, 0x40, 0x1C, 0x90, 0x70, 0xD0, 0x78,
+	0x00, 0x28, 0x03, 0xD0, 0xD6, 0x70, 0x20, 0x46,
+	0xFF, 0xF7, 0x80, 0xFF, 0x60, 0x69, 0x28, 0x43,
+	0x60, 0x61, 0x70, 0xBD, 0xE0, 0x05, 0x00, 0x20,
+	0x00, 0x02, 0x00, 0x50, 0xFE, 0xB5, 0x2F, 0x48,
+	0x10, 0x26, 0x12, 0x27, 0x86, 0x5F, 0xC7, 0x5F,
+	0x2D, 0x48, 0x01, 0x90, 0x2D, 0x48, 0x01, 0x25,
+	0x01, 0x68, 0x2A, 0x04, 0x91, 0x43, 0x00, 0x24,
+	0x01, 0x60, 0x2B, 0x48, 0x00, 0x68, 0x00, 0x19,
+	0xC0, 0x7E, 0x41, 0x28, 0x26, 0xD0, 0x01, 0x99,
+	0x60, 0x00, 0x09, 0x5A, 0x4A, 0x05, 0x52, 0x0D,
+	0x26, 0x49, 0x53, 0x05, 0x0A, 0x52, 0x03, 0xD5,
+	0x89, 0x23, 0x9B, 0x00, 0xD2, 0x1A, 0x0A, 0x52,
+	0x23, 0x4A, 0x0B, 0x5A, 0x12, 0x5A, 0xD0, 0x1A,
+	0x00, 0xB2, 0xB0, 0x42, 0x01, 0xDC, 0xB8, 0x42,
+	0x0B, 0xDA, 0x00, 0x90, 0x21, 0x46, 0x1F, 0xA0,
+	0x00, 0xF0, 0x17, 0xFB, 0x23, 0x48, 0x10, 0x22,
+	0x01, 0x5D, 0x11, 0x43, 0x01, 0x55, 0x00, 0x25,
+	0x04, 0xE0, 0x20, 0x48, 0xEF, 0x22, 0x01, 0x5D,
+	0x11, 0x40, 0x01, 0x55, 0x64, 0x1C, 0xE4, 0xB2,
+	0x30, 0x2C, 0xCE, 0xD3, 0x01, 0x2D, 0x18, 0xD0,
+	0x10, 0x4A, 0x01, 0x20, 0x11, 0x68, 0x00, 0x04,
+	0x01, 0x43, 0x19, 0xA0, 0x11, 0x60, 0x00, 0xF0,
+	0xFC, 0xFA, 0x1C, 0xA0, 0x00, 0xF0, 0xF9, 0xFA,
+	0x01, 0x21, 0x0C, 0x48, 0xFF, 0xF7, 0xDC, 0xFE,
+	0x1A, 0xA0, 0x00, 0xF0, 0xF2, 0xFA, 0x01, 0x21,
+	0x09, 0x48, 0xFF, 0xF7, 0xD5, 0xFE, 0x28, 0x46,
+	0xFE, 0xBD, 0x19, 0xA0, 0x00, 0xF0, 0xE9, 0xFA,
+	0xEB, 0xE7, 0x00, 0x00, 0x84, 0x06, 0x00, 0x20,
+	0x00, 0x20, 0x00, 0x50, 0x0C, 0x05, 0x00, 0x20,
+	0x98, 0x01, 0x00, 0x20, 0x10, 0x04, 0x00, 0x20,
+	0xEC, 0x06, 0x00, 0x20, 0x55, 0x43, 0x5B, 0x25,
+	0x64, 0x5D, 0x20, 0x4E, 0x47, 0x21, 0x20, 0x25,
+	0x64, 0x2D, 0x25, 0x64, 0x3D, 0x25, 0x64, 0x0D,
+	0x0A, 0x00, 0x00, 0x00, 0xD8, 0x04, 0x00, 0x20,
+	0x55, 0x43, 0x20, 0x54, 0x65, 0x73, 0x74, 0x20,
+	0x5B, 0x4E, 0x47, 0x5D, 0x3A, 0x30, 0x78, 0x25,
+	0x78, 0x0D, 0x0A, 0x00, 0x55, 0x43, 0x0D, 0x0A,
+	0x00, 0x00, 0x00, 0x00, 0x47, 0x6F, 0x6C, 0x64,
+	0x65, 0x6E, 0x20, 0x55, 0x43, 0x0D, 0x0A, 0x00,
+	0x55, 0x43, 0x20, 0x54, 0x65, 0x73, 0x74, 0x20,
+	0x5B, 0x50, 0x61, 0x73, 0x73, 0x5D, 0x0D, 0x0A,
+	0x00, 0x00, 0x00, 0x00, 0x70, 0xB5, 0x0E, 0x48,
+	0x01, 0x25, 0x44, 0x79, 0x0D, 0x48, 0x80, 0x88,
+	0xC0, 0x06, 0x01, 0xD4, 0x01, 0x20, 0x70, 0xBD,
+	0xFF, 0xF7, 0x5C, 0xFF, 0x00, 0x28, 0x0C, 0xD1,
+	0x00, 0x25, 0x00, 0x2C, 0x09, 0xD0, 0x64, 0x1E,
+	0xE4, 0xB2, 0xFE, 0xF7, 0x0B, 0xF9, 0x21, 0x46,
+	0x05, 0xA0, 0x00, 0xF0, 0x96, 0xFA, 0x01, 0x25,
+	0xEE, 0xE7, 0x28, 0x46, 0x70, 0xBD, 0x00, 0x00,
+	0xC8, 0x06, 0x00, 0x20, 0x74, 0x06, 0x00, 0x20,
+	0x55, 0x43, 0x20, 0x4E, 0x47, 0x20, 0x52, 0x65,
+	0x74, 0x72, 0x79, 0x20, 0x28, 0x25, 0x64, 0x29,
+	0x0D, 0x0A, 0x00, 0x00, 0xF8, 0xB5, 0x3D, 0x4F,
+	0x01, 0x24, 0x38, 0x7B, 0x3C, 0x49, 0x0A, 0x78,
+	0x3C, 0x4E, 0x3D, 0x4D, 0x90, 0x42, 0x05, 0xD1,
+	0x28, 0x78, 0x81, 0x28, 0x02, 0xD0, 0x30, 0x78,
+	0xC0, 0x07, 0x63, 0xD0, 0x00, 0x20, 0x30, 0x70,
+	0x35, 0x48, 0x00, 0x78, 0x07, 0x28, 0x0C, 0xD3,
+	0x36, 0x48, 0x00, 0x68, 0x40, 0x05, 0x40, 0x0F,
+	0x38, 0x73, 0x01, 0x20, 0xFD, 0xF7, 0xDC, 0xFC,
+	0x00, 0x20, 0x30, 0x70, 0x38, 0x7B, 0x02, 0x28,
+	0x13, 0xD0, 0x38, 0x7B, 0x2C, 0x4F, 0x38, 0x70,
+	0x81, 0x20, 0x28, 0x70, 0x2E, 0x4A, 0x01, 0x21,
+	0x10, 0x88, 0x09, 0x03, 0x08, 0x43, 0x10, 0x80,
+	0x38, 0x78, 0x03, 0x00, 0xFD, 0xF7, 0x76, 0xFB,
+	0x07, 0x25, 0x27, 0x37, 0x3D, 0x0B, 0x18, 0x0B,
+	0x3D, 0x00, 0x83, 0x20, 0x28, 0x70, 0x05, 0x20,
+	0xFF, 0xF7, 0x36, 0xFC, 0xF8, 0xBD, 0x25, 0xA0,
+	0x00, 0xF0, 0x43, 0xFA, 0x01, 0x20, 0xFE, 0xF7,
+	0x1B, 0xFE, 0x00, 0x20, 0xFF, 0xF7, 0x2C, 0xFC,
+	0x00, 0x28, 0x1E, 0xD0, 0x01, 0x20, 0x1A, 0xE0,
+	0x20, 0xA0, 0x00, 0xF0, 0x36, 0xFA, 0x01, 0x20,
+	0xFE, 0xF7, 0x0E, 0xFE, 0x01, 0x20, 0xFF, 0xF7,
+	0x1F, 0xFC, 0x00, 0x28, 0x11, 0xD0, 0x04, 0x20,
+	0x0D, 0xE0, 0x1C, 0xA0, 0x00, 0xE0, 0x1D, 0xA0,
+	0x00, 0xF0, 0x27, 0xFA, 0x00, 0x20, 0xFE, 0xF7,
+	0xFF, 0xFD, 0x02, 0x20, 0xFF, 0xF7, 0x10, 0xFC,
+	0x00, 0x28, 0x02, 0xD0, 0x02, 0x20, 0x28, 0x70,
+	0x07, 0xE0, 0x00, 0x24, 0x05, 0xE0, 0x83, 0x20,
+	0x28, 0x70, 0x05, 0x20, 0xFF, 0xF7, 0x04, 0xFC,
+	0x04, 0x46, 0x2A, 0x78, 0x39, 0x78, 0x13, 0xA0,
+	0x00, 0xF0, 0x0F, 0xFA, 0x30, 0x78, 0xC0, 0x07,
+	0x02, 0xD0, 0x81, 0x20, 0x28, 0x70, 0x00, 0x24,
+	0x20, 0x46, 0xF8, 0xBD, 0x20, 0x00, 0x00, 0x20,
+	0x39, 0x01, 0x00, 0x20, 0x30, 0x00, 0x00, 0x20,
+	0x20, 0x08, 0x00, 0x20, 0x00, 0x11, 0x00, 0x50,
+	0x4A, 0x01, 0x00, 0x20, 0x44, 0x41, 0x0D, 0x0A,
+	0x00, 0x00, 0x00, 0x00, 0x44, 0x49, 0x0D, 0x0A,
+	0x00, 0x00, 0x00, 0x00, 0x44, 0x53, 0x54, 0x42,
+	0x0D, 0x0A, 0x00, 0x00, 0x44, 0x47, 0x0D, 0x0A,
+	0x00, 0x00, 0x00, 0x00, 0x44, 0x53, 0x50, 0x3D,
+	0x25, 0x64, 0x2C, 0x50, 0x57, 0x52, 0x3D, 0x25,
+	0x64, 0x20, 0x0D, 0x0A, 0x00, 0x00, 0x00, 0x00,
+	0x70, 0xB5, 0x05, 0x46, 0x01, 0x24, 0xFE, 0xF7,
+	0x83, 0xFA, 0x00, 0x2D, 0x09, 0xD0, 0xF0, 0x20,
+	0xFD, 0xF7, 0xCA, 0xFA, 0xFE, 0xF7, 0x4A, 0xFB,
+	0xFE, 0xF7, 0xE0, 0xFA, 0x00, 0x20, 0xFE, 0xF7,
+	0xAB, 0xFD, 0xFE, 0xF7, 0xDF, 0xFD, 0xFF, 0xF7,
+	0xDF, 0xFA, 0x20, 0x46, 0x70, 0xBD, 0x00, 0x00,
+	0x30, 0xB5, 0x08, 0x4A, 0x14, 0x68, 0x1B, 0x34,
+	0x00, 0x22, 0xA3, 0x5C, 0x41, 0x2B, 0x03, 0xD0,
+	0x55, 0x00, 0x45, 0x5B, 0x5B, 0x00, 0xCD, 0x52,
+	0x52, 0x1C, 0xD2, 0xB2, 0x30, 0x2A, 0xF4, 0xD3,
+	0x30, 0xBD, 0x00, 0x00, 0x98, 0x01, 0x00, 0x20,
+	0x70, 0xB5, 0x17, 0x4D, 0x16, 0x20, 0x28, 0x70,
+	0x68, 0x78, 0x29, 0x46, 0x00, 0x24, 0x14, 0x31,
+	0x02, 0x28, 0x0C, 0xD8, 0x00, 0x28, 0x05, 0xD1,
+	0xA8, 0x78, 0x00, 0x28, 0x02, 0xD0, 0x08, 0x46,
+	0x1C, 0x30, 0x04, 0x70, 0x48, 0x88, 0x00, 0x28,
+	0x01, 0xD1, 0x03, 0x20, 0x68, 0x71, 0xAC, 0x70,
+	0x2C, 0x81, 0xEC, 0x80, 0xAC, 0x81, 0x6C, 0x81,
+	0x4C, 0x80, 0x8C, 0x80, 0x0C, 0x72, 0x09, 0x48,
+	0x44, 0x70, 0x84, 0x70, 0x04, 0x70, 0x06, 0x48,
+	0xA0, 0x21, 0x30, 0x30, 0xFD, 0xF7, 0x4D, 0xFA,
+	0xEC, 0x70, 0x5A, 0x20, 0xE8, 0x81, 0x2C, 0x71,
+	0x2C, 0x61, 0x00, 0xF0, 0x11, 0xFA, 0x70, 0xBD,
+	0x0C, 0x08, 0x00, 0x20, 0x90, 0x02, 0x00, 0x20,
+	0x06, 0x4A, 0x07, 0x4B, 0x00, 0x21, 0x00, 0x20,
+	0x40, 0x1C, 0x90, 0x42, 0xFC, 0xDB, 0x49, 0x1C,
+	0x99, 0x42, 0xF8, 0xDB, 0x03, 0x48, 0x01, 0x21,
+	0x01, 0x60, 0x70, 0x47, 0x10, 0x27, 0x00, 0x00,
+	0xB8, 0x0B, 0x00, 0x00, 0x04, 0x00, 0x00, 0x40,
+	0x70, 0xB5, 0x7B, 0x24, 0x21, 0x48, 0x24, 0x02,
+	0x04, 0x60, 0xF7, 0x20, 0xC0, 0x01, 0x20, 0x4A,
+	0xC1, 0x7C, 0x11, 0x70, 0x1F, 0x4A, 0x01, 0x7D,
+	0x11, 0x70, 0x1F, 0x4A, 0x41, 0x7D, 0x11, 0x70,
+	0x1E, 0x49, 0x80, 0x7D, 0x08, 0x70, 0x60, 0x7C,
+	0x21, 0x7C, 0x00, 0x06, 0x00, 0x14, 0x08, 0x43,
+	0x1B, 0x49, 0x05, 0x46, 0x08, 0x80, 0xE0, 0x7C,
+	0xA1, 0x7C, 0x00, 0x06, 0x00, 0x14, 0x08, 0x43,
+	0x18, 0x49, 0x08, 0x80, 0x20, 0x7E, 0x61, 0x7E,
+	0x48, 0x43, 0x17, 0x49, 0x08, 0x80, 0x17, 0x48,
+	0xFD, 0xF7, 0x0F, 0xFA, 0x10, 0x21, 0xC8, 0x41,
+	0x15, 0x49, 0x08, 0x60, 0x04, 0x22, 0x21, 0x1D,
+	0x14, 0x48, 0xFD, 0xF7, 0xE1, 0xF9, 0x11, 0x49,
+	0x06, 0x22, 0x89, 0x1F, 0x12, 0x48, 0xFD, 0xF7,
+	0xDB, 0xF9, 0x60, 0x7D, 0x22, 0x7D, 0x01, 0x02,
+	0x11, 0x43, 0x28, 0x46, 0x0A, 0x22, 0x50, 0x43,
+	0xFD, 0xF7, 0xBE, 0xF9, 0x0D, 0x49, 0x08, 0x80,
+	0x70, 0xBD, 0x00, 0x00, 0x98, 0x01, 0x00, 0x20,
+	0x0C, 0x00, 0x00, 0x20, 0x0D, 0x00, 0x00, 0x20,
+	0x0E, 0x00, 0x00, 0x20, 0x0F, 0x00, 0x00, 0x20,
+	0x1C, 0x00, 0x00, 0x20, 0x1E, 0x00, 0x00, 0x20,
+	0x18, 0x00, 0x00, 0x20, 0x10, 0x7B, 0x00, 0x00,
+	0xD8, 0x06, 0x00, 0x20, 0xE0, 0x06, 0x00, 0x20,
+	0xE4, 0x06, 0x00, 0x20, 0x1A, 0x00, 0x00, 0x20,
+	0x00, 0x20, 0x0E, 0x49, 0x08, 0x70, 0x17, 0xE0,
+	0x0C, 0x48, 0x00, 0x78, 0x00, 0x28, 0x02, 0xD0,
+	0x07, 0x28, 0x0F, 0xD1, 0x0B, 0xE0, 0x00, 0xF0,
+	0x13, 0xF8, 0x01, 0x28, 0x03, 0xD1, 0x07, 0x20,
+	0x06, 0x49, 0x08, 0x70, 0x02, 0xE0, 0x03, 0x20,
+	0x04, 0x49, 0x08, 0x70, 0x03, 0xE0, 0xFD, 0xF7,
+	0x63, 0xFA, 0x00, 0xE0, 0x00, 0xBF, 0x00, 0xBF,
+	0xE6, 0xE7, 0x00, 0x00, 0xD2, 0x00, 0x00, 0x20,
+	0x10, 0xB5, 0x00, 0x24, 0xFD, 0xF7, 0xC4, 0xFC,
+	0x00, 0xF0, 0xF2, 0xF8, 0x01, 0x20, 0xFF, 0xF7,
+	0x03, 0xFF, 0x04, 0x46, 0x00, 0x2C, 0x04, 0xD1,
+	0x05, 0xA0, 0x00, 0xF0, 0xDE, 0xF8, 0x00, 0x20,
+	0x10, 0xBD, 0x06, 0x49, 0x06, 0x48, 0x81, 0x70,
+	0x09, 0x0A, 0xC1, 0x70, 0x01, 0x20, 0xF7, 0xE7,
+	0x49, 0x4E, 0x49, 0x54, 0x20, 0x4E, 0x47, 0x0D,
+	0x0A, 0x00, 0x00, 0x00, 0xAA, 0x55, 0x00, 0x00,
+	0x88, 0x02, 0x00, 0x20, 0xF7, 0xB5, 0x00, 0x25,
+	0x84, 0xB0, 0x0C, 0x46, 0x16, 0x46, 0x61, 0x27,
+	0x5C, 0xE0, 0x25, 0x28, 0x54, 0xD1, 0x64, 0x1C,
+	0x00, 0x22, 0x20, 0x78, 0x13, 0x46, 0x00, 0x28,
+	0x57, 0xD0, 0x25, 0x28, 0x4C, 0xD0, 0x2D, 0x28,
+	0x01, 0xD1, 0x64, 0x1C, 0x01, 0x23, 0x02, 0x20,
+	0x21, 0x78, 0x30, 0x29, 0x07, 0xD1, 0x64, 0x1C,
+	0x03, 0x43, 0xF9, 0xE7, 0x0A, 0x21, 0x4A, 0x43,
+	0x30, 0x3A, 0x82, 0x18, 0x64, 0x1C, 0x20, 0x78,
+	0x01, 0x46, 0x30, 0x39, 0x09, 0x29, 0xF5, 0xD9,
+	0xC1, 0xB2, 0x73, 0x29, 0x0A, 0xD0, 0x64, 0x28,
+	0x10, 0xD0, 0x78, 0x28, 0x13, 0xD0, 0x58, 0x28,
+	0x19, 0xD0, 0x75, 0x28, 0x1F, 0xD0, 0x63, 0x28,
+	0x23, 0xD0, 0x2E, 0xE0, 0x02, 0xCE, 0x00, 0x29,
+	0x00, 0xD1, 0x1D, 0xA1, 0x04, 0x98, 0x00, 0xF0,
+	0xD3, 0xF8, 0x0A, 0xE0, 0x68, 0x46, 0x8C, 0xC0,
+	0x02, 0xCE, 0x01, 0x23, 0x13, 0xE0, 0x68, 0x46,
+	0x8C, 0xC0, 0x08, 0xE0, 0x04, 0x98, 0x00, 0xF0,
+	0x3C, 0xF8, 0x45, 0x19, 0x19, 0xE0, 0x41, 0x20,
+	0x01, 0x93, 0x00, 0x92, 0x02, 0x90, 0x02, 0xCE,
+	0x00, 0x23, 0x10, 0x22, 0xF2, 0xE7, 0x68, 0x46,
+	0x8C, 0xC0, 0x02, 0xCE, 0x00, 0x23, 0x0A, 0x22,
+	0xEC, 0xE7, 0x02, 0xCE, 0x68, 0x46, 0x01, 0x73,
+	0x00, 0x21, 0x41, 0x73, 0x03, 0xA9, 0xD9, 0xE7,
+	0xC1, 0xB2, 0x04, 0x98, 0x00, 0xF0, 0x14, 0xF8,
+	0x6D, 0x1C, 0x64, 0x1C, 0x20, 0x78, 0x00, 0x28,
+	0x9F, 0xD1, 0x04, 0x98, 0x00, 0x28, 0x03, 0xD0,
+	0x04, 0x99, 0x00, 0x20, 0x09, 0x68, 0x08, 0x70,
+	0x28, 0x46, 0x07, 0xB0, 0xF0, 0xBD, 0x00, 0x00,
+	0x28, 0x6E, 0x75, 0x6C, 0x6C, 0x29, 0x00, 0x00,
+	0x10, 0xB5, 0x00, 0x28, 0x05, 0xD0, 0x02, 0x68,
+	0x11, 0x70, 0x01, 0x68, 0x49, 0x1C, 0x01, 0x60,
+	0x10, 0xBD, 0xC8, 0xB2, 0xFF, 0xF7, 0xD4, 0xFC,
+	0x10, 0xBD, 0xFF, 0xB5, 0x00, 0x27, 0x83, 0xB0,
+	0x0C, 0x9D, 0x3E, 0x46, 0x08, 0x00, 0x3A, 0x46,
+	0x05, 0xD0, 0x00, 0x2B, 0x12, 0xD0, 0x05, 0x9B,
+	0x0A, 0x2B, 0x0B, 0xD0, 0x0E, 0xE0, 0x30, 0x20,
+	0x69, 0x46, 0x08, 0x70, 0x4A, 0x70, 0x2A, 0x46,
+	0x0D, 0x9B, 0x03, 0x98, 0x00, 0xF0, 0x74, 0xF8,
+	0x07, 0xB0, 0xF0, 0xBD, 0x00, 0x29, 0x01, 0xDA,
+	0x01, 0x27, 0x40, 0x42, 0x02, 0xAC, 0x69, 0x46,
+	0x03, 0x34, 0xCA, 0x72, 0x0A, 0xE0, 0x05, 0x99,
+	0xFD, 0xF7, 0xA0, 0xF8, 0x0A, 0x29, 0x02, 0xDB,
+	0x0E, 0x9A, 0x89, 0x18, 0x3A, 0x39, 0x30, 0x31,
+	0x64, 0x1E, 0x21, 0x70, 0x00, 0x28, 0xF2, 0xD1,
+	0x00, 0x2F, 0x0E, 0xD0, 0x00, 0x2D, 0x09, 0xD0,
+	0x0D, 0x98, 0x80, 0x07, 0x06, 0xD5, 0x2D, 0x21,
+	0x03, 0x98, 0xFF, 0xF7, 0xB9, 0xFF, 0x76, 0x1C,
+	0x6D, 0x1E, 0x02, 0xE0, 0x2D, 0x20, 0x64, 0x1E,
+	0x20, 0x70, 0x2A, 0x46, 0x21, 0x46, 0x0D, 0x9B,
+	0x03, 0x98, 0x00, 0xF0, 0x45, 0xF8, 0x80, 0x19,
+	0xCE, 0xE7, 0x0F, 0xB4, 0x10, 0xB5, 0x03, 0xAA,
+	0x00, 0x20, 0x02, 0x99, 0xFF, 0xF7, 0x2E, 0xFF,
+	0x10, 0xBC, 0x08, 0xBC, 0x04, 0xB0, 0x18, 0x47,
+	0x10, 0xB5, 0xFF, 0xF7, 0x85, 0xFE, 0x00, 0xF0,
+	0x8D, 0xF8, 0x10, 0xBD, 0x00, 0x00, 0x00, 0x00,
+	0x36, 0x03, 0x35, 0x03, 0x2C, 0x03, 0x9C, 0x03,
+	0xE9, 0x02, 0x28, 0x03, 0x29, 0x03, 0x1C, 0x03,
+	0x9D, 0x03, 0x2F, 0x03, 0x4A, 0x03, 0x4D, 0x03,
+	0xFF, 0x03, 0xFF, 0x03, 0xFF, 0x03, 0xFF, 0x03,
+	0x3D, 0x03, 0x3C, 0x03, 0x2C, 0x03, 0x9C, 0x03,
+	0xD3, 0x02, 0x28, 0x03, 0x29, 0x03, 0xD6, 0x02,
+	0x9E, 0x03, 0x30, 0x03, 0x37, 0x03, 0x3A, 0x03,
+	0xFF, 0x03, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0xFF, 0xB5, 0x00, 0x25, 0x20, 0x27, 0x81, 0xB0,
+	0x0E, 0x46, 0x14, 0x46, 0x00, 0x2A, 0x0E, 0xDD,
+	0x00, 0x20, 0x01, 0xE0, 0x40, 0x1C, 0x49, 0x1C,
+	0x0A, 0x78, 0x00, 0x2A, 0xFA, 0xD1, 0xA0, 0x42,
+	0x01, 0xDB, 0x00, 0x24, 0x00, 0xE0, 0x24, 0x1A,
+	0x98, 0x07, 0x00, 0xD5, 0x30, 0x27, 0xD8, 0x07,
+	0x06, 0xD0, 0x0D, 0xE0, 0x39, 0x46, 0x01, 0x98,
+	0xFF, 0xF7, 0x4A, 0xFF, 0x6D, 0x1C, 0x64, 0x1E,
+	0x00, 0x2C, 0xF7, 0xDC, 0x04, 0xE0, 0x01, 0x98,
+	0xFF, 0xF7, 0x42, 0xFF, 0x6D, 0x1C, 0x76, 0x1C,
+	0x31, 0x78, 0x00, 0x29, 0xF7, 0xD1, 0x05, 0xE0,
+	0x39, 0x46, 0x01, 0x98, 0xFF, 0xF7, 0x38, 0xFF,
+	0x6D, 0x1C, 0x64, 0x1E, 0x00, 0x2C, 0xF7, 0xDC,
+	0x28, 0x46, 0x05, 0xB0, 0xF0, 0xBD, 0x00, 0x00,
+	0x05, 0x48, 0x01, 0x78, 0x82, 0x29, 0x05, 0xD1,
+	0xC0, 0x79, 0x01, 0x28, 0x02, 0xD1, 0x03, 0x49,
+	0xFF, 0x20, 0x08, 0x70, 0x70, 0x47, 0x00, 0x00,
+	0x20, 0x08, 0x00, 0x20, 0x40, 0x01, 0x00, 0x20,
+	0x05, 0x48, 0x00, 0x21, 0x01, 0x80, 0x41, 0x80,
+	0x04, 0x49, 0x81, 0x80, 0xC1, 0x80, 0x04, 0x49,
+	0x03, 0x20, 0x08, 0x70, 0x70, 0x47, 0x00, 0x00,
+	0x10, 0x00, 0x00, 0x20, 0xFF, 0x7F, 0x00, 0x00,
+	0x90, 0x01, 0x00, 0x20, 0x08, 0x49, 0x5A, 0x20,
+	0x08, 0x70, 0x08, 0x49, 0x00, 0x20, 0x08, 0x70,
+	0x08, 0x48, 0x07, 0x49, 0x81, 0x80, 0xC1, 0x80,
+	0x07, 0x49, 0x03, 0x20, 0x08, 0x70, 0x07, 0x49,
+	0x01, 0x20, 0x08, 0x70, 0x70, 0x47, 0x00, 0x00,
+	0x94, 0x01, 0x00, 0x20, 0x0F, 0x08, 0x00, 0x20,
+	0xFF, 0x7F, 0x00, 0x00, 0x10, 0x00, 0x00, 0x20,
+	0x90, 0x01, 0x00, 0x20, 0x91, 0x01, 0x00, 0x20,
+	0x00, 0x00, 0x00, 0x40, 0x10, 0x03, 0x42, 0x88,
+	0x0C, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x09, 0x00, 0x50, 0x1F, 0x00, 0x00, 0x00,
+	0x04, 0x03, 0x00, 0x50, 0x64, 0x00, 0x00, 0x00,
+	0x00, 0x03, 0x00, 0x50, 0x77, 0x00, 0x01, 0x48,
+	0x44, 0x09, 0x00, 0x50, 0x39, 0x5A, 0x5B, 0x00,
+	0x10, 0x06, 0x00, 0x50, 0x00, 0x00, 0x06, 0x07,
+	0x00, 0x06, 0x00, 0x50, 0x00, 0x00, 0x00, 0x78,
+	0x08, 0x06, 0x00, 0x50, 0x0C, 0x30, 0x00, 0x00,
+	0x28, 0x06, 0x00, 0x50, 0x06, 0x00, 0x00, 0x00,
+	0x2C, 0x06, 0x00, 0x50, 0x0A, 0x66, 0x00, 0x00,
+	0x30, 0x06, 0x00, 0x50, 0xCC, 0x02, 0x00, 0x20,
+	0x34, 0x06, 0x00, 0x50, 0x00, 0x20, 0x00, 0x00,
+	0x44, 0x00, 0x00, 0x50, 0x01, 0x00, 0x00, 0x00,
+	0x1C, 0x00, 0x00, 0x50, 0x50, 0x71, 0x00, 0x00,
+	0x20, 0x00, 0x00, 0x50, 0x24, 0x29, 0x00, 0x00,
+	0x14, 0x00, 0x00, 0x40, 0x04, 0x04, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x40, 0x14, 0x33, 0x43, 0xC8,
+	0x0C, 0x00, 0x00, 0x40, 0x29, 0x0A, 0x00, 0x00,
+	0x04, 0x00, 0x00, 0x40, 0x10, 0x32, 0x00, 0x00,
+	0x1C, 0x0E, 0x00, 0x50, 0x03, 0x00, 0x00, 0x00,
+	0x54, 0x00, 0x00, 0x50, 0x14, 0x07, 0x00, 0x00,
+	0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
+	0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
+	0x2C, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00,
+	0x58, 0x10, 0x00, 0x50, 0x01, 0x00, 0x00, 0x00,
+	0x04, 0x11, 0x00, 0x50, 0x78, 0x11, 0x00, 0x00,
+	0x0C, 0x11, 0x00, 0x50, 0x01, 0x00, 0x00, 0x00,
+	0x10, 0x11, 0x00, 0x50, 0x78, 0x01, 0x00, 0x00,
+	0x14, 0x11, 0x00, 0x50, 0xC8, 0x03, 0x60, 0x00,
+	0x4C, 0x00, 0x00, 0x50, 0x31, 0x00, 0x00, 0x00,
+	0x3C, 0x10, 0x00, 0x50, 0x00, 0x00, 0x10, 0x00,
+	0xB4, 0x10, 0x00, 0x50, 0x00, 0x26, 0x31, 0x00,
+	0xC0, 0x10, 0x00, 0x50, 0x33, 0x03, 0x33, 0x03,
+	0xC4, 0x10, 0x00, 0x50, 0x33, 0x03, 0x33, 0x03,
+	0xC8, 0x10, 0x00, 0x50, 0x0C, 0x0A, 0x00, 0x00,
+	0xCC, 0x10, 0x00, 0x50, 0x1A, 0x00, 0x00, 0x00,
+	0xD0, 0x10, 0x00, 0x50, 0x03, 0x19, 0x19, 0x00,
+	0xF0, 0x11, 0x00, 0x50, 0x12, 0x00, 0x00, 0x00,
+	0xEC, 0x11, 0x00, 0x50, 0x5C, 0x00, 0x00, 0x00,
+	0xF4, 0x11, 0x00, 0x50, 0x01, 0x00, 0x01, 0x00,
+	0x2C, 0x10, 0x00, 0x50, 0x10, 0x00, 0x90, 0x00,
+	0x30, 0x10, 0x00, 0x50, 0x20, 0x0C, 0x90, 0x00,
+	0x34, 0x10, 0x00, 0x50, 0x30, 0x0C, 0x30, 0x0C,
+	0x38, 0x10, 0x00, 0x50, 0xFF, 0x0F, 0x00, 0x00,
+	0x7C, 0x10, 0x00, 0x50, 0x88, 0x88, 0xFE, 0x88,
+	0x80, 0x10, 0x00, 0x50, 0x88, 0xFF, 0x00, 0x00,
+	0x84, 0x10, 0x00, 0x50, 0x55, 0x55, 0x55, 0x55,
+	0x88, 0x10, 0x00, 0x50, 0x55, 0x55, 0x55, 0x55,
+	0x8C, 0x10, 0x00, 0x50, 0x55, 0x55, 0x55, 0x55,
+	0xE8, 0x10, 0x00, 0x50, 0x3F, 0x16, 0x3F, 0x15,
+	0x04, 0x00, 0x00, 0x40, 0x04, 0x00, 0x00, 0x00,
+	0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
+	0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
+	0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
+	0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
+	0x30, 0x3B, 0x00, 0x00, 0x04, 0x00, 0x00, 0x20,
+	0x08, 0x00, 0x00, 0x00, 0x5C, 0x0A, 0x00, 0x00,
+	0x38, 0x3B, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x20,
+	0x54, 0x0E, 0x00, 0x00, 0x6A, 0x0A, 0x00, 0x00,
+	0xFF, 0x00, 0x00, 0x00, 0x78, 0x7F, 0x00, 0x00,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0x52, 0x57, 0x41, 0x66,
+};
+const unsigned char u8_rad_testpara_30[] = {
+	0xA1, 0x00, 0x03, 0xF3, 0x02, 0x02, 0x40, 0x01,
+	0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x40, 0x01, 0x68, 0x01, 0x44, 0x01, 0x44, 0x01,
+	0x05, 0x05, 0x00, 0x00, 0x01, 0x05, 0x41, 0x06,
+	0x0A, 0x0B, 0x41, 0x0F, 0x10, 0x14, 0x41, 0x15,
+	0x02, 0x07, 0x41, 0x16, 0x41, 0x41, 0x41, 0x41,
+	0x41, 0x41, 0x41, 0x04, 0x03, 0x09, 0x41, 0x08,
+	0x0E, 0x0D, 0x41, 0x13, 0x12, 0x18, 0x41, 0x17,
+	0x0C, 0x11, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+	0x41, 0x41, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x0C, 0x3E, 0x0C, 0x3E, 0x08, 0x08, 0x02, 0x02,
+	0x14, 0x03, 0x08, 0x08, 0x01, 0x01, 0xC8, 0x00,
+	0x1A, 0x01, 0x08, 0x14, 0x82, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5E, 0x01,
+	0xA0, 0x00, 0x64, 0x00, 0x4A, 0x00, 0x0A, 0x78,
+	0x0F, 0x08, 0x1A, 0x4A, 0x4A, 0x3C, 0x3C, 0x00,
+	0x00, 0x00, 0x6A, 0x00, 0x38, 0x00, 0x1C, 0x00,
+	0x06, 0x32, 0x69, 0xE3, 0x0D, 0x00, 0x00, 0x72,
+	0x45, 0x00, 0x00, 0x06, 0x00, 0xA0, 0x00, 0x78,
+	0x00, 0x64, 0xDB, 0x13, 0xB5, 0x0C, 0x2D, 0x03,
+	0xCB, 0x00, 0x14, 0x0A, 0xB1, 0x01, 0xF1, 0x05,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x55, 0xAA, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0x09, 0x5D, 0xA5, 0xB2, 0x5E, 0xA0, 0x80, 0x7C,
+};

Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden.